aboutsummaryrefslogtreecommitdiff
path: root/engines
diff options
context:
space:
mode:
authorAlejandro Marzini2010-07-13 04:31:15 +0000
committerAlejandro Marzini2010-07-13 04:31:15 +0000
commit609e08d5dbae3179eddf981abe73d69009432de4 (patch)
treecddbd0a0e69eaa53b85f98f96dc410a307773f08 /engines
parent8b6a670391f1b5103e3761d78eef8f41d64cf8cd (diff)
parent03c0faa5d76f547603ee6389cdf958e2a6f0f43d (diff)
downloadscummvm-rg350-609e08d5dbae3179eddf981abe73d69009432de4.tar.gz
scummvm-rg350-609e08d5dbae3179eddf981abe73d69009432de4.tar.bz2
scummvm-rg350-609e08d5dbae3179eddf981abe73d69009432de4.zip
Merged from trunk, from Rev 49499 to HEAD
svn-id: r50840
Diffstat (limited to 'engines')
-rw-r--r--engines/advancedDetector.cpp58
-rw-r--r--engines/advancedDetector.h15
-rw-r--r--engines/agi/agi.cpp35
-rw-r--r--engines/agi/agi.h295
-rw-r--r--engines/agi/console.cpp17
-rw-r--r--engines/agi/cycle.cpp23
-rw-r--r--engines/agi/detection.cpp847
-rw-r--r--engines/agi/detection_tables.h863
-rw-r--r--engines/agi/keyboard.cpp11
-rw-r--r--engines/agi/loader_v2.cpp2
-rw-r--r--engines/agi/loader_v3.cpp18
-rw-r--r--engines/agi/module.mk5
-rw-r--r--engines/agi/op_cmd.cpp1433
-rw-r--r--engines/agi/op_test.cpp101
-rw-r--r--engines/agi/picture.cpp14
-rw-r--r--engines/agi/preagi.cpp4
-rw-r--r--engines/agi/saveload.cpp10
-rw-r--r--engines/agi/sound.cpp1209
-rw-r--r--engines/agi/sound.h425
-rw-r--r--engines/agi/sound_2gs.cpp919
-rw-r--r--engines/agi/sound_2gs.h353
-rw-r--r--engines/agi/sound_coco3.cpp80
-rw-r--r--engines/agi/sound_coco3.h73
-rw-r--r--engines/agi/sound_midi.cpp345
-rw-r--r--engines/agi/sound_midi.h114
-rw-r--r--engines/agi/sound_pcjr.cpp512
-rw-r--r--engines/agi/sound_pcjr.h127
-rw-r--r--engines/agi/sound_sarien.cpp357
-rw-r--r--engines/agi/sound_sarien.h120
-rw-r--r--engines/agi/sprite.cpp70
-rw-r--r--engines/agi/sprite.h4
-rw-r--r--engines/agi/text.cpp14
-rw-r--r--engines/agi/view.cpp33
-rw-r--r--engines/agi/view.h1
-rw-r--r--engines/agos/agos.cpp38
-rw-r--r--engines/agos/agos.h2
-rw-r--r--engines/agos/cursor.cpp18
-rw-r--r--engines/agos/detection.cpp11
-rw-r--r--engines/agos/draw.cpp8
-rw-r--r--engines/agos/event.cpp8
-rw-r--r--engines/agos/gfx.cpp6
-rw-r--r--engines/agos/icons.cpp2
-rw-r--r--engines/agos/input.cpp16
-rw-r--r--engines/agos/verb.cpp6
-rw-r--r--engines/agos/vga_e2.cpp6
-rw-r--r--engines/agos/vga_s2.cpp2
-rw-r--r--engines/cine/detection.cpp497
-rw-r--r--engines/cine/detection_tables.h513
-rw-r--r--engines/cine/gfx.cpp19
-rw-r--r--engines/cine/gfx.h4
-rw-r--r--engines/cine/saveload.cpp4
-rw-r--r--engines/cine/saveload.h2
-rw-r--r--engines/cruise/decompiler.cpp2
-rw-r--r--engines/cruise/detection.cpp6
-rw-r--r--engines/dialogs.cpp27
-rw-r--r--engines/draci/animation.cpp6
-rw-r--r--engines/draci/barchive.cpp5
-rw-r--r--engines/draci/detection.cpp6
-rw-r--r--engines/draci/draci.cpp78
-rw-r--r--engines/draci/game.cpp151
-rw-r--r--engines/draci/game.h20
-rw-r--r--engines/draci/script.cpp8
-rw-r--r--engines/draci/sound.cpp203
-rw-r--r--engines/draci/sound.h130
-rw-r--r--engines/drascula/actors.cpp3
-rw-r--r--engines/drascula/animation.cpp142
-rw-r--r--engines/drascula/console.cpp63
-rw-r--r--engines/drascula/console.h (renamed from engines/sci/graphics/gui32.h)45
-rw-r--r--engines/drascula/converse.cpp10
-rw-r--r--engines/drascula/detection.cpp6
-rw-r--r--engines/drascula/drascula.cpp47
-rw-r--r--engines/drascula/drascula.h5
-rw-r--r--engines/drascula/graphics.cpp25
-rw-r--r--engines/drascula/interface.cpp8
-rw-r--r--engines/drascula/module.mk1
-rw-r--r--engines/drascula/objects.cpp3
-rw-r--r--engines/drascula/palette.cpp4
-rw-r--r--engines/drascula/rooms.cpp4
-rw-r--r--engines/drascula/talk.cpp39
-rw-r--r--engines/engine.cpp1
-rw-r--r--engines/game.cpp4
-rw-r--r--engines/game.h1
-rw-r--r--engines/gob/detection.cpp4995
-rw-r--r--engines/gob/detection_tables.h5013
-rw-r--r--engines/gob/gob.cpp4
-rw-r--r--engines/gob/inter_v1.cpp11
-rw-r--r--engines/groovie/cursor.cpp13
-rw-r--r--engines/groovie/detection.cpp6
-rw-r--r--engines/groovie/font.cpp1
-rw-r--r--engines/groovie/groovie.cpp12
-rw-r--r--engines/groovie/groovie.h2
-rw-r--r--engines/groovie/music.cpp98
-rw-r--r--engines/groovie/music.h3
-rw-r--r--engines/groovie/script.cpp6
-rw-r--r--engines/kyra/detection.cpp1175
-rw-r--r--engines/kyra/detection_tables.h1192
-rw-r--r--engines/kyra/kyra_v1.cpp17
-rw-r--r--engines/kyra/sound_intern.h4
-rw-r--r--engines/kyra/sound_towns.cpp251
-rw-r--r--engines/lure/detection.cpp6
-rw-r--r--engines/lure/sound.cpp8
-rw-r--r--engines/m4/animation.cpp151
-rw-r--r--engines/m4/animation.h17
-rw-r--r--engines/m4/assets.cpp86
-rw-r--r--engines/m4/assets.h23
-rw-r--r--engines/m4/console.cpp2
-rw-r--r--engines/m4/detection.cpp6
-rw-r--r--engines/m4/events.cpp7
-rw-r--r--engines/m4/events.h1
-rw-r--r--engines/m4/font.cpp25
-rw-r--r--engines/m4/font.h8
-rw-r--r--engines/m4/globals.cpp33
-rw-r--r--engines/m4/globals.h18
-rw-r--r--engines/m4/graphics.cpp229
-rw-r--r--engines/m4/graphics.h25
-rw-r--r--engines/m4/m4.cpp42
-rw-r--r--engines/m4/m4.h12
-rw-r--r--engines/m4/mads_anim.cpp93
-rw-r--r--engines/m4/mads_anim.h3
-rw-r--r--engines/m4/mads_logic.cpp259
-rw-r--r--engines/m4/mads_logic.h9
-rw-r--r--engines/m4/mads_menus.cpp19
-rw-r--r--engines/m4/mads_player.cpp454
-rw-r--r--engines/m4/mads_player.h88
-rw-r--r--engines/m4/mads_scene.cpp223
-rw-r--r--engines/m4/mads_scene.h36
-rw-r--r--engines/m4/mads_views.cpp158
-rw-r--r--engines/m4/mads_views.h25
-rw-r--r--engines/m4/midi.cpp2
-rw-r--r--engines/m4/module.mk1
-rw-r--r--engines/m4/scene.cpp1
-rw-r--r--engines/m4/sound.cpp38
-rw-r--r--engines/m4/sound.h2
-rw-r--r--engines/m4/sprite.cpp103
-rw-r--r--engines/m4/sprite.h2
-rw-r--r--engines/made/detection.cpp6
-rw-r--r--engines/made/made.cpp8
-rw-r--r--engines/mohawk/console.cpp41
-rw-r--r--engines/mohawk/console.h1
-rw-r--r--engines/mohawk/detection.cpp883
-rw-r--r--engines/mohawk/detection_tables.h1030
-rw-r--r--engines/mohawk/dialogs.cpp17
-rw-r--r--engines/mohawk/graphics.cpp41
-rw-r--r--engines/mohawk/resource.cpp2
-rw-r--r--engines/mohawk/riven.cpp101
-rw-r--r--engines/mohawk/riven.h12
-rw-r--r--engines/mohawk/riven_external.cpp200
-rw-r--r--engines/mohawk/riven_external.h1
-rw-r--r--engines/mohawk/riven_saveload.cpp67
-rw-r--r--engines/mohawk/riven_saveload.h3
-rw-r--r--engines/mohawk/riven_scripts.cpp68
-rw-r--r--engines/mohawk/riven_scripts.h33
-rw-r--r--engines/mohawk/riven_vars.cpp2
-rw-r--r--engines/mohawk/sound.cpp15
-rw-r--r--engines/parallaction/callables_ns.cpp17
-rw-r--r--engines/parallaction/detection.cpp6
-rw-r--r--engines/parallaction/exec.cpp4
-rw-r--r--engines/parallaction/exec_br.cpp7
-rw-r--r--engines/parallaction/gfxbase.cpp60
-rw-r--r--engines/parallaction/graphics.cpp8
-rw-r--r--engines/parallaction/gui_br.cpp30
-rw-r--r--engines/parallaction/input.cpp8
-rw-r--r--engines/parallaction/parallaction.h3
-rw-r--r--engines/parallaction/parallaction_br.cpp31
-rw-r--r--engines/parallaction/parallaction_ns.cpp5
-rw-r--r--engines/parallaction/parser.h1
-rw-r--r--engines/parallaction/parser_br.cpp13
-rw-r--r--engines/parallaction/walk.cpp35
-rw-r--r--engines/queen/music.cpp9
-rw-r--r--engines/saga/actor.cpp4
-rw-r--r--engines/saga/console.cpp3
-rw-r--r--engines/saga/detection.cpp6
-rw-r--r--engines/saga/events.cpp1
-rw-r--r--engines/saga/font.h2
-rw-r--r--engines/saga/interface.cpp9
-rw-r--r--engines/saga/music.cpp274
-rw-r--r--engines/saga/music.h52
-rw-r--r--engines/saga/puzzle.cpp10
-rw-r--r--engines/saga/render.cpp6
-rw-r--r--engines/saga/saga.cpp18
-rw-r--r--engines/saga/saga.h1
-rw-r--r--engines/saga/scene.cpp12
-rw-r--r--engines/saga/script.h3
-rw-r--r--engines/saga/sfuncs.cpp19
-rw-r--r--engines/saga/sfuncs_ihnm.cpp12
-rw-r--r--engines/saga/sndres.cpp2
-rw-r--r--engines/saga/sound.cpp13
-rw-r--r--engines/saga/sound.h3
-rw-r--r--engines/saga/sthread.cpp8
-rw-r--r--engines/sci/console.cpp935
-rw-r--r--engines/sci/console.h13
-rw-r--r--engines/sci/debug.h7
-rw-r--r--engines/sci/decompressor.cpp16
-rw-r--r--engines/sci/detection.cpp120
-rw-r--r--engines/sci/detection_tables.h69
-rw-r--r--engines/sci/engine/features.cpp116
-rw-r--r--engines/sci/engine/features.h6
-rw-r--r--engines/sci/engine/game.cpp169
-rw-r--r--engines/sci/engine/gc.cpp163
-rw-r--r--engines/sci/engine/gc.h15
-rw-r--r--engines/sci/engine/kernel.cpp1390
-rw-r--r--engines/sci/engine/kernel.h227
-rw-r--r--engines/sci/engine/kernel32.cpp149
-rw-r--r--engines/sci/engine/kevent.cpp27
-rw-r--r--engines/sci/engine/kfile.cpp412
-rw-r--r--engines/sci/engine/kgraphics.cpp696
-rw-r--r--engines/sci/engine/klists.cpp223
-rw-r--r--engines/sci/engine/kmath.cpp29
-rw-r--r--engines/sci/engine/kmenu.cpp8
-rw-r--r--engines/sci/engine/kmisc.cpp87
-rw-r--r--engines/sci/engine/kmovement.cpp117
-rw-r--r--engines/sci/engine/kparse.cpp6
-rw-r--r--engines/sci/engine/kpathing.cpp258
-rw-r--r--engines/sci/engine/kscripts.cpp115
-rw-r--r--engines/sci/engine/ksound.cpp104
-rw-r--r--engines/sci/engine/kstring.cpp29
-rw-r--r--engines/sci/engine/message.cpp7
-rw-r--r--engines/sci/engine/savegame.cpp472
-rw-r--r--engines/sci/engine/savegame.h8
-rw-r--r--engines/sci/engine/script.cpp786
-rw-r--r--engines/sci/engine/script.h359
-rw-r--r--engines/sci/engine/scriptdebug.cpp184
-rw-r--r--engines/sci/engine/seg_manager.cpp439
-rw-r--r--engines/sci/engine/seg_manager.h125
-rw-r--r--engines/sci/engine/segment.cpp512
-rw-r--r--engines/sci/engine/segment.h247
-rw-r--r--engines/sci/engine/selector.cpp39
-rw-r--r--engines/sci/engine/selector.h127
-rw-r--r--engines/sci/engine/state.cpp117
-rw-r--r--engines/sci/engine/state.h82
-rw-r--r--engines/sci/engine/static_selectors.cpp41
-rw-r--r--engines/sci/engine/vm.cpp1106
-rw-r--r--engines/sci/engine/vm.h372
-rw-r--r--engines/sci/engine/vm_types.h7
-rw-r--r--engines/sci/event.cpp70
-rw-r--r--engines/sci/event.h57
-rw-r--r--engines/sci/graphics/animate.cpp466
-rw-r--r--engines/sci/graphics/animate.h20
-rw-r--r--engines/sci/graphics/cache.cpp2
-rw-r--r--engines/sci/graphics/cache.h2
-rw-r--r--engines/sci/graphics/compare.cpp36
-rw-r--r--engines/sci/graphics/compare.h2
-rw-r--r--engines/sci/graphics/controls.cpp1
-rw-r--r--engines/sci/graphics/coordadjuster.cpp1
-rw-r--r--engines/sci/graphics/cursor.cpp60
-rw-r--r--engines/sci/graphics/cursor.h4
-rw-r--r--engines/sci/graphics/font.cpp7
-rw-r--r--engines/sci/graphics/fontsjis.h6
-rw-r--r--engines/sci/graphics/frameout.cpp219
-rw-r--r--engines/sci/graphics/frameout.h9
-rw-r--r--engines/sci/graphics/gui.cpp139
-rw-r--r--engines/sci/graphics/gui.h91
-rw-r--r--engines/sci/graphics/gui32.cpp83
-rw-r--r--engines/sci/graphics/maciconbar.cpp1
-rw-r--r--engines/sci/graphics/menu.cpp88
-rw-r--r--engines/sci/graphics/menu.h11
-rw-r--r--engines/sci/graphics/paint.h2
-rw-r--r--engines/sci/graphics/paint16.cpp123
-rw-r--r--engines/sci/graphics/paint16.h27
-rw-r--r--engines/sci/graphics/paint32.cpp7
-rw-r--r--engines/sci/graphics/paint32.h3
-rw-r--r--engines/sci/graphics/palette.cpp456
-rw-r--r--engines/sci/graphics/palette.h49
-rw-r--r--engines/sci/graphics/picture.cpp8
-rw-r--r--engines/sci/graphics/portrait.cpp12
-rw-r--r--engines/sci/graphics/portrait.h5
-rw-r--r--engines/sci/graphics/ports.cpp47
-rw-r--r--engines/sci/graphics/ports.h3
-rw-r--r--engines/sci/graphics/robot.cpp8
-rw-r--r--engines/sci/graphics/robot.h6
-rw-r--r--engines/sci/graphics/screen.cpp139
-rw-r--r--engines/sci/graphics/screen.h47
-rw-r--r--engines/sci/graphics/text16.cpp90
-rw-r--r--engines/sci/graphics/text16.h6
-rw-r--r--engines/sci/graphics/transitions.cpp77
-rw-r--r--engines/sci/graphics/transitions.h3
-rw-r--r--engines/sci/graphics/view.cpp397
-rw-r--r--engines/sci/graphics/view.h27
-rw-r--r--engines/sci/module.mk6
-rw-r--r--engines/sci/parser/vocabulary.cpp53
-rw-r--r--engines/sci/resource.cpp737
-rw-r--r--engines/sci/resource.h332
-rw-r--r--engines/sci/resource_audio.cpp294
-rw-r--r--engines/sci/resource_intern.h193
-rw-r--r--engines/sci/sci.cpp432
-rw-r--r--engines/sci/sci.h176
-rw-r--r--engines/sci/sound/audio.cpp83
-rw-r--r--engines/sci/sound/audio.h5
-rw-r--r--engines/sci/sound/drivers/adlib.cpp14
-rw-r--r--engines/sci/sound/drivers/fb01.cpp4
-rw-r--r--engines/sci/sound/drivers/midi.cpp25
-rw-r--r--engines/sci/sound/drivers/mididriver.h1
-rw-r--r--engines/sci/sound/iterator/core.cpp1013
-rw-r--r--engines/sci/sound/iterator/core.h209
-rw-r--r--engines/sci/sound/iterator/iterator.cpp1686
-rw-r--r--engines/sci/sound/iterator/iterator.h326
-rw-r--r--engines/sci/sound/iterator/iterator_internal.h276
-rw-r--r--engines/sci/sound/iterator/songlib.cpp189
-rw-r--r--engines/sci/sound/iterator/songlib.h171
-rw-r--r--engines/sci/sound/iterator/test-iterator.cpp423
-rw-r--r--engines/sci/sound/midiparser_sci.cpp659
-rw-r--r--engines/sci/sound/midiparser_sci.h40
-rw-r--r--engines/sci/sound/music.cpp266
-rw-r--r--engines/sci/sound/music.h60
-rw-r--r--engines/sci/sound/soundcmd.cpp919
-rw-r--r--engines/sci/sound/soundcmd.h97
-rw-r--r--engines/scumm/charset.cpp7
-rw-r--r--engines/scumm/detection.cpp60
-rw-r--r--engines/scumm/detection_tables.h29
-rw-r--r--engines/scumm/dialogs.cpp7
-rw-r--r--engines/scumm/saveload.cpp4
-rw-r--r--engines/scumm/script_v5.cpp11
-rw-r--r--engines/scumm/scumm-md5.h47
-rw-r--r--engines/scumm/scumm.cpp38
-rw-r--r--engines/scumm/sound.cpp2
-rw-r--r--engines/scumm/string.cpp2
-rw-r--r--engines/scumm/verbs.h2
-rw-r--r--engines/sky/sky.cpp10
-rw-r--r--engines/sword1/music.cpp2
-rw-r--r--engines/teenagent/detection.cpp4
-rw-r--r--engines/tinsel/cliprect.cpp12
-rw-r--r--engines/tinsel/detection.cpp550
-rw-r--r--engines/tinsel/detection_tables.h567
-rw-r--r--engines/tinsel/graphics.cpp23
-rw-r--r--engines/tinsel/pcode.cpp7
-rw-r--r--engines/tinsel/tinlib.cpp2
-rw-r--r--engines/tinsel/tinsel.cpp8
-rw-r--r--engines/tinsel/tinsel.h6
-rw-r--r--engines/touche/detection.cpp11
-rw-r--r--engines/touche/midi.cpp6
-rw-r--r--engines/touche/touche.cpp6
-rw-r--r--engines/tucker/detection.cpp4
332 files changed, 27345 insertions, 25190 deletions
diff --git a/engines/advancedDetector.cpp b/engines/advancedDetector.cpp
index b149b43ad7..315763a6da 100644
--- a/engines/advancedDetector.cpp
+++ b/engines/advancedDetector.cpp
@@ -208,6 +208,7 @@ static void updateGameDescriptor(GameDescriptor &desc, const ADGameDescription *
desc["extra"] = realDesc->extra;
desc.setGUIOptions(realDesc->guioptions | params.guioptions);
+ desc.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(realDesc->language));
}
GameList AdvancedMetaEngine::detectGames(const Common::FSList &fslist) const {
@@ -305,7 +306,7 @@ Common::Error AdvancedMetaEngine::createInstance(OSystem *syst, Engine **engine)
// If the GUI options were updated, we catch this here and update them in the users config
// file transparently.
- Common::updateGameGUIOptions(agdDesc->guioptions | params.guioptions);
+ Common::updateGameGUIOptions(agdDesc->guioptions | params.guioptions, getGameGUIOptionsDescriptionLanguage(agdDesc->language));
debug(2, "Running %s", toGameDescriptor(*agdDesc, params.list).description().c_str());
if (!createInstance(syst, engine, agdDesc))
@@ -340,24 +341,37 @@ static void reportUnknown(const Common::FSNode &path, const SizeMD5Map &filesSiz
static ADGameDescList detectGameFilebased(const FileMap &allFiles, const ADParams &params);
-static ADGameDescList detectGame(const Common::FSList &fslist, const ADParams &params, Common::Language language, Common::Platform platform, const Common::String &extra) {
- FileMap allFiles;
- SizeMD5Map filesSizeMD5;
-
- const ADGameFileDescription *fileDesc;
- const ADGameDescription *g;
- const byte *descPtr;
+static void composeFileHashMap(const Common::FSList &fslist, FileMap &allFiles, int depth, const char **directoryGlobs) {
+ if (depth <= 0)
+ return;
if (fslist.empty())
- return ADGameDescList();
- Common::FSNode parent = fslist.begin()->getParent();
- debug(3, "Starting detection in dir '%s'", parent.getPath().c_str());
+ return;
// First we compose a hashmap of all files in fslist.
// Includes nifty stuff like removing trailing dots and ignoring case.
for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
- if (file->isDirectory())
- continue;
+ if (file->isDirectory()) {
+ Common::FSList files;
+
+ if (!directoryGlobs)
+ continue;
+
+ bool matched = false;
+ for (const char *glob = *directoryGlobs; *glob; glob++)
+ if (file->getName().matchString(glob, true)) {
+ matched = true;
+ break;
+ }
+
+ if (!matched)
+ continue;
+
+ if (!file->getChildren(files, Common::FSNode::kListAll))
+ continue;
+
+ composeFileHashMap(files, allFiles, depth - 1, directoryGlobs);
+ }
Common::String tstr = file->getName();
@@ -367,6 +381,24 @@ static ADGameDescList detectGame(const Common::FSList &fslist, const ADParams &p
allFiles[tstr] = *file; // Record the presence of this file
}
+}
+
+static ADGameDescList detectGame(const Common::FSList &fslist, const ADParams &params, Common::Language language, Common::Platform platform, const Common::String &extra) {
+ FileMap allFiles;
+ SizeMD5Map filesSizeMD5;
+
+ const ADGameFileDescription *fileDesc;
+ const ADGameDescription *g;
+ const byte *descPtr;
+
+ if (fslist.empty())
+ return ADGameDescList();
+ Common::FSNode parent = fslist.begin()->getParent();
+ debug(3, "Starting detection in dir '%s'", parent.getPath().c_str());
+
+ // First we compose a hashmap of all files in fslist.
+ // Includes nifty stuff like removing trailing dots and ignoring case.
+ composeFileHashMap(fslist, allFiles, (params.depth == 0 ? 1 : params.depth), params.directoryGlobs);
// Check which files are included in some ADGameDescription *and* present
// in fslist. Compute MD5s and file sizes for these files.
diff --git a/engines/advancedDetector.h b/engines/advancedDetector.h
index 370d958ce6..de4fc3bbf8 100644
--- a/engines/advancedDetector.h
+++ b/engines/advancedDetector.h
@@ -190,6 +190,21 @@ struct ADParams {
* enum for the list.
*/
uint32 guioptions;
+
+ /**
+ * Maximum depth of directories to look up
+ * If set to 0, the depth is 1 level
+ */
+ uint32 depth;
+
+ /**
+ * Case-insensitive list of directory globs which could be used for
+ * going deeper int directory structure.
+ * @see String::matchString() method for format description.
+ *
+ * @note Last item must be 0
+ */
+ const char **directoryGlobs;
};
diff --git a/engines/agi/agi.cpp b/engines/agi/agi.cpp
index c2c6d10bfe..4a994b731a 100644
--- a/engines/agi/agi.cpp
+++ b/engines/agi/agi.cpp
@@ -272,20 +272,19 @@ void AgiEngine::processEvents() {
}
void AgiEngine::pollTimer() {
- static uint32 m = 0;
uint32 dm;
- if (_tickTimer < m)
- m = 0;
+ if (_tickTimer < _lastTickTimer)
+ _lastTickTimer = 0;
- while ((dm = _tickTimer - m) < 5) {
+ while ((dm = _tickTimer - _lastTickTimer) < 5) {
processEvents();
if (_console->isAttached())
_console->onFrame();
_system->delayMillis(10);
_system->updateScreen();
}
- m = _tickTimer;
+ _lastTickTimer = _tickTimer;
}
void AgiEngine::agiTimerFunctionLow(void *refCon) {
@@ -506,13 +505,6 @@ AgiEngine::AgiEngine(OSystem *syst, const AGIGameDescription *gameDesc) : AgiBas
_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume"));
_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, ConfMan.getInt("music_volume"));
- const GameSettings *g;
-
- const char *gameid = ConfMan.get("gameid").c_str();
- for (g = agiSettings; g->gameid; ++g)
- if (!scumm_stricmp(g->gameid, gameid))
- _gameId = g->id;
-
parseFeatures();
_rnd = new Common::RandomSource();
@@ -543,6 +535,7 @@ AgiEngine::AgiEngine(OSystem *syst, const AGIGameDescription *gameDesc) : AgiBas
_allowSynthetic = false;
_tickTimer = 0;
+ _lastTickTimer = 0;
_intobj = NULL;
@@ -556,7 +549,7 @@ AgiEngine::AgiEngine(OSystem *syst, const AGIGameDescription *gameDesc) : AgiBas
_restartGame = false;
- _oldMode = -1;
+ _oldMode = INPUT_NONE;
_predictiveDialogRunning = false;
_predictiveDictText = NULL;
@@ -569,6 +562,10 @@ AgiEngine::AgiEngine(OSystem *syst, const AGIGameDescription *gameDesc) : AgiBas
_game.lastController = 0;
for (int i = 0; i < MAX_DIRS; i++)
_game.controllerOccured[i] = false;
+
+ setupOpcodes();
+ _curLogic = NULL;
+ _timerHack = 0;
}
void AgiEngine::initialize() {
@@ -583,13 +580,19 @@ void AgiEngine::initialize() {
} else if (getPlatform() == Common::kPlatformCoCo3) {
_soundemu = SOUND_EMU_COCO3;
} else {
- switch (MidiDriver::detectMusicDriver(MDT_PCSPK)) {
- case MD_PCSPK:
+ switch (MidiDriver::getMusicType(MidiDriver::detectDevice(MDT_PCSPK))) {
+ case MT_PCSPK:
_soundemu = SOUND_EMU_PC;
break;
- default:
+ case MT_PCJR:
+ _soundemu = SOUND_EMU_PCJR;
+ break;
+ case MT_ADLIB:
_soundemu = SOUND_EMU_NONE;
break;
+ default:
+ _soundemu = SOUND_EMU_MIDI;
+ break;
}
}
diff --git a/engines/agi/agi.h b/engines/agi/agi.h
index fb9e204101..507e7f7a11 100644
--- a/engines/agi/agi.h
+++ b/engines/agi/agi.h
@@ -37,6 +37,14 @@
#include "gui/debugger.h"
+// AGI resources
+#include "agi/console.h"
+#include "agi/view.h"
+#include "agi/picture.h"
+#include "agi/logic.h"
+#include "agi/sound.h"
+
+
namespace Common { class RandomSource; }
/**
@@ -110,23 +118,13 @@ enum AgiGameID {
GID_SQ2,
GID_XMASCARD,
GID_FANMADE,
- GID_GETOUTTASQ,
+ GID_GETOUTTASQ, // Fanmade
+ GID_SQ0, // Fanmade
GID_MICKEY, // PreAGI
GID_WINNIE, // PreAGI
- GID_TROLL // PreAGI
+ GID_TROLL // PreAGI
};
-} // End of namespace Agi
-
-// AGI resources
-#include "agi/console.h"
-#include "agi/view.h"
-#include "agi/picture.h"
-#include "agi/logic.h"
-#include "agi/sound.h"
-
-namespace Agi {
-
enum AgiGameType {
GType_PreAGI = 0,
GType_V2 = 1,
@@ -151,7 +149,8 @@ enum AgiGameFeatures {
GF_MENUS = (1 << 7),
GF_ESCPAUSE = (1 << 8),
GF_OLDAMIGAV20 = (1 << 9),
- GF_CLIPCOORDS = (1 << 10)
+ GF_CLIPCOORDS = (1 << 10),
+ GF_2GSOLDSOUND = (1 << 11)
};
struct AGIGameDescription;
@@ -316,6 +315,12 @@ enum AgiComputerType {
kAgiComputerAmigaOld = 20 // Older Amiga AGI interpreters' value (Seldom used)
};
+enum AgiSoundType {
+ kAgiSoundPC = 1,
+ kAgiSoundTandy = 3, // Tandy (This value is also used by the Amiga AGI and Apple IIGS AGI)
+ kAgiSound2GSOld = 8 // Apple IIGS's Gold Rush! (Version 1.0M 1989-02-28 (CE), AGI 3.003) uses value 8
+};
+
/**
* AGI flags
*/
@@ -497,10 +502,32 @@ struct ScriptPos {
int curIP;
};
-#define EGO_VIEW_TABLE 0
-#define HORIZON 36
-#define _WIDTH 160
-#define _HEIGHT 168
+enum {
+ EGO_VIEW_TABLE = 0,
+ HORIZON = 36,
+ _WIDTH = 160,
+ _HEIGHT = 168
+};
+
+enum InputMode {
+ INPUT_NORMAL = 0x01,
+ INPUT_GETSTRING = 0x02,
+ INPUT_MENU = 0x03,
+ INPUT_NONE = 0x04
+};
+
+enum State {
+ STATE_INIT = 0x00,
+ STATE_LOADED = 0x01,
+ STATE_RUNNING = 0x02
+};
+
+enum {
+ SBUF16_OFFSET = 0,
+ SBUF256_OFFSET = ((_WIDTH) * (_HEIGHT)),
+ FROM_SBUF16_TO_SBUF256_OFFSET = ((SBUF256_OFFSET) - (SBUF16_OFFSET)),
+ FROM_SBUF256_TO_SBUF16_OFFSET = ((SBUF16_OFFSET) - (SBUF256_OFFSET))
+};
/**
* AGI game structure.
@@ -508,10 +535,7 @@ struct ScriptPos {
* by the interpreter.
*/
struct AgiGame {
-#define STATE_INIT 0x00
-#define STATE_LOADED 0x01
-#define STATE_RUNNING 0x02
- int state; /**< state of the interpreter */
+ State state; /**< state of the interpreter */
// TODO: Check whether adjMouseX and adjMouseY must be saved and loaded when using savegames.
// If they must be then loading and saving is partially broken at the moment.
@@ -535,12 +559,9 @@ struct AgiGame {
uint8 inputBuffer[40]; /**< buffer for user input */
uint8 echoBuffer[40]; /**< buffer for echo.line */
int keypress;
-#define INPUT_NORMAL 0x01
-#define INPUT_GETSTRING 0x02
-#define INPUT_MENU 0x03
-#define INPUT_NONE 0x04
- int inputMode; /**< keyboard input mode */
- int inputEnabled; /**< keyboard input enabled */
+
+ InputMode inputMode; /**< keyboard input mode */
+ bool inputEnabled; /**< keyboard input enabled */
int lognum; /**< current logic number */
Common::Array<ScriptPos> execStack;
@@ -568,10 +589,7 @@ struct AgiGame {
char cursorChar;
unsigned int colorFg;
unsigned int colorBg;
-#define SBUF16_OFFSET 0
-#define SBUF256_OFFSET ((_WIDTH) * (_HEIGHT))
-#define FROM_SBUF16_TO_SBUF256_OFFSET ((SBUF256_OFFSET) - (SBUF16_OFFSET))
-#define FROM_SBUF256_TO_SBUF16_OFFSET ((SBUF16_OFFSET) - (SBUF256_OFFSET))
+
uint8 *sbufOrig; /**< Pointer to the 160x336 AGI screen buffer that contains vertically two 160x168 screens (16 color and 256 color). */
uint8 *sbuf16c; /**< 160x168 16 color (+control line & priority information) AGI screen buffer. Points at sbufOrig + SBUF16_OFFSET. */
uint8 *sbuf256c; /**< 160x168 256 color AGI screen buffer (For AGI256 and AGI256-2 support). Points at sbufOrig + SBUF256_OFFSET. */
@@ -774,8 +792,6 @@ public:
};
class AgiEngine : public AgiBase {
- int _gameId;
-
protected:
// Engine APIs
virtual Common::Error go();
@@ -788,9 +804,6 @@ protected:
public:
AgiEngine(OSystem *syst, const AGIGameDescription *gameDesc);
virtual ~AgiEngine();
- int getGameId() {
- return _gameId;
- }
Common::Error loadGameState(int slot);
Common::Error saveGameState(int slot, const char *desc);
@@ -798,6 +811,7 @@ public:
private:
uint32 _tickTimer;
+ uint32 _lastTickTimer;
int _keyQueue[KEY_QUEUE_SIZE];
int _keyQueueStart;
@@ -829,7 +843,7 @@ public:
int loadGameSimple();
uint8 *_intobj;
- int _oldMode;
+ InputMode _oldMode;
bool _restartGame;
Menu* _menu;
@@ -871,7 +885,7 @@ public:
static void agiTimerFunctionLow(void *refCon);
void initPriTable();
- void newInputMode(int);
+ void newInputMode(InputMode mode);
void oldInputMode();
int getvar(int);
@@ -920,6 +934,17 @@ public:
int testIfCode(int);
void executeAgiCommand(uint8, uint8 *);
+private:
+ // Some submethods of testIfCode
+ uint8 testObjRight(uint8, uint8, uint8, uint8, uint8);
+ uint8 testObjCentre(uint8, uint8, uint8, uint8, uint8);
+ uint8 testObjInBox(uint8, uint8, uint8, uint8, uint8);
+ uint8 testPosn(uint8, uint8, uint8, uint8, uint8);
+ uint8 testSaid(uint8, uint8 *);
+ uint8 testController(uint8);
+ uint8 testKeypressed();
+ uint8 testCompareStrings(uint8, uint8);
+
// View
private:
@@ -1024,6 +1049,198 @@ private:
bool _predictiveDialogRunning;
public:
char _predictiveResult[40];
+
+private:
+ typedef void (AgiEngine::*AgiCommand)(uint8 *);
+
+ AgiCommand _agiCommands[183];
+ AgiLogic *_curLogic;
+ int _timerHack; // Workaround for timer loop in MH1 logic 153
+
+ void setupOpcodes();
+
+ void cmd_increment(uint8 *p);
+ void cmd_decrement(uint8 *p);
+ void cmd_assignn(uint8 *p);
+ void cmd_assignv(uint8 *p);
+ void cmd_addn(uint8 *p);
+ void cmd_addv(uint8 *p);
+ void cmd_subn(uint8 *p);
+ void cmd_subv(uint8 *p); // 0x08
+ void cmd_lindirectv(uint8 *p);
+ void cmd_rindirect(uint8 *p);
+ void cmd_lindirectn(uint8 *p);
+ void cmd_set(uint8 *p);
+ void cmd_reset(uint8 *p);
+ void cmd_toggle(uint8 *p);
+ void cmd_set_v(uint8 *p);
+ void cmd_reset_v(uint8 *p); // 0x10
+ void cmd_toggle_v(uint8 *p);
+ void cmd_new_room(uint8 *p);
+ void cmd_new_room_f(uint8 *p);
+ void cmd_load_logic(uint8 *p);
+ void cmd_load_logic_f(uint8 *p);
+ void cmd_call(uint8 *p);
+ void cmd_call_f(uint8 *p);
+ void cmd_load_pic(uint8 *p); // 0x18
+ void cmd_draw_pic(uint8 *p);
+ void cmd_show_pic(uint8 *p);
+ void cmd_discard_pic(uint8 *p);
+ void cmd_overlay_pic(uint8 *p);
+ void cmd_show_pri_screen(uint8 *p);
+ void cmd_load_view(uint8 *p);
+ void cmd_load_view_f(uint8 *p);
+ void cmd_discard_view(uint8 *p); // 0x20
+ void cmd_animate_obj(uint8 *p);
+ void cmd_unanimate_all(uint8 *p);
+ void cmd_draw(uint8 *p);
+ void cmd_erase(uint8 *p);
+ void cmd_position(uint8 *p);
+ void cmd_position_f(uint8 *p);
+ void cmd_get_posn(uint8 *p);
+ void cmd_reposition(uint8 *p); // 0x28
+ void cmd_set_view(uint8 *p);
+ void cmd_set_view_f(uint8 *p);
+ void cmd_set_loop(uint8 *p);
+ void cmd_set_loop_f(uint8 *p);
+ void cmd_fix_loop(uint8 *p);
+ void cmd_release_loop(uint8 *p);
+ void cmd_set_cel(uint8 *p);
+ void cmd_set_cel_f(uint8 *p); // 0x30
+ void cmd_last_cel(uint8 *p);
+ void cmd_current_cel(uint8 *p);
+ void cmd_current_loop(uint8 *p);
+ void cmd_current_view(uint8 *p);
+ void cmd_number_of_loops(uint8 *p);
+ void cmd_set_priority(uint8 *p);
+ void cmd_set_priority_f(uint8 *p);
+ void cmd_release_priority(uint8 *p); // 0x38
+ void cmd_get_priority(uint8 *p);
+ void cmd_stop_update(uint8 *p);
+ void cmd_start_update(uint8 *p);
+ void cmd_force_update(uint8 *p);
+ void cmd_ignore_horizon(uint8 *p);
+ void cmd_observe_horizon(uint8 *p);
+ void cmd_set_horizon(uint8 *p);
+ void cmd_object_on_water(uint8 *p); // 0x40
+ void cmd_object_on_land(uint8 *p);
+ void cmd_object_on_anything(uint8 *p);
+ void cmd_ignore_objs(uint8 *p);
+ void cmd_observe_objs(uint8 *p);
+ void cmd_distance(uint8 *p);
+ void cmd_stop_cycling(uint8 *p);
+ void cmd_start_cycling(uint8 *p);
+ void cmd_normal_cycle(uint8 *p); // 0x48
+ void cmd_end_of_loop(uint8 *p);
+ void cmd_reverse_cycle(uint8 *p);
+ void cmd_reverse_loop(uint8 *p);
+ void cmd_cycle_time(uint8 *p);
+ void cmd_stop_motion(uint8 *p);
+ void cmd_start_motion(uint8 *p);
+ void cmd_step_size(uint8 *p);
+ void cmd_step_time(uint8 *p); // 0x50
+ void cmd_move_obj(uint8 *p);
+ void cmd_move_obj_f(uint8 *p);
+ void cmd_follow_ego(uint8 *p);
+ void cmd_wander(uint8 *p);
+ void cmd_normal_motion(uint8 *p);
+ void cmd_set_dir(uint8 *p);
+ void cmd_get_dir(uint8 *p);
+ void cmd_ignore_blocks(uint8 *p); // 0x58
+ void cmd_observe_blocks(uint8 *p);
+ void cmd_block(uint8 *p);
+ void cmd_unblock(uint8 *p);
+ void cmd_get(uint8 *p);
+ void cmd_get_f(uint8 *p);
+ void cmd_drop(uint8 *p);
+ void cmd_put(uint8 *p);
+ void cmd_put_f(uint8 *p); // 0x60
+ void cmd_get_room_f(uint8 *p);
+ void cmd_load_sound(uint8 *p);
+ void cmd_sound(uint8 *p);
+ void cmd_stop_sound(uint8 *p);
+ void cmd_print(uint8 *p);
+ void cmd_print_f(uint8 *p);
+ void cmd_display(uint8 *p);
+ void cmd_display_f(uint8 *p); // 0x68
+ void cmd_clear_lines(uint8 *p);
+ void cmd_text_screen(uint8 *p);
+ void cmd_graphics(uint8 *p);
+ void cmd_set_cursor_char(uint8 *p);
+ void cmd_set_text_attribute(uint8 *p);
+ void cmd_shake_screen(uint8 *p);
+ void cmd_configure_screen(uint8 *p);
+ void cmd_status_line_on(uint8 *p); // 0x70
+ void cmd_status_line_off(uint8 *p);
+ void cmd_set_string(uint8 *p);
+ void cmd_get_string(uint8 *p);
+ void cmd_word_to_string(uint8 *p);
+ void cmd_parse(uint8 *p);
+ void cmd_get_num(uint8 *p);
+ void cmd_prevent_input(uint8 *p);
+ void cmd_accept_input(uint8 *p); // 0x78
+ void cmd_set_key(uint8 *p);
+ void cmd_add_to_pic(uint8 *p);
+ void cmd_add_to_pic_f(uint8 *p);
+ void cmd_status(uint8 *p);
+ void cmd_save_game(uint8 *p);
+ void cmd_load_game(uint8 *p);
+ void cmd_init_disk(uint8 *p);
+ void cmd_restart_game(uint8 *p); // 0x80
+ void cmd_show_obj(uint8 *p);
+ void cmd_random(uint8 *p);
+ void cmd_program_control(uint8 *p);
+ void cmd_player_control(uint8 *p);
+ void cmd_obj_status_f(uint8 *p);
+ void cmd_quit(uint8 *p);
+ void cmd_show_mem(uint8 *p);
+ void cmd_pause(uint8 *p); // 0x88
+ void cmd_echo_line(uint8 *p);
+ void cmd_cancel_line(uint8 *p);
+ void cmd_init_joy(uint8 *p);
+ void cmd_toggle_monitor(uint8 *p);
+ void cmd_version(uint8 *p);
+ void cmd_script_size(uint8 *p);
+ void cmd_set_game_id(uint8 *p);
+ void cmd_log(uint8 *p); // 0x90
+ void cmd_set_scan_start(uint8 *p);
+ void cmd_reset_scan_start(uint8 *p);
+ void cmd_reposition_to(uint8 *p);
+ void cmd_reposition_to_f(uint8 *p);
+ void cmd_trace_on(uint8 *p);
+ void cmd_trace_info(uint8 *p);
+ void cmd_print_at(uint8 *p);
+ void cmd_print_at_v(uint8 *p); // 0x98
+ //void cmd_discard_view(uint8 *p); // Opcode repeated from 0x20 ?
+ void cmd_clear_text_rect(uint8 *p);
+ void cmd_set_upper_left(uint8 *p);
+ void cmd_set_menu(uint8 *p);
+ void cmd_set_menu_item(uint8 *p);
+ void cmd_submit_menu(uint8 *p);
+ void cmd_enable_item(uint8 *p);
+ void cmd_disable_item(uint8 *p); // 0xa0
+ void cmd_menu_input(uint8 *p);
+ void cmd_show_obj_v(uint8 *p);
+ void cmd_open_dialogue(uint8 *p);
+ void cmd_close_dialogue(uint8 *p);
+ void cmd_mul_n(uint8 *p);
+ void cmd_mul_v(uint8 *p);
+ void cmd_div_n(uint8 *p);
+ void cmd_div_v(uint8 *p); // 0xa8
+ void cmd_close_window(uint8 *p);
+ void cmd_set_simple(uint8 *p);
+ void cmd_push_script(uint8 *p);
+ void cmd_pop_script(uint8 *p);
+ void cmd_hold_key(uint8 *p);
+ void cmd_set_pri_base(uint8 *p);
+ void cmd_discard_sound(uint8 *p);
+ void cmd_hide_mouse(uint8 *p); // 0xb0
+ void cmd_allow_menu(uint8 *p);
+ void cmd_show_mouse(uint8 *p);
+ void cmd_fence_mouse(uint8 *p);
+ void cmd_mouse_posn(uint8 *p);
+ void cmd_release_key(uint8 *p);
+ void cmd_adj_ego_move_to_x_y(uint8 *p);
};
} // End of namespace Agi
diff --git a/engines/agi/console.cpp b/engines/agi/console.cpp
index a0621f80dd..e881b092e3 100644
--- a/engines/agi/console.cpp
+++ b/engines/agi/console.cpp
@@ -64,7 +64,7 @@ void Console::postEnter() {
bool Console::Cmd_SetVar(int argc, const char **argv) {
if (argc != 3) {
- DebugPrintf("Usage: setvar <varnum> <value>");
+ DebugPrintf("Usage: setvar <varnum> <value>\n");
return true;
}
int p1 = (int)atoi(argv[1]);
@@ -76,7 +76,7 @@ bool Console::Cmd_SetVar(int argc, const char **argv) {
bool Console::Cmd_SetFlag(int argc, const char **argv) {
if (argc != 3) {
- DebugPrintf("Usage: setvar <varnum> <value>");
+ DebugPrintf("Usage: setvar <varnum> <value>\n");
return true;
}
int p1 = (int)atoi(argv[1]);
@@ -88,7 +88,7 @@ bool Console::Cmd_SetFlag(int argc, const char **argv) {
bool Console::Cmd_SetObj(int argc, const char **argv) {
if (argc != 3) {
- DebugPrintf("Usage: setvar <varnum> <value>");
+ DebugPrintf("Usage: setvar <varnum> <value>\n");
return true;
}
int p1 = (int)atoi(argv[1]);
@@ -99,6 +99,11 @@ bool Console::Cmd_SetObj(int argc, const char **argv) {
}
bool Console::Cmd_RunOpcode(int argc, const char **argv) {
+ if (argc < 2) {
+ DebugPrintf("Usage: runopcode <name> <parameter0> ....\n");
+ return true;
+ }
+
for (int i = 0; logicNamesCmd[i].name; i++) {
if (!strcmp(argv[1], logicNamesCmd[i].name)) {
uint8 p[16];
@@ -120,6 +125,8 @@ bool Console::Cmd_RunOpcode(int argc, const char **argv) {
}
}
+ DebugPrintf("Unknown opcode\n");
+
return true;
}
@@ -243,6 +250,10 @@ bool Console::Cmd_Cont(int argc, const char **argv) {
}
bool Console::Cmd_Room(int argc, const char **argv) {
+ if (argc == 2) {
+ _vm->newRoom(strtoul(argv[1], NULL, 0));
+ }
+
DebugPrintf("Current room: %d\n", _vm->getvar(0));
return true;
diff --git a/engines/agi/cycle.cpp b/engines/agi/cycle.cpp
index 10df40556f..c185c3efb3 100644
--- a/engines/agi/cycle.cpp
+++ b/engines/agi/cycle.cpp
@@ -177,9 +177,12 @@ void AgiEngine::updateTimer() {
setvar(vDays, getvar(vDays) + 1);
}
-void AgiEngine::newInputMode(int i) {
+void AgiEngine::newInputMode(InputMode mode) {
+ if (mode == INPUT_MENU && !getflag(fMenusWork) && !(getFeatures() & GF_MENUS))
+ return;
+
_oldMode = _game.inputMode;
- _game.inputMode = i;
+ _game.inputMode = mode;
}
void AgiEngine::oldInputMode() {
@@ -314,7 +317,8 @@ int AgiEngine::playGame() {
_game.clockEnabled = true;
_game.lineUserInput = 22;
- if (getFeatures() & GF_AGIMOUSE)
+ // We run AGIMOUSE always as a side effect
+ if (getFeatures() & GF_AGIMOUSE || 1)
report("Using AGI Mouse 1.0 protocol\n");
if (getFeatures() & GF_AGIPAL)
@@ -386,28 +390,33 @@ int AgiEngine::runGame() {
_restartGame = false;
}
- // Set computer type (v20 i.e. vComputer)
+ // Set computer type (v20 i.e. vComputer) and sound type
switch (getPlatform()) {
case Common::kPlatformAtariST:
setvar(vComputer, kAgiComputerAtariST);
+ setvar(vSoundgen, kAgiSoundPC);
break;
case Common::kPlatformAmiga:
if (getFeatures() & GF_OLDAMIGAV20)
setvar(vComputer, kAgiComputerAmigaOld);
else
setvar(vComputer, kAgiComputerAmiga);
+ setvar(vSoundgen, kAgiSoundTandy);
break;
case Common::kPlatformApple2GS:
setvar(vComputer, kAgiComputerApple2GS);
+ if (getFeatures() & GF_2GSOLDSOUND)
+ setvar(vSoundgen, kAgiSound2GSOld);
+ else
+ setvar(vSoundgen, kAgiSoundTandy);
break;
case Common::kPlatformPC:
default:
setvar(vComputer, kAgiComputerPC);
+ setvar(vSoundgen, kAgiSoundPC);
break;
}
- setvar(vSoundgen, 1); // IBM PC SOUND
-
// Set monitor type (v26 i.e. vMonitor)
switch (_renderMode) {
case Common::kRenderCGA:
@@ -430,7 +439,7 @@ int AgiEngine::runGame() {
setvar(vFreePages, 180); // Set amount of free memory to realistic value
setvar(vMaxInputChars, 38);
_game.inputMode = INPUT_NONE;
- _game.inputEnabled = 0;
+ _game.inputEnabled = false;
_game.hasPrompt = 0;
_game.state = STATE_RUNNING;
diff --git a/engines/agi/detection.cpp b/engines/agi/detection.cpp
index e72647d5e2..d1bed5d716 100644
--- a/engines/agi/detection.cpp
+++ b/engines/agi/detection.cpp
@@ -125,841 +125,7 @@ static const PlainGameDescriptor agiGames[] = {
{0, 0}
};
-
-namespace Agi {
-
-using Common::GUIO_NONE;
-
-#define GAME_LVFPN(id,name,fname,md5,size,lang,ver,features,gid,platform,interp) { \
- { \
- id, \
- name, \
- AD_ENTRY1s(fname,md5,size), \
- lang, \
- platform, \
- ADGF_NO_FLAGS, \
- GUIO_NONE \
- }, \
- gid, \
- interp, \
- features, \
- ver, \
- }
-
-#define GAME_LVFPNF(id,name,fname,md5,size,lang,ver,features,gid,platform,interp) { \
- { \
- id, \
- name, \
- AD_ENTRY1s(fname,md5,size), \
- lang, \
- platform, \
- ADGF_USEEXTRAASTITLE, \
- GUIO_NONE \
- }, \
- gid, \
- interp, \
- features, \
- ver, \
- }
-
-#define GAME(id,name,md5,ver,gid) GAME_LVFPN(id,name,"logdir",md5,-1,Common::EN_ANY,ver,0,gid,Common::kPlatformPC,GType_V2)
-#define GAME3(id,name,fname,md5,ver,gid) GAME_LVFPN(id,name,fname,md5,-1,Common::EN_ANY,ver,0,gid,Common::kPlatformPC,GType_V3)
-
-#define GAME_P(id,name,md5,ver,gid,platform) GAME_LVFPN(id,name,"logdir",md5,-1,Common::EN_ANY,ver,0,gid,platform,GType_V2)
-
-#define GAME_FP(id,name,md5,ver,flags,gid,platform) GAME_LVFPN(id,name,"logdir",md5,-1,Common::EN_ANY,ver,flags,gid,platform,GType_V2)
-
-#define GAME_PS(id,name,md5,size,ver,gid,platform) GAME_LVFPN(id,name,"logdir",md5,size,Common::EN_ANY,ver,0,gid,platform,GType_V2)
-
-#define GAME_LPS(id,name,md5,size,lang,ver,gid,platform) GAME_LVFPN(id,name,"logdir",md5,size,lang,ver,0,gid,platform,GType_V2)
-
-#define GAME_LFPS(id,name,md5,size,lang,ver,flags,gid,platform) GAME_LVFPN(id,name,"logdir",md5,size,lang,ver,flags,gid,platform,GType_V2)
-
-#define GAME3_P(id,name,fname,md5,ver,flags,gid,platform) GAME_LVFPN(id,name,fname,md5,-1,Common::EN_ANY,ver,flags,gid,platform,GType_V3)
-
-#define GAMEpre_P(id,name,fname,md5,ver,gid,platform) GAME_LVFPN(id,name,fname,md5,-1,Common::EN_ANY,ver,0,gid,platform,GType_PreAGI)
-
-#define GAMEpre_PS(id,name,fname,md5,size,ver,gid,platform) GAME_LVFPN(id,name,fname,md5,size,Common::EN_ANY,ver,0,gid,platform,GType_PreAGI)
-
-#define GAME3_PS(id,name,fname,md5,size,ver,flags,gid,platform) GAME_LVFPN(id,name,fname,md5,size,Common::EN_ANY,ver,flags,gid,platform,GType_V3)
-
-#define FANMADE_ILVF(id,name,md5,lang,ver,features) GAME_LVFPNF(id,name,"logdir",md5,-1,lang,ver,(GF_FANMADE|features),GID_FANMADE,Common::kPlatformPC,GType_V2)
-
-#define FANMADE_ISVP(id,name,md5,size,ver,platform) GAME_LVFPNF(id,name,"logdir",md5,size,Common::EN_ANY,ver,GF_FANMADE,GID_FANMADE,platform,GType_V2)
-#define FANMADE_SVP(name,md5,size,ver,platform) FANMADE_ISVP("agi-fanmade",name,md5,size,ver,platform)
-
-#define FANMADE_LVF(name,md5,lang,ver,features) FANMADE_ILVF("agi-fanmade",name,md5,lang,ver,features)
-
-#define FANMADE_LF(name,md5,lang,features) FANMADE_LVF(name,md5,lang,0x2917,features)
-#define FANMADE_IF(id,name,md5,features) FANMADE_ILVF(id,name,md5,Common::EN_ANY,0x2917,features)
-
-#define FANMADE_V(name,md5,ver) FANMADE_LVF(name,md5,Common::EN_ANY,ver,0)
-#define FANMADE_F(name,md5,features) FANMADE_LF(name,md5,Common::EN_ANY,features)
-#define FANMADE_L(name,md5,lang) FANMADE_LF(name,md5,lang,0)
-#define FANMADE_I(id,name,md5) FANMADE_IF(id,name,md5,0)
-
-#define FANMADE(name,md5) FANMADE_F(name,md5,0)
-
-static const AGIGameDescription gameDescriptions[] = {
-
- // AGI Demo 1 (PC) 05/87 [AGI 2.425]
- GAME("agidemo", "Demo 1 1987-05-20", "9c4a5b09cc3564bc48b4766e679ea332", 0x2440, GID_AGIDEMO),
-
- // AGI Demo 2 (IIgs) 1.0C (Censored)
- GAME_P("agidemo", "Demo 2 1987-11-24 1.0C", "580ffdc569ff158f56fb92761604f70e", 0x2917, GID_AGIDEMO, Common::kPlatformApple2GS),
-
- // AGI Demo 2 (PC 3.5") 11/87 [AGI 2.915]
- GAME("agidemo", "Demo 2 1987-11-24 3.5\"", "e8ebeb0bbe978172fe166f91f51598c7", 0x2917, GID_AGIDEMO),
-
- // AGI Demo 2 (PC 5.25") 11/87 [v1] [AGI 2.915]
- GAME("agidemo", "Demo 2 1987-11-24 [version 1] 5.25\"", "852ac303a374df62571642ca1e2d1f0a", 0x2917, GID_AGIDEMO),
-
- // AGI Demo 2 (PC 5.25") 01/88 [v2] [AGI 2.917]
- GAME("agidemo", "Demo 2 1987-11-25 [version 2] 5.25\"", "1503f02086ea9f388e7e041c039eaa69", 0x2917, GID_AGIDEMO),
-
- // AGI Demo 3 (PC) 09/88 [AGI 3.002.102]
- GAME3("agidemo", "Demo 3 1988-09-13", "dmdir", "289c7a2c881f1d973661e961ced77d74", 0x3149, GID_AGIDEMO),
-
- // AGI Demo for Kings Quest III and Space Quest I
- GAME("agidemo", "Demo Kings Quest III and Space Quest I", "502e6bf96827b6c4d3e67c9cdccd1033", 0x2272, GID_AGIDEMO),
-
- // Black Cauldron (Amiga) 2.00 6/14/87
- GAME_P("bc", "2.00 1987-06-14", "7b01694af21213b4727bb94476f64eb5", 0x2440, GID_BC, Common::kPlatformAmiga),
-
- // Black Cauldron (Apple IIgs) 1.0O 2/24/89 (CE)
- // Menus not tested
- GAME3_P("bc", "1.0O 1989-02-24 (CE)", "bcdir", "dc09d30b147242692f4f85b9811962db", 0x3149, 0, GID_BC, Common::kPlatformApple2GS),
-
- // Black Cauldron (PC) 2.00 6/14/87 [AGI 2.439]
- GAME("bc", "2.00 1987-06-14", "7f598d4712319b09d7bd5b3be10a2e4a", 0x2440, GID_BC),
-
- // Black Cauldron (Russian)
- GAME_LPS("bc", "", "b7de782dfdf8ea7dde8064f09804bcf5", 357, Common::RU_RUS, 0x2440, GID_BC, Common::kPlatformPC),
-
- // Black Cauldron (PC 5.25") 2.10 11/10/88 [AGI 3.002.098]
- GAME3("bc", "2.10 1988-11-10 5.25\"", "bcdir", "0c5a9acbcc7e51127c34818e75806df6", 0x3149, GID_BC),
-
- // Black Cauldron (PC) 2.10 [AGI 3.002.097]
- GAME3("bc", "2.10", "bcdir", "0de3953c9225009dc91e5b0d1692967b", 0x3149, GID_BC),
-
- // Black Cauldron (CoCo3 360k) [AGI 2.023]
- GAME_PS("bc", "", "51212c54808ade96176f201ae0ac7a6f", 357, 0x2440, GID_BC, Common::kPlatformCoCo3),
-
- // Black Cauldron (CoCo3 360k) [AGI 2.072]
- GAME_PS("bc", "updated", "c4e1937f74e8100cd0152b904434d8b4", 357, 0x2440, GID_BC, Common::kPlatformCoCo3),
-
-// TODO
-// These aren't supposed to work now as they require unsupported agi engine 2.01
-#if 0
- // Donald Duck's Playground (Amiga) 1.0C
- // Menus not tested
- GAME_P("ddp", "1.0C 1987-04-27", "550971d196f65190a5c760d2479406ef", 0x2272, GID_DDP, Common::kPlatformAmiga),
-
- // Donald Duck's Playground (ST) 1.0A 8/8/86
- // Menus not tested
- GAME("ddp", "1.0A 1986-08-08", "64388812e25dbd75f7af1103bc348596", 0x2272, GID_DDP),
-
- // reported by Filippos (thebluegr) in bugreport #1654500
- // Menus not tested
- GAME_PS("ddp", "1.0C 1986-06-09", "550971d196f65190a5c760d2479406ef", 132, 0x2272, GID_DDP, Common::kPlatformPC),
-#endif
-
- // Gold Rush! (Amiga) 1.01 1/13/89 aka 2.05 3/9/89 # 2.316
- GAME3_PS("goldrush", "1.01 1989-01-13 aka 2.05 1989-03-09", "dirs", "a1d4de3e75c2688c1e2ca2634ffc3bd8", 2399, 0x3149, 0, GID_GOLDRUSH, Common::kPlatformAmiga),
-
- // Gold Rush! (Apple IIgs) 1.0M 2/28/89 (CE) aka 2.01 12/22/88
- // Menus not tested
- GAME3_P("goldrush", "1.0M 1989-02-28 (CE) aka 2.01 1988-12-22", "grdir", "3f7b9ce62631434389f85371b11921d6", 0x3149, 0, GID_GOLDRUSH, Common::kPlatformApple2GS),
-
- // Gold Rush! (ST) 1.01 1/13/89 aka 2.01 12/22/88
- GAME3_P("goldrush", "1.01 1989-01-13 aka 2.01 1988-12-22", "grdir", "4dd4d50480a3d6c206fa227ce8142735", 0x3149, 0, GID_GOLDRUSH, Common::kPlatformAtariST),
-
- // Gold Rush! (PC 5.25") 2.01 12/22/88 [AGI 3.002.149]
- GAME3("goldrush", "2.01 1988-12-22 5.25\"", "grdir", "db733d199238d4009a9e95f11ece34e9", 0x3149, GID_GOLDRUSH),
-
- // Gold Rush! (PC 3.5") 2.01 12/22/88 [AGI 3.002.149]
- GAME3("goldrush", "2.01 1988-12-22 3.5\"", "grdir", "6a285235745f69b4b421403659497216", 0x3149, GID_GOLDRUSH),
-
- // Gold Rush! (PC 3.5", bought from The Software Farm) 3.0 1998-12-22 [AGI 3.002.149]
- GAME3("goldrush", "3.0 1998-12-22 3.5\"", "grdir", "6882b6090473209da4cd78bb59f78dbe", 0x3149, GID_GOLDRUSH),
-
- {
- // Gold Rush! (PC 5.25") 2.01 12/22/88 [AGI 3.002.149]
- {
- "goldrush",
- "2.01 1988-12-22",
- {
- { "grdir", 0, "db733d199238d4009a9e95f11ece34e9", 2399},
- { "vol.0", 0, "4b6423d143674d3757ab1b875d25951d", 25070},
- { NULL, 0, NULL, 0}
- },
- Common::EN_ANY,
- Common::kPlatformMacintosh,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GID_GOLDRUSH,
- GType_V3,
- GF_MACGOLDRUSH,
- 0x3149,
- },
-
-
- // Gold Rush! (CoCo3 720k) [AGI 2.023]
- GAME_PS("goldrush", "", "0a41b65efc0cd6c4271e957e6ffbbd8e", 744, 0x2440, GID_GOLDRUSH, Common::kPlatformCoCo3),
-
- // Gold Rush! (CoCo3 360k/720k) [AGI 2.072]
- GAME_PS("goldrush", "updated", "c49bf56bf91e31a4601a604e51ef8bfb", 744, 0x2440, GID_GOLDRUSH, Common::kPlatformCoCo3),
-
- // King's Quest 1 (Amiga) 1.0U # 2.082
- // The original game did not have menus, they are enabled under ScummVM
- GAME_FP("kq1", "1.0U 1986", "246c695324f1c514aee2b904fa352fad", 0x2440, GF_MENUS, GID_KQ1, Common::kPlatformAmiga),
-
- // King's Quest 1 (ST) 1.0V
- // The original game did not have menus, they are enabled under ScummVM
- GAME_FP("kq1", "1.0V 1986", "c3a017e556c4b0eece366a4cd9abb657", 0x2272, GF_MENUS, GID_KQ1, Common::kPlatformAtariST),
-
- // King's Quest 1 (IIgs) 1.0S-88223
- // Menus not tested
- GAME_P("kq1", "1.0S 1988-02-23", "f4277aa34b43d37382bc424c81627617", 0x2272, GID_KQ1, Common::kPlatformApple2GS),
-
- // King's Quest 1 (Mac) 2.0C
- GAME_P("kq1", "2.0C 1987-03-26", "d4c4739d4ac63f7dbd29255425077d48", 0x2440, GID_KQ1, Common::kPlatformMacintosh),
-
- // King's Quest 1 (PC 5.25"/3.5") 2.0F [AGI 2.917]
- GAME("kq1", "2.0F 1987-05-05 5.25\"/3.5\"", "10ad66e2ecbd66951534a50aedcd0128", 0x2917, GID_KQ1),
-
- // King's Quest 1 (CoCo3 360k) [AGI 2.023]
- GAME_PS("kq1", "", "10ad66e2ecbd66951534a50aedcd0128", 315, 0x2440, GID_KQ1, Common::kPlatformCoCo3),
-
- // King's Quest 1 (CoCo3 360k) [AGI 2.023]
- GAME_PS("kq1", "fixed", "4c8ef8b5d2f1b6c1a93e456d1f1ffc74", 768, 0x2440, GID_KQ1, Common::kPlatformCoCo3),
-
- // King's Quest 1 (CoCo3 360k) [AGI 2.072]
- GAME_PS("kq1", "updated", "94087178c78933a4af3cd24d1c8dd7b2", 315, 0x2440, GID_KQ1, Common::kPlatformCoCo3),
-
- // King's Quest 2 (IIgs) 2.0A 6/16/88 (CE)
- GAME_P("kq2", "2.0A 1988-06-16 (CE)", "5203c8b95250a2ecfee93ddb99414753", 0x2917, GID_KQ2, Common::kPlatformApple2GS),
-
- // King's Quest 2 (Amiga) 2.0J (Broken)
- GAME_P("kq2", "2.0J 1987-01-29 [OBJECT decrypted]", "b866f0fab2fad91433a637a828cfa410", 0x2440, GID_KQ2, Common::kPlatformAmiga),
-
- // King's Quest 2 (Mac) 2.0R
- GAME_P("kq2", "2.0R 1988-03-23", "cbdb0083317c8e7cfb7ac35da4bc7fdc", 0x2440, GID_KQ2, Common::kPlatformMacintosh),
-
- // King's Quest 2 (PC) 2.1 [AGI 2.411]; entry from DAGII, but missing from Sarien?
- // XXX: any major differences from 2.411 to 2.440?
- GAME("kq2", "2.1 1987-04-10", "759e39f891a0e1d86dd29d7de485c6ac", 0x2440, GID_KQ2),
-
- // King's Quest 2 (PC 5.25"/3.5") 2.2 [AGI 2.426]
- GAME("kq2", "2.2 1987-05-07 5.25\"/3.5\"", "b944c4ff18fb8867362dc21cc688a283", 0x2917, GID_KQ2),
-
- // King's Quest 2 (Russian)
- GAME_LPS("kq2", "", "35211c574ececebdc723b23e35f99275", 543, Common::RU_RUS, 0x2917, GID_KQ2, Common::kPlatformPC),
-
- // King's Quest 2 (CoCo3 360k) [AGI 2.023]
- GAME_PS("kq2", "", "b944c4ff18fb8867362dc21cc688a283", 543, 0x2440, GID_KQ2, Common::kPlatformCoCo3),
-
- // King's Quest 2 (CoCo3 360k) [AGI 2.072]
- GAME_PS("kq2", "updated", "f64a606de740a5348f3d125c03e989fe", 543, 0x2440, GID_KQ2, Common::kPlatformCoCo3),
-
- // King's Quest 2 (CoCo3 360k) [AGI 2.023]
- GAME_PS("kq2", "fixed", "fb33ac2768a94a89117a270771db465c", 768, 0x2440, GID_KQ2, Common::kPlatformCoCo3),
-
- // King's Quest 3 (Amiga) 1.01 11/8/86
- // The original game did not have menus, they are enabled under ScummVM
- GAME_FP("kq3", "1.01 1986-11-08", "8ab343306df0e2d98f136be4e8cfd0ef", 0x2440, GF_MENUS, GID_KQ3, Common::kPlatformAmiga),
-
- // King's Quest 3 (ST) 1.02 11/18/86
- // Does not have menus, crashes if menus are enforced. Therefore, ESC pauses the game
- GAME_FP("kq3", "1.02 1986-11-18", "8846df2654302b623217ba8bd6d657a9", 0x2272, GF_ESCPAUSE, GID_KQ3, Common::kPlatformAtariST),
-
- // King's Quest 3 (Mac) 2.14 3/15/88
- GAME_P("kq3", "2.14 1988-03-15", "7639c0da5ce94848227d409351fabda2", 0x2440, GID_KQ3, Common::kPlatformMacintosh),
-
- // King's Quest 3 (IIgs) 2.0A 8/28/88 (CE)
- GAME_P("kq3", "2.0A 1988-08-28 (CE)", "ac30b7ca5a089b5e642fbcdcbe872c12", 0x2917, GID_KQ3, Common::kPlatformApple2GS),
-
- // King's Quest 3 (Amiga) 2.15 11/15/89 # 2.333
- // Original pauses with ESC, has menus accessible with mouse.
- // ver = 0x3086 -> menus accessible with ESC or mouse, bug #2835581 (KQ3: Game Crash When Leaving Tavern as Fly).
- // ver = 0x3149 -> menus accessible with mouse, ESC pauses game, bug #2835581 disappears.
- GAME3_PS("kq3", "2.15 1989-11-15", "dirs", "8e35bded2bc5cf20f5eec2b15523b155", 1805, 0x3149, 0, GID_KQ3, Common::kPlatformAmiga),
-
- // King's Quest 3 (PC) 1.01 11/08/86 [AGI 2.272]
- // Does not have menus, crashes if menus are enforced. Therefore, ESC pauses the game
- GAME_FP("kq3", "1.01 1986-11-08", "9c2b34e7ffaa89c8e2ecfeb3695d444b", 0x2272, GF_ESCPAUSE, GID_KQ3, Common::kPlatformPC),
-
- // King's Quest 3 (Russian)
- GAME_LFPS("kq3", "", "5856dec6ccb9c4b70aee21044a19270a", 390, Common::RU_RUS, 0x2272, GF_ESCPAUSE, GID_KQ3, Common::kPlatformPC),
-
- // King's Quest 3 (PC 5.25") 2.00 5/25/87 [AGI 2.435]
- GAME("kq3", "2.00 1987-05-25 5.25\"", "18aad8f7acaaff760720c5c6885b6bab", 0x2440, GID_KQ3),
-
- // King's Quest 3 (Mac) 2.14 3/15/88
- // Menus not tested
- GAME_P("kq3", "2.14 1988-03-15 5.25\"", "7650e659c7bc0f1e9f8a410b7a2e9de6", 0x2440, GID_KQ3, Common::kPlatformMacintosh),
-
- // King's Quest 3 (PC 3.5") 2.14 3/15/88 [AGI 2.936]
- GAME("kq3", "2.14 1988-03-15 3.5\"", "d3d17b77b3b3cd13246749231d9473cd", 0x2936, GID_KQ3),
-
- // King's Quest 3 (CoCo3 158k/360k) [AGI 2.023]
- GAME_PS("kq3", "", "5a6be7d16b1c742c369ef5cc64fefdd2", 429, 0x2440, GID_KQ3, Common::kPlatformCoCo3),
-
- // King's Quest 4 (PC 5.25") 2.0 7/27/88 [AGI 3.002.086]
- GAME3("kq4", "2.0 1988-07-27", "kq4dir", "f50f7f997208ca0e35b2650baec43a2d", 0x3086, GID_KQ4),
-
- // King's Quest 4 (PC 3.5") 2.0 7/27/88 [AGI 3.002.086]
- GAME3("kq4", "2.0 1988-07-27 3.5\"", "kq4dir", "fe44655c42f16c6f81046fdf169b6337", 0x3086, GID_KQ4),
-
- // King's Quest 4 (PC 3.5") 2.2 9/27/88 [AGI 3.002.086]
- // Menus not tested
- GAME3("kq4", "2.2 1988-09-27 3.5\"", "kq4dir", "7470b3aeb49d867541fc66cc8454fb7d", 0x3086, GID_KQ4),
-
- // King's Quest 4 (PC 5.25") 2.3 9/27/88 [AGI 3.002.086]
- GAME3("kq4", "2.3 1988-09-27", "kq4dir", "6d7714b8b61466a5f5981242b993498f", 0x3086, GID_KQ4),
-
- // King's Quest 4 (PC 3.5") 2.3 9/27/88 [AGI 3.002.086]
- GAME3("kq4", "2.3 1988-09-27 3.5\"", "kq4dir", "82a0d39af891042e99ac1bd6e0b29046", 0x3086, GID_KQ4),
-
- // King's Quest 4 (IIgs) 1.0K 11/22/88 (CE)
- // Menus not tested
- GAME3_P("kq4", "1.0K 1988-11-22", "kq4dir", "8536859331159f15012e35dc82cb154e", 0x3086, 0, GID_KQ4, Common::kPlatformApple2GS),
-
- // King's Quest 4 demo (PC) [AGI 3.002.102]
- // Menus not tested
- GAME3("kq4", "Demo 1988-12-20", "dmdir", "a3332d70170a878469d870b14863d0bf", 0x3149, GID_KQ4),
-
- // King's Quest 4 (CoCo3 720k) [AGI 2.023]
- GAME_PS("kq4", "", "9e7729a28e749ca241d2bf71b9b2dbde", 741, 0x2440, GID_KQ4, Common::kPlatformCoCo3),
-
- // King's Quest 4 (CoCo3 360k/720k) [AGI 2.072]
- GAME_PS("kq4", "updated", "1959ca10739edb34069bb504dbd74805", 741, 0x2440, GID_KQ4, Common::kPlatformCoCo3),
-
- // Leisure Suit Larry 1 (PC 5.25"/3.5") 1.00 6/1/87 [AGI 2.440]
- GAME("lsl1", "1.00 1987-06-01 5.25\"/3.5\"", "1fe764e66857e7f305a5f03ca3f4971d", 0x2440, GID_LSL1),
-
- // Leisure Suit Larry 1 Polish
- GAME_LPS("lsl1", "2.00 2001-12-11", "7ba1fccc46d27c141e704706c1d0a85f", 303, Common::PL_POL, 0x2440, GID_LSL1, Common::kPlatformPC),
-
- // Leisure Suit Larry 1 Polish - Demo
- GAME_LPS("lsl1", "Demo", "3b2f564306c401dff6334441df967ddd", 666, Common::PL_POL, 0x2917, GID_LSL1, Common::kPlatformPC),
-
- // Leisure Suit Larry 1 (ST) 1.04 6/18/87
- GAME_P("lsl1", "1.04 1987-06-18", "8b579f8673fe9448c2538f5ed9887cf0", 0x2440, GID_LSL1, Common::kPlatformAtariST),
-
- // Leisure Suit Larry 1 (Amiga) 1.05 6/26/87 # x.yyy
- GAME_P("lsl1", "1.05 1987-06-26", "3f5d26d8834ca49c147fb60936869d56", 0x2440, GID_LSL1, Common::kPlatformAmiga),
-
- // Leisure Suit Larry 1 (IIgs) 1.0E
- GAME_P("lsl1", "1.0E 1987", "5f9e1dd68d626c6d303131c119582ad4", 0x2440, GID_LSL1, Common::kPlatformApple2GS),
-
- // Leisure Suit Larry 1 (Mac) 1.05 6/26/87
- GAME_P("lsl1", "1.05 1987-06-26", "8a0076429890531832f0dc113285e31e", 0x2440, GID_LSL1, Common::kPlatformMacintosh),
-
- // Leisure Suit Larry 1 (CoCo3 158k/360k) [AGI 2.072]
- GAME_PS("lsl1", "", "a2de1fe76565c3e8b40c9d036b5e5612", 198, 0x2440, GID_LSL1, Common::kPlatformCoCo3),
-
- // Manhunter NY (ST) 1.03 10/20/88
- GAME3_P("mh1", "1.03 1988-10-20", "mhdir", "f2d58056ad802452d60776ee920a52a6", 0x3149, 0, GID_MH1, Common::kPlatformAtariST),
-
- // Manhunter NY (IIgs) 2.0E 10/05/88 (CE)
- GAME3_P("mh1", "2.0E 1988-10-05 (CE)", "mhdir", "2f1509f76f24e6e7d213f2dadebbf156", 0x3149, 0, GID_MH1, Common::kPlatformApple2GS),
-
- // Manhunter NY (Amiga) 1.06 3/18/89
- GAME3_P("mh1", "1.06 1989-03-18", "dirs", "92c6183042d1c2bb76236236a7d7a847", 0x3149, GF_OLDAMIGAV20, GID_MH1, Common::kPlatformAmiga),
-
- // reported by Filippos (thebluegr) in bugreport #1654500
- // Manhunter NY (PC 5.25") 1.22 8/31/88 [AGI 3.002.107]
- GAME3_PS("mh1", "1.22 1988-08-31", "mhdir", "0c7b86f05fe02c2e26cff1b07450b82a", 2123, 0x3149, 0, GID_MH1, Common::kPlatformPC),
-
- // Manhunter NY (PC 3.5") 1.22 8/31/88 [AGI 3.002.102]
- GAME3_PS("mh1", "1.22 1988-08-31", "mhdir", "5b625329021ad49fd0c1d6f2d6f54bba", 2141, 0x3149, 0, GID_MH1, Common::kPlatformPC),
-
- // Manhunter NY (CoCo3 720k) [AGI 2.023]
- GAME_PS("mh1", "", "b968285caf2f591c78dd9c9e26ab8974", 495, 0x2440, GID_MH1, Common::kPlatformCoCo3),
-
- // Manhunter NY (CoCo3 360k/720k) [AGI 2.072]
- GAME_PS("mh1", "updated", "d47da950c62289f8d4ccf36af73365f2", 495, 0x2440, GID_MH1, Common::kPlatformCoCo3),
-
- // Manhunter SF (ST) 1.0 7/29/89
- GAME3_P("mh2", "1.0 1989-07-29", "mh2dir", "5e3581495708b952fea24438a6c7e040", 0x3149, 0, GID_MH1, Common::kPlatformAtariST),
-
- // Manhunter SF (Amiga) 3.06 8/17/89 # 2.333
- GAME3_PS("mh2", "3.06 1989-08-17", "dirs", "b412e8a126368b76696696f7632d4c16", 2573, 0x3086, GF_OLDAMIGAV20, GID_MH2, Common::kPlatformAmiga),
-
- // Manhunter SF (PC 5.25") 3.03 8/17/89 [AGI 3.002.149]
- GAME3("mh2", "3.03 1989-08-17 5.25\"", "mh2dir", "b90e4795413c43de469a715fb3c1fa93", 0x3149, GID_MH2),
-
- // Manhunter SF (PC 3.5") 3.02 7/26/89 [AGI 3.002.149]
- GAME3("mh2", "3.02 1989-07-26 3.5\"", "mh2dir", "6fb6f0ee2437704c409cf17e081ba152", 0x3149, GID_MH2),
-
- // Manhunter SF (CoCo3 720k) [AGI 2.023]
- GAME_PS("mh2", "", "acaaa577e10d1753c5a74f6ae1d858d4", 591, 0x2440, GID_MH2, Common::kPlatformCoCo3),
-
- // Manhunter SF (CoCo3 720k) [AGI 2.072]
- GAME_PS("mh2", "updated", "c64875766700196e72a92359f70f45a9", 591, 0x2440, GID_MH2, Common::kPlatformCoCo3),
-
- // Mickey's Space Adventure
- // Preagi game
- GAMEpre_P("mickey", "", "1.pic", "b6ec04c91a05df374792872c4d4ce66d", 0x0000, GID_MICKEY, Common::kPlatformPC),
-
-#if 0
- // Mixed-Up Mother Goose (Amiga) 1.1
- // Problematic: crashes
- // Menus not tested
- GAME3_PS("mixedup", "1.1 1986-12-10", "dirs", "5c1295fe6daaf95831195ba12894dbd9", 2021, 0x3086, 0, GID_MIXEDUP, Common::kPlatformAmiga),
-#endif
-
- // Mixed Up Mother Goose (IIgs)
- GAME_P("mixedup", "1987", "3541954a7303467c6df87665312ffb6a", 0x2917, GID_MIXEDUP, Common::kPlatformApple2GS),
-
- // Mixed-Up Mother Goose (PC) [AGI 2.915]
- GAME("mixedup", "1987-11-10", "e524655abf9b96a3b179ffcd1d0f79af", 0x2917, GID_MIXEDUP),
-
- // Mixed-Up Mother Goose (CoCo3 360k) [AGI 2.072]
- GAME_PS("mixedup", "", "44e63e9b4d4822a31edea0e8a7e7eac4", 606, 0x2440, GID_MIXEDUP, Common::kPlatformCoCo3),
-
- // Police Quest 1 (PC) 2.0E 11/17/87 [AGI 2.915]
- GAME("pq1", "2.0E 1987-11-17", "2fd992a92df6ab0461d5a2cd83c72139", 0x2917, GID_PQ1),
-
- // Police Quest 1 (Mac) 2.0G 12/3/87
- GAME_P("pq1", "2.0G 1987-12-03", "805750b66c1c5b88a214e67bfdca17a1", 0x2440, GID_PQ1, Common::kPlatformMacintosh),
-
- // Police Quest 1 (IIgs) 2.0B-88421
- GAME_P("pq1", "2.0B 1988-04-21", "e7c175918372336461e3811d594f482f", 0x2917, GID_PQ1, Common::kPlatformApple2GS),
-
- // Police Quest 1 (Amiga) 2.0B 2/22/89 # 2.310
- GAME3_PS("pq1", "2.0B 1989-02-22", "dirs", "cfa93e5f2aa7378bddd10ad6746a2ffb", 1613, 0x3149, 0, GID_PQ1, Common::kPlatformAmiga),
-
- // Police Quest 1 (IIgs) 2.0A-88318
- GAME_P("pq1", "2.0A 1988-03-18", "8994e39d0901de3d07cecfb954075bb5", 0x2917, GID_PQ1, Common::kPlatformApple2GS),
-
- // Police Quest 1 (PC) 2.0A 10/23/87 [AGI 2.903/2.911]
- GAME("pq1", "2.0A 1987-10-23", "b9dbb305092851da5e34d6a9f00240b1", 0x2917, GID_PQ1),
-
- // Police Quest 1 (Russian)
- GAME_LPS("pq1", "", "604cc8041d24c4c7e5fa8baf386ef76e", 360, Common::RU_RUS, 0x2917, GID_PQ1, Common::kPlatformPC),
-
- // Police Quest 1 2.0G 12/3/87
- GAME("pq1", "2.0G 1987-12-03 5.25\"/ST", "231f3e28170d6e982fc0ced4c98c5c1c", 0x2440, GID_PQ1),
-
- // Police Quest 1 (PC) 2.0G 12/3/87; entry from DAGII, but missing from Sarien?
- // not sure about disk format -- dsymonds
- GAME("pq1", "2.0G 1987-12-03", "d194e5d88363095f55d5096b8e32fbbb", 0x2917, GID_PQ1),
-
- // Police Quest 1 (CoCo3 360k) [AGI 2.023]
- GAME_PS("pq1", "", "28a077041f75aab78f66804800940085", 375, 0x2440, GID_PQ1, Common::kPlatformCoCo3),
-
- // Police Quest 1 (CoCo3 360k) [AGI 2.072]
- GAME_PS("pq1", "updated", "63b9a9c6eec154751dd446cd3693e0e2", 768, 0x2440, GID_PQ1, Common::kPlatformCoCo3),
-
- // Space Quest 1 (ST) 1.1A
- // The original game did not have menus, they are enabled under ScummVM
- GAME_FP("sq1", "1.1A 1986-02-06", "6421fb64b0e6604c9dd065975d9279e9", 0x2440, GF_MENUS, GID_SQ1, Common::kPlatformAtariST),
-
- // Space Quest 1 (PC 360k) 1.1A [AGI 2.272]
- // The original game did not have menus, they are enabled under ScummVM
- GAME_FP("sq1", "1.1A 1986-11-13", "8d8c20ab9f4b6e4817698637174a1cb6", 0x2272, GF_MENUS, GID_SQ1, Common::kPlatformPC),
-
- // Space Quest 1 (PC 720k) 1.1A [AGI 2.272]
- // The original game did not have menus, they are enabled under ScummVM
- GAME_FP("sq1", "1.1A 720kb", "0a92b1be7daf3bb98caad3f849868aeb", 0x2272, GF_MENUS, GID_SQ1, Common::kPlatformPC),
-
- // Space Quest 1 (Amiga) 1.2 # 2.082
- // The original game did not have menus, they are enabled under ScummVM
- GAME_FP("sq1", "1.2 1986", "0b216d931e95750f1f4837d6a4b821e5", 0x2440, GF_MENUS | GF_OLDAMIGAV20, GID_SQ1, Common::kPlatformAmiga),
-
- // Space Quest 1 (Mac) 1.5D
- GAME_P("sq1", "1.5D 1987-04-02", "ce88419aadd073d1c6682d859b3d8aa2", 0x2440, GID_SQ1, Common::kPlatformMacintosh),
-
- // Space Quest 1 (IIgs) 2.2
- GAME_P("sq1", "2.2 1987", "64b9b3d04c1066d36e6a6e56187a83f7", 0x2917, GID_SQ1, Common::kPlatformApple2GS),
-
- // Space Quest 1 (PC) 1.0X [AGI 2.089]
- // Does not have menus, crashes if menus are enforced. Therefore, ESC pauses the game
- GAME_FP("sq1", "1.0X 1986-09-24", "af93941b6c51460790a9efa0e8cb7122", 0x2089, GF_ESCPAUSE, GID_SQ1, Common::kPlatformPC),
-
- // Space Quest 1 (Russian)
- GAME_LFPS("sq1", "", "a279eb8ddbdefdb1ea6adc827a1d632a", 372, Common::RU_RUS, 0x2089, GF_ESCPAUSE, GID_SQ1, Common::kPlatformPC),
-
- // Space Quest 1 (PC 5.25"/3.5") 2.2 [AGI 2.426/2.917]
- GAME("sq1", "2.2 1987-05-07 5.25\"/3.5\"", "5d67630aba008ec5f7f9a6d0a00582f4", 0x2440, GID_SQ1),
-
- // Space Quest 1 (CoCo3 360k) [AGI 2.072]
- GAME_PS("sq1", "", "5d67630aba008ec5f7f9a6d0a00582f4", 372, 0x2440, GID_SQ1, Common::kPlatformCoCo3),
-
- // Space Quest 1 (CoCo3 360k) [AGI 2.023]
- GAME_PS("sq1", "fixed", "ca822b768b6462e410423ea7f498daee", 768, 0x2440, GID_SQ1, Common::kPlatformCoCo3),
-
- // Space Quest 1 (CoCo3 360k) [AGI 2.072]
- GAME_PS("sq1", "updated", "7fa54e6bb7ffeb4cf20eca39d86f5fb2", 387, 0x2440, GID_SQ1, Common::kPlatformCoCo3),
-
- // Space Quest 2 (PC 3.5") 2.0D [AGI 2.936]
- GAME("sq2", "2.0D 1988-03-14 3.5\"", "85390bde8958c39830e1adbe9fff87f3", 0x2936, GID_SQ2),
-
- // Space Quest 2 (IIgs) 2.0A 7/25/88 (CE)
- GAME_P("sq2", "2.0A 1988-07-25 (CE)", "5dfdac98dd3c01fcfb166529f917e911", 0x2936, GID_SQ2, Common::kPlatformApple2GS),
-
- {
- // Space Quest 2 (Amiga) 2.0F
- {
- "sq2",
- "2.0F 1986-12-09 [VOL.2->PICTURE.16 broken]",
- {
- { "logdir", 0, "28add5125484302d213911df60d2aded", 426},
- { "object", 0, "5dc52be721257719f4b311a84ce22b16", 372},
- { NULL, 0, NULL, 0}
- },
- Common::EN_ANY,
- Common::kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GID_SQ2,
- GType_V2,
- 0,
- 0x2936,
- },
-
-
- // Space Quest 2 (Mac) 2.0D
- GAME_P("sq2", "2.0D 1988-04-04", "bfbebe0b59d83f931f2e1c62ce9484a7", 0x2936, GID_SQ2, Common::kPlatformMacintosh),
-
- // reported by Filippos (thebluegr) in bugreport #1654500
- // Space Quest 2 (PC 5.25") 2.0A [AGI 2.912]
- GAME_PS("sq2", "2.0A 1987-11-06 5.25\"", "ad7ce8f800581ecc536f3e8021d7a74d", 423, 0x2917, GID_SQ2, Common::kPlatformPC),
-
- // Space Quest 2 (Russian)
- GAME_LPS("sq2", "", "ba21c8934caf28e3ba45ce7d1cd6b041", 423, Common::RU_RUS, 0x2917, GID_SQ2, Common::kPlatformPC),
-
- // Space Quest 2 (PC 3.5") 2.0A [AGI 2.912]
- GAME_PS("sq2", "2.0A 1987-11-06 3.5\"", "6c25e33d23b8bed42a5c7fa63d588e5c", 423, 0x2917, GID_SQ2, Common::kPlatformPC),
-
- // Space Quest 2 (PC 5.25"/ST) 2.0C/A [AGI 2.915]
- // Menus not tested
- GAME("sq2", "2.0C/A 5.25\"/ST", "bd71fe54869e86945041700f1804a651", 0x2917, GID_SQ2),
-
- // Space Quest 2 (PC 3.5") 2.0F [AGI 2.936]
- GAME("sq2", "2.0F 1989-01-05 3.5\"", "28add5125484302d213911df60d2aded", 0x2936, GID_SQ2),
-
- // Space Quest 2 (CoCo3 360k) [AGI 2.023]
- GAME_PS("sq2", "", "12973d39b892dc9d280257fd271e9597", 768, 0x2440, GID_SQ2, Common::kPlatformCoCo3),
-
- // Space Quest 2 (CoCo3 360k) [AGI 2.072]
- GAME_PS("sq2", "updated", "d24f19b047e65e1763eff4b46f3d50df", 768, 0x2440, GID_SQ2, Common::kPlatformCoCo3),
-
- // Troll's Tale
- GAMEpre_PS("troll", "", "troll.img", "62903f264b3d849be4214b3a5c42a2fa", 184320, 0x0000, GID_TROLL, Common::kPlatformPC),
-
- // Winnie the Pooh in the Hundred Acre Wood
- GAMEpre_P("winnie", "", "title.pic", "2e7900c1ccaa7671d65405f6d1efed30", 0x0000, GID_WINNIE, Common::kPlatformPC),
-
- // Winnie the Pooh in the Hundred Acre Wood (Amiga)
- GAMEpre_P("winnie", "", "title", "2e7900c1ccaa7671d65405f6d1efed30", 0x0000, GID_WINNIE, Common::kPlatformAmiga),
-
- // Winnie the Pooh in the Hundred Acre Wood (C64)
- GAMEpre_P("winnie", "", "title.pic", "d4eb97cffc866110f71e1ec9f84fe643", 0x0000, GID_WINNIE, Common::kPlatformC64),
-
- // Winnie the Pooh in the Hundred Acre Wood (Apple //gs)
- GAMEpre_P("winnie", "", "title.pic", "45e06010a3c61d78f4661103c901ae11", 0x0000, GID_WINNIE, Common::kPlatformApple2GS),
-
- // Xmas Card 1986 (PC) [AGI 2.272]
- GAME("xmascard", "1986-11-13 [version 1]", "3067b8d5957e2861e069c3c0011bd43d", 0x2272, GID_XMASCARD),
-
- // Xmas Card 1986 (CoCo3 360k) [AGI 2.072]
- GAME_PS("xmascard", "", "25ad35e9628fc77e5e0dd35852a272b6", 768, 0x2440, GID_XMASCARD, Common::kPlatformCoCo3),
-
- FANMADE_F("2 Player Demo", "4279f46b3cebd855132496476b1d2cca", GF_AGIMOUSE),
- FANMADE("AGI Contest 1 Template", "d879aed25da6fc655564b29567358ae2"),
- FANMADE("AGI Contest 2 Template", "5a2fb2894207eff36c72f5c1b08bcc07"),
- FANMADE("AGI Mouse Demo 0.60 demo 1", "c07e2519de674c67386cb2cc6f2e3904"),
- FANMADE("AGI Mouse Demo 0.60 demo 2", "cc49d8b88ed6faf4f53ce92c84e0fe1b"),
- FANMADE("AGI Mouse Demo 0.70", "3497c291e4afb6f758e61740678a2aec"),
- FANMADE_F("AGI Mouse Demo 1.00", "20397f0bf0ef936f416bb321fb768fc7", GF_AGIMOUSE),
- FANMADE_F("AGI Mouse Demo 1.10", "f4ad396b496d6167635ad0b410312ab8", GF_AGIMOUSE|GF_AGIPAL),
- FANMADE("AGI Piano (v1.0)", "8778b3d89eb93c1d50a70ef06ef10310"),
- FANMADE("AGI Quest (v1.46-TJ0)", "1cf1a5307c1a0a405f5039354f679814"),
- FANMADE_I("tetris", "", "7a874e2db2162e7a4ce31c9130248d8a"),
- FANMADE_V("AGI Trek (Demo)", "c02882b8a8245b629c91caf7eb78eafe", 0x2440),
- FANMADE_F("AGI256 Demo", "79261ac143b2e2773b2753674733b0d5", GF_AGI256),
- FANMADE_F("AGI256-2 Demo", "3cad9b3aff1467cebf0c5c5b110985c5", GF_AGI256_2),
- FANMADE_LF("Abrah: L'orphelin de l'espace (v1.2)", "b7b6d1539e14d5a26fa3088288e1badc", Common::FR_FRA, GF_AGIPAL),
- FANMADE("Acidopolis", "7017db1a4b726d0d59e65e9020f7d9f7"),
- FANMADE("Agent 0055 (v1.0)", "c2b34a0c77acb05482781dda32895f24"),
- FANMADE("Agent 06 vs. The Super Nazi", "136f89ca9f117c617e88a85119777529"),
- FANMADE("Agent Quest", "59e49e8f72058a33c00d60ee1097e631"),
- FANMADE("Al Pond - On Holiday (v1.0)", "a84975496b42d485920e886e92eed68b"),
- FANMADE("Al Pond - On Holiday (v1.1)", "7c95ac4689d0c3bfec61e935f3093634"),
- FANMADE("Al Pond - On Holiday (v1.3)", "8f30c260de9e1dd3d8b8f89cc19d2633"),
- FANMADE("Al Pond 1 - Al Lives Forever (v1.0)", "e8921c3043b749b056ff51f56d1b451b"),
- FANMADE("Al Pond 1 - Al Lives Forever (v1.3)", "fb4699474054962e0dbfb4cf12ca52f6"),
- FANMADE("Apocalyptic Quest (v0.03 Teaser)", "42ced528b67965d3bc3b52c635f94a57"),
- FANMADE_F("Apocalyptic Quest (v4.00 Alpha 1)", "e15581628d84949b8d352d224ec3184b", GF_AGIMOUSE),
- FANMADE_F("Apocalyptic Quest (v4.00 Alpha 2)", "0eee850005860e46345b38fea093d194", GF_AGIMOUSE),
- FANMADE_F("Band Quest (Demo)", "7326abefd793571cc17ed0db647bdf34", GF_AGIMOUSE),
- FANMADE_F("Band Quest (Early Demo)", "de4758dd34676b248c8301b32d93bc6f", GF_AGIMOUSE),
- FANMADE("Beyond the Titanic 2", "9b8de38dc64ffb3f52b7877ea3ebcef9"),
- FANMADE("Biri Quest 1", "1b08f34f2c43e626c775c9d6649e2f17"),
- FANMADE("Bob The Farmboy", "e4b7df9d0830addee5af946d380e66d7"),
- FANMADE_F("Boring Man 1: The Toad to Robinland", "d74481cbd227f67ace37ce6a5493039f", GF_AGIMOUSE),
- FANMADE_F("Boring Man 2: Ho Man! This Game Sucks!", "250032ba105bdf7c1bc4fed767c2d37e", GF_AGIMOUSE),
- FANMADE("Botz", "a8fabe4e807adfe5ec02bfec6d983695"),
- FANMADE("Brian's Quest (v1.0)", "0964aa79b9cdcff7f33a12b1d7e04b9c"),
- FANMADE("CPU-21 (v1.0)", "35b7cdb4d17e890e4c52018d96e9cbf4"),
- FANMADE_I("caitlyn", "Demo", "5b8a3cdb2fc05469f8119d49f50fbe98"),
- FANMADE_I("caitlyn", "", "818469c484cae6dad6f0e9a353f68bf8"),
- FANMADE("Car Driver (v1.1)", "2311611d2d36d20ccc9da806e6cba157"),
- FANMADE("Cloak of Darkness (v1.0)", "5ba6e18bf0b53be10db8f2f3831ee3e5"),
- FANMADE("Coco Coq (English) - Coco Coq In Grostesteing's Base (v.1.0.3)", "97631f8e710544a58bd6da9e780f9320"),
- FANMADE_L("Coco Coq (French) - Coco Coq Dans la Base de Grostesteing (v1.0.2)", "ef579ebccfe5e356f9a557eb3b2d8649", Common::FR_FRA),
- FANMADE("Corby's Murder Mystery (v1.0)", "4ebe62ac24c5a8c7b7898c8eb070efe5"),
- FANMADE_F("DG: The AGIMouse Adventure (English v1.1)", "efe453b92bc1487ea69fbebede4d5f26", GF_AGIMOUSE|GF_AGIPAL),
- FANMADE_LF("DG: The AGIMouse Adventure (French v1.1)", "eb3d17ca466d672cbb95947e8d6e846a", Common::FR_FRA, GF_AGIMOUSE|GF_AGIPAL),
- FANMADE("DG: The Adventure Game (English v1.1)", "0d6376d493fa7a21ec4da1a063e12b25"),
- FANMADE_L("DG: The Adventure Game (French v1.1)", "258bdb3bb8e61c92b71f2f456cc69e23", Common::FR_FRA),
- FANMADE("Dashiki (16 Colors)", "9b2c7b9b0283ab9f12bedc0cb6770a07"),
- FANMADE_F("Dashiki (256 Colors)", "c68052bb209e23b39b55ff3d759958e6", GF_AGIMOUSE|GF_AGI256),
- FANMADE("Date Quest 1 (v1.0)", "ba3dcb2600645be53a13170aa1a12e69"),
- FANMADE("Date Quest 2 (v1.0 Demo)", "1602d6a2874856e928d9a8c8d2d166e9"),
- FANMADE("Date Quest 2 (v1.0)", "f13f6fc85aa3e6e02b0c20408fb63b47"),
- FANMADE("Dave's Quest (v0.07)", "f29c3660de37bacc1d23547a167f27c9"),
- FANMADE("Dave's Quest (v0.17)", "da3772624cc4a86f7137db812f6d7c39"),
- FANMADE("Disco Nights (Demo)", "dc5a2b21182ba38bdcd992a3a978e690"),
- FANMADE("Dogs Quest - The Quest for the Golden Bone (v1.0)", "f197357edaaea0ff70880602d2f09b3e"),
- FANMADE("Dr. Jummybummy's Space Adventure", "988bd81785f8a452440a2a8ac67f96aa"),
- FANMADE("Ed Ward", "98be839b9f30cbedea4c9cee5442d827"),
- FANMADE("Elfintard", "c3b847e9e9e978af9708df76a0751dc2"),
- FANMADE("Enclosure (v1.01)", "f08e66fee9ecdde77db7ee9a10c96ba2"),
- FANMADE("Enclosure (v1.03)", "e4a0613ed02401502e506ba3565a8c40"),
- FANMADE_SVP("Enclosure", "fe98e6126db74c6cc6fd8fe395cc6e8c", 345, 0x2440, Common::kPlatformCoCo3),
- FANMADE("Epic Fighting (v0.1)", "aff24a1b3bdd676187685c4d95ba4294"),
- FANMADE("Escape Quest (v0.0.3)", "2346b65619b1da0298b715b06d1a45a1"),
- FANMADE("Escape from the Desert (beta 1)", "dfdc634d340854bd6ece28024010758d"),
- FANMADE("Escape from the Salesman", "e723ca4fe0f6f56affe039fbb4dbeb6c"),
- FANMADE("Fu$k Quest 1 (final)", "1cd0587422313f6ca77d6a95988e88ed"),
- FANMADE("Fu$k Quest 1", "1cd0587422313f6ca77d6a95988e88ed"),
- FANMADE("Fu$k Quest 2 - Romancing the Bone (Teaser)", "d288355d71d9bb1639260ccaa3b2fbfe"),
- FANMADE("Fu$k Quest 2 - Romancing the Bone", "294beeb7765c7ea6b05ed7b9bf7bff4f"),
- FANMADE("Gennadi Tahab Autot - Mission Pack 1 - Kuressaare", "bfa5fe71978e6ccf3d4eedd430124015"),
- FANMADE("Go West, Young Hippie", "ff31484ea465441cb5f3a0f8e956b716"),
- FANMADE("Good Man (demo v3.41)", "3facd8a8f856b7b6e0f6c3200274d88c"),
-
- {
- // Groza
- {
- "agi-fanmade",
- "Groza (russian) [AGDS sample]",
- AD_ENTRY1("logdir", "421da3a18004122a966d64ab6bd86d2e"),
- Common::RU_RUS,
- Common::kPlatformPC,
- ADGF_USEEXTRAASTITLE,
- GUIO_NONE
- },
- GID_FANMADE,
- GType_V2,
- GF_AGDS,
- 0x2440,
- },
-
- {
- // Get Outta SQ
- {
- "agi-fanmade",
- "Get Outta Space Quest",
- AD_ENTRY1("logdir", "aaea5b4a348acb669d13b0e6f22d4dc9"),
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_USEEXTRAASTITLE,
- GUIO_NONE
- },
- GID_GETOUTTASQ,
- GType_V2,
- 0,
- 0x2440,
- },
-
- FANMADE_F("Half-Death - Terror At White-Mesa", "b62c05d0ace878261392073f57ae788c", GF_AGIMOUSE),
- FANMADE("Hank's Quest (v1.0 English) - Victim of Society", "64c15b3d0483d17888129100dc5af213"),
- FANMADE("Hank's Quest (v1.1 English) - Victim of Society", "86d1f1dd9b0c4858d096e2a60cca8a14"),
- FANMADE_L("Hank's Quest (v1.81 Dutch) - Slachtoffer Van Het Gebeuren", "41e53972d55ff3dff9e90d15fe1b659f", Common::NL_NLD),
- FANMADE("Hank's Quest (v1.81 English) - Victim of Society", "7a776383282f62a57c3a960dafca62d1"),
- FANMADE("Herbao (v0.2)", "6a5186fc8383a9060517403e85214fc2"),
- FANMADE_F("Hitler's Legacy (v.0004q)", "a412881269ba34584bd0a3268e5a9863", GF_AGIMOUSE),
- FANMADE("Hobbits", "4a1c1ef3a7901baf0ab45fde0cfadd89"),
- FANMADE_F("Isabella Coq - A Present For My Dad", "55c6819f2330c4d5d6459874c9f123d9", GF_AGIMOUSE),
- FANMADE("Jack & Julia - VAMPYR", "8aa0b9a26f8d5a4421067ab8cc3706f6"),
- FANMADE("Jeff's Quest (v.5 alpha Jun 1)", "10f1720eed40c12b02a0f32df3e72ded"),
- FANMADE("Jeff's Quest (v.5 alpha May 31)", "51ff71c0ed90db4e987a488ed3bf0551"),
- FANMADE("Jen's Quest (Demo 1)", "361afb5bdb6160213a1857245e711939"),
- FANMADE("Jen's Quest (Demo 2)", "3c321eee33013b289ab8775449df7df2"),
- FANMADE("Jiggy Jiggy Uh! Uh!", "bc331588a71e7a1c8840f6cc9b9487e4"),
- FANMADE("Jimmy In: The Alien Attack (v0.1)", "a4e9db0564a494728de7873684a4307c"),
- FANMADE("Joe McMuffin In \"What's Cooking, Doc\" (v1.0)", "8a3de7e61a99cb605fa6d233dd91c8e1"),
- FANMADE_LVF("Jolimie, le Village Maudit (v0.5)", "21818501636b3cb8ad5de5c1a66de5c2", Common::FR_FRA, 0x2936, GF_AGIMOUSE|GF_AGIPAL),
- FANMADE_LVF("Jolimie, le Village Maudit (v1.1)", "68d7aef1161bb5972fe03efdf29ccb7f", Common::FR_FRA, 0x2936, GF_AGIMOUSE|GF_AGIPAL),
- FANMADE("Journey Of Chef", "aa0a0b5a6364801ae65fdb96d6741df5"),
- FANMADE("Jukebox (v1.0)", "c4b9c5528cc67f6ba777033830de7751"),
- FANMADE("Justin Quest (v1.0 in development)", "103050989da7e0ffdc1c5e1793a4e1ec"),
- FANMADE("J\xf5ulumaa (v0.05) (Estonian)", "53982ecbfb907e41392b3961ad1c3475"),
- FANMADE("Kings Quest 2 - Breast Intentions (v2.0 Mar 26)", "a25d7379d281b1b296d4785df90a8e78"),
- FANMADE("Kings Quest 2 - Breast Intentions (v2.0 Aug 16)", "6b4f796d0421d2e12e501b511962e03a"),
- FANMADE("Lasse Holm: The Quest for Revenge (v1.0)", "f9fbcc8a4ef510bfbb92423296ff4abb"),
- FANMADE("Lawman for Hire", "c78b28bfd3767dd455b992cd8b7854fa"),
- FANMADE("Lefty Goes on Vacation (Not in The Right Place)", "ccdc49a33870310b01f2c48b8a1f3c34"),
- FANMADE("Les Ins\xe3parables (v1.0)", "4b780887cab0ecabc5eca319acb3acf2"),
- FANMADE("Little Pirate (Demo 2 v0.6)", "437068efe4ec32d436da09d6f2ea56e1"),
- FANMADE("Lost Eternity (v1.0)", "95f15c5632feb8a39e9ca3d9af35fcc9"),
- FANMADE("MD Quest - The Search for Michiel (v0.10)", "2a6fcb21d2b5e4144c38ed817fabe8ee"),
- FANMADE("Maale Adummin Quest", "ddfbeb33feb7cf78504fe4dba14ec63b"),
- FANMADE("Monkey Man", "2322d03f997e8cc235d4578efff69cfa"),
- FANMADE_F("Napalm Quest (v0.5)", "b659afb491d967bb34810d1c6ce22093", GF_AGIMOUSE),
- FANMADE("Naturette 1 (English v1.2)", "0a75884e7f010974a230bdf269651117"),
- FANMADE("Naturette 1 (English v1.3)", "f15bbf999ac55ebd404aa1eb84f7c1d9"),
- FANMADE_L("Naturette 1 (French v1.2)", "d3665622cc41aeb9c7ecf4fa43f20e53", Common::FR_FRA),
- FANMADE_F("Naturette 2: Daughter of the Moon (v1.0)", "bdf76a45621c7f56d1c9d40292c6137a", GF_AGIMOUSE|GF_AGIPAL),
- FANMADE_F("Naturette 3: Adventure in Treeworld (v1.0a)", "6dbb0e7fc75fec442e6d9e5a06f1530e", GF_AGIMOUSE|GF_AGIPAL),
- FANMADE_F("Naturette 4: From a Planet to Another Planet (Not Finished)", "13be8cd9cf35aeff0a39b8757057fbc8", GF_AGIMOUSE),
- // FIXME: Actually Naturette 4 has both English and French language support built into it. How to add that information?
- FANMADE_F("Naturette 4: From a Planet to Another Planet (2007-10-05)", "8253706b6ef5423a79413b216760297c", GF_AGIMOUSE|GF_AGIPAL),
- FANMADE("New AGI Hangman Test", "d69c0e9050ccc29fd662b74d9fc73a15"),
- FANMADE("Nick's Quest - In Pursuit of QuakeMovie (v2.1 Gold)", "e29cbf9222551aee40397fabc83eeca0"),
- FANMADE_F("Open Mic Night (v0.1)", "70000a2f67aac27d1133d019df70246d", GF_AGIMOUSE|GF_AGIPAL),
- FANMADE("Operation: Recon", "0679ce8405411866ccffc8a6743370d0"),
- FANMADE("Patrick's Quest (Demo v1.0)", "f254f5b894b98fec5f92acc07fb62841"),
- FANMADE("Phantasmagoria", "87d20c1c11aee99a4baad3797b63146b"),
- FANMADE("Pharaoh Quest (v0.0)", "51c630899d076cf799e573dadaa2276d"),
- FANMADE("Phil's Quest - the Search for Tolbaga", "5e7ca45c360e03164b8358e49900c588"),
- FANMADE("Pinkun Maze Quest (v0.1)", "148ff0843af389928b3939f463bfd20d"),
- FANMADE("Pirate Quest", "bb612a919ed2b9ea23bbf03ce69fed42"),
- FANMADE("Pothead (v0.1)", "d181101385d3a45082f418cd4b3c5b01"),
- FANMADE("President's Quest", "4937d0e8ecadb7888faeb347799b0388"),
- FANMADE("Prince Quest", "266248d75c3130c8ccc9c9bf2ad30a0d"),
- FANMADE("Professor (English) - The Professor is Missing (Mar 17)", "6232de31cc204affdf2e92dfe3dc0e4d"),
- FANMADE("Professor (English) - The Professor is Missing (Mar 22)", "b5fcf0ca2f0d1c073be82f01e2170961"),
- FANMADE_L("Professor (French) - Le Professeur a Disparu", "7d9f8a4d4610bb9b0b97caa17590c2d3", Common::FR_FRA),
- FANMADE("Quest for Glory VI - Hero's Adventure", "d26765c3075064c80d284c5e06e33a7e"),
- FANMADE("Quest for Home", "d2895dc1cd3930f2489af0f843b144b3"),
- FANMADE("Quest for Ladies (demo v1.1 Apr 1)", "3f6e02f16e1154a0daf296c8895edd97"),
- FANMADE("Quest for Ladies (demo v1.1 Apr 6)", "f75e7b6a0769a3fa926eea0854711591"),
- FANMADE("Quest for Piracy 1 - Enter the Silver Pirate (v0.15)", "d23f5c2a26f6dc60c686f8a2436ea4a6"),
- FANMADE("Quest for a Record Deal", "f4fbd7abf056d2d3204f790da5ac89ab"),
- FANMADE("Ralph's Quest (v0.1)", "5cf56378aa01a26ec30f25295f0750ca"),
- FANMADE("Residence 44 Quest (Dutch v0.99)", "7c5cc64200660c70240053b33d379d7d"),
- FANMADE("Residence 44 Quest (English v0.99)", "fe507851fddc863d540f2bec67cc67fd"),
- FANMADE("Residence 44 Quest (English v1.0a)", "f99e3f69dc8c77a45399da9472ef5801"),
- FANMADE("SQ2Eye (v0.3)", "2be2519401d38ad9ce8f43b948d093a3"),
- // FANMADE("SQ2Eye (v0.4)", "2be2519401d38ad9ce8f43b948d093a3"),
- FANMADE("SQ2Eye (v0.41)", "f0e82c55f10eb3542d7cd96c107ae113"),
- FANMADE("SQ2Eye (v0.42)", "d7beae55f6328ef8b2da47b1aafea40c"),
- FANMADE("SQ2Eye (v0.43)", "2a895f06e45de153bb4b77c982009e06"),
- FANMADE("SQ2Eye (v0.44)", "5174fc4b6d8a477ba0ff0575cd64e0aa"),
- FANMADE("SQ2Eye (v0.45)", "6e06f8bb7b90ce6f6aabf1a0e620159c"),
- FANMADE("SQ2Eye (v0.46)", "bf0ad7a035ff9113951d09d1efe380c4"),
- FANMADE("SQ2Eye (v0.47)", "85dc3be1d33ff932c292b74f9037abaa"),
- FANMADE("SQ2Eye (v0.48)", "587574252972a5b5c070a647973a9b4a"),
- FANMADE("SQ2Eye (v0.481)", "fc9234beb49804ae869696ce5af8ef30"),
- FANMADE("SQ2Eye (v0.482)", "3ed84b7b87fa6840f25c15f250a11ffb"),
- FANMADE("SQ2Eye (v0.483)", "647c31298d3f9cda641231b893e347c0"),
- FANMADE("SQ2Eye (v0.484)", "f2c86fae7b9046d408c62c8c49a4b882"),
- FANMADE("SQ2Eye (v0.485)", "af59e36bc28f44545458b68a93e91e67"),
- FANMADE("SQ2Eye (v0.486)", "3fd86436e93456770dbdd4593eded70a"),
- FANMADE("Save Santa (v1.0)", "4644f6beb5802081772f14be56ae196c"),
- FANMADE("Save Santa (v1.3)", "f8afdb6efc5af5e7c0228b44633066af"),
- FANMADE("Schiller (preview 1)", "ade39dea968c959cfebe1cf935d653e9"),
- FANMADE("Schiller (preview 2)", "62cd1f8fc758bf6b4aa334e553624cef"),
- FANMADE_IF("serguei1", "v1.0", "b86725f067e456e10cdbdf5f58e01dec", GF_AGIMOUSE|GF_AGIPAL),
- FANMADE_IF("serguei1", "v1.1 2002 Sep 5", "91975c1fb4b13b0f9a8e9ff74731030d", GF_AGIMOUSE|GF_AGIPAL),
- FANMADE_IF("serguei1", "v1.1 2003 Apr 10", "91975c1fb4b13b0f9a8e9ff74731030d", GF_AGIMOUSE|GF_AGIPAL),
- FANMADE_IF("serguei2", "v0.1.1 Demo", "906ccbc2ddedb29b63141acc6d10cd28", GF_AGIMOUSE),
- FANMADE_IF("serguei2", "v1.3.1 Demo (March 22nd 2008)", "ad1308fcb8f48723cd388e012ebf5e20", GF_AGIMOUSE|GF_AGIPAL),
- FANMADE("Shifty (v1.0)", "2a07984d27b938364bf6bd243ac75080"),
- FANMADE_F("Sliding Tile Game (v1.00)", "949bfff5d8a81c3139152eed4d84ca75", GF_AGIMOUSE),
- FANMADE("Snowboarding Demo (v1.0)", "24bb8f29f1eddb5c0a099705267c86e4"),
- FANMADE("Solar System Tour", "b5a3d0f392dfd76a6aa63f3d5f578403"),
- FANMADE("Sorceror's Appraisal", "fe62615557b3cb7b08dd60c9d35efef1"),
- FANMADE_I("sq0", "v1.03", "d2fd6f7404e86182458494e64375e590"),
- FANMADE_I("sq0", "v1.04", "2ad9d1a4624a98571ee77dcc83f231b6"),
- FANMADE_ISVP("sq0", "", "e1a8e4efcce86e1efcaa14633b9eb986", 762, 0x2440, Common::kPlatformCoCo3),
- FANMADE_I("sqx", "v10.0 Feb 05", "c992ae2f8ab18360404efdf16fa9edd1"),
- FANMADE_I("sqx", "v10.0 Jul 18", "812edec45cefad559d190ffde2f9c910"),
- FANMADE_ISVP("sqx", "", "f0a59044475a5fa37c055d8c3eb4d1a7", 768, 0x2440, Common::kPlatformCoCo3),
- FANMADE_F("Space Quest 3.5", "c077bc28d7b36213dd99dc9ecb0147fc", GF_AGIMOUSE|GF_AGIPAL),
- FANMADE_F("Space Trek (v1.0)", "807a1aeadb2ace6968831d36ab5ea37a", GF_CLIPCOORDS),
- FANMADE("Special Delivery", "88764dfe61126b8e73612c851b510a33"),
- FANMADE("Speeder Bike Challenge (v1.0)", "2deb25bab379285ca955df398d96c1e7"),
- FANMADE("Star Commander 1 - The Escape (v1.0)", "a7806f01e6fa14ebc029faa58f263750"),
- FANMADE("Star Pilot: Bigger Fish", "8cb26f8e1c045b75c6576c839d4a0172"),
- FANMADE_F("Street Quest (Demo)", "cf2aa94a7eb78dce6892c37f03e310d6", GF_AGIPAL),
- FANMADE("Tales of the Tiki", "8103c9c87e3964690a14a3d0d83f7ddc"),
- FANMADE("Tex McPhilip 1 - Quest For The Papacy", "3c74b9a24b51aa8020ac82bee3132266"),
- FANMADE("Tex McPhilip 2 - Road To Divinity (v1.5)", "7387e8df854440bc26620ca0ea43af9a"),
- FANMADE("Tex McPhilip 3 - A Destiny of Sin (Demo v0.25)", "992d12031a486ad84e592ff5d7c9d782"),
- FANMADE("The 13th Disciple (v1.00)", "887719ad59afce9a41ec057dbb73ad73"),
- FANMADE("The Adventures of a Crazed Hermit", "6e3086cbb794d3299a9c5a9792295511"),
- FANMADE("The Grateful Dead", "c2146631afacf8cb455ce24f3d2d46e7"),
- FANMADE("The Legend of Shay-Larah 1 - The Lost Prince", "04e720c8e30c9cf12db22ea14a24a3dd"),
- FANMADE("The Legend of Zelda: The Fungus of Time (Demo v1.00)", "dcaf8166ceb62a3d9b9aea7f3b197c09"),
- FANMADE("The Legendary Harry Soupsmith (Demo 1998 Apr 2)", "64c46b0d6fc135c9835afa80980d2831"),
- FANMADE("The Legendary Harry Soupsmith (Demo 1998 Aug 19)", "8d06d82970f2c591d880a95476efbcf0"),
- FANMADE("The Long Haired Dude: Encounter of the 18-th Kind", "86ea17b9fc2f3e537a7e40863d352c29"),
- FANMADE("The Lost Planet (v0.9)", "590dffcbd932a9fbe554be13b769cac0"),
- FANMADE("The Lost Planet (v1.0)", "58564df8b6394612dd4b6f5c0fd68d44"),
- FANMADE("The New Adventure of Roger Wilco (v1.00)", "e5f0a7cb8d49f66b89114951888ca688"),
- FANMADE("The Ruby Cast (v0.02)", "ed138e461bb1516e097007e017ab62df"),
- FANMADE("The Shadow Plan", "c02cd10267e721f4e836b1431f504a0a"),
- FANMADE("Time Quest (Demo v0.1)", "12e1a6f03ea4b8c5531acd0400b4ed8d"),
- FANMADE("Time Quest (Demo v0.2)", "7b710608abc99e0861ac59b967bf3f6d"),
- FANMADE_SVP("Time Quest", "90314f473d8317be5cd1f0306f139aea", 300, 0x2440, Common::kPlatformCoCo3),
- FANMADE("Tonight The Shrieking Corpses Bleed (Demo v0.11)", "bcc57a7c8d563fa0c333107ae1c0a6e6"),
- FANMADE("Tonight The Shrieking Corpses Bleed (v1.01)", "36b38f621b38e8d104aa0807302dc8c9"),
- FANMADE("Turks' Quest - Heir to the Planet", "3d19254b737c8b218e5bc4580542b79a"),
- FANMADE("URI Quest (v0.173 Feb 27)", "3986eefcf546dafc45f920ae91a697c3"),
- FANMADE("URI Quest (v0.173 Jan 29)", "494150940d34130605a4f2e67ee40b12"),
- {
- // V - The Graphical Adventure
- {
- "agi-fanmade",
- "V - The Graphical Adventure (Demo 2)",
- AD_ENTRY1s("vdir", "c71f5c1e008d352ae9040b77fcf79327", 3080),
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_USEEXTRAASTITLE,
- GUIO_NONE
- },
- GID_FANMADE,
- GType_V3,
- GF_FANMADE,
- 0x3149,
- },
- FANMADE_SVP("V - The Graphical Adventure", "1646eaade74f137a9041eb427a389969", 768, 0x2440, Common::kPlatformCoCo3),
-
- FANMADE("Voodoo Girl - Queen of the Darned (v1.2 2002 Jan 1)", "ae95f0c77d9a97b61420fd192348b937"),
- FANMADE("Voodoo Girl - Queen of the Darned (v1.2 2002 Mar 29)", "11d0417b7b886f963d0b36789dac4c8f"),
- FANMADE("Wizaro (v0.1)", "abeec1eda6eaf8dbc52443ea97ff140c"),
-
- { AD_TABLE_END_MARKER, 0, 0, 0, 0 }
-};
-
-/**
- * The fallback game descriptor used by the AGI engine's fallbackDetector.
- * Contents of this struct are to be overwritten by the fallbackDetector.
- */
-static AGIGameDescription g_fallbackDesc = {
- {
- "",
- "",
- AD_ENTRY1(0, 0), // This should always be AD_ENTRY1(0, 0) in the fallback descriptor
- Common::UNK_LANG,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GID_FANMADE,
- GType_V2,
- GF_FANMADE,
- 0x2917,
-};
+#include "agi/detection_tables.h"
static const ADParams detectionParams = {
// Pointer to ADGameDescription or its superset structure
@@ -979,11 +145,13 @@ static const ADParams detectionParams = {
// Flags
0,
// Additional GUI options (for every game}
- Common::GUIO_NOSPEECH | Common::GUIO_NOMIDI
+ Common::GUIO_NOSPEECH,
+ // Maximum directory depth
+ 1,
+ // List of directory globs
+ 0
};
-} // End of namespace Agi
-
using namespace Agi;
class AgiMetaEngine : public AdvancedMetaEngine {
@@ -1322,6 +490,9 @@ bool AgiBase::canLoadGameStateCurrently() {
}
bool AgiBase::canSaveGameStateCurrently() {
+ if (getGameID() == GID_BC) // Technically in Black Cauldron we may save anytime
+ return true;
+
return (!(getGameType() == GType_PreAGI) && getflag(fMenusWork) && !_noSaveLoadAllowed && _game.inputEnabled);
}
diff --git a/engines/agi/detection_tables.h b/engines/agi/detection_tables.h
new file mode 100644
index 0000000000..711701f55a
--- /dev/null
+++ b/engines/agi/detection_tables.h
@@ -0,0 +1,863 @@
+/* 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$
+ *
+ */
+
+namespace Agi {
+
+using Common::GUIO_NONE;
+
+#define GAME_LVFPN(id,extra,fname,md5,size,lang,ver,features,gid,platform,interp) { \
+ { \
+ id, \
+ extra, \
+ AD_ENTRY1s(fname,md5,size), \
+ lang, \
+ platform, \
+ ADGF_NO_FLAGS, \
+ GUIO_NONE \
+ }, \
+ gid, \
+ interp, \
+ features, \
+ ver, \
+ }
+
+#define GAME_LVFPNF(id,name,fname,md5,size,lang,ver,features,gid,platform,interp) { \
+ { \
+ id, \
+ name, \
+ AD_ENTRY1s(fname,md5,size), \
+ lang, \
+ platform, \
+ ADGF_USEEXTRAASTITLE, \
+ GUIO_NONE \
+ }, \
+ gid, \
+ interp, \
+ features, \
+ ver, \
+ }
+
+#define GAME(id,extra,md5,ver,gid) GAME_LVFPN(id,extra,"logdir",md5,-1,Common::EN_ANY,ver,0,gid,Common::kPlatformPC,GType_V2)
+#define GAME3(id,extra,fname,md5,ver,gid) GAME_LVFPN(id,extra,fname,md5,-1,Common::EN_ANY,ver,0,gid,Common::kPlatformPC,GType_V3)
+
+#define GAME_P(id,extra,md5,ver,gid,platform) GAME_LVFPN(id,extra,"logdir",md5,-1,Common::EN_ANY,ver,0,gid,platform,GType_V2)
+
+#define GAME_FP(id,extra,md5,ver,flags,gid,platform) GAME_LVFPN(id,extra,"logdir",md5,-1,Common::EN_ANY,ver,flags,gid,platform,GType_V2)
+#define GAME_F(id,extra,md5,ver,flags,gid) GAME_FP(id,extra,md5,ver,flags,gid,Common::kPlatformPC)
+
+#define GAME_PS(id,extra,md5,size,ver,gid,platform) GAME_LVFPN(id,extra,"logdir",md5,size,Common::EN_ANY,ver,0,gid,platform,GType_V2)
+
+#define GAME_LPS(id,extra,md5,size,lang,ver,gid,platform) GAME_LVFPN(id,extra,"logdir",md5,size,lang,ver,0,gid,platform,GType_V2)
+
+#define GAME_LFPS(id,extra,md5,size,lang,ver,flags,gid,platform) GAME_LVFPN(id,extra,"logdir",md5,size,lang,ver,flags,gid,platform,GType_V2)
+
+#define GAME3_P(id,extra,fname,md5,ver,flags,gid,platform) GAME_LVFPN(id,extra,fname,md5,-1,Common::EN_ANY,ver,flags,gid,platform,GType_V3)
+
+#define GAMEpre_P(id,extra,fname,md5,ver,gid,platform) GAME_LVFPN(id,extra,fname,md5,-1,Common::EN_ANY,ver,0,gid,platform,GType_PreAGI)
+
+#define GAMEpre_PS(id,extra,fname,md5,size,ver,gid,platform) GAME_LVFPN(id,extra,fname,md5,size,Common::EN_ANY,ver,0,gid,platform,GType_PreAGI)
+
+#define GAME3_PS(id,extra,fname,md5,size,ver,flags,gid,platform) GAME_LVFPN(id,extra,fname,md5,size,Common::EN_ANY,ver,flags,gid,platform,GType_V3)
+
+#define FANMADE_ILVF(id,name,md5,lang,ver,features) GAME_LVFPNF(id,name,"logdir",md5,-1,lang,ver,(GF_FANMADE|features),GID_FANMADE,Common::kPlatformPC,GType_V2)
+
+#define FANMADE_ISVP(id,name,md5,size,ver,platform) GAME_LVFPNF(id,name,"logdir",md5,size,Common::EN_ANY,ver,GF_FANMADE,GID_FANMADE,platform,GType_V2)
+#define FANMADE_SVP(name,md5,size,ver,platform) FANMADE_ISVP("agi-fanmade",name,md5,size,ver,platform)
+
+#define FANMADE_LVF(name,md5,lang,ver,features) FANMADE_ILVF("agi-fanmade",name,md5,lang,ver,features)
+
+#define FANMADE_LF(name,md5,lang,features) FANMADE_LVF(name,md5,lang,0x2917,features)
+#define FANMADE_IF(id,name,md5,features) FANMADE_ILVF(id,name,md5,Common::EN_ANY,0x2917,features)
+
+#define FANMADE_V(name,md5,ver) FANMADE_LVF(name,md5,Common::EN_ANY,ver,0)
+#define FANMADE_F(name,md5,features) FANMADE_LF(name,md5,Common::EN_ANY,features)
+#define FANMADE_L(name,md5,lang) FANMADE_LF(name,md5,lang,0)
+#define FANMADE_I(id,name,md5) FANMADE_IF(id,name,md5,0)
+
+#define FANMADE(name,md5) FANMADE_F(name,md5,0)
+
+static const AGIGameDescription gameDescriptions[] = {
+
+ // AGI Demo 1 (PC) 05/87 [AGI 2.425]
+ GAME("agidemo", "Demo 1 1987-05-20", "9c4a5b09cc3564bc48b4766e679ea332", 0x2440, GID_AGIDEMO),
+
+ // AGI Demo 2 (IIgs) 1.0C (Censored)
+ GAME_P("agidemo", "Demo 2 1987-11-24 1.0C", "580ffdc569ff158f56fb92761604f70e", 0x2917, GID_AGIDEMO, Common::kPlatformApple2GS),
+
+ // AGI Demo 2 (PC 3.5") 11/87 [AGI 2.915]
+ GAME("agidemo", "Demo 2 1987-11-24 3.5\"", "e8ebeb0bbe978172fe166f91f51598c7", 0x2917, GID_AGIDEMO),
+
+ // AGI Demo 2 (PC 5.25") 11/87 [v1] [AGI 2.915]
+ GAME("agidemo", "Demo 2 1987-11-24 [version 1] 5.25\"", "852ac303a374df62571642ca1e2d1f0a", 0x2917, GID_AGIDEMO),
+
+ // AGI Demo 2 (PC 5.25") 01/88 [v2] [AGI 2.917]
+ GAME("agidemo", "Demo 2 1987-11-25 [version 2] 5.25\"", "1503f02086ea9f388e7e041c039eaa69", 0x2917, GID_AGIDEMO),
+
+ // AGI Demo 3 (PC) 09/88 [AGI 3.002.102]
+ GAME3("agidemo", "Demo 3 1988-09-13", "dmdir", "289c7a2c881f1d973661e961ced77d74", 0x3149, GID_AGIDEMO),
+
+ // AGI Demo for Kings Quest III and Space Quest I
+ GAME("agidemo", "Demo Kings Quest III and Space Quest I", "502e6bf96827b6c4d3e67c9cdccd1033", 0x2272, GID_AGIDEMO),
+
+ // Black Cauldron (Amiga) 2.00 6/14/87
+ GAME_P("bc", "2.00 1987-06-14", "7b01694af21213b4727bb94476f64eb5", 0x2440, GID_BC, Common::kPlatformAmiga),
+
+ // Black Cauldron (Apple IIgs) 1.0O 2/24/89 (CE)
+ // Menus not tested
+ GAME3_P("bc", "1.0O 1989-02-24 (CE)", "bcdir", "dc09d30b147242692f4f85b9811962db", 0x3149, 0, GID_BC, Common::kPlatformApple2GS),
+
+ // Black Cauldron (PC) 2.00 6/14/87 [AGI 2.439]
+ GAME("bc", "2.00 1987-06-14", "7f598d4712319b09d7bd5b3be10a2e4a", 0x2440, GID_BC),
+
+ // Black Cauldron (Russian)
+ GAME_LPS("bc", "", "b7de782dfdf8ea7dde8064f09804bcf5", 357, Common::RU_RUS, 0x2440, GID_BC, Common::kPlatformPC),
+
+ // Black Cauldron (PC 5.25") 2.10 11/10/88 [AGI 3.002.098]
+ GAME3("bc", "2.10 1988-11-10 5.25\"", "bcdir", "0c5a9acbcc7e51127c34818e75806df6", 0x3149, GID_BC),
+
+ // Black Cauldron (PC) 2.10 [AGI 3.002.097]
+ GAME3("bc", "2.10", "bcdir", "0de3953c9225009dc91e5b0d1692967b", 0x3149, GID_BC),
+
+ // Black Cauldron (CoCo3 360k) [AGI 2.023]
+ GAME_PS("bc", "", "51212c54808ade96176f201ae0ac7a6f", 357, 0x2440, GID_BC, Common::kPlatformCoCo3),
+
+ // Black Cauldron (CoCo3 360k) [AGI 2.072]
+ GAME_PS("bc", "updated", "c4e1937f74e8100cd0152b904434d8b4", 357, 0x2440, GID_BC, Common::kPlatformCoCo3),
+
+// TODO
+// These aren't supposed to work now as they require unsupported agi engine 2.01
+#if 0
+ // Donald Duck's Playground (Amiga) 1.0C
+ // Menus not tested
+ GAME_P("ddp", "1.0C 1987-04-27", "550971d196f65190a5c760d2479406ef", 0x2272, GID_DDP, Common::kPlatformAmiga),
+
+ // Donald Duck's Playground (ST) 1.0A 8/8/86
+ // Menus not tested
+ GAME("ddp", "1.0A 1986-08-08", "64388812e25dbd75f7af1103bc348596", 0x2272, GID_DDP),
+
+ // reported by Filippos (thebluegr) in bugreport #1654500
+ // Menus not tested
+ GAME_PS("ddp", "1.0C 1986-06-09", "550971d196f65190a5c760d2479406ef", 132, 0x2272, GID_DDP, Common::kPlatformPC),
+#endif
+
+ // Gold Rush! (Amiga) 1.01 1/13/89 aka 2.05 3/9/89 # 2.316
+ GAME3_PS("goldrush", "1.01 1989-01-13 aka 2.05 1989-03-09", "dirs", "a1d4de3e75c2688c1e2ca2634ffc3bd8", 2399, 0x3149, 0, GID_GOLDRUSH, Common::kPlatformAmiga),
+
+ // Gold Rush! (Apple IIgs) 1.0M 2/28/89 (CE) aka 2.01 12/22/88
+ // Menus not tested
+ GAME3_P("goldrush", "1.0M 1989-02-28 (CE) aka 2.01 1988-12-22", "grdir", "3f7b9ce62631434389f85371b11921d6", 0x3149, GF_2GSOLDSOUND, GID_GOLDRUSH, Common::kPlatformApple2GS),
+
+ // Gold Rush! (ST) 1.01 1/13/89 aka 2.01 12/22/88
+ GAME3_P("goldrush", "1.01 1989-01-13 aka 2.01 1988-12-22", "grdir", "4dd4d50480a3d6c206fa227ce8142735", 0x3149, 0, GID_GOLDRUSH, Common::kPlatformAtariST),
+
+ // Gold Rush! (PC 5.25") 2.01 12/22/88 [AGI 3.002.149]
+ GAME3("goldrush", "2.01 1988-12-22 5.25\"", "grdir", "db733d199238d4009a9e95f11ece34e9", 0x3149, GID_GOLDRUSH),
+
+ // Gold Rush! (PC 3.5") 2.01 12/22/88 [AGI 3.002.149]
+ GAME3("goldrush", "2.01 1988-12-22 3.5\"", "grdir", "6a285235745f69b4b421403659497216", 0x3149, GID_GOLDRUSH),
+
+ // Gold Rush! (PC 3.5", bought from The Software Farm) 3.0 1998-12-22 [AGI 3.002.149]
+ GAME3("goldrush", "3.0 1998-12-22 3.5\"", "grdir", "6882b6090473209da4cd78bb59f78dbe", 0x3149, GID_GOLDRUSH),
+
+ {
+ // Gold Rush! (PC 5.25") 2.01 12/22/88 [AGI 3.002.149]
+ {
+ "goldrush",
+ "2.01 1988-12-22",
+ {
+ { "grdir", 0, "db733d199238d4009a9e95f11ece34e9", 2399},
+ { "vol.0", 0, "4b6423d143674d3757ab1b875d25951d", 25070},
+ { NULL, 0, NULL, 0}
+ },
+ Common::EN_ANY,
+ Common::kPlatformMacintosh,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GID_GOLDRUSH,
+ GType_V3,
+ GF_MACGOLDRUSH,
+ 0x3149,
+ },
+
+
+ // Gold Rush! (CoCo3 720k) [AGI 2.023]
+ GAME_PS("goldrush", "", "0a41b65efc0cd6c4271e957e6ffbbd8e", 744, 0x2440, GID_GOLDRUSH, Common::kPlatformCoCo3),
+
+ // Gold Rush! (CoCo3 360k/720k) [AGI 2.072]
+ GAME_PS("goldrush", "updated", "c49bf56bf91e31a4601a604e51ef8bfb", 744, 0x2440, GID_GOLDRUSH, Common::kPlatformCoCo3),
+
+ // King's Quest 1 (Amiga) 1.0U # 2.082
+ // The original game did not have menus, they are enabled under ScummVM
+ GAME_FP("kq1", "1.0U 1986", "246c695324f1c514aee2b904fa352fad", 0x2440, GF_MENUS, GID_KQ1, Common::kPlatformAmiga),
+
+ // King's Quest 1 (ST) 1.0V
+ // The original game did not have menus, they are enabled under ScummVM
+ GAME_FP("kq1", "1.0V 1986", "c3a017e556c4b0eece366a4cd9abb657", 0x2272, GF_MENUS, GID_KQ1, Common::kPlatformAtariST),
+
+ // King's Quest 1 (IIgs) 1.0S-88223
+ // Menus not tested
+ GAME_P("kq1", "1.0S 1988-02-23", "f4277aa34b43d37382bc424c81627617", 0x2272, GID_KQ1, Common::kPlatformApple2GS),
+
+ // King's Quest 1 (Mac) 2.0C
+ GAME_P("kq1", "2.0C 1987-03-26", "d4c4739d4ac63f7dbd29255425077d48", 0x2440, GID_KQ1, Common::kPlatformMacintosh),
+
+ // King's Quest 1 (PC 5.25"/3.5") 2.0F [AGI 2.917]
+ GAME("kq1", "2.0F 1987-05-05 5.25\"/3.5\"", "10ad66e2ecbd66951534a50aedcd0128", 0x2917, GID_KQ1),
+
+ // King's Quest 1 (CoCo3 360k) [AGI 2.023]
+ GAME_PS("kq1", "", "10ad66e2ecbd66951534a50aedcd0128", 315, 0x2440, GID_KQ1, Common::kPlatformCoCo3),
+
+ // King's Quest 1 (CoCo3 360k) [AGI 2.023]
+ GAME_PS("kq1", "fixed", "4c8ef8b5d2f1b6c1a93e456d1f1ffc74", 768, 0x2440, GID_KQ1, Common::kPlatformCoCo3),
+
+ // King's Quest 1 (CoCo3 360k) [AGI 2.072]
+ GAME_PS("kq1", "updated", "94087178c78933a4af3cd24d1c8dd7b2", 315, 0x2440, GID_KQ1, Common::kPlatformCoCo3),
+
+ // King's Quest 2 (IIgs) 2.0A 6/16/88 (CE)
+ GAME_P("kq2", "2.0A 1988-06-16 (CE)", "5203c8b95250a2ecfee93ddb99414753", 0x2917, GID_KQ2, Common::kPlatformApple2GS),
+
+ // King's Quest 2 (Amiga) 2.0J (Broken)
+ GAME_P("kq2", "2.0J 1987-01-29 [OBJECT decrypted]", "b866f0fab2fad91433a637a828cfa410", 0x2440, GID_KQ2, Common::kPlatformAmiga),
+
+ // King's Quest 2 (Mac) 2.0R
+ GAME_P("kq2", "2.0R 1988-03-23", "cbdb0083317c8e7cfb7ac35da4bc7fdc", 0x2440, GID_KQ2, Common::kPlatformMacintosh),
+
+ // King's Quest 2 (PC) 2.1 [AGI 2.411]; entry from DAGII, but missing from Sarien?
+ // XXX: any major differences from 2.411 to 2.440?
+ GAME("kq2", "2.1 1987-04-10", "759e39f891a0e1d86dd29d7de485c6ac", 0x2440, GID_KQ2),
+
+ // King's Quest 2 (PC 5.25"/3.5") 2.2 [AGI 2.426]
+ GAME("kq2", "2.2 1987-05-07 5.25\"/3.5\"", "b944c4ff18fb8867362dc21cc688a283", 0x2917, GID_KQ2),
+
+ // King's Quest 2 (Russian)
+ GAME_LPS("kq2", "", "35211c574ececebdc723b23e35f99275", 543, Common::RU_RUS, 0x2917, GID_KQ2, Common::kPlatformPC),
+
+ // King's Quest 2 (CoCo3 360k) [AGI 2.023]
+ GAME_PS("kq2", "", "b944c4ff18fb8867362dc21cc688a283", 543, 0x2440, GID_KQ2, Common::kPlatformCoCo3),
+
+ // King's Quest 2 (CoCo3 360k) [AGI 2.072]
+ GAME_PS("kq2", "updated", "f64a606de740a5348f3d125c03e989fe", 543, 0x2440, GID_KQ2, Common::kPlatformCoCo3),
+
+ // King's Quest 2 (CoCo3 360k) [AGI 2.023]
+ GAME_PS("kq2", "fixed", "fb33ac2768a94a89117a270771db465c", 768, 0x2440, GID_KQ2, Common::kPlatformCoCo3),
+
+ // King's Quest 3 (Amiga) 1.01 11/8/86
+ // The original game did not have menus, they are enabled under ScummVM
+ GAME_FP("kq3", "1.01 1986-11-08", "8ab343306df0e2d98f136be4e8cfd0ef", 0x2440, GF_MENUS, GID_KQ3, Common::kPlatformAmiga),
+
+ // King's Quest 3 (ST) 1.02 11/18/86
+ // Does not have menus, crashes if menus are enforced. Therefore, ESC pauses the game
+ GAME_FP("kq3", "1.02 1986-11-18", "8846df2654302b623217ba8bd6d657a9", 0x2272, GF_ESCPAUSE, GID_KQ3, Common::kPlatformAtariST),
+
+ // King's Quest 3 (Mac) 2.14 3/15/88
+ GAME_P("kq3", "2.14 1988-03-15", "7639c0da5ce94848227d409351fabda2", 0x2440, GID_KQ3, Common::kPlatformMacintosh),
+
+ // King's Quest 3 (IIgs) 2.0A 8/28/88 (CE)
+ GAME_P("kq3", "2.0A 1988-08-28 (CE)", "ac30b7ca5a089b5e642fbcdcbe872c12", 0x2917, GID_KQ3, Common::kPlatformApple2GS),
+
+ // King's Quest 3 (Amiga) 2.15 11/15/89 # 2.333
+ // Original pauses with ESC, has menus accessible with mouse.
+ // ver = 0x3086 -> menus accessible with ESC or mouse, bug #2835581 (KQ3: Game Crash When Leaving Tavern as Fly).
+ // ver = 0x3149 -> menus accessible with mouse, ESC pauses game, bug #2835581 disappears.
+ GAME3_PS("kq3", "2.15 1989-11-15", "dirs", "8e35bded2bc5cf20f5eec2b15523b155", 1805, 0x3149, 0, GID_KQ3, Common::kPlatformAmiga),
+
+ // King's Quest 3 (PC) 1.01 11/08/86 [AGI 2.272]
+ // Does not have menus, crashes if menus are enforced. Therefore, ESC pauses the game
+ GAME_FP("kq3", "1.01 1986-11-08", "9c2b34e7ffaa89c8e2ecfeb3695d444b", 0x2272, GF_ESCPAUSE, GID_KQ3, Common::kPlatformPC),
+
+ // King's Quest 3 (Russian)
+ GAME_LFPS("kq3", "", "5856dec6ccb9c4b70aee21044a19270a", 390, Common::RU_RUS, 0x2272, GF_ESCPAUSE, GID_KQ3, Common::kPlatformPC),
+
+ // King's Quest 3 (PC 5.25") 2.00 5/25/87 [AGI 2.435]
+ GAME("kq3", "2.00 1987-05-25 5.25\"", "18aad8f7acaaff760720c5c6885b6bab", 0x2440, GID_KQ3),
+
+ // King's Quest 3 (Mac) 2.14 3/15/88
+ // Menus not tested
+ GAME_P("kq3", "2.14 1988-03-15 5.25\"", "7650e659c7bc0f1e9f8a410b7a2e9de6", 0x2440, GID_KQ3, Common::kPlatformMacintosh),
+
+ // King's Quest 3 (PC 3.5") 2.14 3/15/88 [AGI 2.936]
+ GAME("kq3", "2.14 1988-03-15 3.5\"", "d3d17b77b3b3cd13246749231d9473cd", 0x2936, GID_KQ3),
+
+ // King's Quest 3 (CoCo3 158k/360k) [AGI 2.023]
+ GAME_PS("kq3", "", "5a6be7d16b1c742c369ef5cc64fefdd2", 429, 0x2440, GID_KQ3, Common::kPlatformCoCo3),
+
+ // King's Quest 4 (PC 5.25") 2.0 7/27/88 [AGI 3.002.086]
+ GAME3("kq4", "2.0 1988-07-27", "kq4dir", "f50f7f997208ca0e35b2650baec43a2d", 0x3086, GID_KQ4),
+
+ // King's Quest 4 (PC 3.5") 2.0 7/27/88 [AGI 3.002.086]
+ GAME3("kq4", "2.0 1988-07-27 3.5\"", "kq4dir", "fe44655c42f16c6f81046fdf169b6337", 0x3086, GID_KQ4),
+
+ // King's Quest 4 (PC 3.5") 2.2 9/27/88 [AGI 3.002.086]
+ // Menus not tested
+ GAME3("kq4", "2.2 1988-09-27 3.5\"", "kq4dir", "7470b3aeb49d867541fc66cc8454fb7d", 0x3086, GID_KQ4),
+
+ // King's Quest 4 (PC 5.25") 2.3 9/27/88 [AGI 3.002.086]
+ GAME3("kq4", "2.3 1988-09-27", "kq4dir", "6d7714b8b61466a5f5981242b993498f", 0x3086, GID_KQ4),
+
+ // King's Quest 4 (PC 3.5") 2.3 9/27/88 [AGI 3.002.086]
+ GAME3("kq4", "2.3 1988-09-27 3.5\"", "kq4dir", "82a0d39af891042e99ac1bd6e0b29046", 0x3086, GID_KQ4),
+
+ // King's Quest 4 (IIgs) 1.0K 11/22/88 (CE)
+ // Menus not tested
+ GAME3_P("kq4", "1.0K 1988-11-22", "kq4dir", "8536859331159f15012e35dc82cb154e", 0x3086, 0, GID_KQ4, Common::kPlatformApple2GS),
+
+ // King's Quest 4 demo (PC) [AGI 3.002.102]
+ // Menus not tested
+ GAME3("kq4", "Demo 1988-12-20", "dmdir", "a3332d70170a878469d870b14863d0bf", 0x3149, GID_KQ4),
+
+ // King's Quest 4 (CoCo3 720k) [AGI 2.023]
+ GAME_PS("kq4", "", "9e7729a28e749ca241d2bf71b9b2dbde", 741, 0x2440, GID_KQ4, Common::kPlatformCoCo3),
+
+ // King's Quest 4 (CoCo3 360k/720k) [AGI 2.072]
+ GAME_PS("kq4", "updated", "1959ca10739edb34069bb504dbd74805", 741, 0x2440, GID_KQ4, Common::kPlatformCoCo3),
+
+ // Leisure Suit Larry 1 (PC 5.25"/3.5") 1.00 6/1/87 [AGI 2.440]
+ GAME("lsl1", "1.00 1987-06-01 5.25\"/3.5\"", "1fe764e66857e7f305a5f03ca3f4971d", 0x2440, GID_LSL1),
+
+ // Leisure Suit Larry 1 Polish
+ GAME_LPS("lsl1", "2.00 2001-12-11", "7ba1fccc46d27c141e704706c1d0a85f", 303, Common::PL_POL, 0x2440, GID_LSL1, Common::kPlatformPC),
+
+ // Leisure Suit Larry 1 Polish - Demo
+ GAME_LPS("lsl1", "Demo", "3b2f564306c401dff6334441df967ddd", 666, Common::PL_POL, 0x2917, GID_LSL1, Common::kPlatformPC),
+
+ // Leisure Suit Larry 1 (ST) 1.04 6/18/87
+ GAME_P("lsl1", "1.04 1987-06-18", "8b579f8673fe9448c2538f5ed9887cf0", 0x2440, GID_LSL1, Common::kPlatformAtariST),
+
+ // Leisure Suit Larry 1 (Amiga) 1.05 6/26/87 # x.yyy
+ GAME_P("lsl1", "1.05 1987-06-26", "3f5d26d8834ca49c147fb60936869d56", 0x2440, GID_LSL1, Common::kPlatformAmiga),
+
+ // Leisure Suit Larry 1 (IIgs) 1.0E
+ GAME_P("lsl1", "1.0E 1987", "5f9e1dd68d626c6d303131c119582ad4", 0x2440, GID_LSL1, Common::kPlatformApple2GS),
+
+ // Leisure Suit Larry 1 (Mac) 1.05 6/26/87
+ GAME_P("lsl1", "1.05 1987-06-26", "8a0076429890531832f0dc113285e31e", 0x2440, GID_LSL1, Common::kPlatformMacintosh),
+
+ // Leisure Suit Larry 1 (CoCo3 158k/360k) [AGI 2.072]
+ GAME_PS("lsl1", "", "a2de1fe76565c3e8b40c9d036b5e5612", 198, 0x2440, GID_LSL1, Common::kPlatformCoCo3),
+
+ // Manhunter NY (ST) 1.03 10/20/88
+ GAME3_P("mh1", "1.03 1988-10-20", "mhdir", "f2d58056ad802452d60776ee920a52a6", 0x3149, 0, GID_MH1, Common::kPlatformAtariST),
+
+ // Manhunter NY (IIgs) 2.0E 10/05/88 (CE)
+ GAME3_P("mh1", "2.0E 1988-10-05 (CE)", "mhdir", "2f1509f76f24e6e7d213f2dadebbf156", 0x3149, 0, GID_MH1, Common::kPlatformApple2GS),
+
+ // Manhunter NY (Amiga) 1.06 3/18/89
+ GAME3_P("mh1", "1.06 1989-03-18", "dirs", "92c6183042d1c2bb76236236a7d7a847", 0x3149, GF_OLDAMIGAV20, GID_MH1, Common::kPlatformAmiga),
+
+ // reported by Filippos (thebluegr) in bugreport #1654500
+ // Manhunter NY (PC 5.25") 1.22 8/31/88 [AGI 3.002.107]
+ GAME3_PS("mh1", "1.22 1988-08-31", "mhdir", "0c7b86f05fe02c2e26cff1b07450b82a", 2123, 0x3149, 0, GID_MH1, Common::kPlatformPC),
+
+ // Manhunter NY (PC 3.5") 1.22 8/31/88 [AGI 3.002.102]
+ GAME3_PS("mh1", "1.22 1988-08-31", "mhdir", "5b625329021ad49fd0c1d6f2d6f54bba", 2141, 0x3149, 0, GID_MH1, Common::kPlatformPC),
+
+ // Manhunter NY (CoCo3 720k) [AGI 2.023]
+ GAME_PS("mh1", "", "b968285caf2f591c78dd9c9e26ab8974", 495, 0x2440, GID_MH1, Common::kPlatformCoCo3),
+
+ // Manhunter NY (CoCo3 360k/720k) [AGI 2.072]
+ GAME_PS("mh1", "updated", "d47da950c62289f8d4ccf36af73365f2", 495, 0x2440, GID_MH1, Common::kPlatformCoCo3),
+
+ // Manhunter SF (ST) 1.0 7/29/89
+ GAME3_P("mh2", "1.0 1989-07-29", "mh2dir", "5e3581495708b952fea24438a6c7e040", 0x3149, 0, GID_MH1, Common::kPlatformAtariST),
+
+ // Manhunter SF (Amiga) 3.06 8/17/89 # 2.333
+ GAME3_PS("mh2", "3.06 1989-08-17", "dirs", "b412e8a126368b76696696f7632d4c16", 2573, 0x3086, GF_OLDAMIGAV20, GID_MH2, Common::kPlatformAmiga),
+
+ // Manhunter SF (PC 5.25") 3.03 8/17/89 [AGI 3.002.149]
+ GAME3("mh2", "3.03 1989-08-17 5.25\"", "mh2dir", "b90e4795413c43de469a715fb3c1fa93", 0x3149, GID_MH2),
+
+ // Manhunter SF (PC 3.5") 3.02 7/26/89 [AGI 3.002.149]
+ GAME3("mh2", "3.02 1989-07-26 3.5\"", "mh2dir", "6fb6f0ee2437704c409cf17e081ba152", 0x3149, GID_MH2),
+
+ // Manhunter SF (CoCo3 720k) [AGI 2.023]
+ GAME_PS("mh2", "", "acaaa577e10d1753c5a74f6ae1d858d4", 591, 0x2440, GID_MH2, Common::kPlatformCoCo3),
+
+ // Manhunter SF (CoCo3 720k) [AGI 2.072]
+ GAME_PS("mh2", "updated", "c64875766700196e72a92359f70f45a9", 591, 0x2440, GID_MH2, Common::kPlatformCoCo3),
+
+ // Mickey's Space Adventure
+ // Preagi game
+ GAMEpre_P("mickey", "", "1.pic", "b6ec04c91a05df374792872c4d4ce66d", 0x0000, GID_MICKEY, Common::kPlatformPC),
+
+#if 0
+ // Mixed-Up Mother Goose (Amiga) 1.1
+ // Problematic: crashes
+ // Menus not tested
+ GAME3_PS("mixedup", "1.1 1986-12-10", "dirs", "5c1295fe6daaf95831195ba12894dbd9", 2021, 0x3086, 0, GID_MIXEDUP, Common::kPlatformAmiga),
+#endif
+
+ // Mixed Up Mother Goose (IIgs)
+ GAME_P("mixedup", "1987", "3541954a7303467c6df87665312ffb6a", 0x2917, GID_MIXEDUP, Common::kPlatformApple2GS),
+
+ // Mixed-Up Mother Goose (PC) [AGI 2.915]
+ GAME("mixedup", "1987-11-10", "e524655abf9b96a3b179ffcd1d0f79af", 0x2917, GID_MIXEDUP),
+
+ // Mixed-Up Mother Goose (CoCo3 360k) [AGI 2.072]
+ GAME_PS("mixedup", "", "44e63e9b4d4822a31edea0e8a7e7eac4", 606, 0x2440, GID_MIXEDUP, Common::kPlatformCoCo3),
+
+ // Police Quest 1 (PC) 2.0E 11/17/87 [AGI 2.915]
+ GAME("pq1", "2.0E 1987-11-17", "2fd992a92df6ab0461d5a2cd83c72139", 0x2917, GID_PQ1),
+
+ // Police Quest 1 (Mac) 2.0G 12/3/87
+ GAME_P("pq1", "2.0G 1987-12-03", "805750b66c1c5b88a214e67bfdca17a1", 0x2440, GID_PQ1, Common::kPlatformMacintosh),
+
+ // Police Quest 1 (IIgs) 2.0B-88421
+ GAME_P("pq1", "2.0B 1988-04-21", "e7c175918372336461e3811d594f482f", 0x2917, GID_PQ1, Common::kPlatformApple2GS),
+
+ // Police Quest 1 (Amiga) 2.0B 2/22/89 # 2.310
+ GAME3_PS("pq1", "2.0B 1989-02-22", "dirs", "cfa93e5f2aa7378bddd10ad6746a2ffb", 1613, 0x3149, 0, GID_PQ1, Common::kPlatformAmiga),
+
+ // Police Quest 1 (IIgs) 2.0A-88318
+ GAME_P("pq1", "2.0A 1988-03-18", "8994e39d0901de3d07cecfb954075bb5", 0x2917, GID_PQ1, Common::kPlatformApple2GS),
+
+ // Police Quest 1 (PC) 2.0A 10/23/87 [AGI 2.903/2.911]
+ GAME("pq1", "2.0A 1987-10-23", "b9dbb305092851da5e34d6a9f00240b1", 0x2917, GID_PQ1),
+
+ // Police Quest 1 (Russian)
+ GAME_LPS("pq1", "", "604cc8041d24c4c7e5fa8baf386ef76e", 360, Common::RU_RUS, 0x2917, GID_PQ1, Common::kPlatformPC),
+
+ // Police Quest 1 2.0G 12/3/87
+ GAME("pq1", "2.0G 1987-12-03 5.25\"/ST", "231f3e28170d6e982fc0ced4c98c5c1c", 0x2440, GID_PQ1),
+
+ // Police Quest 1 (PC) 2.0G 12/3/87; entry from DAGII, but missing from Sarien?
+ // not sure about disk format -- dsymonds
+ GAME("pq1", "2.0G 1987-12-03", "d194e5d88363095f55d5096b8e32fbbb", 0x2917, GID_PQ1),
+
+ // Police Quest 1 (CoCo3 360k) [AGI 2.023]
+ GAME_PS("pq1", "", "28a077041f75aab78f66804800940085", 375, 0x2440, GID_PQ1, Common::kPlatformCoCo3),
+
+ // Police Quest 1 (CoCo3 360k) [AGI 2.072]
+ GAME_PS("pq1", "updated", "63b9a9c6eec154751dd446cd3693e0e2", 768, 0x2440, GID_PQ1, Common::kPlatformCoCo3),
+
+ // Space Quest 1 (ST) 1.1A
+ // The original game did not have menus, they are enabled under ScummVM
+ GAME_FP("sq1", "1.1A 1986-02-06", "6421fb64b0e6604c9dd065975d9279e9", 0x2440, GF_MENUS, GID_SQ1, Common::kPlatformAtariST),
+
+ // Space Quest 1 (PC 360k) 1.1A [AGI 2.272]
+ // The original game did not have menus, they are enabled under ScummVM
+ GAME_FP("sq1", "1.1A 1986-11-13", "8d8c20ab9f4b6e4817698637174a1cb6", 0x2272, GF_MENUS, GID_SQ1, Common::kPlatformPC),
+
+ // Space Quest 1 (PC 720k) 1.1A [AGI 2.272]
+ // The original game did not have menus, they are enabled under ScummVM
+ GAME_FP("sq1", "1.1A 720kb", "0a92b1be7daf3bb98caad3f849868aeb", 0x2272, GF_MENUS, GID_SQ1, Common::kPlatformPC),
+
+ // Space Quest 1 (Amiga) 1.2 # 2.082
+ // The original game did not have menus, they are enabled under ScummVM
+ GAME_FP("sq1", "1.2 1986", "0b216d931e95750f1f4837d6a4b821e5", 0x2440, GF_MENUS | GF_OLDAMIGAV20, GID_SQ1, Common::kPlatformAmiga),
+
+ // Space Quest 1 (Mac) 1.5D
+ GAME_P("sq1", "1.5D 1987-04-02", "ce88419aadd073d1c6682d859b3d8aa2", 0x2440, GID_SQ1, Common::kPlatformMacintosh),
+
+ // Space Quest 1 (IIgs) 2.2
+ GAME_P("sq1", "2.2 1987", "64b9b3d04c1066d36e6a6e56187a83f7", 0x2917, GID_SQ1, Common::kPlatformApple2GS),
+
+ // Space Quest 1 (PC) 1.0X [AGI 2.089]
+ // Does not have menus, crashes if menus are enforced. Therefore, ESC pauses the game
+ GAME_FP("sq1", "1.0X 1986-09-24", "af93941b6c51460790a9efa0e8cb7122", 0x2089, GF_ESCPAUSE, GID_SQ1, Common::kPlatformPC),
+
+ // Space Quest 1 (Russian)
+ GAME_LFPS("sq1", "", "a279eb8ddbdefdb1ea6adc827a1d632a", 372, Common::RU_RUS, 0x2089, GF_ESCPAUSE, GID_SQ1, Common::kPlatformPC),
+
+ // Space Quest 1 (PC 5.25"/3.5") 2.2 [AGI 2.426/2.917]
+ GAME("sq1", "2.2 1987-05-07 5.25\"/3.5\"", "5d67630aba008ec5f7f9a6d0a00582f4", 0x2440, GID_SQ1),
+
+ // Space Quest 1 (CoCo3 360k) [AGI 2.072]
+ GAME_PS("sq1", "", "5d67630aba008ec5f7f9a6d0a00582f4", 372, 0x2440, GID_SQ1, Common::kPlatformCoCo3),
+
+ // Space Quest 1 (CoCo3 360k) [AGI 2.023]
+ GAME_PS("sq1", "fixed", "ca822b768b6462e410423ea7f498daee", 768, 0x2440, GID_SQ1, Common::kPlatformCoCo3),
+
+ // Space Quest 1 (CoCo3 360k) [AGI 2.072]
+ GAME_PS("sq1", "updated", "7fa54e6bb7ffeb4cf20eca39d86f5fb2", 387, 0x2440, GID_SQ1, Common::kPlatformCoCo3),
+
+ // Space Quest 2 (PC 3.5") 2.0D [AGI 2.936]
+ GAME("sq2", "2.0D 1988-03-14 3.5\"", "85390bde8958c39830e1adbe9fff87f3", 0x2936, GID_SQ2),
+
+ // Space Quest 2 (IIgs) 2.0A 7/25/88 (CE)
+ GAME_P("sq2", "2.0A 1988-07-25 (CE)", "5dfdac98dd3c01fcfb166529f917e911", 0x2936, GID_SQ2, Common::kPlatformApple2GS),
+
+ {
+ // Space Quest 2 (Amiga) 2.0F
+ {
+ "sq2",
+ "2.0F 1986-12-09 [VOL.2->PICTURE.16 broken]",
+ {
+ { "logdir", 0, "28add5125484302d213911df60d2aded", 426},
+ { "object", 0, "5dc52be721257719f4b311a84ce22b16", 372},
+ { NULL, 0, NULL, 0}
+ },
+ Common::EN_ANY,
+ Common::kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GID_SQ2,
+ GType_V2,
+ 0,
+ 0x2936,
+ },
+
+
+ // Space Quest 2 (Mac) 2.0D
+ GAME_P("sq2", "2.0D 1988-04-04", "bfbebe0b59d83f931f2e1c62ce9484a7", 0x2936, GID_SQ2, Common::kPlatformMacintosh),
+
+ // reported by Filippos (thebluegr) in bugreport #1654500
+ // Space Quest 2 (PC 5.25") 2.0A [AGI 2.912]
+ GAME_PS("sq2", "2.0A 1987-11-06 5.25\"", "ad7ce8f800581ecc536f3e8021d7a74d", 423, 0x2917, GID_SQ2, Common::kPlatformPC),
+
+ // Space Quest 2 (Russian)
+ GAME_LPS("sq2", "", "ba21c8934caf28e3ba45ce7d1cd6b041", 423, Common::RU_RUS, 0x2917, GID_SQ2, Common::kPlatformPC),
+
+ // Space Quest 2 (PC 3.5") 2.0A [AGI 2.912]
+ GAME_PS("sq2", "2.0A 1987-11-06 3.5\"", "6c25e33d23b8bed42a5c7fa63d588e5c", 423, 0x2917, GID_SQ2, Common::kPlatformPC),
+
+ // Space Quest 2 (PC 5.25"/ST) 2.0C/A [AGI 2.915]
+ // Menus not tested
+ GAME("sq2", "2.0C/A 5.25\"/ST", "bd71fe54869e86945041700f1804a651", 0x2917, GID_SQ2),
+
+ // Space Quest 2 (PC 3.5") 2.0F [AGI 2.936]
+ GAME("sq2", "2.0F 1989-01-05 3.5\"", "28add5125484302d213911df60d2aded", 0x2936, GID_SQ2),
+
+ // Space Quest 2 (CoCo3 360k) [AGI 2.023]
+ GAME_PS("sq2", "", "12973d39b892dc9d280257fd271e9597", 768, 0x2440, GID_SQ2, Common::kPlatformCoCo3),
+
+ // Space Quest 2 (CoCo3 360k) [AGI 2.072]
+ GAME_PS("sq2", "updated", "d24f19b047e65e1763eff4b46f3d50df", 768, 0x2440, GID_SQ2, Common::kPlatformCoCo3),
+
+ // Troll's Tale
+ GAMEpre_PS("troll", "", "troll.img", "62903f264b3d849be4214b3a5c42a2fa", 184320, 0x0000, GID_TROLL, Common::kPlatformPC),
+
+ // Winnie the Pooh in the Hundred Acre Wood
+ GAMEpre_P("winnie", "", "title.pic", "2e7900c1ccaa7671d65405f6d1efed30", 0x0000, GID_WINNIE, Common::kPlatformPC),
+
+ // Winnie the Pooh in the Hundred Acre Wood (Amiga)
+ GAMEpre_P("winnie", "", "title", "2e7900c1ccaa7671d65405f6d1efed30", 0x0000, GID_WINNIE, Common::kPlatformAmiga),
+
+ // Winnie the Pooh in the Hundred Acre Wood (C64)
+ GAMEpre_P("winnie", "", "title.pic", "d4eb97cffc866110f71e1ec9f84fe643", 0x0000, GID_WINNIE, Common::kPlatformC64),
+
+ // Winnie the Pooh in the Hundred Acre Wood (Apple //gs)
+ GAMEpre_P("winnie", "", "title.pic", "45e06010a3c61d78f4661103c901ae11", 0x0000, GID_WINNIE, Common::kPlatformApple2GS),
+
+ // Xmas Card 1986 (PC) [AGI 2.272]
+ GAME("xmascard", "1986-11-13 [version 1]", "3067b8d5957e2861e069c3c0011bd43d", 0x2272, GID_XMASCARD),
+
+ // Xmas Card 1986 (CoCo3 360k) [AGI 2.072]
+ GAME_PS("xmascard", "", "25ad35e9628fc77e5e0dd35852a272b6", 768, 0x2440, GID_XMASCARD, Common::kPlatformCoCo3),
+
+ FANMADE_F("2 Player Demo", "4279f46b3cebd855132496476b1d2cca", GF_AGIMOUSE),
+ FANMADE("AGI Contest 1 Template", "d879aed25da6fc655564b29567358ae2"),
+ FANMADE("AGI Contest 2 Template", "5a2fb2894207eff36c72f5c1b08bcc07"),
+ FANMADE("AGI Mouse Demo 0.60 demo 1", "c07e2519de674c67386cb2cc6f2e3904"),
+ FANMADE("AGI Mouse Demo 0.60 demo 2", "cc49d8b88ed6faf4f53ce92c84e0fe1b"),
+ FANMADE("AGI Mouse Demo 0.70", "3497c291e4afb6f758e61740678a2aec"),
+ FANMADE_F("AGI Mouse Demo 1.00", "20397f0bf0ef936f416bb321fb768fc7", GF_AGIMOUSE),
+ FANMADE_F("AGI Mouse Demo 1.10", "f4ad396b496d6167635ad0b410312ab8", GF_AGIMOUSE|GF_AGIPAL),
+ FANMADE("AGI Piano (v1.0)", "8778b3d89eb93c1d50a70ef06ef10310"),
+ FANMADE("AGI Quest (v1.46-TJ0)", "1cf1a5307c1a0a405f5039354f679814"),
+ GAME("tetris", "", "7a874e2db2162e7a4ce31c9130248d8a", 0x2917, GID_FANMADE),
+ FANMADE_V("AGI Trek (Demo)", "c02882b8a8245b629c91caf7eb78eafe", 0x2440),
+ FANMADE_F("AGI256 Demo", "79261ac143b2e2773b2753674733b0d5", GF_AGI256),
+ FANMADE_F("AGI256-2 Demo", "3cad9b3aff1467cebf0c5c5b110985c5", GF_AGI256_2),
+ FANMADE_LF("Abrah: L'orphelin de l'espace (v1.2)", "b7b6d1539e14d5a26fa3088288e1badc", Common::FR_FRA, GF_AGIPAL),
+ FANMADE("Acidopolis", "7017db1a4b726d0d59e65e9020f7d9f7"),
+ FANMADE("Agent 0055 (v1.0)", "c2b34a0c77acb05482781dda32895f24"),
+ FANMADE("Agent 06 vs. The Super Nazi", "136f89ca9f117c617e88a85119777529"),
+ FANMADE("Agent Quest", "59e49e8f72058a33c00d60ee1097e631"),
+ FANMADE("Al Pond - On Holiday (v1.0)", "a84975496b42d485920e886e92eed68b"),
+ FANMADE("Al Pond - On Holiday (v1.1)", "7c95ac4689d0c3bfec61e935f3093634"),
+ FANMADE("Al Pond - On Holiday (v1.3)", "8f30c260de9e1dd3d8b8f89cc19d2633"),
+ FANMADE("Al Pond 1 - Al Lives Forever (v1.0)", "e8921c3043b749b056ff51f56d1b451b"),
+ FANMADE("Al Pond 1 - Al Lives Forever (v1.3)", "fb4699474054962e0dbfb4cf12ca52f6"),
+ FANMADE("Apocalyptic Quest (v0.03 Teaser)", "42ced528b67965d3bc3b52c635f94a57"),
+ FANMADE_F("Apocalyptic Quest (v4.00 Alpha 1)", "e15581628d84949b8d352d224ec3184b", GF_AGIMOUSE),
+ FANMADE_F("Apocalyptic Quest (v4.00 Alpha 2)", "0eee850005860e46345b38fea093d194", GF_AGIMOUSE),
+ FANMADE_F("Band Quest (Demo)", "7326abefd793571cc17ed0db647bdf34", GF_AGIMOUSE),
+ FANMADE_F("Band Quest (Early Demo)", "de4758dd34676b248c8301b32d93bc6f", GF_AGIMOUSE),
+ FANMADE("Beyond the Titanic 2", "9b8de38dc64ffb3f52b7877ea3ebcef9"),
+ FANMADE("Biri Quest 1", "1b08f34f2c43e626c775c9d6649e2f17"),
+ FANMADE("Bob The Farmboy", "e4b7df9d0830addee5af946d380e66d7"),
+ FANMADE_F("Boring Man 1: The Toad to Robinland", "d74481cbd227f67ace37ce6a5493039f", GF_AGIMOUSE),
+ FANMADE_F("Boring Man 2: Ho Man! This Game Sucks!", "250032ba105bdf7c1bc4fed767c2d37e", GF_AGIMOUSE),
+ FANMADE("Botz", "a8fabe4e807adfe5ec02bfec6d983695"),
+ FANMADE("Brian's Quest (v1.0)", "0964aa79b9cdcff7f33a12b1d7e04b9c"),
+ FANMADE("CPU-21 (v1.0)", "35b7cdb4d17e890e4c52018d96e9cbf4"),
+ GAME("caitlyn", "Demo", "5b8a3cdb2fc05469f8119d49f50fbe98", 0x2917, GID_FANMADE),
+ GAME("caitlyn", "", "818469c484cae6dad6f0e9a353f68bf8", 0x2917, GID_FANMADE),
+ FANMADE("Car Driver (v1.1)", "2311611d2d36d20ccc9da806e6cba157"),
+ FANMADE("Cloak of Darkness (v1.0)", "5ba6e18bf0b53be10db8f2f3831ee3e5"),
+ FANMADE("Coco Coq (English) - Coco Coq In Grostesteing's Base (v.1.0.3)", "97631f8e710544a58bd6da9e780f9320"),
+ FANMADE_L("Coco Coq (French) - Coco Coq Dans la Base de Grostesteing (v1.0.2)", "ef579ebccfe5e356f9a557eb3b2d8649", Common::FR_FRA),
+ FANMADE("Corby's Murder Mystery (v1.0)", "4ebe62ac24c5a8c7b7898c8eb070efe5"),
+ FANMADE_F("DG: The AGIMouse Adventure (English v1.1)", "efe453b92bc1487ea69fbebede4d5f26", GF_AGIMOUSE|GF_AGIPAL),
+ FANMADE_LF("DG: The AGIMouse Adventure (French v1.1)", "eb3d17ca466d672cbb95947e8d6e846a", Common::FR_FRA, GF_AGIMOUSE|GF_AGIPAL),
+ FANMADE("DG: The Adventure Game (English v1.1)", "0d6376d493fa7a21ec4da1a063e12b25"),
+ FANMADE_L("DG: The Adventure Game (French v1.1)", "258bdb3bb8e61c92b71f2f456cc69e23", Common::FR_FRA),
+ FANMADE("Dashiki (16 Colors)", "9b2c7b9b0283ab9f12bedc0cb6770a07"),
+ FANMADE_F("Dashiki (256 Colors)", "c68052bb209e23b39b55ff3d759958e6", GF_AGIMOUSE|GF_AGI256),
+ FANMADE("Date Quest 1 (v1.0)", "ba3dcb2600645be53a13170aa1a12e69"),
+ FANMADE("Date Quest 2 (v1.0 Demo)", "1602d6a2874856e928d9a8c8d2d166e9"),
+ FANMADE("Date Quest 2 (v1.0)", "f13f6fc85aa3e6e02b0c20408fb63b47"),
+ FANMADE("Dave's Quest (v0.07)", "f29c3660de37bacc1d23547a167f27c9"),
+ FANMADE("Dave's Quest (v0.17)", "da3772624cc4a86f7137db812f6d7c39"),
+ FANMADE("Disco Nights (Demo)", "dc5a2b21182ba38bdcd992a3a978e690"),
+ FANMADE("Dogs Quest - The Quest for the Golden Bone (v1.0)", "f197357edaaea0ff70880602d2f09b3e"),
+ FANMADE("Dr. Jummybummy's Space Adventure", "988bd81785f8a452440a2a8ac67f96aa"),
+ FANMADE("Ed Ward", "98be839b9f30cbedea4c9cee5442d827"),
+ FANMADE("Elfintard", "c3b847e9e9e978af9708df76a0751dc2"),
+ FANMADE("Enclosure (v1.01)", "f08e66fee9ecdde77db7ee9a10c96ba2"),
+ FANMADE("Enclosure (v1.03)", "e4a0613ed02401502e506ba3565a8c40"),
+ FANMADE_SVP("Enclosure", "fe98e6126db74c6cc6fd8fe395cc6e8c", 345, 0x2440, Common::kPlatformCoCo3),
+ FANMADE("Epic Fighting (v0.1)", "aff24a1b3bdd676187685c4d95ba4294"),
+ FANMADE("Escape Quest (v0.0.3)", "2346b65619b1da0298b715b06d1a45a1"),
+ FANMADE("Escape from the Desert (beta 1)", "dfdc634d340854bd6ece28024010758d"),
+ FANMADE("Escape from the Salesman", "e723ca4fe0f6f56affe039fbb4dbeb6c"),
+ FANMADE("Fu$k Quest 1 (final)", "1cd0587422313f6ca77d6a95988e88ed"),
+ FANMADE("Fu$k Quest 1", "1cd0587422313f6ca77d6a95988e88ed"),
+ FANMADE("Fu$k Quest 2 - Romancing the Bone (Teaser)", "d288355d71d9bb1639260ccaa3b2fbfe"),
+ FANMADE("Fu$k Quest 2 - Romancing the Bone", "294beeb7765c7ea6b05ed7b9bf7bff4f"),
+ FANMADE("Gennadi Tahab Autot - Mission Pack 1 - Kuressaare", "bfa5fe71978e6ccf3d4eedd430124015"),
+ FANMADE("Go West, Young Hippie", "ff31484ea465441cb5f3a0f8e956b716"),
+ FANMADE("Good Man (demo v3.41)", "3facd8a8f856b7b6e0f6c3200274d88c"),
+
+ {
+ // Groza
+ {
+ "agi-fanmade",
+ "Groza (russian) [AGDS sample]",
+ AD_ENTRY1("logdir", "421da3a18004122a966d64ab6bd86d2e"),
+ Common::RU_RUS,
+ Common::kPlatformPC,
+ ADGF_USEEXTRAASTITLE,
+ GUIO_NONE
+ },
+ GID_FANMADE,
+ GType_V2,
+ GF_AGDS,
+ 0x2440,
+ },
+
+ {
+ // Get Outta SQ
+ {
+ "agi-fanmade",
+ "Get Outta Space Quest",
+ AD_ENTRY1("logdir", "aaea5b4a348acb669d13b0e6f22d4dc9"),
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_USEEXTRAASTITLE,
+ GUIO_NONE
+ },
+ GID_GETOUTTASQ,
+ GType_V2,
+ 0,
+ 0x2440,
+ },
+
+ FANMADE_F("Half-Death - Terror At White-Mesa", "b62c05d0ace878261392073f57ae788c", GF_AGIMOUSE),
+ FANMADE("Hank's Quest (v1.0 English) - Victim of Society", "64c15b3d0483d17888129100dc5af213"),
+ FANMADE("Hank's Quest (v1.1 English) - Victim of Society", "86d1f1dd9b0c4858d096e2a60cca8a14"),
+ FANMADE_L("Hank's Quest (v1.81 Dutch) - Slachtoffer Van Het Gebeuren", "41e53972d55ff3dff9e90d15fe1b659f", Common::NL_NLD),
+ FANMADE("Hank's Quest (v1.81 English) - Victim of Society", "7a776383282f62a57c3a960dafca62d1"),
+ FANMADE("Herbao (v0.2)", "6a5186fc8383a9060517403e85214fc2"),
+ FANMADE_F("Hitler's Legacy (v.0004q)", "a412881269ba34584bd0a3268e5a9863", GF_AGIMOUSE),
+ FANMADE("Hobbits", "4a1c1ef3a7901baf0ab45fde0cfadd89"),
+ FANMADE_F("Isabella Coq - A Present For My Dad", "55c6819f2330c4d5d6459874c9f123d9", GF_AGIMOUSE),
+ FANMADE("Jack & Julia - VAMPYR", "8aa0b9a26f8d5a4421067ab8cc3706f6"),
+ FANMADE("Jeff's Quest (v.5 alpha Jun 1)", "10f1720eed40c12b02a0f32df3e72ded"),
+ FANMADE("Jeff's Quest (v.5 alpha May 31)", "51ff71c0ed90db4e987a488ed3bf0551"),
+ FANMADE("Jen's Quest (Demo 1)", "361afb5bdb6160213a1857245e711939"),
+ FANMADE("Jen's Quest (Demo 2)", "3c321eee33013b289ab8775449df7df2"),
+ FANMADE("Jiggy Jiggy Uh! Uh!", "bc331588a71e7a1c8840f6cc9b9487e4"),
+ FANMADE("Jimmy In: The Alien Attack (v0.1)", "a4e9db0564a494728de7873684a4307c"),
+ FANMADE("Joe McMuffin In \"What's Cooking, Doc\" (v1.0)", "8a3de7e61a99cb605fa6d233dd91c8e1"),
+ FANMADE_LVF("Jolimie, le Village Maudit (v0.5)", "21818501636b3cb8ad5de5c1a66de5c2", Common::FR_FRA, 0x2936, GF_AGIMOUSE|GF_AGIPAL),
+ FANMADE_LVF("Jolimie, le Village Maudit (v1.1)", "68d7aef1161bb5972fe03efdf29ccb7f", Common::FR_FRA, 0x2936, GF_AGIMOUSE|GF_AGIPAL),
+ FANMADE("Journey Of Chef", "aa0a0b5a6364801ae65fdb96d6741df5"),
+ FANMADE("Jukebox (v1.0)", "c4b9c5528cc67f6ba777033830de7751"),
+ FANMADE("Justin Quest (v1.0 in development)", "103050989da7e0ffdc1c5e1793a4e1ec"),
+ FANMADE("J\xf5ulumaa (v0.05) (Estonian)", "53982ecbfb907e41392b3961ad1c3475"),
+ FANMADE("Kings Quest 2 - Breast Intentions (v2.0 Mar 26)", "a25d7379d281b1b296d4785df90a8e78"),
+ FANMADE("Kings Quest 2 - Breast Intentions (v2.0 Aug 16)", "6b4f796d0421d2e12e501b511962e03a"),
+ FANMADE("Lasse Holm: The Quest for Revenge (v1.0)", "f9fbcc8a4ef510bfbb92423296ff4abb"),
+ FANMADE("Lawman for Hire", "c78b28bfd3767dd455b992cd8b7854fa"),
+ FANMADE("Lefty Goes on Vacation (Not in The Right Place)", "ccdc49a33870310b01f2c48b8a1f3c34"),
+ FANMADE("Les Ins\xe3parables (v1.0)", "4b780887cab0ecabc5eca319acb3acf2"),
+ FANMADE("Little Pirate (Demo 2 v0.6)", "437068efe4ec32d436da09d6f2ea56e1"),
+ FANMADE("Lost Eternity (v1.0)", "95f15c5632feb8a39e9ca3d9af35fcc9"),
+ FANMADE("MD Quest - The Search for Michiel (v0.10)", "2a6fcb21d2b5e4144c38ed817fabe8ee"),
+ FANMADE("Maale Adummin Quest", "ddfbeb33feb7cf78504fe4dba14ec63b"),
+ FANMADE("Monkey Man", "2322d03f997e8cc235d4578efff69cfa"),
+ FANMADE_F("Napalm Quest (v0.5)", "b659afb491d967bb34810d1c6ce22093", GF_AGIMOUSE),
+ FANMADE("Naturette 1 (English v1.2)", "0a75884e7f010974a230bdf269651117"),
+ FANMADE("Naturette 1 (English v1.3)", "f15bbf999ac55ebd404aa1eb84f7c1d9"),
+ FANMADE_L("Naturette 1 (French v1.2)", "d3665622cc41aeb9c7ecf4fa43f20e53", Common::FR_FRA),
+ FANMADE_F("Naturette 2: Daughter of the Moon (v1.0)", "bdf76a45621c7f56d1c9d40292c6137a", GF_AGIMOUSE|GF_AGIPAL),
+ FANMADE_F("Naturette 3: Adventure in Treeworld (v1.0a)", "6dbb0e7fc75fec442e6d9e5a06f1530e", GF_AGIMOUSE|GF_AGIPAL),
+ FANMADE_F("Naturette 4: From a Planet to Another Planet (Not Finished)", "13be8cd9cf35aeff0a39b8757057fbc8", GF_AGIMOUSE),
+ // FIXME: Actually Naturette 4 has both English and French language support built into it. How to add that information?
+ FANMADE_F("Naturette 4: From a Planet to Another Planet (2007-10-05)", "8253706b6ef5423a79413b216760297c", GF_AGIMOUSE|GF_AGIPAL),
+ FANMADE("New AGI Hangman Test", "d69c0e9050ccc29fd662b74d9fc73a15"),
+ FANMADE("Nick's Quest - In Pursuit of QuakeMovie (v2.1 Gold)", "e29cbf9222551aee40397fabc83eeca0"),
+ FANMADE_F("Open Mic Night (v0.1)", "70000a2f67aac27d1133d019df70246d", GF_AGIMOUSE|GF_AGIPAL),
+ FANMADE("Operation: Recon", "0679ce8405411866ccffc8a6743370d0"),
+ FANMADE("Patrick's Quest (Demo v1.0)", "f254f5b894b98fec5f92acc07fb62841"),
+ FANMADE("Phantasmagoria", "87d20c1c11aee99a4baad3797b63146b"),
+ FANMADE("Pharaoh Quest (v0.0)", "51c630899d076cf799e573dadaa2276d"),
+ FANMADE("Phil's Quest - the Search for Tolbaga", "5e7ca45c360e03164b8358e49900c588"),
+ FANMADE("Pinkun Maze Quest (v0.1)", "148ff0843af389928b3939f463bfd20d"),
+ FANMADE("Pirate Quest", "bb612a919ed2b9ea23bbf03ce69fed42"),
+ FANMADE("Pothead (v0.1)", "d181101385d3a45082f418cd4b3c5b01"),
+ FANMADE("President's Quest", "4937d0e8ecadb7888faeb347799b0388"),
+ FANMADE("Prince Quest", "266248d75c3130c8ccc9c9bf2ad30a0d"),
+ FANMADE("Professor (English) - The Professor is Missing (Mar 17)", "6232de31cc204affdf2e92dfe3dc0e4d"),
+ FANMADE("Professor (English) - The Professor is Missing (Mar 22)", "b5fcf0ca2f0d1c073be82f01e2170961"),
+ FANMADE_L("Professor (French) - Le Professeur a Disparu", "7d9f8a4d4610bb9b0b97caa17590c2d3", Common::FR_FRA),
+ FANMADE("Quest for Glory VI - Hero's Adventure", "d26765c3075064c80d284c5e06e33a7e"),
+ FANMADE("Quest for Home", "d2895dc1cd3930f2489af0f843b144b3"),
+ FANMADE("Quest for Ladies (demo v1.1 Apr 1)", "3f6e02f16e1154a0daf296c8895edd97"),
+ FANMADE("Quest for Ladies (demo v1.1 Apr 6)", "f75e7b6a0769a3fa926eea0854711591"),
+ FANMADE("Quest for Piracy 1 - Enter the Silver Pirate (v0.15)", "d23f5c2a26f6dc60c686f8a2436ea4a6"),
+ FANMADE("Quest for a Record Deal", "f4fbd7abf056d2d3204f790da5ac89ab"),
+ FANMADE("Ralph's Quest (v0.1)", "5cf56378aa01a26ec30f25295f0750ca"),
+ FANMADE("Residence 44 Quest (Dutch v0.99)", "7c5cc64200660c70240053b33d379d7d"),
+ FANMADE("Residence 44 Quest (English v0.99)", "fe507851fddc863d540f2bec67cc67fd"),
+ FANMADE("Residence 44 Quest (English v1.0a)", "f99e3f69dc8c77a45399da9472ef5801"),
+ FANMADE("SQ2Eye (v0.3)", "2be2519401d38ad9ce8f43b948d093a3"),
+ // FANMADE("SQ2Eye (v0.4)", "2be2519401d38ad9ce8f43b948d093a3"),
+ FANMADE("SQ2Eye (v0.41)", "f0e82c55f10eb3542d7cd96c107ae113"),
+ FANMADE("SQ2Eye (v0.42)", "d7beae55f6328ef8b2da47b1aafea40c"),
+ FANMADE("SQ2Eye (v0.43)", "2a895f06e45de153bb4b77c982009e06"),
+ FANMADE("SQ2Eye (v0.44)", "5174fc4b6d8a477ba0ff0575cd64e0aa"),
+ FANMADE("SQ2Eye (v0.45)", "6e06f8bb7b90ce6f6aabf1a0e620159c"),
+ FANMADE("SQ2Eye (v0.46)", "bf0ad7a035ff9113951d09d1efe380c4"),
+ FANMADE("SQ2Eye (v0.47)", "85dc3be1d33ff932c292b74f9037abaa"),
+ FANMADE("SQ2Eye (v0.48)", "587574252972a5b5c070a647973a9b4a"),
+ FANMADE("SQ2Eye (v0.481)", "fc9234beb49804ae869696ce5af8ef30"),
+ FANMADE("SQ2Eye (v0.482)", "3ed84b7b87fa6840f25c15f250a11ffb"),
+ FANMADE("SQ2Eye (v0.483)", "647c31298d3f9cda641231b893e347c0"),
+ FANMADE("SQ2Eye (v0.484)", "f2c86fae7b9046d408c62c8c49a4b882"),
+ FANMADE("SQ2Eye (v0.485)", "af59e36bc28f44545458b68a93e91e67"),
+ FANMADE("SQ2Eye (v0.486)", "3fd86436e93456770dbdd4593eded70a"),
+ FANMADE("Save Santa (v1.0)", "4644f6beb5802081772f14be56ae196c"),
+ FANMADE("Save Santa (v1.3)", "f8afdb6efc5af5e7c0228b44633066af"),
+ FANMADE("Schiller (preview 1)", "ade39dea968c959cfebe1cf935d653e9"),
+ FANMADE("Schiller (preview 2)", "62cd1f8fc758bf6b4aa334e553624cef"),
+ GAME_F("serguei1", "v1.0", "b86725f067e456e10cdbdf5f58e01dec", 0x2917, GF_FANMADE|GF_AGIMOUSE|GF_AGIPAL, GID_FANMADE),
+ // FIXME: The following two entries have identical MD5 checksums?
+ GAME_F("serguei1", "v1.1 2002 Sep 5", "91975c1fb4b13b0f9a8e9ff74731030d", 0x2917, GF_FANMADE|GF_AGIMOUSE|GF_AGIPAL, GID_FANMADE),
+ GAME_F("serguei1", "v1.1 2003 Apr 10", "91975c1fb4b13b0f9a8e9ff74731030d", 0x2917, GF_FANMADE|GF_AGIMOUSE|GF_AGIPAL, GID_FANMADE),
+ GAME_F("serguei2", "v0.1.1 Demo", "906ccbc2ddedb29b63141acc6d10cd28", 0x2917, GF_FANMADE|GF_AGIMOUSE, GID_FANMADE),
+ GAME_F("serguei2", "v1.3.1 Demo (March 22nd 2008)", "ad1308fcb8f48723cd388e012ebf5e20", 0x2917, GF_FANMADE|GF_AGIMOUSE|GF_AGIPAL, GID_FANMADE),
+ FANMADE("Shifty (v1.0)", "2a07984d27b938364bf6bd243ac75080"),
+ FANMADE_F("Sliding Tile Game (v1.00)", "949bfff5d8a81c3139152eed4d84ca75", GF_AGIMOUSE),
+ FANMADE("Snowboarding Demo (v1.0)", "24bb8f29f1eddb5c0a099705267c86e4"),
+ FANMADE("Solar System Tour", "b5a3d0f392dfd76a6aa63f3d5f578403"),
+ FANMADE("Sorceror's Appraisal", "fe62615557b3cb7b08dd60c9d35efef1"),
+ GAME("sq0", "v1.03", "d2fd6f7404e86182458494e64375e590", 0x2917, GID_FANMADE),
+ GAME("sq0", "v1.04", "2ad9d1a4624a98571ee77dcc83f231b6", 0x2917, GID_FANMADE),
+ GAME_PS("sq0", "", "e1a8e4efcce86e1efcaa14633b9eb986", 762, 0x2440, GID_FANMADE, Common::kPlatformCoCo3),
+ GAME("sqx", "v10.0 Feb 05", "c992ae2f8ab18360404efdf16fa9edd1", 0x2917, GID_FANMADE),
+ GAME("sqx", "v10.0 Jul 18", "812edec45cefad559d190ffde2f9c910", 0x2917, GID_FANMADE),
+ GAME_PS("sqx", "", "f0a59044475a5fa37c055d8c3eb4d1a7", 768, 0x2440, GID_FANMADE, Common::kPlatformCoCo3),
+ FANMADE_F("Space Quest 3.5", "c077bc28d7b36213dd99dc9ecb0147fc", GF_AGIMOUSE|GF_AGIPAL),
+ FANMADE_F("Space Trek (v1.0)", "807a1aeadb2ace6968831d36ab5ea37a", GF_CLIPCOORDS),
+ FANMADE("Special Delivery", "88764dfe61126b8e73612c851b510a33"),
+ FANMADE("Speeder Bike Challenge (v1.0)", "2deb25bab379285ca955df398d96c1e7"),
+ FANMADE("Star Commander 1 - The Escape (v1.0)", "a7806f01e6fa14ebc029faa58f263750"),
+ FANMADE("Star Pilot: Bigger Fish", "8cb26f8e1c045b75c6576c839d4a0172"),
+ FANMADE_F("Street Quest (Demo)", "cf2aa94a7eb78dce6892c37f03e310d6", GF_AGIPAL),
+ FANMADE("Tales of the Tiki", "8103c9c87e3964690a14a3d0d83f7ddc"),
+ FANMADE("Tex McPhilip 1 - Quest For The Papacy", "3c74b9a24b51aa8020ac82bee3132266"),
+ FANMADE("Tex McPhilip 2 - Road To Divinity (v1.5)", "7387e8df854440bc26620ca0ea43af9a"),
+ FANMADE("Tex McPhilip 3 - A Destiny of Sin (Demo v0.25)", "992d12031a486ad84e592ff5d7c9d782"),
+ FANMADE("The 13th Disciple (v1.00)", "887719ad59afce9a41ec057dbb73ad73"),
+ FANMADE("The Adventures of a Crazed Hermit", "6e3086cbb794d3299a9c5a9792295511"),
+ FANMADE("The Grateful Dead", "c2146631afacf8cb455ce24f3d2d46e7"),
+ FANMADE("The Legend of Shay-Larah 1 - The Lost Prince", "04e720c8e30c9cf12db22ea14a24a3dd"),
+ FANMADE("The Legend of Zelda: The Fungus of Time (Demo v1.00)", "dcaf8166ceb62a3d9b9aea7f3b197c09"),
+ FANMADE("The Legendary Harry Soupsmith (Demo 1998 Apr 2)", "64c46b0d6fc135c9835afa80980d2831"),
+ FANMADE("The Legendary Harry Soupsmith (Demo 1998 Aug 19)", "8d06d82970f2c591d880a95476efbcf0"),
+ FANMADE("The Long Haired Dude: Encounter of the 18-th Kind", "86ea17b9fc2f3e537a7e40863d352c29"),
+ FANMADE("The Lost Planet (v0.9)", "590dffcbd932a9fbe554be13b769cac0"),
+ FANMADE("The Lost Planet (v1.0)", "58564df8b6394612dd4b6f5c0fd68d44"),
+ FANMADE("The New Adventure of Roger Wilco (v1.00)", "e5f0a7cb8d49f66b89114951888ca688"),
+ FANMADE("The Ruby Cast (v0.02)", "ed138e461bb1516e097007e017ab62df"),
+ FANMADE("The Shadow Plan", "c02cd10267e721f4e836b1431f504a0a"),
+ FANMADE("Time Quest (Demo v0.1)", "12e1a6f03ea4b8c5531acd0400b4ed8d"),
+ FANMADE("Time Quest (Demo v0.2)", "7b710608abc99e0861ac59b967bf3f6d"),
+ FANMADE_SVP("Time Quest", "90314f473d8317be5cd1f0306f139aea", 300, 0x2440, Common::kPlatformCoCo3),
+ FANMADE("Tonight The Shrieking Corpses Bleed (Demo v0.11)", "bcc57a7c8d563fa0c333107ae1c0a6e6"),
+ FANMADE("Tonight The Shrieking Corpses Bleed (v1.01)", "36b38f621b38e8d104aa0807302dc8c9"),
+ FANMADE("Turks' Quest - Heir to the Planet", "3d19254b737c8b218e5bc4580542b79a"),
+ FANMADE("URI Quest (v0.173 Feb 27)", "3986eefcf546dafc45f920ae91a697c3"),
+ FANMADE("URI Quest (v0.173 Jan 29)", "494150940d34130605a4f2e67ee40b12"),
+ {
+ // V - The Graphical Adventure
+ {
+ "agi-fanmade",
+ "V - The Graphical Adventure (Demo 2)",
+ AD_ENTRY1s("vdir", "c71f5c1e008d352ae9040b77fcf79327", 3080),
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_USEEXTRAASTITLE,
+ GUIO_NONE
+ },
+ GID_FANMADE,
+ GType_V3,
+ GF_FANMADE,
+ 0x3149,
+ },
+ FANMADE_SVP("V - The Graphical Adventure", "1646eaade74f137a9041eb427a389969", 768, 0x2440, Common::kPlatformCoCo3),
+
+ FANMADE("Voodoo Girl - Queen of the Darned (v1.2 2002 Jan 1)", "ae95f0c77d9a97b61420fd192348b937"),
+ FANMADE("Voodoo Girl - Queen of the Darned (v1.2 2002 Mar 29)", "11d0417b7b886f963d0b36789dac4c8f"),
+ FANMADE("Wizaro (v0.1)", "abeec1eda6eaf8dbc52443ea97ff140c"),
+
+ { AD_TABLE_END_MARKER, 0, 0, 0, 0 }
+};
+
+/**
+ * The fallback game descriptor used by the AGI engine's fallbackDetector.
+ * Contents of this struct are to be overwritten by the fallbackDetector.
+ */
+static AGIGameDescription g_fallbackDesc = {
+ {
+ "",
+ "",
+ AD_ENTRY1(0, 0), // This should always be AD_ENTRY1(0, 0) in the fallback descriptor
+ Common::UNK_LANG,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GID_FANMADE,
+ GType_V2,
+ GF_FANMADE,
+ 0x2917,
+};
+
+} // End of namespace Agi
diff --git a/engines/agi/keyboard.cpp b/engines/agi/keyboard.cpp
index 2bea49a807..62bcd5d8d8 100644
--- a/engines/agi/keyboard.cpp
+++ b/engines/agi/keyboard.cpp
@@ -104,10 +104,10 @@ int AgiEngine::handleController(int key) {
VtEntry *v = &_game.viewTable[0];
int i;
- // AGI 3.149 games and The Black Cauldron need KEY_ESCAPE to use menus
+ // AGI 3.149 games, The Black Cauldron and King's Quest 4 need KEY_ESCAPE to use menus
// Games with the GF_ESCPAUSE flag need KEY_ESCAPE to pause the game
if (key == 0 ||
- (key == KEY_ESCAPE && getVersion() != 0x3149 && getGameID() != GID_BC && !(getFeatures() & GF_ESCPAUSE)) )
+ (key == KEY_ESCAPE && getVersion() != 0x3149 && getGameID() != GID_BC && getGameID() != GID_KQ4 && !(getFeatures() & GF_ESCPAUSE)) )
return false;
if ((getGameID() == GID_MH1 || getGameID() == GID_MH2) && (key == KEY_ENTER) &&
@@ -121,7 +121,7 @@ int AgiEngine::handleController(int key) {
if (_game.controllers[i].keycode == key) {
debugC(3, kDebugLevelInput, "event %d: key press", _game.controllers[i].controller);
_game.controllerOccured[_game.controllers[i].controller] = true;
- report("event AC:%i occured\n", _game.controllers[i].controller);
+ report("event AC:%i occurred\n", _game.controllers[i].controller);
return true;
}
}
@@ -191,9 +191,8 @@ int AgiEngine::handleController(int key) {
}
}
- v->flags &= ~ADJ_EGO_XY;
-
if (d || key == KEY_STATIONARY) {
+ v->flags &= ~ADJ_EGO_XY;
v->direction = v->direction == d ? 0 : d;
return true;
}
@@ -320,7 +319,7 @@ void AgiEngine::handleKeys(int key) {
// Clear to start a new line
_game.hasPrompt = 0;
_game.inputBuffer[_game.cursorPos = 0] = 0;
- debugC(3, kDebugLevelInput, "clear lines");
+ debugC(3, kDebugLevelInput | kDebugLevelText, "clear lines");
clearLines(l, l + 1, bg);
flushLines(l, l + 1);
#ifdef __DS__
diff --git a/engines/agi/loader_v2.cpp b/engines/agi/loader_v2.cpp
index 3d1c4fa2cf..de6f8d0653 100644
--- a/engines/agi/loader_v2.cpp
+++ b/engines/agi/loader_v2.cpp
@@ -232,7 +232,7 @@ int AgiLoader_v2::loadResource(int t, int n) {
if (data != NULL) {
// Freeing of the raw resource from memory is delegated to the createFromRawResource-function
- _vm->_game.sounds[n] = AgiSound::createFromRawResource(data, _vm->_game.dirSound[n].len, n, *_vm->_sound);
+ _vm->_game.sounds[n] = AgiSound::createFromRawResource(data, _vm->_game.dirSound[n].len, n, *_vm->_sound, _vm->_soundemu);
_vm->_game.dirSound[n].flags |= RES_LOADED;
} else {
ec = errBadResource;
diff --git a/engines/agi/loader_v3.cpp b/engines/agi/loader_v3.cpp
index cd97c44521..f145140768 100644
--- a/engines/agi/loader_v3.cpp
+++ b/engines/agi/loader_v3.cpp
@@ -227,19 +227,12 @@ uint8 *AgiLoader_v3::loadVolRes(AgiDir *agid) {
compBuffer = (uint8 *)calloc(1, agid->clen + 32);
fp.read(compBuffer, agid->clen);
- if (x[2] & 0x80 || agid->len == agid->clen) {
+ if (x[2] & 0x80) { // compressed pic
+ data = _vm->_picture->convertV3Pic(compBuffer, agid->clen);
+ // compBuffer has been freed inside convertV3Pic()
+ } else if (agid->len == agid->clen) {
// do not decompress
data = compBuffer;
-
-#if 0
- // CM: added to avoid problems in
- // convert_v2_v3_pic() when clen > len
- // e.g. Sierra demo 4, first picture
- // (Tue Mar 16 13:13:43 EST 1999)
- agid->len = agid->clen;
-
- // Now removed to fix Gold Rush! in demo4
-#endif
} else {
// it is compressed
data = (uint8 *)calloc(1, agid->len + 32);
@@ -309,7 +302,6 @@ int AgiLoader_v3::loadResource(int t, int n) {
unloadResource(rPICTURE, n);
data = loadVolRes(&_vm->_game.dirPic[n]);
if (data != NULL) {
- data = _vm->_picture->convertV3Pic(data, _vm->_game.dirPic[n].len);
_vm->_game.pictures[n].rdata = data;
_vm->_game.dirPic[n].flags |= RES_LOADED;
} else {
@@ -324,7 +316,7 @@ int AgiLoader_v3::loadResource(int t, int n) {
data = loadVolRes(&_vm->_game.dirSound[n]);
if (data != NULL) {
// Freeing of the raw resource from memory is delegated to the createFromRawResource-function
- _vm->_game.sounds[n] = AgiSound::createFromRawResource(data, _vm->_game.dirSound[n].len, n, *_vm->_sound);
+ _vm->_game.sounds[n] = AgiSound::createFromRawResource(data, _vm->_game.dirSound[n].len, n, *_vm->_sound, _vm->_soundemu);
_vm->_game.dirSound[n].flags |= RES_LOADED;
} else {
ec = errBadResource;
diff --git a/engines/agi/module.mk b/engines/agi/module.mk
index f031834c9d..2339d1019f 100644
--- a/engines/agi/module.mk
+++ b/engines/agi/module.mk
@@ -30,6 +30,11 @@ MODULE_OBJS := \
predictive.o \
saveload.o \
sound.o \
+ sound_2gs.o \
+ sound_coco3.o \
+ sound_midi.o \
+ sound_pcjr.o \
+ sound_sarien.o \
sprite.o \
text.o \
view.o \
diff --git a/engines/agi/op_cmd.cpp b/engines/agi/op_cmd.cpp
index d7e3ba416c..072ab0114f 100644
--- a/engines/agi/op_cmd.cpp
+++ b/engines/agi/op_cmd.cpp
@@ -43,221 +43,201 @@ namespace Agi {
#define p5 (p[5])
#define p6 (p[6])
-#define game g_agi->_game
-#define g_sprites g_agi->_sprites
-#define g_sound g_agi->_sound
-#define g_gfx g_agi->_gfx
-#define g_picture g_agi->_picture
+#define ip _curLogic->cIP
+#define vt _game.viewTable[p0]
+#define vt_v _game.viewTable[_game.vars[p0]]
-#define ip curLogic->cIP
-#define vt game.viewTable[p0]
-#define vt_v game.viewTable[game.vars[p0]]
+#define _v _game.vars
-static struct AgiLogic *curLogic;
-
-int timerHack; // Workaround for timer loop in MH1
-
-#define _v game.vars
-#define cmd(x) static void cmd_##x (AgiEngine *g_agi, uint8 *p)
-
-cmd(increment) {
+void AgiEngine::cmd_increment(uint8 *p) {
if (_v[p0] != 0xff)
++_v[p0];
}
-cmd(decrement) {
+void AgiEngine::cmd_decrement(uint8 *p) {
if (_v[p0] != 0)
--_v[p0];
}
-cmd(assignn) {
+void AgiEngine::cmd_assignn(uint8 *p) {
_v[p0] = p1;
- // WORKAROUND for a bug in fan game "Get outta SQ"
+ // WORKAROUND for a bug in fan _game "Get outta SQ"
// Total number of points is stored in variable 7, which
- // is then incorrectly assigned to 0. Thus, when the game
+ // is then incorrectly assigned to 0. Thus, when the _game
// is restarted, "Points 0 of 0" is shown. We set the
// variable to the correct value here
// Fixes bug #1942476 - "AGI: Fan(Get Outta SQ) - Score
// is lost on restart"
- if (g_agi->getGameID() == GID_GETOUTTASQ && p0 == 7)
+ if (getGameID() == GID_GETOUTTASQ && p0 == 7)
_v[p0] = 8;
}
-cmd(addn) {
+void AgiEngine::cmd_addn(uint8 *p) {
_v[p0] += p1;
}
-cmd(subn) {
+void AgiEngine::cmd_subn(uint8 *p) {
_v[p0] -= p1;
}
-cmd(assignv) {
+void AgiEngine::cmd_assignv(uint8 *p) {
_v[p0] = _v[p1];
}
-cmd(addv) {
+void AgiEngine::cmd_addv(uint8 *p) {
_v[p0] += _v[p1];
}
-cmd(subv) {
+void AgiEngine::cmd_subv(uint8 *p) {
_v[p0] -= _v[p1];
}
-cmd(mul_n) {
+void AgiEngine::cmd_mul_n(uint8 *p) {
_v[p0] *= p1;
}
-cmd(mul_v) {
+void AgiEngine::cmd_mul_v(uint8 *p) {
_v[p0] *= _v[p1];
}
-cmd(div_n) {
+void AgiEngine::cmd_div_n(uint8 *p) {
_v[p0] /= p1;
}
-cmd(div_v) {
+void AgiEngine::cmd_div_v(uint8 *p) {
_v[p0] /= _v[p1];
}
-cmd(random) {
- _v[p2] = g_agi->_rnd->getRandomNumber(p1 - p0) + p0;
+void AgiEngine::cmd_random(uint8 *p) {
+ _v[p2] = _rnd->getRandomNumber(p1 - p0) + p0;
}
-cmd(lindirectn) {
+void AgiEngine::cmd_lindirectn(uint8 *p) {
_v[_v[p0]] = p1;
}
-cmd(lindirectv) {
+void AgiEngine::cmd_lindirectv(uint8 *p) {
_v[_v[p0]] = _v[p1];
}
-cmd(rindirect) {
+void AgiEngine::cmd_rindirect(uint8 *p) {
_v[p0] = _v[_v[p1]];
}
-cmd(set) {
- g_agi->setflag(*p, true);
+void AgiEngine::cmd_set(uint8 *p) {
+ setflag(*p, true);
}
-cmd(reset) {
- g_agi->setflag(*p, false);
+void AgiEngine::cmd_reset(uint8 *p) {
+ setflag(*p, false);
}
-cmd(toggle) {
- g_agi->setflag(*p, !g_agi->getflag(*p));
+void AgiEngine::cmd_toggle(uint8 *p) {
+ setflag(*p, !getflag(*p));
}
-cmd(set_v) {
- g_agi->setflag(_v[p0], true);
+void AgiEngine::cmd_set_v(uint8 *p) {
+ setflag(_v[p0], true);
}
-cmd(reset_v) {
- g_agi->setflag(_v[p0], false);
+void AgiEngine::cmd_reset_v(uint8 *p) {
+ setflag(_v[p0], false);
}
-cmd(toggle_v) {
- g_agi->setflag(_v[p0], !g_agi->getflag(_v[p0]));
+void AgiEngine::cmd_toggle_v(uint8 *p) {
+ setflag(_v[p0], !getflag(_v[p0]));
}
-cmd(new_room) {
- g_agi->newRoom(p0);
+void AgiEngine::cmd_new_room(uint8 *p) {
+ newRoom(p0);
// WORKAROUND: Works around intro skipping bug (#1737343) in Gold Rush.
// Intro was skipped because the enter-keypress finalizing the entering
// of the copy protection string (Copy protection is in logic.128) was
// left over to the intro scene (Starts with room 73 i.e. logic.073).
// The intro scene checks for any keys pressed and if it finds any it
- // jumps to the game's start (Room 1 i.e. logic.001). We clear the
+ // jumps to the _game's start (Room 1 i.e. logic.001). We clear the
// keyboard buffer when the intro sequence's first room (Room 73) is
// loaded so that no keys from the copy protection scene can be left
- // over to cause the intro to skip to the game's start.
- if (g_agi->getGameID() == GID_GOLDRUSH && p0 == 73)
- game.keypress = 0;
+ // over to cause the intro to skip to the _game's start.
+ if (getGameID() == GID_GOLDRUSH && p0 == 73)
+ _game.keypress = 0;
}
-cmd(new_room_f) {
- g_agi->newRoom(_v[p0]);
+void AgiEngine::cmd_new_room_f(uint8 *p) {
+ newRoom(_v[p0]);
}
-cmd(load_view) {
- g_agi->agiLoadResource(rVIEW, p0);
+void AgiEngine::cmd_load_view(uint8 *p) {
+ agiLoadResource(rVIEW, p0);
}
-cmd(load_logic) {
- g_agi->agiLoadResource(rLOGIC, p0);
+void AgiEngine::cmd_load_logic(uint8 *p) {
+ agiLoadResource(rLOGIC, p0);
}
-cmd(load_sound) {
- g_agi->agiLoadResource(rSOUND, p0);
+void AgiEngine::cmd_load_sound(uint8 *p) {
+ agiLoadResource(rSOUND, p0);
}
-cmd(load_view_f) {
- g_agi->agiLoadResource(rVIEW, _v[p0]);
+void AgiEngine::cmd_load_view_f(uint8 *p) {
+ agiLoadResource(rVIEW, _v[p0]);
}
-cmd(load_logic_f) {
- g_agi->agiLoadResource(rLOGIC, _v[p0]);
+void AgiEngine::cmd_load_logic_f(uint8 *p) {
+ agiLoadResource(rLOGIC, _v[p0]);
}
-cmd(discard_view) {
- g_agi->agiUnloadResource(rVIEW, p0);
+void AgiEngine::cmd_discard_view(uint8 *p) {
+ agiUnloadResource(rVIEW, p0);
}
-cmd(object_on_anything) {
+void AgiEngine::cmd_object_on_anything(uint8 *p) {
vt.flags &= ~(ON_WATER | ON_LAND);
}
-cmd(object_on_land) {
- debugC(4, kDebugLevelScripts, "p0 = %d", p0);
+void AgiEngine::cmd_object_on_land(uint8 *p) {
vt.flags |= ON_LAND;
}
-cmd(object_on_water) {
- debugC(4, kDebugLevelScripts, "p0 = %d", p0);
+void AgiEngine::cmd_object_on_water(uint8 *p) {
vt.flags |= ON_WATER;
}
-cmd(observe_horizon) {
- debugC(4, kDebugLevelScripts, "p0 = %d", p0);
+void AgiEngine::cmd_observe_horizon(uint8 *p) {
vt.flags &= ~IGNORE_HORIZON;
}
-cmd(ignore_horizon) {
- debugC(4, kDebugLevelScripts, "p0 = %d", p0);
+void AgiEngine::cmd_ignore_horizon(uint8 *p) {
vt.flags |= IGNORE_HORIZON;
}
-cmd(observe_objs) {
- debugC(4, kDebugLevelScripts, "p0 = %d", p0);
+void AgiEngine::cmd_observe_objs(uint8 *p) {
vt.flags &= ~IGNORE_OBJECTS;
}
-cmd(ignore_objs) {
- debugC(4, kDebugLevelScripts, "p0 = %d", p0);
+void AgiEngine::cmd_ignore_objs(uint8 *p) {
vt.flags |= IGNORE_OBJECTS;
}
-cmd(observe_blocks) {
- debugC(4, kDebugLevelScripts, "p0 = %d", p0);
+void AgiEngine::cmd_observe_blocks(uint8 *p) {
vt.flags &= ~IGNORE_BLOCKS;
}
-cmd(ignore_blocks) {
- debugC(4, kDebugLevelScripts, "p0 = %d", p0);
+void AgiEngine::cmd_ignore_blocks(uint8 *p) {
vt.flags |= IGNORE_BLOCKS;
}
-cmd(set_horizon) {
- debugC(4, kDebugLevelScripts, "p0 = %d", p0);
- game.horizon = p0;
+void AgiEngine::cmd_set_horizon(uint8 *p) {
+ _game.horizon = p0;
}
-cmd(get_priority) {
+void AgiEngine::cmd_get_priority(uint8 *p) {
_v[p1] = vt.priority;
}
-cmd(set_priority) {
+void AgiEngine::cmd_set_priority(uint8 *p) {
vt.flags |= FIXED_PRIORITY;
vt.priority = p1;
@@ -268,259 +248,256 @@ cmd(set_priority) {
// It seems that in this scene, ego's priority is set to 8, but the priority of
// the last dwarf with the soup bowls (view 152) is also set to 8, which causes
// the dwarf to be drawn behind ego
- // With this workaround, when the game scripts set the priority of view 152
+ // With this workaround, when the _game scripts set the priority of view 152
// (seventh dwarf with soup bowls), ego's priority is set to 7
- // The game script itself sets priotity 8 for ego before she starts walking,
+ // The _game script itself sets priotity 8 for ego before she starts walking,
// and then releases the fixed priority set on ego after ego is seated
// Therefore, this workaround only affects that specific part of this scene
// Ego is set to object 19 by script 54
- if (g_agi->getGameID() == GID_KQ4 && vt.currentView == 152) {
- game.viewTable[19].flags |= FIXED_PRIORITY;
- game.viewTable[19].priority = 7;
+ if (getGameID() == GID_KQ4 && vt.currentView == 152) {
+ _game.viewTable[19].flags |= FIXED_PRIORITY;
+ _game.viewTable[19].priority = 7;
}
}
-cmd(set_priority_f) {
+void AgiEngine::cmd_set_priority_f(uint8 *p) {
vt.flags |= FIXED_PRIORITY;
vt.priority = _v[p1];
}
-cmd(release_priority) {
+void AgiEngine::cmd_release_priority(uint8 *p) {
vt.flags &= ~FIXED_PRIORITY;
}
-cmd(set_upper_left) { // do nothing (AGI 2.917)
+void AgiEngine::cmd_set_upper_left(uint8 *p) { // do nothing (AGI 2.917)
}
-cmd(start_update) {
- g_agi->startUpdate(&vt);
+void AgiEngine::cmd_start_update(uint8 *p) {
+ startUpdate(&vt);
}
-cmd(stop_update) {
- g_agi->stopUpdate(&vt);
+void AgiEngine::cmd_stop_update(uint8 *p) {
+ stopUpdate(&vt);
}
-cmd(current_view) {
+void AgiEngine::cmd_current_view(uint8 *p) {
_v[p1] = vt.currentView;
}
-cmd(current_cel) {
+void AgiEngine::cmd_current_cel(uint8 *p) {
_v[p1] = vt.currentCel;
debugC(4, kDebugLevelScripts, "v%d=%d", p1, _v[p1]);
}
-cmd(current_loop) {
+void AgiEngine::cmd_current_loop(uint8 *p) {
_v[p1] = vt.currentLoop;
}
-cmd(last_cel) {
+void AgiEngine::cmd_last_cel(uint8 *p) {
_v[p1] = vt.loopData->numCels - 1;
}
-cmd(set_cel) {
- g_agi->setCel(&vt, p1);
+void AgiEngine::cmd_set_cel(uint8 *p) {
+ setCel(&vt, p1);
vt.flags &= ~DONTUPDATE;
}
-cmd(set_cel_f) {
- g_agi->setCel(&vt, _v[p1]);
+void AgiEngine::cmd_set_cel_f(uint8 *p) {
+ setCel(&vt, _v[p1]);
vt.flags &= ~DONTUPDATE;
}
-cmd(set_view) {
- debugC(4, kDebugLevelScripts, "o%d, %d", p0, p1);
- g_agi->setView(&vt, p1);
+void AgiEngine::cmd_set_view(uint8 *p) {
+ setView(&vt, p1);
}
-cmd(set_view_f) {
- g_agi->setView(&vt, _v[p1]);
+void AgiEngine::cmd_set_view_f(uint8 *p) {
+ setView(&vt, _v[p1]);
}
-cmd(set_loop) {
- g_agi->setLoop(&vt, p1);
+void AgiEngine::cmd_set_loop(uint8 *p) {
+ setLoop(&vt, p1);
}
-cmd(set_loop_f) {
- g_agi->setLoop(&vt, _v[p1]);
+void AgiEngine::cmd_set_loop_f(uint8 *p) {
+ setLoop(&vt, _v[p1]);
}
-cmd(number_of_loops) {
+void AgiEngine::cmd_number_of_loops(uint8 *p) {
_v[p1] = vt.numLoops;
}
-cmd(fix_loop) {
+void AgiEngine::cmd_fix_loop(uint8 *p) {
vt.flags |= FIX_LOOP;
}
-cmd(release_loop) {
+void AgiEngine::cmd_release_loop(uint8 *p) {
vt.flags &= ~FIX_LOOP;
}
-cmd(step_size) {
+void AgiEngine::cmd_step_size(uint8 *p) {
vt.stepSize = _v[p1];
}
-cmd(step_time) {
+void AgiEngine::cmd_step_time(uint8 *p) {
vt.stepTime = vt.stepTimeCount = _v[p1];
}
-cmd(cycle_time) {
+void AgiEngine::cmd_cycle_time(uint8 *p) {
vt.cycleTime = vt.cycleTimeCount = _v[p1];
}
-cmd(stop_cycling) {
+void AgiEngine::cmd_stop_cycling(uint8 *p) {
vt.flags &= ~CYCLING;
}
-cmd(start_cycling) {
+void AgiEngine::cmd_start_cycling(uint8 *p) {
vt.flags |= CYCLING;
}
-cmd(normal_cycle) {
+void AgiEngine::cmd_normal_cycle(uint8 *p) {
vt.cycle = CYCLE_NORMAL;
vt.flags |= CYCLING;
}
-cmd(reverse_cycle) {
+void AgiEngine::cmd_reverse_cycle(uint8 *p) {
vt.cycle = CYCLE_REVERSE;
vt.flags |= CYCLING;
}
-cmd(set_dir) {
+void AgiEngine::cmd_set_dir(uint8 *p) {
vt.direction = _v[p1];
}
-cmd(get_dir) {
+void AgiEngine::cmd_get_dir(uint8 *p) {
_v[p1] = vt.direction;
}
-cmd(get_room_f) {
- _v[p1] = g_agi->objectGetLocation(_v[p0]);
+void AgiEngine::cmd_get_room_f(uint8 *p) {
+ _v[p1] = objectGetLocation(_v[p0]);
}
-cmd(put) {
- g_agi->objectSetLocation(p0, _v[p1]);
+void AgiEngine::cmd_put(uint8 *p) {
+ objectSetLocation(p0, _v[p1]);
}
-cmd(put_f) {
- g_agi->objectSetLocation(_v[p0], _v[p1]);
+void AgiEngine::cmd_put_f(uint8 *p) {
+ objectSetLocation(_v[p0], _v[p1]);
}
-cmd(drop) {
- g_agi->objectSetLocation(p0, 0);
+void AgiEngine::cmd_drop(uint8 *p) {
+ objectSetLocation(p0, 0);
}
-cmd(get) {
- g_agi->objectSetLocation(p0, EGO_OWNED);
+void AgiEngine::cmd_get(uint8 *p) {
+ objectSetLocation(p0, EGO_OWNED);
}
-cmd(get_f) {
- g_agi->objectSetLocation(_v[p0], EGO_OWNED);
+void AgiEngine::cmd_get_f(uint8 *p) {
+ objectSetLocation(_v[p0], EGO_OWNED);
}
-cmd(word_to_string) {
- strcpy(game.strings[p0], game.egoWords[p1].word);
+void AgiEngine::cmd_word_to_string(uint8 *p) {
+ strcpy(_game.strings[p0], _game.egoWords[p1].word);
}
-cmd(open_dialogue) {
- debugC(4, kDebugLevelScripts, "p0 = %d", p0);
- game.hasWindow = true;
+void AgiEngine::cmd_open_dialogue(uint8 *p) {
+ _game.hasWindow = true;
}
-cmd(close_dialogue) {
- debugC(4, kDebugLevelScripts, "p0 = %d", p0);
- game.hasWindow = false;
+void AgiEngine::cmd_close_dialogue(uint8 *p) {
+ _game.hasWindow = false;
}
-cmd(close_window) {
- g_agi->closeWindow();
+void AgiEngine::cmd_close_window(uint8 *p) {
+ closeWindow();
}
-cmd(status_line_on) {
- game.statusLine = true;
- g_agi->writeStatus();
+void AgiEngine::cmd_status_line_on(uint8 *p) {
+ _game.statusLine = true;
+ writeStatus();
}
-cmd(status_line_off) {
- game.statusLine = false;
- g_agi->writeStatus();
+void AgiEngine::cmd_status_line_off(uint8 *p) {
+ _game.statusLine = false;
+ writeStatus();
}
-cmd(show_obj) {
- g_sprites->showObj(p0);
+void AgiEngine::cmd_show_obj(uint8 *p) {
+ _sprites->showObj(p0);
}
-cmd(show_obj_v) {
- g_sprites->showObj(_v[p0]);
+void AgiEngine::cmd_show_obj_v(uint8 *p) {
+ _sprites->showObj(_v[p0]);
}
-cmd(sound) {
- g_sound->startSound(p0, p1);
+void AgiEngine::cmd_sound(uint8 *p) {
+ _sound->startSound(p0, p1);
}
-cmd(stop_sound) {
- g_sound->stopSound();
+void AgiEngine::cmd_stop_sound(uint8 *p) {
+ _sound->stopSound();
}
-cmd(menu_input) {
- g_agi->newInputMode(INPUT_MENU);
+void AgiEngine::cmd_menu_input(uint8 *p) {
+ newInputMode(INPUT_MENU);
}
-cmd(enable_item) {
- g_agi->_menu->setItem(p0, true);
+void AgiEngine::cmd_enable_item(uint8 *p) {
+ _menu->setItem(p0, true);
}
-cmd(disable_item) {
- g_agi->_menu->setItem(p0, false);
+void AgiEngine::cmd_disable_item(uint8 *p) {
+ _menu->setItem(p0, false);
}
-cmd(submit_menu) {
- g_agi->_menu->submit();
+void AgiEngine::cmd_submit_menu(uint8 *p) {
+ _menu->submit();
}
-cmd(set_scan_start) {
- curLogic->sIP = curLogic->cIP;
+void AgiEngine::cmd_set_scan_start(uint8 *p) {
+ _curLogic->sIP = _curLogic->cIP;
}
-cmd(reset_scan_start) {
- curLogic->sIP = 2;
+void AgiEngine::cmd_reset_scan_start(uint8 *p) {
+ _curLogic->sIP = 2;
}
-cmd(save_game) {
- game.simpleSave ? g_agi->saveGameSimple() : g_agi->saveGameDialog();
+void AgiEngine::cmd_save_game(uint8 *p) {
+ _game.simpleSave ? saveGameSimple() : saveGameDialog();
}
-cmd(load_game) {
+void AgiEngine::cmd_load_game(uint8 *p) {
assert(1);
- game.simpleSave ? g_agi->loadGameSimple() : g_agi->loadGameDialog();
+ _game.simpleSave ? loadGameSimple() : loadGameDialog();
}
-cmd(init_disk) { // do nothing
+void AgiEngine::cmd_init_disk(uint8 *p) { // do nothing
}
-cmd(log) { // do nothing
+void AgiEngine::cmd_log(uint8 *p) { // do nothing
}
-cmd(trace_on) { // do nothing
+void AgiEngine::cmd_trace_on(uint8 *p) { // do nothing
}
-cmd(trace_info) { // do nothing
+void AgiEngine::cmd_trace_info(uint8 *p) { // do nothing
}
-cmd(show_mem) {
- g_agi->messageBox("Enough memory");
+void AgiEngine::cmd_show_mem(uint8 *p) {
+ messageBox("Enough memory");
}
-cmd(init_joy) { // do nothing
+void AgiEngine::cmd_init_joy(uint8 *p) { // do nothing
}
-cmd(script_size) {
+void AgiEngine::cmd_script_size(uint8 *p) {
report("script.size(%d)\n", p0);
}
-cmd(cancel_line) {
- g_agi->_game.inputBuffer[0] = 0;
- g_agi->writePrompt();
+void AgiEngine::cmd_cancel_line(uint8 *p) {
+ _game.inputBuffer[0] = 0;
+ writePrompt();
}
// This implementation is based on observations of Amiga's Gold Rush.
@@ -533,7 +510,7 @@ cmd(cancel_line) {
// 4051 (When ego is stationary),
// 471 (When walking on the first screen's bridge),
// 71 (When walking around, using the mouse or the keyboard).
-cmd(obj_status_f) {
+void AgiEngine::cmd_obj_status_f(uint8 *p) {
const char *cycleDesc; // Object's cycle description line
const char *motionDesc; // Object's motion description line
char msg[256]; // The whole object status message
@@ -594,7 +571,7 @@ cmd(obj_status_f) {
vt_v.stepSize,
cycleDesc,
motionDesc);
- g_agi->messageBox(msg);
+ messageBox(msg);
}
// unknown commands:
@@ -605,49 +582,49 @@ cmd(obj_status_f) {
// unk_174: Change priority table (used in KQ4) -- j5
// unk_177: Disable menus completely -- j5
// unk_181: Deactivate keypressed control (default control of ego)
-cmd(set_simple) {
- if (!(g_agi->getFeatures() & (GF_AGI256 | GF_AGI256_2))) {
- game.simpleSave = true;
+void AgiEngine::cmd_set_simple(uint8 *p) {
+ if (!(getFeatures() & (GF_AGI256 | GF_AGI256_2))) {
+ _game.simpleSave = true;
} else { // AGI256 and AGI256-2 use this unknown170 command to load 256 color pictures.
- // Load the picture. Similar to cmd(load_pic).
- g_sprites->eraseBoth();
- g_agi->agiLoadResource(rPICTURE, _v[p0]);
+ // Load the picture. Similar to void AgiEngine::cmd_load_pic(uint8 *p).
+ _sprites->eraseBoth();
+ agiLoadResource(rPICTURE, _v[p0]);
- // Draw the picture. Similar to cmd(draw_pic).
- g_picture->decodePicture(_v[p0], false, true);
- g_sprites->blitBoth();
- game.pictureShown = 0;
+ // Draw the picture. Similar to void AgiEngine::cmd_draw_pic(uint8 *p).
+ _picture->decodePicture(_v[p0], false, true);
+ _sprites->blitBoth();
+ _game.pictureShown = 0;
- // Show the picture. Similar to cmd(show_pic).
- g_agi->setflag(fOutputMode, false);
- cmd_close_window(g_agi, NULL);
- g_picture->showPic();
- game.pictureShown = 1;
+ // Show the picture. Similar to void AgiEngine::cmd_show_pic(uint8 *p).
+ setflag(fOutputMode, false);
+ closeWindow();
+ _picture->showPic();
+ _game.pictureShown = 1;
// Simulate slowww computer. Many effects rely on this
- g_agi->pause(kPausePicture);
+ pause(kPausePicture);
}
}
-cmd(pop_script) {
- if (g_agi->getVersion() >= 0x2915) {
+void AgiEngine::cmd_pop_script(uint8 *p) {
+ if (getVersion() >= 0x2915) {
report("pop.script\n");
}
}
-cmd(hold_key) {
- if (g_agi->getVersion() >= 0x3098) {
- g_agi->_egoHoldKey = true;
+void AgiEngine::cmd_hold_key(uint8 *p) {
+ if (getVersion() >= 0x3098) {
+ _egoHoldKey = true;
}
}
-cmd(discard_sound) {
- if (g_agi->getVersion() >= 0x2936) {
+void AgiEngine::cmd_discard_sound(uint8 *p) {
+ if (getVersion() >= 0x2936) {
report("discard.sound\n");
}
}
-cmd(hide_mouse) {
+void AgiEngine::cmd_hide_mouse(uint8 *p) {
// WORKAROUND: Turns off current movement that's being caused with the mouse.
// This fixes problems with too many popup boxes appearing in the Amiga
// Gold Rush's copy protection failure scene (i.e. the hanging scene, logic.192).
@@ -655,34 +632,34 @@ cmd(hide_mouse) {
// to walk somewhere else than to the right using the mouse.
// FIXME: Write a proper implementation using disassembly and
// apply it to other games as well if applicable.
- game.viewTable[0].flags &= ~ADJ_EGO_XY;
+ _game.viewTable[0].flags &= ~ADJ_EGO_XY;
g_system->showMouse(false);
}
-cmd(allow_menu) {
- if (g_agi->getVersion() >= 0x3098) {
- g_agi->setflag(fMenusWork, ((p0 != 0) ? true : false));
+void AgiEngine::cmd_allow_menu(uint8 *p) {
+ if (getVersion() >= 0x3098) {
+ setflag(fMenusWork, ((p0 != 0) ? true : false));
}
}
-cmd(show_mouse) {
+void AgiEngine::cmd_show_mouse(uint8 *p) {
g_system->showMouse(true);
}
-cmd(fence_mouse) {
- g_agi->_game.mouseFence.moveTo(p0, p1);
- g_agi->_game.mouseFence.setWidth(p2 - p0);
- g_agi->_game.mouseFence.setHeight(p3 - p1);
+void AgiEngine::cmd_fence_mouse(uint8 *p) {
+ _game.mouseFence.moveTo(p0, p1);
+ _game.mouseFence.setWidth(p2 - p0);
+ _game.mouseFence.setHeight(p3 - p1);
}
-cmd(release_key) {
- if (g_agi->getVersion() >= 0x3098) {
- g_agi->_egoHoldKey = false;
+void AgiEngine::cmd_release_key(uint8 *p) {
+ if (getVersion() >= 0x3098) {
+ _egoHoldKey = false;
}
}
-cmd(adj_ego_move_to_x_y) {
+void AgiEngine::cmd_adj_ego_move_to_x_y(uint8 *p) {
int8 x, y;
switch (logicNamesCmd[182].numArgs) {
@@ -704,56 +681,57 @@ cmd(adj_ego_move_to_x_y) {
// onto the ladder so this is more like it (Although that may be caused
// by something else because this command doesn't do any flag manipulations
// in the Amiga version - checked it with disassembly).
- if (x != game.adjMouseX || y != game.adjMouseY)
- game.viewTable[EGO_VIEW_TABLE].flags &= ~ADJ_EGO_XY;
+ if (x != _game.adjMouseX || y != _game.adjMouseY)
+ _game.viewTable[EGO_VIEW_TABLE].flags &= ~ADJ_EGO_XY;
- game.adjMouseX = x;
- game.adjMouseY = y;
+ _game.adjMouseX = x;
+ _game.adjMouseY = y;
debugC(4, kDebugLevelScripts, "adj.ego.move.to.x.y(%d, %d)", x, y);
break;
// TODO: Check where (if anywhere) the 0 arguments version is used
case 0:
default:
- game.viewTable[0].flags |= ADJ_EGO_XY;
+ _game.viewTable[0].flags |= ADJ_EGO_XY;
break;
}
}
-cmd(parse) {
+void AgiEngine::cmd_parse(uint8 *p) {
_v[vWordNotFound] = 0;
- g_agi->setflag(fEnteredCli, false);
- g_agi->setflag(fSaidAcceptedInput, false);
+ setflag(fEnteredCli, false);
+ setflag(fSaidAcceptedInput, false);
- g_agi->dictionaryWords(g_agi->agiSprintf(game.strings[p0]));
+ dictionaryWords(agiSprintf(_game.strings[p0]));
}
-cmd(call) {
+void AgiEngine::cmd_call(uint8 *p) {
int oldCIP;
int oldLognum;
// CM: we don't save sIP because set.scan.start can be
// used in a called script (fixes xmas demo)
- oldCIP = curLogic->cIP;
- oldLognum = game.lognum;
+ oldCIP = _curLogic->cIP;
+ oldLognum = _game.lognum;
- g_agi->runLogic(p0);
+ runLogic(p0);
- game.lognum = oldLognum;
- curLogic = &game.logics[game.lognum];
- curLogic->cIP = oldCIP;
+ _game.lognum = oldLognum;
+ _curLogic = &_game.logics[_game.lognum];
+ _curLogic->cIP = oldCIP;
}
-cmd(call_f) {
- cmd_call(g_agi, &_v[p0]);
+void AgiEngine::cmd_call_f(uint8 *p) {
+ cmd_call(&_v[p0]);
}
-cmd(draw_pic) {
+void AgiEngine::cmd_draw_pic(uint8 *p) {
debugC(6, kDebugLevelScripts, "=== draw pic %d ===", _v[p0]);
- g_sprites->eraseBoth();
- g_picture->decodePicture(_v[p0], true);
- g_sprites->blitBoth();
- game.pictureShown = 0;
+ _sprites->eraseBoth();
+ _picture->decodePicture(_v[p0], true);
+ _sprites->blitBoth();
+ _sprites->commitBoth();
+ _game.pictureShown = 0;
debugC(6, kDebugLevelScripts, "--- end of draw pic %d ---", _v[p0]);
// WORKAROUND for a script bug which exists in SQ1, logic scripts
@@ -768,63 +746,64 @@ cmd(draw_pic) {
// above the ground), flag 103 is reset, thereby fixing this issue. Note
// that this is a script bug and occurs in the original interpreter as well.
// Fixes bug #1658514: AGI: SQ1 (2.2 DOS ENG) bizzare exploding roger
- if (g_agi->getGameID() == GID_SQ1 && _v[p0] == 20)
- g_agi->setflag(103, false);
+ if (getGameID() == GID_SQ1 && _v[p0] == 20)
+ setflag(103, false);
// Simulate slowww computer. Many effects rely on this
- g_agi->pause(kPausePicture);
+ pause(kPausePicture);
}
-cmd(show_pic) {
+void AgiEngine::cmd_show_pic(uint8 *p) {
debugC(6, kDebugLevelScripts, "=== show pic ===");
- g_agi->setflag(fOutputMode, false);
- cmd_close_window(g_agi, NULL);
- g_picture->showPic();
- game.pictureShown = 1;
+ setflag(fOutputMode, false);
+ closeWindow();
+ _picture->showPic();
+ _game.pictureShown = 1;
debugC(6, kDebugLevelScripts, "--- end of show pic ---");
}
-cmd(load_pic) {
- g_sprites->eraseBoth();
- g_agi->agiLoadResource(rPICTURE, _v[p0]);
- g_sprites->blitBoth();
+void AgiEngine::cmd_load_pic(uint8 *p) {
+ _sprites->eraseBoth();
+ agiLoadResource(rPICTURE, _v[p0]);
+ _sprites->blitBoth();
+ _sprites->commitBoth();
}
-cmd(discard_pic) {
+void AgiEngine::cmd_discard_pic(uint8 *p) {
debugC(6, kDebugLevelScripts, "--- discard pic ---");
// do nothing
}
-cmd(overlay_pic) {
+void AgiEngine::cmd_overlay_pic(uint8 *p) {
debugC(6, kDebugLevelScripts, "--- overlay pic ---");
- g_sprites->eraseBoth();
- g_picture->decodePicture(_v[p0], false);
- g_sprites->blitBoth();
- game.pictureShown = 0;
- g_sprites->commitBoth();
+ _sprites->eraseBoth();
+ _picture->decodePicture(_v[p0], false);
+ _sprites->blitBoth();
+ _game.pictureShown = 0;
+ _sprites->commitBoth();
// Simulate slowww computer. Many effects rely on this
- g_agi->pause(kPausePicture);
+ pause(kPausePicture);
}
-cmd(show_pri_screen) {
- g_agi->_debug.priority = 1;
- g_sprites->eraseBoth();
- g_picture->showPic();
- g_sprites->blitBoth();
+void AgiEngine::cmd_show_pri_screen(uint8 *p) {
+ _debug.priority = 1;
+ _sprites->eraseBoth();
+ _picture->showPic();
+ _sprites->blitBoth();
- g_agi->waitKey();
+ waitKey();
- g_agi->_debug.priority = 0;
- g_sprites->eraseBoth();
- g_picture->showPic();
- g_sprites->blitBoth();
+ _debug.priority = 0;
+ _sprites->eraseBoth();
+ _picture->showPic();
+ _sprites->blitBoth();
}
-cmd(animate_obj) {
+void AgiEngine::cmd_animate_obj(uint8 *p) {
if (vt.flags & ANIMATED)
return;
@@ -835,14 +814,14 @@ cmd(animate_obj) {
vt.direction = 0;
}
-cmd(unanimate_all) {
+void AgiEngine::cmd_unanimate_all(uint8 *p) {
int i;
for (i = 0; i < MAX_VIEWTABLE; i++)
- game.viewTable[i].flags &= ~(ANIMATED | DRAWN);
+ _game.viewTable[i].flags &= ~(ANIMATED | DRAWN);
}
-cmd(draw) {
+void AgiEngine::cmd_draw(uint8 *p) {
if (vt.flags & DRAWN)
return;
@@ -852,19 +831,19 @@ cmd(draw) {
debugC(4, kDebugLevelScripts, "draw entry %d", vt.entry);
vt.flags |= UPDATE;
- if (g_agi->getVersion() >= 0x3000) {
- g_agi->setLoop(&vt, vt.currentLoop);
- g_agi->setCel(&vt, vt.currentCel);
+ if (getVersion() >= 0x3000) {
+ setLoop(&vt, vt.currentLoop);
+ setCel(&vt, vt.currentCel);
}
- g_agi->fixPosition(p0);
+ fixPosition(p0);
vt.xPos2 = vt.xPos;
vt.yPos2 = vt.yPos;
vt.celData2 = vt.celData;
- g_sprites->eraseUpdSprites();
+ _sprites->eraseUpdSprites();
vt.flags |= DRAWN;
- // WORKAROUND: This fixes a bug with AGI Fanmade game Space Trek.
+ // WORKAROUND: This fixes a bug with AGI Fanmade _game Space Trek.
// The original workaround checked if AGI version was <= 2.440, which could
// cause regressions with some AGI games. The original workaround no longer
// works for Space Trek in ScummVM, as all fanmade games are set to use
@@ -875,36 +854,43 @@ cmd(draw) {
// TODO: Investigate this further and check if any other fanmade AGI
// games are affected. If yes, then it'd be best to set this for Space
// Trek only
- if (g_agi->getFeatures() & GF_FANMADE) // See Sarien bug #546562
+ if (getFeatures() & GF_FANMADE) // See Sarien bug #546562
vt.flags |= ANIMATED;
- g_sprites->blitUpdSprites();
+ _sprites->blitUpdSprites();
vt.flags &= ~DONTUPDATE;
- g_sprites->commitBlock(vt.xPos, vt.yPos - vt.ySize + 1, vt.xPos + vt.xSize - 1, vt.yPos);
+ _sprites->commitBlock(vt.xPos, vt.yPos - vt.ySize + 1, vt.xPos + vt.xSize - 1, vt.yPos, true);
debugC(4, kDebugLevelScripts, "vt entry #%d flags = %02x", p0, vt.flags);
}
-cmd(erase) {
+void AgiEngine::cmd_erase(uint8 *p) {
if (~vt.flags & DRAWN)
return;
- g_sprites->eraseUpdSprites();
+ _sprites->eraseUpdSprites();
if (vt.flags & UPDATE) {
vt.flags &= ~DRAWN;
} else {
- g_sprites->eraseNonupdSprites();
+ _sprites->eraseNonupdSprites();
vt.flags &= ~DRAWN;
- g_sprites->blitNonupdSprites();
+ _sprites->blitNonupdSprites();
}
- g_sprites->blitUpdSprites();
+ _sprites->blitUpdSprites();
+
+ int x1, y1, x2, y2;
+
+ x1 = MIN((int)MIN(vt.xPos, vt.xPos2), MIN(vt.xPos + vt.celData->width, vt.xPos2 + vt.celData2->width));
+ x2 = MAX((int)MAX(vt.xPos, vt.xPos2), MAX(vt.xPos + vt.celData->width, vt.xPos2 + vt.celData2->width));
+ y1 = MIN((int)MIN(vt.yPos, vt.yPos2), MIN(vt.yPos - vt.celData->height, vt.yPos2 - vt.celData2->height));
+ y2 = MAX((int)MAX(vt.yPos, vt.yPos2), MAX(vt.yPos - vt.celData->height, vt.yPos2 - vt.celData2->height));
- g_sprites->commitBlock(vt.xPos, vt.yPos - vt.ySize + 1, vt.xPos + vt.xSize - 1, vt.yPos);
+ _sprites->commitBlock(x1, y1, x2, y2, true);
}
-cmd(position) {
+void AgiEngine::cmd_position(uint8 *p) {
vt.xPos = vt.xPos2 = p1;
vt.yPos = vt.yPos2 = p2;
@@ -921,27 +907,27 @@ cmd(position) {
// I haven't checked but if Space Trek solely abuses the position-command we wouldn't
// strictly need the identical workaround in the position.v-command but it does make
// for a nice symmetry.
- if (g_agi->getFeatures() & GF_CLIPCOORDS)
- g_agi->clipViewCoordinates(&vt);
+ if (getFeatures() & GF_CLIPCOORDS)
+ clipViewCoordinates(&vt);
}
-cmd(position_f) {
+void AgiEngine::cmd_position_f(uint8 *p) {
vt.xPos = vt.xPos2 = _v[p1];
vt.yPos = vt.yPos2 = _v[p2];
// WORKAROUND: Part of the fix for bug #1659209 "AGI: Space Trek sprite duplication"
// with an accompanying identical workaround in position-command (i.e. command 0x25).
// See that workaround's comment for more in-depth information.
- if (g_agi->getFeatures() & GF_CLIPCOORDS)
- g_agi->clipViewCoordinates(&vt);
+ if (getFeatures() & GF_CLIPCOORDS)
+ clipViewCoordinates(&vt);
}
-cmd(get_posn) {
- game.vars[p1] = (unsigned char)vt.xPos;
- game.vars[p2] = (unsigned char)vt.yPos;
+void AgiEngine::cmd_get_posn(uint8 *p) {
+ _game.vars[p1] = (unsigned char)vt.xPos;
+ _game.vars[p2] = (unsigned char)vt.yPos;
}
-cmd(reposition) {
+void AgiEngine::cmd_reposition(uint8 *p) {
int dx = (int8) _v[p1], dy = (int8) _v[p2];
debugC(4, kDebugLevelScripts, "dx=%d, dy=%d", dx, dy);
@@ -957,106 +943,106 @@ cmd(reposition) {
else
vt.yPos += dy;
- g_agi->fixPosition(p0);
+ fixPosition(p0);
}
-cmd(reposition_to) {
+void AgiEngine::cmd_reposition_to(uint8 *p) {
vt.xPos = p1;
vt.yPos = p2;
vt.flags |= UPDATE_POS;
- g_agi->fixPosition(p0);
+ fixPosition(p0);
}
-cmd(reposition_to_f) {
+void AgiEngine::cmd_reposition_to_f(uint8 *p) {
vt.xPos = _v[p1];
vt.yPos = _v[p2];
vt.flags |= UPDATE_POS;
- g_agi->fixPosition(p0);
+ fixPosition(p0);
}
-cmd(add_to_pic) {
- g_sprites->addToPic(p0, p1, p2, p3, p4, p5, p6);
+void AgiEngine::cmd_add_to_pic(uint8 *p) {
+ _sprites->addToPic(p0, p1, p2, p3, p4, p5, p6);
}
-cmd(add_to_pic_f) {
- g_sprites->addToPic(_v[p0], _v[p1], _v[p2], _v[p3], _v[p4], _v[p5], _v[p6]);
+void AgiEngine::cmd_add_to_pic_f(uint8 *p) {
+ _sprites->addToPic(_v[p0], _v[p1], _v[p2], _v[p3], _v[p4], _v[p5], _v[p6]);
}
-cmd(force_update) {
- g_sprites->eraseBoth();
- g_sprites->blitBoth();
- g_sprites->commitBoth();
+void AgiEngine::cmd_force_update(uint8 *p) {
+ _sprites->eraseBoth();
+ _sprites->blitBoth();
+ _sprites->commitBoth();
}
-cmd(reverse_loop) {
+void AgiEngine::cmd_reverse_loop(uint8 *p) {
debugC(4, kDebugLevelScripts, "o%d, f%d", p0, p1);
vt.cycle = CYCLE_REV_LOOP;
vt.flags |= (DONTUPDATE | UPDATE | CYCLING);
vt.parm1 = p1;
- g_agi->setflag(p1, false);
+ setflag(p1, false);
}
-cmd(end_of_loop) {
+void AgiEngine::cmd_end_of_loop(uint8 *p) {
debugC(4, kDebugLevelScripts, "o%d, f%d", p0, p1);
vt.cycle = CYCLE_END_OF_LOOP;
vt.flags |= (DONTUPDATE | UPDATE | CYCLING);
vt.parm1 = p1;
- g_agi->setflag(p1, false);
+ setflag(p1, false);
}
-cmd(block) {
+void AgiEngine::cmd_block(uint8 *p) {
debugC(4, kDebugLevelScripts, "x1=%d, y1=%d, x2=%d, y2=%d", p0, p1, p2, p3);
- game.block.active = true;
- game.block.x1 = p0;
- game.block.y1 = p1;
- game.block.x2 = p2;
- game.block.y2 = p3;
+ _game.block.active = true;
+ _game.block.x1 = p0;
+ _game.block.y1 = p1;
+ _game.block.x2 = p2;
+ _game.block.y2 = p3;
}
-cmd(unblock) {
- game.block.active = false;
+void AgiEngine::cmd_unblock(uint8 *p) {
+ _game.block.active = false;
}
-cmd(normal_motion) {
+void AgiEngine::cmd_normal_motion(uint8 *p) {
vt.motion = MOTION_NORMAL;
}
-cmd(stop_motion) {
+void AgiEngine::cmd_stop_motion(uint8 *p) {
vt.direction = 0;
vt.motion = MOTION_NORMAL;
if (p0 == 0) { // ego only
_v[vEgoDir] = 0;
- game.playerControl = false;
+ _game.playerControl = false;
}
}
-cmd(start_motion) {
+void AgiEngine::cmd_start_motion(uint8 *p) {
vt.motion = MOTION_NORMAL;
if (p0 == 0) { // ego only
_v[vEgoDir] = 0;
- game.playerControl = true;
+ _game.playerControl = true;
}
}
-cmd(player_control) {
- game.playerControl = true;
- game.viewTable[0].motion = MOTION_NORMAL;
+void AgiEngine::cmd_player_control(uint8 *p) {
+ _game.playerControl = true;
+ _game.viewTable[0].motion = MOTION_NORMAL;
}
-cmd(program_control) {
- game.playerControl = false;
+void AgiEngine::cmd_program_control(uint8 *p) {
+ _game.playerControl = false;
}
-cmd(follow_ego) {
+void AgiEngine::cmd_follow_ego(uint8 *p) {
vt.motion = MOTION_FOLLOW_EGO;
vt.parm1 = p1 > vt.stepSize ? p1 : vt.stepSize;
vt.parm2 = p2;
vt.parm3 = 0xff;
- g_agi->setflag(p2, false);
+ setflag(p2, false);
vt.flags |= UPDATE;
}
-cmd(move_obj) {
+void AgiEngine::cmd_move_obj(uint8 *p) {
// _D (_D_WARN "o=%d, x=%d, y=%d, s=%d, f=%d", p0, p1, p2, p3, p4);
vt.motion = MOTION_MOVE_OBJ;
@@ -1068,18 +1054,18 @@ cmd(move_obj) {
if (p3 != 0)
vt.stepSize = p3;
- g_agi->setflag(p4, false);
+ setflag(p4, false);
vt.flags |= UPDATE;
if (p0 == 0)
- game.playerControl = false;
+ _game.playerControl = false;
// AGI 2.272 (ddp, xmas) doesn't call move_obj!
- if (g_agi->getVersion() > 0x2272)
- g_agi->moveObj(&vt);
+ if (getVersion() > 0x2272)
+ moveObj(&vt);
}
-cmd(move_obj_f) {
+void AgiEngine::cmd_move_obj_f(uint8 *p) {
vt.motion = MOTION_MOVE_OBJ;
vt.parm1 = _v[p1];
vt.parm2 = _v[p2];
@@ -1089,67 +1075,67 @@ cmd(move_obj_f) {
if (_v[p3] != 0)
vt.stepSize = _v[p3];
- g_agi->setflag(p4, false);
+ setflag(p4, false);
vt.flags |= UPDATE;
if (p0 == 0)
- game.playerControl = false;
+ _game.playerControl = false;
// AGI 2.272 (ddp, xmas) doesn't call move_obj!
- if (g_agi->getVersion() > 0x2272)
- g_agi->moveObj(&vt);
+ if (getVersion() > 0x2272)
+ moveObj(&vt);
}
-cmd(wander) {
+void AgiEngine::cmd_wander(uint8 *p) {
if (p0 == 0)
- game.playerControl = false;
+ _game.playerControl = false;
vt.motion = MOTION_WANDER;
vt.flags |= UPDATE;
}
-cmd(set_game_id) {
- if (curLogic->texts && (p0 - 1) <= curLogic->numTexts)
- strncpy(game.id, curLogic->texts[p0 - 1], 8);
+void AgiEngine::cmd_set_game_id(uint8 *p) {
+ if (_curLogic->texts && (p0 - 1) <= _curLogic->numTexts)
+ strncpy(_game.id, _curLogic->texts[p0 - 1], 8);
else
- game.id[0] = 0;
+ _game.id[0] = 0;
- report("Game ID: \"%s\"\n", game.id);
+ report("Game ID: \"%s\"\n", _game.id);
}
-cmd(pause) {
- int tmp = game.clockEnabled;
+void AgiEngine::cmd_pause(uint8 *p) {
+ int tmp = _game.clockEnabled;
const char *b[] = { "Continue", NULL };
const char *b_ru[] = { "\x8f\xe0\xae\xa4\xae\xab\xa6\xa8\xe2\xec", NULL };
- game.clockEnabled = false;
+ _game.clockEnabled = false;
- switch (g_agi->getLanguage()) {
+ switch (getLanguage()) {
case Common::RU_RUS:
- g_agi->selectionBox(" \x88\xa3\xe0\xa0 \xae\xe1\xe2\xa0\xad\xae\xa2\xab\xa5\xad\xa0. \n\n\n", b_ru);
+ selectionBox(" \x88\xa3\xe0\xa0 \xae\xe1\xe2\xa0\xad\xae\xa2\xab\xa5\xad\xa0. \n\n\n", b_ru);
break;
default:
- g_agi->selectionBox(" Game is paused. \n\n\n", b);
+ selectionBox(" Game is paused. \n\n\n", b);
break;
}
- game.clockEnabled = tmp;
+ _game.clockEnabled = tmp;
}
-cmd(set_menu) {
- debugC(4, kDebugLevelScripts, "text %02x of %02x", p0, curLogic->numTexts);
+void AgiEngine::cmd_set_menu(uint8 *p) {
+ debugC(4, kDebugLevelScripts, "text %02x of %02x", p0, _curLogic->numTexts);
- if (curLogic->texts != NULL && p0 <= curLogic->numTexts)
- g_agi->_menu->add(curLogic->texts[p0 - 1]);
+ if (_curLogic->texts != NULL && p0 <= _curLogic->numTexts)
+ _menu->add(_curLogic->texts[p0 - 1]);
}
-cmd(set_menu_item) {
- debugC(4, kDebugLevelScripts, "text %02x of %02x", p0, curLogic->numTexts);
+void AgiEngine::cmd_set_menu_item(uint8 *p) {
+ debugC(4, kDebugLevelScripts, "text %02x of %02x", p0, _curLogic->numTexts);
- if (curLogic->texts != NULL && p0 <= curLogic->numTexts)
- g_agi->_menu->addItem(curLogic->texts[p0 - 1], p1);
+ if (_curLogic->texts != NULL && p0 <= _curLogic->numTexts)
+ _menu->addItem(_curLogic->texts[p0 - 1], p1);
}
-cmd(version) {
+void AgiEngine::cmd_version(uint8 *p) {
char verMsg[64];
char ver2Msg[] =
"\n"
@@ -1168,7 +1154,7 @@ cmd(version) {
sprintf(verMsg, TITLE " v%s", gScummVMVersion);
- ver = g_agi->getVersion();
+ ver = getVersion();
maj = (ver >> 12) & 0xf;
min = ver & 0xfff;
@@ -1186,88 +1172,88 @@ cmd(version) {
strncpy(q + 1 + gap, verMsg, strlen(verMsg));
sprintf(msg, q, maj, min);
- g_agi->messageBox(msg);
+ messageBox(msg);
}
-cmd(configure_screen) {
- game.lineMinPrint = p0;
- game.lineUserInput = p1;
- game.lineStatus = p2;
+void AgiEngine::cmd_configure_screen(uint8 *p) {
+ _game.lineMinPrint = p0;
+ _game.lineUserInput = p1;
+ _game.lineStatus = p2;
}
-cmd(text_screen) {
+void AgiEngine::cmd_text_screen(uint8 *p) {
debugC(4, kDebugLevelScripts, "switching to text mode");
- game.gfxMode = false;
+ _game.gfxMode = false;
// Simulates the "bright background bit" of the PC video
// controller.
- if (game.colorBg)
- game.colorBg |= 0x08;
+ if (_game.colorBg)
+ _game.colorBg |= 0x08;
- g_gfx->clearScreen(game.colorBg);
+ _gfx->clearScreen(_game.colorBg);
}
-cmd(graphics) {
+void AgiEngine::cmd_graphics(uint8 *p) {
debugC(4, kDebugLevelScripts, "switching to graphics mode");
- if (!game.gfxMode) {
- game.gfxMode = true;
- g_gfx->clearScreen(0);
- g_picture->showPic();
- g_agi->writeStatus();
- g_agi->writePrompt();
+ if (!_game.gfxMode) {
+ _game.gfxMode = true;
+ _gfx->clearScreen(0);
+ _picture->showPic();
+ writeStatus();
+ writePrompt();
}
}
-cmd(set_text_attribute) {
- game.colorFg = p0;
- game.colorBg = p1;
+void AgiEngine::cmd_set_text_attribute(uint8 *p) {
+ _game.colorFg = p0;
+ _game.colorBg = p1;
- if (game.gfxMode) {
- if (game.colorBg != 0) {
- game.colorFg = 0;
- game.colorBg = 15;
+ if (_game.gfxMode) {
+ if (_game.colorBg != 0) {
+ _game.colorFg = 0;
+ _game.colorBg = 15;
}
}
}
-cmd(status) {
- g_agi->inventory();
+void AgiEngine::cmd_status(uint8 *p) {
+ inventory();
}
-cmd(quit) {
+void AgiEngine::cmd_quit(uint8 *p) {
const char *buttons[] = { "Quit", "Continue", NULL };
- g_sound->stopSound();
+ _sound->stopSound();
if (p0) {
- g_agi->quitGame();
+ quitGame();
} else {
- if (g_agi->selectionBox
- (" Quit the game, or continue? \n\n\n", buttons) == 0) {
- g_agi->quitGame();
+ if (selectionBox
+ (" Quit the _game, or continue? \n\n\n", buttons) == 0) {
+ quitGame();
}
}
}
-cmd(restart_game) {
+void AgiEngine::cmd_restart_game(uint8 *p) {
const char *buttons[] = { "Restart", "Continue", NULL };
int sel;
- g_sound->stopSound();
- sel = g_agi->getflag(fAutoRestart) ? 0 :
- g_agi->selectionBox(" Restart game, or continue? \n\n\n", buttons);
+ _sound->stopSound();
+ sel = getflag(fAutoRestart) ? 0 :
+ selectionBox(" Restart _game, or continue? \n\n\n", buttons);
if (sel == 0) {
- g_agi->_restartGame = true;
- g_agi->setflag(fRestartGame, true);
- g_agi->_menu->enableAll();
+ _restartGame = true;
+ setflag(fRestartGame, true);
+ _menu->enableAll();
}
}
-cmd(distance) {
+void AgiEngine::cmd_distance(uint8 *p) {
int16 x1, y1, x2, y2, d;
- VtEntry *v0 = &game.viewTable[p0];
- VtEntry *v1 = &game.viewTable[p1];
+ VtEntry *v0 = &_game.viewTable[p0];
+ VtEntry *v1 = &_game.viewTable[p1];
if (v0->flags & DRAWN && v1->flags & DRAWN) {
x1 = v0->xPos + v0->xSize / 2;
@@ -1290,7 +1276,7 @@ cmd(distance) {
// wouldn't chase Rosella around anymore. If it had worked correctly the zombie
// wouldn't have come up at all or it would have come up and gone back down
// immediately. The latter approach is the one implemented here.
- if (g_agi->getGameID() == GID_KQ4 && (_v[vCurRoom] == 16 || _v[vCurRoom] == 18) && p2 >= 221 && p2 <= 223) {
+ if (getGameID() == GID_KQ4 && (_v[vCurRoom] == 16 || _v[vCurRoom] == 18) && p2 >= 221 && p2 <= 223) {
// Rooms 16 and 18 are graveyards where three zombies come up at night. They use logics 16 and 18.
// Variables 221-223 are used to save the distance between each zombie and Rosella.
// Variables 155, 156 and 162 are used to save the state of each zombie in room 16.
@@ -1315,24 +1301,24 @@ cmd(distance) {
_v[p2] = (unsigned char)d;
}
-cmd(accept_input) {
+void AgiEngine::cmd_accept_input(uint8 *p) {
debugC(4, kDebugLevelScripts | kDebugLevelInput, "input normal");
- g_agi->newInputMode(INPUT_NORMAL);
- game.inputEnabled = true;
- g_agi->writePrompt();
+ newInputMode(INPUT_NORMAL);
+ _game.inputEnabled = true;
+ writePrompt();
}
-cmd(prevent_input) {
+void AgiEngine::cmd_prevent_input(uint8 *p) {
debugC(4, kDebugLevelScripts | kDebugLevelInput, "no input");
- g_agi->newInputMode(INPUT_NONE);
- game.inputEnabled = false;
+ newInputMode(INPUT_NONE);
+ _game.inputEnabled = false;
- g_agi->clearPrompt();
+ clearPrompt();
}
-cmd(get_string) {
+void AgiEngine::cmd_get_string(uint8 *p) {
int tex, row, col;
debugC(4, kDebugLevelScripts, "%d %d %d %d %d", p0, p1, p2, p3, p4);
@@ -1348,63 +1334,63 @@ cmd(get_string) {
if (col > 39)
col = 39;
- g_agi->newInputMode(INPUT_GETSTRING);
+ newInputMode(INPUT_GETSTRING);
- if (curLogic->texts != NULL && curLogic->numTexts >= tex) {
- int len = strlen(curLogic->texts[tex]);
+ if (_curLogic->texts != NULL && _curLogic->numTexts >= tex) {
+ int len = strlen(_curLogic->texts[tex]);
- g_agi->printText(curLogic->texts[tex], 0, col, row, len, game.colorFg, game.colorBg);
- g_agi->getString(col + len - 1, row, p4, p0);
+ printText(_curLogic->texts[tex], 0, col, row, len, _game.colorFg, _game.colorBg);
+ getString(col + len - 1, row, p4, p0);
// SGEO: display input char
- g_gfx->printCharacter((col + len), row, game.cursorChar, game.colorFg, game.colorBg);
+ _gfx->printCharacter((col + len), row, _game.cursorChar, _game.colorFg, _game.colorBg);
}
do {
- g_agi->mainCycle();
- } while (game.inputMode == INPUT_GETSTRING && !(g_agi->shouldQuit() || g_agi->_restartGame));
+ mainCycle();
+ } while (_game.inputMode == INPUT_GETSTRING && !(shouldQuit() || _restartGame));
}
-cmd(get_num) {
+void AgiEngine::cmd_get_num(uint8 *p) {
debugC(4, kDebugLevelScripts, "%d %d", p0, p1);
- g_agi->newInputMode(INPUT_GETSTRING);
+ newInputMode(INPUT_GETSTRING);
- if (curLogic->texts != NULL && curLogic->numTexts >= (p0 - 1)) {
- int len = strlen(curLogic->texts[p0 - 1]);
+ if (_curLogic->texts != NULL && _curLogic->numTexts >= (p0 - 1)) {
+ int len = strlen(_curLogic->texts[p0 - 1]);
- g_agi->printText(curLogic->texts[p0 - 1], 0, 0, 22, len, game.colorFg, game.colorBg);
- g_agi->getString(len - 1, 22, 3, MAX_STRINGS);
+ printText(_curLogic->texts[p0 - 1], 0, 0, 22, len, _game.colorFg, _game.colorBg);
+ getString(len - 1, 22, 3, MAX_STRINGS);
// CM: display input char
- g_gfx->printCharacter((p3 + len), 22, game.cursorChar, game.colorFg, game.colorBg);
+ _gfx->printCharacter((p3 + len), 22, _game.cursorChar, _game.colorFg, _game.colorBg);
}
do {
- g_agi->mainCycle();
- } while (game.inputMode == INPUT_GETSTRING && !(g_agi->shouldQuit() || g_agi->_restartGame));
+ mainCycle();
+ } while (_game.inputMode == INPUT_GETSTRING && !(shouldQuit() || _restartGame));
- _v[p1] = atoi(game.strings[MAX_STRINGS]);
+ _v[p1] = atoi(_game.strings[MAX_STRINGS]);
- debugC(4, kDebugLevelScripts, "[%s] -> %d", game.strings[MAX_STRINGS], _v[p1]);
+ debugC(4, kDebugLevelScripts, "[%s] -> %d", _game.strings[MAX_STRINGS], _v[p1]);
- g_agi->clearLines(22, 22, game.colorBg);
- g_agi->flushLines(22, 22);
+ clearLines(22, 22, _game.colorBg);
+ flushLines(22, 22);
}
-cmd(set_cursor_char) {
- if (curLogic->texts != NULL && (p0 - 1) <= curLogic->numTexts) {
- game.cursorChar = *curLogic->texts[p0 - 1];
+void AgiEngine::cmd_set_cursor_char(uint8 *p) {
+ if (_curLogic->texts != NULL && (p0 - 1) <= _curLogic->numTexts) {
+ _game.cursorChar = *_curLogic->texts[p0 - 1];
} else {
// default
- game.cursorChar = '_';
+ _game.cursorChar = '_';
}
}
-cmd(set_key) {
+void AgiEngine::cmd_set_key(uint8 *p) {
int key;
- if (game.lastController >= MAX_CONTROLLERS) {
+ if (_game.lastController >= MAX_CONTROLLERS) {
warning("Number of set.keys exceeded %d", MAX_CONTROLLERS);
return;
}
@@ -1413,36 +1399,35 @@ cmd(set_key) {
key = 256 * p1 + p0;
- game.controllers[game.lastController].keycode = key;
- game.controllers[game.lastController].controller = p2;
- game.lastController++;
+ _game.controllers[_game.lastController].keycode = key;
+ _game.controllers[_game.lastController].controller = p2;
+ _game.lastController++;
- game.controllerOccured[p2] = false;
+ _game.controllerOccured[p2] = false;
}
-cmd(set_string) {
+void AgiEngine::cmd_set_string(uint8 *p) {
// CM: to avoid crash in Groza (str = 150)
if (p0 > MAX_STRINGS)
return;
- strcpy(game.strings[p0], curLogic->texts[p1 - 1]);
+ strcpy(_game.strings[p0], _curLogic->texts[p1 - 1]);
}
-cmd(display) {
+void AgiEngine::cmd_display(uint8 *p) {
int len = 40;
- char *s = g_agi->wordWrapString(curLogic->texts[p2 - 1], &len);
+ char *s = wordWrapString(_curLogic->texts[p2 - 1], &len);
- g_agi->printText(s, p1, 0, p0, 40, game.colorFg, game.colorBg);
+ printText(s, p1, 0, p0, 40, _game.colorFg, _game.colorBg);
free(s);
}
-cmd(display_f) {
- debugC(4, kDebugLevelScripts, "p0 = %d", p0);
- g_agi->printText(curLogic->texts[_v[p2] - 1], _v[p1], 0, _v[p0], 40, game.colorFg, game.colorBg);
+void AgiEngine::cmd_display_f(uint8 *p) {
+ printText(_curLogic->texts[_v[p2] - 1], _v[p1], 0, _v[p0], 40, _game.colorFg, _game.colorBg);
}
-cmd(clear_text_rect) {
+void AgiEngine::cmd_clear_text_rect(uint8 *p) {
int c, x1, y1, x2, y2;
if ((c = p4) != 0)
@@ -1463,21 +1448,21 @@ cmd(clear_text_rect) {
if (y2 > GFX_HEIGHT)
y2 = GFX_HEIGHT - 1;
- g_gfx->drawRectangle(x1, y1, x2, y2, c);
- g_gfx->flushBlock(x1, y1, x2, y2);
+ _gfx->drawRectangle(x1, y1, x2, y2, c);
+ _gfx->flushBlock(x1, y1, x2, y2);
}
-cmd(toggle_monitor) {
+void AgiEngine::cmd_toggle_monitor(uint8 *p) {
report("toggle.monitor\n");
}
-cmd(echo_line) {
- strcpy((char *)game.inputBuffer, (const char *)game.echoBuffer);
- game.cursorPos = strlen((char *)game.inputBuffer);
- game.hasPrompt = 0;
+void AgiEngine::cmd_echo_line(uint8 *p) {
+ strcpy((char *)_game.inputBuffer, (const char *)_game.echoBuffer);
+ _game.cursorPos = strlen((char *)_game.inputBuffer);
+ _game.hasPrompt = 0;
}
-cmd(clear_lines) {
+void AgiEngine::cmd_clear_lines(uint8 *p) {
uint8 l;
// Residence 44 calls clear.lines(24,0,0), see Sarien bug #558423
@@ -1487,78 +1472,78 @@ cmd(clear_lines) {
// #1935838 and #1935842
l = (l <= 24) ? l : 24;
- g_agi->clearLines(p0, l, p2);
- g_agi->flushLines(p0, l);
+ clearLines(p0, l, p2);
+ flushLines(p0, l);
}
-cmd(print) {
+void AgiEngine::cmd_print(uint8 *p) {
int n = p0 < 1 ? 1 : p0;
- g_agi->print(curLogic->texts[n - 1], 0, 0, 0);
+ print(_curLogic->texts[n - 1], 0, 0, 0);
}
-cmd(print_f) {
+void AgiEngine::cmd_print_f(uint8 *p) {
int n = _v[p0] < 1 ? 1 : _v[p0];
- g_agi->print(curLogic->texts[n - 1], 0, 0, 0);
+ print(_curLogic->texts[n - 1], 0, 0, 0);
}
-cmd(print_at) {
+void AgiEngine::cmd_print_at(uint8 *p) {
int n = p0 < 1 ? 1 : p0;
debugC(4, kDebugLevelScripts, "%d %d %d %d", p0, p1, p2, p3);
- g_agi->print(curLogic->texts[n - 1], p1, p2, p3);
+ print(_curLogic->texts[n - 1], p1, p2, p3);
}
-cmd(print_at_v) {
+void AgiEngine::cmd_print_at_v(uint8 *p) {
int n = _v[p0] < 1 ? 1 : _v[p0];
- g_agi->print(curLogic->texts[n - 1], p1, p2, p3);
+ print(_curLogic->texts[n - 1], p1, p2, p3);
}
-cmd(push_script) {
+void AgiEngine::cmd_push_script(uint8 *p) {
// We run AGIMOUSE always as a side effect
- if (g_agi->getFeatures() & GF_AGIMOUSE || 1) {
- game.vars[27] = g_agi->_mouse.button;
- game.vars[28] = g_agi->_mouse.x / 2;
- game.vars[29] = g_agi->_mouse.y;
+ if (getFeatures() & GF_AGIMOUSE || 1) {
+ _game.vars[27] = _mouse.button;
+ _game.vars[28] = _mouse.x / 2;
+ _game.vars[29] = _mouse.y;
} else {
- if (g_agi->getVersion() >= 0x2915) {
+ if (getVersion() >= 0x2915) {
report("push.script\n");
}
}
}
-cmd(set_pri_base) {
+void AgiEngine::cmd_set_pri_base(uint8 *p) {
int i, x, pri;
report("Priority base set to %d\n", p0);
- // game.alt_pri = true;
+ // _game.alt_pri = true;
x = (_HEIGHT - p0) * _HEIGHT / 10;
for (i = 0; i < _HEIGHT; i++) {
pri = (i - p0) < 0 ? 4 : (i - p0) * _HEIGHT / x + 5;
if (pri > 15)
pri = 15;
- game.priTable[i] = pri;
+ _game.priTable[i] = pri;
}
}
-cmd(mouse_posn) {
- _v[p0] = WIN_TO_PIC_X(g_agi->_mouse.x);
- _v[p1] = WIN_TO_PIC_Y(g_agi->_mouse.y);
+void AgiEngine::cmd_mouse_posn(uint8 *p) {
+ _v[p0] = WIN_TO_PIC_X(_mouse.x);
+ _v[p1] = WIN_TO_PIC_Y(_mouse.y);
}
-cmd(shake_screen) {
+void AgiEngine::cmd_shake_screen(uint8 *p) {
int i;
// AGIPAL uses shake.screen values between 100 and 109 to set the palette
// (Checked the original AGIPAL-hack's shake.screen-routine's disassembly).
if (p0 >= 100 && p0 < 110) {
- if (g_agi->getFeatures() & GF_AGIPAL) {
- g_gfx->setAGIPal(p0);
+ if (getFeatures() & GF_AGIPAL) {
+ _gfx->setAGIPal(p0);
return;
} else {
warning("It looks like GF_AGIPAL flag is missing");
@@ -1567,208 +1552,213 @@ cmd(shake_screen) {
// Disables input while shaking to prevent bug
// #1678230: AGI: Entering text while screen is shaking
- int originalValue = game.inputEnabled;
- game.inputEnabled = 0;
+ bool originalValue = _game.inputEnabled;
+ _game.inputEnabled = false;
- g_gfx->shakeStart();
+ _gfx->shakeStart();
- g_sprites->commitBoth(); // Fixes SQ1 demo
+ _sprites->commitBoth(); // Fixes SQ1 demo
for (i = 4 * p0; i; i--) {
- g_gfx->shakeScreen(i & 1);
- g_gfx->flushBlock(0, 0, GFX_WIDTH - 1, GFX_HEIGHT - 1);
- g_agi->mainCycle();
+ _gfx->shakeScreen(i & 1);
+ _gfx->flushBlock(0, 0, GFX_WIDTH - 1, GFX_HEIGHT - 1);
+ mainCycle();
}
- g_gfx->shakeEnd();
+ _gfx->shakeEnd();
// Sets input back to what it was
- game.inputEnabled = originalValue;
-}
-
-static void (*agiCommand[183])(AgiEngine *, uint8 *) = {
- NULL, // 0x00
- cmd_increment,
- cmd_decrement,
- cmd_assignn,
- cmd_assignv,
- cmd_addn,
- cmd_addv,
- cmd_subn,
- cmd_subv, // 0x08
- cmd_lindirectv,
- cmd_rindirect,
- cmd_lindirectn,
- cmd_set,
- cmd_reset,
- cmd_toggle,
- cmd_set_v,
- cmd_reset_v, // 0x10
- cmd_toggle_v,
- cmd_new_room,
- cmd_new_room_f,
- cmd_load_logic,
- cmd_load_logic_f,
- cmd_call,
- cmd_call_f,
- cmd_load_pic, // 0x18
- cmd_draw_pic,
- cmd_show_pic,
- cmd_discard_pic,
- cmd_overlay_pic,
- cmd_show_pri_screen,
- cmd_load_view,
- cmd_load_view_f,
- cmd_discard_view, // 0x20
- cmd_animate_obj,
- cmd_unanimate_all,
- cmd_draw,
- cmd_erase,
- cmd_position,
- cmd_position_f,
- cmd_get_posn,
- cmd_reposition, // 0x28
- cmd_set_view,
- cmd_set_view_f,
- cmd_set_loop,
- cmd_set_loop_f,
- cmd_fix_loop,
- cmd_release_loop,
- cmd_set_cel,
- cmd_set_cel_f, // 0x30
- cmd_last_cel,
- cmd_current_cel,
- cmd_current_loop,
- cmd_current_view,
- cmd_number_of_loops,
- cmd_set_priority,
- cmd_set_priority_f,
- cmd_release_priority, // 0x38
- cmd_get_priority,
- cmd_stop_update,
- cmd_start_update,
- cmd_force_update,
- cmd_ignore_horizon,
- cmd_observe_horizon,
- cmd_set_horizon,
- cmd_object_on_water, // 0x40
- cmd_object_on_land,
- cmd_object_on_anything,
- cmd_ignore_objs,
- cmd_observe_objs,
- cmd_distance,
- cmd_stop_cycling,
- cmd_start_cycling,
- cmd_normal_cycle, // 0x48
- cmd_end_of_loop,
- cmd_reverse_cycle,
- cmd_reverse_loop,
- cmd_cycle_time,
- cmd_stop_motion,
- cmd_start_motion,
- cmd_step_size,
- cmd_step_time, // 0x50
- cmd_move_obj,
- cmd_move_obj_f,
- cmd_follow_ego,
- cmd_wander,
- cmd_normal_motion,
- cmd_set_dir,
- cmd_get_dir,
- cmd_ignore_blocks, // 0x58
- cmd_observe_blocks,
- cmd_block,
- cmd_unblock,
- cmd_get,
- cmd_get_f,
- cmd_drop,
- cmd_put,
- cmd_put_f, // 0x60
- cmd_get_room_f,
- cmd_load_sound,
- cmd_sound,
- cmd_stop_sound,
- cmd_print,
- cmd_print_f,
- cmd_display,
- cmd_display_f, // 0x68
- cmd_clear_lines,
- cmd_text_screen,
- cmd_graphics,
- cmd_set_cursor_char,
- cmd_set_text_attribute,
- cmd_shake_screen,
- cmd_configure_screen,
- cmd_status_line_on, // 0x70
- cmd_status_line_off,
- cmd_set_string,
- cmd_get_string,
- cmd_word_to_string,
- cmd_parse,
- cmd_get_num,
- cmd_prevent_input,
- cmd_accept_input, // 0x78
- cmd_set_key,
- cmd_add_to_pic,
- cmd_add_to_pic_f,
- cmd_status,
- cmd_save_game,
- cmd_load_game,
- cmd_init_disk,
- cmd_restart_game, // 0x80
- cmd_show_obj,
- cmd_random,
- cmd_program_control,
- cmd_player_control,
- cmd_obj_status_f,
- cmd_quit,
- cmd_show_mem,
- cmd_pause, // 0x88
- cmd_echo_line,
- cmd_cancel_line,
- cmd_init_joy,
- cmd_toggle_monitor,
- cmd_version,
- cmd_script_size,
- cmd_set_game_id,
- cmd_log, // 0x90
- cmd_set_scan_start,
- cmd_reset_scan_start,
- cmd_reposition_to,
- cmd_reposition_to_f,
- cmd_trace_on,
- cmd_trace_info,
- cmd_print_at,
- cmd_print_at_v, // 0x98
- cmd_discard_view,
- cmd_clear_text_rect,
- cmd_set_upper_left,
- cmd_set_menu,
- cmd_set_menu_item,
- cmd_submit_menu,
- cmd_enable_item,
- cmd_disable_item, // 0xa0
- cmd_menu_input,
- cmd_show_obj_v,
- cmd_open_dialogue,
- cmd_close_dialogue,
- cmd_mul_n,
- cmd_mul_v,
- cmd_div_n,
- cmd_div_v, // 0xa8
- cmd_close_window,
- cmd_set_simple,
- cmd_push_script,
- cmd_pop_script,
- cmd_hold_key,
- cmd_set_pri_base,
- cmd_discard_sound,
- cmd_hide_mouse, // 0xb0
- cmd_allow_menu,
- cmd_show_mouse,
- cmd_fence_mouse,
- cmd_mouse_posn,
- cmd_release_key,
- cmd_adj_ego_move_to_x_y
-};
+ _game.inputEnabled = originalValue;
+}
+
+void AgiEngine::setupOpcodes() {
+ AgiCommand tmp[] = {
+ NULL, // 0x00
+ &AgiEngine::cmd_increment,
+ &AgiEngine::cmd_decrement,
+ &AgiEngine::cmd_assignn,
+ &AgiEngine::cmd_assignv,
+ &AgiEngine::cmd_addn,
+ &AgiEngine::cmd_addv,
+ &AgiEngine::cmd_subn,
+ &AgiEngine::cmd_subv, // 0x08
+ &AgiEngine::cmd_lindirectv,
+ &AgiEngine::cmd_rindirect,
+ &AgiEngine::cmd_lindirectn,
+ &AgiEngine::cmd_set,
+ &AgiEngine::cmd_reset,
+ &AgiEngine::cmd_toggle,
+ &AgiEngine::cmd_set_v,
+ &AgiEngine::cmd_reset_v, // 0x10
+ &AgiEngine::cmd_toggle_v,
+ &AgiEngine::cmd_new_room,
+ &AgiEngine::cmd_new_room_f,
+ &AgiEngine::cmd_load_logic,
+ &AgiEngine::cmd_load_logic_f,
+ &AgiEngine::cmd_call,
+ &AgiEngine::cmd_call_f,
+ &AgiEngine::cmd_load_pic, // 0x18
+ &AgiEngine::cmd_draw_pic,
+ &AgiEngine::cmd_show_pic,
+ &AgiEngine::cmd_discard_pic,
+ &AgiEngine::cmd_overlay_pic,
+ &AgiEngine::cmd_show_pri_screen,
+ &AgiEngine::cmd_load_view,
+ &AgiEngine::cmd_load_view_f,
+ &AgiEngine::cmd_discard_view, // 0x20
+ &AgiEngine::cmd_animate_obj,
+ &AgiEngine::cmd_unanimate_all,
+ &AgiEngine::cmd_draw,
+ &AgiEngine::cmd_erase,
+ &AgiEngine::cmd_position,
+ &AgiEngine::cmd_position_f,
+ &AgiEngine::cmd_get_posn,
+ &AgiEngine::cmd_reposition, // 0x28
+ &AgiEngine::cmd_set_view,
+ &AgiEngine::cmd_set_view_f,
+ &AgiEngine::cmd_set_loop,
+ &AgiEngine::cmd_set_loop_f,
+ &AgiEngine::cmd_fix_loop,
+ &AgiEngine::cmd_release_loop,
+ &AgiEngine::cmd_set_cel,
+ &AgiEngine::cmd_set_cel_f, // 0x30
+ &AgiEngine::cmd_last_cel,
+ &AgiEngine::cmd_current_cel,
+ &AgiEngine::cmd_current_loop,
+ &AgiEngine::cmd_current_view,
+ &AgiEngine::cmd_number_of_loops,
+ &AgiEngine::cmd_set_priority,
+ &AgiEngine::cmd_set_priority_f,
+ &AgiEngine::cmd_release_priority, // 0x38
+ &AgiEngine::cmd_get_priority,
+ &AgiEngine::cmd_stop_update,
+ &AgiEngine::cmd_start_update,
+ &AgiEngine::cmd_force_update,
+ &AgiEngine::cmd_ignore_horizon,
+ &AgiEngine::cmd_observe_horizon,
+ &AgiEngine::cmd_set_horizon,
+ &AgiEngine::cmd_object_on_water, // 0x40
+ &AgiEngine::cmd_object_on_land,
+ &AgiEngine::cmd_object_on_anything,
+ &AgiEngine::cmd_ignore_objs,
+ &AgiEngine::cmd_observe_objs,
+ &AgiEngine::cmd_distance,
+ &AgiEngine::cmd_stop_cycling,
+ &AgiEngine::cmd_start_cycling,
+ &AgiEngine::cmd_normal_cycle, // 0x48
+ &AgiEngine::cmd_end_of_loop,
+ &AgiEngine::cmd_reverse_cycle,
+ &AgiEngine::cmd_reverse_loop,
+ &AgiEngine::cmd_cycle_time,
+ &AgiEngine::cmd_stop_motion,
+ &AgiEngine::cmd_start_motion,
+ &AgiEngine::cmd_step_size,
+ &AgiEngine::cmd_step_time, // 0x50
+ &AgiEngine::cmd_move_obj,
+ &AgiEngine::cmd_move_obj_f,
+ &AgiEngine::cmd_follow_ego,
+ &AgiEngine::cmd_wander,
+ &AgiEngine::cmd_normal_motion,
+ &AgiEngine::cmd_set_dir,
+ &AgiEngine::cmd_get_dir,
+ &AgiEngine::cmd_ignore_blocks, // 0x58
+ &AgiEngine::cmd_observe_blocks,
+ &AgiEngine::cmd_block,
+ &AgiEngine::cmd_unblock,
+ &AgiEngine::cmd_get,
+ &AgiEngine::cmd_get_f,
+ &AgiEngine::cmd_drop,
+ &AgiEngine::cmd_put,
+ &AgiEngine::cmd_put_f, // 0x60
+ &AgiEngine::cmd_get_room_f,
+ &AgiEngine::cmd_load_sound,
+ &AgiEngine::cmd_sound,
+ &AgiEngine::cmd_stop_sound,
+ &AgiEngine::cmd_print,
+ &AgiEngine::cmd_print_f,
+ &AgiEngine::cmd_display,
+ &AgiEngine::cmd_display_f, // 0x68
+ &AgiEngine::cmd_clear_lines,
+ &AgiEngine::cmd_text_screen,
+ &AgiEngine::cmd_graphics,
+ &AgiEngine::cmd_set_cursor_char,
+ &AgiEngine::cmd_set_text_attribute,
+ &AgiEngine::cmd_shake_screen,
+ &AgiEngine::cmd_configure_screen,
+ &AgiEngine::cmd_status_line_on, // 0x70
+ &AgiEngine::cmd_status_line_off,
+ &AgiEngine::cmd_set_string,
+ &AgiEngine::cmd_get_string,
+ &AgiEngine::cmd_word_to_string,
+ &AgiEngine::cmd_parse,
+ &AgiEngine::cmd_get_num,
+ &AgiEngine::cmd_prevent_input,
+ &AgiEngine::cmd_accept_input, // 0x78
+ &AgiEngine::cmd_set_key,
+ &AgiEngine::cmd_add_to_pic,
+ &AgiEngine::cmd_add_to_pic_f,
+ &AgiEngine::cmd_status,
+ &AgiEngine::cmd_save_game,
+ &AgiEngine::cmd_load_game,
+ &AgiEngine::cmd_init_disk,
+ &AgiEngine::cmd_restart_game, // 0x80
+ &AgiEngine::cmd_show_obj,
+ &AgiEngine::cmd_random,
+ &AgiEngine::cmd_program_control,
+ &AgiEngine::cmd_player_control,
+ &AgiEngine::cmd_obj_status_f,
+ &AgiEngine::cmd_quit,
+ &AgiEngine::cmd_show_mem,
+ &AgiEngine::cmd_pause, // 0x88
+ &AgiEngine::cmd_echo_line,
+ &AgiEngine::cmd_cancel_line,
+ &AgiEngine::cmd_init_joy,
+ &AgiEngine::cmd_toggle_monitor,
+ &AgiEngine::cmd_version,
+ &AgiEngine::cmd_script_size,
+ &AgiEngine::cmd_set_game_id,
+ &AgiEngine::cmd_log, // 0x90
+ &AgiEngine::cmd_set_scan_start,
+ &AgiEngine::cmd_reset_scan_start,
+ &AgiEngine::cmd_reposition_to,
+ &AgiEngine::cmd_reposition_to_f,
+ &AgiEngine::cmd_trace_on,
+ &AgiEngine::cmd_trace_info,
+ &AgiEngine::cmd_print_at,
+ &AgiEngine::cmd_print_at_v, // 0x98
+ &AgiEngine::cmd_discard_view, // Opcode repeated from 0x20 ?
+ &AgiEngine::cmd_clear_text_rect,
+ &AgiEngine::cmd_set_upper_left,
+ &AgiEngine::cmd_set_menu,
+ &AgiEngine::cmd_set_menu_item,
+ &AgiEngine::cmd_submit_menu,
+ &AgiEngine::cmd_enable_item,
+ &AgiEngine::cmd_disable_item, // 0xa0
+ &AgiEngine::cmd_menu_input,
+ &AgiEngine::cmd_show_obj_v,
+ &AgiEngine::cmd_open_dialogue,
+ &AgiEngine::cmd_close_dialogue,
+ &AgiEngine::cmd_mul_n,
+ &AgiEngine::cmd_mul_v,
+ &AgiEngine::cmd_div_n,
+ &AgiEngine::cmd_div_v, // 0xa8
+ &AgiEngine::cmd_close_window,
+ &AgiEngine::cmd_set_simple,
+ &AgiEngine::cmd_push_script,
+ &AgiEngine::cmd_pop_script,
+ &AgiEngine::cmd_hold_key,
+ &AgiEngine::cmd_set_pri_base,
+ &AgiEngine::cmd_discard_sound,
+ &AgiEngine::cmd_hide_mouse, // 0xb0
+ &AgiEngine::cmd_allow_menu,
+ &AgiEngine::cmd_show_mouse,
+ &AgiEngine::cmd_fence_mouse,
+ &AgiEngine::cmd_mouse_posn,
+ &AgiEngine::cmd_release_key,
+ &AgiEngine::cmd_adj_ego_move_to_x_y
+ };
+ assert(ARRAYSIZE(_agiCommands) == ARRAYSIZE(tmp));
+ for (int i = 0; i < ARRAYSIZE(tmp); ++i)
+ _agiCommands[i] = tmp[i];
+}
/**
* Execute a logic script
@@ -1781,6 +1771,9 @@ int AgiEngine::runLogic(int n) {
int num = 0;
ScriptPos sp;
+ debugC(2, kDebugLevelScripts, "=================");
+ debugC(2, kDebugLevelScripts, "runLogic(%d)", n);
+
sp.script = n;
sp.curIP = 0;
_game.execStack.push_back(sp);
@@ -1792,12 +1785,12 @@ int AgiEngine::runLogic(int n) {
}
_game.lognum = n;
- curLogic = &_game.logics[_game.lognum];
+ _curLogic = &_game.logics[_game.lognum];
- code = curLogic->data;
- curLogic->cIP = curLogic->sIP;
+ code = _curLogic->data;
+ _curLogic->cIP = _curLogic->sIP;
- timerHack = 0;
+ _timerHack = 0;
while (ip < _game.logics[n].size && !(shouldQuit() || _restartGame)) {
if (_debug.enabled) {
if (_debug.steps > 0) {
@@ -1807,6 +1800,7 @@ int AgiEngine::runLogic(int n) {
}
} else {
_sprites->blitBoth();
+ _sprites->commitBoth();
do {
mainCycle();
} while (!_debug.steps && _debug.enabled);
@@ -1816,6 +1810,11 @@ int AgiEngine::runLogic(int n) {
_game.execStack.back().curIP = ip;
+ char st[101];
+ int sz = MIN(_game.execStack.size(), 100u);
+ memset(st, '.', sz);
+ st[sz] = 0;
+
switch (op = *(code + ip++)) {
case 0xff: // if (open/close)
testIfCode(n);
@@ -1826,13 +1825,16 @@ int AgiEngine::runLogic(int n) {
// timer must keep running even in goto loops,
// but AGI engine can't do that :(
- if (timerHack > 20) {
+ if (_timerHack > 20) {
pollTimer();
updateTimer();
- timerHack = 0;
+ _timerHack = 0;
}
break;
case 0x00: // return
+ debugC(2, kDebugLevelScripts, "%sreturn() // Logic %d", st, n);
+ debugC(2, kDebugLevelScripts, "=================");
+
_game.execStack.pop_back();
return 1;
default:
@@ -1840,8 +1842,9 @@ int AgiEngine::runLogic(int n) {
memmove(p, code + ip, num);
memset(p + num, 0, CMD_BSIZE - num);
- debugC(2, kDebugLevelScripts, "%s(%d %d %d)", logicNamesCmd[op].name, p[0], p[1], p[2]);
- agiCommand[op](this, p);
+ debugC(2, kDebugLevelScripts, "%s%s(%d %d %d)", st, logicNamesCmd[op].name, p[0], p[1], p[2]);
+
+ (this->*_agiCommands[op])(p);
ip += num;
}
@@ -1857,7 +1860,7 @@ int AgiEngine::runLogic(int n) {
void AgiEngine::executeAgiCommand(uint8 op, uint8 *p) {
debugC(2, kDebugLevelScripts, "%s(%d %d %d)", logicNamesCmd[op].name, p[0], p[1], p[2]);
- agiCommand[op] (this, p);
+ (this->*_agiCommands[op])(p);
}
} // End of namespace Agi
diff --git a/engines/agi/op_test.cpp b/engines/agi/op_test.cpp
index 71d307556b..ab4f6cadc5 100644
--- a/engines/agi/op_test.cpp
+++ b/engines/agi/op_test.cpp
@@ -29,37 +29,23 @@
namespace Agi {
-static uint8 testObjRight(uint8, uint8, uint8, uint8, uint8);
-static uint8 testObjCentre(uint8, uint8, uint8, uint8, uint8);
-static uint8 testObjInBox(uint8, uint8, uint8, uint8, uint8);
-static uint8 testPosn(uint8, uint8, uint8, uint8, uint8);
-static uint8 testSaid(uint8, uint8 *);
-static uint8 testController(uint8);
-static uint8 testKeypressed();
-static uint8 testCompareStrings(uint8, uint8);
-
-static AgiEngine *g_agi;
-#define game g_agi->_game
-
-#define ip (game.logics[lognum].cIP)
-#define code (game.logics[lognum].data)
-
-#define testEqual(v1, v2) (g_agi->getvar(v1) == (v2))
-#define testLess(v1, v2) (g_agi->getvar(v1) < (v2))
-#define testGreater(v1, v2) (g_agi->getvar(v1) > (v2))
-#define testIsSet(flag) (g_agi->getflag(flag))
-#define testHas(obj) (g_agi->objectGetLocation(obj) == EGO_OWNED)
-#define testObjInRoom(obj, v) (g_agi->objectGetLocation(obj) == g_agi->getvar(v))
-
-extern int timerHack; // For the timer loop in MH1 logic 153
-
-static uint8 testCompareStrings(uint8 s1, uint8 s2) {
+#define ip (_game.logics[lognum].cIP)
+#define code (_game.logics[lognum].data)
+
+#define testEqual(v1, v2) (getvar(v1) == (v2))
+#define testLess(v1, v2) (getvar(v1) < (v2))
+#define testGreater(v1, v2) (getvar(v1) > (v2))
+#define testIsSet(flag) (getflag(flag))
+#define testHas(obj) (objectGetLocation(obj) == EGO_OWNED)
+#define testObjInRoom(obj, v) (objectGetLocation(obj) == getvar(v))
+
+uint8 AgiEngine::testCompareStrings(uint8 s1, uint8 s2) {
char ms1[MAX_STRINGLEN];
char ms2[MAX_STRINGLEN];
int j, k, l;
- strcpy(ms1, game.strings[s1]);
- strcpy(ms2, game.strings[s2]);
+ strcpy(ms1, _game.strings[s1]);
+ strcpy(ms2, _game.strings[s2]);
l = strlen(ms1);
for (k = 0, j = 0; k < l; k++) {
@@ -106,16 +92,16 @@ static uint8 testCompareStrings(uint8 s1, uint8 s2) {
return !strcmp(ms1, ms2);
}
-static uint8 testKeypressed() {
- int x = game.keypress;
+uint8 AgiEngine::testKeypressed() {
+ int x = _game.keypress;
- game.keypress = 0;
+ _game.keypress = 0;
if (!x) {
- int mode = game.inputMode;
+ InputMode mode = _game.inputMode;
- game.inputMode = INPUT_NONE;
- g_agi->mainCycle();
- game.inputMode = mode;
+ _game.inputMode = INPUT_NONE;
+ mainCycle();
+ _game.inputMode = mode;
}
if (x)
@@ -124,12 +110,12 @@ static uint8 testKeypressed() {
return x;
}
-static uint8 testController(uint8 cont) {
- return (game.controllerOccured[cont] ? 1 : 0);
+uint8 AgiEngine::testController(uint8 cont) {
+ return (_game.controllerOccured[cont] ? 1 : 0);
}
-static uint8 testPosn(uint8 n, uint8 x1, uint8 y1, uint8 x2, uint8 y2) {
- VtEntry *v = &game.viewTable[n];
+uint8 AgiEngine::testPosn(uint8 n, uint8 x1, uint8 y1, uint8 x2, uint8 y2) {
+ VtEntry *v = &_game.viewTable[n];
uint8 r;
r = v->xPos >= x1 && v->yPos >= y1 && v->xPos <= x2 && v->yPos <= y2;
@@ -139,35 +125,35 @@ static uint8 testPosn(uint8 n, uint8 x1, uint8 y1, uint8 x2, uint8 y2) {
return r;
}
-static uint8 testObjInBox(uint8 n, uint8 x1, uint8 y1, uint8 x2, uint8 y2) {
- VtEntry *v = &game.viewTable[n];
+uint8 AgiEngine::testObjInBox(uint8 n, uint8 x1, uint8 y1, uint8 x2, uint8 y2) {
+ VtEntry *v = &_game.viewTable[n];
return v->xPos >= x1 &&
v->yPos >= y1 && v->xPos + v->xSize - 1 <= x2 && v->yPos <= y2;
}
// if n is in centre of box
-static uint8 testObjCentre(uint8 n, uint8 x1, uint8 y1, uint8 x2, uint8 y2) {
- VtEntry *v = &game.viewTable[n];
+uint8 AgiEngine::testObjCentre(uint8 n, uint8 x1, uint8 y1, uint8 x2, uint8 y2) {
+ VtEntry *v = &_game.viewTable[n];
return v->xPos + v->xSize / 2 >= x1 &&
v->xPos + v->xSize / 2 <= x2 && v->yPos >= y1 && v->yPos <= y2;
}
// if nect N is in right corner
-static uint8 testObjRight(uint8 n, uint8 x1, uint8 y1, uint8 x2, uint8 y2) {
- VtEntry *v = &game.viewTable[n];
+uint8 AgiEngine::testObjRight(uint8 n, uint8 x1, uint8 y1, uint8 x2, uint8 y2) {
+ VtEntry *v = &_game.viewTable[n];
return v->xPos + v->xSize - 1 >= x1 &&
v->xPos + v->xSize - 1 <= x2 && v->yPos >= y1 && v->yPos <= y2;
}
// When player has entered something, it is parsed elsewhere
-static uint8 testSaid(uint8 nwords, uint8 *cc) {
- int c, n = game.numEgoWords;
+uint8 AgiEngine::testSaid(uint8 nwords, uint8 *cc) {
+ int c, n = _game.numEgoWords;
int z = 0;
- if (g_agi->getflag(fSaidAcceptedInput) || !g_agi->getflag(fEnteredCli))
+ if (getflag(fSaidAcceptedInput) || !getflag(fEnteredCli))
return false;
// FR:
@@ -198,7 +184,7 @@ static uint8 testSaid(uint8 nwords, uint8 *cc) {
case 1: // any word
break;
default:
- if (game.egoWords[c].id != z)
+ if (_game.egoWords[c].id != z)
return false;
break;
}
@@ -213,13 +199,12 @@ static uint8 testSaid(uint8 nwords, uint8 *cc) {
if (nwords != 0 && READ_LE_UINT16(cc) != 9999)
return false;
- g_agi->setflag(fSaidAcceptedInput, true);
+ setflag(fSaidAcceptedInput, true);
return true;
}
int AgiEngine::testIfCode(int lognum) {
- g_agi = this;
int ec = true;
int retval = true;
uint8 op = 0;
@@ -263,32 +248,32 @@ int AgiEngine::testIfCode(int lognum) {
case 0x01:
ec = testEqual(p[0], p[1]);
if (p[0] == 11)
- timerHack++;
+ _timerHack++;
break;
case 0x02:
ec = testEqual(p[0], getvar(p[1]));
if (p[0] == 11 || p[1] == 11)
- timerHack++;
+ _timerHack++;
break;
case 0x03:
ec = testLess(p[0], p[1]);
if (p[0] == 11)
- timerHack++;
+ _timerHack++;
break;
case 0x04:
ec = testLess(p[0], getvar(p[1]));
if (p[0] == 11 || p[1] == 11)
- timerHack++;
+ _timerHack++;
break;
case 0x05:
ec = testGreater(p[0], p[1]);
if (p[0] == 11)
- timerHack++;
+ _timerHack++;
break;
case 0x06:
ec = testGreater(p[0], getvar(p[1]));
if (p[0] == 11 || p[1] == 11)
- timerHack++;
+ _timerHack++;
break;
case 0x07:
ec = testIsSet(p[0]);
@@ -319,7 +304,7 @@ int AgiEngine::testIfCode(int lognum) {
ip++; // skip num_words opcode
break;
case 0x0F:
- debugC(7, kDebugLevelScripts, "comparing [%s], [%s]", game.strings[p[0]], game.strings[p[1]]);
+ debugC(7, kDebugLevelScripts, "comparing [%s], [%s]", _game.strings[p[0]], _game.strings[p[1]]);
ec = testCompareStrings(p[0], p[1]);
break;
case 0x10:
@@ -338,7 +323,7 @@ int AgiEngine::testIfCode(int lognum) {
// This command is used at least in the Amiga version of Gold Rush! v2.05 1989-03-09
// (AGI 2.316) in logics 1, 3, 5, 6, 137 and 192 (Logic.192 revealed this command's nature).
// TODO: Check this command's implementation using disassembly just to be sure.
- ec = game.viewTable[0].flags & ADJ_EGO_XY;
+ ec = _game.viewTable[0].flags & ADJ_EGO_XY;
debugC(7, kDebugLevelScripts, "op_test: in.motion.using.mouse = %s (Amiga-specific testcase 19)", ec ? "true" : "false");
break;
default:
diff --git a/engines/agi/picture.cpp b/engines/agi/picture.cpp
index 60877de430..dc77433cb2 100644
--- a/engines/agi/picture.cpp
+++ b/engines/agi/picture.cpp
@@ -476,7 +476,7 @@ void PictureMgr::plotPattern(int x, int y) {
// new purpose for temp16
- temp16 =( pen_size<<1) +1; // pen size
+ temp16 = (pen_size << 1) + 1; // pen size
pen_final_y += temp16; // the last row of this shape
temp16 = temp16 << 1;
pen_width = temp16; // width of shape?
@@ -495,7 +495,7 @@ void PictureMgr::plotPattern(int x, int y) {
} else {
circleCond = ((_patCode & 0x10) != 0);
counterStep = 4;
- ditherCond = 0x02;
+ ditherCond = 0x01;
}
for (; pen_y < pen_final_y; pen_y++) {
@@ -503,10 +503,12 @@ void PictureMgr::plotPattern(int x, int y) {
for (counter = 0; counter <= pen_width; counter += counterStep) {
if (circleCond || ((binary_list[counter>>1] & circle_word) != 0)) {
- temp8 = t % 2;
- t = t >> 1;
- if (temp8 != 0)
- t = t ^ 0xB8;
+ if ((_patCode & 0x20) != 0) {
+ temp8 = t % 2;
+ t = t >> 1;
+ if (temp8 != 0)
+ t = t ^ 0xB8;
+ }
// == box plot, != circle plot
if ((_patCode & 0x20) == 0 || (t & 0x03) == ditherCond)
diff --git a/engines/agi/preagi.cpp b/engines/agi/preagi.cpp
index 35285798d4..1a5698dffc 100644
--- a/engines/agi/preagi.cpp
+++ b/engines/agi/preagi.cpp
@@ -68,8 +68,8 @@ void PreAgiEngine::initialize() {
// drivers, and I'm not sure what they are. For now, they might
// as well be called "PC Speaker" and "Not PC Speaker".
- switch (MidiDriver::detectMusicDriver(MDT_PCSPK)) {
- case MD_PCSPK:
+ switch (MidiDriver::getMusicType(MidiDriver::detectDevice(MDT_PCSPK))) {
+ case MT_PCSPK:
_soundemu = SOUND_EMU_PC;
break;
default:
diff --git a/engines/agi/saveload.cpp b/engines/agi/saveload.cpp
index b7e830dc53..88b14dcfe2 100644
--- a/engines/agi/saveload.cpp
+++ b/engines/agi/saveload.cpp
@@ -132,7 +132,7 @@ int AgiEngine::saveGame(const char *fileName, const char *description) {
out->writeSint16BE((int16)_game.hasPrompt);
out->writeSint16BE((int16)_game.gameFlags);
- out->writeSint16BE((int16)_game.inputEnabled);
+ out->writeSint16BE(_game.inputEnabled);
for (i = 0; i < _HEIGHT; i++)
out->writeByte(_game.priTable[i]);
@@ -302,7 +302,7 @@ int AgiEngine::loadGame(const char *fileName, bool checkId) {
// TODO: played time
}
- _game.state = in->readByte();
+ _game.state = (State)in->readByte();
in->read(loadId, 8);
if (strcmp(loadId, _game.id) && checkId) {
@@ -361,7 +361,7 @@ int AgiEngine::loadGame(const char *fileName, bool checkId) {
_game.echoBuffer[0] = 0;
_game.keypress = 0;
- _game.inputMode = in->readSint16BE();
+ _game.inputMode = (InputMode)in->readSint16BE();
_game.lognum = in->readSint16BE();
_game.playerControl = in->readSint16BE();
@@ -864,6 +864,10 @@ int AgiEngine::saveGameDialog() {
sprintf(fileName, "%s", getSavegameFilename(_firstSlot + slot));
debugC(8, kDebugLevelMain | kDebugLevelResources, "file is [%s]", fileName);
+ // Make sure all graphics was blitted to screen. This fixes bug
+ // #2960567: "AGI: Ego partly erased in Load/Save thumbnails"
+ _gfx->doUpdate();
+
int result = saveGame(fileName, desc);
if (result == errOK)
diff --git a/engines/agi/sound.cpp b/engines/agi/sound.cpp
index ca5d42d981..cb4e307ea6 100644
--- a/engines/agi/sound.cpp
+++ b/engines/agi/sound.cpp
@@ -23,23 +23,21 @@
*
*/
-#include "common/md5.h"
-#include "common/config-manager.h"
-#include "common/fs.h"
-#include "common/random.h"
-#include "common/str-array.h"
-
#include "agi/agi.h"
-namespace Agi {
+#include "agi/sound_2gs.h"
+#include "agi/sound_coco3.h"
+#include "agi/sound_midi.h"
+#include "agi/sound_sarien.h"
+#include "agi/sound_pcjr.h"
-#define USE_INTERPOLATION
+namespace Agi {
//
// TODO: add support for variable sampling rate in the output device
//
-AgiSound *AgiSound::createFromRawResource(uint8 *data, uint32 len, int resnum, SoundMgr &manager) {
+AgiSound *AgiSound::createFromRawResource(uint8 *data, uint32 len, int resnum, SoundMgr &manager, int soundemu) {
if (data == NULL || len < 2) // Check for too small resource or no resource at all
return NULL;
uint16 type = READ_LE_UINT16(data);
@@ -48,27 +46,19 @@ AgiSound *AgiSound::createFromRawResource(uint8 *data, uint32 len, int resnum, S
case AGI_SOUND_SAMPLE:
return new IIgsSample(data, len, resnum, manager);
case AGI_SOUND_MIDI:
- return new IIgsMidi (data, len, resnum, manager);
+ return new IIgsMidi(data, len, resnum, manager);
case AGI_SOUND_4CHN:
- return new PCjrSound (data, len, resnum, manager);
+ if (soundemu == SOUND_EMU_MIDI) {
+ return new MIDISound(data, len, resnum, manager);
+ } else {
+ return new PCjrSound(data, len, resnum, manager);
+ }
}
warning("Sound resource (%d) has unknown type (0x%04x). Not using the sound", resnum, type);
return NULL;
}
-IIgsMidi::IIgsMidi(uint8 *data, uint32 len, int resnum, SoundMgr &manager) : AgiSound(manager) {
- _data = data; // Save the resource pointer
- _ptr = _data + 2; // Set current position to just after the header
- _len = len; // Save the resource's length
- _type = READ_LE_UINT16(data); // Read sound resource's type
- _midiTicks = _soundBufTicks = 0;
- _isValid = (_type == AGI_SOUND_MIDI) && (_data != NULL) && (_len >= 2);
-
- if (!_isValid) // Check for errors
- warning("Error creating Apple IIGS midi sound from resource %d (Type %d, length %d)", resnum, _type, len);
-}
-
PCjrSound::PCjrSound(uint8 *data, uint32 len, int resnum, SoundMgr &manager) : AgiSound(manager) {
_data = data; // Save the resource pointer
_len = len; // Save the resource's length
@@ -86,247 +76,12 @@ const uint8 *PCjrSound::getVoicePointer(uint voiceNum) {
return _data + voiceStartOffset;
}
-IIgsSample::IIgsSample(uint8 *data, uint32 len, int resnum, SoundMgr &manager) : AgiSound(manager) {
- Common::MemoryReadStream stream(data, len, DisposeAfterUse::YES);
-
- // Check that the header was read ok and that it's of the correct type
- if (_header.read(stream) && _header.type == AGI_SOUND_SAMPLE) { // An Apple IIGS AGI sample resource
- uint32 sampleStartPos = stream.pos();
- uint32 tailLen = stream.size() - sampleStartPos;
-
- if (tailLen < _header.sampleSize) { // Check if there's no room for the sample data in the stream
- // Apple IIGS Manhunter I: Sound resource 16 has only 16074 bytes
- // of sample data although header says it should have 16384 bytes.
- warning("Apple IIGS sample (%d) too short (%d bytes. Should be %d bytes). Using the part that's left",
- resnum, tailLen, _header.sampleSize);
-
- _header.sampleSize = (uint16) tailLen; // Use the part that's left
- }
-
- if (_header.pitch > 0x7F) { // Check if the pitch is invalid
- warning("Apple IIGS sample (%d) has too high pitch (0x%02x)", resnum, _header.pitch);
-
- _header.pitch &= 0x7F; // Apple IIGS AGI probably did it this way too
- }
-
- // Finalize the header info using the 8-bit unsigned sample data
- _header.finalize(stream);
-
- // Convert sample data from 8-bit unsigned to 8-bit signed format
- stream.seek(sampleStartPos);
- _sample = new int8[_header.sampleSize];
-
- if (_sample != NULL)
- _isValid = SoundMgr::convertWave(stream, _sample, _header.sampleSize);
- }
-
- if (!_isValid) // Check for errors
- warning("Error creating Apple IIGS sample from resource %d (Type %d, length %d)", resnum, _header.type, len);
-}
-
-/** Reads an Apple IIGS envelope from then given stream. */
-bool IIgsEnvelope::read(Common::SeekableReadStream &stream) {
- for (int segNum = 0; segNum < ENVELOPE_SEGMENT_COUNT; segNum++) {
- seg[segNum].bp = stream.readByte();
- seg[segNum].inc = stream.readUint16LE();
- }
-
- return !(stream.eos() || stream.err());
-}
-
-/** Reads an Apple IIGS wave information structure from the given stream. */
-bool IIgsWaveInfo::read(Common::SeekableReadStream &stream, bool ignoreAddr) {
- top = stream.readByte();
- addr = stream.readByte() * 256;
- size = (1 << (stream.readByte() & 7)) * 256;
-
- // Read packed mode byte and parse it into parts
- byte packedModeByte = stream.readByte();
- channel = (packedModeByte >> 4) & 1; // Bit 4
- mode = (packedModeByte >> 1) & 3; // Bits 1-2
- halt = (packedModeByte & 1) != 0; // Bit 0 (Converted to boolean)
-
- relPitch = stream.readSint16LE();
-
- // Zero the wave address if we want to ignore the wave address info
- if (ignoreAddr)
- addr = 0;
-
- return !(stream.eos() || stream.err());
-}
-
-bool IIgsWaveInfo::finalize(Common::SeekableReadStream &uint8Wave) {
- uint32 startPos = uint8Wave.pos(); // Save stream's starting position
- uint8Wave.seek(addr, SEEK_CUR); // Seek to wave's address
-
- // Calculate the true sample size (A zero ends the sample prematurely)
- uint trueSize = size; // Set a default value for the result
- for (uint i = 0; i < size; i++) {
- if (uint8Wave.readByte() == 0) {
- trueSize = i;
- // A zero in the sample stream turns off looping
- // (At least that's what MESS 0.117 and KEGS32 0.91 seem to do)
- if (mode == OSC_MODE_LOOP)
- mode = OSC_MODE_ONESHOT;
- break;
- }
- }
- size = trueSize; // Set the true sample size
-
- uint8Wave.seek(startPos); // Seek back to the stream's starting position
-
- return true;
-}
-
-bool IIgsOscillator::finalize(Common::SeekableReadStream &uint8Wave) {
- for (uint i = 0; i < WAVES_PER_OSCILLATOR; i++)
- if (!waves[i].finalize(uint8Wave))
- return false;
-
- return true;
-}
-
-bool IIgsOscillatorList::read(Common::SeekableReadStream &stream, uint oscillatorCount, bool ignoreAddr) {
- // First read the A waves and then the B waves for the oscillators
- for (uint waveNum = 0; waveNum < WAVES_PER_OSCILLATOR; waveNum++)
- for (uint oscNum = 0; oscNum < oscillatorCount; oscNum++)
- if (!osc[oscNum].waves[waveNum].read(stream, ignoreAddr))
- return false;
-
- count = oscillatorCount; // Set the oscillator count
-
- return true;
-}
-
-bool IIgsOscillatorList::finalize(Common::SeekableReadStream &uint8Wave) {
- for (uint i = 0; i < count; i++)
- if (!osc[i].finalize(uint8Wave))
- return false;
-
- return true;
-}
-
-bool IIgsInstrumentHeader::read(Common::SeekableReadStream &stream, bool ignoreAddr) {
- env.read(stream);
- relseg = stream.readByte();
- /*byte priority =*/ stream.readByte(); // Not needed? 32 in all tested data.
- bendrange = stream.readByte();
- vibdepth = stream.readByte();
- vibspeed = stream.readByte();
- /*byte spare =*/ stream.readByte(); // Not needed? 0 in all tested data.
- byte wac = stream.readByte(); // Read A wave count
- byte wbc = stream.readByte(); // Read B wave count
- oscList.read(stream, wac, ignoreAddr); // Read the oscillators
- return (wac == wbc) && !(stream.eos() || stream.err()); // A and B wave counts must match
-}
-
-bool IIgsInstrumentHeader::finalize(Common::SeekableReadStream &uint8Wave) {
- return oscList.finalize(uint8Wave);
-}
-
-bool IIgsSampleHeader::read(Common::SeekableReadStream &stream) {
- type = stream.readUint16LE();
- pitch = stream.readByte();
- unknownByte_Ofs3 = stream.readByte();
- volume = stream.readByte();
- unknownByte_Ofs5 = stream.readByte();
- instrumentSize = stream.readUint16LE();
- sampleSize = stream.readUint16LE();
- // Read the instrument header *ignoring* its wave address info
-
- return instrument.read(stream, true);
-}
-
-bool IIgsSampleHeader::finalize(Common::SeekableReadStream &uint8Wave) {
- return instrument.finalize(uint8Wave);
-}
-
-/** Older Apple IIGS AGI MIDI program change to instrument number mapping. */
-static const MidiProgramChangeMapping progToInstMappingV1 = {
- {19, 20, 22, 23, 21, 24, 5, 5, 5, 5,
- 6, 7, 10, 9, 11, 9, 15, 8, 5, 5,
- 17, 16, 18, 12, 14, 5, 5, 5, 5, 5,
- 0, 1, 2, 9, 3, 4, 15, 2, 2, 2,
- 25, 13, 13, 25},
- 5
-};
-
-/** Newer Apple IIGS AGI MIDI program change to instrument number mapping. */
-static const MidiProgramChangeMapping progToInstMappingV2 = {
- {21, 22, 24, 25, 23, 26, 6, 6, 6, 6,
- 7, 9, 12, 8, 13, 11, 17, 10, 6, 6,
- 19, 18, 20, 14, 16, 6, 6, 6, 6, 6,
- 0, 1, 2, 4, 3, 5, 17, 2, 2, 2,
- 27, 15, 15, 27},
- 6
-};
-
-/** Older Apple IIGS AGI instrument set. Used only by Space Quest I (AGI v1.002). */
-static const InstrumentSetInfo instSetV1 = {
- 1192, 26, "7ee16bbc135171ffd6b9120cc7ff1af2", "edd3bf8905d9c238e02832b732fb2e18", progToInstMappingV1
-};
-
-/** Newer Apple IIGS AGI instrument set (AGI v1.003+). Used by all others than Space Quest I. */
-static const InstrumentSetInfo instSetV2 = {
- 1292, 28, "b7d428955bb90721996de1cbca25e768", "c05fb0b0e11deefab58bc68fbd2a3d07", progToInstMappingV2
-};
-
-/** Information about different Apple IIGS AGI executables. */
-static const IIgsExeInfo IIgsExeInfos[] = {
- {GID_SQ1, "SQ", 0x1002, 138496, 0x80AD, instSetV1},
- {GID_LSL1, "LL", 0x1003, 141003, 0x844E, instSetV2},
- {GID_AGIDEMO, "DEMO", 0x1005, 141884, 0x8469, instSetV2},
- {GID_KQ1, "KQ", 0x1006, 141894, 0x8469, instSetV2},
- {GID_PQ1, "PQ", 0x1007, 141882, 0x8469, instSetV2},
- {GID_MIXEDUP, "MG", 0x1013, 142552, 0x84B7, instSetV2},
- {GID_KQ2, "KQ2", 0x1013, 143775, 0x84B7, instSetV2},
- {GID_KQ3, "KQ3", 0x1014, 144312, 0x84B7, instSetV2},
- {GID_SQ2, "SQ2", 0x1014, 107882, 0x6563, instSetV2},
- {GID_MH1, "MH", 0x2004, 147678, 0x8979, instSetV2},
- {GID_KQ4, "KQ4", 0x2006, 147652, 0x8979, instSetV2},
- {GID_BC, "BC", 0x3001, 148192, 0x8979, instSetV2},
- {GID_GOLDRUSH, "GR", 0x3003, 148268, 0x8979, instSetV2}
-};
-
-static const int16 waveformRamp[WAVEFORM_SIZE] = {
- 0, 8, 16, 24, 32, 40, 48, 56,
- 64, 72, 80, 88, 96, 104, 112, 120,
- 128, 136, 144, 152, 160, 168, 176, 184,
- 192, 200, 208, 216, 224, 232, 240, 255,
- 0, -248, -240, -232, -224, -216, -208, -200,
- -192, -184, -176, -168, -160, -152, -144, -136,
- -128, -120, -112, -104, -96, -88, -80, -72,
- -64, -56, -48, -40, -32, -24, -16, -8 // Ramp up
-};
-
-static const int16 waveformSquare[WAVEFORM_SIZE] = {
- 255, 230, 220, 220, 220, 220, 220, 220,
- 220, 220, 220, 220, 220, 220, 220, 220,
- 220, 220, 220, 220, 220, 220, 220, 220,
- 220, 220, 220, 220, 220, 220, 220, 110,
- -255, -230, -220, -220, -220, -220, -220, -220,
- -220, -220, -220, -220, -220, -220, -220, -220,
- -220, -220, -220, -220, -220, -220, -220, -220,
- -220, -220, -220, -110, 0, 0, 0, 0 // Square
-};
-
-static const int16 waveformMac[WAVEFORM_SIZE] = {
- 45, 110, 135, 161, 167, 173, 175, 176,
- 156, 137, 123, 110, 91, 72, 35, -2,
- -60, -118, -142, -165, -170, -176, -177, -179,
- -177, -176, -164, -152, -117, -82, -17, 47,
- 92, 137, 151, 166, 170, 173, 171, 169,
- 151, 133, 116, 100, 72, 43, -7, -57,
- -99, -141, -156, -170, -174, -177, -178, -179,
- -175, -172, -165, -159, -137, -114, -67, -19
-};
-
+#if 0
static const uint16 period[] = {
1024, 1085, 1149, 1218, 1290, 1367,
1448, 1534, 1625, 1722, 1825, 1933
};
-#if 0
static int noteToPeriod(int note) {
return 10 * (period[note % 12] >> (note / 12 - 3));
}
@@ -346,8 +101,7 @@ void SoundMgr::unloadSound(int resnum) {
}
void SoundMgr::startSound(int resnum, int flag) {
- int i;
- AgiSoundType type;
+ AgiSoundEmuType type;
if (_vm->_game.sounds[resnum] != NULL && _vm->_game.sounds[resnum]->isPlaying())
return;
@@ -357,7 +111,7 @@ void SoundMgr::startSound(int resnum, int flag) {
if (_vm->_game.sounds[resnum] == NULL) // Is this needed at all?
return;
- type = (AgiSoundType)_vm->_game.sounds[resnum]->type();
+ type = (AgiSoundEmuType)_vm->_game.sounds[resnum]->type();
if (type != AGI_SOUND_SAMPLE && type != AGI_SOUND_MIDI && type != AGI_SOUND_4CHN)
return;
@@ -367,39 +121,8 @@ void SoundMgr::startSound(int resnum, int flag) {
debugC(3, kDebugLevelSound, "startSound(resnum = %d, flag = %d) type = %d", resnum, flag, type);
- switch (type) {
- case AGI_SOUND_SAMPLE: {
- IIgsSample *sampleRes = (IIgsSample *) _vm->_game.sounds[_playingSound];
- _gsSound.playSampleSound(sampleRes->getHeader(), sampleRes->getSample());
- break;
- }
- case AGI_SOUND_MIDI:
- ((IIgsMidi *) _vm->_game.sounds[_playingSound])->rewind();
- break;
- case AGI_SOUND_4CHN:
- PCjrSound *pcjrSound = (PCjrSound *) _vm->_game.sounds[resnum];
-
- // Initialize channel info
- for (i = 0; i < NUM_CHANNELS; i++) {
- _chn[i].type = type;
- _chn[i].flags = AGI_SOUND_LOOP;
-
- if (_env) {
- _chn[i].flags |= AGI_SOUND_ENVELOPE;
- _chn[i].adsr = AGI_SOUND_ENV_ATTACK;
- }
-
- _chn[i].ins = _waveform;
- _chn[i].size = WAVEFORM_SIZE;
- _chn[i].ptr = pcjrSound->getVoicePointer(i % 4);
- _chn[i].timer = 0;
- _chn[i].vol = 0;
- _chn[i].end = 0;
- }
- break;
- }
+ _soundGen->play(resnum);
- memset(_sndBuffer, 0, BUFFER_SIZE << 1);
_endflag = flag;
// Nat Budin reports that the flag should be reset when sound starts
@@ -407,907 +130,68 @@ void SoundMgr::startSound(int resnum, int flag) {
}
void SoundMgr::stopSound() {
- int i;
-
debugC(3, kDebugLevelSound, "stopSound() --> %d", _playingSound);
_endflag = -1;
- if (_vm->_soundemu != SOUND_EMU_APPLE2GS) {
- for (i = 0; i < NUM_CHANNELS; i++)
- stopNote(i);
- }
if (_playingSound != -1) {
if (_vm->_game.sounds[_playingSound]) // sanity checking
_vm->_game.sounds[_playingSound]->stop();
- if (_vm->_soundemu == SOUND_EMU_APPLE2GS) {
- _gsSound.stopSounds();
- }
+ _soundGen->stop();
_playingSound = -1;
}
-}
-void IIgsSoundMgr::stopSounds() {
- // Stops all sounds on all MIDI channels
- for (iterator iter = _midiChannels.begin(); iter != _midiChannels.end(); ++iter)
- iter->stopSounds();
+ if (_endflag != -1)
+ _vm->setflag(_endflag, true);
}
-bool IIgsSoundMgr::playSampleSound(const IIgsSampleHeader &sampleHeader, const int8 *sample) {
- stopSounds();
- IIgsMidiChannel &channel = _midiChannels[kSfxMidiChannel];
+int SoundMgr::initSound() {
+ return -1;
+}
- channel.setInstrument(&sampleHeader.instrument, sample);
- channel.setVolume(sampleHeader.volume);
- channel.noteOn(sampleHeader.pitch, 64); // Use default velocity (i.e. 64)
+void SoundMgr::deinitSound() {
+ stopSound();
- return true;
+ delete _soundGen;
}
-void IIgsMidiChannel::stopSounds() {
- // Stops all sounds on this single MIDI channel
- for (iterator iter = _gsChannels.begin(); iter != _gsChannels.end(); ++iter)
- iter->stop();
+void SoundMgr::soundIsFinished() {
+ if (_endflag != -1)
+ _vm->setflag(_endflag, true);
- _gsChannels.clear();
+ if (_playingSound != -1)
+ _vm->_game.sounds[_playingSound]->stop();
+ _playingSound = -1;
+ _endflag = -1;
}
-int SoundMgr::initSound() {
- int r = -1;
-
- memset(_sndBuffer, 0, BUFFER_SIZE << 1);
- _env = false;
+SoundMgr::SoundMgr(AgiEngine *agi, Audio::Mixer *pMixer) {
+ _vm = agi;
+ _endflag = -1;
+ _playingSound = -1;
switch (_vm->_soundemu) {
case SOUND_EMU_NONE:
- _waveform = waveformRamp;
- _env = true;
- break;
case SOUND_EMU_AMIGA:
- case SOUND_EMU_PC:
- _waveform = waveformSquare;
- break;
case SOUND_EMU_MAC:
- _waveform = waveformMac;
+ _soundGen = new SoundGenSarien(_vm, pMixer);
+ break;
+ case SOUND_EMU_PC:
+ case SOUND_EMU_PCJR:
+ _soundGen = new SoundGenPCJr(_vm, pMixer);
break;
case SOUND_EMU_APPLE2GS:
- _disabledMidi = !loadInstruments();
+ _soundGen = new SoundGen2GS(_vm, pMixer);
break;
case SOUND_EMU_COCO3:
+ _soundGen = new SoundGenCoCo3(_vm, pMixer);
break;
- }
-
- report("Initializing sound:\n");
-
- report("sound: envelopes ");
- if (_env) {
- report("enabled (decay=%d, sustain=%d)\n", ENV_DECAY, ENV_SUSTAIN);
- } else {
- report("disabled\n");
- }
-
- _mixer->playStream(Audio::Mixer::kMusicSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
-
- return r;
-}
-
-void SoundMgr::deinitSound() {
- debugC(3, kDebugLevelSound, "()");
-
- _mixer->stopHandle(_soundHandle);
-}
-
-void SoundMgr::stopNote(int i) {
- _chn[i].adsr = AGI_SOUND_ENV_RELEASE;
-
- if (_useChorus) {
- // Stop chorus ;)
- if (_chn[i].type == AGI_SOUND_4CHN &&
- _vm->_soundemu == SOUND_EMU_NONE && i < 3) {
- stopNote(i + 4);
- }
- }
-}
-
-void SoundMgr::playNote(int i, int freq, int vol) {
- if (!_vm->getflag(fSoundOn))
- vol = 0;
- else if (vol && _vm->_soundemu == SOUND_EMU_PC)
- vol = 160;
-
- _chn[i].phase = 0;
- _chn[i].freq = freq;
- _chn[i].vol = vol;
- _chn[i].env = 0x10000;
- _chn[i].adsr = AGI_SOUND_ENV_ATTACK;
-
- if (_useChorus) {
- // Add chorus ;)
- if (_chn[i].type == AGI_SOUND_4CHN &&
- _vm->_soundemu == SOUND_EMU_NONE && i < 3) {
-
- int newfreq = freq * 1007 / 1000;
-
- if (freq == newfreq)
- newfreq++;
-
- playNote(i + 4, newfreq, vol * 2 / 3);
- }
- }
-}
-
-void SoundMgr::playMidiSound() {
- if (_disabledMidi)
- return;
-
- const uint8 *p;
- uint8 parm1, parm2;
- static uint8 cmd, ch;
-
- if (_playingSound == -1 || _vm->_game.sounds[_playingSound] == NULL) {
- warning("Error playing Apple IIGS MIDI sound resource");
- _playing = false;
-
- return;
- }
-
- IIgsMidi *midiObj = (IIgsMidi *) _vm->_game.sounds[_playingSound];
-
- _playing = true;
- p = midiObj->getPtr();
-
- midiObj->_soundBufTicks++;
-
- while (true) {
- uint8 readByte = *p;
-
- // Check for end of MIDI sequence marker (Can also be here before delta-time)
- if (readByte == MIDI_BYTE_STOP_SEQUENCE) {
- debugC(3, kDebugLevelSound, "End of MIDI sequence (Before reading delta-time)");
- _playing = false;
-
- midiObj->rewind();
-
- return;
- } else if (readByte == MIDI_BYTE_TIMER_SYNC) {
- debugC(3, kDebugLevelSound, "Timer sync");
- p++; // Jump over the timer sync byte as it's not needed
-
- continue;
- }
-
- uint8 deltaTime = readByte;
- if (midiObj->_midiTicks + deltaTime > midiObj->_soundBufTicks) {
- break;
- }
- midiObj->_midiTicks += deltaTime;
- p++; // Jump over the delta-time byte as it was already taken care of
-
- // Check for end of MIDI sequence marker (This time it after reading delta-time)
- if (*p == MIDI_BYTE_STOP_SEQUENCE) {
- debugC(3, kDebugLevelSound, "End of MIDI sequence (After reading delta-time)");
- _playing = false;
-
- midiObj->rewind();
-
- return;
- }
-
- // Separate byte into command and channel if it's a command byte.
- // Otherwise use running status (i.e. previously set command and channel).
- if (*p & 0x80) {
- cmd = *p++;
- ch = cmd & 0x0f;
- cmd >>= 4;
- }
-
- switch (cmd) {
- case MIDI_CMD_NOTE_OFF:
- parm1 = *p++;
- parm2 = *p++;
- _gsSound.midiNoteOff(ch, parm1, parm2);
- break;
- case MIDI_CMD_NOTE_ON:
- parm1 = *p++;
- parm2 = *p++;
- _gsSound.midiNoteOn(ch, parm1, parm2);
- break;
- case MIDI_CMD_CONTROLLER:
- parm1 = *p++;
- parm2 = *p++;
- _gsSound.midiController(ch, parm1, parm2);
- break;
- case MIDI_CMD_PROGRAM_CHANGE:
- parm1 = *p++;
- _gsSound.midiProgramChange(ch, parm1);
- break;
- case MIDI_CMD_PITCH_WHEEL:
- parm1 = *p++;
- parm2 = *p++;
-
- uint16 wheelPos = ((parm2 & 0x7F) << 7) | (parm1 & 0x7F); // 14-bit value
- _gsSound.midiPitchWheel(wheelPos);
- break;
- }
- }
-
- midiObj->setPtr(p);
-}
-
-void IIgsSoundMgr::midiNoteOff(uint8 channel, uint8 note, uint8 velocity) {
- _midiChannels[channel].noteOff(note, velocity);
- debugC(3, kDebugLevelSound, "note off, channel %02x, note %02x, velocity %02x", channel, note, velocity);
-}
-
-void IIgsSoundMgr::midiNoteOn(uint8 channel, uint8 note, uint8 velocity) {
- _midiChannels[channel].noteOn(note, velocity);
- debugC(3, kDebugLevelSound, "note on, channel %02x, note %02x, velocity %02x", channel, note, velocity);
-}
-
-// TODO: Check if controllers behave differently on different MIDI channels
-// TODO: Doublecheck what other controllers than the volume controller do
-void IIgsSoundMgr::midiController(uint8 channel, uint8 controller, uint8 value) {
- IIgsMidiChannel &midiChannel = _midiChannels[channel];
-
- // The tested Apple IIGS AGI MIDI resources only used
- // controllers 0 (Bank select?), 7 (Volume) and 64 (Sustain On/Off).
- // Controller 0's parameter was in range 94-127,
- // controller 7's parameter was in range 0-127 and
- // controller 64's parameter was always 0 (i.e. sustain off).
- bool unimplemented = false;
- switch (controller) {
- case 7: // Volume
- midiChannel.setVolume(value);
- break;
- default:
- unimplemented = true;
+ case SOUND_EMU_MIDI:
+ _soundGen = new SoundGenMIDI(_vm, pMixer);
break;
}
- debugC(3, kDebugLevelSound, "controller %02x, ch %02x, val %02x%s", controller, channel, value, unimplemented ? " (Unimplemented)" : "");
-}
-
-void IIgsSoundMgr::midiProgramChange(uint8 channel, uint8 program) {
- _midiChannels[channel].setInstrument(getInstrument(program), _wave.begin());
- debugC(3, kDebugLevelSound, "program change %02x, channel %02x", program, channel);
-}
-
-void IIgsSoundMgr::midiPitchWheel(uint8 wheelPos) {
- // In all the tested Apple IIGS AGI MIDI resources
- // pitch wheel commands always used 0x2000 (Center position).
- // Therefore it should be quite safe to ignore this command.
- debugC(3, kDebugLevelSound, "pitch wheel position %04x (Unimplemented)", wheelPos);
-}
-
-IIgsSoundMgr::IIgsSoundMgr() {
- _midiChannels.resize(16); // Set the amount of available MIDI channels
-}
-
-const IIgsInstrumentHeader* IIgsSoundMgr::getInstrument(uint8 program) const {
- return &_instruments[_midiProgToInst->map(program)];
-}
-
-void IIgsSoundMgr::setProgramChangeMapping(const MidiProgramChangeMapping *mapping) {
- _midiProgToInst = mapping;
-}
-
-void IIgsSoundMgr::removeStoppedSounds() {
- for (Common::Array<IIgsMidiChannel>::iterator iter = _midiChannels.begin(); iter != _midiChannels.end(); ++iter)
- iter->removeStoppedSounds();
-}
-
-void IIgsMidiChannel::removeStoppedSounds() {
- for (int i = _gsChannels.size() - 1; i >= 0; i--)
- if (!_gsChannels[i].playing())
- _gsChannels.remove_at(i);
-}
-
-uint IIgsSoundMgr::activeSounds() const {
- uint result = 0;
-
- for (Common::Array<IIgsMidiChannel>::const_iterator iter = _midiChannels.begin(); iter != _midiChannels.end(); ++iter)
- result += iter->activeSounds();
-
- return result;
-}
-
-uint IIgsMidiChannel::activeSounds() const {
- uint result = 0;
-
- for (const_iterator iter = _gsChannels.begin(); iter != _gsChannels.end(); ++iter)
- if (!iter->end)
- result++;
-
- return result;
-}
-
-void IIgsMidiChannel::setInstrument(const IIgsInstrumentHeader *instrument, const int8 *sample) {
- _instrument = instrument;
- _sample = sample;
-
- // Set program on each Apple IIGS channel playing on this MIDI channel
- for (iterator iter = _gsChannels.begin(); iter != _gsChannels.end(); ++iter)
- iter->setInstrument(instrument, sample);
-}
-
-void IIgsMidiChannel::setVolume(uint8 volume) {
- _volume = volume;
-
- // Set volume on each Apple IIGS channel playing on this MIDI channel
- for (iterator iter = _gsChannels.begin(); iter != _gsChannels.end(); ++iter)
- iter->setChannelVolume(volume);
-}
-
-void IIgsMidiChannel::noteOff(uint8 note, uint8 velocity) {
- // Go through all the notes playing on this MIDI channel
- // and turn off the ones that are playing the given note
- for (iterator iter = _gsChannels.begin(); iter != _gsChannels.end(); ++iter)
- if (iter->origNote == note)
- iter->noteOff(velocity);
-}
-
-void IIgsMidiChannel::noteOn(uint8 note, uint8 velocity) {
- IIgsChannelInfo channel;
-
- // Use the default channel volume and instrument
- channel.setChannelVolume(_volume);
- channel.setInstrument(_instrument, _sample);
-
- // Set the note on and save the channel
- channel.noteOn(note, velocity);
- _gsChannels.push_back(channel);
-}
-
-void IIgsChannelInfo::rewind() {
- this->envVol = this->startEnvVol;
- this->envSeg = 0;
- this->pos = intToFrac(0);
-}
-
-void IIgsChannelInfo::setChannelVolume(uint8 volume) {
- this->chanVol = intToFrac(volume);
-}
-
-void IIgsChannelInfo::setInstrument(const IIgsInstrumentHeader *instrument, const int8 *sample) {
- assert(instrument != NULL && sample != NULL);
- this->ins = instrument;
- this->unrelocatedSample = sample;
-}
-
-// TODO/FIXME: Implement correctly and fully (Take velocity into account etc)
-void IIgsChannelInfo::noteOn(uint8 noteParam, uint8 velocity) {
- this->origNote = noteParam;
- this->startEnvVol = intToFrac(0);
- rewind();
-
- const IIgsWaveInfo *waveInfo = NULL;
-
- for (uint i = 0; i < ins->oscList.count; i++)
- if (ins->oscList(i).waves[0].top >= noteParam)
- waveInfo = &ins->oscList(i).waves[0];
-
- assert(waveInfo != NULL);
-
- this->relocatedSample = this->unrelocatedSample + waveInfo->addr;
- this->posAdd = intToFrac(0);
- this->note = intToFrac(noteParam) + doubleToFrac(waveInfo->relPitch/256.0);
- this->vol = doubleToFrac(fracToDouble(this->envVol) * fracToDouble(this->chanVol) / 127.0);
- this->loop = (waveInfo->mode == OSC_MODE_LOOP);
- this->size = waveInfo->size - waveInfo->addr;
- this->end = waveInfo->halt;
-}
-
-// TODO/FIXME: Implement correctly and fully (Take release time and velocity into account etc)
-void IIgsChannelInfo::noteOff(uint8 velocity) {
- this->loop = false;
- this->envSeg = ins->relseg;
-}
-
-void IIgsChannelInfo::stop() {
- this->end = true;
-}
-
-bool IIgsChannelInfo::playing() {
- return !this->end;
-}
-
-void SoundMgr::playSampleSound() {
- if (_vm->_soundemu != SOUND_EMU_APPLE2GS) {
- warning("Trying to play a sample but not using Apple IIGS sound emulation mode");
- return;
- }
-
- if (_playingSound != -1)
- _playing = _gsSound.activeSounds() > 0;
-}
-
-static int cocoFrequencies[] = {
- 130, 138, 146, 155, 164, 174, 184, 195, 207, 220, 233, 246,
- 261, 277, 293, 311, 329, 349, 369, 391, 415, 440, 466, 493,
- 523, 554, 587, 622, 659, 698, 739, 783, 830, 880, 932, 987,
- 1046, 1108, 1174, 1244, 1318, 1396, 1479, 1567, 1661, 1760, 1864, 1975,
- 2093, 2217, 2349, 2489, 2637, 2793, 2959, 3135, 3322, 3520, 3729, 3951
-};
-
-void SoundMgr::playCoCoSound() {
- int i = 0;
- CoCoNote note;
-
- do {
- note.read(_chn[i].ptr);
-
- if (note.freq != 0xff) {
- playNote(0, cocoFrequencies[note.freq], note.volume);
-
- uint32 start_time = _vm->_system->getMillis();
-
- while (_vm->_system->getMillis() < start_time + note.duration) {
- _vm->_system->updateScreen();
-
- _vm->_system->delayMillis(10);
- }
- }
- } while (note.freq != 0xff);
-}
-
-void SoundMgr::playAgiSound() {
- int i;
- AgiNote note;
-
- _playing = false;
- for (i = 0; i < (_vm->_soundemu == SOUND_EMU_PC ? 1 : 4); i++) {
- _playing |= !_chn[i].end;
- note.read(_chn[i].ptr); // Read a single note (Doesn't advance the pointer)
-
- if (_chn[i].end)
- continue;
-
- if ((--_chn[i].timer) <= 0) {
- stopNote(i);
-
- if (note.freqDiv != 0) {
- int volume = (note.attenuation == 0x0F) ? 0 : (0xFF - note.attenuation * 2);
- playNote(i, note.freqDiv * 10, volume);
- }
-
- _chn[i].timer = note.duration;
-
- if (_chn[i].timer == 0xffff) {
- _chn[i].end = 1;
- _chn[i].vol = 0;
- _chn[i].env = 0;
-
- if (_useChorus) {
- // chorus
- if (_chn[i].type == AGI_SOUND_4CHN && _vm->_soundemu == SOUND_EMU_NONE && i < 3) {
- _chn[i + 4].vol = 0;
- _chn[i + 4].env = 0;
- }
- }
- }
- _chn[i].ptr += 5; // Advance the pointer to the next note data (5 bytes per note)
- }
- }
-}
-
-void SoundMgr::playSound() {
- int i;
-
- if (_endflag == -1)
- return;
-
- if (_vm->_soundemu == SOUND_EMU_APPLE2GS) {
- if (_playingSound != -1) {
- if (_vm->_game.sounds[_playingSound]->type() == AGI_SOUND_MIDI) {
- playMidiSound();
- //warning("playSound: Trying to play an Apple IIGS MIDI sound. Not yet implemented");
- } else if (_vm->_game.sounds[_playingSound]->type() == AGI_SOUND_SAMPLE) {
- //debugC(3, kDebugLevelSound, "playSound: Trying to play an Apple IIGS sample");
- playSampleSound();
- }
- }
- } else if (_vm->_soundemu == SOUND_EMU_COCO3) {
- playCoCoSound();
- } else {
- //debugC(3, kDebugLevelSound, "playSound: Trying to play a PCjr 4-channel sound");
- playAgiSound();
- }
-
- if (!_playing) {
- if (_vm->_soundemu != SOUND_EMU_APPLE2GS) {
- for (i = 0; i < NUM_CHANNELS; _chn[i++].vol = 0)
- ;
- }
-
- if (_endflag != -1)
- _vm->setflag(_endflag, true);
-
- if (_playingSound != -1)
- _vm->_game.sounds[_playingSound]->stop();
- _playingSound = -1;
- _endflag = -1;
- }
-}
-
-uint32 SoundMgr::mixSound() {
- register int i, p;
- const int16 *src;
- int c, b, m;
-
- memset(_sndBuffer, 0, BUFFER_SIZE << 1);
-
- if (!_playing || _playingSound == -1)
- return BUFFER_SIZE;
-
- // Handle Apple IIGS sound mixing here
- // TODO: Implement playing both waves in an oscillator
- // TODO: Implement swap-mode in an oscillator
- if (_vm->_soundemu == SOUND_EMU_APPLE2GS) {
- for (uint midiChan = 0; midiChan < _gsSound._midiChannels.size(); midiChan++) {
- for (uint gsChan = 0; gsChan < _gsSound._midiChannels[midiChan]._gsChannels.size(); gsChan++) {
- IIgsChannelInfo &channel = _gsSound._midiChannels[midiChan]._gsChannels[gsChan];
- if (channel.playing()) { // Only mix in actively playing channels
- // Frequency multiplier was 1076.0 based on tests made with MESS 0.117.
- // Tests made with KEGS32 averaged the multiplier to around 1045.
- // So this is a guess but maybe it's 1046.5... i.e. C6's frequency?
- double hertz = C6_FREQ * pow(SEMITONE, fracToDouble(channel.note));
- channel.posAdd = doubleToFrac(hertz / getRate());
- channel.vol = doubleToFrac(fracToDouble(channel.envVol) * fracToDouble(channel.chanVol) / 127.0);
- double tempVol = fracToDouble(channel.vol)/127.0;
- for (i = 0; i < IIGS_BUFFER_SIZE; i++) {
- b = channel.relocatedSample[fracToInt(channel.pos)];
- // TODO: Find out what volume/amplification setting is loud enough
- // but still doesn't clip when playing many channels on it.
- _sndBuffer[i] += (int16) (b * tempVol * 256/4);
- channel.pos += channel.posAdd;
-
- if (channel.pos >= intToFrac(channel.size)) {
- if (channel.loop) {
- // Don't divide by zero on zero length samples
- channel.pos %= intToFrac(channel.size + (channel.size == 0));
- // Probably we should loop the envelope too
- channel.envSeg = 0;
- channel.envVol = channel.startEnvVol;
- } else {
- channel.pos = channel.chanVol = 0;
- channel.end = true;
- break;
- }
- }
- }
-
- if (channel.envSeg < ENVELOPE_SEGMENT_COUNT) {
- const IIgsEnvelopeSegment &seg = channel.ins->env.seg[channel.envSeg];
- // I currently assume enveloping works with the same speed as the MIDI
- // (i.e. with 1/60ths of a second ticks).
- // TODO: Check if enveloping really works with the same speed as MIDI
- frac_t envVolDelta = doubleToFrac(seg.inc/256.0);
- if (intToFrac(seg.bp) >= channel.envVol) {
- channel.envVol += envVolDelta;
- if (channel.envVol >= intToFrac(seg.bp)) {
- channel.envVol = intToFrac(seg.bp);
- channel.envSeg += 1;
- }
- } else {
- channel.envVol -= envVolDelta;
- if (channel.envVol <= intToFrac(seg.bp)) {
- channel.envVol = intToFrac(seg.bp);
- channel.envSeg += 1;
- }
- }
- }
- }
- }
- }
- _gsSound.removeStoppedSounds();
- return IIGS_BUFFER_SIZE;
- } // else ...
-
- // Handle PCjr 4-channel sound mixing here
- for (c = 0; c < NUM_CHANNELS; c++) {
- if (!_chn[c].vol)
- continue;
-
- m = _chn[c].flags & AGI_SOUND_ENVELOPE ?
- _chn[c].vol * _chn[c].env >> 16 : _chn[c].vol;
-
- if (_chn[c].type != AGI_SOUND_4CHN || c != 3) {
- src = _chn[c].ins;
-
- p = _chn[c].phase;
- for (i = 0; i < BUFFER_SIZE; i++) {
- b = src[p >> 8];
-#ifdef USE_INTERPOLATION
- b += ((src[((p >> 8) + 1) % _chn[c].size] - src[p >> 8]) * (p & 0xff)) >> 8;
-#endif
- _sndBuffer[i] += (b * m) >> 4;
-
- p += (uint32) 118600 *4 / _chn[c].freq;
-
- // FIXME: Fingolfin asks: why is there a FIXME here? Please either clarify what
- // needs fixing, or remove it!
- // FIXME
- if (_chn[c].flags & AGI_SOUND_LOOP) {
- p %= _chn[c].size << 8;
- } else {
- if (p >= _chn[c].size << 8) {
- p = _chn[c].vol = 0;
- _chn[c].end = 1;
- break;
- }
- }
-
- }
- _chn[c].phase = p;
- } else {
- // Add white noise
- for (i = 0; i < BUFFER_SIZE; i++) {
- b = _vm->_rnd->getRandomNumber(255) - 128;
- _sndBuffer[i] += (b * m) >> 4;
- }
- }
-
- switch (_chn[c].adsr) {
- case AGI_SOUND_ENV_ATTACK:
- // not implemented
- _chn[c].adsr = AGI_SOUND_ENV_DECAY;
- break;
- case AGI_SOUND_ENV_DECAY:
- if (_chn[c].env > _chn[c].vol * ENV_SUSTAIN + ENV_DECAY) {
- _chn[c].env -= ENV_DECAY;
- } else {
- _chn[c].env = _chn[c].vol * ENV_SUSTAIN;
- _chn[c].adsr = AGI_SOUND_ENV_SUSTAIN;
- }
- break;
- case AGI_SOUND_ENV_SUSTAIN:
- break;
- case AGI_SOUND_ENV_RELEASE:
- if (_chn[c].env >= ENV_RELEASE) {
- _chn[c].env -= ENV_RELEASE;
- } else {
- _chn[c].env = 0;
- }
- }
- }
-
- return BUFFER_SIZE;
-}
-
-/**
- * Finds information about an Apple IIGS AGI executable based on the game ID.
- * @return A non-null IIgsExeInfo pointer if successful, otherwise NULL.
- */
-const IIgsExeInfo *SoundMgr::getIIgsExeInfo(enum AgiGameID gameid) const {
- for (int i = 0; i < ARRAYSIZE(IIgsExeInfos); i++)
- if (IIgsExeInfos[i].gameid == gameid)
- return &IIgsExeInfos[i];
- return NULL;
-}
-
-bool IIgsSoundMgr::loadInstrumentHeaders(const Common::FSNode &exePath, const IIgsExeInfo &exeInfo) {
- bool loadedOk = false; // Was loading successful?
- Common::File file;
-
- // Open the executable file and check that it has correct size
- file.open(exePath);
- if (file.size() != (int32)exeInfo.exeSize) {
- debugC(3, kDebugLevelSound, "Apple IIGS executable (%s) has wrong size (Is %d, should be %d)",
- exePath.getPath().c_str(), file.size(), exeInfo.exeSize);
- }
-
- // Read the whole executable file into memory
- Common::SharedPtr<Common::MemoryReadStream> data(file.readStream(file.size()));
- file.close();
-
- // Check that we got enough data to be able to parse the instruments
- if (data && data->size() >= (int32)(exeInfo.instSetStart + exeInfo.instSet.byteCount)) {
- // Check instrument set's length (The info's saved in the executable)
- data->seek(exeInfo.instSetStart - 4);
- uint16 instSetByteCount = data->readUint16LE();
- if (instSetByteCount != exeInfo.instSet.byteCount) {
- debugC(3, kDebugLevelSound, "Wrong instrument set size (Is %d, should be %d) in Apple IIGS executable (%s)",
- instSetByteCount, exeInfo.instSet.byteCount, exePath.getPath().c_str());
- }
-
- // Check instrument set's md5sum
- data->seek(exeInfo.instSetStart);
-
- char md5str[32+1];
- Common::md5_file_string(*data, md5str, exeInfo.instSet.byteCount);
- if (scumm_stricmp(md5str, exeInfo.instSet.md5)) {
- warning("Unknown Apple IIGS instrument set (md5: %s) in %s, trying to use it nonetheless",
- md5str, exePath.getPath().c_str());
- }
-
- // Read in the instrument set one instrument at a time
- data->seek(exeInfo.instSetStart);
-
- // Load the instruments
- _instruments.clear();
- _instruments.reserve(exeInfo.instSet.instCount);
-
- IIgsInstrumentHeader instrument;
- for (uint i = 0; i < exeInfo.instSet.instCount; i++) {
- if (!instrument.read(*data)) {
- warning("Error loading Apple IIGS instrument (%d. of %d) from %s, not loading more instruments",
- i + 1, exeInfo.instSet.instCount, exePath.getPath().c_str());
- break;
- }
- _instruments.push_back(instrument); // Add the successfully loaded instrument to the instruments array
- }
-
- // Loading was successful only if all instruments were loaded successfully
- loadedOk = (_instruments.size() == exeInfo.instSet.instCount);
- } else // Couldn't read enough data from the executable file
- warning("Error loading instruments from Apple IIGS executable (%s)", exePath.getPath().c_str());
-
- return loadedOk;
-}
-
-/**
- * Convert sample from 8-bit unsigned to 8-bit signed format.
- * @param source Source stream containing the 8-bit unsigned sample data.
- * @param dest Destination buffer for the 8-bit signed sample data.
- * @param length Length of the sample data to be converted.
- */
-bool SoundMgr::convertWave(Common::SeekableReadStream &source, int8 *dest, uint length) {
- // Convert the wave from 8-bit unsigned to 8-bit signed format
- for (uint i = 0; i < length; i++)
- dest[i] = (int8) ((int) source.readByte() - 128);
- return !(source.eos() || source.err());
-}
-
-bool IIgsSoundMgr::loadWaveFile(const Common::FSNode &wavePath, const IIgsExeInfo &exeInfo) {
- Common::File file;
-
- // Open the wave file and read it into memory
- file.open(wavePath);
- Common::SharedPtr<Common::MemoryReadStream> uint8Wave(file.readStream(file.size()));
- file.close();
-
- // Check that we got the whole wave file
- if (uint8Wave && uint8Wave->size() == SIERRASTANDARD_SIZE) {
- // Check wave file's md5sum
- char md5str[32+1];
- Common::md5_file_string(*uint8Wave, md5str, SIERRASTANDARD_SIZE);
- if (scumm_stricmp(md5str, exeInfo.instSet.waveFileMd5)) {
- warning("Unknown Apple IIGS wave file (md5: %s, game: %s).\n" \
- "Please report the information on the previous line to the ScummVM team.\n" \
- "Using the wave file as it is - music may sound weird", md5str, exeInfo.exePrefix);
- }
-
- uint8Wave->seek(0); // Seek wave to its start
- // Convert the wave file from 8-bit unsigned to 8-bit signed and save the result
- _wave.resize(uint8Wave->size());
- return SoundMgr::convertWave(*uint8Wave, _wave.begin(), uint8Wave->size());
- } else { // Couldn't read the wave file or it had incorrect size
- warning("Error loading Apple IIGS wave file (%s), not loading instruments", wavePath.getPath().c_str());
- return false;
- }
-}
-
-/**
- * A function object (i.e. a functor) for testing if a Common::FSNode
- * object's name is equal (Ignoring case) to a string or to at least
- * one of the strings in a list of strings. Can be used e.g. with find_if().
- */
-struct fsnodeNameEqualsIgnoreCase : public Common::UnaryFunction<const Common::FSNode&, bool> {
-// FIXME: This should be replaced; use SearchMan instead
- fsnodeNameEqualsIgnoreCase(const Common::StringArray &str) : _str(str) {}
- fsnodeNameEqualsIgnoreCase(const Common::String str) { _str.push_back(str); }
- bool operator()(const Common::FSNode &param) const {
- for (Common::StringArray::const_iterator iter = _str.begin(); iter != _str.end(); ++iter)
- if (param.getName().equalsIgnoreCase(*iter))
- return true;
- return false;
- }
-private:
- Common::StringArray _str;
-};
-
-bool SoundMgr::loadInstruments() {
- // Check that the platform is Apple IIGS, as only it uses custom instruments
- if (_vm->getPlatform() != Common::kPlatformApple2GS) {
- debugC(3, kDebugLevelSound, "Platform isn't Apple IIGS so not loading any instruments");
- return true;
- }
-
- // Get info on the particular Apple IIGS AGI game's executable
- const IIgsExeInfo *exeInfo = getIIgsExeInfo((enum AgiGameID) _vm->getGameID());
- if (exeInfo == NULL) {
- warning("Unsupported Apple IIGS game, not loading instruments");
- return false;
- }
-
- // List files in the game path
- Common::FSList fslist;
- Common::FSNode dir(ConfMan.get("path"));
- if (!dir.getChildren(fslist, Common::FSNode::kListFilesOnly)) {
- warning("Invalid game path (\"%s\"), not loading Apple IIGS instruments", dir.getPath().c_str());
- return false;
- }
-
- // Populate executable filenames list (Long filename and short filename) for searching
- Common::StringArray exeNames;
- exeNames.push_back(Common::String(exeInfo->exePrefix) + ".SYS16");
- exeNames.push_back(Common::String(exeInfo->exePrefix) + ".SYS");
-
- // Populate wave filenames list (Long filename and short filename) for searching
- Common::StringArray waveNames;
- waveNames.push_back("SIERRASTANDARD");
- waveNames.push_back("SIERRAST");
-
- // Search for the executable file and the wave file (i.e. check if any of the filenames match)
- Common::FSList::const_iterator exeFsnode, waveFsnode;
- exeFsnode = Common::find_if(fslist.begin(), fslist.end(), fsnodeNameEqualsIgnoreCase(exeNames));
- waveFsnode = Common::find_if(fslist.begin(), fslist.end(), fsnodeNameEqualsIgnoreCase(waveNames));
-
- // Make sure that we found the executable file
- if (exeFsnode == fslist.end()) {
- warning("Couldn't find Apple IIGS game executable (%s), not loading instruments", exeNames.begin()->c_str());
- return false;
- }
-
- // Make sure that we found the wave file
- if (waveFsnode == fslist.end()) {
- warning("Couldn't find Apple IIGS wave file (%s), not loading instruments", waveNames.begin()->c_str());
- return false;
- }
-
- // Set the MIDI program change to instrument number mapping and
- // load the instrument headers and their sample data.
- // None of the tested SIERRASTANDARD-files have zeroes in them so
- // there's no need to check for prematurely ending samples here.
- _gsSound.setProgramChangeMapping(&exeInfo->instSet.progToInst);
- return _gsSound.loadWaveFile(*waveFsnode, *exeInfo) && _gsSound.loadInstrumentHeaders(*exeFsnode, *exeInfo);
-}
-
-void SoundMgr::fillAudio(void *udata, int16 *stream, uint len) {
- SoundMgr *soundMgr = (SoundMgr *)udata;
- uint32 p = 0;
-
- // current number of audio bytes in _sndBuffer
- static uint32 data_available = 0;
- // offset of start of audio bytes in _sndBuffer
- static uint32 data_offset = 0;
-
- len <<= 2;
-
- debugC(5, kDebugLevelSound, "(%p, %p, %d)", (void *)udata, (void *)stream, len);
-
- while (len > data_available) {
- memcpy((uint8 *)stream + p, (uint8*)_sndBuffer + data_offset, data_available);
- p += data_available;
- len -= data_available;
-
- soundMgr->playSound();
- data_available = soundMgr->mixSound() << 1;
- data_offset = 0;
- }
-
- memcpy((uint8 *)stream + p, (uint8*)_sndBuffer + data_offset, len);
- data_offset += len;
- data_available -= len;
-}
-
-SoundMgr::SoundMgr(AgiBase *agi, Audio::Mixer *pMixer) : _chn() {
- _vm = agi;
- _mixer = pMixer;
- _sampleRate = pMixer->getOutputRate();
- _endflag = -1;
- _playingSound = -1;
- _env = false;
- _playing = false;
- _sndBuffer = (int16 *)calloc(2, BUFFER_SIZE);
- _waveform = 0;
- _disabledMidi = false;
- _useChorus = true; // FIXME: Currently always true?
-}
-
-void SoundMgr::premixerCall(int16 *data, uint len) {
- fillAudio(this, data, len);
}
void SoundMgr::setVolume(uint8 volume) {
@@ -1315,7 +199,6 @@ void SoundMgr::setVolume(uint8 volume) {
}
SoundMgr::~SoundMgr() {
- free(_sndBuffer);
}
} // End of namespace Agi
diff --git a/engines/agi/sound.h b/engines/agi/sound.h
index 881e3efd56..63b36e017c 100644
--- a/engines/agi/sound.h
+++ b/engines/agi/sound.h
@@ -26,160 +26,18 @@
#ifndef AGI_SOUND_H
#define AGI_SOUND_H
-#include "sound/audiostream.h"
#include "sound/mixer.h"
-#include "common/frac.h"
namespace Agi {
-#define BUFFER_SIZE 410
-
-// Apple IIGS MIDI uses 60 ticks per second (Based on tests with Apple IIGS
-// KQ1 and SQ1 under MESS 0.124a). So we make the audio buffer size to be a
-// 1/60th of a second in length. That should be getSampleRate() / 60 samples
-// in length but as getSampleRate() is always 22050 at the moment we just use
-// the hardcoded value of 368 (22050/60 = 367.5 which rounds up to 368).
-// FIXME: Use getSampleRate() / 60 rather than a hardcoded value
-#define IIGS_BUFFER_SIZE 368
-
#define SOUND_EMU_NONE 0
#define SOUND_EMU_PC 1
-#define SOUND_EMU_TANDY 2
+#define SOUND_EMU_PCJR 2
#define SOUND_EMU_MAC 3
#define SOUND_EMU_AMIGA 4
#define SOUND_EMU_APPLE2GS 5
#define SOUND_EMU_COCO3 6
-
-#define WAVEFORM_SIZE 64
-#define ENV_ATTACK 10000 /**< envelope attack rate */
-#define ENV_DECAY 1000 /**< envelope decay rate */
-#define ENV_SUSTAIN 100 /**< envelope sustain level */
-#define ENV_RELEASE 7500 /**< envelope release rate */
-#define NUM_CHANNELS 7 /**< number of sound channels */
-
-// MIDI command values (Shifted right by 4 so they're in the lower nibble)
-#define MIDI_CMD_NOTE_OFF 0x08
-#define MIDI_CMD_NOTE_ON 0x09
-#define MIDI_CMD_CONTROLLER 0x0B
-#define MIDI_CMD_PROGRAM_CHANGE 0x0C
-#define MIDI_CMD_PITCH_WHEEL 0x0E
-// Whole MIDI byte values (Command and channel info together)
-#define MIDI_BYTE_STOP_SEQUENCE 0xFC
-#define MIDI_BYTE_TIMER_SYNC 0xF8
-
-struct IIgsEnvelopeSegment {
- uint8 bp;
- uint16 inc; ///< 8b.8b fixed point, very probably little endian
-};
-
-#define ENVELOPE_SEGMENT_COUNT 8
-struct IIgsEnvelope {
- IIgsEnvelopeSegment seg[ENVELOPE_SEGMENT_COUNT];
-
- /** Reads an Apple IIGS envelope from then given stream. */
- bool read(Common::SeekableReadStream &stream);
-};
-
-// 2**(1/12) i.e. the 12th root of 2
-#define SEMITONE 1.059463094359295
-
-// C6's frequency is A4's (440 Hz) frequency but one full octave and three semitones higher
-// i.e. C6_FREQ = 440 * pow(2.0, 15/12.0)
-#define C6_FREQ 1046.502261202395
-
-// Size of the SIERRASTANDARD file (i.e. the wave file i.e. the sample data used by the instruments).
-#define SIERRASTANDARD_SIZE 65536
-
-// Maximum number of instruments in an Apple IIGS instrument set.
-// Chosen empirically based on Apple IIGS AGI game data, increase if needed.
-#define MAX_INSTRUMENTS 28
-
-struct IIgsWaveInfo {
- uint8 top;
- uint addr;
- uint size;
-// Oscillator channel
-#define OSC_CHANNEL_RIGHT 0
-#define OSC_CHANNEL_LEFT 1
- uint channel;
-// Oscillator mode
-#define OSC_MODE_LOOP 0
-#define OSC_MODE_ONESHOT 1
-#define OSC_MODE_SYNC_AM 2
-#define OSC_MODE_SWAP 3
- uint mode;
- bool halt;
- int16 relPitch; ///< Relative pitch in semitones (Signed 8b.8b fixed point)
-
- /** Reads an Apple IIGS wave information structure from the given stream. */
- bool read(Common::SeekableReadStream &stream, bool ignoreAddr = false);
- bool finalize(Common::SeekableReadStream &uint8Wave);
-};
-
-// Number of waves per Apple IIGS sound oscillator
-#define WAVES_PER_OSCILLATOR 2
-
-/** An Apple IIGS sound oscillator. Consists always of two waves. */
-struct IIgsOscillator {
- IIgsWaveInfo waves[WAVES_PER_OSCILLATOR];
-
- bool finalize(Common::SeekableReadStream &uint8Wave);
-};
-
-// Maximum number of oscillators in an Apple IIGS instrument.
-// Chosen empirically based on Apple IIGS AGI game data, increase if needed.
-#define MAX_OSCILLATORS 4
-
-/** An Apple IIGS sound oscillator list. */
-struct IIgsOscillatorList {
- uint count; ///< Oscillator count
- IIgsOscillator osc[MAX_OSCILLATORS]; ///< The oscillators
-
- /** Indexing operators for easier access to the oscillators. */
- const IIgsOscillator &operator()(uint index) const { return osc[index]; }
- IIgsOscillator &operator()(uint index) { return osc[index]; }
-
- /** Reads an Apple IIGS oscillator list from the given stream. */
- bool read(Common::SeekableReadStream &stream, uint oscillatorCount, bool ignoreAddr = false);
- bool finalize(Common::SeekableReadStream &uint8Wave);
-};
-
-struct IIgsInstrumentHeader {
- IIgsEnvelope env;
- uint8 relseg;
- uint8 bendrange;
- uint8 vibdepth;
- uint8 vibspeed;
- IIgsOscillatorList oscList;
-
- /**
- * Read an Apple IIGS instrument header from the given stream.
- * @param stream The source stream from which to read the data.
- * @param ignoreAddr Should we ignore wave infos' wave address variable's value?
- * @return True if successful, false otherwise.
- */
- bool read(Common::SeekableReadStream &stream, bool ignoreAddr = false);
- bool finalize(Common::SeekableReadStream &uint8Wave);
-};
-
-struct IIgsSampleHeader {
- uint16 type;
- uint8 pitch; ///< Logarithmic, base is 2**(1/12), unknown multiplier (Possibly in range 1040-1080)
- uint8 unknownByte_Ofs3; // 0x7F in Gold Rush's sound resource 60, 0 in all others.
- uint8 volume; ///< Current guess: Logarithmic in 6 dB steps
- uint8 unknownByte_Ofs5; ///< 0 in all tested samples.
- uint16 instrumentSize; ///< Little endian. 44 in all tested samples. A guess.
- uint16 sampleSize; ///< Little endian. Accurate in all tested samples excluding Manhunter I's sound resource 16.
- IIgsInstrumentHeader instrument;
-
- /**
- * Read an Apple IIGS AGI sample header from the given stream.
- * @param stream The source stream from which to read the data.
- * @return True if successful, false otherwise.
- */
- bool read(Common::SeekableReadStream &stream);
- bool finalize(Common::SeekableReadStream &uint8Wave);
-};
+#define SOUND_EMU_MIDI 7
/**
* AGI sound note structure.
@@ -200,87 +58,38 @@ struct AgiNote {
}
};
-struct IIgsChannelInfo {
- const IIgsInstrumentHeader *ins; ///< Instrument info
- const int8 *relocatedSample; ///< Source sample data (8-bit signed format) using relocation
- const int8 *unrelocatedSample; ///< Source sample data (8-bit signed format) without relocation
- frac_t pos; ///< Current sample position
- frac_t posAdd; ///< Current sample position adder (Calculated using note, vibrato etc)
- uint8 origNote; ///< The original note without the added relative pitch
- frac_t note; ///< Note (With the added relative pitch)
- frac_t vol; ///< Current volume (Takes both channel volume and enveloping into account)
- frac_t chanVol; ///< Channel volume
- frac_t startEnvVol; ///< Starting envelope volume
- frac_t envVol; ///< Current envelope volume
- uint envSeg; ///< Current envelope segment
- uint size; ///< Sample size
- bool loop; ///< Should we loop the sample?
- bool end; ///< Has the playing ended?
-
- void rewind(); ///< Rewinds the sound playing on this channel to its start
- void setChannelVolume(uint8 volume); ///< Sets the channel volume
- void setInstrument(const IIgsInstrumentHeader *instrument, const int8 *sample); ///< Sets the instrument to be used on this channel
- void noteOn(uint8 noteParam, uint8 velocity); ///< Starts playing a note on this channel
- void noteOff(uint8 velocity); ///< Releases the note on this channel
- void stop(); ///< Stops the note playing on this channel instantly
- bool playing(); ///< Is there a note playing on this channel?
-};
-
-struct CoCoNote {
- uint8 freq;
- uint8 volume;
- uint16 duration; ///< Note duration
-
- /** Reads a CoCoNote through the given pointer. */
- void read(const uint8 *ptr) {
- freq = *ptr;
- volume = *(ptr + 1);
- duration = READ_LE_UINT16(ptr + 2);
- }
-};
-
/**
* AGI sound resource types.
* It's probably coincidence that all the values here are powers of two
* as they're simply the different used values in AGI sound resources'
* starts (The first 16-bit little endian word, to be precise).
*/
-enum AgiSoundType {
+enum AgiSoundEmuType {
AGI_SOUND_SAMPLE = 0x0001,
AGI_SOUND_MIDI = 0x0002,
AGI_SOUND_4CHN = 0x0008
};
-enum AgiSoundFlags {
- AGI_SOUND_LOOP = 0x0001,
- AGI_SOUND_ENVELOPE = 0x0002
-};
-enum AgiSoundEnv {
- AGI_SOUND_ENV_ATTACK = 3,
- AGI_SOUND_ENV_DECAY = 2,
- AGI_SOUND_ENV_SUSTAIN = 1,
- AGI_SOUND_ENV_RELEASE = 0
-};
+class SoundMgr;
-/**
- * AGI engine sound channel structure.
- */
-struct ChannelInfo {
- AgiSoundType type;
- const uint8 *ptr; // Pointer to the AgiNote data
- const int16 *ins;
- int32 size;
- uint32 phase;
- uint32 flags; // ORs values from AgiSoundFlags
- AgiSoundEnv adsr;
- int32 timer;
- uint32 end;
- uint32 freq;
- uint32 vol;
- uint32 env;
-};
+class SoundGen {
+public:
+ SoundGen(AgiEngine *vm, Audio::Mixer *pMixer) : _vm(vm), _mixer(pMixer) {
+ _sampleRate = pMixer->getOutputRate();
+ }
-class SoundMgr;
+ virtual ~SoundGen() {}
+
+ virtual void play(int resnum) = 0;
+ virtual void stop(void) = 0;
+
+ AgiEngine *_vm;
+
+ Audio::Mixer *_mixer;
+ Audio::SoundHandle _soundHandle;
+
+ uint32 _sampleRate;
+};
/**
* AGI sound resource structure.
@@ -302,7 +111,7 @@ public:
* from memory using free() or delegate the responsibility onwards to some other
* function!
*/
- static AgiSound *createFromRawResource(uint8 *data, uint32 len, int resnum, SoundMgr &manager);
+ static AgiSound *createFromRawResource(uint8 *data, uint32 len, int resnum, SoundMgr &manager, int soundemu);
protected:
SoundMgr &_manager; ///< AGI sound manager object
@@ -313,7 +122,7 @@ protected:
class PCjrSound : public AgiSound {
public:
PCjrSound(uint8 *data, uint32 len, int resnum, SoundMgr &manager);
- ~PCjrSound() { if (_data != NULL) free(_data); }
+ ~PCjrSound() { free(_data); }
virtual uint16 type() { return _type; }
const uint8 *getVoicePointer(uint voiceNum);
protected:
@@ -322,192 +131,30 @@ protected:
uint16 _type; ///< Sound resource type
};
-class IIgsMidi : public AgiSound {
-public:
- IIgsMidi(uint8 *data, uint32 len, int resnum, SoundMgr &manager);
- ~IIgsMidi() { if (_data != NULL) free(_data); }
- virtual uint16 type() { return _type; }
- virtual const uint8 *getPtr() { return _ptr; }
- virtual void setPtr(const uint8 *ptr) { _ptr = ptr; }
- virtual void rewind() { _ptr = _data + 2; _midiTicks = _soundBufTicks = 0; }
-protected:
- uint8 *_data; ///< Raw sound resource data
- const uint8 *_ptr; ///< Pointer to the current position in the MIDI data
- uint32 _len; ///< Length of the raw sound resource
- uint16 _type; ///< Sound resource type
-public:
- uint _midiTicks; ///< MIDI song position in ticks (1/60ths of a second)
- uint _soundBufTicks; ///< Sound buffer position in ticks (1/60ths of a second)
-};
-
-class IIgsSample : public AgiSound {
-public:
- IIgsSample(uint8 *data, uint32 len, int resnum, SoundMgr &manager);
- ~IIgsSample() { delete[] _sample; }
- virtual uint16 type() { return _header.type; }
- const IIgsSampleHeader &getHeader() const { return _header; }
- const int8 *getSample() const { return _sample; }
-protected:
- IIgsSampleHeader _header; ///< Apple IIGS AGI sample header
- int8 *_sample; ///< Sample data (8-bit signed format)
-};
-
-/** Apple IIGS MIDI program change to instrument number mapping. */
-struct MidiProgramChangeMapping {
- byte midiProgToInst[44]; ///< Lookup table for the MIDI program number to instrument number mapping
- byte undefinedInst; ///< The undefined instrument number
-
- // Maps the MIDI program number to an instrument number
- byte map(uint midiProg) const {
- return midiProg < ARRAYSIZE(midiProgToInst) ? midiProgToInst[midiProg] : undefinedInst;
- }
-};
-
-/** Apple IIGS AGI instrument set information. */
-struct InstrumentSetInfo {
- uint byteCount; ///< Length of the whole instrument set in bytes
- uint instCount; ///< Amount of instrument in the set
- const char *md5; ///< MD5 hex digest of the whole instrument set
- const char *waveFileMd5; ///< MD5 hex digest of the wave file (i.e. the sample data used by the instruments)
- const MidiProgramChangeMapping &progToInst; ///< Program change to instrument number mapping
-};
-
-/** Apple IIGS AGI executable file information. */
-struct IIgsExeInfo {
- enum AgiGameID gameid; ///< Game ID
- const char *exePrefix; ///< Prefix of the Apple IIGS AGI executable (e.g. "SQ", "PQ", "KQ4" etc)
- uint agiVer; ///< Apple IIGS AGI version number, not strictly needed
- uint exeSize; ///< Size of the Apple IIGS AGI executable file in bytes
- uint instSetStart; ///< Starting offset of the instrument set inside the executable file
- const InstrumentSetInfo &instSet; ///< Information about the used instrument set
-};
-
-class IIgsMidiChannel {
-public:
- IIgsMidiChannel() : _instrument(0), _sample(0), _volume(0) {}
- uint activeSounds() const; ///< How many active sounds are playing?
- void setInstrument(const IIgsInstrumentHeader *instrument, const int8 *sample);
- void setVolume(uint8 volume);
- void noteOff(uint8 note, uint8 velocity);
- void noteOn(uint8 note, uint8 velocity);
- void stopSounds(); ///< Clears the channel of any sounds
- void removeStoppedSounds(); ///< Removes all stopped sounds from this MIDI channel
-public:
- typedef Common::Array<IIgsChannelInfo>::const_iterator const_iterator;
- typedef Common::Array<IIgsChannelInfo>::iterator iterator;
- Common::Array<IIgsChannelInfo> _gsChannels; ///< Apple IIGS channels playing on this MIDI channel
-protected:
- const IIgsInstrumentHeader *_instrument; ///< Instrument used on this MIDI channel
- const int8 *_sample; ///< Sample data used on this MIDI channel
- uint8 _volume; ///< MIDI controller number 7 (Volume)
-};
-
-/**
- * Class for managing Apple IIGS sound channels.
- * TODO: Check what instruments are used by default on the MIDI channels
- * FIXME: Some instrument choices sound wrong
- */
-class IIgsSoundMgr {
-public:
- typedef Common::Array<IIgsMidiChannel>::const_iterator const_iterator;
- typedef Common::Array<IIgsMidiChannel>::iterator iterator;
- static const uint kSfxMidiChannel = 0; ///< The MIDI channel used for playing sound effects
-public:
- // For initializing
- IIgsSoundMgr();
- void setProgramChangeMapping(const MidiProgramChangeMapping *mapping);
- bool loadInstrumentHeaders(const Common::FSNode &exePath, const IIgsExeInfo &exeInfo);
- bool loadWaveFile(const Common::FSNode &wavePath, const IIgsExeInfo &exeInfo);
- // Miscellaneous methods
- uint activeSounds() const; ///< How many active sounds are playing?
- void stopSounds(); ///< Stops all sounds
- void removeStoppedSounds(); ///< Removes all stopped sounds from the MIDI channels
- // For playing Apple IIGS AGI samples (Sound effects etc)
- bool playSampleSound(const IIgsSampleHeader &sampleHeader, const int8 *sample);
- // MIDI commands
- void midiNoteOff(uint8 channel, uint8 note, uint8 velocity);
- void midiNoteOn(uint8 channel, uint8 note, uint8 velocity);
- void midiController(uint8 channel, uint8 controller, uint8 value);
- void midiProgramChange(uint8 channel, uint8 program);
- void midiPitchWheel(uint8 wheelPos);
-protected:
- const IIgsInstrumentHeader* getInstrument(uint8 program) const;
-public:
- Common::Array<IIgsMidiChannel> _midiChannels; ///< Information about each MIDI channel
-protected:
- Common::Array<int8> _wave; ///< Sample data used by the Apple IIGS MIDI instruments
- const MidiProgramChangeMapping *_midiProgToInst; ///< MIDI program change to instrument number mapping
- Common::Array<IIgsInstrumentHeader> _instruments; ///< Instruments used by the Apple IIGS AGI
-};
-
-class AgiEngine;
-class AgiBase;
-
-class SoundMgr : public Audio::AudioStream {
- AgiBase *_vm;
+class SoundMgr {
public:
- SoundMgr(AgiBase *agi, Audio::Mixer *pMixer);
+ SoundMgr(AgiEngine *agi, Audio::Mixer *pMixer);
~SoundMgr();
- virtual void setVolume(uint8 volume);
-
- // AudioStream API
- int readBuffer(int16 *buffer, const int numSamples) {
- premixerCall(buffer, numSamples / 2);
- return numSamples;
- }
-
- bool isStereo() const {
- return false;
- }
- bool endOfData() const {
- return false;
- }
-
- int getRate() const {
- // FIXME: Ideally, we should use _sampleRate.
- return 22050;
- }
-
-private:
- Audio::Mixer *_mixer;
- Audio::SoundHandle _soundHandle;
- uint32 _sampleRate;
-
- bool _playing;
- ChannelInfo _chn[NUM_CHANNELS];
- IIgsSoundMgr _gsSound;
- int _endflag;
- int _playingSound;
- uint8 _env;
- bool _disabledMidi;
-
- int16 *_sndBuffer;
- const int16 *_waveform;
-
- bool _useChorus;
-
- void premixerCall(int16 *buf, uint len);
- void fillAudio(void *udata, int16 *stream, uint len);
+ void setVolume(uint8 volume);
-public:
void unloadSound(int);
void playSound();
int initSound();
void deinitSound();
void startSound(int, int);
void stopSound();
- void stopNote(int i);
- void playNote(int i, int freq, int vol);
- void playAgiSound();
- void playCoCoSound();
- uint32 mixSound();
- bool loadInstruments();
- void playMidiSound();
- void playSampleSound();
- const IIgsExeInfo *getIIgsExeInfo(enum AgiGameID gameid) const;
- static bool convertWave(Common::SeekableReadStream &source, int8 *dest, uint length);
+
+ void soundIsFinished();
+
+private:
+ int _endflag;
+ AgiEngine *_vm;
+
+ SoundGen *_soundGen;
+
+ int _playingSound;
};
} // End of namespace Agi
diff --git a/engines/agi/sound_2gs.cpp b/engines/agi/sound_2gs.cpp
new file mode 100644
index 0000000000..cc1cd0f6d5
--- /dev/null
+++ b/engines/agi/sound_2gs.cpp
@@ -0,0 +1,919 @@
+/* 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/config-manager.h"
+#include "common/fs.h"
+#include "common/md5.h"
+#include "common/str-array.h"
+
+#include "agi/agi.h"
+#include "agi/sound_2gs.h"
+
+namespace Agi {
+
+SoundGen2GS::SoundGen2GS(AgiEngine *vm, Audio::Mixer *pMixer) : SoundGen(vm, pMixer) {
+ _disabledMidi = !loadInstruments();
+
+ _playingSound = -1;
+ _playing = false;
+
+ _sndBuffer = (int16 *)calloc(2, BUFFER_SIZE);
+
+ _midiChannels.resize(16); // Set the amount of available MIDI channels
+
+ _mixer->playStream(Audio::Mixer::kMusicSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
+}
+
+SoundGen2GS::~SoundGen2GS() {
+ _mixer->stopHandle(_soundHandle);
+
+ free(_sndBuffer);
+}
+
+int SoundGen2GS::readBuffer(int16 *buffer, const int numSamples) {
+ fillAudio(buffer, numSamples / 2);
+
+ return numSamples;
+}
+
+void SoundGen2GS::play(int resnum) {
+ AgiSoundEmuType type;
+
+ _playingSound = resnum;
+
+ type = (AgiSoundEmuType)_vm->_game.sounds[resnum]->type();
+
+ assert (type == AGI_SOUND_SAMPLE || type == AGI_SOUND_MIDI);
+
+ switch (type) {
+ case AGI_SOUND_SAMPLE: {
+ IIgsSample *sampleRes = (IIgsSample *) _vm->_game.sounds[_playingSound];
+ playSampleSound(sampleRes->getHeader(), sampleRes->getSample());
+ break;
+ }
+ case AGI_SOUND_MIDI:
+ ((IIgsMidi *) _vm->_game.sounds[_playingSound])->rewind();
+ break;
+ default:
+ break;
+ }
+}
+
+void SoundGen2GS::stop() {
+ _playingSound = -1;
+
+ // Stops all sounds on all MIDI channels
+ for (iterator iter = _midiChannels.begin(); iter != _midiChannels.end(); ++iter)
+ iter->stopSounds();
+}
+
+void SoundGen2GS::playSound() {
+ if (_playingSound == -1)
+ return;
+
+ if (_vm->_game.sounds[_playingSound]->type() == AGI_SOUND_MIDI) {
+ playMidiSound();
+ //warning("playSound: Trying to play an Apple IIGS MIDI sound. Not yet implemented");
+ } else if (_vm->_game.sounds[_playingSound]->type() == AGI_SOUND_SAMPLE) {
+ //debugC(3, kDebugLevelSound, "playSound: Trying to play an Apple IIGS sample");
+ playSampleSound();
+ }
+
+ if (!_playing) {
+ _vm->_sound->soundIsFinished();
+
+ _playingSound = -1;
+ }
+}
+
+uint32 SoundGen2GS::mixSound() {
+ int i, b;
+
+ memset(_sndBuffer, 0, BUFFER_SIZE << 1);
+
+ if (!_playing || _playingSound == -1)
+ return BUFFER_SIZE;
+
+ // Handle Apple IIGS sound mixing here
+ // TODO: Implement playing both waves in an oscillator
+ // TODO: Implement swap-mode in an oscillator
+ for (uint midiChan = 0; midiChan < _midiChannels.size(); midiChan++) {
+ for (uint gsChan = 0; gsChan < _midiChannels[midiChan]._gsChannels.size(); gsChan++) {
+ IIgsChannelInfo &channel = _midiChannels[midiChan]._gsChannels[gsChan];
+ if (channel.playing()) { // Only mix in actively playing channels
+ // Frequency multiplier was 1076.0 based on tests made with MESS 0.117.
+ // Tests made with KEGS32 averaged the multiplier to around 1045.
+ // So this is a guess but maybe it's 1046.5... i.e. C6's frequency?
+ double hertz = C6_FREQ * pow(SEMITONE, fracToDouble(channel.note));
+ channel.posAdd = doubleToFrac(hertz / getRate());
+ channel.vol = doubleToFrac(fracToDouble(channel.envVol) * fracToDouble(channel.chanVol) / 127.0);
+ double tempVol = fracToDouble(channel.vol)/127.0;
+ for (i = 0; i < IIGS_BUFFER_SIZE; i++) {
+ b = channel.relocatedSample[fracToInt(channel.pos)];
+ // TODO: Find out what volume/amplification setting is loud enough
+ // but still doesn't clip when playing many channels on it.
+ _sndBuffer[i] += (int16) (b * tempVol * 256/4);
+ channel.pos += channel.posAdd;
+
+ if (channel.pos >= intToFrac(channel.size)) {
+ if (channel.loop) {
+ // Don't divide by zero on zero length samples
+ channel.pos %= intToFrac(channel.size + (channel.size == 0));
+ // Probably we should loop the envelope too
+ channel.envSeg = 0;
+ channel.envVol = channel.startEnvVol;
+ } else {
+ channel.pos = channel.chanVol = 0;
+ channel.end = true;
+ break;
+ }
+ }
+ }
+
+ if (channel.envSeg < ENVELOPE_SEGMENT_COUNT) {
+ const IIgsEnvelopeSegment &seg = channel.ins->env.seg[channel.envSeg];
+ // I currently assume enveloping works with the same speed as the MIDI
+ // (i.e. with 1/60ths of a second ticks).
+ // TODO: Check if enveloping really works with the same speed as MIDI
+ frac_t envVolDelta = doubleToFrac(seg.inc/256.0);
+ if (intToFrac(seg.bp) >= channel.envVol) {
+ channel.envVol += envVolDelta;
+ if (channel.envVol >= intToFrac(seg.bp)) {
+ channel.envVol = intToFrac(seg.bp);
+ channel.envSeg += 1;
+ }
+ } else {
+ channel.envVol -= envVolDelta;
+ if (channel.envVol <= intToFrac(seg.bp)) {
+ channel.envVol = intToFrac(seg.bp);
+ channel.envSeg += 1;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ removeStoppedSounds();
+
+ return IIGS_BUFFER_SIZE;
+}
+
+void SoundGen2GS::fillAudio(int16 *stream, uint len) {
+ uint32 p = 0;
+
+ // current number of audio bytes in _sndBuffer
+ static uint32 data_available = 0;
+ // offset of start of audio bytes in _sndBuffer
+ static uint32 data_offset = 0;
+
+ len <<= 2;
+
+ debugC(5, kDebugLevelSound, "(%p, %d)", (void *)stream, len);
+
+ while (len > data_available) {
+ memcpy((uint8 *)stream + p, (uint8*)_sndBuffer + data_offset, data_available);
+ p += data_available;
+ len -= data_available;
+
+ playSound();
+ data_available = mixSound() << 1;
+ data_offset = 0;
+ }
+
+ memcpy((uint8 *)stream + p, (uint8*)_sndBuffer + data_offset, len);
+ data_offset += len;
+ data_available -= len;
+}
+
+void SoundGen2GS::playSampleSound() {
+ if (_vm->_soundemu != SOUND_EMU_APPLE2GS) {
+ warning("Trying to play a sample but not using Apple IIGS sound emulation mode");
+ return;
+ }
+
+ if (_playingSound != -1)
+ _playing = activeSounds() > 0;
+}
+
+void SoundGen2GS::stopSounds() {
+ // Stops all sounds on all MIDI channels
+ for (iterator iter = _midiChannels.begin(); iter != _midiChannels.end(); ++iter)
+ iter->stopSounds();
+}
+
+bool SoundGen2GS::playSampleSound(const IIgsSampleHeader &sampleHeader, const int8 *sample) {
+ stopSounds();
+ IIgsMidiChannel &channel = _midiChannels[kSfxMidiChannel];
+
+ channel.setInstrument(&sampleHeader.instrument, sample);
+ channel.setVolume(sampleHeader.volume);
+ channel.noteOn(sampleHeader.pitch, 64); // Use default velocity (i.e. 64)
+
+ return true;
+}
+
+void SoundGen2GS::playMidiSound() {
+ if (_disabledMidi)
+ return;
+
+ const uint8 *p;
+ uint8 parm1, parm2;
+ static uint8 cmd, ch;
+
+ if (_playingSound == -1 || _vm->_game.sounds[_playingSound] == NULL) {
+ warning("Error playing Apple IIGS MIDI sound resource");
+ _playing = false;
+
+ return;
+ }
+
+ IIgsMidi *midiObj = (IIgsMidi *) _vm->_game.sounds[_playingSound];
+
+ _playing = true;
+ p = midiObj->getPtr();
+
+ midiObj->_soundBufTicks++;
+
+ while (true) {
+ uint8 readByte = *p;
+
+ // Check for end of MIDI sequence marker (Can also be here before delta-time)
+ if (readByte == MIDI_BYTE_STOP_SEQUENCE) {
+ debugC(3, kDebugLevelSound, "End of MIDI sequence (Before reading delta-time)");
+ _playing = false;
+
+ midiObj->rewind();
+
+ return;
+ } else if (readByte == MIDI_BYTE_TIMER_SYNC) {
+ debugC(3, kDebugLevelSound, "Timer sync");
+ p++; // Jump over the timer sync byte as it's not needed
+
+ continue;
+ }
+
+ uint8 deltaTime = readByte;
+ if (midiObj->_midiTicks + deltaTime > midiObj->_soundBufTicks) {
+ break;
+ }
+ midiObj->_midiTicks += deltaTime;
+ p++; // Jump over the delta-time byte as it was already taken care of
+
+ // Check for end of MIDI sequence marker (This time it after reading delta-time)
+ if (*p == MIDI_BYTE_STOP_SEQUENCE) {
+ debugC(3, kDebugLevelSound, "End of MIDI sequence (After reading delta-time)");
+ _playing = false;
+
+ midiObj->rewind();
+
+ return;
+ }
+
+ // Separate byte into command and channel if it's a command byte.
+ // Otherwise use running status (i.e. previously set command and channel).
+ if (*p & 0x80) {
+ cmd = *p++;
+ ch = cmd & 0x0f;
+ cmd >>= 4;
+ }
+
+ switch (cmd) {
+ case MIDI_CMD_NOTE_OFF:
+ parm1 = *p++;
+ parm2 = *p++;
+ midiNoteOff(ch, parm1, parm2);
+ break;
+ case MIDI_CMD_NOTE_ON:
+ parm1 = *p++;
+ parm2 = *p++;
+ midiNoteOn(ch, parm1, parm2);
+ break;
+ case MIDI_CMD_CONTROLLER:
+ parm1 = *p++;
+ parm2 = *p++;
+ midiController(ch, parm1, parm2);
+ break;
+ case MIDI_CMD_PROGRAM_CHANGE:
+ parm1 = *p++;
+ midiProgramChange(ch, parm1);
+ break;
+ case MIDI_CMD_PITCH_WHEEL:
+ parm1 = *p++;
+ parm2 = *p++;
+
+ uint16 wheelPos = ((parm2 & 0x7F) << 7) | (parm1 & 0x7F); // 14-bit value
+ midiPitchWheel(wheelPos);
+ break;
+ }
+ }
+
+ midiObj->setPtr(p);
+}
+
+void SoundGen2GS::midiNoteOff(uint8 channel, uint8 note, uint8 velocity) {
+ _midiChannels[channel].noteOff(note, velocity);
+ debugC(3, kDebugLevelSound, "note off, channel %02x, note %02x, velocity %02x", channel, note, velocity);
+}
+
+void SoundGen2GS::midiNoteOn(uint8 channel, uint8 note, uint8 velocity) {
+ _midiChannels[channel].noteOn(note, velocity);
+ debugC(3, kDebugLevelSound, "note on, channel %02x, note %02x, velocity %02x", channel, note, velocity);
+}
+
+// TODO: Check if controllers behave differently on different MIDI channels
+// TODO: Doublecheck what other controllers than the volume controller do
+void SoundGen2GS::midiController(uint8 channel, uint8 controller, uint8 value) {
+ IIgsMidiChannel &midiChannel = _midiChannels[channel];
+
+ // The tested Apple IIGS AGI MIDI resources only used
+ // controllers 0 (Bank select?), 7 (Volume) and 64 (Sustain On/Off).
+ // Controller 0's parameter was in range 94-127,
+ // controller 7's parameter was in range 0-127 and
+ // controller 64's parameter was always 0 (i.e. sustain off).
+ bool unimplemented = false;
+ switch (controller) {
+ case 7: // Volume
+ midiChannel.setVolume(value);
+ break;
+ default:
+ unimplemented = true;
+ break;
+ }
+ debugC(3, kDebugLevelSound, "controller %02x, ch %02x, val %02x%s", controller, channel, value, unimplemented ? " (Unimplemented)" : "");
+}
+
+void SoundGen2GS::midiProgramChange(uint8 channel, uint8 program) {
+ _midiChannels[channel].setInstrument(getInstrument(program), _wave.begin());
+ debugC(3, kDebugLevelSound, "program change %02x, channel %02x", program, channel);
+}
+
+void SoundGen2GS::midiPitchWheel(uint8 wheelPos) {
+ // In all the tested Apple IIGS AGI MIDI resources
+ // pitch wheel commands always used 0x2000 (Center position).
+ // Therefore it should be quite safe to ignore this command.
+ debugC(3, kDebugLevelSound, "pitch wheel position %04x (Unimplemented)", wheelPos);
+}
+
+const IIgsInstrumentHeader* SoundGen2GS::getInstrument(uint8 program) const {
+ return &_instruments[_midiProgToInst->map(program)];
+}
+
+void SoundGen2GS::setProgramChangeMapping(const MidiProgramChangeMapping *mapping) {
+ _midiProgToInst = mapping;
+}
+
+void SoundGen2GS::removeStoppedSounds() {
+ for (Common::Array<IIgsMidiChannel>::iterator iter = _midiChannels.begin(); iter != _midiChannels.end(); ++iter)
+ iter->removeStoppedSounds();
+}
+
+uint SoundGen2GS::activeSounds() const {
+ uint result = 0;
+
+ for (Common::Array<IIgsMidiChannel>::const_iterator iter = _midiChannels.begin(); iter != _midiChannels.end(); ++iter)
+ result += iter->activeSounds();
+
+ return result;
+}
+
+IIgsMidi::IIgsMidi(uint8 *data, uint32 len, int resnum, SoundMgr &manager) : AgiSound(manager) {
+ _data = data; // Save the resource pointer
+ _ptr = _data + 2; // Set current position to just after the header
+ _len = len; // Save the resource's length
+ _type = READ_LE_UINT16(data); // Read sound resource's type
+ _midiTicks = _soundBufTicks = 0;
+ _isValid = (_type == AGI_SOUND_MIDI) && (_data != NULL) && (_len >= 2);
+
+ if (!_isValid) // Check for errors
+ warning("Error creating Apple IIGS midi sound from resource %d (Type %d, length %d)", resnum, _type, len);
+}
+
+/**
+ * Convert sample from 8-bit unsigned to 8-bit signed format.
+ * @param source Source stream containing the 8-bit unsigned sample data.
+ * @param dest Destination buffer for the 8-bit signed sample data.
+ * @param length Length of the sample data to be converted.
+ */
+static bool convertWave(Common::SeekableReadStream &source, int8 *dest, uint length) {
+ // Convert the wave from 8-bit unsigned to 8-bit signed format
+ for (uint i = 0; i < length; i++)
+ dest[i] = (int8) ((int) source.readByte() - 128);
+ return !(source.eos() || source.err());
+}
+
+IIgsSample::IIgsSample(uint8 *data, uint32 len, int resnum, SoundMgr &manager) : AgiSound(manager) {
+ Common::MemoryReadStream stream(data, len, DisposeAfterUse::YES);
+
+ // Check that the header was read ok and that it's of the correct type
+ if (_header.read(stream) && _header.type == AGI_SOUND_SAMPLE) { // An Apple IIGS AGI sample resource
+ uint32 sampleStartPos = stream.pos();
+ uint32 tailLen = stream.size() - sampleStartPos;
+
+ if (tailLen < _header.sampleSize) { // Check if there's no room for the sample data in the stream
+ // Apple IIGS Manhunter I: Sound resource 16 has only 16074 bytes
+ // of sample data although header says it should have 16384 bytes.
+ warning("Apple IIGS sample (%d) too short (%d bytes. Should be %d bytes). Using the part that's left",
+ resnum, tailLen, _header.sampleSize);
+
+ _header.sampleSize = (uint16) tailLen; // Use the part that's left
+ }
+
+ if (_header.pitch > 0x7F) { // Check if the pitch is invalid
+ warning("Apple IIGS sample (%d) has too high pitch (0x%02x)", resnum, _header.pitch);
+
+ _header.pitch &= 0x7F; // Apple IIGS AGI probably did it this way too
+ }
+
+ // Finalize the header info using the 8-bit unsigned sample data
+ _header.finalize(stream);
+
+ // Convert sample data from 8-bit unsigned to 8-bit signed format
+ stream.seek(sampleStartPos);
+ _sample = new int8[_header.sampleSize];
+
+ if (_sample != NULL)
+ _isValid = convertWave(stream, _sample, _header.sampleSize);
+ }
+
+ if (!_isValid) // Check for errors
+ warning("Error creating Apple IIGS sample from resource %d (Type %d, length %d)", resnum, _header.type, len);
+}
+
+/** Reads an Apple IIGS envelope from then given stream. */
+bool IIgsEnvelope::read(Common::SeekableReadStream &stream) {
+ for (int segNum = 0; segNum < ENVELOPE_SEGMENT_COUNT; segNum++) {
+ seg[segNum].bp = stream.readByte();
+ seg[segNum].inc = stream.readUint16LE();
+ }
+
+ return !(stream.eos() || stream.err());
+}
+
+/** Reads an Apple IIGS wave information structure from the given stream. */
+bool IIgsWaveInfo::read(Common::SeekableReadStream &stream, bool ignoreAddr) {
+ top = stream.readByte();
+ addr = stream.readByte() * 256;
+ size = (1 << (stream.readByte() & 7)) * 256;
+
+ // Read packed mode byte and parse it into parts
+ byte packedModeByte = stream.readByte();
+ channel = (packedModeByte >> 4) & 1; // Bit 4
+ mode = (packedModeByte >> 1) & 3; // Bits 1-2
+ halt = (packedModeByte & 1) != 0; // Bit 0 (Converted to boolean)
+
+ relPitch = stream.readSint16LE();
+
+ // Zero the wave address if we want to ignore the wave address info
+ if (ignoreAddr)
+ addr = 0;
+
+ return !(stream.eos() || stream.err());
+}
+
+bool IIgsWaveInfo::finalize(Common::SeekableReadStream &uint8Wave) {
+ uint32 startPos = uint8Wave.pos(); // Save stream's starting position
+ uint8Wave.seek(addr, SEEK_CUR); // Seek to wave's address
+
+ // Calculate the true sample size (A zero ends the sample prematurely)
+ uint trueSize = size; // Set a default value for the result
+ for (uint i = 0; i < size; i++) {
+ if (uint8Wave.readByte() == 0) {
+ trueSize = i;
+ // A zero in the sample stream turns off looping
+ // (At least that's what MESS 0.117 and KEGS32 0.91 seem to do)
+ if (mode == OSC_MODE_LOOP)
+ mode = OSC_MODE_ONESHOT;
+ break;
+ }
+ }
+ size = trueSize; // Set the true sample size
+
+ uint8Wave.seek(startPos); // Seek back to the stream's starting position
+
+ return true;
+}
+
+bool IIgsOscillator::finalize(Common::SeekableReadStream &uint8Wave) {
+ for (uint i = 0; i < WAVES_PER_OSCILLATOR; i++)
+ if (!waves[i].finalize(uint8Wave))
+ return false;
+
+ return true;
+}
+
+bool IIgsOscillatorList::read(Common::SeekableReadStream &stream, uint oscillatorCount, bool ignoreAddr) {
+ // First read the A waves and then the B waves for the oscillators
+ for (uint waveNum = 0; waveNum < WAVES_PER_OSCILLATOR; waveNum++)
+ for (uint oscNum = 0; oscNum < oscillatorCount; oscNum++)
+ if (!osc[oscNum].waves[waveNum].read(stream, ignoreAddr))
+ return false;
+
+ count = oscillatorCount; // Set the oscillator count
+
+ return true;
+}
+
+bool IIgsOscillatorList::finalize(Common::SeekableReadStream &uint8Wave) {
+ for (uint i = 0; i < count; i++)
+ if (!osc[i].finalize(uint8Wave))
+ return false;
+
+ return true;
+}
+
+bool IIgsInstrumentHeader::read(Common::SeekableReadStream &stream, bool ignoreAddr) {
+ env.read(stream);
+ relseg = stream.readByte();
+ /*byte priority =*/ stream.readByte(); // Not needed? 32 in all tested data.
+ bendrange = stream.readByte();
+ vibdepth = stream.readByte();
+ vibspeed = stream.readByte();
+ /*byte spare =*/ stream.readByte(); // Not needed? 0 in all tested data.
+ byte wac = stream.readByte(); // Read A wave count
+ byte wbc = stream.readByte(); // Read B wave count
+ oscList.read(stream, wac, ignoreAddr); // Read the oscillators
+ return (wac == wbc) && !(stream.eos() || stream.err()); // A and B wave counts must match
+}
+
+bool IIgsInstrumentHeader::finalize(Common::SeekableReadStream &uint8Wave) {
+ return oscList.finalize(uint8Wave);
+}
+
+bool IIgsSampleHeader::read(Common::SeekableReadStream &stream) {
+ type = stream.readUint16LE();
+ pitch = stream.readByte();
+ unknownByte_Ofs3 = stream.readByte();
+ volume = stream.readByte();
+ unknownByte_Ofs5 = stream.readByte();
+ instrumentSize = stream.readUint16LE();
+ sampleSize = stream.readUint16LE();
+ // Read the instrument header *ignoring* its wave address info
+
+ return instrument.read(stream, true);
+}
+
+bool IIgsSampleHeader::finalize(Common::SeekableReadStream &uint8Wave) {
+ return instrument.finalize(uint8Wave);
+}
+
+void IIgsMidiChannel::stopSounds() {
+ // Stops all sounds on this single MIDI channel
+ for (iterator iter = _gsChannels.begin(); iter != _gsChannels.end(); ++iter)
+ iter->stop();
+
+ _gsChannels.clear();
+}
+
+void IIgsMidiChannel::removeStoppedSounds() {
+ for (int i = _gsChannels.size() - 1; i >= 0; i--)
+ if (!_gsChannels[i].playing())
+ _gsChannels.remove_at(i);
+}
+
+uint IIgsMidiChannel::activeSounds() const {
+ uint result = 0;
+
+ for (const_iterator iter = _gsChannels.begin(); iter != _gsChannels.end(); ++iter)
+ if (!iter->end)
+ result++;
+
+ return result;
+}
+
+void IIgsMidiChannel::setInstrument(const IIgsInstrumentHeader *instrument, const int8 *sample) {
+ _instrument = instrument;
+ _sample = sample;
+
+ // Set program on each Apple IIGS channel playing on this MIDI channel
+ for (iterator iter = _gsChannels.begin(); iter != _gsChannels.end(); ++iter)
+ iter->setInstrument(instrument, sample);
+}
+
+void IIgsMidiChannel::setVolume(uint8 volume) {
+ _volume = volume;
+
+ // Set volume on each Apple IIGS channel playing on this MIDI channel
+ for (iterator iter = _gsChannels.begin(); iter != _gsChannels.end(); ++iter)
+ iter->setChannelVolume(volume);
+}
+
+void IIgsMidiChannel::noteOff(uint8 note, uint8 velocity) {
+ // Go through all the notes playing on this MIDI channel
+ // and turn off the ones that are playing the given note
+ for (iterator iter = _gsChannels.begin(); iter != _gsChannels.end(); ++iter)
+ if (iter->origNote == note)
+ iter->noteOff(velocity);
+}
+
+void IIgsMidiChannel::noteOn(uint8 note, uint8 velocity) {
+ IIgsChannelInfo channel;
+
+ // Use the default channel volume and instrument
+ channel.setChannelVolume(_volume);
+ channel.setInstrument(_instrument, _sample);
+
+ // Set the note on and save the channel
+ channel.noteOn(note, velocity);
+ _gsChannels.push_back(channel);
+}
+
+void IIgsChannelInfo::rewind() {
+ this->envVol = this->startEnvVol;
+ this->envSeg = 0;
+ this->pos = intToFrac(0);
+}
+
+void IIgsChannelInfo::setChannelVolume(uint8 volume) {
+ this->chanVol = intToFrac(volume);
+}
+
+void IIgsChannelInfo::setInstrument(const IIgsInstrumentHeader *instrument, const int8 *sample) {
+ assert(instrument != NULL && sample != NULL);
+ this->ins = instrument;
+ this->unrelocatedSample = sample;
+}
+
+// TODO/FIXME: Implement correctly and fully (Take velocity into account etc)
+void IIgsChannelInfo::noteOn(uint8 noteParam, uint8 velocity) {
+ this->origNote = noteParam;
+ this->startEnvVol = intToFrac(0);
+ rewind();
+
+ const IIgsWaveInfo *waveInfo = NULL;
+
+ for (uint i = 0; i < ins->oscList.count; i++)
+ if (ins->oscList(i).waves[0].top >= noteParam)
+ waveInfo = &ins->oscList(i).waves[0];
+
+ assert(waveInfo != NULL);
+
+ this->relocatedSample = this->unrelocatedSample + waveInfo->addr;
+ this->posAdd = intToFrac(0);
+ this->note = intToFrac(noteParam) + doubleToFrac(waveInfo->relPitch/256.0);
+ this->vol = doubleToFrac(fracToDouble(this->envVol) * fracToDouble(this->chanVol) / 127.0);
+ this->loop = (waveInfo->mode == OSC_MODE_LOOP);
+ this->size = waveInfo->size - waveInfo->addr;
+ this->end = waveInfo->halt;
+}
+
+// TODO/FIXME: Implement correctly and fully (Take release time and velocity into account etc)
+void IIgsChannelInfo::noteOff(uint8 velocity) {
+ this->loop = false;
+ this->envSeg = ins->relseg;
+}
+
+void IIgsChannelInfo::stop() {
+ this->end = true;
+}
+
+bool IIgsChannelInfo::playing() {
+ return !this->end;
+}
+
+/**
+ * A function object (i.e. a functor) for testing if a Common::FSNode
+ * object's name is equal (Ignoring case) to a string or to at least
+ * one of the strings in a list of strings. Can be used e.g. with find_if().
+ */
+struct fsnodeNameEqualsIgnoreCase : public Common::UnaryFunction<const Common::FSNode&, bool> {
+// FIXME: This should be replaced; use SearchMan instead
+ fsnodeNameEqualsIgnoreCase(const Common::StringArray &str) : _str(str) {}
+ fsnodeNameEqualsIgnoreCase(const Common::String str) { _str.push_back(str); }
+ bool operator()(const Common::FSNode &param) const {
+ for (Common::StringArray::const_iterator iter = _str.begin(); iter != _str.end(); ++iter)
+ if (param.getName().equalsIgnoreCase(*iter))
+ return true;
+ return false;
+ }
+private:
+ Common::StringArray _str;
+};
+
+bool SoundGen2GS::loadInstruments() {
+ // Check that the platform is Apple IIGS, as only it uses custom instruments
+ if (_vm->getPlatform() != Common::kPlatformApple2GS) {
+ debugC(3, kDebugLevelSound, "Platform isn't Apple IIGS so not loading any instruments");
+ return true;
+ }
+
+ // Get info on the particular Apple IIGS AGI game's executable
+ const IIgsExeInfo *exeInfo = getIIgsExeInfo((enum AgiGameID) _vm->getGameID());
+ if (exeInfo == NULL) {
+ warning("Unsupported Apple IIGS game, not loading instruments");
+ return false;
+ }
+
+ // List files in the game path
+ Common::FSList fslist;
+ Common::FSNode dir(ConfMan.get("path"));
+ if (!dir.getChildren(fslist, Common::FSNode::kListFilesOnly)) {
+ warning("Invalid game path (\"%s\"), not loading Apple IIGS instruments", dir.getPath().c_str());
+ return false;
+ }
+
+ // Populate executable filenames list (Long filename and short filename) for searching
+ Common::StringArray exeNames;
+ exeNames.push_back(Common::String(exeInfo->exePrefix) + ".SYS16");
+ exeNames.push_back(Common::String(exeInfo->exePrefix) + ".SYS");
+
+ // Populate wave filenames list (Long filename and short filename) for searching
+ Common::StringArray waveNames;
+ waveNames.push_back("SIERRASTANDARD");
+ waveNames.push_back("SIERRAST");
+
+ // Search for the executable file and the wave file (i.e. check if any of the filenames match)
+ Common::FSList::const_iterator exeFsnode, waveFsnode;
+ exeFsnode = Common::find_if(fslist.begin(), fslist.end(), fsnodeNameEqualsIgnoreCase(exeNames));
+ waveFsnode = Common::find_if(fslist.begin(), fslist.end(), fsnodeNameEqualsIgnoreCase(waveNames));
+
+ // Make sure that we found the executable file
+ if (exeFsnode == fslist.end()) {
+ warning("Couldn't find Apple IIGS game executable (%s), not loading instruments", exeNames.begin()->c_str());
+ return false;
+ }
+
+ // Make sure that we found the wave file
+ if (waveFsnode == fslist.end()) {
+ warning("Couldn't find Apple IIGS wave file (%s), not loading instruments", waveNames.begin()->c_str());
+ return false;
+ }
+
+ // Set the MIDI program change to instrument number mapping and
+ // load the instrument headers and their sample data.
+ // None of the tested SIERRASTANDARD-files have zeroes in them so
+ // there's no need to check for prematurely ending samples here.
+ setProgramChangeMapping(&exeInfo->instSet.progToInst);
+ return loadWaveFile(*waveFsnode, *exeInfo) && loadInstrumentHeaders(*exeFsnode, *exeInfo);
+}
+
+/** Older Apple IIGS AGI MIDI program change to instrument number mapping. */
+static const MidiProgramChangeMapping progToInstMappingV1 = {
+ {19, 20, 22, 23, 21, 24, 5, 5, 5, 5,
+ 6, 7, 10, 9, 11, 9, 15, 8, 5, 5,
+ 17, 16, 18, 12, 14, 5, 5, 5, 5, 5,
+ 0, 1, 2, 9, 3, 4, 15, 2, 2, 2,
+ 25, 13, 13, 25},
+ 5
+};
+
+/** Newer Apple IIGS AGI MIDI program change to instrument number mapping. */
+static const MidiProgramChangeMapping progToInstMappingV2 = {
+ {21, 22, 24, 25, 23, 26, 6, 6, 6, 6,
+ 7, 9, 12, 8, 13, 11, 17, 10, 6, 6,
+ 19, 18, 20, 14, 16, 6, 6, 6, 6, 6,
+ 0, 1, 2, 4, 3, 5, 17, 2, 2, 2,
+ 27, 15, 15, 27},
+ 6
+};
+
+/** Older Apple IIGS AGI instrument set. Used only by Space Quest I (AGI v1.002). */
+static const InstrumentSetInfo instSetV1 = {
+ 1192, 26, "7ee16bbc135171ffd6b9120cc7ff1af2", "edd3bf8905d9c238e02832b732fb2e18", progToInstMappingV1
+};
+
+/** Newer Apple IIGS AGI instrument set (AGI v1.003+). Used by all others than Space Quest I. */
+static const InstrumentSetInfo instSetV2 = {
+ 1292, 28, "b7d428955bb90721996de1cbca25e768", "c05fb0b0e11deefab58bc68fbd2a3d07", progToInstMappingV2
+};
+
+/** Information about different Apple IIGS AGI executables. */
+static const IIgsExeInfo IIgsExeInfos[] = {
+ {GID_SQ1, "SQ", 0x1002, 138496, 0x80AD, instSetV1},
+ {GID_LSL1, "LL", 0x1003, 141003, 0x844E, instSetV2},
+ {GID_AGIDEMO, "DEMO", 0x1005, 141884, 0x8469, instSetV2},
+ {GID_KQ1, "KQ", 0x1006, 141894, 0x8469, instSetV2},
+ {GID_PQ1, "PQ", 0x1007, 141882, 0x8469, instSetV2},
+ {GID_MIXEDUP, "MG", 0x1013, 142552, 0x84B7, instSetV2},
+ {GID_KQ2, "KQ2", 0x1013, 143775, 0x84B7, instSetV2},
+ {GID_KQ3, "KQ3", 0x1014, 144312, 0x84B7, instSetV2},
+ {GID_SQ2, "SQ2", 0x1014, 107882, 0x6563, instSetV2},
+ {GID_MH1, "MH", 0x2004, 147678, 0x8979, instSetV2},
+ {GID_KQ4, "KQ4", 0x2006, 147652, 0x8979, instSetV2},
+ {GID_BC, "BC", 0x3001, 148192, 0x8979, instSetV2},
+ {GID_GOLDRUSH, "GR", 0x3003, 148268, 0x8979, instSetV2}
+};
+
+/**
+ * Finds information about an Apple IIGS AGI executable based on the game ID.
+ * @return A non-null IIgsExeInfo pointer if successful, otherwise NULL.
+ */
+const IIgsExeInfo *SoundGen2GS::getIIgsExeInfo(enum AgiGameID gameid) const {
+ for (int i = 0; i < ARRAYSIZE(IIgsExeInfos); i++)
+ if (IIgsExeInfos[i].gameid == gameid)
+ return &IIgsExeInfos[i];
+ return NULL;
+}
+
+bool SoundGen2GS::loadInstrumentHeaders(const Common::FSNode &exePath, const IIgsExeInfo &exeInfo) {
+ bool loadedOk = false; // Was loading successful?
+ Common::File file;
+
+ // Open the executable file and check that it has correct size
+ file.open(exePath);
+ if (file.size() != (int32)exeInfo.exeSize) {
+ debugC(3, kDebugLevelSound, "Apple IIGS executable (%s) has wrong size (Is %d, should be %d)",
+ exePath.getPath().c_str(), file.size(), exeInfo.exeSize);
+ }
+
+ // Read the whole executable file into memory
+ Common::SharedPtr<Common::MemoryReadStream> data(file.readStream(file.size()));
+ file.close();
+
+ // Check that we got enough data to be able to parse the instruments
+ if (data && data->size() >= (int32)(exeInfo.instSetStart + exeInfo.instSet.byteCount)) {
+ // Check instrument set's length (The info's saved in the executable)
+ data->seek(exeInfo.instSetStart - 4);
+ uint16 instSetByteCount = data->readUint16LE();
+ if (instSetByteCount != exeInfo.instSet.byteCount) {
+ debugC(3, kDebugLevelSound, "Wrong instrument set size (Is %d, should be %d) in Apple IIGS executable (%s)",
+ instSetByteCount, exeInfo.instSet.byteCount, exePath.getPath().c_str());
+ }
+
+ // Check instrument set's md5sum
+ data->seek(exeInfo.instSetStart);
+
+ char md5str[32+1];
+ Common::md5_file_string(*data, md5str, exeInfo.instSet.byteCount);
+ if (scumm_stricmp(md5str, exeInfo.instSet.md5)) {
+ warning("Unknown Apple IIGS instrument set (md5: %s) in %s, trying to use it nonetheless",
+ md5str, exePath.getPath().c_str());
+ }
+
+ // Read in the instrument set one instrument at a time
+ data->seek(exeInfo.instSetStart);
+
+ // Load the instruments
+ _instruments.clear();
+ _instruments.reserve(exeInfo.instSet.instCount);
+
+ IIgsInstrumentHeader instrument;
+ for (uint i = 0; i < exeInfo.instSet.instCount; i++) {
+ if (!instrument.read(*data)) {
+ warning("Error loading Apple IIGS instrument (%d. of %d) from %s, not loading more instruments",
+ i + 1, exeInfo.instSet.instCount, exePath.getPath().c_str());
+ break;
+ }
+ _instruments.push_back(instrument); // Add the successfully loaded instrument to the instruments array
+ }
+
+ // Loading was successful only if all instruments were loaded successfully
+ loadedOk = (_instruments.size() == exeInfo.instSet.instCount);
+ } else // Couldn't read enough data from the executable file
+ warning("Error loading instruments from Apple IIGS executable (%s)", exePath.getPath().c_str());
+
+ return loadedOk;
+}
+
+bool SoundGen2GS::loadWaveFile(const Common::FSNode &wavePath, const IIgsExeInfo &exeInfo) {
+ Common::File file;
+
+ // Open the wave file and read it into memory
+ file.open(wavePath);
+ Common::SharedPtr<Common::MemoryReadStream> uint8Wave(file.readStream(file.size()));
+ file.close();
+
+ // Check that we got the whole wave file
+ if (uint8Wave && uint8Wave->size() == SIERRASTANDARD_SIZE) {
+ // Check wave file's md5sum
+ char md5str[32+1];
+ Common::md5_file_string(*uint8Wave, md5str, SIERRASTANDARD_SIZE);
+ if (scumm_stricmp(md5str, exeInfo.instSet.waveFileMd5)) {
+ warning("Unknown Apple IIGS wave file (md5: %s, game: %s).\n" \
+ "Please report the information on the previous line to the ScummVM team.\n" \
+ "Using the wave file as it is - music may sound weird", md5str, exeInfo.exePrefix);
+ }
+
+ uint8Wave->seek(0); // Seek wave to its start
+ // Convert the wave file from 8-bit unsigned to 8-bit signed and save the result
+ _wave.resize(uint8Wave->size());
+ return convertWave(*uint8Wave, _wave.begin(), uint8Wave->size());
+ } else { // Couldn't read the wave file or it had incorrect size
+ warning("Error loading Apple IIGS wave file (%s), not loading instruments", wavePath.getPath().c_str());
+ return false;
+ }
+}
+
+} // End of namespace Agi
diff --git a/engines/agi/sound_2gs.h b/engines/agi/sound_2gs.h
new file mode 100644
index 0000000000..12dede0b69
--- /dev/null
+++ b/engines/agi/sound_2gs.h
@@ -0,0 +1,353 @@
+/* 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 AGI_SOUND_2GS_H
+#define AGI_SOUND_2GS_H
+
+#include "common/frac.h"
+#include "sound/audiostream.h"
+
+namespace Agi {
+
+#define BUFFER_SIZE 410
+
+// Apple IIGS MIDI uses 60 ticks per second (Based on tests with Apple IIGS
+// KQ1 and SQ1 under MESS 0.124a). So we make the audio buffer size to be a
+// 1/60th of a second in length. That should be getSampleRate() / 60 samples
+// in length but as getSampleRate() is always 22050 at the moment we just use
+// the hardcoded value of 368 (22050/60 = 367.5 which rounds up to 368).
+// FIXME: Use getSampleRate() / 60 rather than a hardcoded value
+#define IIGS_BUFFER_SIZE 368
+
+// MIDI command values (Shifted right by 4 so they're in the lower nibble)
+#define MIDI_CMD_NOTE_OFF 0x08
+#define MIDI_CMD_NOTE_ON 0x09
+#define MIDI_CMD_CONTROLLER 0x0B
+#define MIDI_CMD_PROGRAM_CHANGE 0x0C
+#define MIDI_CMD_PITCH_WHEEL 0x0E
+// Whole MIDI byte values (Command and channel info together)
+#define MIDI_BYTE_STOP_SEQUENCE 0xFC
+#define MIDI_BYTE_TIMER_SYNC 0xF8
+
+struct IIgsEnvelopeSegment {
+ uint8 bp;
+ uint16 inc; ///< 8b.8b fixed point, very probably little endian
+};
+
+#define ENVELOPE_SEGMENT_COUNT 8
+struct IIgsEnvelope {
+ IIgsEnvelopeSegment seg[ENVELOPE_SEGMENT_COUNT];
+
+ /** Reads an Apple IIGS envelope from then given stream. */
+ bool read(Common::SeekableReadStream &stream);
+};
+
+// 2**(1/12) i.e. the 12th root of 2
+#define SEMITONE 1.059463094359295
+
+// C6's frequency is A4's (440 Hz) frequency but one full octave and three semitones higher
+// i.e. C6_FREQ = 440 * pow(2.0, 15/12.0)
+#define C6_FREQ 1046.502261202395
+
+// Size of the SIERRASTANDARD file (i.e. the wave file i.e. the sample data used by the instruments).
+#define SIERRASTANDARD_SIZE 65536
+
+// Maximum number of instruments in an Apple IIGS instrument set.
+// Chosen empirically based on Apple IIGS AGI game data, increase if needed.
+#define MAX_INSTRUMENTS 28
+
+struct IIgsWaveInfo {
+ uint8 top;
+ uint addr;
+ uint size;
+// Oscillator channel
+#define OSC_CHANNEL_RIGHT 0
+#define OSC_CHANNEL_LEFT 1
+ uint channel;
+// Oscillator mode
+#define OSC_MODE_LOOP 0
+#define OSC_MODE_ONESHOT 1
+#define OSC_MODE_SYNC_AM 2
+#define OSC_MODE_SWAP 3
+ uint mode;
+ bool halt;
+ int16 relPitch; ///< Relative pitch in semitones (Signed 8b.8b fixed point)
+
+ /** Reads an Apple IIGS wave information structure from the given stream. */
+ bool read(Common::SeekableReadStream &stream, bool ignoreAddr = false);
+ bool finalize(Common::SeekableReadStream &uint8Wave);
+};
+
+// Number of waves per Apple IIGS sound oscillator
+#define WAVES_PER_OSCILLATOR 2
+
+/** An Apple IIGS sound oscillator. Consists always of two waves. */
+struct IIgsOscillator {
+ IIgsWaveInfo waves[WAVES_PER_OSCILLATOR];
+
+ bool finalize(Common::SeekableReadStream &uint8Wave);
+};
+
+// Maximum number of oscillators in an Apple IIGS instrument.
+// Chosen empirically based on Apple IIGS AGI game data, increase if needed.
+#define MAX_OSCILLATORS 4
+
+/** An Apple IIGS sound oscillator list. */
+struct IIgsOscillatorList {
+ uint count; ///< Oscillator count
+ IIgsOscillator osc[MAX_OSCILLATORS]; ///< The oscillators
+
+ /** Indexing operators for easier access to the oscillators. */
+ const IIgsOscillator &operator()(uint index) const { return osc[index]; }
+ IIgsOscillator &operator()(uint index) { return osc[index]; }
+
+ /** Reads an Apple IIGS oscillator list from the given stream. */
+ bool read(Common::SeekableReadStream &stream, uint oscillatorCount, bool ignoreAddr = false);
+ bool finalize(Common::SeekableReadStream &uint8Wave);
+};
+
+struct IIgsInstrumentHeader {
+ IIgsEnvelope env;
+ uint8 relseg;
+ uint8 bendrange;
+ uint8 vibdepth;
+ uint8 vibspeed;
+ IIgsOscillatorList oscList;
+
+ /**
+ * Read an Apple IIGS instrument header from the given stream.
+ * @param stream The source stream from which to read the data.
+ * @param ignoreAddr Should we ignore wave infos' wave address variable's value?
+ * @return True if successful, false otherwise.
+ */
+ bool read(Common::SeekableReadStream &stream, bool ignoreAddr = false);
+ bool finalize(Common::SeekableReadStream &uint8Wave);
+};
+
+struct IIgsSampleHeader {
+ uint16 type;
+ uint8 pitch; ///< Logarithmic, base is 2**(1/12), unknown multiplier (Possibly in range 1040-1080)
+ uint8 unknownByte_Ofs3; // 0x7F in Gold Rush's sound resource 60, 0 in all others.
+ uint8 volume; ///< Current guess: Logarithmic in 6 dB steps
+ uint8 unknownByte_Ofs5; ///< 0 in all tested samples.
+ uint16 instrumentSize; ///< Little endian. 44 in all tested samples. A guess.
+ uint16 sampleSize; ///< Little endian. Accurate in all tested samples excluding Manhunter I's sound resource 16.
+ IIgsInstrumentHeader instrument;
+
+ /**
+ * Read an Apple IIGS AGI sample header from the given stream.
+ * @param stream The source stream from which to read the data.
+ * @return True if successful, false otherwise.
+ */
+ bool read(Common::SeekableReadStream &stream);
+ bool finalize(Common::SeekableReadStream &uint8Wave);
+};
+
+struct IIgsChannelInfo {
+ const IIgsInstrumentHeader *ins; ///< Instrument info
+ const int8 *relocatedSample; ///< Source sample data (8-bit signed format) using relocation
+ const int8 *unrelocatedSample; ///< Source sample data (8-bit signed format) without relocation
+ frac_t pos; ///< Current sample position
+ frac_t posAdd; ///< Current sample position adder (Calculated using note, vibrato etc)
+ uint8 origNote; ///< The original note without the added relative pitch
+ frac_t note; ///< Note (With the added relative pitch)
+ frac_t vol; ///< Current volume (Takes both channel volume and enveloping into account)
+ frac_t chanVol; ///< Channel volume
+ frac_t startEnvVol; ///< Starting envelope volume
+ frac_t envVol; ///< Current envelope volume
+ uint envSeg; ///< Current envelope segment
+ uint size; ///< Sample size
+ bool loop; ///< Should we loop the sample?
+ bool end; ///< Has the playing ended?
+
+ void rewind(); ///< Rewinds the sound playing on this channel to its start
+ void setChannelVolume(uint8 volume); ///< Sets the channel volume
+ void setInstrument(const IIgsInstrumentHeader *instrument, const int8 *sample); ///< Sets the instrument to be used on this channel
+ void noteOn(uint8 noteParam, uint8 velocity); ///< Starts playing a note on this channel
+ void noteOff(uint8 velocity); ///< Releases the note on this channel
+ void stop(); ///< Stops the note playing on this channel instantly
+ bool playing(); ///< Is there a note playing on this channel?
+};
+
+class IIgsMidi : public AgiSound {
+public:
+ IIgsMidi(uint8 *data, uint32 len, int resnum, SoundMgr &manager);
+ ~IIgsMidi() { if (_data != NULL) free(_data); }
+ virtual uint16 type() { return _type; }
+ virtual const uint8 *getPtr() { return _ptr; }
+ virtual void setPtr(const uint8 *ptr) { _ptr = ptr; }
+ virtual void rewind() { _ptr = _data + 2; _midiTicks = _soundBufTicks = 0; }
+protected:
+ uint8 *_data; ///< Raw sound resource data
+ const uint8 *_ptr; ///< Pointer to the current position in the MIDI data
+ uint32 _len; ///< Length of the raw sound resource
+ uint16 _type; ///< Sound resource type
+public:
+ uint _midiTicks; ///< MIDI song position in ticks (1/60ths of a second)
+ uint _soundBufTicks; ///< Sound buffer position in ticks (1/60ths of a second)
+};
+
+class IIgsSample : public AgiSound {
+public:
+ IIgsSample(uint8 *data, uint32 len, int resnum, SoundMgr &manager);
+ ~IIgsSample() { delete[] _sample; }
+ virtual uint16 type() { return _header.type; }
+ const IIgsSampleHeader &getHeader() const { return _header; }
+ const int8 *getSample() const { return _sample; }
+protected:
+ IIgsSampleHeader _header; ///< Apple IIGS AGI sample header
+ int8 *_sample; ///< Sample data (8-bit signed format)
+};
+
+/** Apple IIGS MIDI program change to instrument number mapping. */
+struct MidiProgramChangeMapping {
+ byte midiProgToInst[44]; ///< Lookup table for the MIDI program number to instrument number mapping
+ byte undefinedInst; ///< The undefined instrument number
+
+ // Maps the MIDI program number to an instrument number
+ byte map(uint midiProg) const {
+ return midiProg < ARRAYSIZE(midiProgToInst) ? midiProgToInst[midiProg] : undefinedInst;
+ }
+};
+
+/** Apple IIGS AGI instrument set information. */
+struct InstrumentSetInfo {
+ uint byteCount; ///< Length of the whole instrument set in bytes
+ uint instCount; ///< Amount of instrument in the set
+ const char *md5; ///< MD5 hex digest of the whole instrument set
+ const char *waveFileMd5; ///< MD5 hex digest of the wave file (i.e. the sample data used by the instruments)
+ const MidiProgramChangeMapping &progToInst; ///< Program change to instrument number mapping
+};
+
+/** Apple IIGS AGI executable file information. */
+struct IIgsExeInfo {
+ enum AgiGameID gameid; ///< Game ID
+ const char *exePrefix; ///< Prefix of the Apple IIGS AGI executable (e.g. "SQ", "PQ", "KQ4" etc)
+ uint agiVer; ///< Apple IIGS AGI version number, not strictly needed
+ uint exeSize; ///< Size of the Apple IIGS AGI executable file in bytes
+ uint instSetStart; ///< Starting offset of the instrument set inside the executable file
+ const InstrumentSetInfo &instSet; ///< Information about the used instrument set
+};
+
+class IIgsMidiChannel {
+public:
+ IIgsMidiChannel() : _instrument(0), _sample(0), _volume(0) {}
+ uint activeSounds() const; ///< How many active sounds are playing?
+ void setInstrument(const IIgsInstrumentHeader *instrument, const int8 *sample);
+ void setVolume(uint8 volume);
+ void noteOff(uint8 note, uint8 velocity);
+ void noteOn(uint8 note, uint8 velocity);
+ void stopSounds(); ///< Clears the channel of any sounds
+ void removeStoppedSounds(); ///< Removes all stopped sounds from this MIDI channel
+public:
+ typedef Common::Array<IIgsChannelInfo>::const_iterator const_iterator;
+ typedef Common::Array<IIgsChannelInfo>::iterator iterator;
+ Common::Array<IIgsChannelInfo> _gsChannels; ///< Apple IIGS channels playing on this MIDI channel
+protected:
+ const IIgsInstrumentHeader *_instrument; ///< Instrument used on this MIDI channel
+ const int8 *_sample; ///< Sample data used on this MIDI channel
+ uint8 _volume; ///< MIDI controller number 7 (Volume)
+};
+
+class SoundGen2GS : public SoundGen, public Audio::AudioStream {
+public:
+ SoundGen2GS(AgiEngine *vm, Audio::Mixer *pMixer);
+ ~SoundGen2GS();
+
+ void play(int resnum);
+ void stop(void);
+
+ // AudioStream API
+ int readBuffer(int16 *buffer, const int numSamples);
+
+ bool isStereo() const {
+ return false;
+ }
+
+ bool endOfData() const {
+ return false;
+ }
+
+ int getRate() const {
+ // FIXME: Ideally, we should use _sampleRate.
+ return 22050;
+ }
+
+private:
+ bool _disabledMidi;
+ int _playingSound;
+ bool _playing;
+
+ int16 *_sndBuffer;
+
+/**
+ * Class for managing Apple IIGS sound channels.
+ * TODO: Check what instruments are used by default on the MIDI channels
+ * FIXME: Some instrument choices sound wrong
+ */
+private:
+ typedef Common::Array<IIgsMidiChannel>::const_iterator const_iterator;
+ typedef Common::Array<IIgsMidiChannel>::iterator iterator;
+ static const uint kSfxMidiChannel = 0; ///< The MIDI channel used for playing sound effects
+
+ bool loadInstruments();
+ const IIgsExeInfo *getIIgsExeInfo(enum AgiGameID gameid) const;
+
+ void setProgramChangeMapping(const MidiProgramChangeMapping *mapping);
+ bool loadInstrumentHeaders(const Common::FSNode &exePath, const IIgsExeInfo &exeInfo);
+ bool loadWaveFile(const Common::FSNode &wavePath, const IIgsExeInfo &exeInfo);
+
+ // Miscellaneous methods
+ void fillAudio(int16 *stream, uint len);
+ uint32 mixSound();
+ void playSound();
+ uint activeSounds() const; ///< How many active sounds are playing?
+ void stopSounds(); ///< Stops all sounds
+ void removeStoppedSounds(); ///< Removes all stopped sounds from the MIDI channels
+
+ // For playing Apple IIGS AGI samples (Sound effects etc)
+ bool playSampleSound(const IIgsSampleHeader &sampleHeader, const int8 *sample);
+ void playMidiSound();
+ void playSampleSound();
+
+ // MIDI commands
+ void midiNoteOff(uint8 channel, uint8 note, uint8 velocity);
+ void midiNoteOn(uint8 channel, uint8 note, uint8 velocity);
+ void midiController(uint8 channel, uint8 controller, uint8 value);
+ void midiProgramChange(uint8 channel, uint8 program);
+ void midiPitchWheel(uint8 wheelPos);
+ //protected:
+ const IIgsInstrumentHeader* getInstrument(uint8 program) const;
+ //public:
+ Common::Array<IIgsMidiChannel> _midiChannels; ///< Information about each MIDI channel
+ //protected:
+ Common::Array<int8> _wave; ///< Sample data used by the Apple IIGS MIDI instruments
+ const MidiProgramChangeMapping *_midiProgToInst; ///< MIDI program change to instrument number mapping
+ Common::Array<IIgsInstrumentHeader> _instruments; ///< Instruments used by the Apple IIGS AGI
+};
+
+} // End of namespace Agi
+
+#endif /* AGI_SOUND_2GS_H */
diff --git a/engines/agi/sound_coco3.cpp b/engines/agi/sound_coco3.cpp
new file mode 100644
index 0000000000..f054be0682
--- /dev/null
+++ b/engines/agi/sound_coco3.cpp
@@ -0,0 +1,80 @@
+/* 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 "agi/agi.h"
+
+#include "agi/sound_coco3.h"
+
+namespace Agi {
+
+static int cocoFrequencies[] = {
+ 130, 138, 146, 155, 164, 174, 184, 195, 207, 220, 233, 246,
+ 261, 277, 293, 311, 329, 349, 369, 391, 415, 440, 466, 493,
+ 523, 554, 587, 622, 659, 698, 739, 783, 830, 880, 932, 987,
+ 1046, 1108, 1174, 1244, 1318, 1396, 1479, 1567, 1661, 1760, 1864, 1975,
+ 2093, 2217, 2349, 2489, 2637, 2793, 2959, 3135, 3322, 3520, 3729, 3951
+};
+
+SoundGenCoCo3::SoundGenCoCo3(AgiEngine *vm, Audio::Mixer *pMixer) : SoundGen(vm, pMixer) {
+}
+
+SoundGenCoCo3::~SoundGenCoCo3() {
+}
+
+void SoundGenCoCo3::play(int resnum) {
+ int i = cocoFrequencies[0]; // Silence warning
+
+ i = i + 1;
+
+#if 0
+ int i = 0;
+ CoCoNote note;
+
+ do {
+ note.read(_chn[i].ptr);
+
+ if (note.freq != 0xff) {
+ playNote(0, cocoFrequencies[note.freq], note.volume);
+
+ uint32 start_time = _vm->_system->getMillis();
+
+ while (_vm->_system->getMillis() < start_time + note.duration) {
+ _vm->_system->updateScreen();
+
+ _vm->_system->delayMillis(10);
+ }
+ }
+ } while (note.freq != 0xff);
+#endif
+}
+
+void SoundGenCoCo3::stop() {
+}
+
+int SoundGenCoCo3::readBuffer(int16 *buffer, const int numSamples) {
+ return numSamples;
+}
+
+} // End of namespace Agi
diff --git a/engines/agi/sound_coco3.h b/engines/agi/sound_coco3.h
new file mode 100644
index 0000000000..b60f1937cd
--- /dev/null
+++ b/engines/agi/sound_coco3.h
@@ -0,0 +1,73 @@
+/* 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 AGI_SOUND_COCO3_H
+#define AGI_SOUND_COCO3_H
+
+#include "sound/audiostream.h"
+
+namespace Agi {
+
+struct CoCoNote {
+ uint8 freq;
+ uint8 volume;
+ uint16 duration; ///< Note duration
+
+ /** Reads a CoCoNote through the given pointer. */
+ void read(const uint8 *ptr) {
+ freq = *ptr;
+ volume = *(ptr + 1);
+ duration = READ_LE_UINT16(ptr + 2);
+ }
+};
+
+class SoundGenCoCo3 : public SoundGen, public Audio::AudioStream {
+public:
+ SoundGenCoCo3(AgiEngine *vm, Audio::Mixer *pMixer);
+ ~SoundGenCoCo3();
+
+ void play(int resnum);
+ void stop(void);
+
+ // AudioStream API
+ int readBuffer(int16 *buffer, const int numSamples);
+
+ bool isStereo() const {
+ return false;
+ }
+
+ bool endOfData() const {
+ return false;
+ }
+
+ int getRate() const {
+ // FIXME: Ideally, we should use _sampleRate.
+ return 22050;
+ }
+};
+
+} // End of namespace Agi
+
+#endif /* AGI_SOUND_COCO3_H */
diff --git a/engines/agi/sound_midi.cpp b/engines/agi/sound_midi.cpp
new file mode 100644
index 0000000000..57c5d54b27
--- /dev/null
+++ b/engines/agi/sound_midi.cpp
@@ -0,0 +1,345 @@
+/* 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$
+ *
+ */
+
+// Code is based on:
+//
+// A very simple program, that converts an AGI-song into a MIDI-song.
+// Feel free to use it for anything.
+//
+// The default instrument is "piano" for all the channels, what gives
+// good results on most games. But I found, that some songs are interesting
+// with other instruments. If you want to experiment, modify the "instr"
+// array.
+//
+// Timing is not perfect, yet. It plays correct, when I use the
+// Gravis-Midiplayer, but the songs are too fast when I use playmidi on
+// Linux.
+//
+// Original program developed by Jens. Christian Restemeier
+//
+
+// MIDI and digital music class
+
+#include "sound/audiostream.h"
+#include "sound/mididrv.h"
+#include "sound/midiparser.h"
+#include "common/config-manager.h"
+#include "common/file.h"
+#include "common/stream.h"
+
+#include "agi/agi.h"
+
+#include "agi/sound.h"
+#include "agi/sound_midi.h"
+
+#define SPEED_FACTOR 6
+
+namespace Agi {
+
+static uint32 convertSND2MIDI(byte *snddata, byte **data);
+
+MIDISound::MIDISound(uint8 *data, uint32 len, int resnum, SoundMgr &manager) : AgiSound(manager) {
+ _data = data; // Save the resource pointer
+ _len = len; // Save the resource's length
+ _type = READ_LE_UINT16(data); // Read sound resource's type
+ _isValid = (_type == AGI_SOUND_4CHN) && (_data != NULL) && (_len >= 2);
+
+ if (!_isValid) // Check for errors
+ warning("Error creating MIDI sound from resource %d (Type %d, length %d)", resnum, _type, len);
+}
+
+SoundGenMIDI::SoundGenMIDI(AgiEngine *vm, Audio::Mixer *pMixer) : SoundGen(vm, pMixer), _parser(0), _isPlaying(false), _passThrough(false), _isGM(false) {
+ DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB);
+ _driver = MidiDriver::createMidi(dev);
+
+ memset(_channel, 0, sizeof(_channel));
+ memset(_channelVolume, 255, sizeof(_channelVolume));
+ _masterVolume = 0;
+ this->open();
+ _smfParser = MidiParser::createParser_SMF();
+ _midiMusicData = NULL;
+}
+
+SoundGenMIDI::~SoundGenMIDI() {
+ _driver->setTimerCallback(NULL, NULL);
+ stop();
+ this->close();
+ _smfParser->setMidiDriver(NULL);
+ delete _smfParser;
+ delete[] _midiMusicData;
+}
+
+void SoundGenMIDI::setChannelVolume(int channel) {
+ int newVolume = _channelVolume[channel] * _masterVolume / 255;
+ _channel[channel]->volume(newVolume);
+}
+
+void SoundGenMIDI::setVolume(int volume) {
+ Common::StackLock lock(_mutex);
+
+ volume = CLIP(volume, 0, 255);
+ if (_masterVolume == volume)
+ return;
+ _masterVolume = volume;
+
+ for (int i = 0; i < 16; ++i) {
+ if (_channel[i]) {
+ setChannelVolume(i);
+ }
+ }
+}
+
+int SoundGenMIDI::open() {
+ // Don't ever call open without first setting the output driver!
+ if (!_driver)
+ return 255;
+
+ int ret = _driver->open();
+ if (ret)
+ return ret;
+
+ _driver->setTimerCallback(this, &onTimer);
+ return 0;
+}
+
+void SoundGenMIDI::close() {
+ stop();
+ if (_driver)
+ _driver->close();
+ _driver = 0;
+}
+
+void SoundGenMIDI::send(uint32 b) {
+ if (_passThrough) {
+ _driver->send(b);
+ return;
+ }
+
+ byte channel = (byte)(b & 0x0F);
+ if ((b & 0xFFF0) == 0x07B0) {
+ // Adjust volume changes by master volume
+ byte volume = (byte)((b >> 16) & 0x7F);
+ _channelVolume[channel] = volume;
+ volume = volume * _masterVolume / 255;
+ b = (b & 0xFF00FFFF) | (volume << 16);
+ } else if ((b & 0xF0) == 0xC0 && !_isGM && !_nativeMT32) {
+ b = (b & 0xFFFF00FF) | MidiDriver::_mt32ToGm[(b >> 8) & 0xFF] << 8;
+ }
+ else if ((b & 0xFFF0) == 0x007BB0) {
+ //Only respond to All Notes Off if this channel
+ //has currently been allocated
+ if (_channel[b & 0x0F])
+ return;
+ }
+
+ if (!_channel[channel]) {
+ _channel[channel] = (channel == 9) ? _driver->getPercussionChannel() : _driver->allocateChannel();
+ // If a new channel is allocated during the playback, make sure
+ // its volume is correctly initialized.
+ if (_channel[channel])
+ setChannelVolume(channel);
+ }
+
+ if (_channel[channel])
+ _channel[channel]->send(b);
+}
+
+void SoundGenMIDI::metaEvent(byte type, byte *data, uint16 length) {
+
+ switch (type) {
+ case 0x2F: // End of Track
+ stop();
+ _vm->_sound->soundIsFinished();
+ break;
+ default:
+ //warning("Unhandled meta event: %02x", type);
+ break;
+ }
+}
+
+void SoundGenMIDI::onTimer(void *refCon) {
+ SoundGenMIDI *music = (SoundGenMIDI *)refCon;
+ Common::StackLock lock(music->_mutex);
+
+ if (music->_parser)
+ music->_parser->onTimer();
+}
+
+void SoundGenMIDI::play(int resnum) {
+ MIDISound *track;
+
+ stop();
+
+ _isGM = true;
+
+ track = (MIDISound *)_vm->_game.sounds[resnum];
+
+ // Convert AGI Sound data to MIDI
+ int midiMusicSize = convertSND2MIDI(track->_data, &_midiMusicData);
+
+ if (_smfParser->loadMusic(_midiMusicData, midiMusicSize)) {
+ MidiParser *parser = _smfParser;
+ parser->setTrack(0);
+ parser->setMidiDriver(this);
+ parser->setTimerRate(getBaseTempo());
+ parser->property(MidiParser::mpCenterPitchWheelOnUnload, 1);
+
+ _parser = parser;
+
+ syncVolume();
+
+ _isPlaying = true;
+ }
+}
+
+void SoundGenMIDI::stop() {
+ Common::StackLock lock(_mutex);
+
+ if (!_isPlaying)
+ return;
+
+ _isPlaying = false;
+ if (_parser) {
+ _parser->unloadMusic();
+ _parser = NULL;
+ }
+}
+
+void SoundGenMIDI::pause() {
+ setVolume(-1);
+ _isPlaying = false;
+}
+
+void SoundGenMIDI::resume() {
+ syncVolume();
+ _isPlaying = true;
+}
+
+void SoundGenMIDI::syncVolume() {
+ int volume = ConfMan.getInt("music_volume");
+ if (ConfMan.getBool("mute")) {
+ volume = -1;
+ }
+ setVolume(volume);
+}
+
+/* channel / intrument setup: */
+
+/* most songs are good with this: */
+unsigned char instr[] = {0, 0, 0};
+
+/* cool for sq2:
+unsigned char instr[] = {50, 51, 19};
+*/
+
+static void writeDelta(Common::MemoryWriteStreamDynamic *st, int32 delta) {
+ int32 i;
+
+ i = delta >> 21; if (i > 0) st->writeByte((i & 127) | 128);
+ i = delta >> 14; if (i > 0) st->writeByte((i & 127) | 128);
+ i = delta >> 7; if (i > 0) st->writeByte((i & 127) | 128);
+ st->writeByte(delta & 127);
+}
+
+static uint32 convertSND2MIDI(byte *snddata, byte **data) {
+ int32 lp, ep;
+ int n;
+ double ll;
+
+ Common::MemoryWriteStreamDynamic st;
+
+ ll = log10(pow(2.0, 1.0 / 12.0));
+
+ /* Header */
+ st.write("MThd", 4);
+ st.writeUint32BE(6);
+ st.writeUint16BE(1); /* mode */
+ st.writeUint16BE(3); /* number of tracks */
+ st.writeUint16BE(192); /* ticks / quarter */
+
+ for (n = 0; n < 3; n++) {
+ uint16 start, end, pos;
+
+ st.write("MTrk", 4);
+ lp = st.pos();
+ st.writeUint32BE(0); /* chunklength */
+ writeDelta(&st, 0); /* set instrument */
+ st.writeByte(0xc0 + n);
+ st.writeByte(instr[n]);
+ start = snddata[n * 2 + 0] | (snddata[n * 2 + 1] << 8);
+ end = ((snddata[n * 2 + 2] | (snddata[n * 2 + 3] << 8))) - 5;
+
+ for (pos = start; pos < end; pos += 5) {
+ uint16 freq, dur;
+ dur = (snddata[pos + 0] | (snddata[pos + 1] << 8)) * SPEED_FACTOR;
+ freq = ((snddata[pos + 2] & 0x3F) << 4) + (snddata[pos + 3] & 0x0F);
+ if (snddata[pos + 2] > 0) {
+ double fr;
+ int note;
+ /* I don't know, what frequency equals midi note 0 ... */
+ /* This moves the song 4 octaves down: */
+ fr = (log10(111860.0 / (double)freq) / ll) - 48;
+ note = (int)floor(fr + 0.5);
+ if (note < 0) note = 0;
+ if (note > 127) note = 127;
+ /* note on */
+ writeDelta(&st, 0);
+ st.writeByte(144 + n);
+ st.writeByte(note);
+ st.writeByte(100);
+ /* note off */
+ writeDelta(&st, dur);
+ st.writeByte(128 + n);
+ st.writeByte(note);
+ st.writeByte(0);
+ } else {
+ /* note on */
+ writeDelta(&st, 0);
+ st.writeByte(144 + n);
+ st.writeByte(0);
+ st.writeByte(0);
+ /* note off */
+ writeDelta(&st, dur);
+ st.writeByte(128 + n);
+ st.writeByte(0);
+ st.writeByte(0);
+ }
+ }
+ writeDelta(&st, 0);
+ st.writeByte(0xff);
+ st.writeByte(0x2f);
+ st.writeByte(0x0);
+ ep = st.pos();
+ st.seek(lp, SEEK_SET);
+ st.writeUint32BE((ep - lp) - 4);
+ st.seek(ep, SEEK_SET);
+ }
+
+ *data = st.getData();
+
+ return st.pos();
+}
+
+} // End of namespace Agi
diff --git a/engines/agi/sound_midi.h b/engines/agi/sound_midi.h
new file mode 100644
index 0000000000..26b75e0d70
--- /dev/null
+++ b/engines/agi/sound_midi.h
@@ -0,0 +1,114 @@
+/* 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$
+ *
+ */
+
+// Music class
+
+#ifndef AGI_SOUND_MIDI_H
+#define AGI_SOUND_MIDI_H
+
+#include "sound/mididrv.h"
+#include "sound/midiparser.h"
+#include "common/mutex.h"
+
+namespace Agi {
+
+class MIDISound : public AgiSound {
+public:
+ MIDISound(uint8 *data, uint32 len, int resnum, SoundMgr &manager);
+ ~MIDISound() { free(_data); }
+ virtual uint16 type() { return _type; }
+ uint8 *_data; ///< Raw sound resource data
+ uint32 _len; ///< Length of the raw sound resource
+
+protected:
+ uint16 _type; ///< Sound resource type
+};
+
+class SoundGenMIDI : public SoundGen, public MidiDriver {
+public:
+ SoundGenMIDI(AgiEngine *vm, Audio::Mixer *pMixer);
+ ~SoundGenMIDI();
+
+ void play(int resnum);
+ void stop();
+
+ bool isPlaying() { return _isPlaying; }
+ void setPlaying(bool playing) { _isPlaying = playing; }
+
+ void setVolume(int volume);
+ int getVolume() { return _masterVolume; }
+ void syncVolume();
+
+ void setNativeMT32(bool b) { _nativeMT32 = b; }
+ bool hasNativeMT32() { return _nativeMT32; }
+ void pause();
+ void resume();
+ void setLoop(bool loop) { _looping = loop; }
+ void setPassThrough(bool b) { _passThrough = b; }
+
+ void setGM(bool isGM) { _isGM = isGM; }
+
+ // MidiDriver interface implementation
+ int open();
+ void close();
+ void send(uint32 b);
+
+ void metaEvent(byte type, byte *data, uint16 length);
+
+ void setTimerCallback(void *timerParam, void (*timerProc)(void *)) { }
+ uint32 getBaseTempo() { return _driver ? _driver->getBaseTempo() : 0; }
+
+ // Channel allocation functions
+ MidiChannel *allocateChannel() { return 0; }
+ MidiChannel *getPercussionChannel() { return 0; }
+
+ MidiParser *_parser;
+ Common::Mutex _mutex;
+
+private:
+
+ static void onTimer(void *data);
+ void setChannelVolume(int channel);
+
+ MidiChannel *_channel[16];
+ MidiDriver *_driver;
+ MidiParser *_smfParser;
+ byte _channelVolume[16];
+ bool _nativeMT32;
+ bool _isGM;
+ bool _passThrough;
+
+ bool _isPlaying;
+ bool _looping;
+ byte _masterVolume;
+
+ byte *_midiMusicData;
+
+ SoundMgr *_manager;
+};
+
+} // End of namespace Agi
+
+#endif
diff --git a/engines/agi/sound_pcjr.cpp b/engines/agi/sound_pcjr.cpp
new file mode 100644
index 0000000000..b9d701d7f7
--- /dev/null
+++ b/engines/agi/sound_pcjr.cpp
@@ -0,0 +1,512 @@
+/* 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$
+ *
+ */
+
+/* Heavily based on code from NAGI
+ *
+ * COPYRIGHT AND PERMISSION NOTICE
+ *
+ * Copyright (c) 2001, 2001, 2002 Nick Sonneveld
+ *
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, and/or sell copies of the Software, and to permit persons
+ * to whom the Software is furnished to do so, provided that the above
+ * copyright notice(s) and this permission notice appear in all copies of
+ * the Software and that both the above copyright notice(s) and this
+ * permission notice appear in supporting documentation.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+ * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL
+ * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING
+ * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Except as contained in this notice, the name of a copyright holder
+ * shall not be used in advertising or otherwise to promote the sale, use
+ * or other dealings in this Software without prior written authorization
+ *
+ */
+
+#include "agi/agi.h"
+#include "agi/sound.h"
+#include "agi/sound_pcjr.h"
+
+namespace Agi {
+
+// "fade out" or possibly "dissolve"
+// v2.9xx
+const int8 dissolveDataV2[] = {
+ -2, -3, -2, -1,
+ 0x00, 0x00,
+ 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x04, 0x04, 0x04, 0x04,
+ 0x05, 0x05, 0x05, 0x05,
+ 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x07, 0x07, 0x07, 0x07,
+ 0x08, 0x08, 0x08, 0x08,
+ 0x09, 0x09, 0x09, 0x09,
+ 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B,
+ 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
+ 0x0D,
+ -100
+};
+
+// v3
+const int8 dissolveDataV3[] = {
+ -2, -3, -2, -1,
+ 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x07, 0x07, 0x07, 0x07,
+ 0x08, 0x08, 0x08, 0x08,
+ 0x09, 0x09, 0x09, 0x09,
+ 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B,
+ 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
+ 0x0D,
+ -100
+};
+
+
+SoundGenPCJr::SoundGenPCJr(AgiEngine *vm, Audio::Mixer *pMixer) : SoundGen(vm, pMixer) {
+ _chanAllocated = 10240; // preallocate something which will most likely fit
+ _chanData = (int16 *)malloc(_chanAllocated << 1);
+
+ // Pick dissolve method
+ //
+ // 0 = no dissolve.. just play for as long as it's meant to be played
+ // this was used in older v2.4 and under games i THINK
+ // 1 = not used
+ // 2 = v2.9+ games used a shorter dissolve
+ // 3 (default) = v3 games used this dissolve pattern.. slightly longer
+ if (_vm->getVersion() >= 0x3000)
+ _dissolveMethod = 3;
+ else if (_vm->getVersion() >= 0x2900)
+ _dissolveMethod = 2;
+ else
+ _dissolveMethod = 0;
+
+ _dissolveMethod = 3;
+
+ _mixer->playStream(Audio::Mixer::kMusicSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
+}
+
+SoundGenPCJr::~SoundGenPCJr() {
+ free(_chanData);
+
+ _mixer->stopHandle(_soundHandle);
+}
+
+void SoundGenPCJr::play(int resnum) {
+ PCjrSound *pcjrSound = (PCjrSound *)_vm->_game.sounds[resnum];
+
+ for (int i = 0; i < CHAN_MAX; i++) {
+ _channel[i].data = pcjrSound->getVoicePointer(i % 4);
+ _channel[i].duration = 0;
+ _channel[i].avail = 0xffff;
+ _channel[i].dissolveCount = 0xFFFF;
+ _channel[i].attenuation = 0;
+ _channel[i].attenuationCopy = 0;
+
+ _tchannel[i].avail = 1;
+ _tchannel[i].noteCount = 0;
+ _tchannel[i].freqCount = 250;
+ _tchannel[i].freqCountPrev = -1;
+ _tchannel[i].atten = 0xF; // silence
+ _tchannel[i].genType = kGenTone;
+ _tchannel[i].genTypePrev = -1;
+ }
+}
+
+void SoundGenPCJr::stop(void) {
+ int i;
+
+ for (i = 0; i < CHAN_MAX ; i++) {
+ _channel[i].avail = 0;
+ _tchannel[i].avail = 0;
+ }
+}
+
+int SoundGenPCJr::volumeCalc(SndGenChan *chan) {
+ int8 attenuation, dissolveValue;
+
+ const int8 *dissolveData;
+
+ switch (_dissolveMethod) {
+ case 2:
+ dissolveData = dissolveDataV2;
+ break;
+ case 3:
+ default:
+ dissolveData = dissolveDataV3;
+ break;
+ }
+
+ assert(chan);
+
+ attenuation = chan->attenuation;
+ if (attenuation != 0x0F) { // != silence
+ if (chan->dissolveCount != 0xFFFF) {
+ dissolveValue = dissolveData[chan->dissolveCount];
+ if (dissolveValue == -100) { // if at end of list
+ chan->dissolveCount = 0xFFFF;
+ chan->attenuation = chan->attenuationCopy;
+ attenuation = chan->attenuation;
+ } else {
+ chan->dissolveCount++;
+
+ attenuation += dissolveValue;
+ if (attenuation < 0)
+ attenuation = 0;
+ if (attenuation > 0x0F)
+ attenuation = 0x0F;
+
+ chan->attenuationCopy = attenuation;
+
+ attenuation &= 0x0F;
+ attenuation += _vm->getvar(vVolume);
+ if (attenuation > 0x0F)
+ attenuation = 0x0F;
+ }
+ }
+ //if (computer_type == 2) && (attenuation < 8)
+ if (attenuation < 8)
+ attenuation += 2;
+ }
+
+ return attenuation;
+}
+
+// read the next channel data.. fill it in *tone
+// if tone isn't touched.. it should be inited so it just plays silence
+// return 0 if it's passing more data
+// return -1 if it's passing nothing (end of data)
+int SoundGenPCJr::getNextNote(int ch, Tone *tone) {
+ SndGenChan *chan;
+ const byte *data;
+
+ assert(tone);
+ assert(ch < CHAN_MAX);
+
+ if (!_vm->getflag(fSoundOn))
+ return -1;
+
+ chan = &_channel[ch];
+ if (!chan->avail)
+ return -1;
+
+ while ((chan->duration == 0) && (chan->duration != 0xFFFF)) {
+ data = chan->data;
+
+ // read the duration of the note
+ chan->duration = READ_LE_UINT16(data); // duration
+
+ // if it's 0 then it's not going to be played
+ // if it's 0xFFFF then the channel data has finished.
+ if ((chan->duration != 0) && (chan->duration != 0xFFFF)) {
+ // only tone channels dissolve
+ if ((ch != 3) && (_dissolveMethod != 0)) // != noise??
+ chan->dissolveCount = 0;
+
+ // attenuation (volume)
+ chan->attenuation = data[4] & 0xF;
+
+ // frequency
+ if (ch < (CHAN_MAX - 1)) {
+ chan->freqCount = (uint16)data[2] & 0x3F;
+ chan->freqCount <<= 4;
+ chan->freqCount |= data[3] & 0x0F;
+
+ chan->genType = kGenTone;
+ } else {
+ int noiseFreq;
+
+ // check for white noise (1) or periodic (0)
+ chan->genType = (data[3] & 0x04) ? kGenWhite : kGenPeriod;
+
+ noiseFreq = data[3] & 0x03;
+
+ switch (noiseFreq) {
+ case 0:
+ chan->freqCount = 32;
+ break;
+ case 1:
+ chan->freqCount = 64;
+ break;
+ case 2:
+ chan->freqCount = 128;
+ break;
+ case 3:
+ chan->freqCount = _channel[2].freqCount * 2;
+ break;
+ }
+ }
+ }
+ // data now points to the next data seg-a-ment
+ chan->data += 5;
+ }
+
+ if (chan->duration != 0xFFFF) {
+ tone->freqCount = chan->freqCount;
+ tone->atten = volumeCalc(chan); // calc volume, sent vol is different from saved vol
+ tone->type = chan->genType;
+ chan->duration--;
+ } else {
+ // kill channel
+ chan->avail = 0;
+ chan->attenuation = 0x0F; // silent
+ chan->attenuationCopy = 0x0F; // dunno really
+
+ return -1;
+ }
+
+ return 0;
+}
+
+// Formulas for noise generator
+// bit0 = output
+
+// noise feedback for white noise mode
+#define FB_WNOISE 0x12000 // bit15.d(16bits) = bit0(out) ^ bit2
+//#define FB_WNOISE 0x14000 // bit15.d(16bits) = bit0(out) ^ bit1
+//#define FB_WNOISE 0x28000 // bit16.d(17bits) = bit0(out) ^ bit2 (same to AY-3-8910)
+//#define FB_WNOISE 0x50000 // bit17.d(18bits) = bit0(out) ^ bit2
+
+// noise feedback for periodic noise mode
+// it is correct maybe (it was in the Megadrive sound manual)
+//#define FB_PNOISE 0x10000 // 16bit rorate
+#define FB_PNOISE 0x08000
+
+// noise generator start preset (for periodic noise)
+#define NG_PRESET 0x0f35
+
+//#define WAVE_HEIGHT (0x7FFF)
+
+// Volume table.
+//
+// 2dB = 20*log(a/b)
+// 10^(2/20)*b = a;
+// value = 0x7fff;
+// value /= 1.258925411794;
+const int16 volTable[16] = {
+ 32767, 26027, 20674, 16422, 13044, 10361, 8230, 6537, 5193, 4125, 3276, 2602, 2067, 1642, 1304, 0
+};
+
+#define FREQ_DIV 111844
+#define MULT FREQ_DIV
+
+// fill buff
+int SoundGenPCJr::chanGen(int chan, int16 *stream, int len) {
+ ToneChan *tpcm;
+ Tone toneNew;
+ int fillSize;
+ int retVal;
+
+ tpcm = &_tchannel[chan];
+
+ retVal = -1;
+
+ while (len > 0) {
+ if (tpcm->noteCount <= 0) {
+ // get new tone data
+ toneNew.freqCount = 0;
+ toneNew.atten = 0xF;
+ toneNew.type = kGenTone;
+ if ((tpcm->avail) && (getNextNote(chan, &toneNew) == 0)) {
+ tpcm->atten = toneNew.atten;
+ tpcm->freqCount = toneNew.freqCount;
+ tpcm->genType = toneNew.type;
+
+ // setup counters 'n stuff
+ // SAMPLE_RATE samples per sec.. tone changes 60 times per sec
+ tpcm->noteCount = SAMPLE_RATE / 60;
+ retVal = 0;
+ } else {
+ // if it doesn't return an
+ tpcm->genType = kGenSilence;
+ tpcm->noteCount = len;
+ tpcm->avail = 0;
+ }
+ }
+
+ // write nothing
+ if ((tpcm->freqCount == 0) || (tpcm->atten == 0xf)) {
+ tpcm->genType = kGenSilence;
+ }
+
+ // find which is smaller.. the buffer or the
+ fillSize = (tpcm->noteCount <= len) ? tpcm->noteCount : len;
+
+ switch (tpcm->genType) {
+ case kGenTone:
+ fillSize = fillSquare(tpcm, stream, fillSize);
+ break;
+ case kGenPeriod:
+ case kGenWhite:
+ fillSize = fillNoise(tpcm, stream, fillSize);
+ break;
+ case kGenSilence:
+ default:
+ // fill with whitespace
+ memset(stream, 0, fillSize * sizeof(int16));
+ break;
+ }
+
+ tpcm->noteCount -= fillSize;
+ stream += fillSize;
+ len -= fillSize;
+ }
+
+ return retVal;
+}
+
+int SoundGenPCJr::fillSquare(ToneChan *t, int16 *buf, int len) {
+ int count;
+
+ if (t->genType != t->genTypePrev) {
+ // make sure the freqCount is checked
+ t->freqCountPrev = -1;
+ t->sign = 1;
+ t->genTypePrev = t->genType;
+ }
+
+ if (t->freqCount != t->freqCountPrev) {
+ //t->scale = (int)( (double)t->samp->freq*t->freqCount/FREQ_DIV * MULT + 0.5);
+ t->scale = (SAMPLE_RATE / 2) * t->freqCount;
+ t->count = t->scale;
+ t->freqCountPrev = t->freqCount;
+ }
+
+ count = len;
+
+ while (count > 0) {
+ *(buf++) = t->sign ? volTable[t->atten] : -volTable[t->atten];
+ count--;
+
+ // get next sample
+ t->count -= MULT;
+ while (t->count <= 0) {
+ t->sign ^= 1;
+ t->count += t->scale;
+ }
+ }
+
+ return len;
+}
+
+int SoundGenPCJr::fillNoise(ToneChan *t, int16 *buf, int len) {
+ int count;
+
+ if (t->genType != t->genTypePrev) {
+ // make sure the freqCount is checked
+ t->freqCountPrev = -1;
+ t->genTypePrev = t->genType;
+ }
+
+ if (t->freqCount != t->freqCountPrev) {
+ //t->scale = (int)( (double)t->samp->freq*t->freqCount/FREQ_DIV * MULT + 0.5);
+ t->scale = (SAMPLE_RATE / 2) * t->freqCount;
+ t->count = t->scale;
+ t->freqCountPrev = t->freqCount;
+
+ t->feedback = (t->genType == kGenWhite) ? FB_WNOISE : FB_PNOISE;
+ // reset noise shifter
+ t->noiseState = NG_PRESET;
+ t->sign = t->noiseState & 1;
+ }
+
+ count = len;
+
+ while (count > 0) {
+ *(buf++) = t->sign ? volTable[t->atten] : -volTable[t->atten];
+ count--;
+
+ // get next sample
+ t->count -= MULT;
+ while (t->count <= 0) {
+ if (t->noiseState & 1)
+ t->noiseState ^= t->feedback;
+
+ t->noiseState >>= 1;
+ t->sign = t->noiseState & 1;
+ t->count += t->scale;
+ }
+ }
+
+ return len;
+}
+
+int SoundGenPCJr::readBuffer(int16 *stream, const int len) {
+ int streamCount;
+ int16 *sPtr, *cPtr;
+
+ if (_chanAllocated < len) {
+ free(_chanData);
+ _chanData = (int16 *)malloc(len << 1);
+ _chanAllocated = len;
+ }
+ memset(stream, 0, len << 1);
+
+ assert(stream);
+
+ bool finished = true;
+
+ for (int i = 0; i < CHAN_MAX; i++) {
+ // get channel data(chan.userdata)
+ if (chanGen(i, _chanData, len) == 0) {
+ // divide by number of channels then add to stream
+ streamCount = len;
+ sPtr = stream;
+ cPtr = _chanData;
+
+ while (streamCount--)
+ *(sPtr++) += *(cPtr++) / CHAN_MAX;
+
+ finished = false;
+ }
+ }
+
+ if (finished)
+ _vm->_sound->soundIsFinished();
+
+ return len;
+}
+
+} // End of namespace Agi
diff --git a/engines/agi/sound_pcjr.h b/engines/agi/sound_pcjr.h
new file mode 100644
index 0000000000..fe0e762f4e
--- /dev/null
+++ b/engines/agi/sound_pcjr.h
@@ -0,0 +1,127 @@
+/* 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 AGI_SOUND_PCJR_H
+#define AGI_SOUND_PCJR_H
+
+#include "sound/audiostream.h"
+
+namespace Agi {
+
+#define CHAN_MAX 4
+
+#define SAMPLE_RATE 22050
+
+enum GenType {
+ kGenSilence,
+ kGenTone,
+ kGenPeriod,
+ kGenWhite
+};
+
+struct SndGenChan {
+ const byte *data;
+ uint16 duration;
+ uint16 avail; // turned on (1) but when the channel's data runs out, it's set to (0)
+ uint16 dissolveCount;
+ byte attenuation;
+ byte attenuationCopy;
+
+ GenType genType;
+
+ // for the sample mixer
+ int freqCount;
+};
+
+struct ToneChan {
+ int avail;
+
+ int noteCount; // length of tone.. duration
+
+ int freqCount;
+ int freqCountPrev;
+ int atten; // volume
+
+ GenType genType;
+ int genTypePrev;
+
+ int count;
+ int scale;
+ int sign;
+ unsigned int noiseState; /* noise generator */
+ int feedback; /* noise feedback mask */
+};
+
+struct Tone {
+ int freqCount;
+ int atten;
+ GenType type;
+};
+
+class SoundGenPCJr : public SoundGen, public Audio::AudioStream {
+public:
+ SoundGenPCJr(AgiEngine *vm, Audio::Mixer *pMixer);
+ ~SoundGenPCJr();
+
+ void play(int resnum);
+ void stop(void);
+
+ // AudioStream API
+ int readBuffer(int16 *buffer, const int numSamples);
+
+ bool isStereo() const {
+ return false;
+ }
+
+ bool endOfData() const {
+ return false;
+ }
+
+ int getRate() const {
+ // FIXME: Ideally, we should use _sampleRate.
+ return 22050;
+ }
+
+private:
+ int getNextNote(int ch, Tone *tone);
+ int volumeCalc(SndGenChan *chan);
+
+ int chanGen(int chan, int16 *stream, int len);
+
+ int fillNoise(ToneChan *t, int16 *buf, int len);
+ int fillSquare(ToneChan *t, int16 *buf, int len);
+
+private:
+ SndGenChan _channel[CHAN_MAX];
+ ToneChan _tchannel[CHAN_MAX];
+ int16 *_chanData;
+ int _chanAllocated;
+
+ int _dissolveMethod;
+};
+
+} // End of namespace Agi
+
+#endif /* AGI_SOUND_PCJR_H */
diff --git a/engines/agi/sound_sarien.cpp b/engines/agi/sound_sarien.cpp
new file mode 100644
index 0000000000..08bdd47497
--- /dev/null
+++ b/engines/agi/sound_sarien.cpp
@@ -0,0 +1,357 @@
+/* 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/md5.h"
+#include "common/config-manager.h"
+#include "common/fs.h"
+#include "common/random.h"
+#include "common/str-array.h"
+
+#include "sound/mididrv.h"
+
+#include "agi/agi.h"
+
+#include "agi/sound_sarien.h"
+
+namespace Agi {
+
+#define USE_INTERPOLATION
+
+static const int16 waveformRamp[WAVEFORM_SIZE] = {
+ 0, 8, 16, 24, 32, 40, 48, 56,
+ 64, 72, 80, 88, 96, 104, 112, 120,
+ 128, 136, 144, 152, 160, 168, 176, 184,
+ 192, 200, 208, 216, 224, 232, 240, 255,
+ 0, -248, -240, -232, -224, -216, -208, -200,
+ -192, -184, -176, -168, -160, -152, -144, -136,
+ -128, -120, -112, -104, -96, -88, -80, -72,
+ -64, -56, -48, -40, -32, -24, -16, -8 // Ramp up
+};
+
+static const int16 waveformSquare[WAVEFORM_SIZE] = {
+ 255, 230, 220, 220, 220, 220, 220, 220,
+ 220, 220, 220, 220, 220, 220, 220, 220,
+ 220, 220, 220, 220, 220, 220, 220, 220,
+ 220, 220, 220, 220, 220, 220, 220, 110,
+ -255, -230, -220, -220, -220, -220, -220, -220,
+ -220, -220, -220, -220, -220, -220, -220, -220,
+ -220, -220, -220, -220, -220, -220, -220, -220,
+ -220, -220, -220, -110, 0, 0, 0, 0 // Square
+};
+
+static const int16 waveformMac[WAVEFORM_SIZE] = {
+ 45, 110, 135, 161, 167, 173, 175, 176,
+ 156, 137, 123, 110, 91, 72, 35, -2,
+ -60, -118, -142, -165, -170, -176, -177, -179,
+ -177, -176, -164, -152, -117, -82, -17, 47,
+ 92, 137, 151, 166, 170, 173, 171, 169,
+ 151, 133, 116, 100, 72, 43, -7, -57,
+ -99, -141, -156, -170, -174, -177, -178, -179,
+ -175, -172, -165, -159, -137, -114, -67, -19
+};
+
+SoundGenSarien::SoundGenSarien(AgiEngine *vm, Audio::Mixer *pMixer) : SoundGen(vm, pMixer), _chn() {
+ _sndBuffer = (int16 *)calloc(2, BUFFER_SIZE);
+
+ memset(_sndBuffer, 0, BUFFER_SIZE << 1);
+ _env = false;
+ _playingSound = -1;
+ _playing = false;
+ _useChorus = true; // FIXME: Currently always true?
+
+ switch (_vm->_soundemu) {
+ case SOUND_EMU_NONE:
+ _waveform = waveformRamp;
+ _env = true;
+ break;
+ case SOUND_EMU_AMIGA:
+ case SOUND_EMU_PC:
+ _waveform = waveformSquare;
+ break;
+ case SOUND_EMU_MAC:
+ _waveform = waveformMac;
+ break;
+ }
+
+ report("Initializing sound:\n");
+
+ report("sound: envelopes ");
+ if (_env) {
+ report("enabled (decay=%d, sustain=%d)\n", ENV_DECAY, ENV_SUSTAIN);
+ } else {
+ report("disabled\n");
+ }
+
+ _mixer->playStream(Audio::Mixer::kMusicSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
+}
+
+SoundGenSarien::~SoundGenSarien() {
+ _mixer->stopHandle(_soundHandle);
+
+ free(_sndBuffer);
+}
+
+int SoundGenSarien::readBuffer(int16 *buffer, const int numSamples) {
+ fillAudio(buffer, numSamples / 2);
+
+ return numSamples;
+}
+
+void SoundGenSarien::play(int resnum) {
+ AgiSoundEmuType type;
+
+ type = (AgiSoundEmuType)_vm->_game.sounds[resnum]->type();
+
+ assert(type == AGI_SOUND_4CHN);
+
+ _playingSound = resnum;
+
+ PCjrSound *pcjrSound = (PCjrSound *) _vm->_game.sounds[resnum];
+
+ // Initialize channel info
+ for (int i = 0; i < NUM_CHANNELS; i++) {
+ _chn[i].type = type;
+ _chn[i].flags = AGI_SOUND_LOOP;
+
+ if (_env) {
+ _chn[i].flags |= AGI_SOUND_ENVELOPE;
+ _chn[i].adsr = AGI_SOUND_ENV_ATTACK;
+ }
+
+ _chn[i].ins = _waveform;
+ _chn[i].size = WAVEFORM_SIZE;
+ _chn[i].ptr = pcjrSound->getVoicePointer(i % 4);
+ _chn[i].timer = 0;
+ _chn[i].vol = 0;
+ _chn[i].end = 0;
+ }
+
+ memset(_sndBuffer, 0, BUFFER_SIZE << 1);
+}
+
+void SoundGenSarien::stop() {
+ _playingSound = -1;
+
+ for (int i = 0; i < NUM_CHANNELS; i++)
+ stopNote(i);
+}
+
+void SoundGenSarien::stopNote(int i) {
+ _chn[i].adsr = AGI_SOUND_ENV_RELEASE;
+
+ if (_useChorus) {
+ // Stop chorus ;)
+ if (_chn[i].type == AGI_SOUND_4CHN &&
+ _vm->_soundemu == SOUND_EMU_NONE && i < 3) {
+ stopNote(i + 4);
+ }
+ }
+}
+
+void SoundGenSarien::playNote(int i, int freq, int vol) {
+ if (!_vm->getflag(fSoundOn))
+ vol = 0;
+ else if (vol && _vm->_soundemu == SOUND_EMU_PC)
+ vol = 160;
+
+ _chn[i].phase = 0;
+ _chn[i].freq = freq;
+ _chn[i].vol = vol;
+ _chn[i].env = 0x10000;
+ _chn[i].adsr = AGI_SOUND_ENV_ATTACK;
+
+ if (_useChorus) {
+ // Add chorus ;)
+ if (_chn[i].type == AGI_SOUND_4CHN &&
+ _vm->_soundemu == SOUND_EMU_NONE && i < 3) {
+
+ int newfreq = freq * 1007 / 1000;
+
+ if (freq == newfreq)
+ newfreq++;
+
+ playNote(i + 4, newfreq, vol * 2 / 3);
+ }
+ }
+}
+
+void SoundGenSarien::playSound() {
+ int i;
+ AgiNote note;
+
+ if (_playingSound == -1)
+ return;
+
+ _playing = false;
+ for (i = 0; i < (_vm->_soundemu == SOUND_EMU_PC ? 1 : 4); i++) {
+ _playing |= !_chn[i].end;
+ note.read(_chn[i].ptr); // Read a single note (Doesn't advance the pointer)
+
+ if (_chn[i].end)
+ continue;
+
+ if ((--_chn[i].timer) <= 0) {
+ stopNote(i);
+
+ if (note.freqDiv != 0) {
+ int volume = (note.attenuation == 0x0F) ? 0 : (0xFF - note.attenuation * 2);
+ playNote(i, note.freqDiv * 10, volume);
+ }
+
+ _chn[i].timer = note.duration;
+
+ if (_chn[i].timer == 0xffff) {
+ _chn[i].end = 1;
+ _chn[i].vol = 0;
+ _chn[i].env = 0;
+
+ if (_useChorus) {
+ // chorus
+ if (_chn[i].type == AGI_SOUND_4CHN && _vm->_soundemu == SOUND_EMU_NONE && i < 3) {
+ _chn[i + 4].vol = 0;
+ _chn[i + 4].env = 0;
+ }
+ }
+ }
+ _chn[i].ptr += 5; // Advance the pointer to the next note data (5 bytes per note)
+ }
+ }
+
+ if (!_playing) {
+ _vm->_sound->soundIsFinished();
+
+ _playingSound = -1;
+ }
+}
+
+uint32 SoundGenSarien::mixSound() {
+ register int i, p;
+ const int16 *src;
+ int c, b, m;
+
+ memset(_sndBuffer, 0, BUFFER_SIZE << 1);
+
+ if (!_playing || _playingSound == -1)
+ return BUFFER_SIZE;
+
+ // Handle PCjr 4-channel sound mixing here
+ for (c = 0; c < NUM_CHANNELS; c++) {
+ if (!_chn[c].vol)
+ continue;
+
+ m = _chn[c].flags & AGI_SOUND_ENVELOPE ?
+ _chn[c].vol * _chn[c].env >> 16 : _chn[c].vol;
+
+ if (_chn[c].type != AGI_SOUND_4CHN || c != 3) {
+ src = _chn[c].ins;
+
+ p = _chn[c].phase;
+ for (i = 0; i < BUFFER_SIZE; i++) {
+ b = src[p >> 8];
+#ifdef USE_INTERPOLATION
+ b += ((src[((p >> 8) + 1) % _chn[c].size] - src[p >> 8]) * (p & 0xff)) >> 8;
+#endif
+ _sndBuffer[i] += (b * m) >> 4;
+
+ p += (uint32) 118600 *4 / _chn[c].freq;
+
+ // FIXME: Fingolfin asks: why is there a FIXME here? Please either clarify what
+ // needs fixing, or remove it!
+ // FIXME
+ if (_chn[c].flags & AGI_SOUND_LOOP) {
+ p %= _chn[c].size << 8;
+ } else {
+ if (p >= _chn[c].size << 8) {
+ p = _chn[c].vol = 0;
+ _chn[c].end = 1;
+ break;
+ }
+ }
+
+ }
+ _chn[c].phase = p;
+ } else {
+ // Add white noise
+ for (i = 0; i < BUFFER_SIZE; i++) {
+ b = _vm->_rnd->getRandomNumber(255) - 128;
+ _sndBuffer[i] += (b * m) >> 4;
+ }
+ }
+
+ switch (_chn[c].adsr) {
+ case AGI_SOUND_ENV_ATTACK:
+ // not implemented
+ _chn[c].adsr = AGI_SOUND_ENV_DECAY;
+ break;
+ case AGI_SOUND_ENV_DECAY:
+ if (_chn[c].env > _chn[c].vol * ENV_SUSTAIN + ENV_DECAY) {
+ _chn[c].env -= ENV_DECAY;
+ } else {
+ _chn[c].env = _chn[c].vol * ENV_SUSTAIN;
+ _chn[c].adsr = AGI_SOUND_ENV_SUSTAIN;
+ }
+ break;
+ case AGI_SOUND_ENV_SUSTAIN:
+ break;
+ case AGI_SOUND_ENV_RELEASE:
+ if (_chn[c].env >= ENV_RELEASE) {
+ _chn[c].env -= ENV_RELEASE;
+ } else {
+ _chn[c].env = 0;
+ }
+ }
+ }
+
+ return BUFFER_SIZE;
+}
+
+void SoundGenSarien::fillAudio(int16 *stream, uint len) {
+ uint32 p = 0;
+
+ // current number of audio bytes in _sndBuffer
+ static uint32 data_available = 0;
+ // offset of start of audio bytes in _sndBuffer
+ static uint32 data_offset = 0;
+
+ len <<= 2;
+
+ debugC(5, kDebugLevelSound, "(%p, %d)", (void *)stream, len);
+
+ while (len > data_available) {
+ memcpy((uint8 *)stream + p, (uint8*)_sndBuffer + data_offset, data_available);
+ p += data_available;
+ len -= data_available;
+
+ playSound();
+ data_available = mixSound() << 1;
+ data_offset = 0;
+ }
+
+ memcpy((uint8 *)stream + p, (uint8*)_sndBuffer + data_offset, len);
+ data_offset += len;
+ data_available -= len;
+}
+
+} // End of namespace Agi
diff --git a/engines/agi/sound_sarien.h b/engines/agi/sound_sarien.h
new file mode 100644
index 0000000000..54222ba624
--- /dev/null
+++ b/engines/agi/sound_sarien.h
@@ -0,0 +1,120 @@
+/* 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 AGI_SOUND_SARIEN_H
+#define AGI_SOUND_SARIEN_H
+
+#include "sound/audiostream.h"
+
+namespace Agi {
+
+#define BUFFER_SIZE 410
+
+#define WAVEFORM_SIZE 64
+#define ENV_ATTACK 10000 /**< envelope attack rate */
+#define ENV_DECAY 1000 /**< envelope decay rate */
+#define ENV_SUSTAIN 100 /**< envelope sustain level */
+#define ENV_RELEASE 7500 /**< envelope release rate */
+#define NUM_CHANNELS 7 /**< number of sound channels */
+
+enum AgiSoundFlags {
+ AGI_SOUND_LOOP = 0x0001,
+ AGI_SOUND_ENVELOPE = 0x0002
+};
+enum AgiSoundEnv {
+ AGI_SOUND_ENV_ATTACK = 3,
+ AGI_SOUND_ENV_DECAY = 2,
+ AGI_SOUND_ENV_SUSTAIN = 1,
+ AGI_SOUND_ENV_RELEASE = 0
+};
+
+
+/**
+ * AGI engine sound channel structure.
+ */
+struct ChannelInfo {
+ AgiSoundEmuType type;
+ const uint8 *ptr; // Pointer to the AgiNote data
+ const int16 *ins;
+ int32 size;
+ uint32 phase;
+ uint32 flags; // ORs values from AgiSoundFlags
+ AgiSoundEnv adsr;
+ int32 timer;
+ uint32 end;
+ uint32 freq;
+ uint32 vol;
+ uint32 env;
+};
+
+class SoundGenSarien : public SoundGen, public Audio::AudioStream {
+public:
+ SoundGenSarien(AgiEngine *vm, Audio::Mixer *pMixer);
+ ~SoundGenSarien();
+
+ void play(int resnum);
+ void stop(void);
+
+ // AudioStream API
+ int readBuffer(int16 *buffer, const int numSamples);
+
+ bool isStereo() const {
+ return false;
+ }
+
+ bool endOfData() const {
+ return false;
+ }
+
+ int getRate() const {
+ // FIXME: Ideally, we should use _sampleRate.
+ return 22050;
+ }
+
+private:
+ ChannelInfo _chn[NUM_CHANNELS];
+ uint8 _env;
+
+ int16 *_sndBuffer;
+ const int16 *_waveform;
+
+ bool _useChorus;
+
+ bool _playing;
+ int _playingSound;
+
+private:
+ void playSound();
+ uint32 mixSound();
+ void fillAudio(int16 *stream, uint len);
+
+ void stopNote(int i);
+ void playNote(int i, int freq, int vol);
+
+};
+
+} // End of namespace Agi
+
+#endif /* AGI_SOUND_SARIEN_H */
diff --git a/engines/agi/sprite.cpp b/engines/agi/sprite.cpp
index 63ac880267..25118b69a6 100644
--- a/engines/agi/sprite.cpp
+++ b/engines/agi/sprite.cpp
@@ -241,6 +241,14 @@ void SpritesMgr::objsRestoreArea(Sprite *s) {
q += xSize;
pos0 += _WIDTH;
}
+
+ // WORKAROUND (see ScummVM bug #1945716)
+ // When set.view command is called, current code cannot detect this situation while updating
+ // Thus we force removal of the old sprite
+ if (s->v->viewReplaced) {
+ commitBlock(xPos, yPos, xPos + xSize, yPos + ySize);
+ s->v->viewReplaced = false;
+ }
}
@@ -332,6 +340,8 @@ void SpritesMgr::buildList(SpriteList &l, bool (*test)(VtEntry *, AgiEngine *))
}
}
+ debugC(5, kDebugLevelSprites, "buildList() --> entries %d", i);
+
// now look for the smallest y value in the array and put that
// sprite in the list
for (j = 0; j < i; j++) {
@@ -381,38 +391,20 @@ void SpritesMgr::freeList(SpriteList &l) {
* Copy sprites from the pic buffer to the screen buffer, and check if
* sprites of the given list have moved.
*/
-void SpritesMgr::commitSprites(SpriteList &l) {
+void SpritesMgr::commitSprites(SpriteList &l, bool immediate) {
SpriteList::iterator iter;
for (iter = l.begin(); iter != l.end(); ++iter) {
Sprite *s = *iter;
- int x1, y1, x2, y2, w, h;
+ int x1, y1, x2, y2;
- w = (s->v->celData->width > s->v->celData2->width) ?
- s->v->celData->width : s->v->celData2->width;
-
- h = (s->v->celData->height >
- s->v->celData2->height) ? s->v->celData->
- height : s->v->celData2->height;
+ x1 = MIN((int)MIN(s->v->xPos, s->v->xPos2), MIN(s->v->xPos + s->v->celData->width, s->v->xPos2 + s->v->celData2->width));
+ x2 = MAX((int)MAX(s->v->xPos, s->v->xPos2), MAX(s->v->xPos + s->v->celData->width, s->v->xPos2 + s->v->celData2->width));
+ y1 = MIN((int)MIN(s->v->yPos, s->v->yPos2), MIN(s->v->yPos - s->v->celData->height, s->v->yPos2 - s->v->celData2->height));
+ y2 = MAX((int)MAX(s->v->yPos, s->v->yPos2), MAX(s->v->yPos - s->v->celData->height, s->v->yPos2 - s->v->celData2->height));
s->v->celData2 = s->v->celData;
- if (s->v->xPos < s->v->xPos2) {
- x1 = s->v->xPos;
- x2 = s->v->xPos2 + w - 1;
- } else {
- x1 = s->v->xPos2;
- x2 = s->v->xPos + w - 1;
- }
-
- if (s->v->yPos < s->v->yPos2) {
- y1 = s->v->yPos - h + 1;
- y2 = s->v->yPos2;
- } else {
- y1 = s->v->yPos2 - h + 1;
- y2 = s->v->yPos;
- }
-
- commitBlock(x1, y1, x2, y2);
+ commitBlock(x1, y1, x2, y2, immediate);
if (s->v->stepTimeCount != s->v->stepTime)
continue;
@@ -452,7 +444,7 @@ void SpritesMgr::blitSprites(SpriteList& l) {
Sprite *s = *iter;
objsSaveArea(s);
- debugC(8, kDebugLevelSprites, "s->v->entry = %d (prio %d)", s->v->entry, s->v->priority);
+ debugC(8, kDebugLevelSprites, "blitSprites(): s->v->entry = %d (prio %d)", s->v->entry, s->v->priority);
hidden = blitCel(s->xPos, s->yPos, s->v->priority, s->v->celData, s->v->viewData->agi256_2);
if (s->v->entry == 0) { // if ego, update f1
@@ -466,11 +458,11 @@ void SpritesMgr::blitSprites(SpriteList& l) {
*/
void SpritesMgr::commitUpdSprites() {
- commitSprites(_sprUpd);
+ commitSprites(_sprUpd, true);
}
void SpritesMgr::commitNonupdSprites() {
- commitSprites(_sprNonupd);
+ commitSprites(_sprNonupd, true);
}
// check moves in both lists
@@ -528,7 +520,7 @@ void SpritesMgr::eraseBoth() {
* @see blit_both()
*/
void SpritesMgr::blitUpdSprites() {
- debugC(7, kDebugLevelSprites, "blit updating");
+ debugC(7, kDebugLevelSprites, "blitUpdSprites()");
buildUpdBlitlist();
blitSprites(_sprUpd);
}
@@ -542,7 +534,7 @@ void SpritesMgr::blitUpdSprites() {
* @see blit_both()
*/
void SpritesMgr::blitNonupdSprites() {
- debugC(7, kDebugLevelSprites, "blit non-updating");
+ debugC(7, kDebugLevelSprites, "blitNonupdSprites()");
buildNonupdBlitlist();
blitSprites(_sprNonupd);
}
@@ -578,7 +570,7 @@ void SpritesMgr::addToPic(int view, int loop, int cel, int x, int y, int pri, in
int x1, y1, x2, y2, y3;
uint8 *p1, *p2;
- debugC(3, kDebugLevelSprites, "v=%d, l=%d, c=%d, x=%d, y=%d, p=%d, m=%d", view, loop, cel, x, y, pri, mar);
+ debugC(3, kDebugLevelSprites, "addToPic(view=%d, loop=%d, cel=%d, x=%d, y=%d, pri=%d, mar=%d)", view, loop, cel, x, y, pri, mar);
_vm->recordImageStackCall(ADD_VIEW, view, loop, cel, x, y, pri, mar);
@@ -609,7 +601,7 @@ void SpritesMgr::addToPic(int view, int loop, int cel, int x, int y, int pri, in
eraseBoth();
- debugC(4, kDebugLevelSprites, "blit_cel (%d, %d, %d, c)", x, y, pri);
+ debugC(4, kDebugLevelSprites, "blitCel(%d, %d, %d, c)", x, y, pri);
blitCel(x1, y1, pri, c, _vm->_game.views[view].agi256_2);
// If margin is 0, 1, 2, or 3, the base of the cel is
@@ -659,8 +651,7 @@ void SpritesMgr::addToPic(int view, int loop, int cel, int x, int y, int pri, in
blitBoth();
- debugC(4, kDebugLevelSprites, "commit_block (%d, %d, %d, %d)", x1, y1, x2, y2);
- commitBlock(x1, y1, x2, y2);
+ commitBlock(x1, y1, x2, y2, true);
}
/**
@@ -691,15 +682,15 @@ void SpritesMgr::showObj(int n) {
objsSaveArea(&s);
blitCel(x1, y1, 15, c, _vm->_game.views[n].agi256_2);
- commitBlock(x1, y1, x2, y2);
+ commitBlock(x1, y1, x2, y2, true);
_vm->messageBox(_vm->_game.views[n].descr);
objsRestoreArea(&s);
- commitBlock(x1, y1, x2, y2);
+ commitBlock(x1, y1, x2, y2, true);
free(s.buffer);
}
-void SpritesMgr::commitBlock(int x1, int y1, int x2, int y2) {
+void SpritesMgr::commitBlock(int x1, int y1, int x2, int y2, bool immediate) {
int i, w, offset;
uint8 *q;
@@ -711,7 +702,7 @@ void SpritesMgr::commitBlock(int x1, int y1, int x2, int y2) {
y1 = CLIP(y1, 0, _HEIGHT - 1);
y2 = CLIP(y2, 0, _HEIGHT - 1);
- debugC(7, kDebugLevelSprites, "%d, %d, %d, %d", x1, y1, x2, y2);
+ debugC(7, kDebugLevelSprites, "commitBlock(%d, %d, %d, %d)", x1, y1, x2, y2);
w = x2 - x1 + 1;
q = &_vm->_game.sbuf16c[x1 + _WIDTH * y1];
@@ -723,6 +714,9 @@ void SpritesMgr::commitBlock(int x1, int y1, int x2, int y2) {
}
_gfx->flushBlockA(x1, y1 + offset, x2, y2 + offset);
+
+ if (immediate)
+ _gfx->doUpdate();
}
SpritesMgr::SpritesMgr(AgiEngine *agi, GfxMgr *gfx) {
diff --git a/engines/agi/sprite.h b/engines/agi/sprite.h
index 7d6d7bb97e..57fd0dacf2 100644
--- a/engines/agi/sprite.h
+++ b/engines/agi/sprite.h
@@ -65,7 +65,7 @@ private:
void buildUpdBlitlist();
void buildNonupdBlitlist();
void freeList(SpriteList &l);
- void commitSprites(SpriteList &l);
+ void commitSprites(SpriteList &l, bool immediate = false);
void eraseSprites(SpriteList &l);
void blitSprites(SpriteList &l);
static bool testUpdating(VtEntry *v, AgiEngine *);
@@ -88,7 +88,7 @@ public:
void commitBoth();
void addToPic(int, int, int, int, int, int, int);
void showObj(int);
- void commitBlock(int, int, int, int);
+ void commitBlock(int x1, int y1, int x2, int y2, bool immediate = false);
};
} // End of namespace Agi
diff --git a/engines/agi/text.cpp b/engines/agi/text.cpp
index ee9aebf240..778da0a527 100644
--- a/engines/agi/text.cpp
+++ b/engines/agi/text.cpp
@@ -135,8 +135,8 @@ void AgiEngine::blitTextbox(const char *p, int y, int x, int len) {
if (x == 0 && y == 0 && len == 0)
x = y = -1;
- if (len <= 0 || len >= 40)
- len = 32;
+ if (len <= 0)
+ len = 30;
xoff = x * CHAR_COLS;
yoff = y * CHAR_LINES;
@@ -214,6 +214,7 @@ void AgiEngine::printTextConsole(const char *msg, int x, int y, int len, int fg,
x *= CHAR_COLS;
y *= 10;
+ debugC(4, kDebugLevelText, "printTextConsole(): %s, %d, %d, %d, %d, %d", msg, x, y, len, fg, bg);
printText2(1, msg, 0, x, y, len, fg, bg);
}
@@ -488,7 +489,7 @@ int AgiEngine::print(const char *p, int lin, int col, int len) {
_game.keypress = 0;
break;
}
- } while (_game.msgBoxTicks > 0);
+ } while (_game.msgBoxTicks > 0 && !(shouldQuit() || _restartGame));
setvar(vWindowReset, 0);
@@ -655,11 +656,8 @@ void AgiEngine::writePrompt() {
int l, fg, bg, pos;
int promptLength = strlen(agiSprintf(_game.strings[0]));
- if (!_game.inputEnabled || _game.inputMode != INPUT_NORMAL) {
- clearPrompt();
-
+ if (!_game.inputEnabled || _game.inputMode != INPUT_NORMAL)
return;
- }
l = _game.lineUserInput;
fg = _game.colorFg;
@@ -699,6 +697,8 @@ void AgiEngine::clearLines(int l1, int l2, int c) {
// inc for endline so it matches the correct num
// ie, from 22 to 24 is 3 lines, not 2 lines.
+ debugC(4, kDebugLevelText, "clearLines(%d, %d, %d)", l1, l2, c);
+
l1 *= CHAR_LINES;
l2 *= CHAR_LINES;
l2 += CHAR_LINES - 1;
diff --git a/engines/agi/view.cpp b/engines/agi/view.cpp
index fb417e86a9..45244bb292 100644
--- a/engines/agi/view.cpp
+++ b/engines/agi/view.cpp
@@ -294,37 +294,10 @@ void AgiEngine::setLoop(VtEntry *v, int n) {
* @param n number of AGI view resource
*/
void AgiEngine::setView(VtEntry *v, int n) {
-
- uint16 viewFlags = 0;
-
- // When setting a view to the view table, if there's already another view set in that
- // view table entry and it's still drawn, erase the existing view before setting the new one
- // Fixes bug #1658643: AGI: SQ1 (2.2 DOS ENG) Graphic error, ego leaves behind copy
- // Update: Apparently, this makes ego dissapear at times, e.g. when textboxes are shown
- // Therefore, it's limited to view 118 in SQ1 (Roger climbing the ladder)
- // Fixes bug #1715284: Roger sometimes disappears
- if (v->viewData != NULL) {
- if (v->currentView == 118 && v->flags & DRAWN && getGameID() == GID_SQ1) {
- viewFlags = v->flags; // Store the flags for the view
- _sprites->eraseUpdSprites();
-
- if (v->flags & UPDATE) {
- v->flags &= ~DRAWN;
- } else {
- _sprites->eraseNonupdSprites();
- v->flags &= ~DRAWN;
- _sprites->blitNonupdSprites();
- }
- _sprites->blitUpdSprites();
-
- _sprites->commitBlock(v->xPos, v->yPos - v->ySize + 1, v->xPos + v->xSize - 1, v->yPos);
- v->flags = viewFlags; // Restore the view's flags
- }
- }
-
v->viewData = &_game.views[n];
v->currentView = n;
v->numLoops = v->viewData->numLoops;
+ v->viewReplaced = true;
setLoop(v, v->currentLoop >= v->numLoops ? 0 : v->currentLoop);
}
@@ -338,6 +311,7 @@ void AgiEngine::startUpdate(VtEntry *v) {
v->flags |= UPDATE;
_sprites->blitBoth();
+ _sprites->commitBoth();
}
}
@@ -351,6 +325,7 @@ void AgiEngine::stopUpdate(VtEntry *v) {
v->flags &= ~UPDATE;
_sprites->blitBoth();
+ _sprites->commitBoth();
}
}
@@ -393,7 +368,7 @@ void AgiEngine::updateViewtable() {
break;
default:
// for KQ4
- if (getVersion() == 0x3086)
+ if (getVersion() == 0x3086 || getGameID() == GID_KQ4)
loop = loopTable4[v->direction];
break;
}
diff --git a/engines/agi/view.h b/engines/agi/view.h
index f9017ec4ae..85f2d6eaf9 100644
--- a/engines/agi/view.h
+++ b/engines/agi/view.h
@@ -63,6 +63,7 @@ struct VtEntry {
int16 xPos;
int16 yPos;
uint8 currentView;
+ bool viewReplaced;
struct AgiView *viewData;
uint8 currentLoop;
uint8 numLoops;
diff --git a/engines/agos/agos.cpp b/engines/agos/agos.cpp
index 6502866e8d..1b7802acb6 100644
--- a/engines/agos/agos.cpp
+++ b/engines/agos/agos.cpp
@@ -201,19 +201,19 @@ AGOSEngine::AGOSEngine(OSystem *syst)
_scanFlag = false;
_scriptVar2 = 0;
- _runScriptReturn1 = 0;
- _skipVgaWait = 0;
- _noParentNotify = 0;
- _beardLoaded = 0;
- _litBoxFlag = 0;
- _mortalFlag = 0;
+ _runScriptReturn1 = false;
+ _skipVgaWait = false;
+ _noParentNotify = false;
+ _beardLoaded = false;
+ _litBoxFlag = false;
+ _mortalFlag = false;
_displayFlag = 0;
- _syncFlag2 = 0;
- _inCallBack = 0;
- _cepeFlag = 0;
- _fastMode = 0;
+ _syncFlag2 = false;
+ _inCallBack = false;
+ _cepeFlag = false;
+ _fastMode = false;
- _backFlag = 0;
+ _backFlag = false;
_debugMode = 0;
_dumpScripts = false;
@@ -556,10 +556,10 @@ Common::Error AGOSEngine::init() {
(getPlatform() == Common::kPlatformPC)) {
// Setup midi driver
- MidiDriverType midiDriver = MidiDriver::detectMusicDriver(MDT_ADLIB | MDT_MIDI);
- _nativeMT32 = ((midiDriver == MD_MT32) || ConfMan.getBool("native_mt32"));
+ MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_ADLIB | MDT_MIDI | (getGameType() == GType_SIMON1 ? MDT_PREFER_MT32 : MDT_PREFER_GM));
+ _nativeMT32 = ((MidiDriver::getMusicType(dev) == MT_MT32) || ConfMan.getBool("native_mt32"));
- _driver = MidiDriver::createMidi(midiDriver);
+ _driver = MidiDriver::createMidi(dev);
if (_nativeMT32) {
_driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
@@ -630,9 +630,11 @@ Common::Error AGOSEngine::init() {
if (ConfMan.hasKey("sfx_mute") && ConfMan.getBool("sfx_mute") == 1) {
if (getGameId() == GID_SIMON1DOS)
- _midi._enable_sfx ^= 1;
- else
- _sound->effectsPause(_effectsPaused ^= 1);
+ _midi._enable_sfx = !_midi._enable_sfx;
+ else {
+ _effectsPaused = !_effectsPaused;
+ _sound->effectsPause(_effectsPaused);
+ }
}
_copyProtection = ConfMan.getBool("copy_protection");
@@ -647,7 +649,7 @@ Common::Error AGOSEngine::init() {
if (getGameType() == GType_SIMON1) {
// English and German versions don't have full subtitles
- if (_language == Common::EN_ANY || _language == Common::DE_DEU)
+ if (_language == Common::EN_ANY || _language == Common::DE_DEU)
_subtitles = false;
// Other versions require speech to be enabled
else
diff --git a/engines/agos/agos.h b/engines/agos/agos.h
index ab1009e02a..b12bf09d62 100644
--- a/engines/agos/agos.h
+++ b/engines/agos/agos.h
@@ -393,7 +393,7 @@ protected:
Common::Point _mouseOld;
byte *_mouseData;
- byte _animatePointer;
+ bool _animatePointer;
byte _maxCursorWidth, _maxCursorHeight;
byte _mouseAnim, _mouseAnimMax, _mouseCursor;
byte _currentMouseAnim, _currentMouseCursor;
diff --git a/engines/agos/cursor.cpp b/engines/agos/cursor.cpp
index 109184e9c7..5ff2f014a6 100644
--- a/engines/agos/cursor.cpp
+++ b/engines/agos/cursor.cpp
@@ -459,7 +459,7 @@ void AGOSEngine_Simon1::handleMouseMoved() {
_leftButtonDown = false;
x = 1;
} else {
- if (_litBoxFlag == 0 && _needHitAreaRecalc == 0)
+ if (!_litBoxFlag && _needHitAreaRecalc == 0)
goto get_out;
}
@@ -473,7 +473,7 @@ get_out:
drawMousePointer();
_needHitAreaRecalc = 0;
- _litBoxFlag = 0;
+ _litBoxFlag = false;
}
void AGOSEngine_PN::handleMouseMoved() {
@@ -538,7 +538,7 @@ void AGOSEngine_PN::handleMouseMoved() {
drawMousePointer();
_needHitAreaRecalc = 0;
- _litBoxFlag = 0;
+ _litBoxFlag = false;
}
void AGOSEngine::handleMouseMoved() {
@@ -610,7 +610,7 @@ void AGOSEngine::handleMouseMoved() {
_oneClick = 0;
x = 1;
} else {
- if (_litBoxFlag == 0 && _needHitAreaRecalc == 0)
+ if (!_litBoxFlag && _needHitAreaRecalc == 0)
goto get_out;
}
@@ -622,7 +622,7 @@ get_out:
drawMousePointer();
_needHitAreaRecalc = 0;
- _litBoxFlag = 0;
+ _litBoxFlag = false;
}
void AGOSEngine::mouseOff() {
@@ -706,10 +706,10 @@ void AGOSEngine_Feeble::drawMousePointer() {
uint cursor;
int image, offs;
- if (_animatePointer != 0) {
+ if (_animatePointer) {
if (getBitFlag(99)) {
- _mouseToggle ^= 1;
- if (_mouseToggle != 0)
+ _mouseToggle = !_mouseToggle;
+ if (_mouseToggle)
_mouseAnim++;
} else {
_mouseAnim++;
@@ -720,7 +720,7 @@ void AGOSEngine_Feeble::drawMousePointer() {
cursor = _mouseCursor;
- if (_animatePointer == 0 && getBitFlag(99)) {
+ if (!_animatePointer && getBitFlag(99)) {
_mouseAnim = 1;
cursor = 6;
} else if (_mouseCursor != 5 && getBitFlag(72)) {
diff --git a/engines/agos/detection.cpp b/engines/agos/detection.cpp
index 39974f9d53..646e63dacf 100644
--- a/engines/agos/detection.cpp
+++ b/engines/agos/detection.cpp
@@ -84,6 +84,11 @@ static const PlainGameDescriptor simonGames[] = {
#include "agos/detection_tables.h"
+static const char *directoryGlobs[] = {
+ "execute", // Used by Simon1 Acorn CD
+ 0
+};
+
static const ADParams detectionParams = {
// Pointer to ADGameDescription or its superset structure
(const byte *)AGOS::gameDescriptions,
@@ -102,7 +107,11 @@ static const ADParams detectionParams = {
// Flags
0,
// Additional GUI options (for every game}
- Common::GUIO_NOLAUNCHLOAD
+ Common::GUIO_NOLAUNCHLOAD,
+ // Maximum directory depth
+ 2,
+ // List of directory globs
+ directoryGlobs
};
using namespace AGOS;
diff --git a/engines/agos/draw.cpp b/engines/agos/draw.cpp
index 300ed4c52b..4d32b4521d 100644
--- a/engines/agos/draw.cpp
+++ b/engines/agos/draw.cpp
@@ -176,9 +176,9 @@ void AGOSEngine::animateSprites() {
_windowNum = 4;
- _backFlag = 1;
+ _backFlag = true;
drawImage(&state);
- _backFlag = 0;
+ _backFlag = false;
_vgaSpriteChanged++;
}
@@ -451,14 +451,14 @@ void AGOSEngine::restoreBackGround() {
state.paletteMod = 0;
state.flags = kDFNonTrans;
- _backFlag = 1;
+ _backFlag = true;
drawImage(&state);
if (getGameType() != GType_SIMON1 && getGameType() != GType_SIMON2) {
animTable->srcPtr = 0;
}
}
- _backFlag = 0;
+ _backFlag = false;
if (getGameType() == GType_SIMON1 || getGameType() == GType_SIMON2) {
AnimTable *animTableTmp;
diff --git a/engines/agos/event.cpp b/engines/agos/event.cpp
index 3bbc0624aa..f3ec948f0f 100644
--- a/engines/agos/event.cpp
+++ b/engines/agos/event.cpp
@@ -479,7 +479,7 @@ void AGOSEngine::delay(uint amount) {
_aboutDialog = new GUI::AboutDialog();
_aboutDialog->runModal();
} else if (event.kbd.keycode == Common::KEYCODE_f) {
- _fastMode ^= 1;
+ _fastMode = !_fastMode;
} else if (event.kbd.keycode == Common::KEYCODE_d) {
_debugger->attach();
} else if (event.kbd.keycode == Common::KEYCODE_s) {
@@ -566,7 +566,7 @@ void AGOSEngine_Feeble::timerProc() {
_videoLockOut |= 2;
if (!(_videoLockOut & 0x10)) {
- _syncFlag2 ^= 1;
+ _syncFlag2 = !_syncFlag2;
if (!_syncFlag2) {
processVgaEvents();
} else {
@@ -635,7 +635,7 @@ void AGOSEngine_PN::timerProc() {
processVgaEvents();
processVgaEvents();
- _cepeFlag ^= 1;
+ _cepeFlag = !_cepeFlag;
if (!_cepeFlag)
processVgaEvents();
}
@@ -661,7 +661,7 @@ void AGOSEngine::timerProc() {
if (!(_videoLockOut & 0x10)) {
processVgaEvents();
processVgaEvents();
- _cepeFlag ^= 1;
+ _cepeFlag = !_cepeFlag;
if (!_cepeFlag)
processVgaEvents();
}
diff --git a/engines/agos/gfx.cpp b/engines/agos/gfx.cpp
index e2c634007c..82a4cb714b 100644
--- a/engines/agos/gfx.cpp
+++ b/engines/agos/gfx.cpp
@@ -730,7 +730,7 @@ void AGOSEngine_Simon1::drawImage(VC10_state *state) {
state->paletteMod = 208;
}
- if (_backFlag == 1) {
+ if (_backFlag) {
drawBackGroundImage(state);
} else if (state->flags & kDFMasked) {
drawMaskedImage(state);
@@ -947,7 +947,7 @@ void AGOSEngine::drawImage(VC10_state *state) {
if (getGameType() == GType_ELVIRA2 && getPlatform() == Common::kPlatformAtariST && yoffs > 133)
state->palette = 208;
- if (_backFlag == 1) {
+ if (_backFlag) {
drawBackGroundImage(state);
} else {
drawVertImage(state);
@@ -1351,7 +1351,7 @@ void AGOSEngine::setWindowImage(uint16 mode, uint16 vgaSpriteId, bool specialCas
if (getGameType() == GType_FF || getGameType() == GType_PP) {
fillBackGroundFromBack();
- _syncFlag2 = 1;
+ _syncFlag2 = true;
} else {
_copyScnFlag = 2;
_vgaSpriteChanged++;
diff --git a/engines/agos/icons.cpp b/engines/agos/icons.cpp
index fdc5d1707e..08a3d4e2f0 100644
--- a/engines/agos/icons.cpp
+++ b/engines/agos/icons.cpp
@@ -448,7 +448,7 @@ void AGOSEngine_Feeble::drawIconArray(uint num, Item *itemRef, int line, int cla
setupIconHitArea(window, k++, xp, yp, itemRef);
} else {
/*
- * Just remember the overflow has occured
+ * Just remember the overflow has occurred
*/
window->iconPtr->iconArray[icount].item = NULL; /* END MARKINGS */
_iOverflow = 1;
diff --git a/engines/agos/input.cpp b/engines/agos/input.cpp
index 35ed045675..5fc2a64416 100644
--- a/engines/agos/input.cpp
+++ b/engines/agos/input.cpp
@@ -99,7 +99,7 @@ void AGOSEngine::setup_cond_c_helper() {
animMax = 9;
}
- _animatePointer = 0;
+ _animatePointer = false;
_mouseCursor = cursor;
_mouseAnimMax = animMax;
_mouseAnim = 1;
@@ -574,13 +574,13 @@ bool AGOSEngine::processSpecialKeys() {
if (getGameType() == GType_FF || (getGameType() == GType_SIMON2 && (getFeatures() & GF_TALKIE)) ||
((getFeatures() & GF_TALKIE) && _language != Common::EN_ANY && _language != Common::DE_DEU)) {
if (_speech)
- _subtitles ^= 1;
+ _subtitles = !_subtitles;
}
break;
case 'v':
if (getGameType() == GType_FF || (getGameType() == GType_SIMON2 && (getFeatures() & GF_TALKIE))) {
if (_subtitles)
- _speech ^= 1;
+ _speech = !_speech;
}
break;
case '+':
@@ -598,7 +598,7 @@ bool AGOSEngine::processSpecialKeys() {
syncSoundSettings();
break;
case 'm':
- _musicPaused ^= 1;
+ _musicPaused = !_musicPaused;
if (_midiEnabled) {
_midi.pause(_musicPaused);
}
@@ -606,14 +606,16 @@ bool AGOSEngine::processSpecialKeys() {
break;
case 's':
if (getGameId() == GID_SIMON1DOS) {
- _midi._enable_sfx ^= 1;
+ _midi._enable_sfx = !_midi._enable_sfx;
} else {
- _sound->effectsPause(_effectsPaused ^= 1);
+ _effectsPaused = !_effectsPaused;
+ _sound->effectsPause(_effectsPaused);
}
break;
case 'b':
if (getGameType() == GType_SIMON2) {
- _sound->ambientPause(_ambientPaused ^= 1);
+ _ambientPaused = !_ambientPaused;
+ _sound->ambientPause(_ambientPaused);
}
break;
default:
diff --git a/engines/agos/verb.cpp b/engines/agos/verb.cpp
index a85c1627bf..b05bac1e57 100644
--- a/engines/agos/verb.cpp
+++ b/engines/agos/verb.cpp
@@ -207,7 +207,7 @@ static const char *const czech_verb_prep_names[] = {
void AGOSEngine_Feeble::clearName() {
stopAnimateSimon2(2, 6);
_lastNameOn = NULL;
- _animatePointer = 0;
+ _animatePointer = false;
_mouseAnim = 1;
return;
}
@@ -898,7 +898,7 @@ void AGOSEngine::displayName(HitArea *ha) {
if (getBitFlag(99))
_animatePointer = ((ha->flags & kBFTextBox) == 0);
else
- _animatePointer = 1;
+ _animatePointer = true;
if (!getBitFlag(73))
return;
@@ -933,7 +933,7 @@ void AGOSEngine_Feeble::invertBox(HitArea *ha, bool state) {
_mouseCursor = _oldMouseCursor;
} else if (_mouseCursor != 18) {
_oldMouseCursor = _mouseCursor;
- _animatePointer = 0;
+ _animatePointer = false;
_oldMouseAnimMax = _mouseAnimMax;
_mouseAnimMax = 2;
_mouseCursor = 18;
diff --git a/engines/agos/vga_e2.cpp b/engines/agos/vga_e2.cpp
index 1bbc7f4849..54ec45b967 100644
--- a/engines/agos/vga_e2.cpp
+++ b/engines/agos/vga_e2.cpp
@@ -265,7 +265,7 @@ void AGOSEngine::vc53_dissolveIn() {
*dst &= color;
*dst |= *src & 0xF;
- _system->unlockScreen();
+ _system->unlockScreen();
dissolveCount--;
if (!dissolveCount) {
@@ -319,7 +319,7 @@ void AGOSEngine::vc54_dissolveOut() {
dst += xoffs;
*dst = color;
- _system->unlockScreen();
+ _system->unlockScreen();
dissolveCount--;
if (!dissolveCount) {
@@ -388,7 +388,7 @@ void AGOSEngine::vc56_fullScreen() {
src += 320;
dst += screen->pitch;
}
- _system->unlockScreen();
+ _system->unlockScreen();
fullFade();
}
diff --git a/engines/agos/vga_s2.cpp b/engines/agos/vga_s2.cpp
index 4eb739e974..db3a7c18f3 100644
--- a/engines/agos/vga_s2.cpp
+++ b/engines/agos/vga_s2.cpp
@@ -138,7 +138,7 @@ void AGOSEngine::vc69_playSeq() {
// This is a "play track". The original
// design stored the track to play if one was
// already in progress, so that the next time a
- // "fill MIDI stream" event occured, the MIDI
+ // "fill MIDI stream" event occurred, the MIDI
// player would find the change and switch
// tracks. We use a different architecture that
// allows for an immediate response here, but
diff --git a/engines/cine/detection.cpp b/engines/cine/detection.cpp
index fcfa1f7f20..b92ad8a0a2 100644
--- a/engines/cine/detection.cpp
+++ b/engines/cine/detection.cpp
@@ -23,8 +23,6 @@
*
*/
-
-
#include "base/plugins.h"
#include "engines/advancedDetector.h"
@@ -62,494 +60,7 @@ static const ADObsoleteGameID obsoleteGameIDsTable[] = {
{0, 0, Common::kPlatformUnknown}
};
-namespace Cine {
-
-using Common::GUIO_NONE;
-
-static const CINEGameDescription gameDescriptions[] = {
- {
- {
- "fw",
- "",
- AD_ENTRY1("part01", "61d003202d301c29dd399acfb1354310"),
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_FW,
- 0,
- },
-
- // This is a CD version of Future Wars published by Sony.
- // This version has a crypted AUTO00.PRC.
- {
- {
- "fw",
- "Sony CD version",
- {
- { "AUTO00.PRC", 0, "4fe1e7930b38e3c63f0f2474d471bf8f", -1},
- { "PART01", 0, "61d003202d301c29dd399acfb1354310", -1},
- { NULL, 0, NULL, 0}
- },
- Common::EN_USA,
- Common::kPlatformPC,
- ADGF_CD,
- GUIO_NONE
- },
- GType_FW,
- GF_CD | GF_CRYPTED_BOOT_PRC,
- },
-
- {
- // This is the version included in the UK "Classic Collection"
- {
- "fw",
- "",
- AD_ENTRY1("part01", "91d7271155520eae6915a9dd2dac120c"),
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_FW,
- 0,
- },
-
- {
- {
- "fw",
- "",
- AD_ENTRY1("part01", "f5e98fcca3fb5e7afa284c81c39d8b14"),
- Common::DE_DEU,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_FW,
- GF_ALT_FONT,
- },
-
- {
- {
- "fw",
- "",
- AD_ENTRY1("part01", "570109f965c7f53984b98c83d86eb206"),
- Common::ES_ESP,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_FW,
- GF_ALT_FONT,
- },
-
- {
- {
- "fw",
- "",
- AD_ENTRY1("part01", "5d1acb97abe9591f9008e00d07add95a"),
- Common::FR_FRA,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_FW,
- 0,
- },
-
- {
- {
- "fw",
- "",
- AD_ENTRY1("part01", "57afd280b598b4180fda6689fbedc4b8"),
- Common::EN_ANY,
- Common::kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_FW,
- 0,
- },
-
- { // Amiga "Interplay" labeled version
- {
- "fw",
- "",
- AD_ENTRY1("part01", "a17a5eb15200c63276d486a88263ccd0"),
- Common::EN_USA,
- Common::kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_FW,
- 0,
- },
-
- {
- {
- "fw",
- "",
- AD_ENTRY1("part01", "3a87a913e0e33963a48a7f822ca0eb0e"),
- Common::DE_DEU,
- Common::kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_FW,
- GF_ALT_FONT,
- },
-
- {
- {
- "fw",
- "",
- AD_ENTRY1("part01", "5ad0007ccd5f7b3dd6b15ea7f281f9e1"),
- Common::ES_ESP,
- Common::kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_FW,
- 0,
- },
-
- {
- {
- "fw",
- "",
- AD_ENTRY1("part01", "460f2da8793bc581a2d4b6fc19ccb5ae"),
- Common::FR_FRA,
- Common::kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_FW,
- 0,
- },
-
- {
- {
- "fw",
- "",
- AD_ENTRY1("part01", "1c8e5207743172134409ac58860021af"),
- Common::IT_ITA,
- Common::kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_FW,
- 0,
- },
-
- {
- {
- "fw",
- "Demo",
- {
- { "demo", 0, "0f50767cd964e302d3af0ba2528df8c4", -1},
- { "demo.prc", 0, "d2ac3a743d288359c63644ea7071edae", -1},
- { NULL, 0, NULL, 0}
- },
- Common::EN_ANY,
- Common::kPlatformAmiga,
- ADGF_DEMO,
- GUIO_NONE
- },
- GType_FW,
- 0,
- },
-
- {
- {
- "fw",
- "",
- AD_ENTRY1("part01", "36050db13af57e462ca1adc4df99de4e"),
- Common::EN_ANY,
- Common::kPlatformAtariST,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_FW,
- 0,
- },
-
- {
- {
- "fw",
- "",
- AD_ENTRY1("part01", "ef245573b7dab0d4825ceb98e37cef4d"),
- Common::FR_FRA,
- Common::kPlatformAtariST,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_FW,
- 0,
- },
-
- {
- {
- "os",
- "256 colors",
- AD_ENTRY1("procs00", "d6752e7d25924cb866b61eb7cb0c8b56"),
- Common::EN_GRB,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_OS,
- 0,
- },
-
- {
- // This is a 16 color PC version (It came on three 720kB 3.5" disks).
- // The protagonist is named John Glames in this version.
- {
- "os",
- "",
- AD_ENTRY1("procs1", "9629129b86979fa592c1787385bf3695"),
- Common::EN_GRB,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_OS,
- 0,
- },
-
- {
- {
- "os",
- "",
- AD_ENTRY1("procs1", "d8c3a9d05a63e4cfa801826a7063a126"),
- Common::EN_USA,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_OS,
- 0,
- },
-
- {
- {
- "os",
- "256 colors",
- AD_ENTRY1("procs00", "862a75d76fb7fffec30e52be9ad1c474"),
- Common::EN_USA,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_OS,
- GF_CD,
- },
-
- {
- {
- "os",
- "",
- AD_ENTRY1("procs1", "39b91ae35d1297ce0a76a1a803ca1593"),
- Common::DE_DEU,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_OS,
- 0,
- },
-
- {
- {
- "os",
- "",
- AD_ENTRY1("procs1", "74c2dabd9d212525fca8875a5f6d8994"),
- Common::ES_ESP,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_OS,
- 0,
- },
-
- {
- {
- "os",
- "256 colors",
- {
- { "procs1", 0, "74c2dabd9d212525fca8875a5f6d8994", -1},
- { "sds1", 0, "75443ba39cdc95667e07d7118e5c151c", -1},
- { NULL, 0, NULL, 0}
- },
- Common::ES_ESP,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_OS,
- GF_CD,
- },
-
- {
- {
- "os",
- "256 colors",
- AD_ENTRY1("procs00", "f143567f08cfd1a9b1c9a41c89eadfef"),
- Common::FR_FRA,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_OS,
- 0,
- },
-
- {
- {
- "os",
- "",
- AD_ENTRY1("procs1", "da066e6b8dd93f2502c2a3755f08dc12"),
- Common::IT_ITA,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_OS,
- 0,
- },
-
- {
- {
- "os",
- "",
- AD_ENTRY1("procs0", "a9da5531ead0ebf9ad387fa588c0cbb0"),
- Common::EN_GRB,
- Common::kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_OS,
- 0,
- },
-
- {
- {
- "os",
- "alt",
- AD_ENTRY1("procs0", "8a429ced2f4acff8a15ae125174042e8"),
- Common::EN_GRB,
- Common::kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_OS,
- 0,
- },
-
- {
- {
- "os",
- "",
- AD_ENTRY1("procs0", "d5f27e33fc29c879f36f15b86ccfa58c"),
- Common::EN_USA,
- Common::kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_OS,
- 0,
- },
-
- {
- {
- "os",
- "",
- AD_ENTRY1("procs0", "8b7dce249821d3a62b314399c4334347"),
- Common::DE_DEU,
- Common::kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_OS,
- 0,
- },
-
- {
- {
- "os",
- "",
- AD_ENTRY1("procs0", "35fc295ddd0af9da932d256ba799a4b0"),
- Common::ES_ESP,
- Common::kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_OS,
- 0,
- },
-
- {
- {
- "os",
- "",
- AD_ENTRY1("procs0", "d4ea4a97e01fa67ea066f9e785050ed2"),
- Common::FR_FRA,
- Common::kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_OS,
- 0,
- },
-
- {
- {
- "os",
- "Demo",
- AD_ENTRY1("demo", "8d3a750d1c840b1b1071e42f9e6f6aa2"),
- Common::EN_GRB,
- Common::kPlatformAmiga,
- ADGF_DEMO,
- GUIO_NONE
- },
- GType_OS,
- GF_DEMO,
- },
-
- {
- {
- "os",
- "",
- AD_ENTRY1("procs0", "1501d5ae364b2814a33ed19347c3fcae"),
- Common::EN_GRB,
- Common::kPlatformAtariST,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_OS,
- 0,
- },
-
- {
- {
- "os",
- "",
- AD_ENTRY1("procs0", "2148d25de3219dd4a36580ca735d0afa"),
- Common::FR_FRA,
- Common::kPlatformAtariST,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GType_OS,
- 0,
- },
-
- { AD_TABLE_END_MARKER, 0, 0 }
-};
-
-} // End of namespace Cine
+#include "cine/detection_tables.h"
static const ADParams detectionParams = {
// Pointer to ADGameDescription or its superset structure
@@ -569,7 +80,11 @@ static const ADParams detectionParams = {
// Flags
0,
// Additional GUI options (for every game}
- Common::GUIO_NOSPEECH | Common::GUIO_NOMIDI
+ Common::GUIO_NOSPEECH | Common::GUIO_NOMIDI,
+ // Maximum directory depth
+ 1,
+ // List of directory globs
+ 0
};
class CineMetaEngine : public AdvancedMetaEngine {
diff --git a/engines/cine/detection_tables.h b/engines/cine/detection_tables.h
new file mode 100644
index 0000000000..6e450ebc80
--- /dev/null
+++ b/engines/cine/detection_tables.h
@@ -0,0 +1,513 @@
+/* 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$
+ *
+ */
+
+namespace Cine {
+
+using Common::GUIO_NONE;
+
+static const CINEGameDescription gameDescriptions[] = {
+ {
+ {
+ "fw",
+ "",
+ AD_ENTRY1("part01", "61d003202d301c29dd399acfb1354310"),
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_FW,
+ 0,
+ },
+
+ // This is a CD version of Future Wars published by Sony.
+ // This version has a crypted AUTO00.PRC.
+ {
+ {
+ "fw",
+ "Sony CD version",
+ {
+ { "AUTO00.PRC", 0, "4fe1e7930b38e3c63f0f2474d471bf8f", -1},
+ { "PART01", 0, "61d003202d301c29dd399acfb1354310", -1},
+ { NULL, 0, NULL, 0}
+ },
+ Common::EN_USA,
+ Common::kPlatformPC,
+ ADGF_CD,
+ GUIO_NONE
+ },
+ GType_FW,
+ GF_CD | GF_CRYPTED_BOOT_PRC,
+ },
+
+ {
+ // This is the version included in the UK "Classic Collection"
+ {
+ "fw",
+ "",
+ AD_ENTRY1("part01", "91d7271155520eae6915a9dd2dac120c"),
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_FW,
+ 0,
+ },
+
+ {
+ {
+ "fw",
+ "",
+ AD_ENTRY1("part01", "f5e98fcca3fb5e7afa284c81c39d8b14"),
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_FW,
+ GF_ALT_FONT,
+ },
+
+ {
+ {
+ "fw",
+ "",
+ AD_ENTRY1("part01", "570109f965c7f53984b98c83d86eb206"),
+ Common::ES_ESP,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_FW,
+ GF_ALT_FONT,
+ },
+
+ {
+ {
+ "fw",
+ "",
+ AD_ENTRY1("part01", "5d1acb97abe9591f9008e00d07add95a"),
+ Common::FR_FRA,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_FW,
+ 0,
+ },
+
+ {
+ {
+ "fw",
+ "",
+ AD_ENTRY1("part01", "57afd280b598b4180fda6689fbedc4b8"),
+ Common::EN_ANY,
+ Common::kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_FW,
+ 0,
+ },
+
+ { // Amiga "Interplay" labeled version
+ {
+ "fw",
+ "",
+ AD_ENTRY1("part01", "a17a5eb15200c63276d486a88263ccd0"),
+ Common::EN_USA,
+ Common::kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_FW,
+ 0,
+ },
+
+ {
+ {
+ "fw",
+ "",
+ AD_ENTRY1("part01", "3a87a913e0e33963a48a7f822ca0eb0e"),
+ Common::DE_DEU,
+ Common::kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_FW,
+ GF_ALT_FONT,
+ },
+
+ {
+ {
+ "fw",
+ "",
+ AD_ENTRY1("part01", "5ad0007ccd5f7b3dd6b15ea7f281f9e1"),
+ Common::ES_ESP,
+ Common::kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_FW,
+ 0,
+ },
+
+ {
+ {
+ "fw",
+ "",
+ AD_ENTRY1("part01", "460f2da8793bc581a2d4b6fc19ccb5ae"),
+ Common::FR_FRA,
+ Common::kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_FW,
+ 0,
+ },
+
+ {
+ {
+ "fw",
+ "",
+ AD_ENTRY1("part01", "1c8e5207743172134409ac58860021af"),
+ Common::IT_ITA,
+ Common::kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_FW,
+ 0,
+ },
+
+ {
+ {
+ "fw",
+ "Demo",
+ {
+ { "demo", 0, "0f50767cd964e302d3af0ba2528df8c4", -1},
+ { "demo.prc", 0, "d2ac3a743d288359c63644ea7071edae", -1},
+ { NULL, 0, NULL, 0}
+ },
+ Common::EN_ANY,
+ Common::kPlatformAmiga,
+ ADGF_DEMO,
+ GUIO_NONE
+ },
+ GType_FW,
+ 0,
+ },
+
+ {
+ {
+ "fw",
+ "",
+ AD_ENTRY1("part01", "36050db13af57e462ca1adc4df99de4e"),
+ Common::EN_ANY,
+ Common::kPlatformAtariST,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_FW,
+ 0,
+ },
+
+ {
+ {
+ "fw",
+ "",
+ AD_ENTRY1("part01", "ef245573b7dab0d4825ceb98e37cef4d"),
+ Common::FR_FRA,
+ Common::kPlatformAtariST,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_FW,
+ 0,
+ },
+
+ {
+ {
+ "os",
+ "256 colors",
+ AD_ENTRY1("procs00", "d6752e7d25924cb866b61eb7cb0c8b56"),
+ Common::EN_GRB,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_OS,
+ 0,
+ },
+
+ {
+ // This is a 16 color PC version (It came on three 720kB 3.5" disks).
+ // The protagonist is named John Glames in this version.
+ {
+ "os",
+ "",
+ AD_ENTRY1("procs1", "9629129b86979fa592c1787385bf3695"),
+ Common::EN_GRB,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_OS,
+ 0,
+ },
+
+ {
+ {
+ "os",
+ "",
+ AD_ENTRY1("procs1", "d8c3a9d05a63e4cfa801826a7063a126"),
+ Common::EN_USA,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_OS,
+ 0,
+ },
+
+ {
+ {
+ "os",
+ "256 colors",
+ AD_ENTRY1("procs00", "862a75d76fb7fffec30e52be9ad1c474"),
+ Common::EN_USA,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_OS,
+ GF_CD,
+ },
+
+ {
+ {
+ "os",
+ "",
+ AD_ENTRY1("procs1", "39b91ae35d1297ce0a76a1a803ca1593"),
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_OS,
+ 0,
+ },
+
+ {
+ {
+ "os",
+ "",
+ AD_ENTRY1("procs1", "74c2dabd9d212525fca8875a5f6d8994"),
+ Common::ES_ESP,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_OS,
+ 0,
+ },
+
+ {
+ {
+ "os",
+ "256 colors",
+ {
+ { "procs1", 0, "74c2dabd9d212525fca8875a5f6d8994", -1},
+ { "sds1", 0, "75443ba39cdc95667e07d7118e5c151c", -1},
+ { NULL, 0, NULL, 0}
+ },
+ Common::ES_ESP,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_OS,
+ GF_CD,
+ },
+
+ {
+ {
+ "os",
+ "256 colors",
+ AD_ENTRY1("procs00", "f143567f08cfd1a9b1c9a41c89eadfef"),
+ Common::FR_FRA,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_OS,
+ 0,
+ },
+
+ {
+ {
+ "os",
+ "",
+ AD_ENTRY1("procs1", "da066e6b8dd93f2502c2a3755f08dc12"),
+ Common::IT_ITA,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_OS,
+ 0,
+ },
+
+ {
+ {
+ "os",
+ "",
+ AD_ENTRY1("procs0", "a9da5531ead0ebf9ad387fa588c0cbb0"),
+ Common::EN_GRB,
+ Common::kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_OS,
+ 0,
+ },
+
+ {
+ {
+ "os",
+ "alt",
+ AD_ENTRY1("procs0", "8a429ced2f4acff8a15ae125174042e8"),
+ Common::EN_GRB,
+ Common::kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_OS,
+ 0,
+ },
+
+ {
+ {
+ "os",
+ "",
+ AD_ENTRY1("procs0", "d5f27e33fc29c879f36f15b86ccfa58c"),
+ Common::EN_USA,
+ Common::kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_OS,
+ 0,
+ },
+
+ {
+ {
+ "os",
+ "",
+ AD_ENTRY1("procs0", "8b7dce249821d3a62b314399c4334347"),
+ Common::DE_DEU,
+ Common::kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_OS,
+ 0,
+ },
+
+ {
+ {
+ "os",
+ "",
+ AD_ENTRY1("procs0", "35fc295ddd0af9da932d256ba799a4b0"),
+ Common::ES_ESP,
+ Common::kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_OS,
+ 0,
+ },
+
+ {
+ {
+ "os",
+ "",
+ AD_ENTRY1("procs0", "d4ea4a97e01fa67ea066f9e785050ed2"),
+ Common::FR_FRA,
+ Common::kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_OS,
+ 0,
+ },
+
+ {
+ {
+ "os",
+ "Demo",
+ AD_ENTRY1("demo", "8d3a750d1c840b1b1071e42f9e6f6aa2"),
+ Common::EN_GRB,
+ Common::kPlatformAmiga,
+ ADGF_DEMO,
+ GUIO_NONE
+ },
+ GType_OS,
+ GF_DEMO,
+ },
+
+ {
+ {
+ "os",
+ "",
+ AD_ENTRY1("procs0", "1501d5ae364b2814a33ed19347c3fcae"),
+ Common::EN_GRB,
+ Common::kPlatformAtariST,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_OS,
+ 0,
+ },
+
+ {
+ {
+ "os",
+ "",
+ AD_ENTRY1("procs0", "2148d25de3219dd4a36580ca735d0afa"),
+ Common::FR_FRA,
+ Common::kPlatformAtariST,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GType_OS,
+ 0,
+ },
+
+ { AD_TABLE_END_MARKER, 0, 0 }
+};
+
+} // End of namespace Cine
diff --git a/engines/cine/gfx.cpp b/engines/cine/gfx.cpp
index a4220e6e35..1f747c5a01 100644
--- a/engines/cine/gfx.cpp
+++ b/engines/cine/gfx.cpp
@@ -772,7 +772,7 @@ const char *FWRenderer::getBgName(uint idx) const {
* Restore active and backup palette from save
* @param fHandle Savefile open for reading
*/
-void FWRenderer::restorePalette(Common::SeekableReadStream &fHandle) {
+void FWRenderer::restorePalette(Common::SeekableReadStream &fHandle, int version) {
byte buf[kLowPalNumBytes];
// Load the active 16 color palette from file
@@ -819,9 +819,8 @@ void FWRenderer::savePalette(Common::OutSaveFile &fHandle) {
void OSRenderer::savePalette(Common::OutSaveFile &fHandle) {
byte buf[kHighPalNumBytes];
- // Make sure the active palette has the correct format and color count
- assert(_activePal.colorFormat() == kHighPalFormat);
- assert(_activePal.colorCount() == kHighPalNumColors);
+ // We can have 16 color palette in many cases
+ fHandle.writeUint16LE(_activePal.colorCount());
// Write the active 256 color palette.
_activePal.save(buf, sizeof(buf), CINE_LITTLE_ENDIAN);
@@ -836,12 +835,18 @@ void OSRenderer::savePalette(Common::OutSaveFile &fHandle) {
* Restore active and backup palette from save
* @param fHandle Savefile open for reading
*/
-void OSRenderer::restorePalette(Common::SeekableReadStream &fHandle) {
+void OSRenderer::restorePalette(Common::SeekableReadStream &fHandle, int version) {
byte buf[kHighPalNumBytes];
+ uint colorCount = (version > 0) ? fHandle.readUint16LE() : kHighPalNumBytes;
- // Load the active 256 color palette from file
fHandle.read(buf, kHighPalNumBytes);
- _activePal.load(buf, sizeof(buf), kHighPalFormat, kHighPalNumColors, CINE_LITTLE_ENDIAN);
+
+ if (colorCount == kHighPalNumBytes) {
+ // Load the active 256 color palette from file
+ _activePal.load(buf, sizeof(buf), kHighPalFormat, kHighPalNumColors, CINE_LITTLE_ENDIAN);
+ } else {
+ _activePal.load(buf, sizeof(buf), kLowPalFormat, kLowPalNumColors, CINE_LITTLE_ENDIAN);
+ }
// Jump over the backup 256 color palette.
// FIXME: Load the backup 256 color palette and use it properly.
diff --git a/engines/cine/gfx.h b/engines/cine/gfx.h
index 56ba6885f4..da7e3dd572 100644
--- a/engines/cine/gfx.h
+++ b/engines/cine/gfx.h
@@ -197,7 +197,7 @@ public:
virtual void refreshPalette();
virtual void reloadPalette();
- virtual void restorePalette(Common::SeekableReadStream &fHandle);
+ virtual void restorePalette(Common::SeekableReadStream &fHandle, int version);
virtual void savePalette(Common::OutSaveFile &fHandle);
virtual void rotatePalette(int a, int b, int c);
virtual void transformPalette(int first, int last, int r, int g, int b);
@@ -257,7 +257,7 @@ public:
const char *getBgName(uint idx = 0) const;
void reloadPalette();
- void restorePalette(Common::SeekableReadStream &fHandle);
+ void restorePalette(Common::SeekableReadStream &fHandle, int version);
void savePalette(Common::OutSaveFile &fHandle);
void transformPalette(int first, int last, int r, int g, int b);
diff --git a/engines/cine/saveload.cpp b/engines/cine/saveload.cpp
index c76bed3f8e..b5adebcd0b 100644
--- a/engines/cine/saveload.cpp
+++ b/engines/cine/saveload.cpp
@@ -566,7 +566,7 @@ bool CineEngine::loadTempSaveOS(Common::SeekableReadStream &in) {
}
loadObjectTable(in);
- renderer->restorePalette(in);
+ renderer->restorePalette(in, hdr.version);
globalVars.load(in, NUM_MAX_VAR);
loadZoneData(in);
loadCommandVariables(in);
@@ -698,7 +698,7 @@ bool CineEngine::loadPlainSaveFW(Common::SeekableReadStream &in, CineSaveGameFor
loadObjectTable(in);
// At 0x2043 (i.e. 0x005F + 2 * 2 + 255 * 32):
- renderer->restorePalette(in);
+ renderer->restorePalette(in, 0);
// At 0x2083 (i.e. 0x2043 + 16 * 2 * 2):
globalVars.load(in, NUM_MAX_VAR);
diff --git a/engines/cine/saveload.h b/engines/cine/saveload.h
index 65f24f838d..a6e0e3f1ab 100644
--- a/engines/cine/saveload.h
+++ b/engines/cine/saveload.h
@@ -74,7 +74,7 @@ enum CineSaveGameFormat {
static const uint32 TEMP_OS_FORMAT_ID = MKID_BE('TEMP');
/** The current version number of Operation Stealth's savegame format. */
-static const uint32 CURRENT_OS_SAVE_VER = 0;
+static const uint32 CURRENT_OS_SAVE_VER = 1;
/** Chunk header used by the temporary Operation Stealth savegame format. */
struct ChunkHeader {
diff --git a/engines/cruise/decompiler.cpp b/engines/cruise/decompiler.cpp
index ba4ade56a7..31d9dcef9b 100644
--- a/engines/cruise/decompiler.cpp
+++ b/engines/cruise/decompiler.cpp
@@ -760,8 +760,6 @@ int decompFunction() {
char *var1;
char *objIdxStr;
char *ovlStr;
- char varName[256];
- int i;
var1 = popDecomp();
objIdxStr = popDecomp();
diff --git a/engines/cruise/detection.cpp b/engines/cruise/detection.cpp
index e1f12b734e..e43fadf598 100644
--- a/engines/cruise/detection.cpp
+++ b/engines/cruise/detection.cpp
@@ -237,7 +237,11 @@ static const ADParams detectionParams = {
// Flags
0,
// Additional GUI options (for every game}
- Common::GUIO_NOSPEECH | Common::GUIO_NOMIDI
+ Common::GUIO_NOSPEECH | Common::GUIO_NOMIDI,
+ // Maximum directory depth
+ 1,
+ // List of directory globs
+ 0
};
class CruiseMetaEngine : public AdvancedMetaEngine {
diff --git a/engines/dialogs.cpp b/engines/dialogs.cpp
index 954bc81470..2397a474c6 100644
--- a/engines/dialogs.cpp
+++ b/engines/dialogs.cpp
@@ -28,6 +28,7 @@
#include "common/savefile.h"
#include "common/system.h"
#include "common/events.h"
+#include "common/translation.h"
#include "graphics/scaler.h"
@@ -85,37 +86,37 @@ MainMenuDialog::MainMenuDialog(Engine *engine)
StaticTextWidget *version = new StaticTextWidget(this, "GlobalMenu.Version", gScummVMVersionDate);
version->setAlign(Graphics::kTextAlignCenter);
- new GUI::ButtonWidget(this, "GlobalMenu.Resume", "Resume", kPlayCmd, 'P');
+ new GUI::ButtonWidget(this, "GlobalMenu.Resume", _("~R~esume"), 0, kPlayCmd, 'P');
- _loadButton = new GUI::ButtonWidget(this, "GlobalMenu.Load", "Load", kLoadCmd, 'L');
+ _loadButton = new GUI::ButtonWidget(this, "GlobalMenu.Load", _("~L~oad"), 0, kLoadCmd);
// TODO: setEnabled -> setVisible
_loadButton->setEnabled(_engine->hasFeature(Engine::kSupportsLoadingDuringRuntime));
- _saveButton = new GUI::ButtonWidget(this, "GlobalMenu.Save", "Save", kSaveCmd, 'S');
+ _saveButton = new GUI::ButtonWidget(this, "GlobalMenu.Save", _("~S~ave"), 0, kSaveCmd);
// TODO: setEnabled -> setVisible
_saveButton->setEnabled(_engine->hasFeature(Engine::kSupportsSavingDuringRuntime));
- new GUI::ButtonWidget(this, "GlobalMenu.Options", "Options", kOptionsCmd, 'O');
+ new GUI::ButtonWidget(this, "GlobalMenu.Options", _("~O~ptions"), 0, kOptionsCmd);
// 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 = new GUI::ButtonWidget(this, "GlobalMenu.Help", _("~H~elp"), 0, kHelpCmd);
_helpButton->setEnabled(false);
- new GUI::ButtonWidget(this, "GlobalMenu.About", "About", kAboutCmd, 'A');
+ new GUI::ButtonWidget(this, "GlobalMenu.About", _("~A~bout"), 0, kAboutCmd);
- _rtlButton = new GUI::ButtonWidget(this, "GlobalMenu.RTL", "Return to Launcher", kRTLCmd, 'R');
+ _rtlButton = new GUI::ButtonWidget(this, "GlobalMenu.RTL", _("~R~eturn to Launcher"), 0, kRTLCmd);
_rtlButton->setEnabled(_engine->hasFeature(Engine::kSupportsRTL));
- new GUI::ButtonWidget(this, "GlobalMenu.Quit", "Quit", kQuitCmd, 'Q');
+ new GUI::ButtonWidget(this, "GlobalMenu.Quit", _("~Q~uit"), 0, kQuitCmd);
_aboutDialog = new GUI::AboutDialog();
_optionsDialog = new ConfigDialog(_engine->hasFeature(Engine::kSupportsSubtitleOptions));
- _loadDialog = new GUI::SaveLoadChooser("Load game:", "Load");
+ _loadDialog = new GUI::SaveLoadChooser(_("Load game:"), _("Load"));
_loadDialog->setSaveMode(false);
- _saveDialog = new GUI::SaveLoadChooser("Save game:", "Save");
+ _saveDialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save"));
_saveDialog->setSaveMode(true);
}
@@ -297,11 +298,11 @@ ConfigDialog::ConfigDialog(bool subtitleControls)
// Add the buttons
//
- new GUI::ButtonWidget(this, "GlobalConfig.Ok", "OK", GUI::kOKCmd, 'O');
- new GUI::ButtonWidget(this, "GlobalConfig.Cancel", "Cancel", GUI::kCloseCmd, 'C');
+ new GUI::ButtonWidget(this, "GlobalConfig.Ok", _("~O~K"), 0, GUI::kOKCmd);
+ new GUI::ButtonWidget(this, "GlobalConfig.Cancel", _("~C~ancel"), 0, GUI::kCloseCmd);
#ifdef SMALL_SCREEN_DEVICE
- new GUI::ButtonWidget(this, "GlobalConfig.Keys", "Keys", kKeysCmd, 'K');
+ new GUI::ButtonWidget(this, "GlobalConfig.Keys", _("~K~eys"), 0, kKeysCmd);
_keysDialog = NULL;
#endif
}
diff --git a/engines/draci/animation.cpp b/engines/draci/animation.cpp
index e46582487c..2bedbe1801 100644
--- a/engines/draci/animation.cpp
+++ b/engines/draci/animation.cpp
@@ -166,10 +166,10 @@ void Animation::drawFrame(Surface *surface) {
const SoundSample *sample = _samples[_currentFrame];
if (_hasChangedFrame && sample) {
+ uint duration = _vm->_sound->playSound(sample, Audio::Mixer::kMaxChannelVolume, false);
debugC(3, kDraciSoundDebugLevel,
- "Playing sample on animation %d, frame %d: %d+%d at %dHz",
- _id, _currentFrame, sample->_offset, sample->_length, sample->_frequency);
- _vm->_sound->playSound(sample, Audio::Mixer::kMaxChannelVolume, false);
+ "Playing sample on animation %d, frame %d: %d+%d at %dHz: %dms",
+ _id, _currentFrame, sample->_offset, sample->_length, sample->_frequency, duration);
}
_hasChangedFrame = false;
}
diff --git a/engines/draci/barchive.cpp b/engines/draci/barchive.cpp
index 2ed2a9b591..8f9e836ba3 100644
--- a/engines/draci/barchive.cpp
+++ b/engines/draci/barchive.cpp
@@ -283,8 +283,7 @@ BAFile *BArchive::loadFileBAR(uint i) {
tmp ^= _files[i]._data[j];
}
- debugC(3, kDraciArchiverDebugLevel, "Cached file %d from archive %s",
- i, _path.c_str());
+ debugC(2, kDraciArchiverDebugLevel, "Read %d bytes", _files[i]._length);
assert(tmp == _files[i]._crc && "CRC checksum mismatch");
return _files + i;
@@ -385,7 +384,7 @@ const BAFile *BArchive::getFile(uint i) {
// Check if file has already been opened and return that
if (_files[i]._data) {
- debugC(2, kDraciArchiverDebugLevel, "Success");
+ debugC(2, kDraciArchiverDebugLevel, "Cached");
return _files + i;
}
diff --git a/engines/draci/detection.cpp b/engines/draci/detection.cpp
index c3204fc656..e1025e698a 100644
--- a/engines/draci/detection.cpp
+++ b/engines/draci/detection.cpp
@@ -94,7 +94,11 @@ const ADParams detectionParams = {
// Flags
0,
// Global GUI options
- Common::GUIO_NONE
+ Common::GUIO_NONE,
+ // Maximum directory depth
+ 1,
+ // List of directory globs
+ 0
};
class DraciMetaEngine : public AdvancedMetaEngine {
diff --git a/engines/draci/draci.cpp b/engines/draci/draci.cpp
index cbf878279b..cd3920b30d 100644
--- a/engines/draci/draci.cpp
+++ b/engines/draci/draci.cpp
@@ -71,7 +71,7 @@ const char *dubbingPath = "CD.SAM";
const char *musicPathMask = "HUDBA%d.MID";
const uint kSoundsFrequency = 13000;
-const uint kDubbingFrequency = 22000;
+const uint kDubbingFrequency = 22050;
DraciEngine::DraciEngine(OSystem *syst, const ADGameDescription *gameDesc)
: Engine(syst) {
@@ -105,6 +105,39 @@ bool DraciEngine::hasFeature(EngineFeature f) const {
(f == kSupportsSavingDuringRuntime);
}
+static SoundArchive* openAnyPossibleDubbing() {
+ debugC(1, kDraciGeneralDebugLevel, "Trying to find original dubbing");
+ LegacySoundArchive *legacy = new LegacySoundArchive(dubbingPath, kDubbingFrequency);
+ if (legacy->isOpen() && legacy->size()) {
+ debugC(1, kDraciGeneralDebugLevel, "Found original dubbing");
+ return legacy;
+ }
+ delete legacy;
+
+ // The original uncompressed dubbing cannot be found. Try to open the
+ // newer compressed version.
+ debugC(1, kDraciGeneralDebugLevel, "Trying to find compressed dubbing");
+ ZipSoundArchive *zip = new ZipSoundArchive();
+
+ zip->openArchive("dub-raw.zzz", "buf", RAW80, kDubbingFrequency);
+ if (zip->isOpen() && zip->size()) return zip;
+#ifdef USE_FLAC
+ zip->openArchive("dub-flac.zzz", "flac", FLAC);
+ if (zip->isOpen() && zip->size()) return zip;
+#endif
+#ifdef USE_VORBIS
+ zip->openArchive("dub-ogg.zzz", "ogg", OGG);
+ if (zip->isOpen() && zip->size()) return zip;
+#endif
+#ifdef USE_MAD
+ zip->openArchive("dub-mp3.zzz", "mp3", MP3);
+ if (zip->isOpen() && zip->size()) return zip;
+#endif
+
+ // Return an empty (but initialized) archive anyway.
+ return zip;
+}
+
int DraciEngine::init() {
// Initialize graphics using following:
initGraphics(kScreenWidth, kScreenHeight, false);
@@ -123,15 +156,15 @@ int DraciEngine::init() {
_itemImagesArchive = new BArchive(itemImagesPath);
_stringsArchive = new BArchive(stringsPath);
- _soundsArchive = new SoundArchive(soundsPath, kSoundsFrequency);
- _dubbingArchive = new SoundArchive(dubbingPath, kDubbingFrequency);
+ _soundsArchive = new LegacySoundArchive(soundsPath, kSoundsFrequency);
+ _dubbingArchive = openAnyPossibleDubbing();
_sound = new Sound(_mixer);
- MidiDriverType midiDriver = MidiDriver::detectMusicDriver(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MIDI);
- bool native_mt32 = ((midiDriver == MD_MT32) || ConfMan.getBool("native_mt32"));
- //bool adlib = (midiDriver == MD_ADLIB);
+ MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
+ bool native_mt32 = ((MidiDriver::getMusicType(dev) == MT_MT32) || ConfMan.getBool("native_mt32"));
+ //bool adlib = (MidiDriver::getMusicType(dev) == MT_ADLIB);
- _midiDriver = MidiDriver::createMidi(midiDriver);
+ _midiDriver = MidiDriver::createMidi(dev);
if (native_mt32)
_midiDriver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
@@ -253,19 +286,20 @@ void DraciEngine::handleEvents() {
if (escRoom >= 0) {
// Schedule room change
- // TODO: gate 0 (always present) is not
- // always best for returning from the
- // map, e.g. in the starting location.
- // also, after loading the game, we
- // shouldn't run any gate program, but
- // rather restore the state of all
- // objects.
+ // TODO: gate 0 (always present) is not always best for
+ // returning from the map, e.g. in the starting location.
+ // also, after loading the game, we shouldn't run any gate
+ // program, but rather restore the state of all objects.
_game->scheduleEnteringRoomUsingGate(escRoom, 0);
- // Immediately cancel any running animation or dubbing.
+ // Immediately cancel any running animation or dubbing and
+ // end any currently running GPL programs. In the intro it
+ // works as intended---skipping the rest of it.
+ //
+ // In the map, this causes that animation on newly
+ // discovered locations will be re-run next time and
+ // cut-scenes won't be played.
_game->setExitLoop(true);
-
- // End any currently running GPL programs
_script->endCurrentProgram(true);
}
break;
@@ -301,6 +335,16 @@ void DraciEngine::handleEvents() {
openMainMenuDialog();
}
break;
+ case Common::KEYCODE_COMMA:
+ case Common::KEYCODE_PERIOD:
+ case Common::KEYCODE_SLASH:
+ if ((_game->getLoopStatus() == kStatusOrdinary ||
+ _game->getLoopStatus() == kStatusInventory) &&
+ _game->getLoopSubstatus() == kOuterLoop &&
+ _game->getRoomNum() != _game->getMapRoom()) {
+ _game->inventorySwitch(event.kbd.keycode);
+ }
+ break;
default:
break;
}
diff --git a/engines/draci/game.cpp b/engines/draci/game.cpp
index 0e4b3386ec..8f3ad12cfd 100644
--- a/engines/draci/game.cpp
+++ b/engines/draci/game.cpp
@@ -23,6 +23,7 @@
*
*/
+#include "common/keyboard.h"
#include "common/serializer.h"
#include "common/stream.h"
#include "common/system.h"
@@ -174,8 +175,20 @@ void Game::start() {
// Call the outer loop doing all the hard job.
loop(kOuterLoop, false);
- }
+ // Fade out the palette after leaving the location.
+ fadePalette(true);
+
+ if (!isReloaded()) {
+ // We are changing location. Run the hero's LOOK
+ // program to trigger a possible cut-scene. This is
+ // the behavior of the original game player, whose
+ // intention was to run the cut sequences after the
+ // certain location change.
+ const GameObject *dragon = getObject(kDragonObject);
+ _vm->_script->run(dragon->_program, dragon->_look);
+ }
+ }
}
void Game::init() {
@@ -193,7 +206,8 @@ void Game::init() {
_animUnderCursor = NULL;
_currentItem = _itemUnderCursor = NULL;
-
+ _previousItemPosition = -1;
+
_vm->_mouse->setCursorType(kHighlightedCursor); // anything different from kNormalCursor
_objUnderCursor = NULL;
@@ -263,8 +277,8 @@ void Game::handleOrdinaryLoop(int x, int y) {
if (_vm->_mouse->lButtonPressed()) {
_vm->_mouse->lButtonSet(false);
- if (_currentItem) {
- putItem(_currentItem, 0);
+ if (getCurrentItem()) {
+ putItem(getCurrentItem(), getPreviousItemPosition());
updateOrdinaryCursor();
} else {
if (_objUnderCursor) {
@@ -318,6 +332,16 @@ void Game::handleOrdinaryLoop(int x, int y) {
}
}
+int Game::inventoryPositionFromMouse() const {
+ const int column = CLIP(scummvm_lround(
+ (_vm->_mouse->getPosX() - kInventoryX + kInventoryItemWidth / 2.) /
+ kInventoryItemWidth) - 1, 0L, (long) kInventoryColumns - 1);
+ const int line = CLIP(scummvm_lround(
+ (_vm->_mouse->getPosY() - kInventoryY + kInventoryItemHeight / 2.) /
+ kInventoryItemHeight) - 1, 0L, (long) kInventoryLines - 1);
+ return line * kInventoryColumns + column;
+}
+
void Game::handleInventoryLoop() {
if (_loopSubstatus != kOuterLoop) {
return;
@@ -344,19 +368,12 @@ void Game::handleInventoryLoop() {
// If there is an inventory item under the cursor and we aren't
// holding any item, run its look GPL program
- if (_itemUnderCursor && !_currentItem) {
+ if (_itemUnderCursor && !getCurrentItem()) {
_vm->_script->runWrapper(_itemUnderCursor->_program, _itemUnderCursor->_look, true, false);
// Otherwise, if we are holding an item, try to place it inside the
// inventory
- } else if (_currentItem) {
- const int column = CLIP(scummvm_lround(
- (_vm->_mouse->getPosX() - kInventoryX + kInventoryItemWidth / 2.) /
- kInventoryItemWidth) - 1, 0L, (long) kInventoryColumns - 1);
- const int line = CLIP(scummvm_lround(
- (_vm->_mouse->getPosY() - kInventoryY + kInventoryItemHeight / 2.) /
- kInventoryItemHeight) - 1, 0L, (long) kInventoryLines - 1);
- const int index = line * kInventoryColumns + column;
- putItem(_currentItem, index);
+ } else if (getCurrentItem()) {
+ putItem(getCurrentItem(), inventoryPositionFromMouse());
updateInventoryCursor();
}
} else if (_vm->_mouse->rButtonPressed()) {
@@ -372,8 +389,9 @@ void Game::handleInventoryLoop() {
// The first is that there is no item in our hands.
// In that case, just take the inventory item from the inventory.
- if (!_currentItem) {
- _currentItem = _itemUnderCursor;
+ if (!getCurrentItem()) {
+ setCurrentItem(_itemUnderCursor);
+ setPreviousItemPosition(inventoryPositionFromMouse());
removeItem(_itemUnderCursor);
// The second is that there *is* an item in our hands.
@@ -413,6 +431,22 @@ void Game::handleDialogueLoop() {
}
}
+void Game::fadePalette(bool fading_out) {
+ const byte *startPal = NULL;
+ const byte *endPal = _currentRoom._palette >= 0
+ ? _vm->_paletteArchive->getFile(_currentRoom._palette)->_data
+ : NULL;
+ if (fading_out) {
+ startPal = endPal;
+ endPal = NULL;
+ }
+ for (int i = 1; i <= kBlackFadingIterations; ++i) {
+ _vm->_system->delayMillis(kBlackFadingTimeUnit);
+ _vm->_screen->interpolatePalettes(startPal, endPal, 0, kNumColours, i, kBlackFadingIterations);
+ _vm->_screen->copyToScreen();
+ }
+}
+
void Game::advanceAnimationsAndTestLoopExit() {
// Fade the palette if requested
if (_fadePhase > 0 && (_vm->_system->getMillis() - _fadeTick) >= kFadingTimeUnit) {
@@ -470,7 +504,7 @@ void Game::advanceAnimationsAndTestLoopExit() {
// callbacks) and redraw screen
_vm->_anims->drawScene(_vm->_screen->getSurface());
_vm->_screen->copyToScreen();
- _vm->_system->delayMillis(20);
+ _vm->_system->delayMillis(kTimeUnit);
// If the hero has arrived at his destination, after even the last
// phase was correctly animated, run the callback.
@@ -614,10 +648,10 @@ void Game::updateOrdinaryCursor() {
// If there is no game object under the cursor, try using the room itself
if (!_objUnderCursor) {
if (_vm->_script->testExpression(_currentRoom._program, _currentRoom._canUse)) {
- if (!_currentItem) {
+ if (!getCurrentItem()) {
_vm->_mouse->setCursorType(kHighlightedCursor);
} else {
- _vm->_mouse->loadItemCursor(_currentItem, true);
+ _vm->_mouse->loadItemCursor(getCurrentItem(), true);
}
mouseChanged = true;
}
@@ -628,10 +662,10 @@ void Game::updateOrdinaryCursor() {
// update the cursor image (highlight it).
if (_objUnderCursor->_walkDir == 0) {
if (_vm->_script->testExpression(_objUnderCursor->_program, _objUnderCursor->_canUse)) {
- if (!_currentItem) {
+ if (!getCurrentItem()) {
_vm->_mouse->setCursorType(kHighlightedCursor);
} else {
- _vm->_mouse->loadItemCursor(_currentItem, true);
+ _vm->_mouse->loadItemCursor(getCurrentItem(), true);
}
mouseChanged = true;
}
@@ -645,10 +679,10 @@ void Game::updateOrdinaryCursor() {
// Load the appropriate cursor (item image if an item is held or ordinary cursor
// if not)
if (!mouseChanged) {
- if (!_currentItem) {
+ if (!getCurrentItem()) {
_vm->_mouse->setCursorType(kNormalCursor);
} else {
- _vm->_mouse->loadItemCursor(_currentItem, false);
+ _vm->_mouse->loadItemCursor(getCurrentItem(), false);
}
}
}
@@ -659,19 +693,19 @@ void Game::updateInventoryCursor() {
if (_itemUnderCursor) {
if (_vm->_script->testExpression(_itemUnderCursor->_program, _itemUnderCursor->_canUse)) {
- if (!_currentItem) {
+ if (!getCurrentItem()) {
_vm->_mouse->setCursorType(kHighlightedCursor);
} else {
- _vm->_mouse->loadItemCursor(_currentItem, true);
+ _vm->_mouse->loadItemCursor(getCurrentItem(), true);
}
mouseChanged = true;
}
}
if (!mouseChanged) {
- if (!_currentItem) {
+ if (!getCurrentItem()) {
_vm->_mouse->setCursorType(kNormalCursor);
} else {
- _vm->_mouse->loadItemCursor(_currentItem, false);
+ _vm->_mouse->loadItemCursor(getCurrentItem(), false);
}
}
}
@@ -723,6 +757,8 @@ const GameObject *Game::getObjectWithAnimation(const Animation *anim) const {
}
void Game::removeItem(GameItem *item) {
+ if (!item)
+ return;
for (uint i = 0; i < kInventorySlots; ++i) {
if (_inventory[i] == item) {
_inventory[i] = NULL;
@@ -744,7 +780,7 @@ void Game::loadItemAnimation(GameItem *item) {
void Game::putItem(GameItem *item, int position) {
// Empty our hands
- _currentItem = NULL;
+ setCurrentItem(NULL);
if (!item)
return;
@@ -758,6 +794,7 @@ void Game::putItem(GameItem *item, int position) {
break;
}
}
+ setPreviousItemPosition(position);
const int line = position / kInventoryColumns + 1;
const int column = position % kInventoryColumns + 1;
@@ -845,6 +882,55 @@ void Game::inventoryReload() {
for (uint i = 0; i < kInventorySlots; ++i) {
putItem(_inventory[i], i);
}
+ setPreviousItemPosition(0);
+}
+
+void Game::inventorySwitch(int keycode) {
+ switch (keycode) {
+ case Common::KEYCODE_SLASH:
+ // Switch between holding an item and the ordinary mouse cursor.
+ if (!getCurrentItem()) {
+ if (getPreviousItemPosition() >= 0) {
+ GameItem* last_item = _inventory[getPreviousItemPosition()];
+ setCurrentItem(last_item);
+ removeItem(last_item);
+ }
+ } else {
+ putItem(getCurrentItem(), getPreviousItemPosition());
+ }
+ break;
+ case Common::KEYCODE_COMMA:
+ case Common::KEYCODE_PERIOD:
+ // Iterate between the items in the inventory.
+ if (getCurrentItem()) {
+ assert(getPreviousItemPosition() >= 0);
+ int direction = keycode == Common::KEYCODE_PERIOD ? +1 : -1;
+ // Find the next available item.
+ int pos = getPreviousItemPosition() + direction;
+ while (true) {
+ if (pos < 0)
+ pos += kInventorySlots;
+ else if (pos >= kInventorySlots)
+ pos -= kInventorySlots;
+ if (pos == getPreviousItemPosition() || _inventory[pos]) {
+ break;
+ }
+ pos += direction;
+ }
+ // Swap it with the current item.
+ putItem(getCurrentItem(), getPreviousItemPosition());
+ GameItem* new_item = _inventory[pos];
+ setCurrentItem(new_item);
+ setPreviousItemPosition(pos);
+ removeItem(new_item);
+ }
+ break;
+ }
+ if (getLoopStatus() == kStatusOrdinary) {
+ updateOrdinaryCursor();
+ } else {
+ updateInventoryCursor();
+ }
}
void Game::dialogueMenu(int dialogueID) {
@@ -1297,10 +1383,11 @@ void Game::enterNewRoom() {
loadRoomObjects();
loadOverlays();
- // Set room palette
- const BAFile *f;
- f = _vm->_paletteArchive->getFile(_currentRoom._palette);
- _vm->_screen->setPalette(f->_data, 0, kNumColours);
+ // Draw the scene with the black palette and slowly fade into the right palette.
+ _vm->_screen->setPalette(NULL, 0, kNumColours);
+ _vm->_anims->drawScene(_vm->_screen->getSurface());
+ _vm->_screen->copyToScreen();
+ fadePalette(false);
// Run the program for the gate the dragon came through
debugC(6, kDraciLogicDebugLevel, "Running program for gate %d", _newGate);
diff --git a/engines/draci/game.h b/engines/draci/game.h
index 3d02489da7..21baaed5cc 100644
--- a/engines/draci/game.h
+++ b/engines/draci/game.h
@@ -65,9 +65,16 @@ enum SpeechConstants {
kStandardSpeed = 60
};
-// One fading phase is 50ms.
enum FadeConstants {
- kFadingTimeUnit = 50
+ // One fading phase called from the game scripts is 50ms.
+ kFadingTimeUnit = 50,
+ // Fading in/out when entering/leaving a location takes 15 iterations of (at least) 7ms each.
+ kBlackFadingIterations = 15,
+ kBlackFadingTimeUnit = 7
+};
+
+enum AnimationConstants {
+ kTimeUnit = 20
};
/** Inventory related magical constants */
@@ -255,6 +262,8 @@ public:
GameItem *getItem(int id) { return id >= 0 && id < (int) _info._numItems ? &_items[id] : NULL; }
GameItem *getCurrentItem() const { return _currentItem; }
void setCurrentItem(GameItem *item) { _currentItem = item; }
+ int getPreviousItemPosition() const { return _previousItemPosition; }
+ void setPreviousItemPosition(int pos) { _previousItemPosition = pos; }
void removeItem(GameItem *item);
void loadItemAnimation(GameItem *item);
void putItem(GameItem *item, int position);
@@ -292,6 +301,7 @@ public:
void inventoryDraw();
void inventoryDone();
void inventoryReload();
+ void inventorySwitch(int keycode);
void dialogueMenu(int dialogueID);
int dialogueDraw();
@@ -325,11 +335,13 @@ public:
private:
void updateOrdinaryCursor();
void updateInventoryCursor();
+ int inventoryPositionFromMouse() const;
void handleOrdinaryLoop(int x, int y);
void handleInventoryLoop();
void handleDialogueLoop();
void updateTitle(int x, int y);
void updateCursor();
+ void fadePalette(bool fading_out);
void advanceAnimationsAndTestLoopExit();
void handleStatusChangeByMouse();
@@ -353,6 +365,10 @@ private:
GameItem *_currentItem;
GameItem *_itemUnderCursor;
+ // Last position in the inventory of the item currently in the hands, resp. of the item that
+ // was last in our hands.
+ int _previousItemPosition;
+
GameItem *_inventory[kInventorySlots];
Room _currentRoom;
diff --git a/engines/draci/script.cpp b/engines/draci/script.cpp
index a366740526..7a6a68618d 100644
--- a/engines/draci/script.cpp
+++ b/engines/draci/script.cpp
@@ -553,6 +553,7 @@ void Script::icoStat(const Common::Array<int> &params) {
// arrow leading outside a location), set it to standard.
if (_vm->_game->getCurrentItem() == item) {
_vm->_game->setCurrentItem(NULL);
+ _vm->_game->setPreviousItemPosition(-1);
if (_vm->_mouse->getCursorType() >= kItemCursor) {
_vm->_mouse->setCursorType(kNormalCursor);
}
@@ -561,6 +562,7 @@ void Script::icoStat(const Common::Array<int> &params) {
} else {
_vm->_game->loadItemAnimation(item);
_vm->_game->setCurrentItem(item);
+ _vm->_game->setPreviousItemPosition(0); // next time, try to place the item from the beginning
_vm->_mouse->loadItemCursor(item, false);
}
}
@@ -727,10 +729,10 @@ void Script::talk(const Common::Array<int> &params) {
// Speak the dubbing if possible
uint dubbingDuration = 0;
if (sample) {
- dubbingDuration = (uint) (1000.0 * sample->_length / sample->_frequency + 500.0);
+ dubbingDuration = _vm->_sound->playVoice(sample);
debugC(3, kDraciSoundDebugLevel, "Playing sentence %d: %d+%d with duration %dms",
sentenceID, sample->_offset, sample->_length, dubbingDuration);
- _vm->_sound->playVoice(sample);
+ dubbingDuration += 500;
}
// Record time
@@ -879,7 +881,7 @@ void Script::setPalette(const Common::Array<int> &params) {
}
// Immediately update the palette
_vm->_screen->copyToScreen();
- _vm->_system->delayMillis(20);
+ _vm->_system->delayMillis(kTimeUnit);
}
void Script::quitGame(const Common::Array<int> &params) {
diff --git a/engines/draci/sound.cpp b/engines/draci/sound.cpp
index 4fb2fd6309..c9244d7eac 100644
--- a/engines/draci/sound.cpp
+++ b/engines/draci/sound.cpp
@@ -23,11 +23,13 @@
*
*/
+#include "common/archive.h"
#include "common/config-manager.h"
#include "common/debug.h"
#include "common/file.h"
#include "common/str.h"
#include "common/stream.h"
+#include "common/unzip.h"
#include "draci/sound.h"
#include "draci/draci.h"
@@ -36,21 +38,24 @@
#include "sound/audiostream.h"
#include "sound/mixer.h"
#include "sound/decoders/raw.h"
+#include "sound/decoders/mp3.h"
+#include "sound/decoders/vorbis.h"
+#include "sound/decoders/flac.h"
namespace Draci {
-void SoundArchive::openArchive(const Common::String &path) {
+void LegacySoundArchive::openArchive(const char *path) {
// Close previously opened archive (if any)
closeArchive();
- debugCN(2, kDraciArchiverDebugLevel, "Loading samples %s: ", path.c_str());
+ debugCN(1, kDraciArchiverDebugLevel, "Loading samples %s: ", path);
_f = new Common::File();
_f->open(path);
if (_f->isOpen()) {
- debugC(2, kDraciArchiverDebugLevel, "Success");
+ debugC(1, kDraciArchiverDebugLevel, "Success");
} else {
- debugC(2, kDraciArchiverDebugLevel, "Error");
+ debugC(1, kDraciArchiverDebugLevel, "Error");
delete _f;
_f = NULL;
return;
@@ -60,7 +65,7 @@ void SoundArchive::openArchive(const Common::String &path) {
_path = path;
// Read archive header
- debugC(2, kDraciArchiverDebugLevel, "Loading header");
+ debugC(1, kDraciArchiverDebugLevel, "Loading header");
uint totalLength = _f->readUint32LE();
const uint kMaxSamples = 4095; // The no-sound file is exactly 16K bytes long, so don't fail on short reads
@@ -76,26 +81,25 @@ void SoundArchive::openArchive(const Common::String &path) {
break;
}
if (_sampleCount > 0) {
- debugC(2, kDraciArchiverDebugLevel, "Archive info: %d samples, %d total length",
+ debugC(1, kDraciArchiverDebugLevel, "Archive info: %d samples, %d total length",
_sampleCount, totalLength);
_samples = new SoundSample[_sampleCount];
for (uint i = 0; i < _sampleCount; ++i) {
_samples[i]._offset = sampleStarts[i];
_samples[i]._length = sampleStarts[i+1] - sampleStarts[i];
_samples[i]._frequency = 0; // set in getSample()
- _samples[i]._data = NULL;
}
if (_samples[_sampleCount-1]._offset + _samples[_sampleCount-1]._length != totalLength &&
_samples[_sampleCount-1]._offset + _samples[_sampleCount-1]._length - _samples[0]._offset != totalLength) {
// WORKAROUND: the stored length is stored with the header for sounds and without the hader for dubbing. Crazy.
- debugC(2, kDraciArchiverDebugLevel, "Broken sound archive: %d != %d",
+ debugC(1, kDraciArchiverDebugLevel, "Broken sound archive: %d != %d",
_samples[_sampleCount-1]._offset + _samples[_sampleCount-1]._length,
totalLength);
closeArchive();
return;
}
} else {
- debugC(2, kDraciArchiverDebugLevel, "Archive info: empty");
+ debugC(1, kDraciArchiverDebugLevel, "Archive info: empty");
}
// Indicate that the archive has been successfully opened
@@ -103,12 +107,12 @@ void SoundArchive::openArchive(const Common::String &path) {
}
/**
- * @brief SoundArchive close method
+ * @brief LegacySoundArchive close method
*
* Closes the currently opened archive. It can be called explicitly to
* free up memory.
*/
-void SoundArchive::closeArchive() {
+void LegacySoundArchive::closeArchive() {
clearCache();
delete _f;
_f = NULL;
@@ -123,7 +127,7 @@ void SoundArchive::closeArchive() {
* Clears the cache of the open files inside the archive without closing it.
* If the files are subsequently accessed, they are read from the disk.
*/
-void SoundArchive::clearCache() {
+void LegacySoundArchive::clearCache() {
// Delete all cached data
for (uint i = 0; i < _sampleCount; ++i) {
_samples[i].close();
@@ -137,32 +141,121 @@ void SoundArchive::clearCache() {
*
* Loads individual samples from an archive to memory on demand.
*/
-SoundSample *SoundArchive::getSample(int i, uint freq) {
+SoundSample *LegacySoundArchive::getSample(int i, uint freq) {
// Check whether requested file exists
if (i < 0 || i >= (int) _sampleCount) {
return NULL;
}
debugCN(2, kDraciArchiverDebugLevel, "Accessing sample %d from archive %s... ",
- i, _path.c_str());
+ i, _path);
// Check if file has already been opened and return that
if (_samples[i]._data) {
- debugC(2, kDraciArchiverDebugLevel, "Success");
+ debugC(2, kDraciArchiverDebugLevel, "Cached");
} else {
+ // It would be nice to unify the approach with ZipSoundArchive
+ // and allocate a MemoryReadStream with buffer stored inside it
+ // that playSoundBuffer() would just play. Unfortunately,
+ // streams are not thread-safe and the same sample couldn't
+ // thus be played more than once at the same time (this holds
+ // even if we create a SeekableSubReadStream from it as this
+ // just uses the parent). The only thread-safe solution is to
+ // share a read-only buffer and allocate separate
+ // MemoryReadStream's on top of it.
+ _samples[i]._data = new byte[_samples[i]._length];
+ _samples[i]._format = RAW;
+
// Read in the file (without the file header)
_f->seek(_samples[i]._offset);
- _samples[i]._data = new byte[_samples[i]._length];
_f->read(_samples[i]._data, _samples[i]._length);
- debugC(3, kDraciArchiverDebugLevel, "Cached sample %d from archive %s",
- i, _path.c_str());
+ debugC(2, kDraciArchiverDebugLevel, "Read sample %d from archive %s",
+ i, _path);
}
_samples[i]._frequency = freq ? freq : _defaultFreq;
return _samples + i;
}
+void ZipSoundArchive::openArchive(const char *path, const char *extension, SoundFormat format, int raw_frequency) {
+ closeArchive();
+ if ((format == RAW || format == RAW80) && !raw_frequency) {
+ error("openArchive() expects frequency for RAW data");
+ return;
+ }
+
+ debugCN(1, kDraciArchiverDebugLevel, "Trying to open ZIP archive %s: ", path);
+ _archive = Common::makeZipArchive(path);
+ _path = path;
+ _extension = extension;
+ _format = format;
+ _defaultFreq = raw_frequency;
+
+ if (_archive) {
+ Common::ArchiveMemberList files;
+ _archive->listMembers(files);
+ _sampleCount = files.size();
+ debugC(1, kDraciArchiverDebugLevel, "Capacity %d", _sampleCount);
+ } else {
+ debugC(1, kDraciArchiverDebugLevel, "Failed");
+ }
+}
+
+void ZipSoundArchive::closeArchive() {
+ clearCache();
+ delete _archive;
+ _archive = NULL;
+ _path = _extension = NULL;
+ _sampleCount = _defaultFreq = 0;
+ _format = RAW;
+}
+
+void ZipSoundArchive::clearCache() {
+ // Just deallocate the link-list of (very short) headers for each
+ // dubbed sentence played in the current location. If the callers have
+ // not called .close() on any of the items, call them now.
+ for (Common::List<SoundSample>::iterator it = _cache.begin(); it != _cache.end(); ++it) {
+ it->close();
+ }
+ _cache.clear();
+}
+
+SoundSample *ZipSoundArchive::getSample(int i, uint freq) {
+ if (i < 0 || i >= (int) _sampleCount) {
+ return NULL;
+ }
+ debugCN(2, kDraciArchiverDebugLevel, "Accessing sample %d.%s from archive %s (format %d@%d, capacity %d): ",
+ i, _extension, _path, static_cast<int> (_format), _defaultFreq, _sampleCount);
+ if (freq != 0 && (_format != RAW && _format != RAW80)) {
+ error("Cannot resample a sound in compressed format");
+ return NULL;
+ }
+
+ // We cannot really cache anything, because createReadStreamForMember()
+ // returns the data as a ReadStream, which is not thread-safe. We thus
+ // read it again each time even if it has possibly been already cached
+ // a while ago. This is not such a problem for dubbing as for regular
+ // sound samples.
+ SoundSample sample;
+ sample._frequency = freq ? freq : _defaultFreq;
+ sample._format = _format;
+ // Read in the file (without the file header)
+ char file_name[20];
+ sprintf(file_name, "%d.%s", i+1, _extension);
+ sample._stream = _archive->createReadStreamForMember(file_name);
+ if (!sample._stream) {
+ debugC(2, kDraciArchiverDebugLevel, "Doesn't exist");
+ return NULL;
+ } else {
+ debugC(2, kDraciArchiverDebugLevel, "Read");
+ _cache.push_back(sample);
+ // Return a pointer that we own and which we will deallocate
+ // including its contents.
+ return &_cache.back();
+ }
+}
+
Sound::Sound(Audio::Mixer *mixer) : _mixer(mixer), _muteSound(false), _muteVoice(false),
_showSubtitles(true), _talkSpeed(kStandardSpeed) {
@@ -192,28 +285,74 @@ SndHandle *Sound::getHandle() {
return NULL; // for compilers that don't support NORETURN
}
-void Sound::playSoundBuffer(Audio::SoundHandle *handle, const SoundSample &buffer, int volume,
+uint Sound::playSoundBuffer(Audio::SoundHandle *handle, const SoundSample &buffer, int volume,
sndHandleType handleType, bool loop) {
+ if (!buffer._stream && !buffer._data) {
+ warning("Empty stream");
+ return 0;
+ }
+ // Create a new SeekableReadStream which will be automatically disposed
+ // after the sample stops playing. Do not dispose the original
+ // data/stream though.
+ // Beware that if the sample comes from an archive (i.e., is stored in
+ // buffer._stream), then you must NOT play it more than once at the
+ // same time, because streams are not thread-safe. Playing it
+ // repeatedly is OK. Currently this is ensured by that archives are
+ // only used for dubbing, which is only played from one place in
+ // script.cpp, which blocks until the dubbed sentence has finished
+ // playing.
+ Common::SeekableReadStream* stream;
+ const int skip = buffer._format == RAW80 ? 80 : 0;
+ if (buffer._stream) {
+ stream = new Common::SeekableSubReadStream(
+ buffer._stream, skip, buffer._stream->size() /* end */, DisposeAfterUse::NO);
+ } else {
+ stream = new Common::MemoryReadStream(
+ buffer._data + skip, buffer._length - skip /* length */, DisposeAfterUse::NO);
+ }
- byte flags = Audio::FLAG_UNSIGNED;
+ Audio::SeekableAudioStream *reader = NULL;
+ switch (buffer._format) {
+ case RAW:
+ case RAW80:
+ reader = Audio::makeRawStream(stream, buffer._frequency, Audio::FLAG_UNSIGNED, DisposeAfterUse::YES);
+ break;
+#ifdef USE_MAD
+ case MP3:
+ reader = Audio::makeMP3Stream(stream, DisposeAfterUse::YES);
+ break;
+#endif
+#ifdef USE_VORBIS
+ case OGG:
+ reader = Audio::makeVorbisStream(stream, DisposeAfterUse::YES);
+ break;
+#endif
+#ifdef USE_FLAC
+ case FLAC:
+ reader = Audio::makeFLACStream(stream, DisposeAfterUse::YES);
+ break;
+#endif
+ default:
+ error("Unsupported compression format %d", static_cast<int> (buffer._format));
+ delete stream;
+ return 0;
+ }
+ const uint length = reader->getLength().msecs();
const Audio::Mixer::SoundType soundType = (handleType == kVoiceHandle) ?
Audio::Mixer::kSpeechSoundType : Audio::Mixer::kSFXSoundType;
-
- // Don't use DisposeAfterUse::YES, because our caching system deletes samples by itself.
- Audio::AudioStream *stream = Audio::makeLoopingAudioStream(
- Audio::makeRawStream(buffer._data, buffer._length, buffer._frequency, flags, DisposeAfterUse::NO),
- loop ? 0 : 1);
- _mixer->playStream(soundType, handle, stream, -1, volume);
+ Audio::AudioStream *audio_stream = Audio::makeLoopingAudioStream(reader, loop ? 0 : 1);
+ _mixer->playStream(soundType, handle, audio_stream, -1, volume);
+ return length;
}
-void Sound::playSound(const SoundSample *buffer, int volume, bool loop) {
+uint Sound::playSound(const SoundSample *buffer, int volume, bool loop) {
if (!buffer || _muteSound)
- return;
+ return 0;
SndHandle *handle = getHandle();
handle->type = kEffectHandle;
- playSoundBuffer(&handle->handle, *buffer, 2 * volume, handle->type, loop);
+ return playSoundBuffer(&handle->handle, *buffer, 2 * volume, handle->type, loop);
}
void Sound::pauseSound() {
@@ -237,13 +376,13 @@ void Sound::stopSound() {
}
}
-void Sound::playVoice(const SoundSample *buffer) {
+uint Sound::playVoice(const SoundSample *buffer) {
if (!buffer || _muteVoice)
- return;
+ return 0;
SndHandle *handle = getHandle();
handle->type = kVoiceHandle;
- playSoundBuffer(&handle->handle, *buffer, Audio::Mixer::kMaxChannelVolume, handle->type, false);
+ return playSoundBuffer(&handle->handle, *buffer, Audio::Mixer::kMaxChannelVolume, handle->type, false);
}
void Sound::pauseVoice() {
diff --git a/engines/draci/sound.h b/engines/draci/sound.h
index 28379b5429..6e9aae1b6e 100644
--- a/engines/draci/sound.h
+++ b/engines/draci/sound.h
@@ -28,50 +28,101 @@
#include "common/str.h"
#include "common/file.h"
+#include "common/list.h"
#include "sound/mixer.h"
+namespace Common {
+class Archive;
+class SeekableReadStream;
+}
+
namespace Draci {
+enum SoundFormat { RAW, RAW80, MP3, OGG, FLAC }; // RAW80 means skip the first 80 bytes
+
/**
* Represents individual files inside the archive.
*/
struct SoundSample {
- uint _offset;
+ uint _offset; // For internal use of LegacySoundArchive
uint _length;
- uint _frequency;
- byte* _data;
+ uint _frequency; // Only when _format == RAW or RAW80
+ SoundFormat _format;
+
+ byte *_data; // At most one of these two pointer can be non-NULL
+ Common::SeekableReadStream* _stream;
+
+ SoundSample() : _offset(0), _length(0), _frequency(0), _format(RAW), _data(NULL), _stream(NULL) { }
+ // The standard copy constructor is good enough, since we only store numbers and pointers.
+ // Don't call close() automaticall in the destructor, otherwise copying causes SIGSEGV.
void close() {
delete[] _data;
+ delete _stream;
_data = NULL;
+ _stream = NULL;
}
};
+/**
+ * An abstract wrapper around archives of sound samples or dubbing.
+ */
class SoundArchive {
public:
- SoundArchive(const Common::String &path, uint defaultFreq) :
- _path(), _samples(NULL), _sampleCount(0), _defaultFreq(defaultFreq), _opened(false), _f(NULL) {
- openArchive(path);
- }
+ SoundArchive() { }
+ virtual ~SoundArchive() { }
- ~SoundArchive() { closeArchive(); }
-
- void closeArchive();
- void openArchive(const Common::String &path);
- uint size() const { return _sampleCount; }
+ /**
+ * Returns the number of sound samples in the archive. Zero means that
+ * a fake empty archive has been opened and the caller may consider
+ * opening a different one, for example with compressed music.
+ */
+ virtual uint size() const = 0;
/**
* Checks whether there is an archive opened. Should be called before reading
- * from the archive to check whether openArchive() succeeded.
+ * from the archive to check whether opening of the archive has succeeded.
+ */
+ virtual bool isOpen() const = 0;
+
+ /**
+ * Removes cached samples from memory.
*/
- bool isOpen() const { return _opened; }
+ virtual void clearCache() = 0;
+
+ /**
+ * Caches a given sample into memory and returns a pointer into it. We
+ * own the returned pointer, but the user may call .close() on it,
+ * which deallocates the memory of the actual sample data. If freq is
+ * nonzero, then the sample is played at a different frequency (only
+ * works for uncompressed samples).
+ */
+ virtual SoundSample *getSample(int i, uint freq) = 0;
+};
- void clearCache();
+/**
+ * Reads CD.SAM (with dubbing) and CD2.SAM (with sound samples) from the
+ * original game. Caches all read samples in a thread-safe manner.
+ */
+class LegacySoundArchive : public SoundArchive {
+public:
+ LegacySoundArchive(const char *path, uint defaultFreq) :
+ _path(NULL), _samples(NULL), _sampleCount(0), _defaultFreq(defaultFreq), _opened(false), _f(NULL) {
+ openArchive(path);
+ }
+ virtual ~LegacySoundArchive() { closeArchive(); }
- SoundSample *getSample(int i, uint freq);
+ void openArchive(const char *path);
+ void closeArchive();
+
+ virtual uint size() const { return _sampleCount; }
+ virtual bool isOpen() const { return _opened; }
+
+ virtual void clearCache();
+ virtual SoundSample *getSample(int i, uint freq);
private:
- Common::String _path; ///< Path to file
+ const char *_path; ///< Path to file
SoundSample *_samples; ///< Internal array of files
uint _sampleCount; ///< Number of files in archive
uint _defaultFreq; ///< The default sampling frequency of the archived samples
@@ -79,6 +130,44 @@ private:
Common::File *_f; ///< Opened file
};
+/**
+ * Reads ZIP archives with uncompressed files containing lossy-compressed
+ * samples in arbitrary format. Doesn't do any real caching and is
+ * thread-safe.
+ */
+class ZipSoundArchive : public SoundArchive {
+public:
+ ZipSoundArchive() : _archive(NULL), _path(NULL), _extension(NULL), _format(RAW), _sampleCount(0), _defaultFreq(0), _cache() { }
+ virtual ~ZipSoundArchive() { closeArchive(); }
+
+ void openArchive(const char *path, const char *extension, SoundFormat format, int raw_frequency = 0);
+ void closeArchive();
+
+ virtual uint size() const { return _sampleCount; }
+ virtual bool isOpen() const { return _archive != NULL; }
+
+ virtual void clearCache();
+ virtual SoundSample *getSample(int i, uint freq);
+
+private:
+ Common::Archive *_archive;
+ const char *_path;
+ const char *_extension;
+ SoundFormat _format;
+ uint _sampleCount;
+ uint _defaultFreq;
+
+ // Since we typically play at most 1 dubbing at a time, we could get
+ // away with having just 1 record allocated and reusing it each time.
+ // However, that would be thread-unsafe if two samples were played.
+ // Although the player does not do that, it is nicer to allow for it in
+ // principle. For each dubbed sentence, we allocate a new record in
+ // the following link-list, which is cleared during each location
+ // change. The dubbed samples themselves are manually deallocated
+ // after they end.
+ Common::List<SoundSample> _cache;
+};
+
#define SOUND_HANDLES 10
enum sndHandleType {
@@ -100,13 +189,13 @@ public:
Sound(Audio::Mixer *mixer);
~Sound() {}
- void playSound(const SoundSample *buffer, int volume, bool loop);
+ uint playSound(const SoundSample *buffer, int volume, bool loop);
void pauseSound();
void resumeSound();
void stopSound();
bool isMutedSound() const { return _muteSound; }
- void playVoice(const SoundSample *buffer);
+ uint playVoice(const SoundSample *buffer);
void pauseVoice();
void resumeVoice();
void stopVoice();
@@ -120,7 +209,8 @@ public:
int talkSpeed() const { return _talkSpeed; }
private:
- void playSoundBuffer(Audio::SoundHandle *handle, const SoundSample &buffer, int volume,
+ // Returns the length of the sound sample in miliseconds.
+ uint playSoundBuffer(Audio::SoundHandle *handle, const SoundSample &buffer, int volume,
sndHandleType handleType, bool loop);
SndHandle *getHandle();
diff --git a/engines/drascula/actors.cpp b/engines/drascula/actors.cpp
index ff46d8201a..33cb7fd478 100644
--- a/engines/drascula/actors.cpp
+++ b/engines/drascula/actors.cpp
@@ -77,6 +77,7 @@ void DrasculaEngine::hiccup(int counter) {
do {
counter--;
+ updateEvents();
updateRoom();
if (currentChapter == 3)
updateScreen(0, 0, 0, y, 320, 200, screenSurface);
@@ -99,6 +100,7 @@ void DrasculaEngine::hiccup(int counter) {
if (y == 0)
trackCharacter = 0;
}
+ pause(3);
} while (counter > 0);
updateRoom();
@@ -449,6 +451,7 @@ void DrasculaEngine::placeVonBraun(int pointX) {
vonBraunHasMoved = 1;
for (;;) {
+ updateEvents();
updateRoom();
updateScreen();
if (trackVonBraun == 0) {
diff --git a/engines/drascula/animation.cpp b/engines/drascula/animation.cpp
index e4bd844d75..d6a3bafd9f 100644
--- a/engines/drascula/animation.cpp
+++ b/engines/drascula/animation.cpp
@@ -32,17 +32,23 @@ void DrasculaEngine::updateAnim(int y, int destX, int destY, int width, int heig
for (int n = 0; n < count; n++){
x++;
- copyBackground(x, y, destX, destY, width, height, src, screenSurface);
- if (copyRectangle)
+ if (copyRectangle) {
+ copyBackground(destX, destY, destX, destY, width, height, bgSurface, screenSurface);
copyRect(x, y, destX, destY, width, height, src, screenSurface);
+ } else {
+ copyBackground(x, y, destX, destY, width, height, src, screenSurface);
+ }
updateScreen(destX, destY, destX, destY, width, height, screenSurface);
x += width;
+ updateEvents();
pause(delayVal);
}
}
// This is the game's introduction sequence
void DrasculaEngine::animation_1_1() {
+ debug(4, "animation_1_1()");
+
int l, l2, p;
//int pixelPos[6];
@@ -141,9 +147,9 @@ void DrasculaEngine::animation_1_1() {
copyBackground(0, 0, 320 - l, 0, l, 200, drawSurface3, screenSurface);
copyBackground(l, 0, 0, 0, 320 - l, 200, bgSurface, screenSurface);
- copyRect(interf_x[l2], interf_y[l2], 156 - l, 45, 63, 31,
- drawSurface2, screenSurface);
+ copyRect(interf_x[l2], interf_y[l2], 156 - l, 45, 63, 31, drawSurface2, screenSurface);
updateScreen();
+ updateEvents();
p++;
if (p == 6) {
p = 0;
@@ -371,6 +377,8 @@ void DrasculaEngine::animation_1_1() {
// John falls in love with BJ, who is then abducted by Drascula
void DrasculaEngine::animation_2_1() {
+ debug(4, "animation_2_1()");
+
int l;
gotoObject(231, 91);
@@ -560,6 +568,8 @@ void DrasculaEngine::animation_2_1() {
// John Hacker talks with the bartender to book a room
void DrasculaEngine::animation_3_1() {
+ debug(4, "animation_3_1()");
+
loadPic("an11y13.alg", extraSurface);
playTalkSequence(3); // sequence 3, chapter 1
@@ -569,6 +579,8 @@ void DrasculaEngine::animation_3_1() {
// John Hacker talks with the pianist
void DrasculaEngine::animation_4_1() {
+ debug(4, "animation_4_1()");
+
loadPic("an12.alg", extraSurface);
talk(205);
@@ -605,6 +617,8 @@ void DrasculaEngine::animation_4_1() {
}
void DrasculaEngine::animation_2_2() {
+ debug(4, "animation_2_2()");
+
trackProtagonist = 0;
copyBackground();
moveCharacters();
@@ -640,6 +654,8 @@ void DrasculaEngine::animation_2_2() {
}
void DrasculaEngine::animation_4_2() {
+ debug(4, "animation_4_2()");
+
stopMusic();
flags[9] = 1;
@@ -704,6 +720,8 @@ void DrasculaEngine::animation_4_2() {
}
void DrasculaEngine::animation_14_2() {
+ debug(4, "animation_14_2()");
+
int cY = -160;
int l = 0;
@@ -735,6 +753,8 @@ void DrasculaEngine::animation_14_2() {
// The drunk tells us about Von Braun
void DrasculaEngine::animation_16_2() {
+ debug(4, "animation_16_2()");
+
char curPic[20];
talk_drunk(12);
talk(371);
@@ -812,6 +832,8 @@ asco:
}
void DrasculaEngine::animation_20_2() {
+ debug(4, "animation_20_2()");
+
talk_vonBraun(7, kVonBraunDoor);
talk_vonBraun(8, kVonBraunDoor);
talk(383);
@@ -842,6 +864,8 @@ void DrasculaEngine::animation_20_2() {
}
void DrasculaEngine::animation_23_2() {
+ debug(4, "animation_23_2()");
+
loadPic("an24.alg", frontSurface);
flags[21] = 1;
@@ -897,6 +921,8 @@ void DrasculaEngine::animation_23_2() {
}
void DrasculaEngine::animation_23_joined() {
+ debug(4, "animation_23_joined()");
+
int p_x = curX + 2, p_y = curY - 3;
int x[] = {1, 38, 75, 112, 75, 112, 75, 112, 149, 112, 149, 112, 149, 186, 223, 260,
1, 38, 75, 112, 149, 112, 149, 112, 149, 112, 149, 186, 223, 260, 260, 260, 260, 223};
@@ -910,6 +936,7 @@ void DrasculaEngine::animation_23_joined() {
copyRect(x[n], y[n], p_x, p_y, 36, 74, backSurface, screenSurface);
updateRefresh();
updateScreen(p_x, p_y, p_x, p_y, 36, 74, screenSurface);
+ updateEvents();
pause(5);
}
@@ -917,6 +944,8 @@ void DrasculaEngine::animation_23_joined() {
}
void DrasculaEngine::animation_23_joined2() {
+ debug(4, "animation_23_joined2()");
+
int p_x = curX + 4, p_y = curY;
int x[] = {1, 35, 69, 103, 137, 171, 205, 239, 273, 1, 35, 69, 103, 137};
int y[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 73, 73, 73, 73, 73};
@@ -930,6 +959,7 @@ void DrasculaEngine::animation_23_joined2() {
copyRect(x[n], y[n], p_x, p_y, 33, 71, backSurface, screenSurface);
updateRefresh();
updateScreen(p_x,p_y, p_x,p_y, 33,71, screenSurface);
+ updateEvents();
pause(5);
}
@@ -937,6 +967,8 @@ void DrasculaEngine::animation_23_joined2() {
}
void DrasculaEngine::animation_25_2() {
+ debug(4, "animation_25_2()");
+
int cY = 0;
loadPic("an14_2.alg", backSurface);
@@ -959,6 +991,7 @@ void DrasculaEngine::animation_25_2() {
updateRefresh();
updateScreen();
+ updateEvents();
}
finishSound();
@@ -967,6 +1000,8 @@ void DrasculaEngine::animation_25_2() {
}
void DrasculaEngine::animation_27_2() {
+ debug(4, "animation_27_2()");
+
flags[22] = 1;
selectVerb(kVerbNone);
@@ -986,6 +1021,8 @@ void DrasculaEngine::animation_27_2() {
}
void DrasculaEngine::animation_29_2() {
+ debug(4, "animation_29_2()");
+
if (flags[33] == 0) {
playTalkSequence(29); // sequence 29, chapter 2
} else
@@ -1002,6 +1039,8 @@ void DrasculaEngine::animation_29_2() {
}
void DrasculaEngine::animation_31_2() {
+ debug(4, "animation_31_2()");
+
talk_vonBraun(44, kVonBraunNormal);
placeVonBraun(-50);
pause(15);
@@ -1020,6 +1059,8 @@ void DrasculaEngine::animation_31_2() {
}
void DrasculaEngine::animation_35_2() {
+ debug(4, "animation_35_2()");
+
gotoObject(96, 165);
gotoObject(79, 165);
@@ -1049,7 +1090,10 @@ void DrasculaEngine::animation_35_2() {
fadeToBlack(2);
}
+// Use cross on Yoda
void DrasculaEngine::animation_2_3() {
+ debug(4, "animation_2_3()");
+
flags[0] = 1;
playMusic(13);
animation_3_3();
@@ -1070,6 +1114,8 @@ void DrasculaEngine::animation_2_3() {
}
void DrasculaEngine::animation_3_3() {
+ debug(4, "animation_3_3()");
+
int px = curX - 20, py = curY - 1;
loadPic("an2y_1.alg", frontSurface);
@@ -1085,6 +1131,8 @@ void DrasculaEngine::animation_3_3() {
}
void DrasculaEngine::animation_4_3() {
+ debug(4, "animation_4_3()");
+
int px = 120, py = 63;
loadPic("any_1.alg", frontSurface);
@@ -1100,6 +1148,8 @@ void DrasculaEngine::animation_4_3() {
}
void DrasculaEngine::animation_5_3() {
+ debug(4, "animation_5_3()");
+
int px = curX - 20, py = curY - 1;
loadPic("an3y_1.alg", frontSurface);
@@ -1115,6 +1165,8 @@ void DrasculaEngine::animation_5_3() {
}
void DrasculaEngine::animation_6_3() {
+ debug(4, "animation_6_3()");
+
int frame = 0, px = 112, py = 62;
int yoda_x[] = { 3 ,82, 161, 240, 3, 82 };
int yoda_y[] = { 3, 3, 3, 3, 94, 94 };
@@ -1133,6 +1185,7 @@ void DrasculaEngine::animation_6_3() {
copyBackground();
copyRect(yoda_x[frame], yoda_y[frame], px, py, 78, 90, frontSurface, screenSurface);
updateScreen(px, py, px, py, 78, 90, screenSurface);
+ updateEvents();
}
flags[2] = 1;
@@ -1144,6 +1197,8 @@ void DrasculaEngine::animation_6_3() {
}
void DrasculaEngine::animation_ray() {
+ debug(4, "animation_ray()");
+
loadPic("anr_1.alg", frontSurface, HALF_PAL);
loadPic("anr_2.alg", extraSurface);
loadPic("anr_3.alg", backSurface);
@@ -1171,6 +1226,8 @@ void DrasculaEngine::animation_ray() {
}
void DrasculaEngine::animation_7_4() {
+ debug(4, "animation_7_4()");
+
black();
talk(427);
fadeFromBlack(1);
@@ -1184,6 +1241,8 @@ void DrasculaEngine::animation_7_4() {
}
void DrasculaEngine::animation_1_5() {
+ debug(4, "animation_1_5()");
+
if (flags[0] == 0) {
talk(430);
talk_bj(16);
@@ -1212,6 +1271,7 @@ void DrasculaEngine::animation_1_5() {
break;
updateRoom();
updateScreen();
+ updateEvents();
}
trackProtagonist = 1;
@@ -1224,6 +1284,8 @@ void DrasculaEngine::animation_1_5() {
}
void DrasculaEngine::animation_5_5(){
+ debug(4, "animation_5_5(");
+
int h;
int frame = 0;
int boneX[] = {1, 99, 197, 1, 99, 197, 1, 99, 197};
@@ -1247,6 +1309,7 @@ void DrasculaEngine::animation_5_5(){
copyBackground();
copyRect(boneX[frame], boneY[frame], pixelX, pixelY, 97, 64, backSurface, screenSurface);
updateScreen(pixelX, pixelY, pixelX,pixelY, 97,64, screenSurface);
+ updateEvents();
}
copyBackground(52, 161, 198, 81, 26, 24, drawSurface3, screenSurface);
@@ -1257,6 +1320,7 @@ void DrasculaEngine::animation_5_5(){
copyBackground();
copyRect(boneX[frame], boneY[frame], pixelX, pixelY, 97, 64, frontSurface, screenSurface);
updateScreen(pixelX, pixelY, pixelX,pixelY, 97, 64, screenSurface);
+ updateEvents();
}
flags[6] = 1;
@@ -1279,11 +1343,13 @@ void DrasculaEngine::animation_5_5(){
pause(3);
copyBackground(flyX[frame], 1, 174, 79, 61, 109, backSurface, screenSurface);
updateScreen(174, 79, 174, 79, 61, 109, screenSurface);
+ updateEvents();
}
for (frame = 0; frame < 5; frame++) {
pause(3);
copyBackground(flyX[frame], 1, 174, 79, 61, 109, extraSurface, screenSurface);
updateScreen(174, 79, 174, 79, 61, 109, screenSurface);
+ updateEvents();
}
updateScreen(0, 0, 0, 0, 320, 200, bgSurface);
@@ -1299,6 +1365,8 @@ void DrasculaEngine::animation_5_5(){
}
void DrasculaEngine::animation_11_5() {
+ debug(4, "animation_11_5()");
+
flags[9] = 1;
if (flags[2] == 1 && flags[3] == 1 && flags[4] == 1)
animation_12_5();
@@ -1309,6 +1377,8 @@ void DrasculaEngine::animation_11_5() {
}
void DrasculaEngine::animation_12_5() {
+ debug(4, "animation_12_5()");
+
DacPalette256 bgPalette1;
DacPalette256 bgPalette2;
DacPalette256 bgPalette3;
@@ -1367,6 +1437,7 @@ void DrasculaEngine::animation_12_5() {
copyRect(rayX[frame], 1, 41, 0, 44, 44, backSurface, screenSurface);
copyRect(frusky_x[frame], 113, 205, 50, 38, 86, drawSurface3, screenSurface);
updateScreen();
+ updateEvents();
}
stopSound();
@@ -1383,6 +1454,7 @@ void DrasculaEngine::animation_12_5() {
updateRoom();
copyRect(elfrusky_x[frame], 47, 192, 39, 66, 106, backSurface, screenSurface);
updateScreen();
+ updateEvents();
}
animate("frel.bin", 16);
@@ -1415,6 +1487,8 @@ void DrasculaEngine::animation_12_5() {
}
void DrasculaEngine::animation_13_5() {
+ debug(4, "animation_13_5()");
+
int frank_x = 199;
int frame = 0;
int frus_x[] = {1, 46, 91, 136, 181, 226, 271};
@@ -1441,11 +1515,14 @@ void DrasculaEngine::animation_13_5() {
frame = 0;
trackProtagonist = 3;
}
+ updateEvents();
pause(6);
}
}
void DrasculaEngine::animation_14_5() {
+ debug(4, "animation_14_5()");
+
flags[11] = 1;
playSound(3);
updateRoom();
@@ -1469,6 +1546,8 @@ void DrasculaEngine::animation_14_5() {
}
void DrasculaEngine::animation_1_6() {
+ debug(4, "animation_1_6()");
+
trackProtagonist = 0;
curX = 103;
curY = 108;
@@ -1497,11 +1576,15 @@ void DrasculaEngine::animation_1_6() {
talk_drascula(28, 1);
talk(255);
talk_drascula(29, 1);
+ updateEvents();
fadeToBlack(1);
+ updateEvents();
clearRoom();
loadPic("time1.alg", screenSurface);
updateScreen();
+ updateEvents();
delay(930);
+ updateEvents();
clearRoom();
black();
hare_se_ve = 0;
@@ -1513,10 +1596,13 @@ void DrasculaEngine::animation_1_6() {
talk_drascula(30, 1);
talk(257);
fadeToBlack(0);
+ updateEvents();
clearRoom();
loadPic("time1.alg", screenSurface);
updateScreen();
+ updateEvents();
delay(900);
+ updateEvents();
clearRoom();
black();
updateRoom();
@@ -1540,6 +1626,8 @@ void DrasculaEngine::animation_1_6() {
}
void DrasculaEngine::animation_5_6() {
+ debug(4, "animation_5_6()");
+
int pY = -125;
animate("man.bin", 14);
@@ -1553,6 +1641,7 @@ void DrasculaEngine::animation_5_6() {
updateRefresh();
updateScreen();
+ updateEvents();
pause(2);
}
@@ -1560,6 +1649,8 @@ void DrasculaEngine::animation_5_6() {
}
void DrasculaEngine::animation_6_6() {
+ debug(4, "animation_6_6()");
+
animate("rct.bin", 11);
clearRoom();
selectVerb(kVerbNone);
@@ -1584,6 +1675,8 @@ void DrasculaEngine::animation_6_6() {
}
void DrasculaEngine::animation_9_6() {
+ debug(4, "animation_9_6()");
+
int v_cd;
animate("fin.bin", 14);
@@ -1660,6 +1753,8 @@ void DrasculaEngine::animation_9_6() {
}
void DrasculaEngine::animation_19_6() {
+ debug(4, "animation_19_6()");
+
copyBackground();
copyBackground(140, 23, 161, 69, 35, 80, drawSurface3, screenSurface);
@@ -1675,6 +1770,8 @@ void DrasculaEngine::animation_19_6() {
}
void DrasculaEngine::animation_12_2() {
+ debug(4, "animation_12_2()");
+
loadPic("an12.alg", extraSurface);
talk(356);
@@ -1705,6 +1802,8 @@ void DrasculaEngine::animation_12_2() {
}
void DrasculaEngine::animation_26_2() {
+ debug(4, "animation_26_2()");
+
loadPic("an12.alg", extraSurface);
talk(392);
@@ -1745,6 +1844,7 @@ void DrasculaEngine::animation_26_2() {
x = x + 50;
if (n == 2)
playSound(9);
+ updateEvents();
pause(3);
}
@@ -1762,6 +1862,8 @@ void DrasculaEngine::animation_26_2() {
}
void DrasculaEngine::animation_11_2() {
+ debug(4, "animation_11_2()");
+
loadPic("an11y13.alg", extraSurface);
playTalkSequence(11); // sequence 11, chapter 2
@@ -1770,6 +1872,8 @@ void DrasculaEngine::animation_11_2() {
}
void DrasculaEngine::animation_13_2() {
+ debug(4, "animation_13_2()");
+
loadPic("an11y13.alg", frontSurface);
if (flags[41] == 0) {
@@ -1780,6 +1884,8 @@ void DrasculaEngine::animation_13_2() {
}
void DrasculaEngine::animation_24_2() {
+ debug(4, "animation_24_2()");
+
if (curX < 178)
gotoObject(208, 136);
trackProtagonist = 3;
@@ -1810,6 +1916,8 @@ void DrasculaEngine::animation_24_2() {
}
void DrasculaEngine::animation_32_2() {
+ debug(4, "animation_32_2()");
+
loadPic("an32_1.alg", drawSurface3);
loadPic("an32_2.alg", backSurface);
@@ -1825,12 +1933,16 @@ void DrasculaEngine::animation_32_2() {
x = x + 65;
if (n < 2)
pause(4);
+
+ updateEvents();
}
loadPic("aux18.alg", drawSurface3);
}
void DrasculaEngine::animation_34_2() {
+ debug(4, "animation_34_2()");
+
trackProtagonist = 1;
updateRoom();
updateScreen();
@@ -1858,6 +1970,8 @@ void DrasculaEngine::animation_34_2() {
}
void DrasculaEngine::animation_36_2() {
+ debug(4, "animation_36_2()");
+
loadPic("an11y13.alg", extraSurface);
talk(404);
@@ -1871,7 +1985,10 @@ void DrasculaEngine::animation_36_2() {
loadPic(974, extraSurface);
}
+// Use sickle on plant
void DrasculaEngine::animation_7_2() {
+ debug(4, "animation_7_2()");
+
loadPic("an7_1.alg", backSurface);
loadPic("an7_2.alg", extraSurface);
loadPic("an7_3.alg", frontSurface);
@@ -1928,6 +2045,8 @@ void DrasculaEngine::animation_7_2() {
}
void DrasculaEngine::animation_5_2() {
+ debug(4, "animation_5_2()");
+
trackProtagonist = 0;
updateRoom();
updateScreen();
@@ -1964,6 +2083,8 @@ void DrasculaEngine::animation_5_2() {
}
void DrasculaEngine::animation_6_2() {
+ debug(4, "animation_6_2()");
+
stopMusic();
flags[9] = 1;
@@ -2005,6 +2126,8 @@ void DrasculaEngine::animation_6_2() {
}
void DrasculaEngine::animation_33_2() {
+ debug(4, "animation_33_2()");
+
stopMusic();
flags[9] = 1;
@@ -2052,6 +2175,8 @@ void DrasculaEngine::animation_33_2() {
}
void DrasculaEngine::animation_1_4() {
+ debug(4, "animation_1_4()");
+
if (flags[21] == 0) {
strcpy(objName[2], "igor");
talk(275);
@@ -2108,6 +2233,8 @@ void DrasculaEngine::animation_1_4() {
}
void DrasculaEngine::animation_5_4(){
+ debug(4, "animation_5_4(");
+
trackProtagonist = 3;
loadPic("anh_dr.alg", backSurface);
gotoObject(99, 160);
@@ -2136,6 +2263,8 @@ void DrasculaEngine::animation_5_4(){
}
void DrasculaEngine::animation_6_4() {
+ debug(4, "animation_6_4()");
+
int prevRoom = roomNumber;
roomNumber = 26;
@@ -2161,6 +2290,8 @@ void DrasculaEngine::animation_6_4() {
}
void DrasculaEngine::animation_8_4() {
+ debug(4, "animation_8_4()");
+
int bookcaseX[] = {1, 75, 149, 223, 1, 75, 149, 223, 149, 223, 149, 223, 149, 223};
int bookcaseY[] = {1, 1, 1, 1, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74};
@@ -2170,6 +2301,7 @@ void DrasculaEngine::animation_8_4() {
pause(2);
copyBackground(bookcaseX[frame], bookcaseY[frame], 77, 45, 73, 72, frontSurface, screenSurface);
updateScreen(77, 45, 77, 45, 73, 72, screenSurface);
+ updateEvents();
}
loadPic(96, frontSurface);
@@ -2177,6 +2309,8 @@ void DrasculaEngine::animation_8_4() {
}
void DrasculaEngine::activatePendulum() {
+ debug(4, "activatePendulum()");
+
flags[1] = 2;
hare_se_ve = 0;
roomNumber = 102;
diff --git a/engines/drascula/console.cpp b/engines/drascula/console.cpp
new file mode 100644
index 0000000000..d017468285
--- /dev/null
+++ b/engines/drascula/console.cpp
@@ -0,0 +1,63 @@
+/* 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 "drascula/console.h"
+#include "gui/debugger.h"
+#include "drascula/drascula.h"
+
+namespace Drascula {
+
+Console::Console(DrasculaEngine *vm) : GUI::Debugger(), _vm(vm) {
+ DCmd_Register("room", WRAP_METHOD(Console, Cmd_Room));
+}
+
+Console::~Console() {
+}
+
+void Console::preEnter() {
+}
+
+void Console::postEnter() {
+}
+
+bool Console::Cmd_Room(int argc, const char **argv) {
+ if (argc < 2) {
+ DebugPrintf("Usage: changeCard <card>\n");
+ return true;
+ }
+
+ int roomNum = atoi(argv[1]);
+
+ _vm->loadedDifferentChapter = 0;
+ _vm->enterRoom(roomNum);
+ _vm->selectVerb(kVerbNone);
+ _vm->clearRoom();
+ _vm->loadPic(roomNum, _vm->bgSurface, HALF_PAL);
+ _vm->selectionMade = 0;
+
+ return false;
+}
+
+} // End of namespace Drascula
diff --git a/engines/sci/graphics/gui32.h b/engines/drascula/console.h
index 99eb03b321..33e2f626e4 100644
--- a/engines/sci/graphics/gui32.h
+++ b/engines/drascula/console.h
@@ -23,46 +23,29 @@
*
*/
-#ifndef SCI_GRAPHICS_GUI32_H
-#define SCI_GRAPHICS_GUI32_H
+#ifndef DRASCULA_CONSOLE_H
+#define DRASCULA_CONSOLE_H
-#include "sci/graphics/helpers.h"
+#include "gui/debugger.h"
-namespace Sci {
+namespace Drascula {
-class GfxCursor;
-class GfxScreen;
-class GfxPalette;
-class GfxCache;
-class GfxCoordAdjuster32;
-class GfxCompare;
-class GfxFrameout;
-class GfxPaint32;
+class DrasculaEngine;
-class SciGui32 {
+class Console : public GUI::Debugger {
public:
- SciGui32(SegManager *segMan, SciEvent *event, GfxScreen *screen, GfxPalette *palette, GfxCache *cache, GfxCursor *cursor);
- ~SciGui32();
-
- void init();
-
- void textSize(const char *text, int16 font, int16 maxWidth, int16 *textWidth, int16 *textHeight);
-
- void drawRobot(GuiResourceId robotId);
+ Console(DrasculaEngine *vm);
+ virtual ~Console(void);
protected:
- GfxCursor *_cursor;
- GfxScreen *_screen;
- GfxPalette *_palette;
- GfxCache *_cache;
- GfxCoordAdjuster32 *_coordAdjuster;
- GfxCompare *_compare;
- GfxFrameout *_frameout;
- GfxPaint32 *_paint32;
+ virtual void preEnter();
+ virtual void postEnter();
private:
-};
+ DrasculaEngine *_vm;
-} // End of namespace Sci
+ bool Cmd_Room(int argc, const char **argv);
+};
+} // End of namespace Drascula
#endif
diff --git a/engines/drascula/converse.cpp b/engines/drascula/converse.cpp
index b2a7e217e6..0e70348148 100644
--- a/engines/drascula/converse.cpp
+++ b/engines/drascula/converse.cpp
@@ -131,6 +131,8 @@ void DrasculaEngine::cleanupString(char *string) {
}
void DrasculaEngine::converse(int index) {
+ debug(4, "converse(%d)", index);
+
char fileName[20];
sprintf(fileName, "op_%d.cal", index);
Common::SeekableReadStream *stream = _archives.open(fileName);
@@ -279,9 +281,15 @@ void DrasculaEngine::converse(int index) {
}
void DrasculaEngine::response(int function) {
- playTalkSequence(function);
+ debug(4, "response(%d)", function);
+
+ if (function != 31)
+ playTalkSequence(function);
if (currentChapter == 2) {
+ if (function == 16 || function == 20 || function == 23 || function == 29 || function == 31)
+ loadPic(menuBackground, backSurface);
+
if (function == 16)
animation_16_2();
else if (function == 20)
diff --git a/engines/drascula/detection.cpp b/engines/drascula/detection.cpp
index 76d48b7b89..c10222cadd 100644
--- a/engines/drascula/detection.cpp
+++ b/engines/drascula/detection.cpp
@@ -264,7 +264,11 @@ static const ADParams detectionParams = {
// Flags
0,
// Additional GUI options (for every game}
- Common::GUIO_NOMIDI
+ Common::GUIO_NOMIDI,
+ // Maximum directory depth
+ 1,
+ // List of directory globs
+ 0
};
class DrasculaMetaEngine : public AdvancedMetaEngine {
diff --git a/engines/drascula/drascula.cpp b/engines/drascula/drascula.cpp
index 39f07e5875..7c87f3574d 100644
--- a/engines/drascula/drascula.cpp
+++ b/engines/drascula/drascula.cpp
@@ -38,6 +38,7 @@
#include "sound/mixer.h"
#include "drascula/drascula.h"
+#include "drascula/console.h"
namespace Drascula {
@@ -115,6 +116,8 @@ DrasculaEngine::~DrasculaEngine() {
freeRoomsTable();
+ delete _console;
+
free(_charMap);
free(_itemLocations);
free(_polX);
@@ -173,6 +176,8 @@ Common::Error DrasculaEngine::run() {
_lang = kEnglish;
}
+ _console = new Console(this);
+
if (!loadDrasculaDat())
return Common::kUnknownError;
@@ -241,6 +246,8 @@ Common::Error DrasculaEngine::run() {
if (currentChapter != 3)
loadPic(96, frontSurface, COMPLETE_PAL);
+ loadPic(99, cursorSurface);
+
if (currentChapter == 1) {
} else if (currentChapter == 2) {
loadPic("pts.alg", drawSurface2);
@@ -272,6 +279,7 @@ Common::Error DrasculaEngine::run() {
loadPic(974, tableSurface);
if (currentChapter != 2) {
+ loadPic(99, cursorSurface);
loadPic(99, backSurface);
loadPic(97, extraSurface);
}
@@ -499,11 +507,15 @@ bool DrasculaEngine::runCurrentChapter() {
#else
if (rightMouseButton == 1 && _menuScreen) {
#endif
+ rightMouseButton = 0;
delay(100);
- if (currentChapter == 2)
+ if (currentChapter == 2) {
+ loadPic(menuBackground, cursorSurface);
loadPic(menuBackground, backSurface);
- else
+ } else {
+ loadPic(99, cursorSurface);
loadPic(99, backSurface);
+ }
setPalette((byte *)&gamePalette);
_menuScreen = false;
#ifndef _WIN32_WCE
@@ -524,18 +536,24 @@ bool DrasculaEngine::runCurrentChapter() {
if (rightMouseButton == 1 && !_menuScreen &&
!(currentChapter == 5 && pickedObject == 16)) {
#endif
+ rightMouseButton = 0;
delay(100);
characterMoved = 0;
if (trackProtagonist == 2)
trackProtagonist = 1;
- if (currentChapter == 4)
+ if (currentChapter == 4) {
loadPic("icons2.alg", backSurface);
- else if (currentChapter == 5)
+ loadPic("icons2.alg", cursorSurface);
+ } else if (currentChapter == 5) {
loadPic("icons3.alg", backSurface);
- else if (currentChapter == 6)
+ loadPic("icons3.alg", cursorSurface);
+ } else if (currentChapter == 6) {
loadPic("iconsp.alg", backSurface);
- else
+ loadPic("iconsp.alg", cursorSurface);
+ } else {
loadPic("icons.alg", backSurface);
+ loadPic("icons.alg", cursorSurface);
+ }
_menuScreen = true;
#ifndef _WIN32_WCE
updateEvents();
@@ -594,6 +612,9 @@ bool DrasculaEngine::runCurrentChapter() {
} else if (key == Common::KEYCODE_ESCAPE) {
if (!confirmExit())
return false;
+ } else if (key == Common::KEYCODE_TILDE || key == Common::KEYCODE_BACKQUOTE) {
+ _console->attach();
+ _console->onFrame();
} else if (currentChapter == 6 && key == Common::KEYCODE_0 && roomNumber == 61) {
loadPic("alcbar.alg", bgSurface, 255);
}
@@ -741,10 +762,10 @@ void DrasculaEngine::updateEvents() {
leftMouseButton = 0;
break;
case Common::EVENT_RBUTTONDOWN:
- rightMouseButton = 1;
+ // We changed semantic and react only on button up event
break;
case Common::EVENT_RBUTTONUP:
- rightMouseButton = 0;
+ rightMouseButton = 1;
break;
case Common::EVENT_QUIT:
// TODO
@@ -758,11 +779,17 @@ void DrasculaEngine::updateEvents() {
}
void DrasculaEngine::delay(int ms) {
- _system->delayMillis(ms * 2); // originally was 1
+ uint32 end = _system->getMillis() + ms * 2; // originally was 1
+
+ do {
+ _system->delayMillis(10);
+ updateEvents();
+ _system->updateScreen();
+ } while (_system->getMillis() < end);
}
void DrasculaEngine::pause(int duration) {
- _system->delayMillis(duration * 30); // was originally 2
+ delay(duration * 15);
}
int DrasculaEngine::getTime() {
diff --git a/engines/drascula/drascula.h b/engines/drascula/drascula.h
index 4876cf3390..0a8b7c8c9b 100644
--- a/engines/drascula/drascula.h
+++ b/engines/drascula/drascula.h
@@ -317,6 +317,8 @@ static const int interf_y[] = { 51, 51, 51, 51, 83, 83, 83 };
struct RoomHandlers;
+class Console;
+
class DrasculaEngine : public ::Engine {
protected:
// Engine APIs
@@ -389,6 +391,7 @@ public:
// Graphics buffers/pointers
byte *bgSurface;
byte *backSurface;
+ byte *cursorSurface;
byte *drawSurface3;
byte *drawSurface2;
byte *tableSurface;
@@ -733,6 +736,8 @@ public:
private:
int _lang;
+ Console *_console;
+
CharInfo *_charMap;
int _charMapSize;
diff --git a/engines/drascula/graphics.cpp b/engines/drascula/graphics.cpp
index 00399816da..70085b99af 100644
--- a/engines/drascula/graphics.cpp
+++ b/engines/drascula/graphics.cpp
@@ -54,6 +54,7 @@ void DrasculaEngine::allocMemory() {
assert(crosshairCursor);
mouseCursor = (byte *)malloc(OBJWIDTH * OBJHEIGHT);
assert(mouseCursor);
+ cursorSurface = (byte *)malloc(64000);
}
void DrasculaEngine::freeMemory() {
@@ -67,6 +68,7 @@ void DrasculaEngine::freeMemory() {
free(frontSurface);
free(crosshairCursor);
free(mouseCursor);
+ free(cursorSurface);
}
void DrasculaEngine::moveCursor() {
@@ -90,6 +92,8 @@ void DrasculaEngine::moveCursor() {
}
void DrasculaEngine::loadPic(const char *NamePcc, byte *targetSurface, int colorCount) {
+ debug(5, "loadPic(%s)", NamePcc);
+
uint dataSize = 0;
byte *pcxData;
@@ -147,8 +151,7 @@ void DrasculaEngine::showFrame(Common::SeekableReadStream *stream, bool firstFra
free(prevFrame);
}
-void DrasculaEngine::copyBackground(int xorg, int yorg, int xdes, int ydes, int width,
- int height, byte *src, byte *dest) {
+void DrasculaEngine::copyBackground(int xorg, int yorg, int xdes, int ydes, int width, int height, byte *src, byte *dest) {
dest += xdes + ydes * 320;
src += xorg + yorg * 320;
/* Unoptimized code
@@ -190,16 +193,20 @@ void DrasculaEngine::copyRect(int xorg, int yorg, int xdes, int ydes, int width,
dest += xdes + ydes * 320;
src += xorg + yorg * 320;
- for (y = 0; y < height; y++)
- for (x = 0; x < width; x++)
- if (src[x + y * 320] != 255)
- dest[x + y * 320] = src[x + y * 320];
+ int ptr = 0;
+ for (y = 0; y < height; y++) {
+ for (x = 0; x < width; x++) {
+ if (src[ptr] != 255)
+ dest[ptr] = src[ptr];
+ ptr++;
+ }
+ ptr += 320 - width;
+ }
+
}
void DrasculaEngine::updateScreen(int xorg, int yorg, int xdes, int ydes, int width, int height, byte *buffer) {
- byte *screenBuffer = (byte *)_system->lockScreen()->pixels;
- copyBackground(xorg, yorg, xdes, ydes, width, height, buffer, screenBuffer);
- _system->unlockScreen();
+ _system->copyRectToScreen(buffer + xorg + yorg * 320, 320, xdes, ydes, width, height);
_system->updateScreen();
}
diff --git a/engines/drascula/interface.cpp b/engines/drascula/interface.cpp
index 21803a8932..1495694a1b 100644
--- a/engines/drascula/interface.cpp
+++ b/engines/drascula/interface.cpp
@@ -65,6 +65,8 @@ void DrasculaEngine::selectVerbFromBar() {
}
void DrasculaEngine::selectVerb(int verb) {
+ debug(4, "selectVerb(%d)", verb);
+
int c = _menuScreen ? 0 : 171;
if (currentChapter == 5) {
@@ -76,7 +78,7 @@ void DrasculaEngine::selectVerb(int verb) {
}
for (int i = 0; i < OBJHEIGHT; i++)
- memcpy(mouseCursor + i * OBJWIDTH, backSurface + OBJWIDTH * verb + (c + i) * 320, OBJWIDTH);
+ memcpy(mouseCursor + i * OBJWIDTH, cursorSurface + OBJWIDTH * verb + (c + i) * 320, OBJWIDTH);
setCursor(kCursorCurrentItem);
if (verb > 0) {
@@ -126,7 +128,7 @@ void DrasculaEngine::showMenu() {
OBJWIDTH, OBJHEIGHT, srcSurface, screenSurface);
}
copyRect(_x1d_menu[h], _y1d_menu[h], _itemLocations[n].x, _itemLocations[n].y,
- OBJWIDTH, OBJHEIGHT, backSurface, screenSurface);
+ OBJWIDTH, OBJHEIGHT, cursorSurface, screenSurface);
}
if (x < 7)
@@ -140,7 +142,7 @@ void DrasculaEngine::clearMenu() {
if (mouseX > _verbBarX[n] && mouseX < _verbBarX[n + 1])
verbActivated = 0;
copyRect(OBJWIDTH * n, OBJHEIGHT * verbActivated, _verbBarX[n], 2,
- OBJWIDTH, OBJHEIGHT, backSurface, screenSurface);
+ OBJWIDTH, OBJHEIGHT, cursorSurface, screenSurface);
verbActivated = 1;
}
}
diff --git a/engines/drascula/module.mk b/engines/drascula/module.mk
index a9fa257549..20fd900124 100644
--- a/engines/drascula/module.mk
+++ b/engines/drascula/module.mk
@@ -3,6 +3,7 @@ MODULE := engines/drascula
MODULE_OBJS := \
actors.o \
animation.o \
+ console.o \
converse.o \
detection.o \
drascula.o \
diff --git a/engines/drascula/objects.cpp b/engines/drascula/objects.cpp
index 13c8a742ca..73aea7b7f2 100644
--- a/engines/drascula/objects.cpp
+++ b/engines/drascula/objects.cpp
@@ -78,8 +78,11 @@ void DrasculaEngine::gotoObject(int pointX, int pointY) {
for (;;) {
updateRoom();
updateScreen();
+ updateEvents();
if (characterMoved == 0)
break;
+
+ pause(3);
}
if (walkToObject == 1) {
diff --git a/engines/drascula/palette.cpp b/engines/drascula/palette.cpp
index 1e51deffd9..0f75bb7959 100644
--- a/engines/drascula/palette.cpp
+++ b/engines/drascula/palette.cpp
@@ -106,6 +106,8 @@ void DrasculaEngine::fadeToBlack(int fadeSpeed) {
pause(fadeSpeed);
setPalette((byte *)&palFade);
+
+ updateEvents();
}
}
@@ -124,6 +126,8 @@ void DrasculaEngine::fadeFromBlack(int fadeSpeed) {
pause(fadeSpeed);
setPalette((byte *)&palFade);
+
+ updateEvents();
}
}
diff --git a/engines/drascula/rooms.cpp b/engines/drascula/rooms.cpp
index a71545feca..57bfad26af 100644
--- a/engines/drascula/rooms.cpp
+++ b/engines/drascula/rooms.cpp
@@ -1091,7 +1091,7 @@ void DrasculaEngine::updateRefresh() {
sprintf(rm, "update_%d", roomNumber);
for (uint i = 0; i < _roomHandlers->roomUpdaters.size(); i++) {
if (!strcmp(rm, _roomHandlers->roomUpdaters[i]->desc)) {
- debug(4, "Calling room updater %d", roomNumber);
+ debug(8, "Calling room updater %d", roomNumber);
(this->*(_roomHandlers->roomUpdaters[i]->proc))();
break;
}
@@ -1129,7 +1129,7 @@ void DrasculaEngine::updateRefresh_pre() {
sprintf(rm, "update_%d_pre", roomNumber);
for (uint i = 0; i < _roomHandlers->roomPreupdaters.size(); i++) {
if (!strcmp(rm, _roomHandlers->roomPreupdaters[i]->desc)) {
- debug(4, "Calling room preupdater %d", roomNumber);
+ debug(8, "Calling room preupdater %d", roomNumber);
(this->*(_roomHandlers->roomPreupdaters[i]->proc))();
break;
}
diff --git a/engines/drascula/talk.cpp b/engines/drascula/talk.cpp
index 54175c5e5b..8cefe0385c 100644
--- a/engines/drascula/talk.cpp
+++ b/engines/drascula/talk.cpp
@@ -170,6 +170,7 @@ void DrasculaEngine::talk_drascula(int index, int talkerType) {
centerText(said, drasculaX + 19, drasculaY);
updateScreen();
+ updateEvents();
pause(3);
@@ -215,6 +216,7 @@ void DrasculaEngine::talk_drascula_big(int index) {
centerText(said, 191, 69);
updateScreen();
+ updateEvents();
pause(3);
@@ -245,7 +247,9 @@ void DrasculaEngine::talk_solo(const char *said, const char *filename) {
else if (currentChapter == 5)
centerText(said, 173, 92);
}
+ updateEvents();
updateScreen();
+ pause(3);
} while (!isTalkFinished());
if (currentChapter == 6) {
@@ -304,6 +308,7 @@ void DrasculaEngine::talk_bartender(int index, int talkerType) {
centerText(said, 132, 45);
updateScreen();
+ updateEvents();
pause(3);
} while (!isTalkFinished());
@@ -331,11 +336,9 @@ void DrasculaEngine::talk_bj(int index) {
updateRefresh_pre();
- copyBackground(bjX + 2, bjY - 1, bjX + 2, bjY - 1, 27, 40,
- bgSurface, screenSurface);
+ copyBackground(bjX + 2, bjY - 1, bjX + 2, bjY - 1, 27, 40, bgSurface, screenSurface);
- copyRect(x_talk[face], 99, bjX + 2, bjY - 1, 27, 40,
- drawSurface3, screenSurface);
+ copyRect(x_talk[face], 99, bjX + 2, bjY - 1, 27, 40, drawSurface3, screenSurface);
moveCharacters();
updateRefresh();
@@ -353,6 +356,7 @@ void DrasculaEngine::talk_bj(int index) {
updateScreen();
}
+ updateEvents();
} while (!isTalkFinished());
updateRoom();
@@ -467,6 +471,7 @@ void DrasculaEngine::talk(const char *said, const char *filename) {
centerText(said, curX, curY);
updateScreen();
+ updateEvents();
pause(3);
} while (!isTalkFinished());
@@ -558,16 +563,15 @@ void DrasculaEngine::talk_vonBraun(int index, int talkerType) {
if (!_subtitlesDisabled)
centerText(said, vonBraunX, 66);
- updateScreen();
- pause(3);
} else {
updateRoom();
if (!_subtitlesDisabled)
centerText(said, 150, 80);
-
- updateScreen();
}
+ updateScreen();
+ updateEvents();
+ pause(3);
} while (!isTalkFinished());
updateRoom();
@@ -621,6 +625,7 @@ void DrasculaEngine::talk_blind(int index) {
centerText(said, 260, 71);
updateScreen();
+ updateEvents();
pause(2);
p++;
} while (!isTalkFinished());
@@ -641,7 +646,9 @@ void DrasculaEngine::talk_hacker(int index) {
do {
if (!_subtitlesDisabled)
centerText(said, 156, 170);
+ updateEvents();
updateScreen();
+ pause(3);
} while (!isTalkFinished());
}
@@ -693,13 +700,13 @@ void DrasculaEngine::talk_pen(const char *said, const char *filename, int talker
copyBackground();
updateRefresh_pre();
+ updateRefresh();
+
if (talkerType == 0)
copyRect(x_talk[face], 145, 145, 105, 25, 29, drawSurface3, screenSurface);
else
copyBackground(x_talk2[face], 171, 173, 116, 25, 28, drawSurface3, screenSurface);
- updateRefresh();
-
if (!_subtitlesDisabled) {
if (talkerType == 0)
centerText(said, 160, 105);
@@ -708,6 +715,7 @@ void DrasculaEngine::talk_pen(const char *said, const char *filename, int talker
}
updateScreen();
+ updateEvents();
pause(3);
} while (!isTalkFinished());
@@ -745,6 +753,7 @@ void DrasculaEngine::talk_bj_bed(int index) {
centerText(said, 104, 102);
updateScreen();
+ updateEvents();
pause(3);
} while (!isTalkFinished());
@@ -781,6 +790,7 @@ void DrasculaEngine::talk_htel(int index) {
centerText(said, 90, 50);
updateScreen();
+ updateEvents();
pause(3);
} while (!isTalkFinished());
@@ -862,6 +872,7 @@ void DrasculaEngine::talk_sync(const char *said, const char *filename, const cha
centerText(said, curX, curY);
updateScreen();
+ updateEvents();
p++;
pause(3);
@@ -895,6 +906,7 @@ void DrasculaEngine::talk_trunk(int index) {
centerText(said, 263, 69);
updateScreen();
+ updateEvents();
pause(4);
} while (!isTalkFinished());
@@ -922,6 +934,7 @@ void DrasculaEngine::talk_generic(const char* said, const char* filename, int* f
centerText(said, coords[5], coords[6]);
updateScreen();
+ updateEvents();
pause(3);
} while (!isTalkFinished());
@@ -944,8 +957,10 @@ void DrasculaEngine::grr() {
updateScreen();
- while (!isTalkFinished())
- ;
+ while (!isTalkFinished()) {
+ updateEvents();
+ pause(3);
+ }
updateRoom();
updateScreen();
diff --git a/engines/engine.cpp b/engines/engine.cpp
index 0f42cd493d..84fc0bbe4e 100644
--- a/engines/engine.cpp
+++ b/engines/engine.cpp
@@ -23,6 +23,7 @@
*/
#if defined(WIN32) && !defined(_WIN32_WCE) && !defined(__SYMBIAN32__)
+#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <direct.h>
// winnt.h defines ARRAYSIZE, but we want our own one...
diff --git a/engines/game.cpp b/engines/game.cpp
index c7f26019d6..dea6d37485 100644
--- a/engines/game.cpp
+++ b/engines/game.cpp
@@ -73,6 +73,10 @@ void GameDescriptor::setGUIOptions(uint32 guioptions) {
erase("guioptions");
}
+void GameDescriptor::appendGUIOptions(const Common::String &str) {
+ setVal("guioptions", getVal("guioptions", "") + " " + str);
+}
+
void GameDescriptor::updateDesc(const char *extra) {
// TODO: The format used here (LANG/PLATFORM/EXTRA) is not set in stone.
// We may want to change the order (PLATFORM/EXTRA/LANG, anybody?), or
diff --git a/engines/game.h b/engines/game.h
index 49136ecf5a..b125421ff6 100644
--- a/engines/game.h
+++ b/engines/game.h
@@ -83,6 +83,7 @@ public:
void updateDesc(const char *extra = 0);
void setGUIOptions(uint32 options);
+ void appendGUIOptions(const Common::String &str);
Common::String &gameid() { return getVal("gameid"); }
Common::String &description() { return getVal("description"); }
diff --git a/engines/gob/detection.cpp b/engines/gob/detection.cpp
index 1f8bfdc138..a1eb8055aa 100644
--- a/engines/gob/detection.cpp
+++ b/engines/gob/detection.cpp
@@ -87,4994 +87,7 @@ static const ADObsoleteGameID obsoleteGameIDsTable[] = {
{0, 0, kPlatformUnknown}
};
-namespace Gob {
-
-using Common::GUIO_NOSPEECH;
-using Common::GUIO_NOSUBTITLES;
-using Common::GUIO_NONE;
-
-static const GOBGameDescription gameDescriptions[] = {
- { // Supplied by Florian Zeitz on scummvm-devel
- {
- "gob1",
- "EGA",
- AD_ENTRY1("intro.stk", "c65e9cc8ba23a38456242e1f2b1caad4"),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesEGA,
- 0, 0, 0
- },
- {
- {
- "gob1",
- "EGA",
- AD_ENTRY1("intro.stk", "f9233283a0be2464248d83e14b95f09c"),
- RU_RUS,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesEGA,
- 0, 0, 0
- },
- { // Supplied by Theruler76 in bug report #1201233
- {
- "gob1",
- "VGA",
- AD_ENTRY1("intro.stk", "26a9118c0770fa5ac93a9626761600b2"),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesNone,
- 0, 0, 0
- },
- { // Supplied by raziel_ in bug report #1891864
- {
- "gob1",
- "VGA",
- AD_ENTRY1s("intro.stk", "e157cb59c6d330ca70d12ab0ef1dd12b", 288972),
- EN_GRB,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Supplied by raina in the forums
- {
- "gob1",
- "",
- AD_ENTRY1s("intro.stk", "6d837c6380d8f4d984c9f6cc0026df4f", 192712),
- EN_ANY,
- kPlatformMacintosh,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesNone,
- 0, 0, 0
- },
- { // Supplied by paul66 in bug report #1652352
- {
- "gob1",
- "",
- AD_ENTRY1("intro.stk", "00a42a7d2d22e6b6ab1b8c673c4ed267"),
- EN_ANY,
- kPlatformMacintosh,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Supplied by paul66 in bug report #1652352
- {
- "gob1",
- "",
- AD_ENTRY1("intro.stk", "00a42a7d2d22e6b6ab1b8c673c4ed267"),
- DE_DEU,
- kPlatformMacintosh,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Supplied by paul66 in bug report #1652352
- {
- "gob1",
- "",
- AD_ENTRY1("intro.stk", "00a42a7d2d22e6b6ab1b8c673c4ed267"),
- FR_FRA,
- kPlatformMacintosh,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Supplied by paul66 in bug report #1652352
- {
- "gob1",
- "",
- AD_ENTRY1("intro.stk", "00a42a7d2d22e6b6ab1b8c673c4ed267"),
- IT_ITA,
- kPlatformMacintosh,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Supplied by paul66 in bug report #1652352
- {
- "gob1",
- "",
- AD_ENTRY1("intro.stk", "00a42a7d2d22e6b6ab1b8c673c4ed267"),
- ES_ESP,
- kPlatformMacintosh,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Supplied by Hkz on #scummvm
- {
- "gob1",
- "",
- {
- {"intro.stk", 0, "f5f028ee39c456fa51fa63b606583918", 313472},
- {"musmac1.mid", 0, "4f66903b33df8a20edd4c748809c0b56", 8161},
- {0, 0, 0, 0}
- },
- FR_FRA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Supplied by Hkz on #scummvm
- {
- "gob1",
- "",
- {
- {"intro.stk", 0, "f5f028ee39c456fa51fa63b606583918", 313472},
- {"musmac1.mid", 0, "4f66903b33df8a20edd4c748809c0b56", 8161},
- {0, 0, 0, 0}
- },
- IT_ITA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Supplied by Hkz on #scummvm
- {
- "gob1",
- "",
- {
- {"intro.stk", 0, "f5f028ee39c456fa51fa63b606583918", 313472},
- {"musmac1.mid", 0, "4f66903b33df8a20edd4c748809c0b56", 8161},
- {0, 0, 0, 0}
- },
- EN_GRB,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Supplied by Hkz on #scummvm
- {
- "gob1",
- "",
- {
- {"intro.stk", 0, "f5f028ee39c456fa51fa63b606583918", 313472},
- {"musmac1.mid", 0, "4f66903b33df8a20edd4c748809c0b56", 8161},
- {0, 0, 0, 0}
- },
- DE_DEU,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Supplied by Hkz on #scummvm
- {
- "gob1",
- "",
- {
- {"intro.stk", 0, "f5f028ee39c456fa51fa63b606583918", 313472},
- {"musmac1.mid", 0, "4f66903b33df8a20edd4c748809c0b56", 8161},
- {0, 0, 0, 0}
- },
- ES_ESP,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob1",
- "",
- {
- {"intro.stk", 0, "e157cb59c6d330ca70d12ab0ef1dd12b", 288972},
- {"musmac1.mid", 0, "4f66903b33df8a20edd4c748809c0b56", 8161},
- {0, 0, 0, 0}
- },
- EN_GRB,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob1",
- "",
- {
- {"intro.stk", 0, "e157cb59c6d330ca70d12ab0ef1dd12b", 288972},
- {"musmac1.mid", 0, "4f66903b33df8a20edd4c748809c0b56", 8161},
- {0, 0, 0, 0}
- },
- FR_FRA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob1",
- "",
- {
- {"intro.stk", 0, "e157cb59c6d330ca70d12ab0ef1dd12b", 288972},
- {"musmac1.mid", 0, "4f66903b33df8a20edd4c748809c0b56", 8161},
- {0, 0, 0, 0}
- },
- ES_ESP,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob1",
- "",
- {
- {"intro.stk", 0, "e157cb59c6d330ca70d12ab0ef1dd12b", 288972},
- {"musmac1.mid", 0, "4f66903b33df8a20edd4c748809c0b56", 8161},
- {0, 0, 0, 0}
- },
- IT_ITA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob1",
- "",
- {
- {"intro.stk", 0, "e157cb59c6d330ca70d12ab0ef1dd12b", 288972},
- {"musmac1.mid", 0, "4f66903b33df8a20edd4c748809c0b56", 8161},
- {0, 0, 0, 0}
- },
- DE_DEU,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Found in Found in french ADI 2.5 Anglais Multimedia 5e
- {
- "gob1",
- "",
- AD_ENTRY1s("intro.stk", "f5f028ee39c456fa51fa63b606583918", 313472),
- FR_FRA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Found in Found in french ADI 2.5 Anglais Multimedia 5e
- {
- "gob1",
- "",
- AD_ENTRY1s("intro.stk", "f5f028ee39c456fa51fa63b606583918", 313472),
- EN_GRB,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Found in Found in french ADI 2.5 Anglais Multimedia 5e
- {
- "gob1",
- "",
- AD_ENTRY1s("intro.stk", "f5f028ee39c456fa51fa63b606583918", 313472),
- DE_DEU,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Found in Found in french ADI 2.5 Anglais Multimedia 5e
- {
- "gob1",
- "",
- AD_ENTRY1s("intro.stk", "f5f028ee39c456fa51fa63b606583918", 313472),
- IT_ITA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Found in Found in french ADI 2.5 Anglais Multimedia 5e
- {
- "gob1",
- "",
- AD_ENTRY1s("intro.stk", "f5f028ee39c456fa51fa63b606583918", 313472),
- ES_ESP,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // CD 1.000 version.
- {
- "gob1cd",
- "v1.000",
- AD_ENTRY1("intro.stk", "2fbf4b5b82bbaee87eb45d4404c28998"),
- EN_USA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesCD,
- 0, 0, 0
- },
- { // CD 1.000 version.
- {
- "gob1cd",
- "v1.000",
- AD_ENTRY1("intro.stk", "2fbf4b5b82bbaee87eb45d4404c28998"),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesCD,
- 0, 0, 0
- },
- { // CD 1.000 version.
- {
- "gob1cd",
- "v1.000",
- AD_ENTRY1("intro.stk", "2fbf4b5b82bbaee87eb45d4404c28998"),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesCD,
- 0, 0, 0
- },
- { // CD 1.000 version.
- {
- "gob1cd",
- "v1.000",
- AD_ENTRY1("intro.stk", "2fbf4b5b82bbaee87eb45d4404c28998"),
- IT_ITA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesCD,
- 0, 0, 0
- },
- { // CD 1.000 version.
- {
- "gob1cd",
- "v1.000",
- AD_ENTRY1("intro.stk", "2fbf4b5b82bbaee87eb45d4404c28998"),
- ES_ESP,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesCD,
- 0, 0, 0
- },
- { // CD 1.02 version. Multilingual
- {
- "gob1cd",
- "v1.02",
- AD_ENTRY1("intro.stk", "8bd873137b6831c896ee8ad217a6a398"),
- EN_USA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesCD,
- 0, 0, 0
- },
- { // CD 1.02 version. Multilingual
- {
- "gob1cd",
- "v1.02",
- AD_ENTRY1("intro.stk", "8bd873137b6831c896ee8ad217a6a398"),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesCD,
- 0, 0, 0
- },
- { // CD 1.02 version. Multilingual
- {
- "gob1cd",
- "v1.02",
- AD_ENTRY1("intro.stk", "8bd873137b6831c896ee8ad217a6a398"),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesCD,
- 0, 0, 0
- },
- { // CD 1.02 version. Multilingual
- {
- "gob1cd",
- "v1.02",
- AD_ENTRY1("intro.stk", "8bd873137b6831c896ee8ad217a6a398"),
- IT_ITA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesCD,
- 0, 0, 0
- },
- { // CD 1.02 version. Multilingual
- {
- "gob1cd",
- "v1.02",
- AD_ENTRY1("intro.stk", "8bd873137b6831c896ee8ad217a6a398"),
- ES_ESP,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesCD,
- 0, 0, 0
- },
- { // Supplied by goodoldgeorg in bug report #2810082
- {
- "gob1cd",
- "v1.02",
- AD_ENTRY1s("intro.stk", "40d4a53818f4fce3f5997d02c3fafe73", 4049248),
- HU_HUN,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesCD,
- 0, 0, 0
- },
- { // Supplied by goodoldgeorg in bug report #2810082
- {
- "gob1cd",
- "v1.02",
- AD_ENTRY1s("intro.stk", "40d4a53818f4fce3f5997d02c3fafe73", 4049248),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesCD,
- 0, 0, 0
- },
- { // Supplied by goodoldgeorg in bug report #2810082
- {
- "gob1cd",
- "v1.02",
- AD_ENTRY1s("intro.stk", "40d4a53818f4fce3f5997d02c3fafe73", 4049248),
- ES_ESP,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesCD,
- 0, 0, 0
- },
- { // Supplied by goodoldgeorg in bug report #2810082
- {
- "gob1cd",
- "v1.02",
- AD_ENTRY1s("intro.stk", "40d4a53818f4fce3f5997d02c3fafe73", 4049248),
- IT_ITA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesCD,
- 0, 0, 0
- },
- {
- {
- "gob1",
- "Demo",
- AD_ENTRY1("intro.stk", "972f22c6ff8144a6636423f0354ca549"),
- UNK_LANG,
- kPlatformAmiga,
- ADGF_DEMO,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesNone,
- 0, 0, 0
- },
- {
- {
- "gob1",
- "Interactive Demo",
- AD_ENTRY1("intro.stk", "e72bd1e3828c7dec4c8a3e58c48bdfdb"),
- UNK_LANG,
- kPlatformPC,
- ADGF_DEMO,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesNone,
- 0, 0, 0
- },
- {
- {
- "gob1",
- "Interactive Demo",
- AD_ENTRY1s("intro.stk", "a796096280d5efd48cf8e7dfbe426eb5", 193595),
- UNK_LANG,
- kPlatformPC,
- ADGF_DEMO,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesNone,
- 0, 0, 0
- },
- { // Supplied by goodoldgeorg in bug report #2785958
- {
- "gob1",
- "Interactive Demo",
- AD_ENTRY1s("intro.stk", "35a098571af9a03c04e2303aec7c9249", 116582),
- FR_FRA,
- kPlatformPC,
- ADGF_DEMO,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesNone,
- 0, 0, 0
- },
- {
- {
- "gob1",
- "",
- AD_ENTRY1s("intro.stk", "0e022d3f2481b39e9175d37b2c6ad4c6", 2390121),
- FR_FRA,
- kPlatformCDi,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesAdLib,
- 0, "AVT003.TOT", 0
- },
- { // Supplied by fac76 in bug report #1883808
- {
- "gob2",
- "",
- AD_ENTRY1s("intro.stk", "eebf2810122cfd17399260cd1468e994", 554014),
- EN_ANY,
- kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesNone,
- 0, 0, 0
- },
- {
- {
- "gob2",
- "",
- AD_ENTRY1("intro.stk", "d28b9e9b41f31acfa58dcd12406c7b2c"),
- DE_DEU,
- kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesNone,
- 0, 0, 0
- },
- { // Supplied by goodoldgeorg in bug report #2602057
- {
- "gob2",
- "",
- AD_ENTRY1("intro.stk", "686c88f7302a80b744aae9f8413e853d"),
- IT_ITA,
- kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesNone,
- 0, 0, 0
- },
- { // Supplied by bgk in bug report #1706861
- {
- "gob2",
- "",
- AD_ENTRY1s("intro.stk", "4b13c02d1069b86bcfec80f4e474b98b", 554680),
- FR_FRA,
- kPlatformAtariST,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesNone,
- 0, 0, 0
- },
- { // Supplied by fac76 in bug report #1673397
- {
- "gob2",
- "",
- {
- {"intro.stk", 0, "b45b984ee8017efd6ea965b9becd4d66", 828443},
- {"musmac1.mid", 0, "7f96f491448c7a001b32df89cf8d2af2", 1658},
- {0, 0, 0, 0}
- },
- UNK_LANG,
- kPlatformMacintosh,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Supplied by koalet in bug report #2478585
- {
- "gob2",
- "",
- {
- {"intro.stk", 0, "a13ecb4f6d8fd881ebbcc02e45cb5475", 837275},
- {"musmac1.mid", 0, "7f96f491448c7a001b32df89cf8d2af2", 1658},
- {0, 0, 0, 0}
- },
- FR_FRA,
- kPlatformMacintosh,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob2",
- "",
- AD_ENTRY1("intro.stk", "b45b984ee8017efd6ea965b9becd4d66"),
- EN_GRB,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob2",
- "",
- AD_ENTRY1("intro.stk", "dedb5d31d8c8050a8cf77abedcc53dae"),
- EN_USA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Supplied by raziel_ in bug report #1891867
- {
- "gob2",
- "",
- AD_ENTRY1s("intro.stk", "25a99827cd59751a80bed9620fb677a0", 893302),
- EN_USA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob2",
- "",
- AD_ENTRY1s("intro.stk", "a13ecb4f6d8fd881ebbcc02e45cb5475", 837275),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Supplied by blackwhiteeagle in bug report #1605235
- {
- "gob2",
- "",
- AD_ENTRY1("intro.stk", "3e4e7db0d201587dd2df4003b2993ef6"),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob2",
- "",
- AD_ENTRY1("intro.stk", "a13892cdf4badda85a6f6fb47603a128"),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Supplied by goodoldgeorg in bug report #2602017
- {
- "gob2",
- "",
- AD_ENTRY1("intro.stk", "c47faf1d406504e6ffe63243610bb1f4"),
- IT_ITA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob2",
- "",
- AD_ENTRY1("intro.stk", "cd3e1df8b273636ee32e34b7064f50e8"),
- RU_RUS,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Supplied by arcepi in bug report #1659884
- {
- "gob2",
- "",
- AD_ENTRY1s("intro.stk", "5f53c56e3aa2f1e76c2e4f0caa15887f", 829232),
- ES_ESP,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob2",
- "",
- {
- {"intro.stk", 0, "285d7340f98ebad65d465585da12910b", 837286},
- {"musmac1.mid", 0, "834e55205b710d0af5f14a6f2320dd8e", 8661},
- {0, 0, 0, 0}
- },
- FR_FRA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob2",
- "",
- {
- {"intro.stk", 0, "25a99827cd59751a80bed9620fb677a0", 893302},
- {"musmac1.mid", 0, "834e55205b710d0af5f14a6f2320dd8e", 8661},
- {0, 0, 0, 0}
- },
- EN_USA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob2",
- "",
- {
- {"intro.stk", 0, "25a99827cd59751a80bed9620fb677a0", 893302},
- {"musmac1.mid", 0, "834e55205b710d0af5f14a6f2320dd8e", 8661},
- {0, 0, 0, 0}
- },
- FR_FRA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob2",
- "",
- {
- {"intro.stk", 0, "25a99827cd59751a80bed9620fb677a0", 893302},
- {"musmac1.mid", 0, "834e55205b710d0af5f14a6f2320dd8e", 8661},
- {0, 0, 0, 0}
- },
- DE_DEU,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob2",
- "",
- {
- {"intro.stk", 0, "6efac0a14c0de4d57dde8592456c8acf", 845172},
- {"musmac1.mid", 0, "834e55205b710d0af5f14a6f2320dd8e", 8661},
- {0, 0, 0, 0}
- },
- EN_USA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob2",
- "",
- {
- {"intro.stk", 0, "6efac0a14c0de4d57dde8592456c8acf", 845172},
- {"musmac1.mid", 0, "834e55205b710d0af5f14a6f2320dd8e", 8661},
- {0, 0, 0, 0}
- },
- FR_FRA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Found in french ADI 2 Francais-Maths CM1
- {
- "gob2",
- "",
- AD_ENTRY1s("intro.stk", "24489330a1d67ff978211f574822a5a6", 883756),
- FR_FRA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Found in french ADI 2.5 Anglais Multimedia 5e
- {
- "gob2",
- "",
- AD_ENTRY1s("intro.stk", "285d7340f98ebad65d465585da12910b", 837286),
- FR_FRA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob2cd",
- "v1.000",
- AD_ENTRY1("intro.stk", "9de5fbb41cf97182109e5fecc9d90347"),
- EN_USA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesCD,
- 0, 0, 0
- },
- {
- {
- "gob2cd",
- "v2.01",
- AD_ENTRY1("intro.stk", "24a6b32757752ccb1917ce92fd7c2a04"),
- EN_ANY,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesCD,
- 0, 0, 0
- },
- {
- {
- "gob2cd",
- "v2.01",
- AD_ENTRY1("intro.stk", "24a6b32757752ccb1917ce92fd7c2a04"),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesCD,
- 0, 0, 0
- },
- {
- {
- "gob2cd",
- "v2.01",
- AD_ENTRY1("intro.stk", "24a6b32757752ccb1917ce92fd7c2a04"),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesCD,
- 0, 0, 0
- },
- {
- {
- "gob2cd",
- "v2.01",
- AD_ENTRY1("intro.stk", "24a6b32757752ccb1917ce92fd7c2a04"),
- IT_ITA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesCD,
- 0, 0, 0
- },
- {
- {
- "gob2cd",
- "v2.01",
- AD_ENTRY1("intro.stk", "24a6b32757752ccb1917ce92fd7c2a04"),
- ES_ESP,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesCD,
- 0, 0, 0
- },
- { // Supplied by goodoldgeorg in bug report #2810082
- {
- "gob2cd",
- "v1.02",
- AD_ENTRY1s("intro.stk", "5ba85a4769a1ab03a283dd694588d526", 5006236),
- HU_HUN,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesCD,
- 0, 0, 0
- },
- { // Supplied by goodoldgeorg in bug report #2810082
- {
- "gob2cd",
- "v1.02",
- AD_ENTRY1s("intro.stk", "5ba85a4769a1ab03a283dd694588d526", 5006236),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesCD,
- 0, 0, 0
- },
- { // Supplied by goodoldgeorg in bug report #2810082
- {
- "gob2cd",
- "v1.02",
- AD_ENTRY1s("intro.stk", "5ba85a4769a1ab03a283dd694588d526", 5006236),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesCD,
- 0, 0, 0
- },
- { // Supplied by goodoldgeorg in bug report #2810082
- {
- "gob2cd",
- "v1.02",
- AD_ENTRY1s("intro.stk", "5ba85a4769a1ab03a283dd694588d526", 5006236),
- ES_ESP,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesCD,
- 0, 0, 0
- },
- { // Supplied by goodoldgeorg in bug report #2810082
- {
- "gob2cd",
- "v1.02",
- AD_ENTRY1s("intro.stk", "5ba85a4769a1ab03a283dd694588d526", 5006236),
- IT_ITA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesCD,
- 0, 0, 0
- },
- {
- {
- "gob2",
- "Non-Interactive Demo",
- AD_ENTRY1("intro.stk", "8b1c98ff2ab2e14f47a1b891e9b92217"),
- UNK_LANG,
- kPlatformPC,
- ADGF_DEMO,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib,
- 0, "usa.tot", 0
- },
- {
- {
- "gob2",
- "Interactive Demo",
- AD_ENTRY1("intro.stk", "cf1c95b2939bd8ff58a25c756cb6125e"),
- UNK_LANG,
- kPlatformPC,
- ADGF_DEMO,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob2",
- "Interactive Demo",
- AD_ENTRY1("intro.stk", "4b278c2678ea01383fd5ca114d947eea"),
- UNK_LANG,
- kPlatformAmiga,
- ADGF_DEMO,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesNone,
- 0, 0, 0
- },
- { // Supplied by polluks in bug report #1895126
- {
- "gob2",
- "Interactive Demo",
- AD_ENTRY1s("intro.stk", "9fa85aea959fa8c582085855fbd99346", 553063),
- UNK_LANG,
- kPlatformAmiga,
- ADGF_DEMO,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesNone,
- 0, 0, 0
- },
- { // Supplied by vampir_raziel in bug report #1658373
- {
- "ween",
- "",
- {
- {"intro.stk", 0, "bfd9d02faf3d8d60a2cf744f95eb48dd", 456570},
- {"ween.ins", 0, "d2cb24292c9ddafcad07e23382027218", 87800},
- {0, 0, 0, 0}
- },
- EN_GRB,
- kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeWeen,
- kFeaturesNone,
- 0, 0, 0
- },
- { // Supplied by vampir_raziel in bug report #1658373
- {
- "ween",
- "",
- AD_ENTRY1s("intro.stk", "257fe669705ac4971efdfd5656eef16a", 457719),
- FR_FRA,
- kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeWeen,
- kFeaturesNone,
- 0, 0, 0
- },
- { // Supplied by vampir_raziel in bug report #1658373
- {
- "ween",
- "",
- AD_ENTRY1s("intro.stk", "dffd1ab98fe76150d6933329ca6f4cc4", 459458),
- FR_FRA,
- kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeWeen,
- kFeaturesNone,
- 0, 0, 0
- },
- { // Supplied by vampir_raziel in bug report #1658373
- {
- "ween",
- "",
- AD_ENTRY1s("intro.stk", "af83debf2cbea21faa591c7b4608fe92", 458192),
- DE_DEU,
- kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeWeen,
- kFeaturesNone,
- 0, 0, 0
- },
- { // Supplied by goodoldgeorg in bug report #2563539
- {
- "ween",
- "",
- {
- {"intro.stk", 0, "dffd1ab98fe76150d6933329ca6f4cc4", 459458},
- {"ween.ins", 0, "d2cb24292c9ddafcad07e23382027218", 87800},
- {0, 0, 0, 0}
- },
- IT_ITA,
- kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeWeen,
- kFeaturesNone,
- 0, 0, 0
- },
- { // Supplied by pwigren in bug report #1764174
- {
- "ween",
- "",
- {
- {"intro.stk", 0, "bfd9d02faf3d8d60a2cf744f95eb48dd", 456570},
- {"music__5.snd", 0, "7d1819b9981ecddd53d3aacbc75f1cc8", 13446},
- {0, 0, 0, 0}
- },
- EN_GRB,
- kPlatformAtariST,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeWeen,
- kFeaturesNone,
- 0, 0, 0
- },
- {
- {
- "ween",
- "",
- AD_ENTRY1("intro.stk", "e6d13fb3b858cb4f78a8780d184d5b2c"),
- FR_FRA,
- kPlatformAtariST,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeWeen,
- kFeaturesNone,
- 0, 0, 0
- },
- {
- {
- "ween",
- "",
- AD_ENTRY1("intro.stk", "2bb8878a8042244dd2b96ff682381baa"),
- EN_GRB,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeWeen,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "ween",
- "",
- AD_ENTRY1s("intro.stk", "de92e5c6a8c163007ffceebef6e67f7d", 7117568),
- EN_USA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeWeen,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Supplied by cybot_tmin in bug report #1667743
- {
- "ween",
- "",
- AD_ENTRY1s("intro.stk", "6d60f9205ecfbd8735da2ee7823a70dc", 7014426),
- ES_ESP,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeWeen,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "ween",
- "",
- AD_ENTRY1("intro.stk", "4b10525a3782aa7ecd9d833b5c1d308b"),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeWeen,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Supplied by cartman_ on #scummvm
- {
- "ween",
- "",
- AD_ENTRY1("intro.stk", "63170e71f04faba88673b3f510f9c4c8"),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeWeen,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Supplied by glorfindel in bugreport #1722142
- {
- "ween",
- "",
- AD_ENTRY1s("intro.stk", "8b57cd510da8a3bbd99e3a0297a8ebd1", 7018771),
- IT_ITA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeWeen,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "ween",
- "Demo",
- AD_ENTRY1("intro.stk", "2e9c2898f6bf206ede801e3b2e7ee428"),
- UNK_LANG,
- kPlatformPC,
- ADGF_DEMO,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeWeen,
- kFeaturesAdLib,
- 0, "show.tot", 0
- },
- {
- {
- "ween",
- "Demo",
- AD_ENTRY1("intro.stk", "15fb91a1b9b09684b28ac75edf66e504"),
- EN_USA,
- kPlatformPC,
- ADGF_DEMO,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeWeen,
- kFeaturesAdLib,
- 0, "show.tot", 0
- },
- {
- {
- "bargon",
- "",
- AD_ENTRY1("intro.stk", "da3c54be18ab73fbdb32db24624a9c23"),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeBargon,
- kFeaturesNone,
- 0, 0, 0
- },
- { // Supplied by Trekky in the forums
- {
- "bargon",
- "",
- AD_ENTRY1s("intro.stk", "2f54b330d21f65b04b7c1f8cca76426c", 262109),
- FR_FRA,
- kPlatformAtariST,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeBargon,
- kFeaturesNone,
- 0, 0, 0
- },
- { // Supplied by cesardark in bug #1681649
- {
- "bargon",
- "",
- AD_ENTRY1s("intro.stk", "11103b304286c23945560b391fd37e7d", 3181890),
- ES_ESP,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeBargon,
- kFeaturesNone,
- 0, 0, 0
- },
- { // Supplied by paul66 in bug #1692667
- {
- "bargon",
- "",
- AD_ENTRY1s("intro.stk", "da3c54be18ab73fbdb32db24624a9c23", 3181825),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeBargon,
- kFeaturesNone,
- 0, 0, 0
- },
- { // Supplied by pwigren in bugreport #1764174
- {
- "bargon",
- "",
- AD_ENTRY1s("intro.stk", "569d679fe41d49972d34c9fce5930dda", 269825),
- EN_ANY,
- kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeBargon,
- kFeaturesNone,
- 0, 0, 0
- },
- { // Supplied by kizkoool in bugreport #2089734
- {
- "bargon",
- "",
- AD_ENTRY1s("intro.stk", "00f6b4e2ee26e5c40b488e2df5adcf03", 3975580),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeBargon,
- kFeaturesNone,
- 0, 0, 0
- },
- { // Supplied by glorfindel in bugreport #1722142
- {
- "bargon",
- "Fanmade",
- AD_ENTRY1s("intro.stk", "da3c54be18ab73fbdb32db24624a9c23", 3181825),
- IT_ITA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeBargon,
- kFeaturesNone,
- 0, 0, 0
- },
- {
- {
- "littlered",
- "",
- AD_ENTRY1s("intro.stk", "0b72992f5d8b5e6e0330572a5753ea25", 256490),
- EN_GRB,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib | kFeaturesEGA,
- 0, 0, 0
- },
- {
- {
- "littlered",
- "",
- AD_ENTRY1s("intro.stk", "0b72992f5d8b5e6e0330572a5753ea25", 256490),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib | kFeaturesEGA,
- 0, 0, 0
- },
- {
- {
- "littlered",
- "",
- AD_ENTRY1s("intro.stk", "0b72992f5d8b5e6e0330572a5753ea25", 256490),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib | kFeaturesEGA,
- 0, 0, 0
- },
- {
- {
- "littlered",
- "",
- AD_ENTRY1s("intro.stk", "0b72992f5d8b5e6e0330572a5753ea25", 256490),
- ES_ESP,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib | kFeaturesEGA,
- 0, 0, 0
- },
- {
- {
- "littlered",
- "",
- AD_ENTRY1s("intro.stk", "0b72992f5d8b5e6e0330572a5753ea25", 256490),
- IT_ITA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib | kFeaturesEGA,
- 0, 0, 0
- },
- {
- {
- "littlered",
- "",
- {
- {"intro.stk", 0, "0b72992f5d8b5e6e0330572a5753ea25", 256490},
- {"mod.babayaga", 0, "43484cde74e0860785f8e19f0bc776d1", 60248},
- {0, 0, 0, 0}
- },
- UNK_LANG,
- kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesNone,
- 0, 0, 0
- },
- {
- {
- "littlered",
- "",
- AD_ENTRY1s("intro.stk", "113a16877e4f72037d9714be1c2b0221", 1187522),
- EN_GRB,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib | kFeaturesEGA,
- 0, 0, 0
- },
- {
- {
- "littlered",
- "",
- AD_ENTRY1s("intro.stk", "113a16877e4f72037d9714be1c2b0221", 1187522),
- FR_FRA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib | kFeaturesEGA,
- 0, 0, 0
- },
- {
- {
- "littlered",
- "",
- AD_ENTRY1s("intro.stk", "113a16877e4f72037d9714be1c2b0221", 1187522),
- DE_DEU,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib | kFeaturesEGA,
- 0, 0, 0
- },
- {
- {
- "littlered",
- "",
- AD_ENTRY1s("intro.stk", "113a16877e4f72037d9714be1c2b0221", 1187522),
- IT_ITA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib | kFeaturesEGA,
- 0, 0, 0
- },
- {
- {
- "littlered",
- "",
- AD_ENTRY1s("intro.stk", "113a16877e4f72037d9714be1c2b0221", 1187522),
- ES_ESP,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib | kFeaturesEGA,
- 0, 0, 0
- },
- { // Found in french ADI 2 Francais-Maths CM1
- {
- "littlered",
- "",
- AD_ENTRY1s("intro.stk", "5c15b37ed27ac2470854e9e09374d50e", 1248610),
- FR_FRA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib | kFeaturesEGA,
- 0, 0, 0
- },
- { // Found in french ADI 2 Francais-Maths CM1
- {
- "littlered",
- "",
- AD_ENTRY1s("intro.stk", "5c15b37ed27ac2470854e9e09374d50e", 1248610),
- ES_ESP,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib | kFeaturesEGA,
- 0, 0, 0
- },
- { // Found in french ADI 2 Francais-Maths CM1
- {
- "littlered",
- "",
- AD_ENTRY1s("intro.stk", "5c15b37ed27ac2470854e9e09374d50e", 1248610),
- EN_GRB,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib | kFeaturesEGA,
- 0, 0, 0
- },
- { // Found in french ADI 2 Francais-Maths CM1
- {
- "littlered",
- "",
- AD_ENTRY1s("intro.stk", "5c15b37ed27ac2470854e9e09374d50e", 1248610),
- IT_ITA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib | kFeaturesEGA,
- 0, 0, 0
- },
- { // Found in french ADI 2 Francais-Maths CM1
- {
- "littlered",
- "",
- AD_ENTRY1s("intro.stk", "5c15b37ed27ac2470854e9e09374d50e", 1248610),
- DE_DEU,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib | kFeaturesEGA,
- 0, 0, 0
- },
- {
- {
- "lit",
- "",
- AD_ENTRY1s("intro.stk", "7b7f48490dedc8a7cb999388e2fadbe3", 3930674),
- EN_USA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "lit",
- "",
- AD_ENTRY1s("intro.stk", "e0767783ff662ed93665446665693aef", 4371238),
- HE_ISR,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Supplied by cartman_ on #scummvm
- {
- "lit",
- "",
- AD_ENTRY1s("intro.stk", "f1f78b663893b58887add182a77df151", 3944090),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Supplied by goodoldgeorg in bug report #2105220
- {
- "lit",
- "",
- AD_ENTRY1s("intro.stk", "cd322cb3c64ef2ba2f2134aa2122cfe9", 3936700),
- ES_ESP,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Supplied by koalet in bug report #2479034
- {
- "lit",
- "",
- {
- {"intro.stk", 0, "af98bcdc70e1f1c1635577fd726fe7f1", 3937310},
- {"musmac1.mid", 0, "ae7229bb09c6abe4e60a2768b24bc890", 9398},
- {0, 0, 0, 0}
- },
- FR_FRA,
- kPlatformMacintosh,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "lit",
- "",
- AD_ENTRY1s("intro.stk", "6263d09e996c1b4e84ef2d650b820e57", 4831170),
- EN_USA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesCD,
- 0, 0, 0
- },
- {
- {
- "lit",
- "",
- AD_ENTRY1s("intro.stk", "6263d09e996c1b4e84ef2d650b820e57", 4831170),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesCD,
- 0, 0, 0
- },
- {
- {
- "lit",
- "",
- AD_ENTRY1s("intro.stk", "6263d09e996c1b4e84ef2d650b820e57", 4831170),
- IT_ITA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesCD,
- 0, 0, 0
- },
- {
- {
- "lit",
- "",
- AD_ENTRY1s("intro.stk", "6263d09e996c1b4e84ef2d650b820e57", 4831170),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesCD,
- 0, 0, 0
- },
- {
- {
- "lit",
- "",
- AD_ENTRY1s("intro.stk", "6263d09e996c1b4e84ef2d650b820e57", 4831170),
- ES_ESP,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesCD,
- 0, 0, 0
- },
- {
- {
- "lit",
- "",
- AD_ENTRY1s("intro.stk", "6263d09e996c1b4e84ef2d650b820e57", 4831170),
- EN_GRB,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesCD,
- 0, 0, 0
- },
- { // Supplied by SiRoCs in bug report #2093672
- {
- "lit",
- "",
- AD_ENTRY1s("intro.stk", "795be7011ec31bf5bb8ce4efdb9ee5d3", 4838904),
- EN_USA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesCD,
- 0, 0, 0
- },
- { // Supplied by SiRoCs in bug report #2093672
- {
- "lit",
- "",
- AD_ENTRY1s("intro.stk", "795be7011ec31bf5bb8ce4efdb9ee5d3", 4838904),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesCD,
- 0, 0, 0
- },
- { // Supplied by SiRoCs in bug report #2093672
- {
- "lit",
- "",
- AD_ENTRY1s("intro.stk", "795be7011ec31bf5bb8ce4efdb9ee5d3", 4838904),
- IT_ITA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesCD,
- 0, 0, 0
- },
- { // Supplied by SiRoCs in bug report #2093672
- {
- "lit",
- "",
- AD_ENTRY1s("intro.stk", "795be7011ec31bf5bb8ce4efdb9ee5d3", 4838904),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesCD,
- 0, 0, 0
- },
- { // Supplied by SiRoCs in bug report #2093672
- {
- "lit",
- "",
- AD_ENTRY1s("intro.stk", "795be7011ec31bf5bb8ce4efdb9ee5d3", 4838904),
- ES_ESP,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesCD,
- 0, 0, 0
- },
- { // Supplied by SiRoCs in bug report #2093672
- {
- "lit",
- "",
- AD_ENTRY1s("intro.stk", "795be7011ec31bf5bb8ce4efdb9ee5d3", 4838904),
- EN_GRB,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesCD,
- 0, 0, 0
- },
- {
- {
- "lit",
- "",
- AD_ENTRY1s("intro.stk", "0ddf39cea1ec30ecc8bfe444ebd7b845", 4207330),
- EN_GRB,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "lit",
- "",
- AD_ENTRY1s("intro.stk", "0ddf39cea1ec30ecc8bfe444ebd7b845", 4207330),
- FR_FRA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "lit",
- "",
- AD_ENTRY1s("intro.stk", "0ddf39cea1ec30ecc8bfe444ebd7b845", 4207330),
- ES_ESP,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "lit",
- "",
- AD_ENTRY1s("intro.stk", "0ddf39cea1ec30ecc8bfe444ebd7b845", 4219382),
- DE_DEU,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "lit",
- "",
- AD_ENTRY1s("intro.stk", "0ddf39cea1ec30ecc8bfe444ebd7b845", 4219382),
- FR_FRA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Found in french ADI 2.6 Francais-Maths 4e
- {
- "lit",
- "",
- AD_ENTRY1s("intro.stk", "58ee9583a4fb837f02d9a58e5f442656", 3937120),
- FR_FRA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "lit1",
- "Full install",
- {
- {"intro.stk", 0, "93c91bc9e783d00033042ae83144d7dd", 72318},
- {"partie2.itk", 0, "78f00bd8eb9e680e6289bba0130b1b33", 4396644},
- {0, 0, 0, 0}
- },
- FR_FRA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "lit1",
- "Light install",
- {
- {"intro.stk", 0, "93c91bc9e783d00033042ae83144d7dd", 72318},
- {"partie2.itk", 0, "78f00bd8eb9e680e6289bba0130b1b33", 664064},
- {0, 0, 0, 0}
- },
- FR_FRA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "lit2",
- "Light install",
- AD_ENTRY1s("intro.stk", "17acbb212e62addbe48dc8f2282c98cb", 72318),
- FR_FRA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "lit2",
- "Full install",
- {
- {"intro.stk", 0, "17acbb212e62addbe48dc8f2282c98cb", 72318},
- {"partie4.itk", 0, "6ce4967e0c79d7daeabc6c1d26783d4c", 2612087},
- {0, 0, 0, 0}
- },
- FR_FRA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "lit",
- "Demo",
- AD_ENTRY1("demo.stk", "c06f8cc20eb239d4c71f225ce3093edf"),
- UNK_LANG,
- kPlatformPC,
- ADGF_DEMO,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesAdLib,
- "demo.stk", "demo.tot", 0
- },
- {
- {
- "lit",
- "Non-interactive Demo",
- AD_ENTRY1("demo.stk", "2eba8abd9e3878c57307576012dd2fec"),
- UNK_LANG,
- kPlatformPC,
- ADGF_DEMO,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesAdLib,
- "demo.stk", "demo.tot", 0
- },
- {
- {
- "fascination",
- "CD Version (Censored)",
- AD_ENTRY1s("disk0.stk", "9c61e9c22077f72921f07153e37ccf01", 545953),
- EN_ANY,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES
- },
- kGameTypeFascination,
- kFeaturesCD,
- "disk0.stk", 0, 0
- },
- {
- {
- "fascination",
- "VGA 3 disks edition",
- AD_ENTRY1s("disk0.stk", "a50a8495e1b2d67699fb562cb98fc3e2", 1064387),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeFascination,
- kFeaturesAdLib,
- "disk0.stk", 0, 0
- },
- {
- {
- "fascination",
- "VGA 3 disks edition",
- AD_ENTRY1s("intro.stk", "d6e45ce548598727e2b5587a99718eba", 1055909),
- HE_ISR,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeFascination,
- kFeaturesAdLib,
- "intro.stk", 0, 0
- },
- { // Supplied by sanguine
- {
- "fascination",
- "VGA 3 disks edition",
- AD_ENTRY1s("disk0.stk", "c14330d052fe4da5a441ac9d81bc5891", 1061955),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeFascination,
- kFeaturesAdLib,
- "disk0.stk", 0, 0
- },
- { // Supplied by windlepoons in bug report #2809247
- {
- "fascination",
- "VGA 3 disks edition",
- AD_ENTRY1s("disk0.stk", "3a24e60a035250189643c86a9ceafb97", 1062480),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeFascination,
- kFeaturesAdLib,
- "disk0.stk", 0, 0
- },
- {
- {
- "fascination",
- "VGA",
- AD_ENTRY1s("disk0.stk", "e8ab4f200a2304849f462dc901705599", 183337),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeFascination,
- kFeaturesAdLib,
- "disk0.stk", 0, 0
- },
- {
- {
- "fascination",
- "",
- AD_ENTRY1s("disk0.stk", "68b1c01564f774c0b640075fbad1b695", 189968),
- DE_DEU,
- kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeFascination,
- kFeaturesNone,
- "disk0.stk", 0, 0
- },
- {
- {
- "fascination",
- "",
- AD_ENTRY1s("disk0.stk", "7062117e9c5adfb6bfb2dac3ff74df9e", 189951),
- EN_ANY,
- kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeFascination,
- kFeaturesNone,
- "disk0.stk", 0, 0
- },
- {
- {
- "fascination",
- "",
- AD_ENTRY1s("disk0.stk", "55c154e5a3e8e98afebdcff4b522e1eb", 190005),
- FR_FRA,
- kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeFascination,
- kFeaturesNone,
- "disk0.stk", 0, 0
- },
- {
- {
- "fascination",
- "",
- AD_ENTRY1s("disk0.stk", "7691827fff35df7799f14cfd6be178ad", 189931),
- IT_ITA,
- kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeFascination,
- kFeaturesNone,
- "disk0.stk", 0, 0
- },
- {
- {
- "fascination",
- "",
- AD_ENTRY1s("disk0.stk", "aff9fcc619f4dd19eae228affd0d34c8", 189964),
- EN_ANY,
- kPlatformAtariST,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeFascination,
- kFeaturesNone,
- "disk0.stk", 0, 0
- },
- {
- {
- "geisha",
- "",
- AD_ENTRY1s("disk1.stk", "6eebbb98ad90cd3c44549fc2ab30f632", 212153),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGeisha,
- kFeaturesNone,
- "disk1.stk", "intro.tot", 0
- },
- {
- {
- "geisha",
- "",
- AD_ENTRY1s("disk1.stk", "f4d4d9d20f7ad1f879fc417d47faba89", 336732),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGeisha,
- kFeaturesNone,
- "disk1.stk", "intro.tot", 0
- },
- {
- {
- "gob3",
- "",
- AD_ENTRY1s("intro.stk", "32b0f57f5ae79a9ae97e8011df38af42", 157084),
- EN_GRB,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob3",
- "",
- AD_ENTRY1s("intro.stk", "904fc32032295baa3efb3a41f17db611", 178582),
- HE_ISR,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Supplied by raziel_ in bug report #1891869
- {
- "gob3",
- "",
- AD_ENTRY1s("intro.stk", "16b014bf32dbd6ab4c5163c44f56fed1", 445104),
- EN_GRB,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob3",
- "",
- {
- {"intro.stk", 0, "16b014bf32dbd6ab4c5163c44f56fed1", 445104},
- {"musmac1.mid", 0, "948c546cad3a9de5bff3fe4107c82bf1", 6404},
- {0, 0, 0, 0}
- },
- DE_DEU,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob3",
- "",
- {
- {"intro.stk", 0, "16b014bf32dbd6ab4c5163c44f56fed1", 445104},
- {"musmac1.mid", 0, "948c546cad3a9de5bff3fe4107c82bf1", 6404},
- {0, 0, 0, 0}
- },
- FR_FRA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob3",
- "",
- {
- {"intro.stk", 0, "16b014bf32dbd6ab4c5163c44f56fed1", 445104},
- {"musmac1.mid", 0, "948c546cad3a9de5bff3fe4107c82bf1", 6404},
- {0, 0, 0, 0}
- },
- EN_GRB,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Supplied by fac76 in bug report #1742716
- {
- "gob3",
- "",
- {
- {"intro.stk", 0, "32b0f57f5ae79a9ae97e8011df38af42", 157084},
- {"musmac1.mid", 0, "834e55205b710d0af5f14a6f2320dd8e", 8661},
- {0, 0, 0, 0}
- },
- EN_GRB,
- kPlatformMacintosh,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob3",
- "",
- AD_ENTRY1("intro.stk", "1e2f64ec8dfa89f42ee49936a27e66e7"),
- EN_USA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Supplied by paul66 in bug report #1652352
- {
- "gob3",
- "",
- AD_ENTRY1("intro.stk", "f6d225b25a180606fa5dbe6405c97380"),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob3",
- "",
- AD_ENTRY1("intro.stk", "e42a4f2337d6549487a80864d7826972"),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Supplied by Paranoimia on #scummvm
- {
- "gob3",
- "",
- AD_ENTRY1s("intro.stk", "fe8144daece35538085adb59c2d29613", 159402),
- IT_ITA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob3",
- "",
- AD_ENTRY1("intro.stk", "4e3af248a48a2321364736afab868527"),
- RU_RUS,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob3",
- "",
- AD_ENTRY1("intro.stk", "8d28ce1591b0e9cc79bf41cad0fc4c9c"),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Supplied by SiRoCs in bug report #2098621
- {
- "gob3",
- "",
- AD_ENTRY1s("intro.stk", "d3b72938fbbc8159198088811f9e6d19", 160382),
- ES_ESP,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob3",
- "",
- AD_ENTRY1("intro.stk", "bd679eafde2084d8011f247e51b5a805"),
- EN_GRB,
- kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesNone,
- 0, "menu.tot", 0
- },
- {
- {
- "gob3",
- "",
- AD_ENTRY1("intro.stk", "bd679eafde2084d8011f247e51b5a805"),
- DE_DEU,
- kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesNone,
- 0, "menu.tot", 0
- },
- {
- {
- "gob3",
- "",
- {
- {"intro.stk", 0, "edd7403e5dc2a14459d2665a4c17714d", 209534},
- {"musmac1.mid", 0, "948c546cad3a9de5bff3fe4107c82bf1", 6404},
- {0, 0, 0, 0}
- },
- FR_FRA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob3",
- "",
- {
- {"intro.stk", 0, "428e2de130cf3b303c938924539dc50d", 324420},
- {"musmac1.mid", 0, "948c546cad3a9de5bff3fe4107c82bf1", 6404},
- {0, 0, 0, 0}
- },
- FR_FRA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob3",
- "",
- {
- {"intro.stk", 0, "428e2de130cf3b303c938924539dc50d", 324420},
- {"musmac1.mid", 0, "948c546cad3a9de5bff3fe4107c82bf1", 6404},
- {0, 0, 0, 0}
- },
- EN_ANY,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { // Found in Found in french ADI 2.5 Anglais Multimedia 5e
- {
- "gob3",
- "",
- AD_ENTRY1s("intro.stk", "edd7403e5dc2a14459d2665a4c17714d", 209534),
- FR_FRA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob3cd",
- "v1.000",
- AD_ENTRY1("intro.stk", "6f2c226c62dd7ab0ab6f850e89d3fc47"),
- EN_USA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesCD,
- 0, 0, 0
- },
- { // Supplied by paul66 and noizert in bug reports #1652352 and #1691230
- {
- "gob3cd",
- "v1.02",
- AD_ENTRY1("intro.stk", "c3e9132ea9dc0fb866b6d60dcda10261"),
- EN_ANY,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesCD,
- 0, 0, 0
- },
- { // Supplied by paul66 and noizert in bug reports #1652352 and #1691230
- {
- "gob3cd",
- "v1.02",
- AD_ENTRY1("intro.stk", "c3e9132ea9dc0fb866b6d60dcda10261"),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesCD,
- 0, 0, 0
- },
- { // Supplied by paul66 and noizert in bug reports #1652352 and #1691230
- {
- "gob3cd",
- "v1.02",
- AD_ENTRY1("intro.stk", "c3e9132ea9dc0fb866b6d60dcda10261"),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesCD,
- 0, 0, 0
- },
- { // Supplied by paul66 and noizert in bug reports #1652352 and #1691230
- {
- "gob3cd",
- "v1.02",
- AD_ENTRY1("intro.stk", "c3e9132ea9dc0fb866b6d60dcda10261"),
- IT_ITA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesCD,
- 0, 0, 0
- },
- { // Supplied by paul66 and noizert in bug reports #1652352 and #1691230
- {
- "gob3cd",
- "v1.02",
- AD_ENTRY1("intro.stk", "c3e9132ea9dc0fb866b6d60dcda10261"),
- ES_ESP,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesCD,
- 0, 0, 0
- },
- { // Supplied by goodoldgeorg in bug report #2810082
- {
- "gob3cd",
- "v1.02",
- AD_ENTRY1s("intro.stk", "bfd7d4c6fedeb2cfcc8baa4d5ddb1f74", 616220),
- HU_HUN,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesCD,
- 0, 0, 0
- },
- { // Supplied by goodoldgeorg in bug report #2810082
- {
- "gob3cd",
- "v1.02",
- AD_ENTRY1s("intro.stk", "bfd7d4c6fedeb2cfcc8baa4d5ddb1f74", 616220),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesCD,
- 0, 0, 0
- },
- { // Supplied by goodoldgeorg in bug report #2810082
- {
- "gob3cd",
- "v1.02",
- AD_ENTRY1s("intro.stk", "bfd7d4c6fedeb2cfcc8baa4d5ddb1f74", 616220),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesCD,
- 0, 0, 0
- },
- { // Supplied by goodoldgeorg in bug report #2810082
- {
- "gob3cd",
- "v1.02",
- AD_ENTRY1s("intro.stk", "bfd7d4c6fedeb2cfcc8baa4d5ddb1f74", 616220),
- ES_ESP,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesCD,
- 0, 0, 0
- },
- {
- {
- "gob3",
- "Interactive Demo",
- AD_ENTRY1("intro.stk", "7aebd94e49c2c5c518c9e7b74f25de9d"),
- FR_FRA,
- kPlatformPC,
- ADGF_DEMO,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob3",
- "Interactive Demo 2",
- AD_ENTRY1("intro.stk", "e5dcbc9f6658ebb1e8fe26bc4da0806d"),
- FR_FRA,
- kPlatformPC,
- ADGF_DEMO,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob3",
- "Interactive Demo 3",
- AD_ENTRY1s("intro.stk", "9e20ad7b471b01f84db526da34eaf0a2", 395561),
- EN_ANY,
- kPlatformPC,
- ADGF_DEMO,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "gob3",
- "Non-interactive Demo",
- AD_ENTRY1("intro.stk", "b9b898fccebe02b69c086052d5024a55"),
- UNK_LANG,
- kPlatformPC,
- ADGF_DEMO,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "inca2",
- "",
- AD_ENTRY1s("intro.stk", "47c3b452767c4f49ea7b109143e77c30", 916828),
- EN_USA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeInca2,
- kFeaturesCD,
- 0, 0, 0
- },
- {
- {
- "inca2",
- "",
- AD_ENTRY1s("intro.stk", "47c3b452767c4f49ea7b109143e77c30", 916828),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeInca2,
- kFeaturesCD,
- 0, 0, 0
- },
- {
- {
- "inca2",
- "",
- AD_ENTRY1s("intro.stk", "47c3b452767c4f49ea7b109143e77c30", 916828),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeInca2,
- kFeaturesCD,
- 0, 0, 0
- },
- {
- {
- "inca2",
- "",
- AD_ENTRY1s("intro.stk", "47c3b452767c4f49ea7b109143e77c30", 916828),
- IT_ITA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeInca2,
- kFeaturesCD,
- 0, 0, 0
- },
- {
- {
- "inca2",
- "",
- AD_ENTRY1s("intro.stk", "47c3b452767c4f49ea7b109143e77c30", 916828),
- ES_ESP,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeInca2,
- kFeaturesCD,
- 0, 0, 0
- },
- {
- {
- "inca2",
- "",
- AD_ENTRY1s("intro.stk", "1fa92b00fe80a20f34ec34a8e2fa869e", 923072),
- EN_USA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeInca2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "inca2",
- "",
- AD_ENTRY1s("intro.stk", "1fa92b00fe80a20f34ec34a8e2fa869e", 923072),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeInca2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "inca2",
- "",
- AD_ENTRY1s("intro.stk", "1fa92b00fe80a20f34ec34a8e2fa869e", 923072),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeInca2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "inca2",
- "",
- AD_ENTRY1s("intro.stk", "d33011df8758ac64ca3dca77c7719001", 908612),
- EN_USA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeInca2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "inca2",
- "",
- AD_ENTRY1s("intro.stk", "d33011df8758ac64ca3dca77c7719001", 908612),
- DE_DEU,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeInca2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "inca2",
- "",
- AD_ENTRY1s("intro.stk", "d33011df8758ac64ca3dca77c7719001", 908612),
- IT_ITA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeInca2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "inca2",
- "",
- AD_ENTRY1s("intro.stk", "d33011df8758ac64ca3dca77c7719001", 908612),
- ES_ESP,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeInca2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "inca2",
- "",
- AD_ENTRY1s("intro.stk", "d33011df8758ac64ca3dca77c7719001", 908612),
- FR_FRA,
- kPlatformWindows,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeInca2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "inca2",
- "Non-Interactive Demo",
- {
- {"cons.imd", 0, "f896ba0c4a1ac7f7260d342655980b49", 17804},
- {"conseil.imd", 0, "aaedd5482d5b271e233e86c5a03cf62e", 33999},
- {"int.imd", 0, "6308222fcefbcb20925f01c1aff70dee", 30871},
- {"inter.imd", 0, "39bd6d3540f3bedcc97293f352c7f3fc", 191719},
- {"machu.imd", 0, "c0bc8211d93b467bfd063b63fe61b85c", 34609},
- {"post.imd", 0, "d75cad0e3fc22cb0c8b6faf597f509b2", 1047709},
- {"posta.imd", 0, "2a5b3fe75681ddf4d21ac724db8111b4", 547250},
- {"postb.imd", 0, "24260ce4e80a4c472352b76637265d09", 868312},
- {"postc.imd", 0, "24accbcc8b83a9c2be4bd82849a2bd29", 415637},
- {"tum.imd", 0, "0993d4810ec9deb3f77c5e92095320fd", 20330},
- {"tumi.imd", 0, "bf53f229480d694de0947fe3366fbec6", 248952},
- {0, 0, 0, 0}
- },
- EN_ANY,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeInca2,
- kFeaturesAdLib | kFeaturesBATDemo,
- 0, 0, 7
- },
- {
- {
- "woodruff",
- "",
- AD_ENTRY1s("intro.stk", "dccf9d31cb720b34d75487408821b77e", 20296390),
- EN_GRB,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "woodruff",
- "",
- AD_ENTRY1s("intro.stk", "dccf9d31cb720b34d75487408821b77e", 20296390),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "woodruff",
- "",
- AD_ENTRY1s("intro.stk", "dccf9d31cb720b34d75487408821b77e", 20296390),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "woodruff",
- "",
- AD_ENTRY1s("intro.stk", "dccf9d31cb720b34d75487408821b77e", 20296390),
- IT_ITA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "woodruff",
- "",
- AD_ENTRY1s("intro.stk", "dccf9d31cb720b34d75487408821b77e", 20296390),
- ES_ESP,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "woodruff",
- "",
- AD_ENTRY1s("intro.stk", "b50fee012a5abcd0ac2963e1b4b56bec", 20298108),
- EN_GRB,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "woodruff",
- "",
- AD_ENTRY1s("intro.stk", "b50fee012a5abcd0ac2963e1b4b56bec", 20298108),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "woodruff",
- "",
- AD_ENTRY1s("intro.stk", "b50fee012a5abcd0ac2963e1b4b56bec", 20298108),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "woodruff",
- "",
- AD_ENTRY1s("intro.stk", "b50fee012a5abcd0ac2963e1b4b56bec", 20298108),
- IT_ITA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "woodruff",
- "",
- AD_ENTRY1s("intro.stk", "b50fee012a5abcd0ac2963e1b4b56bec", 20298108),
- ES_ESP,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "woodruff",
- "",
- AD_ENTRY1s("intro.stk", "5f5f4e0a72c33391e67a47674b120cc6", 20296422),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640,
- 0, 0, 0
- },
- { // Supplied by jvprat on #scummvm
- {
- "woodruff",
- "",
- AD_ENTRY1s("intro.stk", "270529d9b8cce770b1575908a3800b52", 20296452),
- ES_ESP,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640,
- 0, 0, 0
- },
- { // Supplied by jvprat on #scummvm
- {
- "woodruff",
- "",
- AD_ENTRY1s("intro.stk", "270529d9b8cce770b1575908a3800b52", 20296452),
- EN_GRB,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640,
- 0, 0, 0
- },
- { // Supplied by jvprat on #scummvm
- {
- "woodruff",
- "",
- AD_ENTRY1s("intro.stk", "270529d9b8cce770b1575908a3800b52", 20296452),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640,
- 0, 0, 0
- },
- { // Supplied by jvprat on #scummvm
- {
- "woodruff",
- "",
- AD_ENTRY1s("intro.stk", "270529d9b8cce770b1575908a3800b52", 20296452),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640,
- 0, 0, 0
- },
- { // Supplied by jvprat on #scummvm
- {
- "woodruff",
- "",
- AD_ENTRY1s("intro.stk", "270529d9b8cce770b1575908a3800b52", 20296452),
- IT_ITA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640,
- 0, 0, 0
- },
- { // Supplied by Hkz on #scummvm
- {
- "woodruff",
- "",
- AD_ENTRY1s("intro.stk", "f4c344023b073782d2fddd9d8b515318", 7069736),
- IT_ITA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640,
- 0, 0, 0
- },
- { // Supplied by Hkz on #scummvm
- {
- "woodruff",
- "",
- AD_ENTRY1s("intro.stk", "f4c344023b073782d2fddd9d8b515318", 7069736),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640,
- 0, 0, 0
- },
- { // Supplied by Hkz on #scummvm
- {
- "woodruff",
- "",
- AD_ENTRY1s("intro.stk", "f4c344023b073782d2fddd9d8b515318", 7069736),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640,
- 0, 0, 0
- },
- { // Supplied by DjDiabolik in bug report #1971294
- {
- "woodruff",
- "",
- AD_ENTRY1s("intro.stk", "60348a87651f92e8492ee070556a96d8", 7069736),
- EN_GRB,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640,
- 0, 0, 0
- },
- { // Supplied by DjDiabolik in bug report #1971294
- {
- "woodruff",
- "",
- AD_ENTRY1s("intro.stk", "60348a87651f92e8492ee070556a96d8", 7069736),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640,
- 0, 0, 0
- },
- { // Supplied by DjDiabolik in bug report #1971294
- {
- "woodruff",
- "",
- AD_ENTRY1s("intro.stk", "60348a87651f92e8492ee070556a96d8", 7069736),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640,
- 0, 0, 0
- },
- { // Supplied by DjDiabolik in bug report #1971294
- {
- "woodruff",
- "",
- AD_ENTRY1s("intro.stk", "60348a87651f92e8492ee070556a96d8", 7069736),
- IT_ITA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640,
- 0, 0, 0
- },
- { // Supplied by DjDiabolik in bug report #1971294
- {
- "woodruff",
- "",
- AD_ENTRY1s("intro.stk", "60348a87651f92e8492ee070556a96d8", 7069736),
- ES_ESP,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640,
- 0, 0, 0
- },
- { // Supplied by goodoldgeorg in bug report #2098838
- {
- "woodruff",
- "",
- AD_ENTRY1s("intro.stk", "08a96bf061af1fa4f75c6a7cc56b60a4", 20734979),
- PL_POL,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "woodruff",
- "Non-Interactive Demo",
- {
- {"demo.scn", 0, "16bb85fc5f8e519147b60475dbf33962", 89},
- {"wooddem3.vmd", 0, "a1700596172c2d4e264760030c3a3d47", 8994250},
- {0, 0, 0, 0}
- },
- EN_ANY,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640 | kFeaturesSCNDemo,
- 0, 0, 1
- },
- {
- {
- "dynasty",
- "",
- AD_ENTRY1s("intro.stk", "6190e32404b672f4bbbc39cf76f41fda", 2511470),
- EN_USA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeDynasty,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "dynasty",
- "",
- AD_ENTRY1s("intro.stk", "61e4069c16e27775a6cc6d20f529fb36", 2511300),
- EN_USA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeDynasty,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "dynasty",
- "",
- AD_ENTRY1s("intro.stk", "61e4069c16e27775a6cc6d20f529fb36", 2511300),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeDynasty,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "dynasty",
- "",
- AD_ENTRY1s("intro.stk", "b3f8472484b7a1df94557b51e7b6fca0", 2322644),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeDynasty,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "dynasty",
- "",
- AD_ENTRY1s("intro.stk", "bdbdac8919200a5e71ffb9fb0709f704", 2446652),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeDynasty,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "dynasty",
- "Demo",
- AD_ENTRY1s("intro.stk", "464538a17ed39755d7f1ba9c751af1bd", 1847864),
- EN_USA,
- kPlatformPC,
- ADGF_DEMO,
- GUIO_NONE
- },
- kGameTypeDynasty,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "dynasty",
- "Demo",
- AD_ENTRY1s("lda1.stk", "0e56a899357cbc0bf503260fd2dd634e", 15032774),
- UNK_LANG,
- kPlatformWindows,
- ADGF_DEMO,
- GUIO_NONE
- },
- kGameTypeDynasty,
- kFeatures640,
- "lda1.stk", 0, 0
- },
- {
- {
- "dynasty",
- "Demo",
- AD_ENTRY1s("lda1.stk", "8669ea2e9a8239c070dc73958fbc8753", 15567724),
- DE_DEU,
- kPlatformWindows,
- ADGF_DEMO,
- GUIO_NONE
- },
- kGameTypeDynasty,
- kFeatures640,
- "lda1.stk", 0, 0
- },
- {
- {
- "urban",
- "",
- AD_ENTRY1s("intro.stk", "3ab2c542bd9216ae5d02cc6f45701ae1", 1252436),
- EN_USA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeUrban,
- kFeatures640,
- 0, 0, 0
- },
- { // Supplied by gamin in the forums
- {
- "urban",
- "",
- AD_ENTRY1s("intro.stk", "b991ed1d31c793e560edefdb349882ef", 1276408),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeUrban,
- kFeatures640,
- 0, 0, 0
- },
- { // Supplied by jvprat on #scummvm
- {
- "urban",
- "",
- AD_ENTRY1s("intro.stk", "4ec3c0864e2b54c5b4ccf9f6ad96528d", 1253328),
- ES_ESP,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeUrban,
- kFeatures640,
- 0, 0, 0
- },
- { // Supplied by goodoldgeorg in bug report #2770340
- {
- "urban",
- "",
- AD_ENTRY1s("intro.stk", "4bd31979ea3d77a58a358c09000a85ed", 1253018),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeUrban,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "urban",
- "Non-Interactive Demo",
- {
- {"wdemo.s24", 0, "14ac9bd51db7a075d69ddb144904b271", 87},
- {"demo.vmd", 0, "65d04715d871c292518b56dd160b0161", 9091237},
- {"urband.vmd", 0, "60343891868c91854dd5c82766c70ecc", 922461},
- {0, 0, 0, 0}
- },
- EN_ANY,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeUrban,
- kFeatures640 | kFeaturesSCNDemo,
- 0, 0, 2
- },
- {
- {
- "playtoons1",
- "",
- {
- {"playtoon.stk", 0, "8c98e9a11be9bb203a55e8c6e68e519b", 25574338},
- {"archi.stk", 0, "8d44b2a0d4e3139471213f9f0ed21e81", 5524674},
- {0, 0, 0, 0}
- },
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640,
- "intro2.stk", 0, 0
- },
- {
- {
- "playtoons1",
- "Pack mes histoires anim\xE9""es",
- {
- {"playtoon.stk", 0, "55f0293202963854192e39474e214f5f", 30448474},
- {"archi.stk", 0, "8d44b2a0d4e3139471213f9f0ed21e81", 5524674},
- {0, 0, 0, 0}
- },
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640,
- "intro2.stk", 0, 0
- },
- {
- {
- "playtoons1",
- "",
- {
- {"playtoon.stk", 0, "c5ca2a288cdaefca9556cd9ae4b579cf", 25158926},
- {"archi.stk", 0, "8d44b2a0d4e3139471213f9f0ed21e81", 5524674},
- {0, 0, 0, 0}
- },
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640,
- "intro2.stk", 0, 0
- },
- { // Supplied by scoriae in the forums
- {
- "playtoons1",
- "",
- {
- {"playtoon.stk", 0, "9e513e993a5b0e2496add3f50c08764b", 30448506},
- {"archi.stk", 0, "00d8274519dfcf8a0d8ae3099daea0f8", 5532135},
- {0, 0, 0, 0}
- },
- EN_ANY,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640,
- "intro2.stk", 0, 0
- },
- {
- {
- "playtoons1",
- "Non-Interactive Demo",
- {
- {"play123.scn", 0, "4689a31f543915e488c3bc46ea358add", 258},
- {"archi.vmd", 0, "a410fcc8116bc173f038100f5857191c", 5617210},
- {"chato.vmd", 0, "5a10e39cb66c396f2f9d8fb35e9ac016", 5445937},
- {"genedeb.vmd", 0, "3bb4a45585f88f4d839efdda6a1b582b", 1244228},
- {"generik.vmd", 0, "b46bdd64b063e86927fb2826500ad512", 603242},
- {"genespi.vmd", 0, "b7611916f32a370ae9832962fc17ef72", 758719},
- {"spirou.vmd", 0, "8513dbf7ac51c057b21d371d6b217b47", 2550788},
- {0, 0, 0, 0}
- },
- EN_ANY,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640 | kFeaturesSCNDemo,
- 0, 0, 3
- },
- {
- {
- "playtoons1",
- "Non-Interactive Demo",
- {
- {"e.scn", 0, "8a0db733c3f77be86e74e8242e5caa61", 124},
- {"demarchg.vmd", 0, "d14a95da7d8792faf5503f649ffcbc12", 5619415},
- {0, 0, 0, 0}
- },
- EN_ANY,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640 | kFeaturesSCNDemo,
- 0, 0, 4
- },
- {
- {
- "playtoons1",
- "Non-Interactive Demo",
- {
- {"i.scn", 0, "8b3294474d39970463663edd22341730", 285},
- {"demarita.vmd", 0, "84c8672b91c7312462603446e224bfec", 5742533},
- {"dembouit.vmd", 0, "7a5fdf0a4dbdfe72e31dd489ea0f8aa2", 3536786},
- {"demo5.vmd", 0, "2abb7b6a26406c984f389f0b24b5e28e", 13290970},
- {"demoita.vmd", 0, "b4c0622d14c8749965cd0f5dfca4cf4b", 1183566},
- {"wooddem3.vmd", 0, "a1700596172c2d4e264760030c3a3d47", 8994250},
- {0, 0, 0, 0}
- },
- IT_ITA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640 | kFeaturesSCNDemo,
- 0, 0, 5
- },
- {
- {
- "playtoons1",
- "Non-Interactive Demo",
- {
- {"s.scn", 0, "1f527010626b5490761f16ba7a6f639a", 251},
- {"demaresp.vmd", 0, "3f860f944056842b35a5fd05416f208e", 5720619},
- {"demboues.vmd", 0, "3a0caa10c98ef92a15942f8274075b43", 3535838},
- {"demo5.vmd", 0, "2abb7b6a26406c984f389f0b24b5e28e", 13290970},
- {"wooddem3.vmd", 0, "a1700596172c2d4e264760030c3a3d47", 8994250},
- {0, 0, 0, 0}
- },
- ES_ESP,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640 | kFeaturesSCNDemo,
- 0, 0, 6
- },
- {
- {
- "playtoons2",
- "",
- {
- {"playtoon.stk", 0, "4772c96be88a57f0561519e4a1526c62", 24406262},
- {"spirou.stk", 0, "5d9c7644d0c47840169b4d016765cc1a", 9816201},
- {0, 0, 0, 0}
- },
- EN_ANY,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640,
- "intro2.stk", 0, 0
- },
- {
- {
- "playtoons2",
- "",
- {
- {"playtoon.stk", 0, "55a85036dd93cce93532d8f743d90074", 17467154},
- {"spirou.stk", 0, "e3e1b6148dd72fafc3637f1a8e5764f5", 9812043},
- {0, 0, 0, 0}
- },
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640,
- "intro2.stk", 0, 0
- },
- {
- {
- "playtoons2",
- "",
- {
- {"playtoon.stk", 0, "c5ca2a288cdaefca9556cd9ae4b579cf", 25158926},
- {"spirou.stk", 0, "91080dc148de1bbd6a97321c1a1facf3", 9817086},
- {0, 0, 0, 0}
- },
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640,
- "intro2.stk", 0, 0
- },
- { // Supplied by scoriae in the forums
- {
- "playtoons2",
- "",
- {
- {"playtoon.stk", 0, "9e513e993a5b0e2496add3f50c08764b", 30448506},
- {"spirou.stk", 0, "993737f112ca6a9b33c814273280d832", 9825760},
- {0, 0, 0, 0}
- },
- EN_ANY,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640,
- "intro2.stk", 0, 0
- },
- {
- {
- "playtoons3",
- "",
- {
- {"playtoon.stk", 0, "8c98e9a11be9bb203a55e8c6e68e519b", 25574338},
- {"chato.stk", 0, "4fa4ed96a427c344e9f916f9f236598d", 6033793},
- {0, 0, 0, 0}
- },
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640,
- "intro2.stk", 0, 0
- },
- {
- {
- "playtoons3",
- "",
- {
- {"playtoon.stk", 0, "9e513e993a5b0e2496add3f50c08764b", 30448506},
- {"chato.stk", 0, "8fc8d0da5b3e758908d1d7298d497d0b", 6041026},
- {0, 0, 0, 0}
- },
- EN_ANY,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640,
- "intro2.stk", 0, 0
- },
- {
- {
- "playtoons3",
- "Pack mes histoires anim\xE9""es",
- {
- {"playtoon.stk", 0, "55f0293202963854192e39474e214f5f", 30448474},
- {"chato.stk", 0, "4fa4ed96a427c344e9f916f9f236598d", 6033793},
- {0, 0, 0, 0}
- },
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640,
- "intro2.stk", 0, 0
- },
- {
- {
- "playtoons3",
- "",
- {
- {"playtoon.stk", 0, "c5ca2a288cdaefca9556cd9ae4b579cf", 25158926},
- {"chato.stk", 0, "3c6cb3ac8a5a7cf681a19971a92a748d", 6033791},
- {0, 0, 0, 0}
- },
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640,
- "intro2.stk", 0, 0
- },
- { // Supplied by Hkz on #scummvm
- {
- "playtoons3",
- "",
- {
- {"playtoon.stk", 0, "4772c96be88a57f0561519e4a1526c62", 24406262},
- {"chato.stk", 0, "bdef407387112bfcee90e664865ac3af", 6033867},
- {0, 0, 0, 0}
- },
- EN_ANY,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640,
- "intro2.stk", 0, 0
- },
- {
- {
- "playtoons4",
- "",
- {
- {"playtoon.stk", 0, "b7f5afa2dc1b0f75970b7c07d175db1b", 24340406},
- {"manda.stk", 0, "92529e0b927191d9898a34c2892e9a3a", 6485072},
- {0, 0, 0, 0}
- },
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640,
- "intro2.stk", 0, 0
- },
- { //Supplied by goodoldgeorg in bug report #2820006
- {
- "playtoons4",
- "",
- {
- {"playtoon.stk", 0, "9e513e993a5b0e2496add3f50c08764b", 30448506},
- {"manda.stk", 0, "69a79c9f61b2618e482726f2ff68078d", 6499208},
- {0, 0, 0, 0}
- },
- EN_ANY,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640,
- "intro2.stk", 0, 0
- },
- {
- {
- "playtoons5",
- "",
- {
- {"playtoon.stk", 0, "55f0293202963854192e39474e214f5f", 30448474},
- {"wakan.stk", 0, "f493bf82851bc5ba74d57de6b7e88df8", 5520153},
- {0, 0, 0, 0}
- },
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640,
- "intro2.stk", 0, 0
- },
- {
- {
- "bambou",
- "",
- {
- {"intro.stk", 0, "2f8db6963ff8d72a8331627ebda918f4", 3613238},
- {"bambou.itk", 0, "0875914d31126d0749313428f10c7768", 114440192},
- {0, 0, 0, 0}
- },
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeBambou,
- kFeatures640,
- "intro.stk", "intro.tot", 0
- },
- {
- {
- "playtnck1",
- "",
- {
- {"playtoon.stk", 0, "5f9aae29265f1f105ad8ec195dff81de", 68382024},
- {"dan.itk", 0, "906d67b3e438d5e95ec7ea9e781a94f3", 3000320},
- {0, 0, 0, 0}
- },
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640,
- "intro2.stk", 0, 0
- },
- {
- {
- "playtnck2",
- "",
- {
- {"playtoon.stk", 0, "5f9aae29265f1f105ad8ec195dff81de", 68382024},
- {"dan.itk", 0, "74eeb075bd2cb47b243349730264af01", 3213312},
- {0, 0, 0, 0}
- },
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640,
- "intro2.stk", 0, 0
- },
- {
- {
- "playtnck3",
- "",
- {
- {"playtoon.stk", 0, "5f9aae29265f1f105ad8ec195dff81de", 68382024},
- {"dan.itk", 0, "9a8f62809eca5a52f429b5b6a8e70f8f", 2861056},
- {0, 0, 0, 0}
- },
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640,
- "intro2.stk", 0, 0
- },
- {
- {
- "adi2",
- "Adi 2.0 for Teachers",
- AD_ENTRY1s("adi2.stk", "da6f1fb68bff32260c5eecdf9286a2f5", 1533168),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdi2,
- kFeaturesNone,
- "adi2.stk", "ediintro.tot", 0
- },
- { // Found in french ADI 2 Francais-Maths CM1. Exact version not specified.
- {
- "adi2",
- "Adi 2",
- AD_ENTRY1s("adi2.stk", "23f279615c736dc38320f1348e70c36e", 10817668),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdi2,
- kFeatures640,
- "adi2.stk", "ediintro.tot", 0
- },
- { // Found in french ADI 2 Francais-Maths CE2. Exact version not specified.
- {
- "adi2",
- "Adi 2",
- AD_ENTRY1s("adi2.stk", "d4162c4298f9423ecc1fb04965557e90", 11531214),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdi2,
- kFeatures640,
- "adi2.stk", "ediintro.tot", 0
- },
- {
- {
- "adi2",
- "Adi 2",
- AD_ENTRY1s("adi2.stk", "29694c5a649298a42f87ae731d6d6f6d", 311132),
- EN_ANY,
- kPlatformAmiga,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdi2,
- kFeaturesNone,
- "adi2.stk", "ediintro.tot", 0
- },
- {
- {
- "adi2",
- "Adi 2",
- AD_ENTRY1s("adi2.stk", "2a40bb48ccbd4e6fb3f7f0fc2f069d80", 17720132),
- ES_ESP,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdi2,
- kFeatures640,
- "adi2.stk", "ediintro.tot", 0
- },
- {
- {
- "adi2",
- "Adi 2.5",
- AD_ENTRY1s("adi2.stk", "fcac60e6627f37aee219575b60859de9", 16944268),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdi2,
- kFeatures640,
- "adi2.stk", "ediintro.tot", 0
- },
- {
- {
- "adi2",
- "Adi 2.5",
- AD_ENTRY1s("adi2.stk", "072d5e2d7826a7c055865568ebf918bb", 16934596),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdi2,
- kFeatures640,
- "adi2.stk", "ediintro.tot", 0
- },
- {
- {
- "adi2",
- "Adi 2.6",
- AD_ENTRY1s("adi2.stk", "2fb940eb8105b12871f6b88c8c4d1615", 16780058),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdi2,
- kFeatures640,
- "adi2.stk", "ediintro.tot", 0
- },
- {
- {
- "adi2",
- "Adi 2.6",
- AD_ENTRY1s("adi2.stk", "fde7d98a67dbf859423b6473796e932a", 18044780),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdi2,
- kFeatures640,
- "adi2.stk", "ediintro.tot", 0
- },
- {
- {
- "adi2",
- "Adi 2.7.1",
- AD_ENTRY1s("adi2.stk", "6fa5dffebf5c7243c6af6b8c188ee00a", 19278008),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdi2,
- kFeatures640,
- "adi2.stk", "ediintro.tot", 0
- },
- {
- {
- "adi2",
- "Non-Interactive Demo",
- {
- {"demo.scn", 0, "8b5ba359fd87d586ad39c1754bf6ea35", 168},
- {"demadi2t.vmd", 0, "08a1b18cfe2015d3b43270da35cc813d", 7250723},
- {"demarch.vmd", 0, "4c4a4616585d40ef3df209e3c3911062", 5622731},
- {"demobou.vmd", 0, "2208b9855775564d15c4a5a559da0aec", 3550511},
- {0, 0, 0, 0}
- },
- EN_ANY,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeAdi2,
- kFeatures640 | kFeaturesSCNDemo,
- 0, 0, 1
- },
- {
- {
- "adi4",
- "Addy 4 Grundschule Basis CD",
- AD_ENTRY1s("intro.stk", "d2f0fb8909e396328dc85c0e29131ba8", 5847588),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdi4,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "adi4",
- "Addy 4 Sekundarstufe Basis CD",
- AD_ENTRY1s("intro.stk", "367340e59c461b4fa36651cd74e32c4e", 5847378),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdi4,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "adi4",
- "Adi 4.0",
- AD_ENTRY1s("intro.stk", "a3c35d19b2d28ea261d96321d208cb5a", 6021466),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdi4,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "adi4",
- "Adi 4.0",
- AD_ENTRY1s("intro.stk", "44491d85648810bc6fcf84f9b3aa47d5", 5834944),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdi4,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "adi4",
- "Adi 4.0",
- AD_ENTRY1s("intro.stk", "29374c0e3c10b17dd8463b06a55ad093", 6012072),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdi4,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "adi4",
- "Adi 4.0 Limited Edition",
- AD_ENTRY1s("intro.stk", "ebbbc5e28a4adb695535ed989c1b8d66", 5929644),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdi4,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "adi4",
- "ADI 4.10",
- AD_ENTRY1s("intro.stk", "3e3fa9656e37d802027635ace88c4cc5", 5359144),
- EN_GRB,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdi4,
- kFeaturesNone,
- 0, 0, 0
- },
- {
- {
- "adi4",
- "ADI 4.10",
- AD_ENTRY1s("intro.stk", "6afc2590856433b9f5295b032f2b205d", 5923112),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdi4,
- kFeaturesNone,
- 0, 0, 0
- },
- {
- {
- "adi4",
- "ADI 4.11",
- AD_ENTRY1s("intro.stk", "6296e4be4e0c270c24d1330881900c7f", 5921234),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdi4,
- kFeaturesNone,
- 0, 0, 0
- },
- {
- {
- "adi4",
- "Addy 4.21",
- AD_ENTRY1s("intro.stk", "534f0b674cd4830df94a9c32c4ea7225", 6878034),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdi4,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "adi4",
- "ADI 4.21",
- AD_ENTRY1s("intro.stk", "c5b9f6222c0b463f51dab47317c5b687", 5950490),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdi4,
- kFeaturesNone,
- 0, 0, 0
- },
- {
- {
- "adi4",
- "Adi 4.0 Interactive Demo",
- AD_ENTRY1s("intro.stk", "89ace204dbaac001425c73f394334f6f", 2413102),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdi4,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "adi4",
- "Adi 4.0 / Adibou 2 Demo",
- AD_ENTRY1s("intro.stk", "d41d8cd98f00b204e9800998ecf8427e", 0),
- FR_FRA,
- kPlatformPC,
- ADGF_DEMO,
- GUIO_NONE
- },
- kGameTypeAdi4,
- kFeatures640,
- 0, 0, 0
- },
- {
- {
- "ajworld",
- "",
- AD_ENTRY1s("intro.stk", "e453bea7b28a67c930764d945f64d898", 3913628),
- EN_ANY,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "adibou1",
- "ADIBOU 1 Environnement 4-7 ans",
- AD_ENTRY1s("intro.stk", "6db110188fcb7c5208d9721b5282682a", 4805104),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeAdibou1,
- kFeaturesAdLib,
- 0, 0, 0
- },
- {
- {
- "adibou2",
- "ADIBOU 2",
- AD_ENTRY1s("intro.stk", "94ae7004348dc8bf99c23a9a6ef81827", 956162),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdibou2,
- kFeaturesNone,
- 0, 0, 0
- },
- {
- {
- "adibou2",
- "Le Jardin Magique d'Adibou",
- AD_ENTRY1s("intro.stk", "a8ff86f3cc40dfe5898e0a741217ef27", 956328),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdibou2,
- kFeaturesNone,
- 0, 0, 0
- },
- {
- {
- "adibou2",
- "ADIBOU 2",
- AD_ENTRY1s("intro.stk", "092707829555f27706920e4cacf1fada", 8737958),
- DE_DEU,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdibou2,
- kFeaturesNone,
- 0, 0, 0
- },
- {
- {
- "adibou2",
- "ADIB\xD9 2",
- AD_ENTRY1s("intro.stk", "092707829555f27706920e4cacf1fada", 8737958),
- IT_ITA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdibou2,
- kFeaturesNone,
- 0, 0, 0
- },
- {
- {
- "adibou2",
- "ADIBOU Version Decouverte",
- AD_ENTRY1s("intro.stk", "558c14327b79ed39214b49d567a75e33", 8737856),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdibou2,
- kFeaturesNone,
- 0, 0, 0
- },
- {
- {
- "adibou2",
- "ADIBOU 2.10 Environnement",
- AD_ENTRY1s("intro.stk", "f2b797819aeedee557e904b0b5ccd82e", 8736454),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdibou2,
- kFeaturesNone,
- 0, 0, 0
- },
- {
- {
- "adibou2",
- "ADIBOU 2.11 Environnement",
- AD_ENTRY1s("intro.stk", "7b1f1f6f6477f54401e95d913f75e333", 8736904),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdibou2,
- kFeaturesNone,
- 0, 0, 0
- },
- {
- {
- "adibou2",
- "ADIBOU 2.12 Environnement",
- AD_ENTRY1s("intro.stk", "1e49c39a4a3ce6032a84b712539c2d63", 8738134),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdibou2,
- kFeaturesNone,
- 0, 0, 0
- },
- {
- {
- "adibou2",
- "ADIBOU 2.13s Environnement",
- AD_ENTRY1s("intro.stk", "092707829555f27706920e4cacf1fada", 8737958),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdibou2,
- kFeaturesNone,
- 0, 0, 0
- },
- {
- {
- "adibou2",
- "ADIBOO 2.14 Environnement",
- AD_ENTRY1s("intro.stk", "ff63637e3cb7f0a457edf79457b1c6b3", 9333874),
- FR_FRA,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeAdibou2,
- kFeaturesNone,
- 0, 0, 0
- },
- { AD_TABLE_END_MARKER, kGameTypeNone, kFeaturesNone, 0, 0, 0}
-};
-
-static const GOBGameDescription fallbackDescs[] = {
- { //0
- {
- "gob1",
- "unknown",
- AD_ENTRY1(0, 0),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesNone,
- 0, 0, 0
- },
- { //1
- {
- "gob1cd",
- "unknown",
- AD_ENTRY1(0, 0),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob1,
- kFeaturesCD,
- 0, 0, 0
- },
- { //2
- {
- "gob2",
- "unknown",
- AD_ENTRY1(0, 0),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { //3
- {
- "gob2mac",
- "unknown",
- AD_ENTRY1(0, 0),
- UNK_LANG,
- kPlatformMacintosh,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { //4
- {
- "gob2cd",
- "unknown",
- AD_ENTRY1(0, 0),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob2,
- kFeaturesCD,
- 0, 0, 0
- },
- { //5
- {
- "bargon",
- "",
- AD_ENTRY1(0, 0),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeBargon,
- kFeaturesNone,
- 0, 0, 0
- },
- { //6
- {
- "gob3",
- "unknown",
- AD_ENTRY1(0, 0),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { //7
- {
- "gob3cd",
- "unknown",
- AD_ENTRY1(0, 0),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGob3,
- kFeaturesCD,
- 0, 0, 0
- },
- { //8
- {
- "woodruff",
- "unknown",
- AD_ENTRY1(0, 0),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeWoodruff,
- kFeatures640,
- 0, 0, 0
- },
- { //9
- {
- "lostintime",
- "unknown",
- AD_ENTRY1(0, 0),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { //10
- {
- "lostintime",
- "unknown",
- AD_ENTRY1(0, 0),
- UNK_LANG,
- kPlatformMacintosh,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesAdLib,
- 0, 0, 0
- },
- { //11
- {
- "lostintime",
- "unknown",
- AD_ENTRY1(0, 0),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeLostInTime,
- kFeaturesCD,
- 0, 0, 0
- },
- { //12
- {
- "urban",
- "unknown",
- AD_ENTRY1(0, 0),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeUrban,
- kFeaturesCD,
- 0, 0, 0
- },
- { //13
- {
- "playtoons1",
- "unknown",
- AD_ENTRY1(0, 0),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640,
- 0, 0, 0
- },
- { //14
- {
- "playtoons2",
- "unknown",
- AD_ENTRY1(0, 0),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640,
- 0, 0, 0
- },
- { //15
- {
- "playtoons3",
- "unknown",
- AD_ENTRY1(0, 0),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640,
- 0, 0, 0
- },
- { //16
- {
- "playtoons4",
- "unknown",
- AD_ENTRY1(0, 0),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640,
- 0, 0, 0
- },
- { //17
- {
- "playtoons5",
- "unknown",
- AD_ENTRY1(0, 0),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640,
- 0, 0, 0
- },
- { //18
- {
- "playtoons construction kit",
- "unknown",
- AD_ENTRY1(0, 0),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypePlaytoons,
- kFeatures640,
- 0, 0, 0
- },
- { //19
- {
- "bambou",
- "unknown",
- AD_ENTRY1(0, 0),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeBambou,
- kFeatures640,
- 0, 0, 0
- },
- { //20
- {
- "fascination",
- "unknown",
- AD_ENTRY1(0, 0),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeFascination,
- kFeaturesNone,
- "disk0.stk", 0, 0
- },
- { //21
- {
- "geisha",
- "unknown",
- AD_ENTRY1(0, 0),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeGeisha,
- kFeaturesNone,
- "disk1.stk", "intro.tot", 0
- },
- { //22
- {
- "adi2",
- "",
- AD_ENTRY1(0, 0),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeAdi2,
- kFeatures640,
- "adi2.stk", 0, 0
- },
- { //23
- {
- "adi4",
- "",
- AD_ENTRY1(0, 0),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSUBTITLES | GUIO_NOSPEECH
- },
- kGameTypeAdi4,
- kFeatures640,
- "adif41.stk", 0, 0
- },
- { //24
- {
- "coktelplayer",
- "unknown",
- AD_ENTRY1(0, 0),
- UNK_LANG,
- kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- kGameTypeUrban,
- kFeaturesAdLib | kFeatures640 | kFeaturesSCNDemo,
- "", "", 8
- }
-};
-
-static const ADFileBasedFallback fileBased[] = {
- { &fallbackDescs[ 0], { "intro.stk", "disk1.stk", "disk2.stk", "disk3.stk", "disk4.stk", 0 } },
- { &fallbackDescs[ 1], { "intro.stk", "gob.lic", 0 } },
- { &fallbackDescs[ 2], { "intro.stk", 0 } },
- { &fallbackDescs[ 2], { "intro.stk", "disk2.stk", "disk3.stk", 0 } },
- { &fallbackDescs[ 3], { "intro.stk", "disk2.stk", "disk3.stk", "musmac1.mid", 0 } },
- { &fallbackDescs[ 4], { "intro.stk", "gobnew.lic", 0 } },
- { &fallbackDescs[ 5], { "intro.stk", "scaa.imd", "scba.imd", "scbf.imd", 0 } },
- { &fallbackDescs[ 6], { "intro.stk", "imd.itk", 0 } },
- { &fallbackDescs[ 7], { "intro.stk", "mus_gob3.lic", 0 } },
- { &fallbackDescs[ 8], { "intro.stk", "woodruff.itk", 0 } },
- { &fallbackDescs[ 9], { "intro.stk", "commun1.itk", 0 } },
- { &fallbackDescs[10], { "intro.stk", "commun1.itk", "musmac1.mid", 0 } },
- { &fallbackDescs[11], { "intro.stk", "commun1.itk", "lost.lic", 0 } },
- { &fallbackDescs[12], { "intro.stk", "cd1.itk", "objet1.itk", 0 } },
- { &fallbackDescs[13], { "playtoon.stk", "archi.stk", 0 } },
- { &fallbackDescs[14], { "playtoon.stk", "spirou.stk", 0 } },
- { &fallbackDescs[15], { "playtoon.stk", "chato.stk", 0 } },
- { &fallbackDescs[16], { "playtoon.stk", "manda.stk", 0 } },
- { &fallbackDescs[17], { "playtoon.stk", "wakan.stk", 0 } },
- { &fallbackDescs[18], { "playtoon.stk", "dan.itk" } },
- { &fallbackDescs[19], { "intro.stk", "bambou.itk", 0 } },
- { &fallbackDescs[20], { "disk0.stk", "disk1.stk", "disk2.stk", "disk3.stk", 0 } },
- { &fallbackDescs[21], { "disk1.stk", "disk2.stk", "disk3.stk", 0 } },
- { &fallbackDescs[22], { "adi2.stk", 0 } },
- { &fallbackDescs[23], { "adif41.stk", "adim41.stk", 0 } },
- { &fallbackDescs[24], { "coktelplayer.scn", 0 } },
- { 0, { 0 } }
-};
-
-}
+#include "gob/detection_tables.h"
static const ADParams detectionParams = {
// Pointer to ADGameDescription or its superset structure
@@ -5094,7 +107,11 @@ static const ADParams detectionParams = {
// Flags
0,
// Additional GUI options (for every game}
- Common::GUIO_NOLAUNCHLOAD
+ Common::GUIO_NOLAUNCHLOAD,
+ // Maximum directory depth
+ 1,
+ // List of directory globs
+ 0
};
class GobMetaEngine : public AdvancedMetaEngine {
diff --git a/engines/gob/detection_tables.h b/engines/gob/detection_tables.h
new file mode 100644
index 0000000000..20edb9fbc3
--- /dev/null
+++ b/engines/gob/detection_tables.h
@@ -0,0 +1,5013 @@
+/* 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$
+ *
+ */
+
+namespace Gob {
+
+using Common::GUIO_NOSPEECH;
+using Common::GUIO_NOSUBTITLES;
+using Common::GUIO_NONE;
+
+static const GOBGameDescription gameDescriptions[] = {
+ { // Supplied by Florian Zeitz on scummvm-devel
+ {
+ "gob1",
+ "EGA",
+ AD_ENTRY1("intro.stk", "c65e9cc8ba23a38456242e1f2b1caad4"),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesEGA,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob1",
+ "EGA",
+ AD_ENTRY1("intro.stk", "f9233283a0be2464248d83e14b95f09c"),
+ RU_RUS,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesEGA,
+ 0, 0, 0
+ },
+ { // Supplied by Theruler76 in bug report #1201233
+ {
+ "gob1",
+ "VGA",
+ AD_ENTRY1("intro.stk", "26a9118c0770fa5ac93a9626761600b2"),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ { // Supplied by raziel_ in bug report #1891864
+ {
+ "gob1",
+ "VGA",
+ AD_ENTRY1s("intro.stk", "e157cb59c6d330ca70d12ab0ef1dd12b", 288972),
+ EN_GRB,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Supplied by raina in the forums
+ {
+ "gob1",
+ "",
+ AD_ENTRY1s("intro.stk", "6d837c6380d8f4d984c9f6cc0026df4f", 192712),
+ EN_ANY,
+ kPlatformMacintosh,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ { // Supplied by paul66 in bug report #1652352
+ {
+ "gob1",
+ "",
+ AD_ENTRY1("intro.stk", "00a42a7d2d22e6b6ab1b8c673c4ed267"),
+ EN_ANY,
+ kPlatformMacintosh,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Supplied by paul66 in bug report #1652352
+ {
+ "gob1",
+ "",
+ AD_ENTRY1("intro.stk", "00a42a7d2d22e6b6ab1b8c673c4ed267"),
+ DE_DEU,
+ kPlatformMacintosh,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Supplied by paul66 in bug report #1652352
+ {
+ "gob1",
+ "",
+ AD_ENTRY1("intro.stk", "00a42a7d2d22e6b6ab1b8c673c4ed267"),
+ FR_FRA,
+ kPlatformMacintosh,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Supplied by paul66 in bug report #1652352
+ {
+ "gob1",
+ "",
+ AD_ENTRY1("intro.stk", "00a42a7d2d22e6b6ab1b8c673c4ed267"),
+ IT_ITA,
+ kPlatformMacintosh,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Supplied by paul66 in bug report #1652352
+ {
+ "gob1",
+ "",
+ AD_ENTRY1("intro.stk", "00a42a7d2d22e6b6ab1b8c673c4ed267"),
+ ES_ESP,
+ kPlatformMacintosh,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Supplied by Hkz on #scummvm
+ {
+ "gob1",
+ "",
+ {
+ {"intro.stk", 0, "f5f028ee39c456fa51fa63b606583918", 313472},
+ {"musmac1.mid", 0, "4f66903b33df8a20edd4c748809c0b56", 8161},
+ {0, 0, 0, 0}
+ },
+ FR_FRA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Supplied by Hkz on #scummvm
+ {
+ "gob1",
+ "",
+ {
+ {"intro.stk", 0, "f5f028ee39c456fa51fa63b606583918", 313472},
+ {"musmac1.mid", 0, "4f66903b33df8a20edd4c748809c0b56", 8161},
+ {0, 0, 0, 0}
+ },
+ IT_ITA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Supplied by Hkz on #scummvm
+ {
+ "gob1",
+ "",
+ {
+ {"intro.stk", 0, "f5f028ee39c456fa51fa63b606583918", 313472},
+ {"musmac1.mid", 0, "4f66903b33df8a20edd4c748809c0b56", 8161},
+ {0, 0, 0, 0}
+ },
+ EN_GRB,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Supplied by Hkz on #scummvm
+ {
+ "gob1",
+ "",
+ {
+ {"intro.stk", 0, "f5f028ee39c456fa51fa63b606583918", 313472},
+ {"musmac1.mid", 0, "4f66903b33df8a20edd4c748809c0b56", 8161},
+ {0, 0, 0, 0}
+ },
+ DE_DEU,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Supplied by Hkz on #scummvm
+ {
+ "gob1",
+ "",
+ {
+ {"intro.stk", 0, "f5f028ee39c456fa51fa63b606583918", 313472},
+ {"musmac1.mid", 0, "4f66903b33df8a20edd4c748809c0b56", 8161},
+ {0, 0, 0, 0}
+ },
+ ES_ESP,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob1",
+ "",
+ {
+ {"intro.stk", 0, "e157cb59c6d330ca70d12ab0ef1dd12b", 288972},
+ {"musmac1.mid", 0, "4f66903b33df8a20edd4c748809c0b56", 8161},
+ {0, 0, 0, 0}
+ },
+ EN_GRB,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob1",
+ "",
+ {
+ {"intro.stk", 0, "e157cb59c6d330ca70d12ab0ef1dd12b", 288972},
+ {"musmac1.mid", 0, "4f66903b33df8a20edd4c748809c0b56", 8161},
+ {0, 0, 0, 0}
+ },
+ FR_FRA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob1",
+ "",
+ {
+ {"intro.stk", 0, "e157cb59c6d330ca70d12ab0ef1dd12b", 288972},
+ {"musmac1.mid", 0, "4f66903b33df8a20edd4c748809c0b56", 8161},
+ {0, 0, 0, 0}
+ },
+ ES_ESP,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob1",
+ "",
+ {
+ {"intro.stk", 0, "e157cb59c6d330ca70d12ab0ef1dd12b", 288972},
+ {"musmac1.mid", 0, "4f66903b33df8a20edd4c748809c0b56", 8161},
+ {0, 0, 0, 0}
+ },
+ IT_ITA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob1",
+ "",
+ {
+ {"intro.stk", 0, "e157cb59c6d330ca70d12ab0ef1dd12b", 288972},
+ {"musmac1.mid", 0, "4f66903b33df8a20edd4c748809c0b56", 8161},
+ {0, 0, 0, 0}
+ },
+ DE_DEU,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Found in Found in french ADI 2.5 Anglais Multimedia 5e
+ {
+ "gob1",
+ "",
+ AD_ENTRY1s("intro.stk", "f5f028ee39c456fa51fa63b606583918", 313472),
+ FR_FRA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Found in Found in french ADI 2.5 Anglais Multimedia 5e
+ {
+ "gob1",
+ "",
+ AD_ENTRY1s("intro.stk", "f5f028ee39c456fa51fa63b606583918", 313472),
+ EN_GRB,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Found in Found in french ADI 2.5 Anglais Multimedia 5e
+ {
+ "gob1",
+ "",
+ AD_ENTRY1s("intro.stk", "f5f028ee39c456fa51fa63b606583918", 313472),
+ DE_DEU,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Found in Found in french ADI 2.5 Anglais Multimedia 5e
+ {
+ "gob1",
+ "",
+ AD_ENTRY1s("intro.stk", "f5f028ee39c456fa51fa63b606583918", 313472),
+ IT_ITA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Found in Found in french ADI 2.5 Anglais Multimedia 5e
+ {
+ "gob1",
+ "",
+ AD_ENTRY1s("intro.stk", "f5f028ee39c456fa51fa63b606583918", 313472),
+ ES_ESP,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // CD 1.000 version.
+ {
+ "gob1cd",
+ "v1.000",
+ AD_ENTRY1("intro.stk", "2fbf4b5b82bbaee87eb45d4404c28998"),
+ EN_USA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // CD 1.000 version.
+ {
+ "gob1cd",
+ "v1.000",
+ AD_ENTRY1("intro.stk", "2fbf4b5b82bbaee87eb45d4404c28998"),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // CD 1.000 version.
+ {
+ "gob1cd",
+ "v1.000",
+ AD_ENTRY1("intro.stk", "2fbf4b5b82bbaee87eb45d4404c28998"),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // CD 1.000 version.
+ {
+ "gob1cd",
+ "v1.000",
+ AD_ENTRY1("intro.stk", "2fbf4b5b82bbaee87eb45d4404c28998"),
+ IT_ITA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // CD 1.000 version.
+ {
+ "gob1cd",
+ "v1.000",
+ AD_ENTRY1("intro.stk", "2fbf4b5b82bbaee87eb45d4404c28998"),
+ ES_ESP,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // CD 1.02 version. Multilingual
+ {
+ "gob1cd",
+ "v1.02",
+ AD_ENTRY1("intro.stk", "8bd873137b6831c896ee8ad217a6a398"),
+ EN_USA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // CD 1.02 version. Multilingual
+ {
+ "gob1cd",
+ "v1.02",
+ AD_ENTRY1("intro.stk", "8bd873137b6831c896ee8ad217a6a398"),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // CD 1.02 version. Multilingual
+ {
+ "gob1cd",
+ "v1.02",
+ AD_ENTRY1("intro.stk", "8bd873137b6831c896ee8ad217a6a398"),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // CD 1.02 version. Multilingual
+ {
+ "gob1cd",
+ "v1.02",
+ AD_ENTRY1("intro.stk", "8bd873137b6831c896ee8ad217a6a398"),
+ IT_ITA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // CD 1.02 version. Multilingual
+ {
+ "gob1cd",
+ "v1.02",
+ AD_ENTRY1("intro.stk", "8bd873137b6831c896ee8ad217a6a398"),
+ ES_ESP,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // Supplied by goodoldgeorg in bug report #2810082
+ {
+ "gob1cd",
+ "v1.02",
+ AD_ENTRY1s("intro.stk", "40d4a53818f4fce3f5997d02c3fafe73", 4049248),
+ HU_HUN,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // Supplied by goodoldgeorg in bug report #2810082
+ {
+ "gob1cd",
+ "v1.02",
+ AD_ENTRY1s("intro.stk", "40d4a53818f4fce3f5997d02c3fafe73", 4049248),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // Supplied by goodoldgeorg in bug report #2810082
+ {
+ "gob1cd",
+ "v1.02",
+ AD_ENTRY1s("intro.stk", "40d4a53818f4fce3f5997d02c3fafe73", 4049248),
+ ES_ESP,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // Supplied by goodoldgeorg in bug report #2810082
+ {
+ "gob1cd",
+ "v1.02",
+ AD_ENTRY1s("intro.stk", "40d4a53818f4fce3f5997d02c3fafe73", 4049248),
+ IT_ITA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob1",
+ "Demo",
+ AD_ENTRY1("intro.stk", "972f22c6ff8144a6636423f0354ca549"),
+ UNK_LANG,
+ kPlatformAmiga,
+ ADGF_DEMO,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob1",
+ "Interactive Demo",
+ AD_ENTRY1("intro.stk", "e72bd1e3828c7dec4c8a3e58c48bdfdb"),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_DEMO,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob1",
+ "Interactive Demo",
+ AD_ENTRY1s("intro.stk", "a796096280d5efd48cf8e7dfbe426eb5", 193595),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_DEMO,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ { // Supplied by goodoldgeorg in bug report #2785958
+ {
+ "gob1",
+ "Interactive Demo",
+ AD_ENTRY1s("intro.stk", "35a098571af9a03c04e2303aec7c9249", 116582),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_DEMO,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob1",
+ "",
+ AD_ENTRY1s("intro.stk", "0e022d3f2481b39e9175d37b2c6ad4c6", 2390121),
+ FR_FRA,
+ kPlatformCDi,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesAdLib,
+ 0, "AVT003.TOT", 0
+ },
+ { // Supplied by fac76 in bug report #1883808
+ {
+ "gob2",
+ "",
+ AD_ENTRY1s("intro.stk", "eebf2810122cfd17399260cd1468e994", 554014),
+ EN_ANY,
+ kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob2",
+ "",
+ AD_ENTRY1("intro.stk", "d28b9e9b41f31acfa58dcd12406c7b2c"),
+ DE_DEU,
+ kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ { // Supplied by goodoldgeorg in bug report #2602057
+ {
+ "gob2",
+ "",
+ AD_ENTRY1("intro.stk", "686c88f7302a80b744aae9f8413e853d"),
+ IT_ITA,
+ kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ { // Supplied by bgk in bug report #1706861
+ {
+ "gob2",
+ "",
+ AD_ENTRY1s("intro.stk", "4b13c02d1069b86bcfec80f4e474b98b", 554680),
+ FR_FRA,
+ kPlatformAtariST,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ { // Supplied by fac76 in bug report #1673397
+ {
+ "gob2",
+ "",
+ {
+ {"intro.stk", 0, "b45b984ee8017efd6ea965b9becd4d66", 828443},
+ {"musmac1.mid", 0, "7f96f491448c7a001b32df89cf8d2af2", 1658},
+ {0, 0, 0, 0}
+ },
+ UNK_LANG,
+ kPlatformMacintosh,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Supplied by koalet in bug report #2478585
+ {
+ "gob2",
+ "",
+ {
+ {"intro.stk", 0, "a13ecb4f6d8fd881ebbcc02e45cb5475", 837275},
+ {"musmac1.mid", 0, "7f96f491448c7a001b32df89cf8d2af2", 1658},
+ {0, 0, 0, 0}
+ },
+ FR_FRA,
+ kPlatformMacintosh,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob2",
+ "",
+ AD_ENTRY1("intro.stk", "b45b984ee8017efd6ea965b9becd4d66"),
+ EN_GRB,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob2",
+ "",
+ AD_ENTRY1("intro.stk", "dedb5d31d8c8050a8cf77abedcc53dae"),
+ EN_USA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Supplied by raziel_ in bug report #1891867
+ {
+ "gob2",
+ "",
+ AD_ENTRY1s("intro.stk", "25a99827cd59751a80bed9620fb677a0", 893302),
+ EN_USA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob2",
+ "",
+ AD_ENTRY1s("intro.stk", "a13ecb4f6d8fd881ebbcc02e45cb5475", 837275),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Supplied by blackwhiteeagle in bug report #1605235
+ {
+ "gob2",
+ "",
+ AD_ENTRY1("intro.stk", "3e4e7db0d201587dd2df4003b2993ef6"),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob2",
+ "",
+ AD_ENTRY1("intro.stk", "a13892cdf4badda85a6f6fb47603a128"),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Supplied by goodoldgeorg in bug report #2602017
+ {
+ "gob2",
+ "",
+ AD_ENTRY1("intro.stk", "c47faf1d406504e6ffe63243610bb1f4"),
+ IT_ITA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob2",
+ "",
+ AD_ENTRY1("intro.stk", "cd3e1df8b273636ee32e34b7064f50e8"),
+ RU_RUS,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Supplied by arcepi in bug report #1659884
+ {
+ "gob2",
+ "",
+ AD_ENTRY1s("intro.stk", "5f53c56e3aa2f1e76c2e4f0caa15887f", 829232),
+ ES_ESP,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob2",
+ "",
+ {
+ {"intro.stk", 0, "285d7340f98ebad65d465585da12910b", 837286},
+ {"musmac1.mid", 0, "834e55205b710d0af5f14a6f2320dd8e", 8661},
+ {0, 0, 0, 0}
+ },
+ FR_FRA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob2",
+ "",
+ {
+ {"intro.stk", 0, "25a99827cd59751a80bed9620fb677a0", 893302},
+ {"musmac1.mid", 0, "834e55205b710d0af5f14a6f2320dd8e", 8661},
+ {0, 0, 0, 0}
+ },
+ EN_USA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob2",
+ "",
+ {
+ {"intro.stk", 0, "25a99827cd59751a80bed9620fb677a0", 893302},
+ {"musmac1.mid", 0, "834e55205b710d0af5f14a6f2320dd8e", 8661},
+ {0, 0, 0, 0}
+ },
+ FR_FRA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob2",
+ "",
+ {
+ {"intro.stk", 0, "25a99827cd59751a80bed9620fb677a0", 893302},
+ {"musmac1.mid", 0, "834e55205b710d0af5f14a6f2320dd8e", 8661},
+ {0, 0, 0, 0}
+ },
+ DE_DEU,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob2",
+ "",
+ {
+ {"intro.stk", 0, "6efac0a14c0de4d57dde8592456c8acf", 845172},
+ {"musmac1.mid", 0, "834e55205b710d0af5f14a6f2320dd8e", 8661},
+ {0, 0, 0, 0}
+ },
+ EN_USA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob2",
+ "",
+ {
+ {"intro.stk", 0, "6efac0a14c0de4d57dde8592456c8acf", 845172},
+ {"musmac1.mid", 0, "834e55205b710d0af5f14a6f2320dd8e", 8661},
+ {0, 0, 0, 0}
+ },
+ FR_FRA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Found in french ADI 2 Francais-Maths CM1
+ {
+ "gob2",
+ "",
+ AD_ENTRY1s("intro.stk", "24489330a1d67ff978211f574822a5a6", 883756),
+ FR_FRA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Found in french ADI 2.5 Anglais Multimedia 5e
+ {
+ "gob2",
+ "",
+ AD_ENTRY1s("intro.stk", "285d7340f98ebad65d465585da12910b", 837286),
+ FR_FRA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob2cd",
+ "v1.000",
+ AD_ENTRY1("intro.stk", "9de5fbb41cf97182109e5fecc9d90347"),
+ EN_USA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob2cd",
+ "v2.01",
+ AD_ENTRY1("intro.stk", "24a6b32757752ccb1917ce92fd7c2a04"),
+ EN_ANY,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob2cd",
+ "v2.01",
+ AD_ENTRY1("intro.stk", "24a6b32757752ccb1917ce92fd7c2a04"),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob2cd",
+ "v2.01",
+ AD_ENTRY1("intro.stk", "24a6b32757752ccb1917ce92fd7c2a04"),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob2cd",
+ "v2.01",
+ AD_ENTRY1("intro.stk", "24a6b32757752ccb1917ce92fd7c2a04"),
+ IT_ITA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob2cd",
+ "v2.01",
+ AD_ENTRY1("intro.stk", "24a6b32757752ccb1917ce92fd7c2a04"),
+ ES_ESP,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // Supplied by goodoldgeorg in bug report #2810082
+ {
+ "gob2cd",
+ "v1.02",
+ AD_ENTRY1s("intro.stk", "5ba85a4769a1ab03a283dd694588d526", 5006236),
+ HU_HUN,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // Supplied by goodoldgeorg in bug report #2810082
+ {
+ "gob2cd",
+ "v1.02",
+ AD_ENTRY1s("intro.stk", "5ba85a4769a1ab03a283dd694588d526", 5006236),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // Supplied by goodoldgeorg in bug report #2810082
+ {
+ "gob2cd",
+ "v1.02",
+ AD_ENTRY1s("intro.stk", "5ba85a4769a1ab03a283dd694588d526", 5006236),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // Supplied by goodoldgeorg in bug report #2810082
+ {
+ "gob2cd",
+ "v1.02",
+ AD_ENTRY1s("intro.stk", "5ba85a4769a1ab03a283dd694588d526", 5006236),
+ ES_ESP,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // Supplied by goodoldgeorg in bug report #2810082
+ {
+ "gob2cd",
+ "v1.02",
+ AD_ENTRY1s("intro.stk", "5ba85a4769a1ab03a283dd694588d526", 5006236),
+ IT_ITA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob2",
+ "Non-Interactive Demo",
+ AD_ENTRY1("intro.stk", "8b1c98ff2ab2e14f47a1b891e9b92217"),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_DEMO,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib,
+ 0, "usa.tot", 0
+ },
+ {
+ {
+ "gob2",
+ "Interactive Demo",
+ AD_ENTRY1("intro.stk", "cf1c95b2939bd8ff58a25c756cb6125e"),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_DEMO,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob2",
+ "Interactive Demo",
+ AD_ENTRY1("intro.stk", "4b278c2678ea01383fd5ca114d947eea"),
+ UNK_LANG,
+ kPlatformAmiga,
+ ADGF_DEMO,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ { // Supplied by polluks in bug report #1895126
+ {
+ "gob2",
+ "Interactive Demo",
+ AD_ENTRY1s("intro.stk", "9fa85aea959fa8c582085855fbd99346", 553063),
+ UNK_LANG,
+ kPlatformAmiga,
+ ADGF_DEMO,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ { // Supplied by vampir_raziel in bug report #1658373
+ {
+ "ween",
+ "",
+ {
+ {"intro.stk", 0, "bfd9d02faf3d8d60a2cf744f95eb48dd", 456570},
+ {"ween.ins", 0, "d2cb24292c9ddafcad07e23382027218", 87800},
+ {0, 0, 0, 0}
+ },
+ EN_GRB,
+ kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeWeen,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ { // Supplied by vampir_raziel in bug report #1658373
+ {
+ "ween",
+ "",
+ AD_ENTRY1s("intro.stk", "257fe669705ac4971efdfd5656eef16a", 457719),
+ FR_FRA,
+ kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeWeen,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ { // Supplied by vampir_raziel in bug report #1658373
+ {
+ "ween",
+ "",
+ AD_ENTRY1s("intro.stk", "dffd1ab98fe76150d6933329ca6f4cc4", 459458),
+ FR_FRA,
+ kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeWeen,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ { // Supplied by vampir_raziel in bug report #1658373
+ {
+ "ween",
+ "",
+ AD_ENTRY1s("intro.stk", "af83debf2cbea21faa591c7b4608fe92", 458192),
+ DE_DEU,
+ kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeWeen,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ { // Supplied by goodoldgeorg in bug report #2563539
+ {
+ "ween",
+ "",
+ {
+ {"intro.stk", 0, "dffd1ab98fe76150d6933329ca6f4cc4", 459458},
+ {"ween.ins", 0, "d2cb24292c9ddafcad07e23382027218", 87800},
+ {0, 0, 0, 0}
+ },
+ IT_ITA,
+ kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeWeen,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ { // Supplied by pwigren in bug report #1764174
+ {
+ "ween",
+ "",
+ {
+ {"intro.stk", 0, "bfd9d02faf3d8d60a2cf744f95eb48dd", 456570},
+ {"music__5.snd", 0, "7d1819b9981ecddd53d3aacbc75f1cc8", 13446},
+ {0, 0, 0, 0}
+ },
+ EN_GRB,
+ kPlatformAtariST,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeWeen,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ {
+ {
+ "ween",
+ "",
+ AD_ENTRY1("intro.stk", "e6d13fb3b858cb4f78a8780d184d5b2c"),
+ FR_FRA,
+ kPlatformAtariST,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeWeen,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ {
+ {
+ "ween",
+ "",
+ AD_ENTRY1("intro.stk", "2bb8878a8042244dd2b96ff682381baa"),
+ EN_GRB,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeWeen,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "ween",
+ "",
+ AD_ENTRY1s("intro.stk", "de92e5c6a8c163007ffceebef6e67f7d", 7117568),
+ EN_USA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeWeen,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Supplied by cybot_tmin in bug report #1667743
+ {
+ "ween",
+ "",
+ AD_ENTRY1s("intro.stk", "6d60f9205ecfbd8735da2ee7823a70dc", 7014426),
+ ES_ESP,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeWeen,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "ween",
+ "",
+ AD_ENTRY1("intro.stk", "4b10525a3782aa7ecd9d833b5c1d308b"),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeWeen,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Supplied by cartman_ on #scummvm
+ {
+ "ween",
+ "",
+ AD_ENTRY1("intro.stk", "63170e71f04faba88673b3f510f9c4c8"),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeWeen,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Supplied by glorfindel in bugreport #1722142
+ {
+ "ween",
+ "",
+ AD_ENTRY1s("intro.stk", "8b57cd510da8a3bbd99e3a0297a8ebd1", 7018771),
+ IT_ITA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeWeen,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "ween",
+ "Demo",
+ AD_ENTRY1("intro.stk", "2e9c2898f6bf206ede801e3b2e7ee428"),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_DEMO,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeWeen,
+ kFeaturesAdLib,
+ 0, "show.tot", 0
+ },
+ {
+ {
+ "ween",
+ "Demo",
+ AD_ENTRY1("intro.stk", "15fb91a1b9b09684b28ac75edf66e504"),
+ EN_USA,
+ kPlatformPC,
+ ADGF_DEMO,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeWeen,
+ kFeaturesAdLib,
+ 0, "show.tot", 0
+ },
+ {
+ {
+ "bargon",
+ "",
+ AD_ENTRY1("intro.stk", "da3c54be18ab73fbdb32db24624a9c23"),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeBargon,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ { // Supplied by Trekky in the forums
+ {
+ "bargon",
+ "",
+ AD_ENTRY1s("intro.stk", "2f54b330d21f65b04b7c1f8cca76426c", 262109),
+ FR_FRA,
+ kPlatformAtariST,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeBargon,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ { // Supplied by cesardark in bug #1681649
+ {
+ "bargon",
+ "",
+ AD_ENTRY1s("intro.stk", "11103b304286c23945560b391fd37e7d", 3181890),
+ ES_ESP,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeBargon,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ { // Supplied by paul66 in bug #1692667
+ {
+ "bargon",
+ "",
+ AD_ENTRY1s("intro.stk", "da3c54be18ab73fbdb32db24624a9c23", 3181825),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeBargon,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ { // Supplied by pwigren in bugreport #1764174
+ {
+ "bargon",
+ "",
+ AD_ENTRY1s("intro.stk", "569d679fe41d49972d34c9fce5930dda", 269825),
+ EN_ANY,
+ kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeBargon,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ { // Supplied by kizkoool in bugreport #2089734
+ {
+ "bargon",
+ "",
+ AD_ENTRY1s("intro.stk", "00f6b4e2ee26e5c40b488e2df5adcf03", 3975580),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeBargon,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ { // Supplied by glorfindel in bugreport #1722142
+ {
+ "bargon",
+ "Fanmade",
+ AD_ENTRY1s("intro.stk", "da3c54be18ab73fbdb32db24624a9c23", 3181825),
+ IT_ITA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeBargon,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ {
+ {
+ "littlered",
+ "",
+ AD_ENTRY1s("intro.stk", "0b72992f5d8b5e6e0330572a5753ea25", 256490),
+ EN_GRB,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib | kFeaturesEGA,
+ 0, 0, 0
+ },
+ {
+ {
+ "littlered",
+ "",
+ AD_ENTRY1s("intro.stk", "0b72992f5d8b5e6e0330572a5753ea25", 256490),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib | kFeaturesEGA,
+ 0, 0, 0
+ },
+ {
+ {
+ "littlered",
+ "",
+ AD_ENTRY1s("intro.stk", "0b72992f5d8b5e6e0330572a5753ea25", 256490),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib | kFeaturesEGA,
+ 0, 0, 0
+ },
+ {
+ {
+ "littlered",
+ "",
+ AD_ENTRY1s("intro.stk", "0b72992f5d8b5e6e0330572a5753ea25", 256490),
+ ES_ESP,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib | kFeaturesEGA,
+ 0, 0, 0
+ },
+ {
+ {
+ "littlered",
+ "",
+ AD_ENTRY1s("intro.stk", "0b72992f5d8b5e6e0330572a5753ea25", 256490),
+ IT_ITA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib | kFeaturesEGA,
+ 0, 0, 0
+ },
+ {
+ {
+ "littlered",
+ "",
+ {
+ {"intro.stk", 0, "0b72992f5d8b5e6e0330572a5753ea25", 256490},
+ {"mod.babayaga", 0, "43484cde74e0860785f8e19f0bc776d1", 60248},
+ {0, 0, 0, 0}
+ },
+ UNK_LANG,
+ kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ {
+ {
+ "littlered",
+ "",
+ AD_ENTRY1s("intro.stk", "113a16877e4f72037d9714be1c2b0221", 1187522),
+ EN_GRB,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib | kFeaturesEGA,
+ 0, 0, 0
+ },
+ {
+ {
+ "littlered",
+ "",
+ AD_ENTRY1s("intro.stk", "113a16877e4f72037d9714be1c2b0221", 1187522),
+ FR_FRA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib | kFeaturesEGA,
+ 0, 0, 0
+ },
+ {
+ {
+ "littlered",
+ "",
+ AD_ENTRY1s("intro.stk", "113a16877e4f72037d9714be1c2b0221", 1187522),
+ DE_DEU,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib | kFeaturesEGA,
+ 0, 0, 0
+ },
+ {
+ {
+ "littlered",
+ "",
+ AD_ENTRY1s("intro.stk", "113a16877e4f72037d9714be1c2b0221", 1187522),
+ IT_ITA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib | kFeaturesEGA,
+ 0, 0, 0
+ },
+ {
+ {
+ "littlered",
+ "",
+ AD_ENTRY1s("intro.stk", "113a16877e4f72037d9714be1c2b0221", 1187522),
+ ES_ESP,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib | kFeaturesEGA,
+ 0, 0, 0
+ },
+ { // Found in french ADI 2 Francais-Maths CM1
+ {
+ "littlered",
+ "",
+ AD_ENTRY1s("intro.stk", "5c15b37ed27ac2470854e9e09374d50e", 1248610),
+ FR_FRA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib | kFeaturesEGA,
+ 0, 0, 0
+ },
+ { // Found in french ADI 2 Francais-Maths CM1
+ {
+ "littlered",
+ "",
+ AD_ENTRY1s("intro.stk", "5c15b37ed27ac2470854e9e09374d50e", 1248610),
+ ES_ESP,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib | kFeaturesEGA,
+ 0, 0, 0
+ },
+ { // Found in french ADI 2 Francais-Maths CM1
+ {
+ "littlered",
+ "",
+ AD_ENTRY1s("intro.stk", "5c15b37ed27ac2470854e9e09374d50e", 1248610),
+ EN_GRB,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib | kFeaturesEGA,
+ 0, 0, 0
+ },
+ { // Found in french ADI 2 Francais-Maths CM1
+ {
+ "littlered",
+ "",
+ AD_ENTRY1s("intro.stk", "5c15b37ed27ac2470854e9e09374d50e", 1248610),
+ IT_ITA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib | kFeaturesEGA,
+ 0, 0, 0
+ },
+ { // Found in french ADI 2 Francais-Maths CM1
+ {
+ "littlered",
+ "",
+ AD_ENTRY1s("intro.stk", "5c15b37ed27ac2470854e9e09374d50e", 1248610),
+ DE_DEU,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib | kFeaturesEGA,
+ 0, 0, 0
+ },
+ {
+ {
+ "lit",
+ "",
+ AD_ENTRY1s("intro.stk", "7b7f48490dedc8a7cb999388e2fadbe3", 3930674),
+ EN_USA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "lit",
+ "",
+ AD_ENTRY1s("intro.stk", "e0767783ff662ed93665446665693aef", 4371238),
+ HE_ISR,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Supplied by cartman_ on #scummvm
+ {
+ "lit",
+ "",
+ AD_ENTRY1s("intro.stk", "f1f78b663893b58887add182a77df151", 3944090),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Supplied by goodoldgeorg in bug report #2105220
+ {
+ "lit",
+ "",
+ AD_ENTRY1s("intro.stk", "cd322cb3c64ef2ba2f2134aa2122cfe9", 3936700),
+ ES_ESP,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Supplied by koalet in bug report #2479034
+ {
+ "lit",
+ "",
+ {
+ {"intro.stk", 0, "af98bcdc70e1f1c1635577fd726fe7f1", 3937310},
+ {"musmac1.mid", 0, "ae7229bb09c6abe4e60a2768b24bc890", 9398},
+ {0, 0, 0, 0}
+ },
+ FR_FRA,
+ kPlatformMacintosh,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "lit",
+ "",
+ AD_ENTRY1s("intro.stk", "6263d09e996c1b4e84ef2d650b820e57", 4831170),
+ EN_USA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ {
+ {
+ "lit",
+ "",
+ AD_ENTRY1s("intro.stk", "6263d09e996c1b4e84ef2d650b820e57", 4831170),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ {
+ {
+ "lit",
+ "",
+ AD_ENTRY1s("intro.stk", "6263d09e996c1b4e84ef2d650b820e57", 4831170),
+ IT_ITA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ {
+ {
+ "lit",
+ "",
+ AD_ENTRY1s("intro.stk", "6263d09e996c1b4e84ef2d650b820e57", 4831170),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ {
+ {
+ "lit",
+ "",
+ AD_ENTRY1s("intro.stk", "6263d09e996c1b4e84ef2d650b820e57", 4831170),
+ ES_ESP,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ {
+ {
+ "lit",
+ "",
+ AD_ENTRY1s("intro.stk", "6263d09e996c1b4e84ef2d650b820e57", 4831170),
+ EN_GRB,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // Supplied by SiRoCs in bug report #2093672
+ {
+ "lit",
+ "",
+ AD_ENTRY1s("intro.stk", "795be7011ec31bf5bb8ce4efdb9ee5d3", 4838904),
+ EN_USA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // Supplied by SiRoCs in bug report #2093672
+ {
+ "lit",
+ "",
+ AD_ENTRY1s("intro.stk", "795be7011ec31bf5bb8ce4efdb9ee5d3", 4838904),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // Supplied by SiRoCs in bug report #2093672
+ {
+ "lit",
+ "",
+ AD_ENTRY1s("intro.stk", "795be7011ec31bf5bb8ce4efdb9ee5d3", 4838904),
+ IT_ITA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // Supplied by SiRoCs in bug report #2093672
+ {
+ "lit",
+ "",
+ AD_ENTRY1s("intro.stk", "795be7011ec31bf5bb8ce4efdb9ee5d3", 4838904),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // Supplied by SiRoCs in bug report #2093672
+ {
+ "lit",
+ "",
+ AD_ENTRY1s("intro.stk", "795be7011ec31bf5bb8ce4efdb9ee5d3", 4838904),
+ ES_ESP,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // Supplied by SiRoCs in bug report #2093672
+ {
+ "lit",
+ "",
+ AD_ENTRY1s("intro.stk", "795be7011ec31bf5bb8ce4efdb9ee5d3", 4838904),
+ EN_GRB,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ {
+ {
+ "lit",
+ "",
+ AD_ENTRY1s("intro.stk", "0ddf39cea1ec30ecc8bfe444ebd7b845", 4207330),
+ EN_GRB,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "lit",
+ "",
+ AD_ENTRY1s("intro.stk", "0ddf39cea1ec30ecc8bfe444ebd7b845", 4207330),
+ FR_FRA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "lit",
+ "",
+ AD_ENTRY1s("intro.stk", "0ddf39cea1ec30ecc8bfe444ebd7b845", 4207330),
+ ES_ESP,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "lit",
+ "",
+ AD_ENTRY1s("intro.stk", "0ddf39cea1ec30ecc8bfe444ebd7b845", 4219382),
+ DE_DEU,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "lit",
+ "",
+ AD_ENTRY1s("intro.stk", "0ddf39cea1ec30ecc8bfe444ebd7b845", 4219382),
+ FR_FRA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Found in french ADI 2.6 Francais-Maths 4e
+ {
+ "lit",
+ "",
+ AD_ENTRY1s("intro.stk", "58ee9583a4fb837f02d9a58e5f442656", 3937120),
+ FR_FRA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "lit1",
+ "Full install",
+ {
+ {"intro.stk", 0, "93c91bc9e783d00033042ae83144d7dd", 72318},
+ {"partie2.itk", 0, "78f00bd8eb9e680e6289bba0130b1b33", 4396644},
+ {0, 0, 0, 0}
+ },
+ FR_FRA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "lit1",
+ "Light install",
+ {
+ {"intro.stk", 0, "93c91bc9e783d00033042ae83144d7dd", 72318},
+ {"partie2.itk", 0, "78f00bd8eb9e680e6289bba0130b1b33", 664064},
+ {0, 0, 0, 0}
+ },
+ FR_FRA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "lit2",
+ "Light install",
+ AD_ENTRY1s("intro.stk", "17acbb212e62addbe48dc8f2282c98cb", 72318),
+ FR_FRA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "lit2",
+ "Full install",
+ {
+ {"intro.stk", 0, "17acbb212e62addbe48dc8f2282c98cb", 72318},
+ {"partie4.itk", 0, "6ce4967e0c79d7daeabc6c1d26783d4c", 2612087},
+ {0, 0, 0, 0}
+ },
+ FR_FRA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "lit",
+ "Demo",
+ AD_ENTRY1("demo.stk", "c06f8cc20eb239d4c71f225ce3093edf"),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_DEMO,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesAdLib,
+ "demo.stk", "demo.tot", 0
+ },
+ {
+ {
+ "lit",
+ "Non-interactive Demo",
+ AD_ENTRY1("demo.stk", "2eba8abd9e3878c57307576012dd2fec"),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_DEMO,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesAdLib,
+ "demo.stk", "demo.tot", 0
+ },
+ {
+ {
+ "fascination",
+ "CD Version (Censored)",
+ AD_ENTRY1s("disk0.stk", "9c61e9c22077f72921f07153e37ccf01", 545953),
+ EN_ANY,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES
+ },
+ kGameTypeFascination,
+ kFeaturesCD,
+ "disk0.stk", 0, 0
+ },
+ {
+ {
+ "fascination",
+ "VGA 3 disks edition",
+ AD_ENTRY1s("disk0.stk", "a50a8495e1b2d67699fb562cb98fc3e2", 1064387),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeFascination,
+ kFeaturesAdLib,
+ "disk0.stk", 0, 0
+ },
+ {
+ {
+ "fascination",
+ "VGA 3 disks edition",
+ AD_ENTRY1s("intro.stk", "d6e45ce548598727e2b5587a99718eba", 1055909),
+ HE_ISR,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeFascination,
+ kFeaturesAdLib,
+ "intro.stk", 0, 0
+ },
+ { // Supplied by sanguine
+ {
+ "fascination",
+ "VGA 3 disks edition",
+ AD_ENTRY1s("disk0.stk", "c14330d052fe4da5a441ac9d81bc5891", 1061955),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeFascination,
+ kFeaturesAdLib,
+ "disk0.stk", 0, 0
+ },
+ { // Supplied by windlepoons in bug report #2809247
+ {
+ "fascination",
+ "VGA 3 disks edition",
+ AD_ENTRY1s("disk0.stk", "3a24e60a035250189643c86a9ceafb97", 1062480),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeFascination,
+ kFeaturesAdLib,
+ "disk0.stk", 0, 0
+ },
+ {
+ {
+ "fascination",
+ "VGA",
+ AD_ENTRY1s("disk0.stk", "e8ab4f200a2304849f462dc901705599", 183337),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeFascination,
+ kFeaturesAdLib,
+ "disk0.stk", 0, 0
+ },
+ {
+ {
+ "fascination",
+ "",
+ AD_ENTRY1s("disk0.stk", "68b1c01564f774c0b640075fbad1b695", 189968),
+ DE_DEU,
+ kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeFascination,
+ kFeaturesNone,
+ "disk0.stk", 0, 0
+ },
+ {
+ {
+ "fascination",
+ "",
+ AD_ENTRY1s("disk0.stk", "7062117e9c5adfb6bfb2dac3ff74df9e", 189951),
+ EN_ANY,
+ kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeFascination,
+ kFeaturesNone,
+ "disk0.stk", 0, 0
+ },
+ {
+ {
+ "fascination",
+ "",
+ AD_ENTRY1s("disk0.stk", "55c154e5a3e8e98afebdcff4b522e1eb", 190005),
+ FR_FRA,
+ kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeFascination,
+ kFeaturesNone,
+ "disk0.stk", 0, 0
+ },
+ {
+ {
+ "fascination",
+ "",
+ AD_ENTRY1s("disk0.stk", "7691827fff35df7799f14cfd6be178ad", 189931),
+ IT_ITA,
+ kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeFascination,
+ kFeaturesNone,
+ "disk0.stk", 0, 0
+ },
+ {
+ {
+ "fascination",
+ "",
+ AD_ENTRY1s("disk0.stk", "aff9fcc619f4dd19eae228affd0d34c8", 189964),
+ EN_ANY,
+ kPlatformAtariST,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeFascination,
+ kFeaturesNone,
+ "disk0.stk", 0, 0
+ },
+ {
+ {
+ "geisha",
+ "",
+ AD_ENTRY1s("disk1.stk", "6eebbb98ad90cd3c44549fc2ab30f632", 212153),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGeisha,
+ kFeaturesNone,
+ "disk1.stk", "intro.tot", 0
+ },
+ {
+ {
+ "geisha",
+ "",
+ AD_ENTRY1s("disk1.stk", "f4d4d9d20f7ad1f879fc417d47faba89", 336732),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGeisha,
+ kFeaturesNone,
+ "disk1.stk", "intro.tot", 0
+ },
+ {
+ {
+ "gob3",
+ "",
+ AD_ENTRY1s("intro.stk", "32b0f57f5ae79a9ae97e8011df38af42", 157084),
+ EN_GRB,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob3",
+ "",
+ AD_ENTRY1s("intro.stk", "904fc32032295baa3efb3a41f17db611", 178582),
+ HE_ISR,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Supplied by raziel_ in bug report #1891869
+ {
+ "gob3",
+ "",
+ AD_ENTRY1s("intro.stk", "16b014bf32dbd6ab4c5163c44f56fed1", 445104),
+ EN_GRB,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob3",
+ "",
+ {
+ {"intro.stk", 0, "16b014bf32dbd6ab4c5163c44f56fed1", 445104},
+ {"musmac1.mid", 0, "948c546cad3a9de5bff3fe4107c82bf1", 6404},
+ {0, 0, 0, 0}
+ },
+ DE_DEU,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob3",
+ "",
+ {
+ {"intro.stk", 0, "16b014bf32dbd6ab4c5163c44f56fed1", 445104},
+ {"musmac1.mid", 0, "948c546cad3a9de5bff3fe4107c82bf1", 6404},
+ {0, 0, 0, 0}
+ },
+ FR_FRA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob3",
+ "",
+ {
+ {"intro.stk", 0, "16b014bf32dbd6ab4c5163c44f56fed1", 445104},
+ {"musmac1.mid", 0, "948c546cad3a9de5bff3fe4107c82bf1", 6404},
+ {0, 0, 0, 0}
+ },
+ EN_GRB,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Supplied by fac76 in bug report #1742716
+ {
+ "gob3",
+ "",
+ {
+ {"intro.stk", 0, "32b0f57f5ae79a9ae97e8011df38af42", 157084},
+ {"musmac1.mid", 0, "834e55205b710d0af5f14a6f2320dd8e", 8661},
+ {0, 0, 0, 0}
+ },
+ EN_GRB,
+ kPlatformMacintosh,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob3",
+ "",
+ AD_ENTRY1("intro.stk", "1e2f64ec8dfa89f42ee49936a27e66e7"),
+ EN_USA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Supplied by paul66 in bug report #1652352
+ {
+ "gob3",
+ "",
+ AD_ENTRY1("intro.stk", "f6d225b25a180606fa5dbe6405c97380"),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob3",
+ "",
+ AD_ENTRY1("intro.stk", "e42a4f2337d6549487a80864d7826972"),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Supplied by Paranoimia on #scummvm
+ {
+ "gob3",
+ "",
+ AD_ENTRY1s("intro.stk", "fe8144daece35538085adb59c2d29613", 159402),
+ IT_ITA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob3",
+ "",
+ AD_ENTRY1("intro.stk", "4e3af248a48a2321364736afab868527"),
+ RU_RUS,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob3",
+ "",
+ AD_ENTRY1("intro.stk", "8d28ce1591b0e9cc79bf41cad0fc4c9c"),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Supplied by SiRoCs in bug report #2098621
+ {
+ "gob3",
+ "",
+ AD_ENTRY1s("intro.stk", "d3b72938fbbc8159198088811f9e6d19", 160382),
+ ES_ESP,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob3",
+ "",
+ AD_ENTRY1("intro.stk", "bd679eafde2084d8011f247e51b5a805"),
+ EN_GRB,
+ kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesNone,
+ 0, "menu.tot", 0
+ },
+ {
+ {
+ "gob3",
+ "",
+ AD_ENTRY1("intro.stk", "bd679eafde2084d8011f247e51b5a805"),
+ DE_DEU,
+ kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesNone,
+ 0, "menu.tot", 0
+ },
+ {
+ {
+ "gob3",
+ "",
+ {
+ {"intro.stk", 0, "edd7403e5dc2a14459d2665a4c17714d", 209534},
+ {"musmac1.mid", 0, "948c546cad3a9de5bff3fe4107c82bf1", 6404},
+ {0, 0, 0, 0}
+ },
+ FR_FRA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob3",
+ "",
+ {
+ {"intro.stk", 0, "428e2de130cf3b303c938924539dc50d", 324420},
+ {"musmac1.mid", 0, "948c546cad3a9de5bff3fe4107c82bf1", 6404},
+ {0, 0, 0, 0}
+ },
+ FR_FRA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob3",
+ "",
+ {
+ {"intro.stk", 0, "428e2de130cf3b303c938924539dc50d", 324420},
+ {"musmac1.mid", 0, "948c546cad3a9de5bff3fe4107c82bf1", 6404},
+ {0, 0, 0, 0}
+ },
+ EN_ANY,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { // Found in Found in french ADI 2.5 Anglais Multimedia 5e
+ {
+ "gob3",
+ "",
+ AD_ENTRY1s("intro.stk", "edd7403e5dc2a14459d2665a4c17714d", 209534),
+ FR_FRA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob3cd",
+ "v1.000",
+ AD_ENTRY1("intro.stk", "6f2c226c62dd7ab0ab6f850e89d3fc47"),
+ EN_USA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // Supplied by paul66 and noizert in bug reports #1652352 and #1691230
+ {
+ "gob3cd",
+ "v1.02",
+ AD_ENTRY1("intro.stk", "c3e9132ea9dc0fb866b6d60dcda10261"),
+ EN_ANY,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // Supplied by paul66 and noizert in bug reports #1652352 and #1691230
+ {
+ "gob3cd",
+ "v1.02",
+ AD_ENTRY1("intro.stk", "c3e9132ea9dc0fb866b6d60dcda10261"),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // Supplied by paul66 and noizert in bug reports #1652352 and #1691230
+ {
+ "gob3cd",
+ "v1.02",
+ AD_ENTRY1("intro.stk", "c3e9132ea9dc0fb866b6d60dcda10261"),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // Supplied by paul66 and noizert in bug reports #1652352 and #1691230
+ {
+ "gob3cd",
+ "v1.02",
+ AD_ENTRY1("intro.stk", "c3e9132ea9dc0fb866b6d60dcda10261"),
+ IT_ITA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // Supplied by paul66 and noizert in bug reports #1652352 and #1691230
+ {
+ "gob3cd",
+ "v1.02",
+ AD_ENTRY1("intro.stk", "c3e9132ea9dc0fb866b6d60dcda10261"),
+ ES_ESP,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // Supplied by goodoldgeorg in bug report #2810082
+ {
+ "gob3cd",
+ "v1.02",
+ AD_ENTRY1s("intro.stk", "bfd7d4c6fedeb2cfcc8baa4d5ddb1f74", 616220),
+ HU_HUN,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // Supplied by goodoldgeorg in bug report #2810082
+ {
+ "gob3cd",
+ "v1.02",
+ AD_ENTRY1s("intro.stk", "bfd7d4c6fedeb2cfcc8baa4d5ddb1f74", 616220),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // Supplied by goodoldgeorg in bug report #2810082
+ {
+ "gob3cd",
+ "v1.02",
+ AD_ENTRY1s("intro.stk", "bfd7d4c6fedeb2cfcc8baa4d5ddb1f74", 616220),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { // Supplied by goodoldgeorg in bug report #2810082
+ {
+ "gob3cd",
+ "v1.02",
+ AD_ENTRY1s("intro.stk", "bfd7d4c6fedeb2cfcc8baa4d5ddb1f74", 616220),
+ ES_ESP,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob3",
+ "Interactive Demo",
+ AD_ENTRY1("intro.stk", "7aebd94e49c2c5c518c9e7b74f25de9d"),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_DEMO,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob3",
+ "Interactive Demo 2",
+ AD_ENTRY1("intro.stk", "e5dcbc9f6658ebb1e8fe26bc4da0806d"),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_DEMO,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob3",
+ "Interactive Demo 3",
+ AD_ENTRY1s("intro.stk", "9e20ad7b471b01f84db526da34eaf0a2", 395561),
+ EN_ANY,
+ kPlatformPC,
+ ADGF_DEMO,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "gob3",
+ "Non-interactive Demo",
+ AD_ENTRY1("intro.stk", "b9b898fccebe02b69c086052d5024a55"),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_DEMO,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "inca2",
+ "",
+ AD_ENTRY1s("intro.stk", "47c3b452767c4f49ea7b109143e77c30", 916828),
+ EN_USA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeInca2,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ {
+ {
+ "inca2",
+ "",
+ AD_ENTRY1s("intro.stk", "47c3b452767c4f49ea7b109143e77c30", 916828),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeInca2,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ {
+ {
+ "inca2",
+ "",
+ AD_ENTRY1s("intro.stk", "47c3b452767c4f49ea7b109143e77c30", 916828),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeInca2,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ {
+ {
+ "inca2",
+ "",
+ AD_ENTRY1s("intro.stk", "47c3b452767c4f49ea7b109143e77c30", 916828),
+ IT_ITA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeInca2,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ {
+ {
+ "inca2",
+ "",
+ AD_ENTRY1s("intro.stk", "47c3b452767c4f49ea7b109143e77c30", 916828),
+ ES_ESP,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeInca2,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ {
+ {
+ "inca2",
+ "",
+ AD_ENTRY1s("intro.stk", "1fa92b00fe80a20f34ec34a8e2fa869e", 923072),
+ EN_USA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeInca2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "inca2",
+ "",
+ AD_ENTRY1s("intro.stk", "1fa92b00fe80a20f34ec34a8e2fa869e", 923072),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeInca2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "inca2",
+ "",
+ AD_ENTRY1s("intro.stk", "1fa92b00fe80a20f34ec34a8e2fa869e", 923072),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeInca2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "inca2",
+ "",
+ AD_ENTRY1s("intro.stk", "d33011df8758ac64ca3dca77c7719001", 908612),
+ EN_USA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeInca2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "inca2",
+ "",
+ AD_ENTRY1s("intro.stk", "d33011df8758ac64ca3dca77c7719001", 908612),
+ DE_DEU,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeInca2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "inca2",
+ "",
+ AD_ENTRY1s("intro.stk", "d33011df8758ac64ca3dca77c7719001", 908612),
+ IT_ITA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeInca2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "inca2",
+ "",
+ AD_ENTRY1s("intro.stk", "d33011df8758ac64ca3dca77c7719001", 908612),
+ ES_ESP,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeInca2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "inca2",
+ "",
+ AD_ENTRY1s("intro.stk", "d33011df8758ac64ca3dca77c7719001", 908612),
+ FR_FRA,
+ kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeInca2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "inca2",
+ "Non-Interactive Demo",
+ {
+ {"cons.imd", 0, "f896ba0c4a1ac7f7260d342655980b49", 17804},
+ {"conseil.imd", 0, "aaedd5482d5b271e233e86c5a03cf62e", 33999},
+ {"int.imd", 0, "6308222fcefbcb20925f01c1aff70dee", 30871},
+ {"inter.imd", 0, "39bd6d3540f3bedcc97293f352c7f3fc", 191719},
+ {"machu.imd", 0, "c0bc8211d93b467bfd063b63fe61b85c", 34609},
+ {"post.imd", 0, "d75cad0e3fc22cb0c8b6faf597f509b2", 1047709},
+ {"posta.imd", 0, "2a5b3fe75681ddf4d21ac724db8111b4", 547250},
+ {"postb.imd", 0, "24260ce4e80a4c472352b76637265d09", 868312},
+ {"postc.imd", 0, "24accbcc8b83a9c2be4bd82849a2bd29", 415637},
+ {"tum.imd", 0, "0993d4810ec9deb3f77c5e92095320fd", 20330},
+ {"tumi.imd", 0, "bf53f229480d694de0947fe3366fbec6", 248952},
+ {0, 0, 0, 0}
+ },
+ EN_ANY,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeInca2,
+ kFeaturesAdLib | kFeaturesBATDemo,
+ 0, 0, 7
+ },
+ {
+ {
+ "woodruff",
+ "",
+ AD_ENTRY1s("intro.stk", "dccf9d31cb720b34d75487408821b77e", 20296390),
+ EN_GRB,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "woodruff",
+ "",
+ AD_ENTRY1s("intro.stk", "dccf9d31cb720b34d75487408821b77e", 20296390),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "woodruff",
+ "",
+ AD_ENTRY1s("intro.stk", "dccf9d31cb720b34d75487408821b77e", 20296390),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "woodruff",
+ "",
+ AD_ENTRY1s("intro.stk", "dccf9d31cb720b34d75487408821b77e", 20296390),
+ IT_ITA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "woodruff",
+ "",
+ AD_ENTRY1s("intro.stk", "dccf9d31cb720b34d75487408821b77e", 20296390),
+ ES_ESP,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "woodruff",
+ "",
+ AD_ENTRY1s("intro.stk", "b50fee012a5abcd0ac2963e1b4b56bec", 20298108),
+ EN_GRB,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "woodruff",
+ "",
+ AD_ENTRY1s("intro.stk", "b50fee012a5abcd0ac2963e1b4b56bec", 20298108),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "woodruff",
+ "",
+ AD_ENTRY1s("intro.stk", "b50fee012a5abcd0ac2963e1b4b56bec", 20298108),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "woodruff",
+ "",
+ AD_ENTRY1s("intro.stk", "b50fee012a5abcd0ac2963e1b4b56bec", 20298108),
+ IT_ITA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "woodruff",
+ "",
+ AD_ENTRY1s("intro.stk", "b50fee012a5abcd0ac2963e1b4b56bec", 20298108),
+ ES_ESP,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "woodruff",
+ "",
+ AD_ENTRY1s("intro.stk", "5f5f4e0a72c33391e67a47674b120cc6", 20296422),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640,
+ 0, 0, 0
+ },
+ { // Supplied by jvprat on #scummvm
+ {
+ "woodruff",
+ "",
+ AD_ENTRY1s("intro.stk", "270529d9b8cce770b1575908a3800b52", 20296452),
+ ES_ESP,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640,
+ 0, 0, 0
+ },
+ { // Supplied by jvprat on #scummvm
+ {
+ "woodruff",
+ "",
+ AD_ENTRY1s("intro.stk", "270529d9b8cce770b1575908a3800b52", 20296452),
+ EN_GRB,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640,
+ 0, 0, 0
+ },
+ { // Supplied by jvprat on #scummvm
+ {
+ "woodruff",
+ "",
+ AD_ENTRY1s("intro.stk", "270529d9b8cce770b1575908a3800b52", 20296452),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640,
+ 0, 0, 0
+ },
+ { // Supplied by jvprat on #scummvm
+ {
+ "woodruff",
+ "",
+ AD_ENTRY1s("intro.stk", "270529d9b8cce770b1575908a3800b52", 20296452),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640,
+ 0, 0, 0
+ },
+ { // Supplied by jvprat on #scummvm
+ {
+ "woodruff",
+ "",
+ AD_ENTRY1s("intro.stk", "270529d9b8cce770b1575908a3800b52", 20296452),
+ IT_ITA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640,
+ 0, 0, 0
+ },
+ { // Supplied by Hkz on #scummvm
+ {
+ "woodruff",
+ "",
+ AD_ENTRY1s("intro.stk", "f4c344023b073782d2fddd9d8b515318", 7069736),
+ IT_ITA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640,
+ 0, 0, 0
+ },
+ { // Supplied by Hkz on #scummvm
+ {
+ "woodruff",
+ "",
+ AD_ENTRY1s("intro.stk", "f4c344023b073782d2fddd9d8b515318", 7069736),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640,
+ 0, 0, 0
+ },
+ { // Supplied by Hkz on #scummvm
+ {
+ "woodruff",
+ "",
+ AD_ENTRY1s("intro.stk", "f4c344023b073782d2fddd9d8b515318", 7069736),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640,
+ 0, 0, 0
+ },
+ { // Supplied by DjDiabolik in bug report #1971294
+ {
+ "woodruff",
+ "",
+ AD_ENTRY1s("intro.stk", "60348a87651f92e8492ee070556a96d8", 7069736),
+ EN_GRB,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640,
+ 0, 0, 0
+ },
+ { // Supplied by DjDiabolik in bug report #1971294
+ {
+ "woodruff",
+ "",
+ AD_ENTRY1s("intro.stk", "60348a87651f92e8492ee070556a96d8", 7069736),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640,
+ 0, 0, 0
+ },
+ { // Supplied by DjDiabolik in bug report #1971294
+ {
+ "woodruff",
+ "",
+ AD_ENTRY1s("intro.stk", "60348a87651f92e8492ee070556a96d8", 7069736),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640,
+ 0, 0, 0
+ },
+ { // Supplied by DjDiabolik in bug report #1971294
+ {
+ "woodruff",
+ "",
+ AD_ENTRY1s("intro.stk", "60348a87651f92e8492ee070556a96d8", 7069736),
+ IT_ITA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640,
+ 0, 0, 0
+ },
+ { // Supplied by DjDiabolik in bug report #1971294
+ {
+ "woodruff",
+ "",
+ AD_ENTRY1s("intro.stk", "60348a87651f92e8492ee070556a96d8", 7069736),
+ ES_ESP,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640,
+ 0, 0, 0
+ },
+ { // Supplied by goodoldgeorg in bug report #2098838
+ {
+ "woodruff",
+ "",
+ AD_ENTRY1s("intro.stk", "08a96bf061af1fa4f75c6a7cc56b60a4", 20734979),
+ PL_POL,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "woodruff",
+ "Non-Interactive Demo",
+ {
+ {"demo.scn", 0, "16bb85fc5f8e519147b60475dbf33962", 89},
+ {"wooddem3.vmd", 0, "a1700596172c2d4e264760030c3a3d47", 8994250},
+ {0, 0, 0, 0}
+ },
+ EN_ANY,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640 | kFeaturesSCNDemo,
+ 0, 0, 1
+ },
+ {
+ {
+ "dynasty",
+ "",
+ AD_ENTRY1s("intro.stk", "6190e32404b672f4bbbc39cf76f41fda", 2511470),
+ EN_USA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeDynasty,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "dynasty",
+ "",
+ AD_ENTRY1s("intro.stk", "61e4069c16e27775a6cc6d20f529fb36", 2511300),
+ EN_USA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeDynasty,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "dynasty",
+ "",
+ AD_ENTRY1s("intro.stk", "61e4069c16e27775a6cc6d20f529fb36", 2511300),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeDynasty,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "dynasty",
+ "",
+ AD_ENTRY1s("intro.stk", "b3f8472484b7a1df94557b51e7b6fca0", 2322644),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeDynasty,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "dynasty",
+ "",
+ AD_ENTRY1s("intro.stk", "bdbdac8919200a5e71ffb9fb0709f704", 2446652),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeDynasty,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "dynasty",
+ "Demo",
+ AD_ENTRY1s("intro.stk", "464538a17ed39755d7f1ba9c751af1bd", 1847864),
+ EN_USA,
+ kPlatformPC,
+ ADGF_DEMO,
+ GUIO_NONE
+ },
+ kGameTypeDynasty,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "dynasty",
+ "Demo",
+ AD_ENTRY1s("lda1.stk", "0e56a899357cbc0bf503260fd2dd634e", 15032774),
+ UNK_LANG,
+ kPlatformWindows,
+ ADGF_DEMO,
+ GUIO_NONE
+ },
+ kGameTypeDynasty,
+ kFeatures640,
+ "lda1.stk", 0, 0
+ },
+ {
+ {
+ "dynasty",
+ "Demo",
+ AD_ENTRY1s("lda1.stk", "8669ea2e9a8239c070dc73958fbc8753", 15567724),
+ DE_DEU,
+ kPlatformWindows,
+ ADGF_DEMO,
+ GUIO_NONE
+ },
+ kGameTypeDynasty,
+ kFeatures640,
+ "lda1.stk", 0, 0
+ },
+ {
+ {
+ "urban",
+ "",
+ AD_ENTRY1s("intro.stk", "3ab2c542bd9216ae5d02cc6f45701ae1", 1252436),
+ EN_USA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeUrban,
+ kFeatures640,
+ 0, 0, 0
+ },
+ { // Supplied by gamin in the forums
+ {
+ "urban",
+ "",
+ AD_ENTRY1s("intro.stk", "b991ed1d31c793e560edefdb349882ef", 1276408),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeUrban,
+ kFeatures640,
+ 0, 0, 0
+ },
+ { // Supplied by jvprat on #scummvm
+ {
+ "urban",
+ "",
+ AD_ENTRY1s("intro.stk", "4ec3c0864e2b54c5b4ccf9f6ad96528d", 1253328),
+ ES_ESP,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeUrban,
+ kFeatures640,
+ 0, 0, 0
+ },
+ { // Supplied by goodoldgeorg in bug report #2770340
+ {
+ "urban",
+ "",
+ AD_ENTRY1s("intro.stk", "4bd31979ea3d77a58a358c09000a85ed", 1253018),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeUrban,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "urban",
+ "Non-Interactive Demo",
+ {
+ {"wdemo.s24", 0, "14ac9bd51db7a075d69ddb144904b271", 87},
+ {"demo.vmd", 0, "65d04715d871c292518b56dd160b0161", 9091237},
+ {"urband.vmd", 0, "60343891868c91854dd5c82766c70ecc", 922461},
+ {0, 0, 0, 0}
+ },
+ EN_ANY,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeUrban,
+ kFeatures640 | kFeaturesSCNDemo,
+ 0, 0, 2
+ },
+ {
+ {
+ "playtoons1",
+ "",
+ {
+ {"playtoon.stk", 0, "8c98e9a11be9bb203a55e8c6e68e519b", 25574338},
+ {"archi.stk", 0, "8d44b2a0d4e3139471213f9f0ed21e81", 5524674},
+ {0, 0, 0, 0}
+ },
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640,
+ "intro2.stk", 0, 0
+ },
+ {
+ {
+ "playtoons1",
+ "Pack mes histoires anim\xE9""es",
+ {
+ {"playtoon.stk", 0, "55f0293202963854192e39474e214f5f", 30448474},
+ {"archi.stk", 0, "8d44b2a0d4e3139471213f9f0ed21e81", 5524674},
+ {0, 0, 0, 0}
+ },
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640,
+ "intro2.stk", 0, 0
+ },
+ {
+ {
+ "playtoons1",
+ "",
+ {
+ {"playtoon.stk", 0, "c5ca2a288cdaefca9556cd9ae4b579cf", 25158926},
+ {"archi.stk", 0, "8d44b2a0d4e3139471213f9f0ed21e81", 5524674},
+ {0, 0, 0, 0}
+ },
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640,
+ "intro2.stk", 0, 0
+ },
+ { // Supplied by scoriae in the forums
+ {
+ "playtoons1",
+ "",
+ {
+ {"playtoon.stk", 0, "9e513e993a5b0e2496add3f50c08764b", 30448506},
+ {"archi.stk", 0, "00d8274519dfcf8a0d8ae3099daea0f8", 5532135},
+ {0, 0, 0, 0}
+ },
+ EN_ANY,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640,
+ "intro2.stk", 0, 0
+ },
+ {
+ {
+ "playtoons1",
+ "Non-Interactive Demo",
+ {
+ {"play123.scn", 0, "4689a31f543915e488c3bc46ea358add", 258},
+ {"archi.vmd", 0, "a410fcc8116bc173f038100f5857191c", 5617210},
+ {"chato.vmd", 0, "5a10e39cb66c396f2f9d8fb35e9ac016", 5445937},
+ {"genedeb.vmd", 0, "3bb4a45585f88f4d839efdda6a1b582b", 1244228},
+ {"generik.vmd", 0, "b46bdd64b063e86927fb2826500ad512", 603242},
+ {"genespi.vmd", 0, "b7611916f32a370ae9832962fc17ef72", 758719},
+ {"spirou.vmd", 0, "8513dbf7ac51c057b21d371d6b217b47", 2550788},
+ {0, 0, 0, 0}
+ },
+ EN_ANY,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640 | kFeaturesSCNDemo,
+ 0, 0, 3
+ },
+ {
+ {
+ "playtoons1",
+ "Non-Interactive Demo",
+ {
+ {"e.scn", 0, "8a0db733c3f77be86e74e8242e5caa61", 124},
+ {"demarchg.vmd", 0, "d14a95da7d8792faf5503f649ffcbc12", 5619415},
+ {0, 0, 0, 0}
+ },
+ EN_ANY,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640 | kFeaturesSCNDemo,
+ 0, 0, 4
+ },
+ {
+ {
+ "playtoons1",
+ "Non-Interactive Demo",
+ {
+ {"i.scn", 0, "8b3294474d39970463663edd22341730", 285},
+ {"demarita.vmd", 0, "84c8672b91c7312462603446e224bfec", 5742533},
+ {"dembouit.vmd", 0, "7a5fdf0a4dbdfe72e31dd489ea0f8aa2", 3536786},
+ {"demo5.vmd", 0, "2abb7b6a26406c984f389f0b24b5e28e", 13290970},
+ {"demoita.vmd", 0, "b4c0622d14c8749965cd0f5dfca4cf4b", 1183566},
+ {"wooddem3.vmd", 0, "a1700596172c2d4e264760030c3a3d47", 8994250},
+ {0, 0, 0, 0}
+ },
+ IT_ITA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640 | kFeaturesSCNDemo,
+ 0, 0, 5
+ },
+ {
+ {
+ "playtoons1",
+ "Non-Interactive Demo",
+ {
+ {"s.scn", 0, "1f527010626b5490761f16ba7a6f639a", 251},
+ {"demaresp.vmd", 0, "3f860f944056842b35a5fd05416f208e", 5720619},
+ {"demboues.vmd", 0, "3a0caa10c98ef92a15942f8274075b43", 3535838},
+ {"demo5.vmd", 0, "2abb7b6a26406c984f389f0b24b5e28e", 13290970},
+ {"wooddem3.vmd", 0, "a1700596172c2d4e264760030c3a3d47", 8994250},
+ {0, 0, 0, 0}
+ },
+ ES_ESP,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640 | kFeaturesSCNDemo,
+ 0, 0, 6
+ },
+ {
+ {
+ "playtoons2",
+ "",
+ {
+ {"playtoon.stk", 0, "4772c96be88a57f0561519e4a1526c62", 24406262},
+ {"spirou.stk", 0, "5d9c7644d0c47840169b4d016765cc1a", 9816201},
+ {0, 0, 0, 0}
+ },
+ EN_ANY,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640,
+ "intro2.stk", 0, 0
+ },
+ {
+ {
+ "playtoons2",
+ "",
+ {
+ {"playtoon.stk", 0, "55a85036dd93cce93532d8f743d90074", 17467154},
+ {"spirou.stk", 0, "e3e1b6148dd72fafc3637f1a8e5764f5", 9812043},
+ {0, 0, 0, 0}
+ },
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640,
+ "intro2.stk", 0, 0
+ },
+ {
+ {
+ "playtoons2",
+ "",
+ {
+ {"playtoon.stk", 0, "c5ca2a288cdaefca9556cd9ae4b579cf", 25158926},
+ {"spirou.stk", 0, "91080dc148de1bbd6a97321c1a1facf3", 9817086},
+ {0, 0, 0, 0}
+ },
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640,
+ "intro2.stk", 0, 0
+ },
+ { // Supplied by scoriae in the forums
+ {
+ "playtoons2",
+ "",
+ {
+ {"playtoon.stk", 0, "9e513e993a5b0e2496add3f50c08764b", 30448506},
+ {"spirou.stk", 0, "993737f112ca6a9b33c814273280d832", 9825760},
+ {0, 0, 0, 0}
+ },
+ EN_ANY,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640,
+ "intro2.stk", 0, 0
+ },
+ {
+ {
+ "playtoons3",
+ "",
+ {
+ {"playtoon.stk", 0, "8c98e9a11be9bb203a55e8c6e68e519b", 25574338},
+ {"chato.stk", 0, "4fa4ed96a427c344e9f916f9f236598d", 6033793},
+ {0, 0, 0, 0}
+ },
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640,
+ "intro2.stk", 0, 0
+ },
+ {
+ {
+ "playtoons3",
+ "",
+ {
+ {"playtoon.stk", 0, "9e513e993a5b0e2496add3f50c08764b", 30448506},
+ {"chato.stk", 0, "8fc8d0da5b3e758908d1d7298d497d0b", 6041026},
+ {0, 0, 0, 0}
+ },
+ EN_ANY,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640,
+ "intro2.stk", 0, 0
+ },
+ {
+ {
+ "playtoons3",
+ "Pack mes histoires anim\xE9""es",
+ {
+ {"playtoon.stk", 0, "55f0293202963854192e39474e214f5f", 30448474},
+ {"chato.stk", 0, "4fa4ed96a427c344e9f916f9f236598d", 6033793},
+ {0, 0, 0, 0}
+ },
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640,
+ "intro2.stk", 0, 0
+ },
+ {
+ {
+ "playtoons3",
+ "",
+ {
+ {"playtoon.stk", 0, "c5ca2a288cdaefca9556cd9ae4b579cf", 25158926},
+ {"chato.stk", 0, "3c6cb3ac8a5a7cf681a19971a92a748d", 6033791},
+ {0, 0, 0, 0}
+ },
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640,
+ "intro2.stk", 0, 0
+ },
+ { // Supplied by Hkz on #scummvm
+ {
+ "playtoons3",
+ "",
+ {
+ {"playtoon.stk", 0, "4772c96be88a57f0561519e4a1526c62", 24406262},
+ {"chato.stk", 0, "bdef407387112bfcee90e664865ac3af", 6033867},
+ {0, 0, 0, 0}
+ },
+ EN_ANY,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640,
+ "intro2.stk", 0, 0
+ },
+ {
+ {
+ "playtoons4",
+ "",
+ {
+ {"playtoon.stk", 0, "b7f5afa2dc1b0f75970b7c07d175db1b", 24340406},
+ {"manda.stk", 0, "92529e0b927191d9898a34c2892e9a3a", 6485072},
+ {0, 0, 0, 0}
+ },
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640,
+ "intro2.stk", 0, 0
+ },
+ { //Supplied by goodoldgeorg in bug report #2820006
+ {
+ "playtoons4",
+ "",
+ {
+ {"playtoon.stk", 0, "9e513e993a5b0e2496add3f50c08764b", 30448506},
+ {"manda.stk", 0, "69a79c9f61b2618e482726f2ff68078d", 6499208},
+ {0, 0, 0, 0}
+ },
+ EN_ANY,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640,
+ "intro2.stk", 0, 0
+ },
+ {
+ {
+ "playtoons5",
+ "",
+ {
+ {"playtoon.stk", 0, "55f0293202963854192e39474e214f5f", 30448474},
+ {"wakan.stk", 0, "f493bf82851bc5ba74d57de6b7e88df8", 5520153},
+ {0, 0, 0, 0}
+ },
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640,
+ "intro2.stk", 0, 0
+ },
+ {
+ {
+ "bambou",
+ "",
+ {
+ {"intro.stk", 0, "2f8db6963ff8d72a8331627ebda918f4", 3613238},
+ {"bambou.itk", 0, "0875914d31126d0749313428f10c7768", 114440192},
+ {0, 0, 0, 0}
+ },
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeBambou,
+ kFeatures640,
+ "intro.stk", "intro.tot", 0
+ },
+ {
+ {
+ "playtnck1",
+ "",
+ {
+ {"playtoon.stk", 0, "5f9aae29265f1f105ad8ec195dff81de", 68382024},
+ {"dan.itk", 0, "906d67b3e438d5e95ec7ea9e781a94f3", 3000320},
+ {0, 0, 0, 0}
+ },
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640,
+ "intro2.stk", 0, 0
+ },
+ {
+ {
+ "playtnck2",
+ "",
+ {
+ {"playtoon.stk", 0, "5f9aae29265f1f105ad8ec195dff81de", 68382024},
+ {"dan.itk", 0, "74eeb075bd2cb47b243349730264af01", 3213312},
+ {0, 0, 0, 0}
+ },
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640,
+ "intro2.stk", 0, 0
+ },
+ {
+ {
+ "playtnck3",
+ "",
+ {
+ {"playtoon.stk", 0, "5f9aae29265f1f105ad8ec195dff81de", 68382024},
+ {"dan.itk", 0, "9a8f62809eca5a52f429b5b6a8e70f8f", 2861056},
+ {0, 0, 0, 0}
+ },
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640,
+ "intro2.stk", 0, 0
+ },
+ {
+ {
+ "adi2",
+ "Adi 2.0 for Teachers",
+ AD_ENTRY1s("adi2.stk", "da6f1fb68bff32260c5eecdf9286a2f5", 1533168),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdi2,
+ kFeaturesNone,
+ "adi2.stk", "ediintro.tot", 0
+ },
+ { // Found in french ADI 2 Francais-Maths CM1. Exact version not specified.
+ {
+ "adi2",
+ "Adi 2",
+ AD_ENTRY1s("adi2.stk", "23f279615c736dc38320f1348e70c36e", 10817668),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdi2,
+ kFeatures640,
+ "adi2.stk", "ediintro.tot", 0
+ },
+ { // Found in french ADI 2 Francais-Maths CE2. Exact version not specified.
+ {
+ "adi2",
+ "Adi 2",
+ AD_ENTRY1s("adi2.stk", "d4162c4298f9423ecc1fb04965557e90", 11531214),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdi2,
+ kFeatures640,
+ "adi2.stk", "ediintro.tot", 0
+ },
+ {
+ {
+ "adi2",
+ "Adi 2",
+ AD_ENTRY1s("adi2.stk", "29694c5a649298a42f87ae731d6d6f6d", 311132),
+ EN_ANY,
+ kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdi2,
+ kFeaturesNone,
+ "adi2.stk", "ediintro.tot", 0
+ },
+ {
+ {
+ "adi2",
+ "Adi 2",
+ AD_ENTRY1s("adi2.stk", "2a40bb48ccbd4e6fb3f7f0fc2f069d80", 17720132),
+ ES_ESP,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdi2,
+ kFeatures640,
+ "adi2.stk", "ediintro.tot", 0
+ },
+ {
+ {
+ "adi2",
+ "Adi 2.5",
+ AD_ENTRY1s("adi2.stk", "fcac60e6627f37aee219575b60859de9", 16944268),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdi2,
+ kFeatures640,
+ "adi2.stk", "ediintro.tot", 0
+ },
+ {
+ {
+ "adi2",
+ "Adi 2.5",
+ AD_ENTRY1s("adi2.stk", "072d5e2d7826a7c055865568ebf918bb", 16934596),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdi2,
+ kFeatures640,
+ "adi2.stk", "ediintro.tot", 0
+ },
+ {
+ {
+ "adi2",
+ "Adi 2.6",
+ AD_ENTRY1s("adi2.stk", "2fb940eb8105b12871f6b88c8c4d1615", 16780058),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdi2,
+ kFeatures640,
+ "adi2.stk", "ediintro.tot", 0
+ },
+ {
+ {
+ "adi2",
+ "Adi 2.6",
+ AD_ENTRY1s("adi2.stk", "fde7d98a67dbf859423b6473796e932a", 18044780),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdi2,
+ kFeatures640,
+ "adi2.stk", "ediintro.tot", 0
+ },
+ {
+ {
+ "adi2",
+ "Adi 2.7.1",
+ AD_ENTRY1s("adi2.stk", "6fa5dffebf5c7243c6af6b8c188ee00a", 19278008),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdi2,
+ kFeatures640,
+ "adi2.stk", "ediintro.tot", 0
+ },
+ {
+ {
+ "adi2",
+ "Non-Interactive Demo",
+ {
+ {"demo.scn", 0, "8b5ba359fd87d586ad39c1754bf6ea35", 168},
+ {"demadi2t.vmd", 0, "08a1b18cfe2015d3b43270da35cc813d", 7250723},
+ {"demarch.vmd", 0, "4c4a4616585d40ef3df209e3c3911062", 5622731},
+ {"demobou.vmd", 0, "2208b9855775564d15c4a5a559da0aec", 3550511},
+ {0, 0, 0, 0}
+ },
+ EN_ANY,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeAdi2,
+ kFeatures640 | kFeaturesSCNDemo,
+ 0, 0, 1
+ },
+ {
+ {
+ "adi4",
+ "Addy 4 Grundschule Basis CD",
+ AD_ENTRY1s("intro.stk", "d2f0fb8909e396328dc85c0e29131ba8", 5847588),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdi4,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "adi4",
+ "Addy 4 Sekundarstufe Basis CD",
+ AD_ENTRY1s("intro.stk", "367340e59c461b4fa36651cd74e32c4e", 5847378),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdi4,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "adi4",
+ "Adi 4.0",
+ AD_ENTRY1s("intro.stk", "a3c35d19b2d28ea261d96321d208cb5a", 6021466),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdi4,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "adi4",
+ "Adi 4.0",
+ AD_ENTRY1s("intro.stk", "44491d85648810bc6fcf84f9b3aa47d5", 5834944),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdi4,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "adi4",
+ "Adi 4.0",
+ AD_ENTRY1s("intro.stk", "29374c0e3c10b17dd8463b06a55ad093", 6012072),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdi4,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "adi4",
+ "Adi 4.0 Limited Edition",
+ AD_ENTRY1s("intro.stk", "ebbbc5e28a4adb695535ed989c1b8d66", 5929644),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdi4,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "adi4",
+ "ADI 4.10",
+ AD_ENTRY1s("intro.stk", "3e3fa9656e37d802027635ace88c4cc5", 5359144),
+ EN_GRB,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdi4,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ {
+ {
+ "adi4",
+ "ADI 4.10",
+ AD_ENTRY1s("intro.stk", "6afc2590856433b9f5295b032f2b205d", 5923112),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdi4,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ {
+ {
+ "adi4",
+ "ADI 4.11",
+ AD_ENTRY1s("intro.stk", "6296e4be4e0c270c24d1330881900c7f", 5921234),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdi4,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ {
+ {
+ "adi4",
+ "Addy 4.21",
+ AD_ENTRY1s("intro.stk", "534f0b674cd4830df94a9c32c4ea7225", 6878034),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdi4,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "adi4",
+ "ADI 4.21",
+ AD_ENTRY1s("intro.stk", "c5b9f6222c0b463f51dab47317c5b687", 5950490),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdi4,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ {
+ {
+ "adi4",
+ "Adi 4.0 Interactive Demo",
+ AD_ENTRY1s("intro.stk", "89ace204dbaac001425c73f394334f6f", 2413102),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdi4,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "adi4",
+ "Adi 4.0 / Adibou 2 Demo",
+ AD_ENTRY1s("intro.stk", "d41d8cd98f00b204e9800998ecf8427e", 0),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_DEMO,
+ GUIO_NONE
+ },
+ kGameTypeAdi4,
+ kFeatures640,
+ 0, 0, 0
+ },
+ {
+ {
+ "ajworld",
+ "",
+ AD_ENTRY1s("intro.stk", "e453bea7b28a67c930764d945f64d898", 3913628),
+ EN_ANY,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "adibou1",
+ "ADIBOU 1 Environnement 4-7 ans",
+ AD_ENTRY1s("intro.stk", "6db110188fcb7c5208d9721b5282682a", 4805104),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeAdibou1,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ {
+ {
+ "adibou2",
+ "ADIBOU 2",
+ AD_ENTRY1s("intro.stk", "94ae7004348dc8bf99c23a9a6ef81827", 956162),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdibou2,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ {
+ {
+ "adibou2",
+ "Le Jardin Magique d'Adibou",
+ AD_ENTRY1s("intro.stk", "a8ff86f3cc40dfe5898e0a741217ef27", 956328),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdibou2,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ {
+ {
+ "adibou2",
+ "ADIBOU 2",
+ AD_ENTRY1s("intro.stk", "092707829555f27706920e4cacf1fada", 8737958),
+ DE_DEU,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdibou2,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ {
+ {
+ "adibou2",
+ "ADIB\xD9 2",
+ AD_ENTRY1s("intro.stk", "092707829555f27706920e4cacf1fada", 8737958),
+ IT_ITA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdibou2,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ {
+ {
+ "adibou2",
+ "ADIBOU Version Decouverte",
+ AD_ENTRY1s("intro.stk", "558c14327b79ed39214b49d567a75e33", 8737856),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdibou2,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ {
+ {
+ "adibou2",
+ "ADIBOU 2.10 Environnement",
+ AD_ENTRY1s("intro.stk", "f2b797819aeedee557e904b0b5ccd82e", 8736454),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdibou2,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ {
+ {
+ "adibou2",
+ "ADIBOU 2.11 Environnement",
+ AD_ENTRY1s("intro.stk", "7b1f1f6f6477f54401e95d913f75e333", 8736904),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdibou2,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ {
+ {
+ "adibou2",
+ "ADIBOU 2.12 Environnement",
+ AD_ENTRY1s("intro.stk", "1e49c39a4a3ce6032a84b712539c2d63", 8738134),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdibou2,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ {
+ {
+ "adibou2",
+ "ADIBOU 2.13s Environnement",
+ AD_ENTRY1s("intro.stk", "092707829555f27706920e4cacf1fada", 8737958),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdibou2,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ {
+ {
+ "adibou2",
+ "ADIBOO 2.14 Environnement",
+ AD_ENTRY1s("intro.stk", "ff63637e3cb7f0a457edf79457b1c6b3", 9333874),
+ FR_FRA,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeAdibou2,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ { AD_TABLE_END_MARKER, kGameTypeNone, kFeaturesNone, 0, 0, 0}
+};
+
+static const GOBGameDescription fallbackDescs[] = {
+ { //0
+ {
+ "gob1",
+ "unknown",
+ AD_ENTRY1(0, 0),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ { //1
+ {
+ "gob1cd",
+ "unknown",
+ AD_ENTRY1(0, 0),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob1,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { //2
+ {
+ "gob2",
+ "unknown",
+ AD_ENTRY1(0, 0),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { //3
+ {
+ "gob2mac",
+ "unknown",
+ AD_ENTRY1(0, 0),
+ UNK_LANG,
+ kPlatformMacintosh,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { //4
+ {
+ "gob2cd",
+ "unknown",
+ AD_ENTRY1(0, 0),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob2,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { //5
+ {
+ "bargon",
+ "",
+ AD_ENTRY1(0, 0),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeBargon,
+ kFeaturesNone,
+ 0, 0, 0
+ },
+ { //6
+ {
+ "gob3",
+ "unknown",
+ AD_ENTRY1(0, 0),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { //7
+ {
+ "gob3cd",
+ "unknown",
+ AD_ENTRY1(0, 0),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGob3,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { //8
+ {
+ "woodruff",
+ "unknown",
+ AD_ENTRY1(0, 0),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeWoodruff,
+ kFeatures640,
+ 0, 0, 0
+ },
+ { //9
+ {
+ "lostintime",
+ "unknown",
+ AD_ENTRY1(0, 0),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { //10
+ {
+ "lostintime",
+ "unknown",
+ AD_ENTRY1(0, 0),
+ UNK_LANG,
+ kPlatformMacintosh,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesAdLib,
+ 0, 0, 0
+ },
+ { //11
+ {
+ "lostintime",
+ "unknown",
+ AD_ENTRY1(0, 0),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeLostInTime,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { //12
+ {
+ "urban",
+ "unknown",
+ AD_ENTRY1(0, 0),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeUrban,
+ kFeaturesCD,
+ 0, 0, 0
+ },
+ { //13
+ {
+ "playtoons1",
+ "unknown",
+ AD_ENTRY1(0, 0),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640,
+ 0, 0, 0
+ },
+ { //14
+ {
+ "playtoons2",
+ "unknown",
+ AD_ENTRY1(0, 0),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640,
+ 0, 0, 0
+ },
+ { //15
+ {
+ "playtoons3",
+ "unknown",
+ AD_ENTRY1(0, 0),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640,
+ 0, 0, 0
+ },
+ { //16
+ {
+ "playtoons4",
+ "unknown",
+ AD_ENTRY1(0, 0),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640,
+ 0, 0, 0
+ },
+ { //17
+ {
+ "playtoons5",
+ "unknown",
+ AD_ENTRY1(0, 0),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640,
+ 0, 0, 0
+ },
+ { //18
+ {
+ "playtoons construction kit",
+ "unknown",
+ AD_ENTRY1(0, 0),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypePlaytoons,
+ kFeatures640,
+ 0, 0, 0
+ },
+ { //19
+ {
+ "bambou",
+ "unknown",
+ AD_ENTRY1(0, 0),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeBambou,
+ kFeatures640,
+ 0, 0, 0
+ },
+ { //20
+ {
+ "fascination",
+ "unknown",
+ AD_ENTRY1(0, 0),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeFascination,
+ kFeaturesNone,
+ "disk0.stk", 0, 0
+ },
+ { //21
+ {
+ "geisha",
+ "unknown",
+ AD_ENTRY1(0, 0),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeGeisha,
+ kFeaturesNone,
+ "disk1.stk", "intro.tot", 0
+ },
+ { //22
+ {
+ "adi2",
+ "",
+ AD_ENTRY1(0, 0),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeAdi2,
+ kFeatures640,
+ "adi2.stk", 0, 0
+ },
+ { //23
+ {
+ "adi4",
+ "",
+ AD_ENTRY1(0, 0),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSUBTITLES | GUIO_NOSPEECH
+ },
+ kGameTypeAdi4,
+ kFeatures640,
+ "adif41.stk", 0, 0
+ },
+ { //24
+ {
+ "coktelplayer",
+ "unknown",
+ AD_ENTRY1(0, 0),
+ UNK_LANG,
+ kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ kGameTypeUrban,
+ kFeaturesAdLib | kFeatures640 | kFeaturesSCNDemo,
+ "", "", 8
+ }
+};
+
+static const ADFileBasedFallback fileBased[] = {
+ { &fallbackDescs[ 0], { "intro.stk", "disk1.stk", "disk2.stk", "disk3.stk", "disk4.stk", 0 } },
+ { &fallbackDescs[ 1], { "intro.stk", "gob.lic", 0 } },
+ { &fallbackDescs[ 2], { "intro.stk", 0 } },
+ { &fallbackDescs[ 2], { "intro.stk", "disk2.stk", "disk3.stk", 0 } },
+ { &fallbackDescs[ 3], { "intro.stk", "disk2.stk", "disk3.stk", "musmac1.mid", 0 } },
+ { &fallbackDescs[ 4], { "intro.stk", "gobnew.lic", 0 } },
+ { &fallbackDescs[ 5], { "intro.stk", "scaa.imd", "scba.imd", "scbf.imd", 0 } },
+ { &fallbackDescs[ 6], { "intro.stk", "imd.itk", 0 } },
+ { &fallbackDescs[ 7], { "intro.stk", "mus_gob3.lic", 0 } },
+ { &fallbackDescs[ 8], { "intro.stk", "woodruff.itk", 0 } },
+ { &fallbackDescs[ 9], { "intro.stk", "commun1.itk", 0 } },
+ { &fallbackDescs[10], { "intro.stk", "commun1.itk", "musmac1.mid", 0 } },
+ { &fallbackDescs[11], { "intro.stk", "commun1.itk", "lost.lic", 0 } },
+ { &fallbackDescs[12], { "intro.stk", "cd1.itk", "objet1.itk", 0 } },
+ { &fallbackDescs[13], { "playtoon.stk", "archi.stk", 0 } },
+ { &fallbackDescs[14], { "playtoon.stk", "spirou.stk", 0 } },
+ { &fallbackDescs[15], { "playtoon.stk", "chato.stk", 0 } },
+ { &fallbackDescs[16], { "playtoon.stk", "manda.stk", 0 } },
+ { &fallbackDescs[17], { "playtoon.stk", "wakan.stk", 0 } },
+ { &fallbackDescs[18], { "playtoon.stk", "dan.itk" } },
+ { &fallbackDescs[19], { "intro.stk", "bambou.itk", 0 } },
+ { &fallbackDescs[20], { "disk0.stk", "disk1.stk", "disk2.stk", "disk3.stk", 0 } },
+ { &fallbackDescs[21], { "disk1.stk", "disk2.stk", "disk3.stk", 0 } },
+ { &fallbackDescs[22], { "adi2.stk", 0 } },
+ { &fallbackDescs[23], { "adif41.stk", "adim41.stk", 0 } },
+ { &fallbackDescs[24], { "coktelplayer.scn", 0 } },
+ { 0, { 0 } }
+};
+
+}
diff --git a/engines/gob/gob.cpp b/engines/gob/gob.cpp
index d36a1c65c9..9c61bc1b75 100644
--- a/engines/gob/gob.cpp
+++ b/engines/gob/gob.cpp
@@ -345,8 +345,8 @@ void GobEngine::pauseGame() {
}
bool GobEngine::initGameParts() {
- _noMusic = MidiDriver::parseMusicDriver(ConfMan.get("music_driver")) == MD_NULL;
-
+ // just detect some devices some of which will be always there if the music is not disabled
+ _noMusic = MidiDriver::getMusicType(MidiDriver::detectDevice(MDT_PCSPK | MDT_MIDI | MDT_ADLIB)) == MT_NULL ? true : false;
_saveLoad = 0;
_global = new Global(this);
diff --git a/engines/gob/inter_v1.cpp b/engines/gob/inter_v1.cpp
index 805893d8a7..9e841e7e68 100644
--- a/engines/gob/inter_v1.cpp
+++ b/engines/gob/inter_v1.cpp
@@ -813,6 +813,14 @@ bool Inter_v1::o1_if(OpFuncParams &params) {
byte cmd;
bool boolRes;
+ // WORKAROUND: Windows Gob1 OUTODDV reload goblin stuck bug present in original
+ if ((_vm->getGameType() == kGameTypeGob1) && (_vm->_game->_script->pos() == 11294) &&
+ !scumm_stricmp(_vm->_game->_curTotFile, "avt00.tot") && VAR(59) == 1) {
+ warning("Workaround for Win Gob1 OUTODDV Reload Goblin Stuck Bug...");
+ WRITE_VAR(285, 0);
+ WRITE_VAR(59, 0);
+ }
+
boolRes = _vm->_game->_script->evalBoolResult();
if (boolRes) {
if ((params.counter == params.cmdCount) && (params.retFlag == 2))
@@ -1331,7 +1339,8 @@ bool Inter_v1::o1_goblinFunc(OpFuncParams &params) {
gobParams.retVarPtr.set(*_variables, 236);
cmd = _vm->_game->_script->readInt16();
- _vm->_game->_script->skip(2);
+ gobParams.paramCount = _vm->_game->_script->readInt16();
+
if ((cmd > 0) && (cmd < 17)) {
objDescSet = true;
gobParams.extraData = _vm->_game->_script->readInt16();
diff --git a/engines/groovie/cursor.cpp b/engines/groovie/cursor.cpp
index 3f304c7859..2d0a2df245 100644
--- a/engines/groovie/cursor.cpp
+++ b/engines/groovie/cursor.cpp
@@ -403,18 +403,15 @@ GrvCursorMan_v2::GrvCursorMan_v2(OSystem *system) :
// Open the icons file
Common::File iconsFile;
- if (!iconsFile.open("icons.ph")) {
+ if (!iconsFile.open("icons.ph"))
error("Groovie::Cursor: Couldn't open icons.ph");
- return;
- }
// Verify the signature
- uint32 tmp32 = iconsFile.readUint32LE();
+ uint32 tmp32 = iconsFile.readUint32BE();
uint16 tmp16 = iconsFile.readUint16LE();
- if (tmp32 != 0x6e6f6369 || tmp16 != 1) {
- error("Groovie::Cursor: icons.ph signature failed: %04X %d", tmp32, tmp16);
- return;
- }
+ if (tmp32 != MKID_BE('icon') || tmp16 != 1)
+ error("Groovie::Cursor: icons.ph signature failed: %s %d", tag2str(tmp32), tmp16);
+
// Read the number of icons
uint16 nicons = iconsFile.readUint16LE();
diff --git a/engines/groovie/detection.cpp b/engines/groovie/detection.cpp
index ec401e7d24..b30c2361d2 100644
--- a/engines/groovie/detection.cpp
+++ b/engines/groovie/detection.cpp
@@ -176,7 +176,11 @@ static const ADParams detectionParams = {
// Flags
kADFlagUseExtraAsHint,
// Additional GUI options (for every game}
- Common::GUIO_NOSUBTITLES | Common::GUIO_NOSFX
+ Common::GUIO_NOSUBTITLES | Common::GUIO_NOSFX,
+ // Maximum directory depth
+ 1,
+ // List of directory globs
+ 0
};
diff --git a/engines/groovie/font.cpp b/engines/groovie/font.cpp
index 6aa6c89d31..dc1d7ae73a 100644
--- a/engines/groovie/font.cpp
+++ b/engines/groovie/font.cpp
@@ -54,6 +54,7 @@ bool T7GFont::load(Common::SeekableReadStream &stream) {
if (stream.eos()) {
error("Groovie::T7GFont: Couldn't read the glyph offsets");
+ delete[] glyphOffsets;
return false;
}
diff --git a/engines/groovie/groovie.cpp b/engines/groovie/groovie.cpp
index 93675b3d1e..276b26ffe7 100644
--- a/engines/groovie/groovie.cpp
+++ b/engines/groovie/groovie.cpp
@@ -110,7 +110,7 @@ Common::Error GroovieEngine::run() {
// Prepare the font too
switch (_gameDescription->version) {
case kGroovieT7G:
- if (_gameDescription->desc.platform == Common::kPlatformMacintosh) {
+ if (getPlatform() == 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);
@@ -141,7 +141,7 @@ Common::Error GroovieEngine::run() {
}
// Create the music player
- if (_gameDescription->desc.platform == Common::kPlatformMacintosh)
+ if (getPlatform() == Common::kPlatformMacintosh)
_musicPlayer = new MusicPlayerMac(this);
else
_musicPlayer = new MusicPlayerXMI(this, _gameDescription->version == kGroovieT7G ? "fat" : "sample");
@@ -154,8 +154,8 @@ Common::Error GroovieEngine::run() {
if (_gameDescription->version == kGroovieT7G) {
// Run The 7th Guest's demo if requested
if (ConfMan.hasKey("demo_mode") && ConfMan.getBool("demo_mode"))
- filename = Common::String("demo.grv");
- else if (_gameDescription->desc.platform == Common::kPlatformMacintosh)
+ filename = "demo.grv";
+ else if (getPlatform() == Common::kPlatformMacintosh)
filename = "script.grv"; // Stored inside the executable's resource fork
} else if (_gameDescription->version == kGroovieV2) {
// Open the disk index
@@ -307,6 +307,10 @@ Common::Error GroovieEngine::run() {
return Common::kNoError;
}
+Common::Platform GroovieEngine::getPlatform() const {
+ return _gameDescription->desc.platform;
+}
+
bool GroovieEngine::hasFeature(EngineFeature f) const {
return
(f == kSupportsRTL) ||
diff --git a/engines/groovie/groovie.h b/engines/groovie/groovie.h
index 437debfd17..8ae5f4157f 100644
--- a/engines/groovie/groovie.h
+++ b/engines/groovie/groovie.h
@@ -82,6 +82,8 @@ public:
GroovieEngine(OSystem *syst, const GroovieGameDescription *gd);
~GroovieEngine();
+ Common::Platform getPlatform() const;
+
protected:
// Engine APIs
diff --git a/engines/groovie/music.cpp b/engines/groovie/music.cpp
index f2b190985d..34b180a68e 100644
--- a/engines/groovie/music.cpp
+++ b/engines/groovie/music.cpp
@@ -110,6 +110,20 @@ void MusicPlayer::playCD(uint8 track) {
// Play the track starting at the requested offset (1000ms = 75 frames)
g_system->getAudioCDManager()->play(track - 1, 1, startms * 75 / 1000, 0);
+
+ // If the audio is not playing from the CD, play the "fallback" MIDI.
+ // The Mac version has no CD tracks, so it will always use the MIDI.
+ if (!g_system->getAudioCDManager()->isPlaying()) {
+ if (track == 2) {
+ // Intro MIDI fallback
+ if (_vm->getPlatform() == Common::kPlatformMacintosh)
+ playSong(70);
+ else
+ playSong((19 << 10) | 36); // XMI.GJD, file 36
+ } else if (track == 3) {
+ // TODO: Credits MIDI fallback
+ }
+ }
}
void MusicPlayer::startBackground() {
@@ -385,8 +399,8 @@ MusicPlayerXMI::MusicPlayerXMI(GroovieEngine *vm, const Common::String &gtlName)
_midiParser = MidiParser::createParser_XMIDI();
// Create the driver
- MidiDriverType driver = detectMusicDriver(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MIDI);
- _driver = createMidi(driver);
+ MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
+ _driver = createMidi(dev);
this->open();
// Set the parser's driver
@@ -401,9 +415,9 @@ MusicPlayerXMI::MusicPlayerXMI(GroovieEngine *vm, const Common::String &gtlName)
}
// Load the Global Timbre Library
- if (driver == MD_ADLIB) {
+ if (MidiDriver::getMusicType(dev) == MT_ADLIB) {
// MIDI through AdLib
- _musicType = MD_ADLIB;
+ _musicType = MT_ADLIB;
loadTimbres(gtlName + ".ad");
// Setup the percussion channel
@@ -411,9 +425,9 @@ MusicPlayerXMI::MusicPlayerXMI(GroovieEngine *vm, const Common::String &gtlName)
if (_timbres[i].bank == 0x7F)
setTimbreAD(9, _timbres[i]);
}
- } else if ((driver == MD_MT32) || ConfMan.getBool("native_mt32")) {
+ } else if ((MidiDriver::getMusicType(dev) == MT_MT32) || ConfMan.getBool("native_mt32")) {
// MT-32
- _musicType = MD_MT32;
+ _musicType = MT_MT32;
loadTimbres(gtlName + ".mt");
} else {
// GM
@@ -454,9 +468,9 @@ void MusicPlayerXMI::send(uint32 b) {
for (int i = 0; i < numTimbres; i++) {
if ((_timbres[i].bank == _chanBanks[chan]) &&
(_timbres[i].patch == patch)) {
- if (_musicType == MD_ADLIB) {
+ if (_musicType == MT_ADLIB) {
setTimbreAD(chan, _timbres[i]);
- } else if (_musicType == MD_MT32) {
+ } else if (_musicType == MT_MT32) {
setTimbreMT(chan, _timbres[i]);
}
return;
@@ -681,8 +695,8 @@ MusicPlayerMac::MusicPlayerMac(GroovieEngine *vm) : MusicPlayerMidi(vm) {
_midiParser = MidiParser::createParser_SMF();
// Create the driver
- MidiDriverType driver = detectMusicDriver(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MIDI);
- _driver = createMidi(driver);
+ MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
+ _driver = createMidi(dev);
this->open();
// Set the parser's driver
@@ -702,20 +716,64 @@ bool MusicPlayerMac::load(uint32 fileref, bool loop) {
Common::SeekableReadStream *file = _vm->_macResFork->getResource(MKID_BE('cmid'), fileref & 0x3FF);
if (file) {
- // TODO: A form of LZSS, not supported by the current Groovie decoder.
- // The offset/length uint16 is BE, but not sure the amount of length bits
- // nor whether the offset is absolute/relative.
- warning("TODO: Mac Compressed MIDI: cmid 0x%04x", fileref & 0x3FF);
+ // Found the resource, decompress it
+ Common::SeekableReadStream *tmp = decompressMidi(file);
delete file;
- return false;
+ file = tmp;
+ } else {
+ // Otherwise, it's uncompressed
+ file = _vm->_macResFork->getResource(MKID_BE('Midi'), fileref & 0x3FF);
+ if (!file)
+ error("Groovie::Music: Couldn't find resource 0x%04X", fileref);
}
- // Otherwise, it's uncompressed
- file = _vm->_macResFork->getResource(MKID_BE('Midi'), fileref & 0x3FF);
- if (!file)
- error("Groovie::Music: Couldn't find resource 0x%04X", fileref);
-
return loadParser(file, loop);
}
+Common::SeekableReadStream *MusicPlayerMac::decompressMidi(Common::SeekableReadStream *stream) {
+ // Initialize an output buffer of the given size
+ uint32 size = stream->readUint32BE();
+ byte *output = (byte *)malloc(size);
+
+ byte *current = output;
+ uint32 decompBytes = 0;
+ while ((decompBytes < size) && !stream->eos()) {
+ // 8 flags
+ byte flags = stream->readByte();
+
+ for (byte i = 0; (i < 8) && !stream->eos(); i++) {
+ if (flags & 1) {
+ // 1: Next byte is a literal
+ *(current++) = stream->readByte();
+ if (stream->eos())
+ continue;
+ decompBytes++;
+ } else {
+ // 0: It's a reference to part of the history
+ uint16 args = stream->readUint16BE();
+ if (stream->eos())
+ continue;
+
+ // Length = 4bit unsigned (3 minimal)
+ uint8 length = (args >> 12) + 3;
+
+ // Offset = 12bit signed (all values are negative)
+ int16 offset = (args & 0xFFF) | 0xF000;
+
+ // Copy from the past decompressed bytes
+ decompBytes += length;
+ while (length > 0) {
+ *(current) = *(current + offset);
+ current++;
+ length--;
+ }
+ }
+ flags = flags >> 1;
+ }
+ }
+
+ // Return the output buffer wrapped in a MemoryReadStream
+ return new Common::MemoryReadStream(output, size, DisposeAfterUse::YES);
+}
+
} // End of Groovie namespace
diff --git a/engines/groovie/music.h b/engines/groovie/music.h
index 2feef9cbf7..5b5f5bd346 100644
--- a/engines/groovie/music.h
+++ b/engines/groovie/music.h
@@ -161,6 +161,9 @@ public:
protected:
bool load(uint32 fileref, bool loop);
+
+private:
+ Common::SeekableReadStream *decompressMidi(Common::SeekableReadStream *stream);
};
} // End of Groovie namespace
diff --git a/engines/groovie/script.cpp b/engines/groovie/script.cpp
index 297da6ccc2..9fd7fa7d63 100644
--- a/engines/groovie/script.cpp
+++ b/engines/groovie/script.cpp
@@ -86,11 +86,11 @@ Script::Script(GroovieEngine *vm, EngineVersion version) :
}
// Initialize the music type variable
- int midiDriver = MidiDriver::detectMusicDriver(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MIDI);
- if (midiDriver == MD_ADLIB) {
+ MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
+ if (MidiDriver::getMusicType(dev) == MT_ADLIB) {
// MIDI through AdLib
setVariable(0x100, 0);
- } else if ((midiDriver == MD_MT32) || ConfMan.getBool("native_mt32")) {
+ } else if ((MidiDriver::getMusicType(dev) == MT_MT32) || ConfMan.getBool("native_mt32")) {
// MT-32
setVariable(0x100, 2);
} else {
diff --git a/engines/kyra/detection.cpp b/engines/kyra/detection.cpp
index 6e9359e7fc..f3e6c7c8cc 100644
--- a/engines/kyra/detection.cpp
+++ b/engines/kyra/detection.cpp
@@ -41,1172 +41,7 @@ struct KYRAGameDescription {
Kyra::GameFlags flags;
};
-namespace {
-
-#define FLAGS(x, y, z, a, b, c, d, id) { Common::UNK_LANG, Common::UNK_LANG, Common::UNK_LANG, Common::kPlatformUnknown, x, y, z, a, b, c, d, id }
-#define FLAGS_FAN(fanLang, repLang, x, y, z, a, b, c, d, id) { Common::UNK_LANG, fanLang, repLang, Common::kPlatformUnknown, x, y, z, a, b, c, d, id }
-
-#define KYRA1_FLOPPY_FLAGS FLAGS(false, false, false, false, false, false, false, Kyra::GI_KYRA1)
-#define KYRA1_FLOPPY_CMP_FLAGS FLAGS(false, false, false, false, false, false, true, Kyra::GI_KYRA1)
-#define KYRA1_AMIGA_FLAGS FLAGS(false, false, false, false, false, false, false, Kyra::GI_KYRA1)
-#define KYRA1_TOWNS_FLAGS FLAGS(false, true, false, false, false, false, false, Kyra::GI_KYRA1)
-#define KYRA1_TOWNS_SJIS_FLAGS FLAGS(false, true, false, true, false, false, false, Kyra::GI_KYRA1)
-#define KYRA1_CD_FLAGS FLAGS(false, true, true, false, false, false, false, Kyra::GI_KYRA1)
-#define KYRA1_DEMO_FLAGS FLAGS(true, false, false, false, false, false, false, Kyra::GI_KYRA1)
-#define KYRA1_DEMO_CD_FLAGS FLAGS(true, true, true, false, false, false, false, Kyra::GI_KYRA1)
-
-#define KYRA2_FLOPPY_FLAGS FLAGS(false, false, false, false, false, false, false, Kyra::GI_KYRA2)
-#define KYRA2_FLOPPY_CMP_FLAGS FLAGS(false, false, false, false, false, false, true, Kyra::GI_KYRA2)
-#define KYRA2_CD_FLAGS FLAGS(false, false, true, false, false, false, false, Kyra::GI_KYRA2)
-#define KYRA2_CD_FAN_FLAGS(x, y) FLAGS_FAN(x, y, false, false, true, false, false, false, false, Kyra::GI_KYRA2)
-#define KYRA2_CD_DEMO_FLAGS FLAGS(true, false, true, false, false, false, false, Kyra::GI_KYRA2)
-#define KYRA2_DEMO_FLAGS FLAGS(true, false, false, false, false, false, false, Kyra::GI_KYRA2)
-#define KYRA2_TOWNS_FLAGS FLAGS(false, false, false, false, false, false, false, Kyra::GI_KYRA2)
-#define KYRA2_TOWNS_SJIS_FLAGS FLAGS(false, false, false, true, false, false, false, Kyra::GI_KYRA2)
-
-#define KYRA3_CD_FLAGS FLAGS(false, false, true, false, false, true, true, Kyra::GI_KYRA3)
-#define KYRA3_CD_INS_FLAGS FLAGS(false, false, true, false, false, true, false, Kyra::GI_KYRA3)
-#define KYRA3_CD_FAN_FLAGS(x, y) FLAGS_FAN(x, y, false, false, true, false, false, true, false, Kyra::GI_KYRA3)
-
-#define LOL_CD_FLAGS FLAGS(false, false, true, false, false, false, false, Kyra::GI_LOL)
-#define LOL_FLOPPY_FLAGS FLAGS(false, false, false, false, false, false, false, Kyra::GI_LOL)
-#define LOL_FLOPPY_CMP_FLAGS FLAGS(false, false, false, false, false, false, true, Kyra::GI_LOL)
-#define LOL_PC98_SJIS_FLAGS FLAGS(false, false, false, true, true, false, false, Kyra::GI_LOL)
-#define LOL_DEMO_FLAGS FLAGS(true, true, false, false, false, false, false, Kyra::GI_LOL)
-#define LOL_KYRA2_DEMO_FLAGS FLAGS(true, false, false, false, false, false, false, Kyra::GI_KYRA2)
-
-const KYRAGameDescription adGameDescs[] = {
- /* disable these targets until they get supported
- {
- {
- "kyra1",
- 0,
- AD_ENTRY1("DISK1.EXE", "c8641d0414d6c966d0a3dad79db07bf4"),
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- KYRA1_FLOPPY_CMP_FLAGS
- },
-
- {
- {
- "kyra1",
- 0,
- AD_ENTRY1("DISK1.EXE", "5d5cee4c3d0b68d586788b74243d254a"),
- Common::DE_DEU,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- KYRA1_FLOPPY_CMP_FLAGS
- },
- */
-
- {
- {
- "kyra1",
- "Extracted",
- AD_ENTRY1("GEMCUT.EMC", "3c244298395520bb62b5edfe41688879"),
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- KYRA1_FLOPPY_FLAGS
- },
- {
- {
- "kyra1",
- "Extracted",
- AD_ENTRY1("GEMCUT.EMC", "796e44863dd22fa635b042df1bf16673"),
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- KYRA1_FLOPPY_FLAGS
- },
- {
- {
- "kyra1",
- "Extracted",
- AD_ENTRY1("GEMCUT.EMC", "abf8eb360e79a6c2a837751fbd4d3d24"),
- Common::FR_FRA,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- KYRA1_FLOPPY_FLAGS
- },
- {
- {
- "kyra1",
- "Extracted",
- AD_ENTRY1("GEMCUT.EMC", "6018e1dfeaca7fe83f8d0b00eb0dd049"),
- Common::DE_DEU,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- KYRA1_FLOPPY_FLAGS
- },
- { // from Arne.F
- {
- "kyra1",
- "Extracted",
- AD_ENTRY1("GEMCUT.EMC", "f0b276781f47c130f423ec9679fe9ed9"),
- Common::DE_DEU,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- KYRA1_FLOPPY_FLAGS
- },
- { // from VooD
- {
- "kyra1",
- "Extracted",
- AD_ENTRY1("GEMCUT.EMC", "8909b41596913b3f5deaf3c9f1017b01"),
- Common::ES_ESP,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- KYRA1_FLOPPY_FLAGS
- },
- { // floppy 1.8 from clemmy
- {
- "kyra1",
- "Extracted",
- AD_ENTRY1("GEMCUT.EMC", "747861d2a9c643c59fdab570df5b9093"),
- Common::ES_ESP,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- KYRA1_FLOPPY_FLAGS
- },
- { // from gourry
- {
- "kyra1",
- "Extracted",
- AD_ENTRY1("GEMCUT.EMC", "ef08c8c237ee1473fd52578303fc36df"),
- Common::IT_ITA,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- KYRA1_FLOPPY_FLAGS
- },
-
- {
- {
- "kyra1",
- 0,
- {
- { "GEMCUT.PAK", 0, "2bd1da653eaefd691e050e4a9eb68a64", -1 },
- { "GEMCUT.EMC", 0, "2a3f44e179f1e9f7643e90083c747571", -1 },
- { NULL, 0, NULL, 0 }
- },
- Common::EN_ANY,
- Common::kPlatformAmiga,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- KYRA1_AMIGA_FLAGS
- },
-
- {
- {
- "kyra1",
- 0,
- {
- { "GEMCUT.PAK", 0, "2bd1da653eaefd691e050e4a9eb68a64", -1 },
- { "GEMCUT.EMC", 0, "74f99e9ed99abf8d0429826d78485a2a", -1 },
- { NULL, 0, NULL, 0 }
- },
- Common::DE_DEU,
- Common::kPlatformAmiga,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- KYRA1_AMIGA_FLAGS
- },
-
- {
- {
- "kyra1",
- 0,
- {
- { "GEMCUT.EMC", 0, "796e44863dd22fa635b042df1bf16673", -1 },
- { "BEAD.CPS", 0, "3038466f65b7751451844707187aa401", -1 },
- { NULL, 0, NULL, 0 }
- },
- Common::EN_ANY,
- Common::kPlatformMacintosh,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- KYRA1_FLOPPY_FLAGS
- },
-
- { // FM-TOWNS version
- {
- "kyra1",
- 0,
- {
- { "EMC.PAK", 0, "a046bb0b422061aab8e4c4689400343a", -1 },
- { "TWMUSIC.PAK", 0, "e53bca3a3e3fb49107d59463ec387a59", -1 },
- { NULL, 0, NULL, 0 }
- },
- Common::EN_ANY,
- Common::kPlatformFMTowns,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- KYRA1_TOWNS_FLAGS
- },
- {
- {
- "kyra1",
- 0,
- {
- { "JMC.PAK", 0, "9c5707a2a478e8167e44283246612d2c", -1 },
- { "TWMUSIC.PAK", 0, "e53bca3a3e3fb49107d59463ec387a59", -1 },
- { NULL, 0, NULL, 0 }
- },
- Common::JA_JPN,
- Common::kPlatformFMTowns,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- KYRA1_TOWNS_SJIS_FLAGS
- },
-
- // PC-9801 floppy + CD / PC-9821 floppy version are all using the same data files,
- // thus we will mark it as non CD game.
- {
- {
- "kyra1",
- "",
- {
- { "JMC.PAK", 0, "9c5707a2a478e8167e44283246612d2c", -1 },
- { "MUSIC98.PAK", 0, "02fc212f799331b769b274e33d87b37f", -1 },
- { NULL, 0, NULL, 0 }
- },
- Common::JA_JPN,
- Common::kPlatformPC98,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- KYRA1_TOWNS_SJIS_FLAGS
- },
-
- {
- {
- "kyra1",
- "CD",
- AD_ENTRY1("GEMCUT.PAK", "fac399fe62f98671e56a005c5e94e39f"),
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_CD,
- Common::GUIO_NONE
- },
- KYRA1_CD_FLAGS
- },
- {
- {
- "kyra1",
- "CD",
- AD_ENTRY1("GEMCUT.PAK", "230f54e6afc007ab4117159181a1c722"),
- Common::DE_DEU,
- Common::kPlatformPC,
- ADGF_CD,
- Common::GUIO_NONE
- },
- KYRA1_CD_FLAGS
- },
- {
- {
- "kyra1",
- "CD",
- AD_ENTRY1("GEMCUT.PAK", "b037c41768b652a040360ffa3556fd2a"),
- Common::FR_FRA,
- Common::kPlatformPC,
- ADGF_CD,
- Common::GUIO_NONE
- },
- KYRA1_CD_FLAGS
- },
-
- { // italian fan translation see fr#1727941 "KYRA: add Italian CD Version to kyra.dat"
- {
- "kyra1",
- "CD",
- AD_ENTRY1("GEMCUT.PAK", "d8327fc4b7a72b23c900fa13aef4093a"),
- Common::IT_ITA,
- Common::kPlatformPC,
- ADGF_CD,
- Common::GUIO_NONE
- },
- KYRA1_CD_FLAGS
- },
-
- { // Kyra 1 Mac CD as mentioned in fr #2766454 "KYRA1: Add support for Macintosh CD" by nnooiissee
- {
- "kyra1",
- "CD",
- {
- { "GEMCUT.PAK", 0, "d3d4b281cd357230aabcec46843d04bd", -1 },
- { "BEAD.CPS", 0, "3038466f65b7751451844707187aa401", -1 },
- { NULL, 0, NULL, 0 }
- },
- Common::EN_ANY,
- Common::kPlatformMacintosh,
- ADGF_CD,
- Common::GUIO_NONE
- },
- KYRA1_CD_FLAGS
- },
- {
- {
- "kyra1",
- "CD",
- {
- { "GEMCUT.PAK", 0, "4a0cb720e824295bcbccbd1407652110", -1 },
- { "BEAD.CPS", 0, "3038466f65b7751451844707187aa401", -1 },
- { NULL, 0, NULL, 0 }
- },
- Common::DE_DEU,
- Common::kPlatformMacintosh,
- ADGF_CD,
- Common::GUIO_NONE
- },
- KYRA1_CD_FLAGS
- },
- {
- {
- "kyra1",
- "CD",
- {
- { "GEMCUT.PAK", 0, "b71ee090aa12e80ed2ba068826d92bed", -1 },
- { "BEAD.CPS", 0, "3038466f65b7751451844707187aa401", -1 },
- { NULL, 0, NULL, 0 }
- },
- Common::FR_FRA,
- Common::kPlatformMacintosh,
- ADGF_CD,
- Common::GUIO_NONE
- },
- KYRA1_CD_FLAGS
- },
-
- {
- {
- "kyra1",
- "Demo",
- AD_ENTRY1("DEMO1.WSA", "fb722947d94897512b13b50cc84fd648"),
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_DEMO,
- Common::GUIO_NOSPEECH
- },
- KYRA1_DEMO_FLAGS
- },
-
- { // Special Kyrandia 1 CD demo
- {
- "kyra1",
- "Demo/CD",
- AD_ENTRY1("INTRO.VRM", "e3045fb69b8c29db84b8fda3ccbdac54"),
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_DEMO | ADGF_CD,
- Common::GUIO_NONE
- },
- KYRA1_DEMO_CD_FLAGS
- },
-
- { // Floppy version
- {
- "kyra2",
- 0,
- AD_ENTRY1("WESTWOOD.001", "3f52dda68c4f7696c8309038be9f4151"),
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- KYRA2_FLOPPY_CMP_FLAGS
- },
-
- { // Floppy version
- {
- "kyra2",
- 0,
- AD_ENTRY1("WESTWOOD.001", "d787b9559afddfe058b84c0b3a787224"),
- Common::DE_DEU,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- KYRA2_FLOPPY_CMP_FLAGS
- },
-
- { // Floppy version extracted
- {
- "kyra2",
- "Extracted",
- AD_ENTRY1("FATE.PAK", "1ba18be685ad8e5a0ab5d46a0ce4d345"),
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- KYRA2_FLOPPY_FLAGS
- },
-
- { // Floppy version extracted
- {
- "kyra2",
- "Extracted",
- AD_ENTRY1("FATE.PAK", "262fb69dd8e52e596c7aefc6456f7c1b"),
- Common::DE_DEU,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- KYRA2_FLOPPY_FLAGS
- },
-
- { // Floppy version extracted
- {
- "kyra2",
- "Extracted",
- AD_ENTRY1("FATE.PAK", "f7de11506b4c8fdf64bc763206c3e4e7"),
- Common::FR_FRA,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- KYRA2_FLOPPY_FLAGS
- },
-
- { // Floppy version extracted
- {
- "kyra2",
- "Extracted",
- AD_ENTRY1("FATE.PAK", "e0a70c31b022cb4bb3061890020fc27c"),
- Common::IT_ITA,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- KYRA2_FLOPPY_FLAGS
- },
-
- { // CD version
- {
- "kyra2",
- "CD",
- AD_ENTRY1("FATE.PAK", "28cbad1c5bf06b2d3825ae57d760d032"),
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE | ADGF_CD,
- Common::GUIO_NONE
- },
- KYRA2_CD_FLAGS
- },
- {
- {
- "kyra2",
- "CD",
- AD_ENTRY1("FATE.PAK", "28cbad1c5bf06b2d3825ae57d760d032"),
- Common::DE_DEU,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE | ADGF_CD,
- Common::GUIO_NONE
- },
- KYRA2_CD_FLAGS
- },
- {
- {
- "kyra2",
- "CD",
- AD_ENTRY1("FATE.PAK", "28cbad1c5bf06b2d3825ae57d760d032"),
- Common::FR_FRA,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE | ADGF_CD,
- Common::GUIO_NONE
- },
- KYRA2_CD_FLAGS
- },
-
- // Italian fan translation, see fr#2003504 "KYRA: add support for Italian version of Kyrandia 2&3"
- { // CD version
- {
- "kyra2",
- "CD",
- AD_ENTRY1("FATE.PAK", "30487f3b8d7790c7857f4769ff2dd125"),
- Common::IT_ITA,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE | ADGF_CD,
- Common::GUIO_NONE
- },
- KYRA2_CD_FAN_FLAGS(Common::IT_ITA, Common::EN_ANY)
- },
- {
- {
- "kyra2",
- "CD",
- AD_ENTRY1("FATE.PAK", "30487f3b8d7790c7857f4769ff2dd125"),
- Common::DE_DEU,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE | ADGF_CD,
- Common::GUIO_NONE
- },
- KYRA2_CD_FAN_FLAGS(Common::IT_ITA, Common::EN_ANY)
- },
- {
- {
- "kyra2",
- "CD",
- AD_ENTRY1("FATE.PAK", "30487f3b8d7790c7857f4769ff2dd125"),
- Common::FR_FRA,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE | ADGF_CD,
- Common::GUIO_NONE
- },
- KYRA2_CD_FAN_FLAGS(Common::IT_ITA, Common::EN_ANY)
- },
-
- {
- {
- "kyra2",
- "CD",
- AD_ENTRY1("FATE.PAK", "39772ff82e42c4c520050518deb82e64"),
- Common::IT_ITA,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE | ADGF_CD,
- Common::GUIO_NONE
- },
- KYRA2_CD_FAN_FLAGS(Common::IT_ITA, Common::EN_ANY)
- },
-
- {
- {
- "kyra2",
- "CD",
- AD_ENTRY1("FATE.PAK", "39772ff82e42c4c520050518deb82e64"),
- Common::DE_DEU,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE | ADGF_CD,
- Common::GUIO_NONE
- },
- KYRA2_CD_FAN_FLAGS(Common::IT_ITA, Common::EN_ANY)
- },
-
- {
- {
- "kyra2",
- "CD",
- AD_ENTRY1("FATE.PAK", "39772ff82e42c4c520050518deb82e64"),
- Common::FR_FRA,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE | ADGF_CD,
- Common::GUIO_NONE
- },
- KYRA2_CD_FAN_FLAGS(Common::IT_ITA, Common::EN_ANY)
- },
-
- { // Interactive Demo
- {
- "kyra2",
- "CD/Demo",
- AD_ENTRY1("THANKS.CPS", "b1a78d990b120bb2234b7094f74e30a5"),
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE | ADGF_CD | ADGF_DEMO,
- Common::GUIO_NONE
- },
- KYRA2_CD_DEMO_FLAGS
- },
-
- { // Interactive Demo
- {
- "kyra2",
- "CD/Demo",
- AD_ENTRY1("THANKS.CPS", "b1a78d990b120bb2234b7094f74e30a5"),
- Common::DE_DEU,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE | ADGF_CD | ADGF_DEMO,
- Common::GUIO_NONE
- },
- KYRA2_CD_DEMO_FLAGS
- },
-
- { // Interactive Demo
- {
- "kyra2",
- "CD/Demo",
- AD_ENTRY1("THANKS.CPS", "b1a78d990b120bb2234b7094f74e30a5"),
- Common::FR_FRA,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE | ADGF_CD | ADGF_DEMO,
- Common::GUIO_NONE
- },
- KYRA2_CD_DEMO_FLAGS
- },
-
- { // Non-Interactive Demos
- {
- "kyra2",
- "Demo",
- AD_ENTRY1("VOC.PAK", "ecb3561b63749158172bf21528cf5f45"),
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_DEMO,
- Common::GUIO_NONE
- },
- KYRA2_DEMO_FLAGS
- },
-
- { // FM-TOWNS
- {
- "kyra2",
- 0,
- AD_ENTRY1("WSCORE.PAK", "c44de1302b67f27d4707409987b7a685"),
- Common::EN_ANY,
- Common::kPlatformFMTowns,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- KYRA2_TOWNS_FLAGS
- },
- {
- {
- "kyra2",
- 0,
- AD_ENTRY1("WSCORE.PAK", "c44de1302b67f27d4707409987b7a685"),
- Common::JA_JPN,
- Common::kPlatformFMTowns,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- KYRA2_TOWNS_SJIS_FLAGS
- },
- { // PC-9821
- {
- "kyra2",
- "CD",
- AD_ENTRY1("WSCORE.PAK", "c44de1302b67f27d4707409987b7a685"),
- Common::EN_ANY,
- Common::kPlatformPC98,
- ADGF_CD,
- Common::GUIO_NOSPEECH
- },
- KYRA2_TOWNS_FLAGS
- },
- {
- {
- "kyra2",
- "CD",
- AD_ENTRY1("WSCORE.PAK", "c44de1302b67f27d4707409987b7a685"),
- Common::JA_JPN,
- Common::kPlatformPC98,
- ADGF_CD,
- Common::GUIO_NOSPEECH
- },
- KYRA2_TOWNS_SJIS_FLAGS
- },
-
- // Kyra3
-
- // non installed version
- {
- {
- "kyra3",
- 0,
- {
- { "ONETIME.PAK", 0, "3833ff312757b8e6147f464cca0a6587", -1 },
- { "WESTWOOD.001", 0, 0, -1 },
- { 0, 0, 0, 0 }
- },
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE,
- Common::GUIO_NOMIDI
- },
- KYRA3_CD_FLAGS
- },
- {
- {
- "kyra3",
- 0,
- {
- { "ONETIME.PAK", 0, "3833ff312757b8e6147f464cca0a6587", -1 },
- { "WESTWOOD.001", 0, 0, -1 },
- { 0, 0, 0, 0 }
- },
- Common::DE_DEU,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE,
- Common::GUIO_NOMIDI
- },
- KYRA3_CD_FLAGS
- },
- {
- {
- "kyra3",
- 0,
- {
- { "ONETIME.PAK", 0, "3833ff312757b8e6147f464cca0a6587", -1 },
- { "WESTWOOD.001", 0, 0, -1 },
- { 0, 0, 0, 0 }
- },
- Common::FR_FRA,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE,
- Common::GUIO_NOMIDI
- },
- KYRA3_CD_FLAGS
- },
-
- // installed version
- {
- {
- "kyra3",
- 0,
- {
- { "ONETIME.PAK", 0, "3833ff312757b8e6147f464cca0a6587", -1 },
- { "AUD.PAK", 0, 0, -1 },
- { 0, 0, 0, 0 }
- },
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE,
- Common::GUIO_NOMIDI
- },
- KYRA3_CD_INS_FLAGS
- },
- {
- {
- "kyra3",
- 0,
- {
- { "ONETIME.PAK", 0, "3833ff312757b8e6147f464cca0a6587", -1 },
- { "AUD.PAK", 0, 0, -1 },
- { 0, 0, 0, 0 }
- },
- Common::DE_DEU,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE,
- Common::GUIO_NOMIDI
- },
- KYRA3_CD_INS_FLAGS
- },
- {
- {
- "kyra3",
- 0,
- {
- { "ONETIME.PAK", 0, "3833ff312757b8e6147f464cca0a6587", -1 },
- { "AUD.PAK", 0, 0, -1 },
- { 0, 0, 0, 0 }
- },
- Common::FR_FRA,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE,
- Common::GUIO_NOMIDI
- },
- KYRA3_CD_INS_FLAGS
- },
-
- // Mac version
- {
- {
- "kyra3",
- 0,
- {
- { "ONETIME.PAK", 0, "3833ff312757b8e6147f464cca0a6587", -1 },
- { "AUD.PAK", 0, 0, -1 },
- { 0, 0, 0, 0 }
- },
- Common::EN_ANY,
- Common::kPlatformMacintosh,
- ADGF_DROPLANGUAGE,
- Common::GUIO_NOMIDI
- },
- KYRA3_CD_INS_FLAGS
- },
- {
- {
- "kyra3",
- 0,
- {
- { "ONETIME.PAK", 0, "3833ff312757b8e6147f464cca0a6587", -1 },
- { "AUD.PAK", 0, 0, -1 },
- { 0, 0, 0, 0 }
- },
- Common::DE_DEU,
- Common::kPlatformMacintosh,
- ADGF_DROPLANGUAGE,
- Common::GUIO_NOMIDI
- },
- KYRA3_CD_INS_FLAGS
- },
- {
- {
- "kyra3",
- 0,
- {
- { "ONETIME.PAK", 0, "3833ff312757b8e6147f464cca0a6587", -1 },
- { "AUD.PAK", 0, 0, -1 },
- { 0, 0, 0, 0 }
- },
- Common::FR_FRA,
- Common::kPlatformMacintosh,
- ADGF_DROPLANGUAGE,
- Common::GUIO_NOMIDI
- },
- KYRA3_CD_INS_FLAGS
- },
-
- // Spanish fan translation, see fr#1994040 "KYRA3: Add support for Spanish fan translation"
- {
- {
- "kyra3",
- 0,
- {
- { "ONETIME.PAK", 0, "9aaca21d2a205ca02ec53132f2911794", -1 },
- { "AUD.PAK", 0, 0, -1 },
- { 0, 0, 0, 0 }
- },
- Common::ES_ESP,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE,
- Common::GUIO_NOMIDI
- },
- KYRA3_CD_FAN_FLAGS(Common::ES_ESP, Common::EN_ANY)
- },
- {
- {
- "kyra3",
- 0,
- {
- { "ONETIME.PAK", 0, "9aaca21d2a205ca02ec53132f2911794", -1 },
- { "AUD.PAK", 0, 0, -1 },
- { 0, 0, 0, 0 }
- },
- Common::DE_DEU,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE,
- Common::GUIO_NOMIDI
- },
- KYRA3_CD_FAN_FLAGS(Common::ES_ESP, Common::EN_ANY)
- },
- {
- {
- "kyra3",
- 0,
- {
- { "ONETIME.PAK", 0, "9aaca21d2a205ca02ec53132f2911794", -1 },
- { "AUD.PAK", 0, 0, -1 },
- { 0, 0, 0, 0 }
- },
- Common::FR_FRA,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE,
- Common::GUIO_NOMIDI
- },
- KYRA3_CD_FAN_FLAGS(Common::ES_ESP, Common::EN_ANY)
- },
-
- // Italian fan translation, see fr#2003504 "KYRA: add support for Italian version of Kyrandia 2&3"
- {
- {
- "kyra3",
- 0,
- {
- { "ONETIME.PAK", 0, "ee2d4d056a5de5333a3c6bda055b3cb4", -1 },
- { "AUD.PAK", 0, 0, -1 },
- { 0, 0, 0, 0 }
- },
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE,
- Common::GUIO_NOMIDI
- },
- KYRA3_CD_FAN_FLAGS(Common::IT_ITA, Common::FR_FRA)
- },
- {
- {
- "kyra3",
- 0,
- {
- { "ONETIME.PAK", 0, "ee2d4d056a5de5333a3c6bda055b3cb4", -1 },
- { "AUD.PAK", 0, 0, -1 },
- { 0, 0, 0, 0 }
- },
- Common::DE_DEU,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE,
- Common::GUIO_NOMIDI
- },
- KYRA3_CD_FAN_FLAGS(Common::IT_ITA, Common::FR_FRA)
- },
- {
- {
- "kyra3",
- 0,
- {
- { "ONETIME.PAK", 0, "ee2d4d056a5de5333a3c6bda055b3cb4", -1 },
- { "AUD.PAK", 0, 0, -1 },
- { 0, 0, 0, 0 }
- },
- Common::IT_ITA,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE,
- Common::GUIO_NOMIDI
- },
- KYRA3_CD_FAN_FLAGS(Common::IT_ITA, Common::FR_FRA)
- },
-
-#ifdef ENABLE_LOL
- // Lands of Lore CD
- {
- {
- "lol",
- "CD",
- {
- { "GENERAL.PAK", 0, "05a4f588fb81dc9c0ef1f2ec20d89e24", -1 },
- { "L01.PAK", 0, "759a0ac26808d77ea968bd392355ba1d", -1 },
- { 0, 0, 0, 0 }
- },
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE | ADGF_CD,
- Common::GUIO_NONE
- },
- LOL_CD_FLAGS
- },
-
- {
- {
- "lol",
- "CD",
- {
- { "GENERAL.PAK", 0, "05a4f588fb81dc9c0ef1f2ec20d89e24", -1 },
- { "L01.PAK", 0, "759a0ac26808d77ea968bd392355ba1d", -1 },
- { 0, 0, 0, 0 }
- },
- Common::DE_DEU,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE | ADGF_CD,
- Common::GUIO_NONE
- },
- LOL_CD_FLAGS
- },
-
- {
- {
- "lol",
- "CD",
- {
- { "GENERAL.PAK", 0, "05a4f588fb81dc9c0ef1f2ec20d89e24", -1 },
- { "L01.PAK", 0, "759a0ac26808d77ea968bd392355ba1d", -1 },
- { 0, 0, 0, 0 }
- },
- Common::FR_FRA,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE | ADGF_CD,
- Common::GUIO_NONE
- },
- LOL_CD_FLAGS
- },
-
- {
- {
- "lol",
- "CD",
- {
- { "GENERAL.PAK", 0, "9e4bab499b7ea9337b91ac29fcba6d13", -1 },
- { "L01.PAK", 0, "759a0ac26808d77ea968bd392355ba1d", -1 },
- { 0, 0, 0, 0 }
- },
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE | ADGF_CD,
- Common::GUIO_NONE
- },
- LOL_CD_FLAGS
- },
-
- {
- {
- "lol",
- "CD",
- {
- { "GENERAL.PAK", 0, "9e4bab499b7ea9337b91ac29fcba6d13", -1 },
- { "L01.PAK", 0, "759a0ac26808d77ea968bd392355ba1d", -1 },
- { 0, 0, 0, 0 }
- },
- Common::DE_DEU,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE | ADGF_CD,
- Common::GUIO_NONE
- },
- LOL_CD_FLAGS
- },
-
- {
- {
- "lol",
- "CD",
- {
- { "GENERAL.PAK", 0, "9e4bab499b7ea9337b91ac29fcba6d13", -1 },
- { "L01.PAK", 0, "759a0ac26808d77ea968bd392355ba1d", -1 },
- { 0, 0, 0, 0 }
- },
- Common::FR_FRA,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE | ADGF_CD,
- Common::GUIO_NONE
- },
- LOL_CD_FLAGS
- },
-
- {
- {
- "lol",
- 0,
- {
- { "WESTWOOD.1", 0, "c656aa9a2b4032d341e3dc8e3525b917", -1 },
- { 0, 0, 0, 0 }
- },
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- LOL_FLOPPY_CMP_FLAGS
- },
-
- {
- {
- "lol",
- 0,
- {
- { "WESTWOOD.1", 0, "3c61cb7de5b2ec452f5851f5075207ee", -1 },
- { 0, 0, 0, 0 }
- },
- Common::DE_DEU,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- LOL_FLOPPY_CMP_FLAGS
- },
-
- {
- {
- "lol",
- "Extracted",
- {
- { "GENERAL.PAK", 0, "2aaa30e120c08af87196820e9dd4bf73", -1 },
- { "CHAPTER7.PAK", 0, "eb92bf7ebb4e890add1233a6b0c810ff", -1 },
- { 0, 0, 0, 0 }
- },
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- LOL_FLOPPY_FLAGS
- },
-
- {
- {
- "lol",
- "Extracted",
- {
- { "GENERAL.PAK", 0, "996e66e81054d36249907a1d8158da3d", -1 },
- { "CHAPTER7.PAK", 0, "cabee57f00d6d84b65a732b6868a4959", -1 },
- { 0, 0, 0, 0 }
- },
- Common::DE_DEU,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- LOL_FLOPPY_FLAGS
- },
-
- {
- {
- "lol",
- 0,
- {
- { "GENERAL.PAK", 0, "3fe6539b9b09084c0984eaf7170464e9", -1 },
- { "MUS.PAK", 0, "008dc69d8cbcdb6bae30e270fab26e76", -1 },
- { 0, 0, 0, 0 }
- },
- Common::JA_JPN,
- Common::kPlatformPC98,
- ADGF_NO_FLAGS,
- Common::GUIO_NOSPEECH
- },
- LOL_PC98_SJIS_FLAGS
- },
-
- {
- {
- "lol",
- "Demo",
- {
- { "INTRO.PAK", 0, "4bc22a3b57f19a49212c5de58ab014d6", -1 },
- { "INTROVOC.PAK", 0, "7e578e4f1da31c1f294e14a8e8f3cc44", -1 },
- { 0, 0, 0, 0 }
- },
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_DEMO,
- Common::GUIO_NONE
- },
- LOL_DEMO_FLAGS
- },
-
- {
- {
- "lol",
- "Demo",
- {
- { "GENERAL.PAK", 0, "e94863d86c4597a2d581d05481c152ba", -1 },
- { 0, 0, 0, 0 }
- },
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_DEMO,
- Common::GUIO_NOSPEECH
- },
- LOL_KYRA2_DEMO_FLAGS
- },
-#endif // ENABLE_LOL
-
- { AD_TABLE_END_MARKER, FLAGS(0, 0, 0, 0, 0, 0, 0, 0) }
-};
-
-const PlainGameDescriptor gameList[] = {
- { "kyra1", "The Legend of Kyrandia" },
- { "kyra2", "The Legend of Kyrandia: The Hand of Fate" },
- { "kyra3", "The Legend of Kyrandia: Malcolm's Revenge" },
-#ifdef ENABLE_LOL
- { "lol", "Lands of Lore: The Throne of Chaos" },
-#endif // ENABLE_LOL
- { 0, 0 }
-};
+#include "kyra/detection_tables.h"
const ADParams detectionParams = {
// Pointer to ADGameDescription or its superset structure
@@ -1226,11 +61,13 @@ const ADParams detectionParams = {
// Flags
0,
// Additional GUI options (for every game}
- Common::GUIO_NONE
+ Common::GUIO_NONE,
+ // Maximum directory depth
+ 1,
+ // List of directory globs
+ 0
};
-} // End of anonymous namespace
-
class KyraMetaEngine : public AdvancedMetaEngine {
public:
KyraMetaEngine() : AdvancedMetaEngine(detectionParams) {}
diff --git a/engines/kyra/detection_tables.h b/engines/kyra/detection_tables.h
new file mode 100644
index 0000000000..390281c356
--- /dev/null
+++ b/engines/kyra/detection_tables.h
@@ -0,0 +1,1192 @@
+/* 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$
+ */
+
+namespace {
+
+#define FLAGS(x, y, z, a, b, c, d, id) { Common::UNK_LANG, Common::UNK_LANG, Common::UNK_LANG, Common::kPlatformUnknown, x, y, z, a, b, c, d, id }
+#define FLAGS_FAN(fanLang, repLang, x, y, z, a, b, c, d, id) { Common::UNK_LANG, fanLang, repLang, Common::kPlatformUnknown, x, y, z, a, b, c, d, id }
+
+#define KYRA1_FLOPPY_FLAGS FLAGS(false, false, false, false, false, false, false, Kyra::GI_KYRA1)
+#define KYRA1_FLOPPY_CMP_FLAGS FLAGS(false, false, false, false, false, false, true, Kyra::GI_KYRA1)
+#define KYRA1_AMIGA_FLAGS FLAGS(false, false, false, false, false, false, false, Kyra::GI_KYRA1)
+#define KYRA1_TOWNS_FLAGS FLAGS(false, true, false, false, false, false, false, Kyra::GI_KYRA1)
+#define KYRA1_TOWNS_SJIS_FLAGS FLAGS(false, true, false, true, false, false, false, Kyra::GI_KYRA1)
+#define KYRA1_CD_FLAGS FLAGS(false, true, true, false, false, false, false, Kyra::GI_KYRA1)
+#define KYRA1_DEMO_FLAGS FLAGS(true, false, false, false, false, false, false, Kyra::GI_KYRA1)
+#define KYRA1_DEMO_CD_FLAGS FLAGS(true, true, true, false, false, false, false, Kyra::GI_KYRA1)
+
+#define KYRA2_FLOPPY_FLAGS FLAGS(false, false, false, false, false, false, false, Kyra::GI_KYRA2)
+#define KYRA2_FLOPPY_CMP_FLAGS FLAGS(false, false, false, false, false, false, true, Kyra::GI_KYRA2)
+#define KYRA2_CD_FLAGS FLAGS(false, false, true, false, false, false, false, Kyra::GI_KYRA2)
+#define KYRA2_CD_FAN_FLAGS(x, y) FLAGS_FAN(x, y, false, false, true, false, false, false, false, Kyra::GI_KYRA2)
+#define KYRA2_CD_DEMO_FLAGS FLAGS(true, false, true, false, false, false, false, Kyra::GI_KYRA2)
+#define KYRA2_DEMO_FLAGS FLAGS(true, false, false, false, false, false, false, Kyra::GI_KYRA2)
+#define KYRA2_TOWNS_FLAGS FLAGS(false, false, false, false, false, false, false, Kyra::GI_KYRA2)
+#define KYRA2_TOWNS_SJIS_FLAGS FLAGS(false, false, false, true, false, false, false, Kyra::GI_KYRA2)
+
+#define KYRA3_CD_FLAGS FLAGS(false, false, true, false, false, true, true, Kyra::GI_KYRA3)
+#define KYRA3_CD_INS_FLAGS FLAGS(false, false, true, false, false, true, false, Kyra::GI_KYRA3)
+#define KYRA3_CD_FAN_FLAGS(x, y) FLAGS_FAN(x, y, false, false, true, false, false, true, false, Kyra::GI_KYRA3)
+
+#define LOL_CD_FLAGS FLAGS(false, false, true, false, false, false, false, Kyra::GI_LOL)
+#define LOL_FLOPPY_FLAGS FLAGS(false, false, false, false, false, false, false, Kyra::GI_LOL)
+#define LOL_FLOPPY_CMP_FLAGS FLAGS(false, false, false, false, false, false, true, Kyra::GI_LOL)
+#define LOL_PC98_SJIS_FLAGS FLAGS(false, false, false, true, true, false, false, Kyra::GI_LOL)
+#define LOL_DEMO_FLAGS FLAGS(true, true, false, false, false, false, false, Kyra::GI_LOL)
+#define LOL_KYRA2_DEMO_FLAGS FLAGS(true, false, false, false, false, false, false, Kyra::GI_KYRA2)
+
+const KYRAGameDescription adGameDescs[] = {
+ /* disable these targets until they get supported
+ {
+ {
+ "kyra1",
+ 0,
+ AD_ENTRY1("DISK1.EXE", "c8641d0414d6c966d0a3dad79db07bf4"),
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIPCSPK
+ },
+ KYRA1_FLOPPY_CMP_FLAGS
+ },
+
+ {
+ {
+ "kyra1",
+ 0,
+ AD_ENTRY1("DISK1.EXE", "5d5cee4c3d0b68d586788b74243d254a"),
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIPCSPK
+ },
+ KYRA1_FLOPPY_CMP_FLAGS
+ },
+ */
+
+ {
+ {
+ "kyra1",
+ "Extracted",
+ AD_ENTRY1("GEMCUT.EMC", "3c244298395520bb62b5edfe41688879"),
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIPCSPK
+ },
+ KYRA1_FLOPPY_FLAGS
+ },
+ {
+ {
+ "kyra1",
+ "Extracted",
+ AD_ENTRY1("GEMCUT.EMC", "796e44863dd22fa635b042df1bf16673"),
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIPCSPK
+ },
+ KYRA1_FLOPPY_FLAGS
+ },
+ {
+ {
+ "kyra1",
+ "Extracted",
+ AD_ENTRY1("GEMCUT.EMC", "abf8eb360e79a6c2a837751fbd4d3d24"),
+ Common::FR_FRA,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIPCSPK
+ },
+ KYRA1_FLOPPY_FLAGS
+ },
+ {
+ {
+ "kyra1",
+ "Extracted",
+ AD_ENTRY1("GEMCUT.EMC", "6018e1dfeaca7fe83f8d0b00eb0dd049"),
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIPCSPK
+ },
+ KYRA1_FLOPPY_FLAGS
+ },
+ { // from Arne.F
+ {
+ "kyra1",
+ "Extracted",
+ AD_ENTRY1("GEMCUT.EMC", "f0b276781f47c130f423ec9679fe9ed9"),
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIPCSPK
+ },
+ KYRA1_FLOPPY_FLAGS
+ },
+ { // from VooD
+ {
+ "kyra1",
+ "Extracted",
+ AD_ENTRY1("GEMCUT.EMC", "8909b41596913b3f5deaf3c9f1017b01"),
+ Common::ES_ESP,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIPCSPK
+ },
+ KYRA1_FLOPPY_FLAGS
+ },
+ { // floppy 1.8 from clemmy
+ {
+ "kyra1",
+ "Extracted",
+ AD_ENTRY1("GEMCUT.EMC", "747861d2a9c643c59fdab570df5b9093"),
+ Common::ES_ESP,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIPCSPK
+ },
+ KYRA1_FLOPPY_FLAGS
+ },
+ { // from gourry
+ {
+ "kyra1",
+ "Extracted",
+ AD_ENTRY1("GEMCUT.EMC", "ef08c8c237ee1473fd52578303fc36df"),
+ Common::IT_ITA,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIPCSPK
+ },
+ KYRA1_FLOPPY_FLAGS
+ },
+
+ {
+ {
+ "kyra1",
+ 0,
+ {
+ { "GEMCUT.PAK", 0, "2bd1da653eaefd691e050e4a9eb68a64", -1 },
+ { "GEMCUT.EMC", 0, "2a3f44e179f1e9f7643e90083c747571", -1 },
+ { NULL, 0, NULL, 0 }
+ },
+ Common::EN_ANY,
+ Common::kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH
+ },
+ KYRA1_AMIGA_FLAGS
+ },
+
+ {
+ {
+ "kyra1",
+ 0,
+ {
+ { "GEMCUT.PAK", 0, "2bd1da653eaefd691e050e4a9eb68a64", -1 },
+ { "GEMCUT.EMC", 0, "74f99e9ed99abf8d0429826d78485a2a", -1 },
+ { NULL, 0, NULL, 0 }
+ },
+ Common::DE_DEU,
+ Common::kPlatformAmiga,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH
+ },
+ KYRA1_AMIGA_FLAGS
+ },
+
+ {
+ {
+ "kyra1",
+ 0,
+ {
+ { "GEMCUT.EMC", 0, "796e44863dd22fa635b042df1bf16673", -1 },
+ { "BEAD.CPS", 0, "3038466f65b7751451844707187aa401", -1 },
+ { NULL, 0, NULL, 0 }
+ },
+ Common::EN_ANY,
+ Common::kPlatformMacintosh,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH
+ },
+ KYRA1_FLOPPY_FLAGS
+ },
+
+ { // FM-TOWNS version
+ {
+ "kyra1",
+ 0,
+ {
+ { "EMC.PAK", 0, "a046bb0b422061aab8e4c4689400343a", -1 },
+ { "TWMUSIC.PAK", 0, "e53bca3a3e3fb49107d59463ec387a59", -1 },
+ { NULL, 0, NULL, 0 }
+ },
+ Common::EN_ANY,
+ Common::kPlatformFMTowns,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDITOWNS
+ },
+ KYRA1_TOWNS_FLAGS
+ },
+ {
+ {
+ "kyra1",
+ 0,
+ {
+ { "JMC.PAK", 0, "9c5707a2a478e8167e44283246612d2c", -1 },
+ { "TWMUSIC.PAK", 0, "e53bca3a3e3fb49107d59463ec387a59", -1 },
+ { NULL, 0, NULL, 0 }
+ },
+ Common::JA_JPN,
+ Common::kPlatformFMTowns,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDITOWNS
+ },
+ KYRA1_TOWNS_SJIS_FLAGS
+ },
+
+ // PC-9801 floppy + CD / PC-9821 floppy version are all using the same data files,
+ // thus we will mark it as non CD game.
+ {
+ {
+ "kyra1",
+ "",
+ {
+ { "JMC.PAK", 0, "9c5707a2a478e8167e44283246612d2c", -1 },
+ { "MUSIC98.PAK", 0, "02fc212f799331b769b274e33d87b37f", -1 },
+ { NULL, 0, NULL, 0 }
+ },
+ Common::JA_JPN,
+ Common::kPlatformPC98,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIPC98
+ },
+ KYRA1_TOWNS_SJIS_FLAGS
+ },
+
+ {
+ {
+ "kyra1",
+ "CD",
+ AD_ENTRY1("GEMCUT.PAK", "fac399fe62f98671e56a005c5e94e39f"),
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_CD,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIPCSPK
+ },
+ KYRA1_CD_FLAGS
+ },
+ {
+ {
+ "kyra1",
+ "CD",
+ AD_ENTRY1("GEMCUT.PAK", "230f54e6afc007ab4117159181a1c722"),
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ ADGF_CD,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIPCSPK
+ },
+ KYRA1_CD_FLAGS
+ },
+ {
+ {
+ "kyra1",
+ "CD",
+ AD_ENTRY1("GEMCUT.PAK", "b037c41768b652a040360ffa3556fd2a"),
+ Common::FR_FRA,
+ Common::kPlatformPC,
+ ADGF_CD,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIPCSPK
+ },
+ KYRA1_CD_FLAGS
+ },
+
+ { // italian fan translation see fr#1727941 "KYRA: add Italian CD Version to kyra.dat"
+ {
+ "kyra1",
+ "CD",
+ AD_ENTRY1("GEMCUT.PAK", "d8327fc4b7a72b23c900fa13aef4093a"),
+ Common::IT_ITA,
+ Common::kPlatformPC,
+ ADGF_CD,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIPCSPK
+ },
+ KYRA1_CD_FLAGS
+ },
+
+ { // Kyra 1 Mac CD as mentioned in fr #2766454 "KYRA1: Add support for Macintosh CD" by nnooiissee
+ {
+ "kyra1",
+ "CD",
+ {
+ { "GEMCUT.PAK", 0, "d3d4b281cd357230aabcec46843d04bd", -1 },
+ { "BEAD.CPS", 0, "3038466f65b7751451844707187aa401", -1 },
+ { NULL, 0, NULL, 0 }
+ },
+ Common::EN_ANY,
+ Common::kPlatformMacintosh,
+ ADGF_CD,
+ Common::GUIO_NONE
+ },
+ KYRA1_CD_FLAGS
+ },
+ {
+ {
+ "kyra1",
+ "CD",
+ {
+ { "GEMCUT.PAK", 0, "4a0cb720e824295bcbccbd1407652110", -1 },
+ { "BEAD.CPS", 0, "3038466f65b7751451844707187aa401", -1 },
+ { NULL, 0, NULL, 0 }
+ },
+ Common::DE_DEU,
+ Common::kPlatformMacintosh,
+ ADGF_CD,
+ Common::GUIO_NONE
+ },
+ KYRA1_CD_FLAGS
+ },
+ {
+ {
+ "kyra1",
+ "CD",
+ {
+ { "GEMCUT.PAK", 0, "b71ee090aa12e80ed2ba068826d92bed", -1 },
+ { "BEAD.CPS", 0, "3038466f65b7751451844707187aa401", -1 },
+ { NULL, 0, NULL, 0 }
+ },
+ Common::FR_FRA,
+ Common::kPlatformMacintosh,
+ ADGF_CD,
+ Common::GUIO_NONE
+ },
+ KYRA1_CD_FLAGS
+ },
+
+ {
+ {
+ "kyra1",
+ "Demo",
+ AD_ENTRY1("DEMO1.WSA", "fb722947d94897512b13b50cc84fd648"),
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_DEMO,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIPCSPK
+ },
+ KYRA1_DEMO_FLAGS
+ },
+
+ { // Special Kyrandia 1 CD demo
+ {
+ "kyra1",
+ "Demo/CD",
+ AD_ENTRY1("INTRO.VRM", "e3045fb69b8c29db84b8fda3ccbdac54"),
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_DEMO | ADGF_CD,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIPCSPK
+ },
+ KYRA1_DEMO_CD_FLAGS
+ },
+
+ { // Floppy version
+ {
+ "kyra2",
+ 0,
+ AD_ENTRY1("WESTWOOD.001", "3f52dda68c4f7696c8309038be9f4151"),
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ KYRA2_FLOPPY_CMP_FLAGS
+ },
+
+ { // Floppy version
+ {
+ "kyra2",
+ 0,
+ AD_ENTRY1("WESTWOOD.001", "d787b9559afddfe058b84c0b3a787224"),
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ KYRA2_FLOPPY_CMP_FLAGS
+ },
+
+ { // Floppy version extracted
+ {
+ "kyra2",
+ "Extracted",
+ AD_ENTRY1("FATE.PAK", "1ba18be685ad8e5a0ab5d46a0ce4d345"),
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ KYRA2_FLOPPY_FLAGS
+ },
+
+ { // Floppy version extracted
+ {
+ "kyra2",
+ "Extracted",
+ AD_ENTRY1("FATE.PAK", "262fb69dd8e52e596c7aefc6456f7c1b"),
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ KYRA2_FLOPPY_FLAGS
+ },
+
+ { // Floppy version extracted
+ {
+ "kyra2",
+ "Extracted",
+ AD_ENTRY1("FATE.PAK", "f7de11506b4c8fdf64bc763206c3e4e7"),
+ Common::FR_FRA,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ KYRA2_FLOPPY_FLAGS
+ },
+
+ { // Floppy version extracted
+ {
+ "kyra2",
+ "Extracted",
+ AD_ENTRY1("FATE.PAK", "e0a70c31b022cb4bb3061890020fc27c"),
+ Common::IT_ITA,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ KYRA2_FLOPPY_FLAGS
+ },
+
+ { // CD version
+ {
+ "kyra2",
+ "CD",
+ AD_ENTRY1("FATE.PAK", "28cbad1c5bf06b2d3825ae57d760d032"),
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE | ADGF_CD,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ KYRA2_CD_FLAGS
+ },
+ {
+ {
+ "kyra2",
+ "CD",
+ AD_ENTRY1("FATE.PAK", "28cbad1c5bf06b2d3825ae57d760d032"),
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE | ADGF_CD,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ KYRA2_CD_FLAGS
+ },
+ {
+ {
+ "kyra2",
+ "CD",
+ AD_ENTRY1("FATE.PAK", "28cbad1c5bf06b2d3825ae57d760d032"),
+ Common::FR_FRA,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE | ADGF_CD,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ KYRA2_CD_FLAGS
+ },
+
+ // Italian fan translation, see fr#2003504 "KYRA: add support for Italian version of Kyrandia 2&3"
+ { // CD version
+ {
+ "kyra2",
+ "CD",
+ AD_ENTRY1("FATE.PAK", "30487f3b8d7790c7857f4769ff2dd125"),
+ Common::IT_ITA,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE | ADGF_CD,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ KYRA2_CD_FAN_FLAGS(Common::IT_ITA, Common::EN_ANY)
+ },
+ {
+ {
+ "kyra2",
+ "CD",
+ AD_ENTRY1("FATE.PAK", "30487f3b8d7790c7857f4769ff2dd125"),
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE | ADGF_CD,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ KYRA2_CD_FAN_FLAGS(Common::IT_ITA, Common::EN_ANY)
+ },
+ {
+ {
+ "kyra2",
+ "CD",
+ AD_ENTRY1("FATE.PAK", "30487f3b8d7790c7857f4769ff2dd125"),
+ Common::FR_FRA,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE | ADGF_CD,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ KYRA2_CD_FAN_FLAGS(Common::IT_ITA, Common::EN_ANY)
+ },
+
+ {
+ {
+ "kyra2",
+ "CD",
+ AD_ENTRY1("FATE.PAK", "39772ff82e42c4c520050518deb82e64"),
+ Common::IT_ITA,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE | ADGF_CD,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ KYRA2_CD_FAN_FLAGS(Common::IT_ITA, Common::EN_ANY)
+ },
+
+ {
+ {
+ "kyra2",
+ "CD",
+ AD_ENTRY1("FATE.PAK", "39772ff82e42c4c520050518deb82e64"),
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE | ADGF_CD,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ KYRA2_CD_FAN_FLAGS(Common::IT_ITA, Common::EN_ANY)
+ },
+
+ {
+ {
+ "kyra2",
+ "CD",
+ AD_ENTRY1("FATE.PAK", "39772ff82e42c4c520050518deb82e64"),
+ Common::FR_FRA,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE | ADGF_CD,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ KYRA2_CD_FAN_FLAGS(Common::IT_ITA, Common::EN_ANY)
+ },
+
+ { // Interactive Demo
+ {
+ "kyra2",
+ "CD/Demo",
+ AD_ENTRY1("THANKS.CPS", "b1a78d990b120bb2234b7094f74e30a5"),
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE | ADGF_CD | ADGF_DEMO,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ KYRA2_CD_DEMO_FLAGS
+ },
+
+ { // Interactive Demo
+ {
+ "kyra2",
+ "CD/Demo",
+ AD_ENTRY1("THANKS.CPS", "b1a78d990b120bb2234b7094f74e30a5"),
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE | ADGF_CD | ADGF_DEMO,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ KYRA2_CD_DEMO_FLAGS
+ },
+
+ { // Interactive Demo
+ {
+ "kyra2",
+ "CD/Demo",
+ AD_ENTRY1("THANKS.CPS", "b1a78d990b120bb2234b7094f74e30a5"),
+ Common::FR_FRA,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE | ADGF_CD | ADGF_DEMO,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ KYRA2_CD_DEMO_FLAGS
+ },
+
+ { // Non-Interactive Demos
+ {
+ "kyra2",
+ "Demo",
+ AD_ENTRY1("VOC.PAK", "ecb3561b63749158172bf21528cf5f45"),
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_DEMO,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ KYRA2_DEMO_FLAGS
+ },
+
+ { // FM-TOWNS
+ {
+ "kyra2",
+ 0,
+ AD_ENTRY1("WSCORE.PAK", "c44de1302b67f27d4707409987b7a685"),
+ Common::EN_ANY,
+ Common::kPlatformFMTowns,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDITOWNS
+ },
+ KYRA2_TOWNS_FLAGS
+ },
+ {
+ {
+ "kyra2",
+ 0,
+ AD_ENTRY1("WSCORE.PAK", "c44de1302b67f27d4707409987b7a685"),
+ Common::JA_JPN,
+ Common::kPlatformFMTowns,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDITOWNS
+ },
+ KYRA2_TOWNS_SJIS_FLAGS
+ },
+ { // PC-9821
+ {
+ "kyra2",
+ "CD",
+ AD_ENTRY1("WSCORE.PAK", "c44de1302b67f27d4707409987b7a685"),
+ Common::EN_ANY,
+ Common::kPlatformPC98,
+ ADGF_CD,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIPC98
+ },
+ KYRA2_TOWNS_FLAGS
+ },
+ {
+ {
+ "kyra2",
+ "CD",
+ AD_ENTRY1("WSCORE.PAK", "c44de1302b67f27d4707409987b7a685"),
+ Common::JA_JPN,
+ Common::kPlatformPC98,
+ ADGF_CD,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIPC98
+ },
+ KYRA2_TOWNS_SJIS_FLAGS
+ },
+
+ // Kyra3
+
+ // non installed version
+ {
+ {
+ "kyra3",
+ 0,
+ {
+ { "ONETIME.PAK", 0, "3833ff312757b8e6147f464cca0a6587", -1 },
+ { "WESTWOOD.001", 0, 0, -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE,
+ Common::GUIO_NOMIDI
+ },
+ KYRA3_CD_FLAGS
+ },
+ {
+ {
+ "kyra3",
+ 0,
+ {
+ { "ONETIME.PAK", 0, "3833ff312757b8e6147f464cca0a6587", -1 },
+ { "WESTWOOD.001", 0, 0, -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE,
+ Common::GUIO_NOMIDI
+ },
+ KYRA3_CD_FLAGS
+ },
+ {
+ {
+ "kyra3",
+ 0,
+ {
+ { "ONETIME.PAK", 0, "3833ff312757b8e6147f464cca0a6587", -1 },
+ { "WESTWOOD.001", 0, 0, -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::FR_FRA,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE,
+ Common::GUIO_NOMIDI
+ },
+ KYRA3_CD_FLAGS
+ },
+
+ // installed version
+ {
+ {
+ "kyra3",
+ 0,
+ {
+ { "ONETIME.PAK", 0, "3833ff312757b8e6147f464cca0a6587", -1 },
+ { "AUD.PAK", 0, 0, -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE,
+ Common::GUIO_NOMIDI
+ },
+ KYRA3_CD_INS_FLAGS
+ },
+ {
+ {
+ "kyra3",
+ 0,
+ {
+ { "ONETIME.PAK", 0, "3833ff312757b8e6147f464cca0a6587", -1 },
+ { "AUD.PAK", 0, 0, -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE,
+ Common::GUIO_NOMIDI
+ },
+ KYRA3_CD_INS_FLAGS
+ },
+ {
+ {
+ "kyra3",
+ 0,
+ {
+ { "ONETIME.PAK", 0, "3833ff312757b8e6147f464cca0a6587", -1 },
+ { "AUD.PAK", 0, 0, -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::FR_FRA,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE,
+ Common::GUIO_NOMIDI
+ },
+ KYRA3_CD_INS_FLAGS
+ },
+
+ // Mac version
+ {
+ {
+ "kyra3",
+ 0,
+ {
+ { "ONETIME.PAK", 0, "3833ff312757b8e6147f464cca0a6587", -1 },
+ { "AUD.PAK", 0, 0, -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::EN_ANY,
+ Common::kPlatformMacintosh,
+ ADGF_DROPLANGUAGE,
+ Common::GUIO_NOMIDI
+ },
+ KYRA3_CD_INS_FLAGS
+ },
+ {
+ {
+ "kyra3",
+ 0,
+ {
+ { "ONETIME.PAK", 0, "3833ff312757b8e6147f464cca0a6587", -1 },
+ { "AUD.PAK", 0, 0, -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::DE_DEU,
+ Common::kPlatformMacintosh,
+ ADGF_DROPLANGUAGE,
+ Common::GUIO_NOMIDI
+ },
+ KYRA3_CD_INS_FLAGS
+ },
+ {
+ {
+ "kyra3",
+ 0,
+ {
+ { "ONETIME.PAK", 0, "3833ff312757b8e6147f464cca0a6587", -1 },
+ { "AUD.PAK", 0, 0, -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::FR_FRA,
+ Common::kPlatformMacintosh,
+ ADGF_DROPLANGUAGE,
+ Common::GUIO_NOMIDI
+ },
+ KYRA3_CD_INS_FLAGS
+ },
+
+ // Spanish fan translation, see fr#1994040 "KYRA3: Add support for Spanish fan translation"
+ {
+ {
+ "kyra3",
+ 0,
+ {
+ { "ONETIME.PAK", 0, "9aaca21d2a205ca02ec53132f2911794", -1 },
+ { "AUD.PAK", 0, 0, -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::ES_ESP,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE,
+ Common::GUIO_NOMIDI
+ },
+ KYRA3_CD_FAN_FLAGS(Common::ES_ESP, Common::EN_ANY)
+ },
+ {
+ {
+ "kyra3",
+ 0,
+ {
+ { "ONETIME.PAK", 0, "9aaca21d2a205ca02ec53132f2911794", -1 },
+ { "AUD.PAK", 0, 0, -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE,
+ Common::GUIO_NOMIDI
+ },
+ KYRA3_CD_FAN_FLAGS(Common::ES_ESP, Common::EN_ANY)
+ },
+ {
+ {
+ "kyra3",
+ 0,
+ {
+ { "ONETIME.PAK", 0, "9aaca21d2a205ca02ec53132f2911794", -1 },
+ { "AUD.PAK", 0, 0, -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::FR_FRA,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE,
+ Common::GUIO_NOMIDI
+ },
+ KYRA3_CD_FAN_FLAGS(Common::ES_ESP, Common::EN_ANY)
+ },
+
+ // Italian fan translation, see fr#2003504 "KYRA: add support for Italian version of Kyrandia 2&3"
+ {
+ {
+ "kyra3",
+ 0,
+ {
+ { "ONETIME.PAK", 0, "ee2d4d056a5de5333a3c6bda055b3cb4", -1 },
+ { "AUD.PAK", 0, 0, -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE,
+ Common::GUIO_NOMIDI
+ },
+ KYRA3_CD_FAN_FLAGS(Common::IT_ITA, Common::FR_FRA)
+ },
+ {
+ {
+ "kyra3",
+ 0,
+ {
+ { "ONETIME.PAK", 0, "ee2d4d056a5de5333a3c6bda055b3cb4", -1 },
+ { "AUD.PAK", 0, 0, -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE,
+ Common::GUIO_NOMIDI
+ },
+ KYRA3_CD_FAN_FLAGS(Common::IT_ITA, Common::FR_FRA)
+ },
+ {
+ {
+ "kyra3",
+ 0,
+ {
+ { "ONETIME.PAK", 0, "ee2d4d056a5de5333a3c6bda055b3cb4", -1 },
+ { "AUD.PAK", 0, 0, -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::IT_ITA,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE,
+ Common::GUIO_NOMIDI
+ },
+ KYRA3_CD_FAN_FLAGS(Common::IT_ITA, Common::FR_FRA)
+ },
+
+#ifdef ENABLE_LOL
+ // Lands of Lore CD
+ {
+ {
+ "lol",
+ "CD",
+ {
+ { "GENERAL.PAK", 0, "05a4f588fb81dc9c0ef1f2ec20d89e24", -1 },
+ { "L01.PAK", 0, "759a0ac26808d77ea968bd392355ba1d", -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE | ADGF_CD,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ LOL_CD_FLAGS
+ },
+
+ {
+ {
+ "lol",
+ "CD",
+ {
+ { "GENERAL.PAK", 0, "05a4f588fb81dc9c0ef1f2ec20d89e24", -1 },
+ { "L01.PAK", 0, "759a0ac26808d77ea968bd392355ba1d", -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE | ADGF_CD,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ LOL_CD_FLAGS
+ },
+
+ {
+ {
+ "lol",
+ "CD",
+ {
+ { "GENERAL.PAK", 0, "05a4f588fb81dc9c0ef1f2ec20d89e24", -1 },
+ { "L01.PAK", 0, "759a0ac26808d77ea968bd392355ba1d", -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::FR_FRA,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE | ADGF_CD,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ LOL_CD_FLAGS
+ },
+
+ {
+ {
+ "lol",
+ "CD",
+ {
+ { "GENERAL.PAK", 0, "9e4bab499b7ea9337b91ac29fcba6d13", -1 },
+ { "L01.PAK", 0, "759a0ac26808d77ea968bd392355ba1d", -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE | ADGF_CD,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ LOL_CD_FLAGS
+ },
+
+ {
+ {
+ "lol",
+ "CD",
+ {
+ { "GENERAL.PAK", 0, "9e4bab499b7ea9337b91ac29fcba6d13", -1 },
+ { "L01.PAK", 0, "759a0ac26808d77ea968bd392355ba1d", -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE | ADGF_CD,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ LOL_CD_FLAGS
+ },
+
+ {
+ {
+ "lol",
+ "CD",
+ {
+ { "GENERAL.PAK", 0, "9e4bab499b7ea9337b91ac29fcba6d13", -1 },
+ { "L01.PAK", 0, "759a0ac26808d77ea968bd392355ba1d", -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::FR_FRA,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE | ADGF_CD,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ LOL_CD_FLAGS
+ },
+
+ {
+ {
+ "lol",
+ 0,
+ {
+ { "WESTWOOD.1", 0, "c656aa9a2b4032d341e3dc8e3525b917", -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ LOL_FLOPPY_CMP_FLAGS
+ },
+
+ {
+ {
+ "lol",
+ 0,
+ {
+ { "WESTWOOD.1", 0, "3c61cb7de5b2ec452f5851f5075207ee", -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ LOL_FLOPPY_CMP_FLAGS
+ },
+
+ {
+ {
+ "lol",
+ "Extracted",
+ {
+ { "GENERAL.PAK", 0, "2aaa30e120c08af87196820e9dd4bf73", -1 },
+ { "CHAPTER7.PAK", 0, "eb92bf7ebb4e890add1233a6b0c810ff", -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ LOL_FLOPPY_FLAGS
+ },
+
+ {
+ {
+ "lol",
+ "Extracted",
+ {
+ { "GENERAL.PAK", 0, "996e66e81054d36249907a1d8158da3d", -1 },
+ { "CHAPTER7.PAK", 0, "cabee57f00d6d84b65a732b6868a4959", -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ LOL_FLOPPY_FLAGS
+ },
+
+ {
+ {
+ "lol",
+ 0,
+ {
+ { "GENERAL.PAK", 0, "3fe6539b9b09084c0984eaf7170464e9", -1 },
+ { "MUS.PAK", 0, "008dc69d8cbcdb6bae30e270fab26e76", -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::JA_JPN,
+ Common::kPlatformPC98,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NOSPEECH | Common::GUIO_MIDIPC98
+ },
+ LOL_PC98_SJIS_FLAGS
+ },
+
+ {
+ {
+ "lol",
+ "Demo",
+ {
+ { "INTRO.PAK", 0, "4bc22a3b57f19a49212c5de58ab014d6", -1 },
+ { "INTROVOC.PAK", 0, "7e578e4f1da31c1f294e14a8e8f3cc44", -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_DEMO,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ LOL_DEMO_FLAGS
+ },
+
+ {
+ {
+ "lol",
+ "Demo",
+ {
+ { "GENERAL.PAK", 0, "e94863d86c4597a2d581d05481c152ba", -1 },
+ { 0, 0, 0, 0 }
+ },
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_DEMO,
+ Common::GUIO_MIDIADLIB | Common::GUIO_MIDIMT32 | Common::GUIO_MIDIGM | Common::GUIO_MIDIPCSPK
+ },
+ LOL_KYRA2_DEMO_FLAGS
+ },
+#endif // ENABLE_LOL
+
+ { AD_TABLE_END_MARKER, FLAGS(0, 0, 0, 0, 0, 0, 0, 0) }
+};
+
+const PlainGameDescriptor gameList[] = {
+ { "kyra1", "The Legend of Kyrandia" },
+ { "kyra2", "The Legend of Kyrandia: The Hand of Fate" },
+ { "kyra3", "The Legend of Kyrandia: Malcolm's Revenge" },
+#ifdef ENABLE_LOL
+ { "lol", "Lands of Lore: The Throne of Chaos" },
+#endif // ENABLE_LOL
+ { 0, 0 }
+};
+
+} // End of anonymous namespace
diff --git a/engines/kyra/kyra_v1.cpp b/engines/kyra/kyra_v1.cpp
index 00b32425c2..abe13cec2b 100644
--- a/engines/kyra/kyra_v1.cpp
+++ b/engines/kyra/kyra_v1.cpp
@@ -105,8 +105,11 @@ Common::Error KyraEngine_v1::init() {
_mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, ConfMan.getInt("speech_volume"));
if (!_flags.useDigSound) {
- // We prefer AdLib over MIDI, since generally AdLib is better supported
- MidiDriverType midiDriver = MidiDriver::detectMusicDriver(MDT_PCSPK | MDT_MIDI | MDT_ADLIB);
+ // We prefer AdLib over MIDI in Kyra 1, since it offers MT-32 support only, most users don't have a real
+ // MT-32/LAPC1/CM32L/CM64 device and AdLib sounds better than our incomplete MT-32 emulator and also better than
+ // MT-32/GM mapping. For Kyra 2 and LoL which have real GM tracks which sound better than AdLib tracks we prefer GM
+ // since most users have a GM compatible device.
+ MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_PCSPK | MDT_MIDI | MDT_ADLIB | ((_flags.gameID == GI_KYRA2 || _flags.gameID == GI_LOL) ? MDT_PREFER_GM : 0));
if (_flags.platform == Common::kPlatformFMTowns) {
if (_flags.gameID == GI_KYRA1)
@@ -120,24 +123,24 @@ Common::Error KyraEngine_v1::init() {
_sound = new SoundTownsPC98_v2(this, _mixer);
} else if (_flags.platform == Common::kPlatformAmiga) {
_sound = new SoundAmiga(this, _mixer);
- } else if (midiDriver == MD_ADLIB) {
+ } else if (MidiDriver::getMusicType(dev) == MT_ADLIB) {
_sound = new SoundAdLibPC(this, _mixer);
} else {
Sound::kType type;
- if (midiDriver == MD_PCSPK)
+ if (MidiDriver::getMusicType(dev) == MT_PCSPK)
type = Sound::kPCSpkr;
- else if (midiDriver == MD_MT32 || ConfMan.getBool("native_mt32"))
+ else if (MidiDriver::getMusicType(dev) == MT_MT32 || ConfMan.getBool("native_mt32"))
type = Sound::kMidiMT32;
else
type = Sound::kMidiGM;
MidiDriver *driver = 0;
- if (midiDriver == MD_PCSPK) {
+ if (MidiDriver::getMusicType(dev) == MT_PCSPK) {
driver = new MidiDriver_PCSpeaker(_mixer);
} else {
- driver = MidiDriver::createMidi(midiDriver);
+ driver = MidiDriver::createMidi(dev);
if (type == Sound::kMidiMT32)
driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
}
diff --git a/engines/kyra/sound_intern.h b/engines/kyra/sound_intern.h
index f4aab4db29..a229dc310d 100644
--- a/engines/kyra/sound_intern.h
+++ b/engines/kyra/sound_intern.h
@@ -180,6 +180,8 @@ public:
int32 voicePlay(const char *file, Audio::SoundHandle *handle, uint8 volume, bool isSfx) { return -1; }
void playSoundEffect(uint8);
+ void updateVolumeSettings();
+
protected:
int _lastTrack;
uint8 *_musicTrackData;
@@ -207,6 +209,8 @@ public:
int32 voicePlay(const char *file, Audio::SoundHandle *handle, uint8 volume, bool isSfx);
void playSoundEffect(uint8 track);
+ void updateVolumeSettings();
+
protected:
Audio::AudioStream *_currentSFX;
int _lastTrack;
diff --git a/engines/kyra/sound_towns.cpp b/engines/kyra/sound_towns.cpp
index b2c8c5fb4b..7a570b3100 100644
--- a/engines/kyra/sound_towns.cpp
+++ b/engines/kyra/sound_towns.cpp
@@ -23,7 +23,9 @@
*
*/
+#include "common/config-manager.h"
#include "common/system.h"
+
#include "kyra/resource.h"
#include "kyra/sound_intern.h"
#include "kyra/screen.h"
@@ -1355,7 +1357,7 @@ public:
void setOutputLevel();
virtual void fadeStep();
- void reset();
+ virtual void reset();
const uint8 _idFlag;
@@ -1432,13 +1434,14 @@ public:
void protect();
void restore();
+ virtual void reset();
void fadeStep();
protected:
void setOutputLevel(uint8 lvl);
- bool control_f0_setInstr(uint8 para);
+ bool control_f0_setPatch(uint8 para);
bool control_f1_setTotalLevel(uint8 para);
bool control_f4_setAlgorithm(uint8 para);
bool control_f9_loadCustomPatch(uint8 para);
@@ -1458,6 +1461,7 @@ public:
~TownsPC98_OpnSfxChannel() {}
void loadData(uint8 *data);
+ void reset();
};
class TownsPC98_OpnChannelPCM : public TownsPC98_OpnChannel {
@@ -1490,9 +1494,12 @@ public:
void nextTick(int32 *buffer, uint32 bufferSize);
+ void setVolumeIntern(int volA, int volB) { _volumeA = volA; _volumeB = volB; }
+ void setVolumeChannelMasks(int channelMaskA, int channelMaskB) { _volMaskA = channelMaskA; _volMaskB = channelMaskB; }
+
uint8 chanEnable() const { return _chanEnable; }
private:
- void updatesRegs();
+ void updateRegs();
uint8 _updateRequestBuf[64];
int _updateRequest;
@@ -1529,6 +1536,11 @@ private:
uint8 **_reg;
+ uint16 _volumeA;
+ uint16 _volumeB;
+ int _volMaskA;
+ int _volMaskB;
+
bool _ready;
};
@@ -1543,6 +1555,9 @@ public:
void nextTick(int32 *buffer, uint32 bufferSize);
+ void setVolumeIntern(int volA, int volB) { _volumeA = volA; _volumeB = volB; }
+ void setVolumeChannelMasks(int channelMaskA, int channelMaskB) { _volMaskA = channelMaskA; _volMaskB = channelMaskB; }
+
private:
struct RhtChannel {
const uint8 *data;
@@ -1578,6 +1593,11 @@ private:
uint8 **_reg;
+ uint16 _volumeA;
+ uint16 _volumeB;
+ int _volMaskA;
+ int _volMaskB;
+
bool _ready;
};
@@ -1604,20 +1624,27 @@ public:
int getRate() const { return _mixer->getOutputRate(); }
protected:
- void generateTables();
-
void toggleRegProtection(bool prot) { _regProtectionFlag = prot; }
uint8 readSSGStatus() { return _ssg->chanEnable(); }
virtual void timerCallbackA() = 0;
virtual void timerCallbackB() = 0;
+ // The audio driver can store and apply two different audio settings
+ // (usually for music and sound effects). The channel mask will determine
+ // which channels get effected by the setting. The first bits will be
+ // the normal opn channels, the next bits the ssg channels and the final
+ // bit the rhythm channel.
+ void setVolumeIntern(int volA, int volB);
+ void setVolumeChannelMasks(int channelMaskA, int channelMaskB);
+
const int _numChan;
const int _numSSG;
const bool _hasPercussion;
Common::Mutex _mutex;
private:
+ void generateTables();
void nextTick(int32 *buffer, uint32 bufferSize);
void generateOutput(int32 &leftSample, int32 &rightSample, int32 *del, int32 *feed);
@@ -1670,6 +1697,9 @@ private:
OpnTimer _timers[2];
+ int _volMaskA, _volMaskB;
+ uint16 _volumeA, _volumeB;
+
const float _baserate;
uint32 _timerbase;
@@ -1709,6 +1739,9 @@ public:
bool looping() { return _looping == _updateChannelsFlag ? true : false; }
bool musicPlaying() { return _musicPlaying; }
+ void setMusicVolume(int volume) { _musicVolume = volume; setVolumeIntern(_musicVolume, _sfxVolume); }
+ void setSoundEffectVolume(int volume) { _sfxVolume = volume; setVolumeIntern(_musicVolume, _sfxVolume); }
+
protected:
void startSoundEffect();
@@ -1751,6 +1784,9 @@ protected:
uint8 *_sfxData;
uint16 _sfxOffsets[2];
+ uint16 _musicVolume;
+ uint16 _sfxVolume;
+
static const uint8 _drvTables[];
bool _ready;
@@ -1823,33 +1859,23 @@ void TownsPC98_OpnChannel::loadData(uint8 *data) {
_dataPtr = data;
_totalLevel = 0x7F;
- uint8 *src_b = _dataPtr;
- int loop = 1;
- uint8 cmd = 0;
- while (loop) {
- if (loop == 1) {
- cmd = *src_b++;
- if (cmd < 0xf0) {
- src_b++;
- loop = 1;
- } else {
- if (cmd == 0xff) {
- loop = *src_b ? 2 : 0;
- if (READ_LE_UINT16(src_b))
- _drv->_looping |= _idFlag;
- } else if (cmd == 0xf6) {
- loop = 3;
- } else {
- loop = 2;
- }
- }
- } else if (loop == 2) {
- src_b += _drv->_opnFxCmdLen[cmd - 240];
- loop = 1;
- } else if (loop == 3) {
- src_b[0] = src_b[1];
- src_b += 4;
- loop = 1;
+ uint8 *tmp = _dataPtr;
+ for (bool loop = true; loop; ) {
+ uint8 cmd = *tmp++;
+ if (cmd < 0xf0) {
+ tmp++;
+ } else if (cmd == 0xff) {
+ if (READ_LE_UINT16(tmp)) {
+ _drv->_looping |= _idFlag;
+ tmp += _drv->_opnFxCmdLen[cmd - 240];
+ } else
+ loop = false;
+ } else if (cmd == 0xf6) {
+ // reset repeat section countdown
+ tmp[0] = tmp[1];
+ tmp += 4;
+ } else {
+ tmp += _drv->_opnFxCmdLen[cmd - 240];
}
}
}
@@ -2178,7 +2204,7 @@ void TownsPC98_OpnChannelSSG::init() {
#define Control(x) &TownsPC98_OpnChannelSSG::control_##x
static const ControlEventFunc ctrlEventsSSG[] = {
- Control(f0_setInstr),
+ Control(f0_setPatch),
Control(f1_setTotalLevel),
Control(f2_setKeyOffTime),
Control(f3_setFreqLSB),
@@ -2372,6 +2398,23 @@ void TownsPC98_OpnChannelSSG::setOutputLevel(uint8 lvl) {
_drv->writeReg(_part, 8 + _regOffset, _ssgTl);
}
+void TownsPC98_OpnChannelSSG::reset() {
+ TownsPC98_OpnChannel::reset();
+
+ // Unlike the original we restore the default patch data. This fixes a bug
+ // where certain sound effects would bring each other out of tune (e.g. the
+ // dragon's fire in Darm's house in Kyra 1 would sound different each time
+ // you triggered another sfx by dropping an item etc.)
+ uint8 i = (10 + _regOffset) << 4;
+ const uint8 *src = &_drv->_drvTables[156];
+ _drv->_ssgPatches[i] = src[i];
+ _drv->_ssgPatches[i + 3] = src[i + 3];
+ _drv->_ssgPatches[i + 4] = src[i + 4];
+ _drv->_ssgPatches[i + 6] = src[i + 6];
+ _drv->_ssgPatches[i + 8] = src[i + 8];
+ _drv->_ssgPatches[i + 12] = src[i + 12];
+}
+
void TownsPC98_OpnChannelSSG::fadeStep() {
_totalLevel--;
if ((int8)_totalLevel < 0)
@@ -2379,7 +2422,7 @@ void TownsPC98_OpnChannelSSG::fadeStep() {
setOutputLevel(_ssgStartLvl);
}
-bool TownsPC98_OpnChannelSSG::control_f0_setInstr(uint8 para) {
+bool TownsPC98_OpnChannelSSG::control_f0_setPatch(uint8 para) {
_instr = para << 4;
para = (para >> 3) & 0x1e;
if (para)
@@ -2463,6 +2506,39 @@ void TownsPC98_OpnSfxChannel::loadData(uint8 *data) {
_dataPtr = data;
_ssgTl = 0xff;
_algorithm = 0x80;
+
+ uint8 *tmp = _dataPtr;
+ for (bool loop = true; loop; ) {
+ uint8 cmd = *tmp++;
+ if (cmd < 0xf0) {
+ tmp++;
+ } else if (cmd == 0xff) {
+ loop = false;
+ } else if (cmd == 0xf6) {
+ // reset repeat section countdown
+ tmp[0] = tmp[1];
+ tmp += 4;
+ } else {
+ tmp += _drv->_opnFxCmdLen[cmd - 240];
+ }
+ }
+}
+
+void TownsPC98_OpnSfxChannel::reset() {
+ TownsPC98_OpnChannel::reset();
+
+ // Unlike the original we restore the default patch data. This fixes a bug
+ // where certain sound effects would bring each other out of tune (e.g. the
+ // dragon's fire in Darm's house in Kyra 1 would sound different each time
+ // you triggered another sfx by dropping an item etc.)
+ uint8 i = (13 + _regOffset) << 4;
+ const uint8 *src = &_drv->_drvTables[156];
+ _drv->_ssgPatches[i] = src[i];
+ _drv->_ssgPatches[i + 3] = src[i + 3];
+ _drv->_ssgPatches[i + 4] = src[i + 4];
+ _drv->_ssgPatches[i + 6] = src[i + 6];
+ _drv->_ssgPatches[i + 8] = src[i + 8];
+ _drv->_ssgPatches[i + 12] = src[i + 12];
}
TownsPC98_OpnChannelPCM::TownsPC98_OpnChannelPCM(TownsPC98_OpnDriver *driver, uint8 regOffs,
@@ -2557,7 +2633,8 @@ bool TownsPC98_OpnChannelPCM::control_ff_endOfTrack(uint8 para) {
TownsPC98_OpnSquareSineSource::TownsPC98_OpnSquareSineSource(const uint32 timerbase) : _tlTable(0),
_tleTable(0), _updateRequest(-1), _tickLength(timerbase * 27), _ready(0), _reg(0), _rand(1), _outN(1),
_nTick(0), _evpUpdateCnt(0), _evpTimer(0x1f), _pReslt(0x1f), _attack(0), _cont(false), _evpUpdate(true),
- _timer(0), _noiseGenerator(0), _chanEnable(0) {
+ _timer(0), _noiseGenerator(0), _chanEnable(0),
+ _volMaskA(0), _volMaskB(0), _volumeA(Audio::Mixer::kMaxMixerVolume), _volumeB(Audio::Mixer::kMaxMixerVolume) {
memset(_channels, 0, sizeof(_channels));
memset(_updateRequestBuf, 0, sizeof(_updateRequestBuf));
@@ -2705,24 +2782,30 @@ void TownsPC98_OpnSquareSineSource::nextTick(int32 *buffer, uint32 bufferSize) {
}
}
_pReslt = _evpTimer ^ _attack;
- updatesRegs();
+ updateRegs();
}
int32 finOut = 0;
for (int ii = 0; ii < 3; ii++) {
- if ((_channels[ii].vol >> 4) & 1)
- finOut += _tleTable[_channels[ii].out ? _pReslt : 0];
- else
- finOut += _tlTable[_channels[ii].out ? (_channels[ii].vol & 0x0f) : 0];
+ int32 finOutTemp = ((_channels[ii].vol >> 4) & 1) ? _tleTable[_channels[ii].out ? _pReslt : 0] : _tlTable[_channels[ii].out ? (_channels[ii].vol & 0x0f) : 0];
+
+ if ((1 << ii) & _volMaskA)
+ finOutTemp = (finOutTemp * _volumeA) / Audio::Mixer::kMaxMixerVolume;
+
+ if ((1 << ii) & _volMaskB)
+ finOutTemp = (finOutTemp * _volumeB) / Audio::Mixer::kMaxMixerVolume;
+
+ finOut += finOutTemp;
}
- finOut /= 3;
+ finOut /= 3;
+
buffer[i << 1] += finOut;
buffer[(i << 1) + 1] += finOut;
}
}
-void TownsPC98_OpnSquareSineSource::updatesRegs() {
+void TownsPC98_OpnSquareSineSource::updateRegs() {
for (int i = 0; i < _updateRequest;) {
uint8 b = _updateRequestBuf[i++];
uint8 a = _updateRequestBuf[i++];
@@ -2732,7 +2815,7 @@ void TownsPC98_OpnSquareSineSource::updatesRegs() {
}
TownsPC98_OpnPercussionSource::TownsPC98_OpnPercussionSource(const uint32 timerbase) :
- _tickLength(timerbase * 2), _timer(0), _ready(false) {
+ _tickLength(timerbase * 2), _timer(0), _ready(false), _volMaskA(0), _volMaskB(0), _volumeA(Audio::Mixer::kMaxMixerVolume), _volumeB(Audio::Mixer::kMaxMixerVolume) {
memset(_rhChan, 0, sizeof(RhtChannel) * 6);
_reg = new uint8 *[40];
@@ -2891,6 +2974,12 @@ void TownsPC98_OpnPercussionSource::nextTick(int32 *buffer, uint32 bufferSize) {
finOut <<= 1;
+ if (1 & _volMaskA)
+ finOut = (finOut * _volumeA) / Audio::Mixer::kMaxMixerVolume;
+
+ if (1 & _volMaskB)
+ finOut = (finOut * _volumeB) / Audio::Mixer::kMaxMixerVolume;
+
buffer[i << 1] += finOut;
buffer[(i << 1) + 1] += finOut;
}
@@ -2927,6 +3016,7 @@ TownsPC98_OpnCore::TownsPC98_OpnCore(Audio::Mixer *mixer, OpnType type) :
_numChan(type == OD_TYPE26 ? 3 : 6), _numSSG(type == OD_TOWNS ? 0 : 3), _hasPercussion(type == OD_TYPE86 ? true : false),
_oprRates(0), _oprRateshift(0), _oprAttackDecay(0), _oprFrq(0), _oprSinTbl(0), _oprLevelOut(0), _oprDetune(0),
_baserate(55125.0f / (float)mixer->getOutputRate()),
+ _volMaskA(0), _volMaskB(0), _volumeA(255), _volumeB(255),
_regProtectionFlag(false), _ready(false) {
memset(&_timers[0], 0, sizeof(OpnTimer));
@@ -2977,7 +3067,7 @@ bool TownsPC98_OpnCore::init() {
_prc->init(_percussionData);
}
- _mixer->playStream(Audio::Mixer::kMusicSoundType,
+ _mixer->playStream(Audio::Mixer::kPlainSoundType,
&_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
_ready = true;
@@ -2989,7 +3079,7 @@ void TownsPC98_OpnCore::reset() {
for (int i = 0; i < _numChan; i++) {
for (int ii = 0; ii < 4; ii++)
_chanInternal[i].opr[ii]->reset();
- memset(&_chanInternal[i].feedbuf, 0, 3);
+ memset(_chanInternal[i].feedbuf, 0, 3);
_chanInternal[i].algorithm = 0;
_chanInternal[i].frqTemp = 0;
_chanInternal[i].enableLeft = _chanInternal[i].enableRight = true;
@@ -3228,6 +3318,26 @@ int TownsPC98_OpnCore::readBuffer(int16 *buffer, const int numSamples) {
return numSamples;
}
+void TownsPC98_OpnCore::setVolumeIntern(int volA, int volB) {
+ Common::StackLock lock(_mutex);
+ _volumeA = volA;
+ _volumeB = volB;
+ if (_ssg)
+ _ssg->setVolumeIntern(volA, volB);
+ if (_prc)
+ _prc->setVolumeIntern(volA, volB);
+}
+
+void TownsPC98_OpnCore::setVolumeChannelMasks(int channelMaskA, int channelMaskB) {
+ Common::StackLock lock(_mutex);
+ _volMaskA = channelMaskA;
+ _volMaskB = channelMaskB;
+ if (_ssg)
+ _ssg->setVolumeChannelMasks(_volMaskA >> _numChan, _volMaskB >> _numChan);
+ if (_prc)
+ _prc->setVolumeChannelMasks(_volMaskA >> (_numChan + _numSSG), _volMaskB >> (_numChan + _numSSG));
+}
+
void TownsPC98_OpnCore::generateTables() {
delete[] _oprRates;
_oprRates = new uint8[128];
@@ -3388,6 +3498,12 @@ void TownsPC98_OpnCore::nextTick(int32 *buffer, uint32 bufferSize) {
int32 finOut = (output << 2) / ((_numChan + _numSSG - 3) / 3);
+ if ((1 << i) & _volMaskA)
+ finOut = (finOut * _volumeA) / Audio::Mixer::kMaxMixerVolume;
+
+ if ((1 << i) & _volMaskB)
+ finOut = (finOut * _volumeB) / Audio::Mixer::kMaxMixerVolume;
+
if (_chanInternal[i].enableLeft)
*leftSample += finOut;
@@ -3408,11 +3524,14 @@ TownsPC98_OpnDriver::TownsPC98_OpnDriver(Audio::Mixer *mixer, OpnType type) : To
_updateChannelsFlag(type == OD_TYPE26 ? 0x07 : 0x3F), _finishedChannelsFlag(0),
_updateSSGFlag(type == OD_TOWNS ? 0x00 : 0x07), _finishedSSGFlag(0),
_updateRhythmFlag(type == OD_TYPE86 ? 0x01 : 0x00), _finishedRhythmFlag(0),
- _updateSfxFlag(type == OD_TOWNS ? 0x00 : 0x06), _finishedSfxFlag(0),
+ _updateSfxFlag(0), _finishedSfxFlag(0),
_musicTickCounter(0),
+ _musicVolume(255), _sfxVolume(255),
+
_musicPlaying(false), _sfxPlaying(false), _fading(false), _looping(0), _ready(false) {
+
_sfxOffsets[0] = _sfxOffsets[1] = 0;
}
@@ -3450,6 +3569,8 @@ bool TownsPC98_OpnDriver::init() {
TownsPC98_OpnCore::init();
+ setVolumeChannelMasks(-1, 0);
+
_channels = new TownsPC98_OpnChannel *[_numChan];
for (int i = 0; i < _numChan; i++) {
int ii = i * 6;
@@ -3664,8 +3785,11 @@ void TownsPC98_OpnDriver::timerCallbackA() {
_trackPtr = _musicBuffer;
}
- if (_finishedSfxFlag == _updateSfxFlag)
+ if (_updateSfxFlag && _finishedSfxFlag == _updateSfxFlag) {
_sfxPlaying = false;
+ _updateSfxFlag = 0;
+ setVolumeChannelMasks(-1, 0);
+ }
}
void TownsPC98_OpnDriver::setMusicTempo(uint8 tempo) {
@@ -3680,14 +3804,22 @@ void TownsPC98_OpnDriver::setSfxTempo(uint16 tempo) {
}
void TownsPC98_OpnDriver::startSoundEffect() {
+ int volFlags = 0;
+
for (int i = 0; i < 2; i++) {
if (_sfxOffsets[i]) {
_ssgChannels[i + 1]->protect();
_sfxChannels[i]->reset();
_sfxChannels[i]->loadData(_sfxData + _sfxOffsets[i]);
+ _updateSfxFlag |= _sfxChannels[i]->_idFlag;
+ volFlags |= (_sfxChannels[i]->_idFlag << _numChan);
+ } else {
+ _ssgChannels[i + 1]->restore();
+ _updateSfxFlag &= ~_sfxChannels[i]->_idFlag;
}
}
-
+
+ setVolumeChannelMasks(~volFlags, volFlags);
_sfxData = 0;
}
@@ -4048,7 +4180,9 @@ SoundPC98::~SoundPC98() {
bool SoundPC98::init() {
_driver = new TownsPC98_OpnDriver(_mixer, TownsPC98_OpnDriver::OD_TYPE26);
- return _driver->init();
+ bool reslt = _driver->init();
+ updateVolumeSettings();
+ return reslt;
}
void SoundPC98::loadSoundFile(uint file) {
@@ -4121,6 +4255,13 @@ void SoundPC98::playSoundEffect(uint8 track) {
_driver->loadSoundEffectData(_sfxTrackData, track);
}
+void SoundPC98::updateVolumeSettings() {
+ if (!_driver)
+ return;
+
+ _driver->setMusicVolume(ConfMan.getInt("music_volume"));
+ _driver->setSoundEffectVolume(ConfMan.getInt("sfx_volume"));
+}
// KYRA 2
@@ -4159,7 +4300,9 @@ bool SoundTownsPC98_v2::init() {
_useFmSfx = true;
}
- return _driver->init();
+ bool reslt = _driver->init();
+ updateVolumeSettings();
+ return reslt;
}
void SoundTownsPC98_v2::loadSoundFile(Common::String file) {
@@ -4314,6 +4457,14 @@ void SoundTownsPC98_v2::playSoundEffect(uint8 track) {
_driver->loadSoundEffectData(_sfxTrackData, track);
}
+void SoundTownsPC98_v2::updateVolumeSettings() {
+ if (!_driver)
+ return;
+
+ _driver->setMusicVolume(ConfMan.getInt("music_volume"));
+ _driver->setSoundEffectVolume(ConfMan.getInt("sfx_volume"));
+}
+
// static resources
const uint32 TownsPC98_OpnCore::_adtStat[] = {
diff --git a/engines/lure/detection.cpp b/engines/lure/detection.cpp
index 36c1cf237d..dd2a702e2a 100644
--- a/engines/lure/detection.cpp
+++ b/engines/lure/detection.cpp
@@ -196,7 +196,11 @@ static const ADParams detectionParams = {
// Flags
kADFlagUseExtraAsHint,
// Additional GUI options (for every game}
- Common::GUIO_NOSPEECH
+ Common::GUIO_NOSPEECH,
+ // Maximum directory depth
+ 1,
+ // List of directory globs
+ 0
};
class LureMetaEngine : public AdvancedMetaEngine {
diff --git a/engines/lure/sound.cpp b/engines/lure/sound.cpp
index e725b7c31a..cd539dfab4 100644
--- a/engines/lure/sound.cpp
+++ b/engines/lure/sound.cpp
@@ -50,13 +50,13 @@ SoundManager::SoundManager() {
_soundData = NULL;
_paused = false;
- MidiDriverType midiDriver = MidiDriver::detectMusicDriver(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MIDI);
- _isRoland = midiDriver != MD_ADLIB;
- _nativeMT32 = ((midiDriver == MD_MT32) || ConfMan.getBool("native_mt32"));
+ MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MT32);
+ _isRoland = MidiDriver::getMusicType(dev) != MT_ADLIB;
+ _nativeMT32 = ((MidiDriver::getMusicType(dev) == MT_MT32) || ConfMan.getBool("native_mt32"));
Common::set_to(_channelsInUse, _channelsInUse + NUM_CHANNELS, false);
- _driver = MidiDriver::createMidi(midiDriver);
+ _driver = MidiDriver::createMidi(dev);
int statusCode = _driver->open();
if (statusCode) {
warning("Sound driver returned error code %d", statusCode);
diff --git a/engines/m4/animation.cpp b/engines/m4/animation.cpp
index 1142ba48d1..0ead57aac9 100644
--- a/engines/m4/animation.cpp
+++ b/engines/m4/animation.cpp
@@ -44,6 +44,7 @@ MadsAnimation::MadsAnimation(MadsM4Engine *vm, MadsView *view): Animation(vm), _
_currentFrame = 0;
_oldFrameEntry = 0;
_nextFrameTimer = _madsVm->_currentTimer;
+ _nextScrollTimer = 0;
}
MadsAnimation::~MadsAnimation() {
@@ -56,14 +57,14 @@ MadsAnimation::~MadsAnimation() {
if (_field12) {
_view->_spriteSlots.deleteSprites(_spriteListIndexes[_spriteListIndex]);
}
-
- delete _font;
}
+#define FILENAME_SIZE 13
+
/**
* Initialises and loads the data of an animation
*/
-void MadsAnimation::initialise(const Common::String &filename, uint16 flags, M4Surface *interfaceSurface, M4Surface *sceneSurface) {
+void MadsAnimation::initialise(const Common::String &filename, uint16 flags, M4Surface *surface, M4Surface *depthSurface) {
MadsPack anim(filename.c_str(), _vm);
bool madsRes = filename[0] == '*';
char buffer[20];
@@ -87,34 +88,49 @@ void MadsAnimation::initialise(const Common::String &filename, uint16 flags, M4S
animStream->skip(2);
_field12 = animStream->readUint16LE() != 0;
_spriteListIndex = animStream->readUint16LE();
- _scrollX = animStream->readUint16LE();
+ _scrollX = animStream->readSint16LE();
_scrollY = animStream->readSint16LE();
- animStream->skip(10);
+ _scrollTicks = animStream->readUint16LE();
+ animStream->skip(8);
- animStream->read(buffer, 13);
- _interfaceFile = Common::String(buffer, 13);
+ animStream->read(buffer, FILENAME_SIZE);
+ buffer[FILENAME_SIZE] = '\0';
+ _interfaceFile = Common::String(buffer);
for (int i = 0; i < 10; ++i) {
- animStream->read(buffer, 13);
- _spriteSetNames[i] = Common::String(buffer, 13);
+ animStream->read(buffer, FILENAME_SIZE);
+ buffer[FILENAME_SIZE] = '\0';
+ _spriteSetNames[i] = Common::String(buffer);
}
animStream->skip(81);
- animStream->read(buffer, 13);
- _lbmFilename = Common::String(buffer, 13);
- animStream->read(buffer, 13);
- _spritesFilename = Common::String(buffer, 13);
+ animStream->read(buffer, FILENAME_SIZE);
+ buffer[FILENAME_SIZE] = '\0';
+ _lbmFilename = Common::String(buffer);
+
+ animStream->skip(365);
+ animStream->read(buffer, FILENAME_SIZE);
+ buffer[FILENAME_SIZE] = '\0';
+ _spritesFilename = Common::String(buffer);
+
animStream->skip(48);
- animStream->read(buffer, 13);
- _soundName = Common::String(buffer, 13);
- animStream->skip(26);
- animStream->read(buffer, 13);
- Common::String fontResource(buffer, 13);
+ animStream->read(buffer, FILENAME_SIZE);
+ buffer[FILENAME_SIZE] = '\0';
+ _soundName = Common::String(buffer);
+
+ animStream->skip(13);
+ animStream->read(buffer, FILENAME_SIZE);
+ buffer[FILENAME_SIZE] = '\0';
+ _dsrName = Common::String(buffer);
+
+ animStream->read(buffer, FILENAME_SIZE);
+ buffer[FILENAME_SIZE] = '\0';
+ Common::String fontResource(buffer);
if (_animMode == 4)
flags |= 0x4000;
if (flags & 0x100)
- loadInterface(interfaceSurface, sceneSurface);
+ loadInterface(surface, depthSurface);
// Initialise the reference list
for (int i = 0; i < spriteListCount; ++i)
@@ -130,21 +146,24 @@ void MadsAnimation::initialise(const Common::String &filename, uint16 flags, M4S
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();
+ rec.soundId = animStream->readSint16LE();
+ animStream->read(rec.msg, 64);
+ animStream->skip(4);
+ rec.pos.x = animStream->readSint16LE();
+ rec.pos.y = animStream->readSint16LE();
+ rec.flags = animStream->readUint16LE();
+ rec.rgb1.r = animStream->readByte() << 2;
+ rec.rgb1.g = animStream->readByte() << 2;
+ rec.rgb1.b = animStream->readByte() << 2;
+ rec.rgb2.r = animStream->readByte() << 2;
+ rec.rgb2.g = animStream->readByte() << 2;
+ rec.rgb2.b = animStream->readByte() << 2;
+ animStream->skip(2); // Space for kernelMsgIndex
+ rec.kernelMsgIndex = -1;
animStream->skip(6);
rec.startFrame = animStream->readUint16LE();
rec.endFrame = animStream->readUint16LE();
- animStream->readUint16LE();
+ animStream->skip(2);
_messages.push_back(rec);
}
@@ -162,9 +181,9 @@ void MadsAnimation::initialise(const Common::String &filename, uint16 flags, M4S
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.xp = animStream->readSint16LE();
+ rec.spriteSlot.yp = animStream->readSint16LE();
+ rec.spriteSlot.depth = animStream->readSByte();
rec.spriteSlot.scale = (int8)animStream->readByte();
_frameEntries.push_back(rec);
@@ -180,7 +199,7 @@ void MadsAnimation::initialise(const Common::String &filename, uint16 flags, M4S
for (int i = 0; i < miscEntriesCount; ++i) {
AnimMiscEntry rec;
rec.soundNum = animStream->readByte();
- animStream->skip(1);
+ rec.msgIndex = animStream->readSByte();
rec.numTicks = animStream->readUint16LE();
rec.posAdjust.x = animStream->readUint16LE();
rec.posAdjust.y = animStream->readUint16LE();
@@ -199,9 +218,16 @@ void MadsAnimation::initialise(const Common::String &filename, uint16 flags, M4S
fontName += "*";
fontName += fontResource;
- _font = _vm->_font->getFont(fontName);
+ if (fontName != "")
+ _font = _vm->_font->getFont(fontName.c_str());
+ else
+ warning("Attempted to set a font with an empty name");
}
+ // If a speech file is specified, then load it
+ if (!_dsrName.empty())
+ _vm->_sound->loadDSRFile(_dsrName.c_str());
+
// Load all the sprite sets for the animation
for (int i = 0; i < spriteListCount; ++i) {
if (_field12 && (i == _spriteListIndex))
@@ -232,6 +258,9 @@ void MadsAnimation::initialise(const Common::String &filename, uint16 flags, M4S
int idx = _frameEntries[i].spriteSlot.spriteListIndex;
_frameEntries[i].spriteSlot.spriteListIndex = _spriteListIndexes[idx];
}
+
+ if (hasScroll())
+ _nextScrollTimer = _madsVm->_currentTimer + _scrollTicks;
}
/**
@@ -259,7 +288,7 @@ void MadsAnimation::load(const Common::String &filename, int abortTimers) {
_abortMode = _madsVm->scene()->_abortTimersMode2;
for (int i = 0; i < 3; ++i)
- _actionNouns[i] = _madsVm->scene()->actionNouns[i];
+ _actionNouns[i] = _madsVm->globals()->actionNouns[i];
// Initialise kernel message list
for (uint i = 0; i < _messages.size(); ++i)
@@ -282,9 +311,24 @@ void MadsAnimation::update() {
load1(newIndex);
}
+ // Check for scroll change
+ bool screenChanged = false;
+
+ // Handle any scrolling of the screen surface
+ if (hasScroll() && (_madsVm->_currentTimer >= _nextScrollTimer)) {
+ _view->_bgSurface->scrollX(_scrollX);
+ _view->_bgSurface->scrollY(_scrollY);
+
+ _nextScrollTimer = _madsVm->_currentTimer + _scrollTicks;
+ screenChanged = true;
+ }
+
// If it's not time for the next frame, then exit
- if (_madsVm->_currentTimer < _nextFrameTimer)
+ if (_madsVm->_currentTimer < _nextFrameTimer) {
+ if (screenChanged)
+ _view->_spriteSlots.fullRefresh();
return;
+ }
// Loop checks for any prior animation sprite slots to be expired
for (int slotIndex = 0; slotIndex < _view->_spriteSlots.startIndex; ++slotIndex) {
@@ -311,25 +355,17 @@ void MadsAnimation::update() {
if (misc.soundNum)
_vm->_sound->playSound(misc.soundNum);
- bool screenChanged = false;
-
- // Handle any scrolling of the screen surface
- if ((_scrollX != 0) || (_scrollY != 0)) {
- _view->_bgSurface->scrollX(_scrollX);
- _view->_bgSurface->scrollY(_scrollY);
-
- screenChanged = true;
- }
-
// Handle any offset adjustment for sprites as of this frame
if (_view->_posAdjust.x != misc.posAdjust.x) {
- misc.posAdjust.x = _view->_posAdjust.x;
+ _view->_posAdjust.x = misc.posAdjust.x;
screenChanged = true;
}
if (_view->_posAdjust.y != misc.posAdjust.y) {
- misc.posAdjust.y = _view->_posAdjust.y;
+ _view->_posAdjust.y = misc.posAdjust.y;
screenChanged = true;
}
+
+
if (screenChanged) {
// Signal the entire screen needs refreshing
_view->_spriteSlots.fullRefresh();
@@ -408,7 +444,12 @@ void MadsAnimation::update() {
_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);
+ me.kernelMsgIndex = _view->_kernelMessages.add(me.pos, colIndex * 0x101 + 0x100, 0, 0, INDEFINITE_TIMEOUT, me.msg);
+ assert(me.kernelMsgIndex >= 0);
+
+ // Play the associated sound, if it exists
+ if (me.soundId > 0)
+ _vm->_sound->playDSRSound(me.soundId - 1, 255, false);
++_messageCtr;
}
}
@@ -424,7 +465,7 @@ void MadsAnimation::update() {
if (_abortMode != ABORTMODE_1) {
// Copy the noun list
for (int i = 0; i < 3; ++i)
- _madsVm->scene()->actionNouns[i] = _actionNouns[i];
+ _madsVm->globals()->actionNouns[i] = _actionNouns[i];
}
}
}
@@ -437,6 +478,12 @@ void MadsAnimation::setCurrentFrame(int frameNumber) {
_currentFrame = frameNumber;
_oldFrameEntry = 0;
_freeFlag = false;
+
+ _nextScrollTimer = _nextFrameTimer = _madsVm->_currentTimer;
+}
+
+int MadsAnimation::getCurrentFrame() {
+ return _currentFrame;
}
void MadsAnimation::load1(int frameNumber) {
diff --git a/engines/m4/animation.h b/engines/m4/animation.h
index 5c7227a256..583d829066 100644
--- a/engines/m4/animation.h
+++ b/engines/m4/animation.h
@@ -39,12 +39,13 @@ class SpriteSlotSubset;
class AnimMessage {
public:
- char msg[70];
+ int16 soundId;
+ char msg[64];
Common::Point pos;
RGB8 rgb1, rgb2;
- int kernelMsgIndex;
-
+ uint16 flags;
int startFrame, endFrame;
+ int kernelMsgIndex;
};
class AnimFrameEntry {
@@ -57,6 +58,7 @@ public:
class AnimMiscEntry {
public:
int soundNum;
+ int msgIndex;
int numTicks;
Common::Point posAdjust;
};
@@ -82,11 +84,13 @@ private:
int _spriteListIndex;
int _scrollX;
int _scrollY;
+ int _scrollTicks;
Common::String _interfaceFile;
Common::String _spriteSetNames[10];
Common::String _lbmFilename;
Common::String _spritesFilename;
Common::String _soundName;
+ Common::String _dsrName;
Common::Array<int> _spriteListIndexes;
int _currentFrame, _oldFrameEntry;
@@ -96,24 +100,29 @@ private:
int _unkIndex;
Common::Point _unkList[2];
uint32 _nextFrameTimer;
+ uint32 _nextScrollTimer;
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);
+ bool hasScroll() const { return (_scrollX != 0) || (_scrollY != 0); }
public:
MadsAnimation(MadsM4Engine *vm, MadsView *view);
virtual ~MadsAnimation();
- virtual void initialise(const Common::String &filename, uint16 flags, M4Surface *interfaceSurface, M4Surface *sceneSurface);
+ virtual void initialise(const Common::String &filename, uint16 flags, M4Surface *surface, M4Surface *depthSurface);
virtual void load(const Common::String &filename, int abortTimers);
virtual void update();
virtual void setCurrentFrame(int frameNumber);
+ virtual int getCurrentFrame();
bool freeFlag() const { return _freeFlag; }
+ bool getAnimMode() const { return _animMode; }
int roomNumber() const { return _roomNumber; }
};
diff --git a/engines/m4/assets.cpp b/engines/m4/assets.cpp
index 1f3cf278ae..23122eb960 100644
--- a/engines/m4/assets.cpp
+++ b/engines/m4/assets.cpp
@@ -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) :
+SpriteAsset::SpriteAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name,
+ bool asStream, int flags) :
BaseAsset(vm) {
_stream = stream;
_palInterface = NULL;
@@ -107,7 +108,7 @@ SpriteAsset::SpriteAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, i
if (_vm->isM4()) {
loadM4SpriteAsset(vm, stream, asStream);
} else {
- loadMadsSpriteAsset(vm, stream);
+ loadMadsSpriteAsset(vm, stream, flags);
}
}
@@ -119,7 +120,7 @@ SpriteAsset::SpriteAsset(MadsM4Engine *vm, const char *name): BaseAsset(vm) {
if (_vm->isM4()) {
loadM4SpriteAsset(vm, _stream, true);
} else {
- loadMadsSpriteAsset(vm, _stream);
+ loadMadsSpriteAsset(vm, _stream, 0);
}
vm->res()->toss(name);
@@ -136,6 +137,8 @@ SpriteAsset::~SpriteAsset() {
for (Common::Array<SpriteAssetFrame>::iterator it = _frames.begin(); it != _frames.end(); ++it) {
delete (*it).frame;
}
+
+ delete _charInfo;
}
void SpriteAsset::loadM4SpriteAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, bool asStream) {
@@ -200,7 +203,7 @@ void SpriteAsset::loadM4SpriteAsset(MadsM4Engine *vm, Common::SeekableReadStream
}
-void SpriteAsset::loadMadsSpriteAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream) {
+void SpriteAsset::loadMadsSpriteAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int flags) {
int curFrame = 0;
uint32 frameOffset = 0;
MadsPack sprite(stream);
@@ -217,7 +220,12 @@ void SpriteAsset::loadMadsSpriteAsset(MadsM4Engine *vm, Common::SeekableReadStre
_isBackground = (type1 != 0) && (type2 < 4);
spriteStream->skip(32);
_frameCount = spriteStream->readUint16LE();
- // we skip the rest of the data
+
+ if (_vm->isM4() || ((flags & SPRITE_SET_CHAR_INFO) == 0))
+ _charInfo = NULL;
+ else
+ _charInfo = new MadsSpriteSetCharInfo(spriteStream);
+
delete spriteStream;
// Get the palette data
@@ -234,12 +242,15 @@ void SpriteAsset::loadMadsSpriteAsset(MadsM4Engine *vm, Common::SeekableReadStre
spriteStream = sprite.getItemStream(1);
Common::SeekableReadStream *spriteDataStream = sprite.getItemStream(3);
SpriteAssetFrame frame;
+ Common::Array<int> frameSizes;
for (curFrame = 0; curFrame < _frameCount; curFrame++) {
frame.stream = 0;
frame.comp = 0;
frameOffset = spriteStream->readUint32LE();
_frameOffsets.push_back(frameOffset);
- spriteStream->readUint32LE(); // frame size
+ uint32 frameSize = spriteStream->readUint32LE();
+ frameSizes.push_back(frameSize);
+
frame.x = spriteStream->readUint16LE();
frame.y = spriteStream->readUint16LE();
frame.w = spriteStream->readUint16LE();
@@ -247,9 +258,44 @@ void SpriteAsset::loadMadsSpriteAsset(MadsM4Engine *vm, Common::SeekableReadStre
if (curFrame == 0)
debugC(1, kDebugGraphics, "%i frames, x = %i, y = %i, w = %i, h = %i\n", _frameCount, frame.x, frame.y, frame.w, frame.h);
- frame.frame = new M4Sprite(spriteDataStream, frame.x, frame.y, frame.w, frame.h, false);
+ if (_mode == 0) {
+ // Create a frame and decompress the raw pixel data
+ uint32 currPos = (uint32)spriteDataStream->pos();
+ frame.frame = new M4Sprite(spriteDataStream, frame.x, frame.y, frame.w, frame.h, false);
+ assert((uint32)spriteDataStream->pos() == (currPos + frameSize));
+ }
+
_frames.push_back(frame);
}
+
+ if (_mode != 0) {
+ // Handle decompressing Fab encoded data
+ for (curFrame = 0; curFrame < _frameCount; curFrame++) {
+ FabDecompressor fab;
+
+ int srcSize = (curFrame == (_frameCount - 1)) ? spriteDataStream->size() - _frameOffsets[curFrame] :
+ _frameOffsets[curFrame + 1] - _frameOffsets[curFrame];
+ byte *srcData = (byte *)malloc(srcSize);
+ assert(srcData);
+ spriteDataStream->read(srcData, srcSize);
+
+ byte *destData = (byte *)malloc(frameSizes[curFrame]);
+ assert(destData);
+
+ fab.decompress(srcData, srcSize, destData, frameSizes[curFrame]);
+
+ // Load the frame
+ Common::MemoryReadStream *rs = new Common::MemoryReadStream(destData, frameSizes[curFrame]);
+ _frames[curFrame].frame = new M4Sprite(rs, _frames[curFrame].x, _frames[curFrame].y,
+ _frames[curFrame].w, _frames[curFrame].h, false);
+ delete rs;
+
+ free(srcData);
+ free(destData);
+ }
+ }
+
+
delete spriteStream;
delete spriteDataStream;
}
@@ -320,7 +366,12 @@ void SpriteAsset::loadFrameHeader(SpriteAssetFrame &frameHeader, bool isBigEndia
}
M4Sprite *SpriteAsset::getFrame(int frameIndex) {
- return _frames[frameIndex].frame;
+ if ((uint)frameIndex < _frames.size()) {
+ return _frames[frameIndex].frame;
+ } else {
+ warning("SpriteAsset::getFrame: Invalid frame %d, out of %d", frameIndex, _frames.size());
+ return _frames[_frames.size() - 1].frame;
+ }
}
void SpriteAsset::loadStreamingFrame(M4Sprite *frame, int frameIndex, int destX, int destY) {
@@ -578,4 +629,23 @@ int32 AssetManager::getSpriteFrameCount(int32 hash) {
return _CELS[hash]->getCount();
}
+//--------------------------------------------------------------------------
+
+MadsSpriteSetCharInfo::MadsSpriteSetCharInfo(Common::SeekableReadStream *s) {
+ _totalFrames = s->readByte();
+ s->skip(1);
+ _numEntries = s->readUint16LE();
+
+ for (int i = 0; i < 16; ++i)
+ _frameList[i] = s->readUint16LE();
+ for (int i = 0; i < 16; ++i)
+ _frameList2[i] = s->readUint16LE();
+ for (int i = 0; i < 16; ++i)
+ _ticksList[i] = s->readUint16LE();
+
+ _unk1 = s->readUint16LE();
+ _ticksAmount = s->readByte();
+ _yScale = s->readByte();
+}
+
} // End of namespace M4
diff --git a/engines/m4/assets.h b/engines/m4/assets.h
index e5beffbcae..3ae7fb2e22 100644
--- a/engines/m4/assets.h
+++ b/engines/m4/assets.h
@@ -44,6 +44,8 @@ namespace M4 {
#define CELS__PAL MKID_BE(' PAL') //' PAL'
#define CELS___SS MKID_BE(' SS') //' SS'
+#define SPRITE_SET_CHAR_INFO 4
+
class MadsM4Engine;
class Palette;
@@ -100,13 +102,28 @@ struct SpriteAssetFrame {
M4Sprite *frame;
};
+class MadsSpriteSetCharInfo {
+public:
+ MadsSpriteSetCharInfo(Common::SeekableReadStream *s);
+
+ int _totalFrames;
+ int _numEntries;
+ int _frameList2[16];
+ int _frameList[16];
+ int _ticksList[16];
+ int _unk1;
+ int _ticksAmount;
+ int _yScale;
+};
+
class SpriteAsset : public BaseAsset {
public:
- SpriteAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name, bool asStream = false);
+ SpriteAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name,
+ bool asStream = false, int flags = 0);
SpriteAsset(MadsM4Engine *vm, const char *name);
~SpriteAsset();
void loadM4SpriteAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, bool asStream);
- void loadMadsSpriteAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream);
+ void loadMadsSpriteAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int flags);
int32 getCount() { return _frameCount; }
int32 getFrameRate() const { return _frameRate; }
int32 getPixelSpeed() const { return _pixelSpeed; }
@@ -124,6 +141,8 @@ public:
void translate(Palette *palette);
int32 getFrameSize(int index);
M4Sprite *operator[](int index) { return getFrame(index); }
+public:
+ MadsSpriteSetCharInfo *_charInfo;
protected:
Common::SeekableReadStream *_stream;
RGB8 _palette[256];
diff --git a/engines/m4/console.cpp b/engines/m4/console.cpp
index 4e14afdfaf..19fbf6e852 100644
--- a/engines/m4/console.cpp
+++ b/engines/m4/console.cpp
@@ -196,7 +196,7 @@ bool Console::cmdShowSprite(int argc, const char **argv) {
if (y >= bg->height())
break;
- spr->copyTo(bg, x, y, (int)spr->getTransparentColor());
+ spr->copyTo(bg, x, y, (int)spr->getTransparencyIndex());
x += spr->width();
yMax = MAX(yMax, spr->height());
diff --git a/engines/m4/detection.cpp b/engines/m4/detection.cpp
index 9493226c1a..4b204996f3 100644
--- a/engines/m4/detection.cpp
+++ b/engines/m4/detection.cpp
@@ -400,7 +400,11 @@ static const ADParams detectionParams = {
// Flags
0,
// Additional GUI options (for every game}
- Common::GUIO_NOMIDI
+ Common::GUIO_NOMIDI,
+ // Maximum directory depth
+ 1,
+ // List of directory globs
+ 0
};
class M4MetaEngine : public AdvancedMetaEngine {
diff --git a/engines/m4/events.cpp b/engines/m4/events.cpp
index c0ca412f11..c66609844a 100644
--- a/engines/m4/events.cpp
+++ b/engines/m4/events.cpp
@@ -57,6 +57,10 @@ Events::Events(MadsM4Engine *vm) : _vm(vm) {
_console = new MadsConsole(_madsVm);
}
+Events::~Events() {
+ delete _console;
+}
+
M4EventType Events::handleEvents() {
static int oldX = -1, oldY = -1;
static uint32 dclickTime = 0;
@@ -252,7 +256,8 @@ bool Mouse::setCursorNum(int cursorIndex) {
_cursor = _cursorSprites->getFrame(cursorIndex);
// Set the cursor to the sprite
- CursorMan.replaceCursor((const byte *)_cursor->getBasePtr(), _cursor->width(), _cursor->height(), _cursor->xOffset, _cursor->yOffset, 0);
+ CursorMan.replaceCursor((const byte *)_cursor->getBasePtr(), _cursor->width(), _cursor->height(),
+ _cursor->xOffset, _cursor->yOffset, TRANSPARENT_COLOUR_INDEX);
return true;
}
diff --git a/engines/m4/events.h b/engines/m4/events.h
index 43b61c8f0d..1c1418d5f8 100644
--- a/engines/m4/events.h
+++ b/engines/m4/events.h
@@ -78,6 +78,7 @@ private:
public:
bool quitFlag;
Events(MadsM4Engine *vm);
+ virtual ~Events();
Common::Event &event() { return _event; }
Common::EventType type() { return _event.type; }
diff --git a/engines/m4/font.cpp b/engines/m4/font.cpp
index 4afa158976..b5965732e5 100644
--- a/engines/m4/font.cpp
+++ b/engines/m4/font.cpp
@@ -35,27 +35,34 @@ FontManager::~FontManager() {
_entries.clear();
}
-Font *FontManager::getFont(const Common::String &filename) {
+Font *FontManager::getFont(const char *filename) {
+ // Append an extension if the filename doesn't already have one
+ char buffer[20];
+ strncpy(buffer, filename, 19);
+ if (!strchr(buffer, '.'))
+ strcat(buffer, ".ff");
+
// Check if the font is already loaded
- for (uint i = 0; i < _entries.size(); ++i)
- {
- if (_entries[i]->_filename.equals(filename))
+ for (uint i = 0; i < _entries.size(); ++i) {
+ if (!strcmp(_entries[i]->_filename, buffer))
return _entries[i];
}
- Font *f = new Font(_vm, filename);
+ Font *f = new Font(_vm, buffer);
_entries.push_back(f);
return f;
}
-void FontManager::setFont(const Common::String &filename) {
+void FontManager::setFont(const char *filename) {
_currentFont = getFont(filename);
}
//--------------------------------------------------------------------------
-Font::Font(MadsM4Engine *vm, const Common::String &filename) : _vm(vm), _filename(filename) {
+Font::Font(MadsM4Engine *vm, const char *filename) : _vm(vm) {
_sysFont = true;
+ strncpy(_filename, filename, 19);
+ _filename[19] = '\0';
//TODO: System font
_fontColors[0] = _vm->_palette->BLACK;
@@ -66,9 +73,9 @@ Font::Font(MadsM4Engine *vm, const Common::String &filename) : _vm(vm), _filenam
_sysFont = false;
if (_vm->isM4())
- setFontM4(filename.c_str());
+ setFontM4(filename);
else
- setFontMads(filename.c_str());
+ setFontMads(filename);
}
void Font::setFontM4(const char *filename) {
diff --git a/engines/m4/font.h b/engines/m4/font.h
index ca47848c61..19d15faa1e 100644
--- a/engines/m4/font.h
+++ b/engines/m4/font.h
@@ -59,7 +59,7 @@ namespace M4 {
class Font {
public:
- Font(MadsM4Engine *vm, const Common::String &filename);
+ Font(MadsM4Engine *vm, const char *filename);
~Font();
void setColour(uint8 colour);
@@ -73,7 +73,7 @@ public:
return write(surface, text, x, y, width, spaceWidth, _fontColors);
}
public:
- const Common::String _filename;
+ char _filename[20];
private:
void setFontM4(const char *filename);
void setFontMads(const char *filename);
@@ -108,8 +108,8 @@ public:
FontManager(MadsM4Engine *vm): _vm(vm) { _currentFont = NULL; }
~FontManager();
- Font *getFont(const Common::String &filename);
- void setFont(const Common::String &filename);
+ Font *getFont(const char *filename);
+ void setFont(const char *filename);
Font *current() {
assert(_currentFont);
diff --git a/engines/m4/globals.cpp b/engines/m4/globals.cpp
index 1768c71787..a96229a0b3 100644
--- a/engines/m4/globals.cpp
+++ b/engines/m4/globals.cpp
@@ -282,6 +282,9 @@ MadsGlobals::MadsGlobals(MadsEngine *vm): Globals(vm) {
playerSpriteChanged = false;
dialogType = DIALOG_NONE;
sceneNumber = -1;
+ for (int i = 0; i < 3; ++i)
+ actionNouns[i] = 0;
+ _difficultyLevel = 0;
}
MadsGlobals::~MadsGlobals() {
@@ -351,16 +354,16 @@ void MadsGlobals::loadMadsMessagesInfo() {
//printf("%i messages\n", count);
for (int i = 0; i < count; i++) {
- MessageItem *curMessage = new MessageItem();
- curMessage->id = messageS->readUint32LE();
- curMessage->offset = messageS->readUint32LE();
- curMessage->uncompSize = messageS->readUint16LE();
+ MessageItem curMessage;
+ curMessage.id = messageS->readUint32LE();
+ curMessage.offset = messageS->readUint32LE();
+ curMessage.uncompSize = messageS->readUint16LE();
if (i > 0)
- _madsMessages[i - 1]->compSize = curMessage->offset - _madsMessages[i - 1]->offset;
+ _madsMessages[i - 1].compSize = curMessage.offset - _madsMessages[i - 1].offset;
if (i == count - 1)
- curMessage->compSize = messageS->size() - curMessage->offset;
+ curMessage.compSize = messageS->size() - curMessage.offset;
//printf("id: %i, offset: %i, uncomp size: %i\n", curMessage->id, curMessage->offset, curMessage->uncompSize);
_madsMessages.push_back(curMessage);
@@ -382,7 +385,7 @@ void MadsGlobals::loadMadsObjects() {
int MadsGlobals::messageIndexOf(uint32 messageId) {
for (uint i = 0; i < _madsMessages.size(); ++i)
{
- if (_madsMessages[i]->id == messageId)
+ if (_madsMessages[i].id == messageId)
return i;
}
return -1;
@@ -395,15 +398,15 @@ const char *MadsGlobals::loadMessage(uint index) {
}
FabDecompressor fab;
- byte *compData = new byte[_madsMessages[index]->compSize];
- byte *buffer = new byte[_madsMessages[index]->uncompSize];
+ byte *compData = new byte[_madsMessages[index].compSize];
+ byte *buffer = new byte[_madsMessages[index].uncompSize];
Common::SeekableReadStream *messageS = _vm->res()->get("messages.dat");
- messageS->seek(_madsMessages[index]->offset, SEEK_SET);
- messageS->read(compData, _madsMessages[index]->compSize);
- fab.decompress(compData, _madsMessages[index]->compSize, buffer, _madsMessages[index]->uncompSize);
+ messageS->seek(_madsMessages[index].offset, SEEK_SET);
+ messageS->read(compData, _madsMessages[index].compSize);
+ fab.decompress(compData, _madsMessages[index].compSize, buffer, _madsMessages[index].uncompSize);
- for (int i = 0; i < _madsMessages[index]->uncompSize - 1; i++)
+ for (int i = 0; i < _madsMessages[index].uncompSize - 1; i++)
if (buffer[i] == '\0') buffer[i] = '\n';
_vm->res()->toss("messages.dat");
@@ -526,7 +529,9 @@ void MadsObject::load(Common::SeekableReadStream *stream) {
roomNumber = READ_LE_UINT16(&obj[2]);
article = (MADSArticles)obj[4];
vocabCount = obj[5] & 0x7f;
- assert(vocabCount <= 3);
+ // Phantom / Dragon
+ if (vocabCount > 3)
+ warning("MadsObject::load(), vocab cound > 3 (it's %d)", vocabCount);
for (int i = 0; i < vocabCount; ++i) {
vocabList[i].flags1 = obj[6 + i * 4];
diff --git a/engines/m4/globals.h b/engines/m4/globals.h
index de6e716ece..3fc31b4ec2 100644
--- a/engines/m4/globals.h
+++ b/engines/m4/globals.h
@@ -28,6 +28,7 @@
#include "common/scummsys.h"
#include "common/array.h"
+#include "common/hashmap.h"
#include "common/rect.h"
#include "common/file.h"
#include "common/list.h"
@@ -149,7 +150,7 @@ public:
void pauseGame(bool value);
};
-#define TOTAL_NUM_VARIABLES 256
+#define TOTAL_NUM_VARIABLES 210
#define PLAYER_INVENTORY 2
@@ -223,6 +224,13 @@ struct MadsConfigData {
int screenFades;
};
+#define GET_GLOBAL(x) (_madsVm->globals()->_globals[x])
+#define GET_GLOBAL32(x) (((uint32)_madsVm->globals()->_globals[x + 1] << 16) | _madsVm->globals()->_globals[x])
+#define SET_GLOBAL(x,y) _madsVm->globals()->_globals[x] = y
+#define SET_GLOBAL32(x,y) { _madsVm->globals()->_globals[x] = (y) & 0xffff; _madsVm->globals()->_globals[(x) + 1] = (y) >> 16; }
+
+typedef Common::HashMap<uint16, uint16> IntStorage;
+
class MadsGlobals : public Globals {
private:
struct MessageItem {
@@ -235,7 +243,7 @@ private:
MadsEngine *_vm;
Common::Array<char* > _madsVocab;
Common::Array<char* > _madsQuotes;
- Common::Array<MessageItem* > _madsMessages;
+ Common::Array<MessageItem> _madsMessages;
MadsObjectArray _madsObjects;
Common::List<int> _visitedScenes;
public:
@@ -243,12 +251,16 @@ public:
~MadsGlobals();
// MADS variables
- int _globals[TOTAL_NUM_VARIABLES];
+ uint16 _globals[TOTAL_NUM_VARIABLES];
MadsConfigData _config;
bool playerSpriteChanged;
MadsDialogType dialogType;
int sceneNumber;
int previousScene;
+ int16 _nextSceneId;
+ uint16 actionNouns[3];
+ IntStorage _dataMap;
+ int _difficultyLevel;
void loadMadsVocab();
uint32 getVocabSize() { return _madsVocab.size(); }
diff --git a/engines/m4/graphics.cpp b/engines/m4/graphics.cpp
index 8624f18da1..c10ea6c9f6 100644
--- a/engines/m4/graphics.cpp
+++ b/engines/m4/graphics.cpp
@@ -65,6 +65,15 @@ void RGBList::setRange(int start, int count, const RGB8 *src) {
Common::copy(&src[0], &src[count], &_data[start]);
}
+/**
+ * Creates a duplicate of the given rgb list
+ */
+RGBList *RGBList::clone() const {
+ RGBList *dest = new RGBList(_size, _data, false);
+ _madsVm->_palette->addRange(dest);
+ return dest;
+}
+
//--------------------------------------------------------------------------
#define VGA_COLOR_TRANS(x) (x == 0x3f ? 255 : x << 2)
@@ -74,6 +83,8 @@ M4Surface::~M4Surface() {
_madsVm->_palette->deleteRange(_rgbList);
delete _rgbList;
}
+ if (_ownsData)
+ free();
}
void M4Surface::loadCodesM4(Common::SeekableReadStream *source) {
@@ -331,6 +342,16 @@ void M4Surface::clear() {
Common::set_to((byte *)pixels, (byte *)pixels + w * h, _vm->_palette->BLACK);
}
+void M4Surface::reset() {
+ ::free(pixels);
+ pixels = NULL;
+ if (_rgbList) {
+ _vm->_palette->deleteRange(_rgbList);
+ delete _rgbList;
+ _rgbList = NULL;
+ }
+}
+
void M4Surface::frameRect(const Common::Rect &r, uint8 color) {
Graphics::Surface::frameRect(r, color);
}
@@ -389,38 +410,35 @@ void M4Surface::copyFrom(M4Surface *src, const Common::Rect &srcBounds, int dest
* Copies a given image onto a destination surface with scaling, transferring only pixels that meet
* the specified depth requirement on a secondary surface contain depth information
*/
-void M4Surface::copyFrom(M4Surface *src, int destX, int destY, int depth, M4Surface *depthsSurface,
- int scale, int transparentColour) {
- /* TODO: This isn't a straight re-implementation of the original draw routine. Double check in future
- * whether this implementation provides equivalent functionality
- */
- Common::Rect copyRect(0, 0, src->width(), src->height());
+void M4Surface::copyFrom(M4Surface *src, int destX, int destY, int depth,
+ M4Surface *depthsSurface, int scale, int transparentColour) {
- if (destX < 0) {
- copyRect.left += -destX;
- destX = 0;
- } else if (destX + copyRect.width() > w) {
- copyRect.right -= destX + copyRect.width() - w;
- }
- if (destY < 0) {
- copyRect.top += -destY;
- destY = 0;
- } else if (destY + copyRect.height() > h) {
- copyRect.bottom -= destY + copyRect.height() - h;
- }
-
- if (!copyRect.isValidRect())
- return;
+ if (scale == 100) {
+ // Copy the specified area
+ Common::Rect copyRect(0, 0, src->width(), src->height());
+
+ if (destX < 0) {
+ copyRect.left += -destX;
+ destX = 0;
+ } else if (destX + copyRect.width() > w) {
+ copyRect.right -= destX + copyRect.width() - w;
+ }
+ if (destY < 0) {
+ copyRect.top += -destY;
+ destY = 0;
+ } else if (destY + copyRect.height() > h) {
+ copyRect.bottom -= destY + copyRect.height() - h;
+ }
- // Copy the specified area
+ if (!copyRect.isValidRect())
+ return;
- byte *data = src->getBasePtr();
- byte *srcPtr = data + (src->width() * copyRect.top + copyRect.left);
- byte *depthsData = depthsSurface->getBasePtr();
- byte *depthsPtr = depthsData + (src->width() * copyRect.top + copyRect.left);
- byte *destPtr = (byte *)pixels + (destY * width()) + destX;
+ byte *data = src->getBasePtr();
+ byte *srcPtr = data + (src->width() * copyRect.top + copyRect.left);
+ byte *depthsData = depthsSurface->getBasePtr();
+ byte *depthsPtr = depthsData + (depthsSurface->pitch * destY) + destX;
+ byte *destPtr = (byte *)pixels + (destY * pitch) + destX;
- if (scale == 100) {
// 100% scaling variation
for (int rowCtr = 0; rowCtr < copyRect.height(); ++rowCtr) {
// Copy each byte one at a time checking against the depth
@@ -433,31 +451,127 @@ void M4Surface::copyFrom(M4Surface *src, int destX, int destY, int depth, M4Surf
depthsPtr += depthsSurface->width();
destPtr += width();
}
- } else {
- // Scaled variation
- for (int rowCtr = 0; rowCtr < copyRect.height(); ++rowCtr) {
- int currX = -1;
- // Loop through the source pixels
- for (int xCtr = 0, xTotal = 0; xCtr < copyRect.width(); ++xCtr, xTotal += (100 - scale)) {
- int srcX = xTotal / 100;
+ src->freeData();
+ depthsSurface->freeData();
+ return;
+ }
- if (srcX != currX) {
- currX = srcX;
+ // Start of draw logic for scaled sprites
+ const byte *srcPixelsP = src->getBasePtr();
+
+ int destRight = this->width() - 1;
+ int destBottom = this->height() - 1;
+ bool normalFrame = true; // TODO: false for negative frame numbers
+ int frameWidth = src->width();
+ int frameHeight = src->height();
+
+ int highestDim = MAX(frameWidth, frameHeight);
+ bool lineDist[MADS_SURFACE_WIDTH];
+ int distIndex = 0;
+ int distXCount = 0, distYCount = 0;
+
+ int distCtr = 0;
+ do {
+ distCtr += scale;
+ if (distCtr < 100) {
+ lineDist[distIndex] = false;
+ } else {
+ lineDist[distIndex] = true;
+ distCtr -= 100;
- if ((depthsPtr[currX] > depth) && (srcPtr[xCtr] != transparentColour))
- destPtr[currX] = srcPtr[xCtr];
- }
- }
+ if (distIndex < frameWidth)
+ ++distXCount;
- srcPtr += src->width();
- depthsPtr += depthsSurface->width();
- destPtr += width();
+ if (distIndex < frameHeight)
+ ++distYCount;
}
+ } while (++distIndex < highestDim);
+
+ destX -= distXCount / 2;
+ destY -= distYCount - 1;
+
+ // Check x bounding area
+ int spriteLeft = 0;
+ int spriteWidth = distXCount;
+ int widthAmount = destX + distXCount - 1;
+
+ if (destX < 0) {
+ spriteWidth += destX;
+ spriteLeft -= destX;
+ }
+ widthAmount -= destRight;
+ if (widthAmount > 0)
+ spriteWidth -= widthAmount;
+
+ int spriteRight = spriteLeft + spriteWidth;
+ if (spriteWidth <= 0)
+ return;
+ if (!normalFrame) {
+ destX += distXCount - 1;
+ spriteLeft = -(distXCount - spriteRight);
+ spriteRight = (-spriteLeft + spriteWidth);
+ }
+
+ // Check y bounding area
+ int spriteTop = 0;
+ int spriteHeight = distYCount;
+ int heightAmount = destY + distYCount - 1;
+
+ if (destY < 0) {
+ spriteHeight += destY;
+ spriteTop -= destY;
+ }
+ heightAmount -= destBottom;
+ if (heightAmount > 0)
+ spriteHeight -= heightAmount;
+ int spriteBottom = spriteTop + spriteHeight;
+
+ if (spriteHeight <= 0)
+ return;
+
+ byte *destPixelsP = this->getBasePtr(destX + spriteLeft, destY + spriteTop);
+ const byte *depthPixelsP = depthsSurface->getBasePtr(destX + spriteLeft, destY + spriteTop);
+
+ spriteLeft = (spriteLeft * (normalFrame ? 1 : -1));
+
+ // Loop through the lines of the sprite
+ for (int yp = 0, sprY = -1; yp < frameHeight; ++yp, srcPixelsP += src->pitch) {
+ if (!lineDist[yp])
+ // Not a display line, so skip it
+ continue;
+ // Check whether the sprite line is in the display range
+ ++sprY;
+ if ((sprY >= spriteBottom) || (sprY < spriteTop))
+ continue;
+
+ // Found a line to display. Loop through the pixels
+ const byte *srcP = srcPixelsP;
+ const byte *depthP = depthPixelsP;
+ byte *destP = destPixelsP;
+ for (int xp = 0, sprX = 0; xp < frameWidth; ++xp, ++srcP) {
+ if (xp < spriteLeft)
+ // Not yet reached start of display area
+ continue;
+ if (!lineDist[sprX++])
+ // Not a display pixel
+ continue;
+
+ if ((*srcP != transparentColour) && (depth <= *depthP))
+ *destP = *srcP;
+
+ ++destP;
+ ++depthP;
+ }
+
+ // Move to the next destination line
+ destPixelsP += this->pitch;
+ depthPixelsP += depthsSurface->pitch;
}
src->freeData();
depthsSurface->freeData();
+ this->freeData();
}
void M4Surface::loadBackgroundRiddle(const char *sceneName) {
@@ -471,8 +585,6 @@ void M4Surface::loadBackgroundRiddle(const char *sceneName) {
}
void M4Surface::loadBackground(int sceneNumber, RGBList **palData) {
- clear(); // clear previous scene
-
if (_vm->isM4() || (_vm->getGameType() == GType_RexNebular)) {
char resourceName[20];
Common::SeekableReadStream *stream;
@@ -752,7 +864,7 @@ void M4Surface::scrollX(int xAmount) {
return;
byte buffer[80];
- int direction = (xAmount > 0) ? 1 : -1;
+ int direction = (xAmount > 0) ? -1 : 1;
int xSize = ABS(xAmount);
assert(xSize <= 80);
@@ -817,15 +929,32 @@ void M4Surface::translate(RGBList *list, bool isTransparent) {
byte *palIndexes = list->palIndexes();
for (int i = 0; i < width() * height(); ++i, ++p) {
- if (!isTransparent || (*p != 0)) {
- assert(*p < list->size());
- *p = palIndexes[*p];
+ if (!isTransparent || (*p != TRANSPARENT_COLOUR_INDEX)) {
+ if (*p < list->size())
+ *p = palIndexes[*p];
+ else
+ warning("Pal index %d exceeds list size %d", *p, list->size());
}
}
freeData();
}
+M4Surface *M4Surface::flipHorizontal() const {
+ M4Surface *dest = new M4Surface(width(), height());
+ dest->_rgbList = (this->_rgbList == NULL) ? NULL : this->_rgbList->clone();
+
+ byte *destP = dest->getBasePtr();
+
+ for (int y = 0; y < height(); ++y) {
+ const byte *srcP = getBasePtr(width() - 1, y);
+ for (int x = 0; x < width(); ++x)
+ *destP++ = *srcP--;
+ }
+
+ return dest;
+}
+
//--------------------------------------------------------------------------
// Palette class
//
diff --git a/engines/m4/graphics.h b/engines/m4/graphics.h
index 8c4b9ac072..ecb5048b26 100644
--- a/engines/m4/graphics.h
+++ b/engines/m4/graphics.h
@@ -40,6 +40,7 @@ namespace M4 {
#define MADS_SCREEN_HEIGHT 200
#define MADS_Y_OFFSET ((MADS_SCREEN_HEIGHT - MADS_SURFACE_HEIGHT) / 2)
+#define TRANSPARENT_COLOUR_INDEX 0xFF
struct BGR8 {
uint8 b, g, r;
@@ -74,6 +75,7 @@ public:
int size() { return _size; }
RGB8 &operator[](int idx) { return _data[idx]; }
void setRange(int start, int count, const RGB8 *src);
+ RGBList *clone() const;
};
// M4Surface
@@ -96,6 +98,7 @@ private:
byte _color;
bool _isScreen;
RGBList *_rgbList;
+ bool _ownsData;
void rexLoadBackground(Common::SeekableReadStream *source, RGBList **palData = NULL);
void madsLoadBackground(int roomNumber, RGBList **palData = NULL);
@@ -105,12 +108,24 @@ public:
create(g_system->getWidth(), isScreen ? g_system->getHeight() : MADS_SURFACE_HEIGHT, 1);
_isScreen = isScreen;
_rgbList = NULL;
+ _ownsData = true;
}
M4Surface(int width_, int height_) {
create(width_, height_, 1);
_isScreen = false;
_rgbList = NULL;
+ _ownsData = true;
}
+ M4Surface(int width_, int height_, byte *srcPixels, int pitch_) {
+ bytesPerPixel = 1;
+ w = width_;
+ h = height_;
+ pitch = pitch_;
+ pixels = srcPixels;
+ _rgbList = NULL;
+ _ownsData = false;
+ }
+
virtual ~M4Surface();
// loads a .COD file into the M4Surface
@@ -142,6 +157,7 @@ public:
inline Common::Rect bounds() const { return Common::Rect(0, 0, width(), height()); }
inline int width() const { return w; }
inline int height() const { return h; }
+ inline int getPitch() const { return pitch; }
void setSize(int sizeX, int sizeY) { create(sizeX, sizeY, 1); }
inline byte *getBasePtr() {
return (byte *)pixels;
@@ -154,12 +170,12 @@ public:
}
void freeData();
void clear();
+ void reset();
void frameRect(const Common::Rect &r, uint8 color);
void fillRect(const Common::Rect &r, uint8 color);
- void copyFrom(M4Surface *src, const Common::Rect &srcBounds, int destX, int destY,
- int transparentColour = -1);
- void copyFrom(M4Surface *src, int destX, int destY, int depth, M4Surface *depthSurface, int scale,
- int transparentColour = -1);
+ void copyFrom(M4Surface *src, const Common::Rect &srcBounds, int destX, int destY, int transparentColour = -1);
+ void copyFrom(M4Surface *src, int destX, int destY, int depth, M4Surface *depthSurface,
+ int scale, int transparentColour = -1);
void update() {
if (_isScreen) {
@@ -188,6 +204,7 @@ public:
void scrollY(int yAmount);
void translate(RGBList *list, bool isTransparent = false);
+ M4Surface *flipHorizontal() const;
};
enum FadeType {FT_TO_GREY, FT_TO_COLOR, FT_TO_BLOCK};
diff --git a/engines/m4/m4.cpp b/engines/m4/m4.cpp
index a5db6660d8..a999a6bd5a 100644
--- a/engines/m4/m4.cpp
+++ b/engines/m4/m4.cpp
@@ -147,6 +147,8 @@ MadsM4Engine::~MadsM4Engine() {
delete _random;
delete _palette;
delete _globals;
+ delete _sound;
+ delete _driver;
delete _resourceManager;
}
@@ -154,14 +156,14 @@ Common::Error MadsM4Engine::run() {
// Initialize backend
_screen = new M4Surface(true); // Special form for creating screen reference
- MidiDriverType midiDriver = MidiDriver::detectMusicDriver(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MIDI);
- bool native_mt32 = ((midiDriver == MD_MT32) || ConfMan.getBool("native_mt32"));
+ MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
+ bool native_mt32 = ((MidiDriver::getMusicType(dev) == MT_MT32) || ConfMan.getBool("native_mt32"));
- MidiDriver *driver = MidiDriver::createMidi(midiDriver);
+ _driver = MidiDriver::createMidi(dev);
if (native_mt32)
- driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
+ _driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
- _midi = new MidiPlayer(this, driver);
+ _midi = new MidiPlayer(this, _driver);
_midi->setGM(true);
_midi->setNativeMT32(native_mt32);
@@ -512,7 +514,6 @@ Common::Error MadsEngine::run() {
// Set up needed common functionality
MadsM4Engine::run();
- _scene = new MadsScene(this);
_palette->setMadsSystemPalette();
_mouse->init("cursor.ss", NULL);
@@ -536,16 +537,20 @@ Common::Error MadsEngine::run() {
//for (int i = 0; i < _globals->getMessagesSize(); i++)
//printf("%s\n----------\n", _globals->loadMessage(i));
- if ((getGameType() == GType_RexNebular) || (getGameType() == GType_DragonSphere)) {
- loadMenu(MAIN_MENU);
+ if (getGameType() == GType_RexNebular) {
+ MadsGameLogic::initialiseGlobals();
+ _scene = NULL;
+ loadMenu(MAIN_MENU);
} else {
- if (getGameType() == GType_DragonSphere) {
- _scene->loadScene(FIRST_SCENE);
- } else if (getGameType() == GType_Phantom) {
- //_scene->loadScene(FIRST_SCENE);
- _scene->loadScene(106); // a more interesting scene
- }
+ // Test code
+ _scene = new MadsScene(this);
+
+ startScene(FIRST_SCENE);
+ RGBList *_bgPalData;
+ _scene->loadBackground(FIRST_SCENE, &_bgPalData);
+ _palette->addRange(_bgPalData);
+ _scene->translate(_bgPalData);
_scene->show();
@@ -564,15 +569,6 @@ Common::Error MadsEngine::run() {
_viewManager->systemHotkeys().add(Common::KEYCODE_ESCAPE, &escapeHotkeyHandler);
_viewManager->systemHotkeys().add(Common::KEYCODE_KP_MULTIPLY, &textviewHotkeyHandler);
- // Load the general game SFX/voices
- if (getGameType() == GType_RexNebular) {
- _sound->loadDSRFile("rex009.dsr");
- } else if (getGameType() == GType_Phantom) {
- _sound->loadDSRFile("phan009.dsr");
- } else if (getGameType() == GType_DragonSphere) {
- _sound->loadDSRFile("drag009.dsr");
- }
-
uint32 nextFrame = g_system->getMillis();
while (!_events->quitFlag) {
eventHandler();
diff --git a/engines/m4/m4.h b/engines/m4/m4.h
index 9937107668..3174c886d5 100644
--- a/engines/m4/m4.h
+++ b/engines/m4/m4.h
@@ -29,6 +29,7 @@
#include "common/scummsys.h"
#include "common/util.h"
#include "common/random.h"
+#include "sound/mididrv.h"
#include "engines/engine.h"
@@ -41,6 +42,7 @@
#include "m4/events.h"
#include "m4/font.h"
#include "m4/scene.h"
+#include "m4/mads_player.h"
#include "m4/mads_scene.h"
#include "m4/m4_scene.h"
#include "m4/actor.h"
@@ -123,7 +125,7 @@ enum {
struct M4GameDescription;
-#define GAME_FRAME_DELAY 50
+#define GAME_FRAME_DELAY 20
#define VALIDATE_MADS assert(!_vm->isM4())
@@ -144,6 +146,7 @@ protected:
void shutdown();
+ MidiDriver *_driver;
MidiPlayer *_midi;
public:
@@ -211,6 +214,7 @@ private:
public:
MadsConversation _converse;
uint32 _currentTimer;
+ MadsPlayer _player;
public:
MadsEngine(OSystem *syst, const M4GameDescription *gameDesc);
virtual ~MadsEngine();
@@ -219,6 +223,12 @@ public:
MadsGlobals *globals() { return (MadsGlobals *)_globals; }
MadsScene *scene() { return (MadsScene *)_scene; }
+ void startScene(int sceneNum) {
+ if (!_scene)
+ _scene = new MadsScene(this);
+ _scene->show();
+ _scene->loadScene(101);
+ }
};
class M4Engine : public MadsM4Engine {
diff --git a/engines/m4/mads_anim.cpp b/engines/m4/mads_anim.cpp
index e1dbbaf106..ca53bdca75 100644
--- a/engines/m4/mads_anim.cpp
+++ b/engines/m4/mads_anim.cpp
@@ -446,7 +446,7 @@ AnimviewView::AnimviewView(MadsM4Engine *vm):
MadsView::_bgSurface = &_backgroundSurface;
MadsView::_depthSurface = &_codeSurface;
- MadsView::_yOffset = MADS_Y_OFFSET;
+ MadsView::setViewport(Common::Rect(0, MADS_Y_OFFSET, MADS_SURFACE_WIDTH, MADS_Y_OFFSET + MADS_SURFACE_HEIGHT));
_screenType = VIEWID_ANIMVIEW;
_screenFlags.layer = LAYER_BACKGROUND;
@@ -458,11 +458,19 @@ AnimviewView::AnimviewView(MadsM4Engine *vm):
_previousUpdate = 0;
_transition = kTransitionNone;
_activeAnimation = NULL;
+ _bgLoadFlag = true;
+ _startFrame = -1;
+ _scriptDone = false;
+
reset();
// Set up system palette colors
_vm->_palette->setMadsSystemPalette();
+ // Block reserved palette ranges
+ _vm->_palette->blockRange(16, 2);
+ _vm->_palette->blockRange(250, 4);
+
clear();
_backgroundSurface.clear();
@@ -510,7 +518,9 @@ bool AnimviewView::onEvent(M4EventType eventType, int32 param, int x, int y, boo
}
void AnimviewView::updateState() {
- if (!_script)
+ MadsView::update();
+
+ if (!_script || _scriptDone)
return;
if (!_activeAnimation) {
@@ -524,19 +534,35 @@ void AnimviewView::updateState() {
delete _activeAnimation;
_activeAnimation = NULL;
- if (_script->eos() || _script->err()) {
+ // Clear up current background and sprites
+ _backgroundSurface.reset();
+ clearLists();
+
+ // Reset flags
+ _startFrame = -1;
+
+ readNextCommand();
+
+ // Check if script is finished
+ if (_scriptDone) {
scriptDone();
return;
}
-
- readNextCommand();
}
refresh();
}
void AnimviewView::readNextCommand() {
+static bool tempFlag = true;//****DEBUG - Temporarily allow me to skip several intro scenes ****
+
while (!_script->eos() && !_script->err()) {
+ if (!tempFlag) {
+ tempFlag = true;
+ strncpy(_currentLine, _script->readLine().c_str(), 79);
+ strncpy(_currentLine, _script->readLine().c_str(), 79);
+ }
+
strncpy(_currentLine, _script->readLine().c_str(), 79);
// Process any switches on the line
@@ -564,15 +590,24 @@ void AnimviewView::readNextCommand() {
break;
}
+ if (!_currentLine[0]) {
+ // A blank line at this point means that the end of the animation has been reached
+ _scriptDone = true;
+ return;
+ }
+
if (strchr(_currentLine, '.') == NULL)
strcat(_currentLine, ".aa");
+ uint16 flags = 0;
+ if (_bgLoadFlag)
+ flags |= 0x100;
+
_activeAnimation = new MadsAnimation(_vm, this);
- _activeAnimation->load(_currentLine, 0);
+ _activeAnimation->initialise(_currentLine, flags, &_backgroundSurface, &_codeSurface);
- _backgroundSurface.loadBackground(_activeAnimation->roomNumber());
- _codeSurface.setSize(_backgroundSurface.width(), _backgroundSurface.height());
- _codeSurface.fillRect(_codeSurface.bounds(), 0xff);
+ if (_startFrame != -1)
+ _activeAnimation->setCurrentFrame(_startFrame);
_spriteSlots.fullRefresh();
/*
@@ -627,6 +662,7 @@ return;
Switches are: (taken from the help of the original executable)
-b Toggle background load status off/on.
-c:char Specify sound card id letter.
+ -f:num Specify a specific starting frame number
-g Stay in graphics mode on exit.
-h[:ex] Disable EMS/XMS high memory support.
-i Switch sound interrupts mode off/on.
@@ -670,18 +706,39 @@ void AnimviewView::processCommand() {
str_upper(commandStr);
char *param = commandStr;
- if (!strncmp(commandStr, "X", 1)) {
- //printf("X ");
- } else if (!strncmp(commandStr, "W", 1)) {
- //printf("W ");
- } else if (!strncmp(commandStr, "R", 1)) {
- param = param + 2;
- //printf("R:%s ", param);
- } else if (!strncmp(commandStr, "O", 1)) {
+ switch (commandStr[0]) {
+ case 'B':
+ // Toggle background load flag
+ _bgLoadFlag = !_bgLoadFlag;
+ break;
+
+ case 'F':
+ // Start animation at a specific frame
+ ++param;
+ assert(*param == ':');
+ _startFrame = atoi(++param);
+ break;
+
+ case 'O':
param = param + 2;
//printf("O:%i ", atoi(param));
_transition = atoi(param);
- } else {
+ break;
+
+ case 'R':
+ param = param + 2;
+ //printf("R:%s ", param);
+ break;
+
+ case 'W':
+ //printf("W ");
+ break;
+
+ case 'X':
+ //printf("X ");
+ break;
+
+ default:
error("Unknown response command: '%s'", commandStr);
}
}
diff --git a/engines/m4/mads_anim.h b/engines/m4/mads_anim.h
index 8c4a5e6fb7..b33ea24071 100644
--- a/engines/m4/mads_anim.h
+++ b/engines/m4/mads_anim.h
@@ -81,6 +81,7 @@ class AnimviewView : public View, MadsView {
private:
char _resourceName[80];
Common::SeekableReadStream *_script;
+ bool _scriptDone;
uint32 _previousUpdate;
char _currentLine[80];
M4Surface _backgroundSurface;
@@ -90,6 +91,8 @@ private:
RGBList *_palData;
int _transition;
MadsAnimation *_activeAnimation;
+ bool _bgLoadFlag;
+ int _startFrame;
void reset();
void readNextCommand();
diff --git a/engines/m4/mads_logic.cpp b/engines/m4/mads_logic.cpp
index 9cb053a876..e451a306ef 100644
--- a/engines/m4/mads_logic.cpp
+++ b/engines/m4/mads_logic.cpp
@@ -29,6 +29,114 @@
namespace M4 {
+void MadsGameLogic::initialiseGlobals() {
+ // Clear the entire globals list
+ Common::set_to(&_madsVm->globals()->_globals[0], &_madsVm->globals()->_globals[TOTAL_NUM_VARIABLES], 0);
+
+ SET_GLOBAL(4, 8);
+ SET_GLOBAL(33, 1);
+ SET_GLOBAL(10, 0xFFFF);
+ SET_GLOBAL(13, 0xFFFF);
+ SET_GLOBAL(15, 0xFFFF);
+ SET_GLOBAL(19, 0xFFFF);
+ SET_GLOBAL(20, 0xFFFF);
+ SET_GLOBAL(21, 0xFFFF);
+ SET_GLOBAL(95, 0xFFFF);
+
+ // TODO: unknown sub call
+
+ // Put the values 0 through 3 in a random ordering in global slots 83 - 86
+ for (int idx = 0; idx < 4; ) {
+ int randVal = _madsVm->_random->getRandomNumber(4);
+ SET_GLOBAL(83 + idx, randVal);
+
+ // Check whether the given value has already been used
+ bool flag = false;
+ for (int idx2 = 0; idx2 < idx; ++idx2) {
+ if (randVal == GET_GLOBAL(83 + idx2))
+ flag = true;
+ }
+
+ if (!flag)
+ ++idx;
+ }
+
+ // Put the values 0 through 3 in a random ordering in global slots 87 - 90
+ for (int idx = 0; idx < 4; ) {
+ int randVal = _madsVm->_random->getRandomNumber(3);
+ SET_GLOBAL(87 + idx, randVal);
+
+ // Check whether the given value has already been used
+ bool flag = false;
+ for (int idx2 = 0; idx2 < idx; ++idx2) {
+ if (randVal == GET_GLOBAL(87 + idx2))
+ flag = true;
+ }
+
+ if (!flag)
+ ++idx;
+ }
+
+ // Miscellaneous global settings
+ SET_GLOBAL(120, 501);
+ SET_GLOBAL(121, 0xFFFF);
+ SET_GLOBAL(110, 0xFFFF);
+ SET_GLOBAL(119, 1);
+ SET_GLOBAL(134, 4);
+ SET_GLOBAL(190, 201);
+ SET_GLOBAL(191, 301);
+ SET_GLOBAL(192, 413);
+ SET_GLOBAL(193, 706);
+ SET_GLOBAL(194, 801);
+ SET_GLOBAL(195, 551);
+ SET_GLOBAL(196, 752);
+
+ // Fill out the globals 200 - 209 with unique random number values less than 10000
+ for (int idx = 0; idx < 10; ) {
+ int randVal = _madsVm->_random->getRandomNumber(9999);
+ SET_GLOBAL(200 + idx, randVal);
+
+ // Check whether the given value has already been used
+ bool flag = false;
+ for (int idx2 = 0; idx2 < idx; ++idx2) {
+ if (randVal == GET_GLOBAL(87 + idx2))
+ flag = true;
+ }
+
+ if (!flag)
+ ++idx;
+ }
+
+ switch (_madsVm->globals()->_difficultyLevel) {
+ case 1:
+ // Very hard
+ SET_GLOBAL(35, 0);
+ // TODO: object set room
+ SET_GLOBAL(137, 5);
+ SET_GLOBAL(136, 0);
+ break;
+
+ case 2:
+ // Hard
+ SET_GLOBAL(35, 0);
+ // TODO: object set room
+ SET_GLOBAL(136, 0xFFFF);
+ SET_GLOBAL(137, 6);
+ break;
+
+ case 3:
+ // Easy
+ SET_GLOBAL(35, 2);
+ // TODO: object set room
+ break;
+ }
+
+ _madsVm->_player._direction = 8;
+ _madsVm->_player._newDirection = 8;
+
+ // TODO: unknown processing routine getting called for 'RXM' and 'ROX'
+}
+
/*--------------------------------------------------------------------------*/
const char *MadsSceneLogic::formAnimName(char sepChar, int16 suffixNum) {
@@ -54,7 +162,7 @@ void MadsSceneLogic::getSceneSpriteSet() {
strcpy(prefix, "");
_madsVm->globals()->playerSpriteChanged = true;
- _madsVm->scene()->loadPlayerSprites(prefix);
+ _madsVm->_player.loadSprites(prefix);
// if ((_sceneNumber == 105) ((_sceneNumber == 109) && (word_84800 != 0)))
// _madsVm->globals()->playerSpriteChanged = true;
@@ -69,6 +177,10 @@ void MadsSceneLogic::getAnimName() {
strcpy(_madsVm->scene()->_aaName, newName);
}
+IntStorage &MadsSceneLogic::dataMap() {
+ return _madsVm->globals()->_dataMap;
+}
+
/*--------------------------------------------------------------------------*/
uint16 MadsSceneLogic::loadSpriteSet(uint16 suffixNum, uint16 sepChar) {
@@ -146,6 +258,59 @@ void MadsSceneLogic::lowRoomsEntrySound() {
}
}
+void MadsSceneLogic::getPlayerSpritesPrefix() {
+ _madsVm->_sound->playSound(5);
+
+ char oldName[80];
+ strcpy(oldName, _madsVm->_player._spritesPrefix);
+
+ if ((_madsVm->globals()->_nextSceneId <= 103) || (_madsVm->globals()->_nextSceneId == 111))
+ strcpy(_madsVm->_player._spritesPrefix, (_madsVm->globals()->_globals[0] == SEX_FEMALE) ? "ROX" : "RXM");
+ else if (_madsVm->globals()->_nextSceneId <= 110)
+ strcpy(_madsVm->_player._spritesPrefix, "RXSM");
+ else if (_madsVm->globals()->_nextSceneId == 112)
+ strcpy(_madsVm->_player._spritesPrefix, "");
+
+ if (strcmp(oldName, _madsVm->_player._spritesPrefix) != 0)
+ _madsVm->_player._spritesChanged = true;
+
+ if ((_madsVm->globals()->_nextSceneId == 105) ||
+ ((_madsVm->globals()->_nextSceneId == 109) && (_madsVm->globals()->_globals[15] != 0))) {
+ // TODO: unknown flag setting
+ _madsVm->_player._spritesChanged = true;
+ }
+
+ _madsVm->_palette->setEntry(16, 40, 255, 255);
+ _madsVm->_palette->setEntry(17, 40, 180, 180);
+
+}
+
+void MadsSceneLogic::getPlayerSpritesPrefix2() {
+ _madsVm->_sound->playSound(5);
+
+ char oldName[80];
+ strcpy(oldName, _madsVm->_player._spritesPrefix);
+
+ if ((_madsVm->globals()->_nextSceneId == 213) || (_madsVm->globals()->_nextSceneId == 216))
+ strcpy(_madsVm->_player._spritesPrefix, "");
+ else if (_madsVm->globals()->_globals[0] == SEX_MALE)
+ strcpy(_madsVm->_player._spritesPrefix, "RXM");
+ else
+ strcpy(_madsVm->_player._spritesPrefix, "ROX");
+
+ // TODO: unknown flag setting for next scene Id > 212
+
+ if (strcmp(oldName, _madsVm->_player._spritesPrefix) != 0)
+ _madsVm->_player._spritesChanged = true;
+
+/* if ((_madsVm->globals()->_nextSceneId == 203) && (_madsVm->globals()->_nextSceneId == 204) &&
+ (_madsVm->globals()->_globals[0x22] == 0))
+ // TODO: unknown flag set
+*/
+ _madsVm->_palette->setEntry(16, 40, 255, 255);
+ _madsVm->_palette->setEntry(17, 40, 180, 180);
+}
+
/*--------------------------------------------------------------------------*/
@@ -171,7 +336,9 @@ void MadsSceneLogic::setupScene() {
// sub_1e754(animName, 3);
if ((_sceneNumber >= 101) && (_sceneNumber <= 112))
- getSceneSpriteSet();
+ getPlayerSpritesPrefix();
+ else
+ getPlayerSpritesPrefix2();
getAnimName();
}
@@ -192,7 +359,7 @@ void MadsSceneLogic::enterScene() {
_spriteIndexes[16] = startCycledSpriteSequence(_spriteIndexes[1], 0, 4, 0, 1, 0);
_spriteIndexes[17] = startCycledSpriteSequence(_spriteIndexes[2], 0, 4, 0, 1, 0);
-// _madsVm->scene()->_sequenceList.addSubEntry(_spriteIndexes[17], SM_FRAME_INDEX, 7, 70);
+ _madsVm->scene()->_sequenceList.addSubEntry(_spriteIndexes[17], SM_FRAME_INDEX, 7, 70);
_spriteIndexes[18] = startReversibleSpriteSequence(_spriteIndexes[3], 0, 10, 0, 0, 60);
_spriteIndexes[19] = startCycledSpriteSequence(_spriteIndexes[4], 0, 5, 0, 1, 0);
@@ -208,15 +375,15 @@ void MadsSceneLogic::enterScene() {
if (_madsVm->globals()->previousScene != -1)
_madsVm->globals()->_globals[10] = 0;
if (_madsVm->globals()->previousScene != -2) {
- _madsVm->scene()->getSceneResources().playerPos = Common::Point(100, 152);
+ _madsVm->_player._playerPos = Common::Point(100, 152);
}
if ((_madsVm->globals()->previousScene == 112) ||
((_madsVm->globals()->previousScene != -2) && (_spriteIndexes[29] != 0))) {
// Returning from probe cutscene?
_spriteIndexes[29] = -1;
- _madsVm->scene()->getSceneResources().playerPos = Common::Point(161, 123);
- _madsVm->scene()->getSceneResources().playerDir = 9;
+ _madsVm->_player._playerPos = Common::Point(161, 123);
+ _madsVm->_player._direction = 9;
// TODO: Extra flags setting
_spriteIndexes[25] = startCycledSpriteSequence(_spriteIndexes[10], 0, 3, 0, 0, 0);
@@ -235,13 +402,19 @@ void MadsSceneLogic::enterScene() {
if (_madsVm->globals()->_globals[10]) {
const char *animName = MADSResourceManager::getResourceName('S', 'e', EXTTYPE_AA, NULL, -1);
- _madsVm->scene()->loadAnimation(animName, 0x47);
+ _madsVm->scene()->loadAnimation(animName, 71);
+
+ _madsVm->_player._playerPos = Common::Point(68, 140);
+ _madsVm->_player._direction = 4;
+ _madsVm->_player._visible = false;
+ _madsVm->_player._stepEnabled = false;
- _madsVm->scene()->getSceneResources().playerPos = Common::Point(68, 140);
- _madsVm->scene()->getSceneResources().playerDir = 4;
- // TODO: Flags setting
+ dataMap()[0x56FC] = 0;
+ dataMap()[0x5482] = 0;
+ dataMap()[0x5484] = 30;
}
+ _madsVm->globals()->_dataMap[0x5486] = 0;
lowRoomsEntrySound();
}
@@ -250,14 +423,66 @@ void MadsSceneLogic::doAction() {
}
void MadsSceneLogic::sceneStep() {
- // FIXME: Temporary code to display a message on-screen
- static bool tempBool = false;
- if (!tempBool) {
- tempBool = true;
-
- _madsVm->scene()->_kernelMessages.add(Common::Point(63, 100), 0x1110, 0, 0, 240,
- _madsVm->globals()->getQuote(49));
+ // TODO: Sound handling
+
+ switch (_madsVm->scene()->_abortTimers) {
+ case 70:
+ _madsVm->_sound->playSound(9);
+ break;
+ case 71:
+ _madsVm->globals()->_globals[10] = 0;
+ _madsVm->_player._visible = true;
+ _madsVm->_player._stepEnabled = true;
+
+ _madsVm->_player._priorTimer = _madsVm->_currentTimer - _madsVm->_player._ticksAmount;
+ break;
+ case 72:
+ case 73:
+ // TODO: Method that should be scripted
+ break;
+
+ default:
+ break;
}
+
+ // Wake up message sequence
+ Animation *anim = _madsVm->scene()->activeAnimation();
+ if (anim) {
+ if ((anim->getCurrentFrame() == 6) && (dataMap()[0x5482] == 0)) {
+ dataMap()[0x5482]++;
+ _madsVm->scene()->_kernelMessages.add(Common::Point(63, dataMap()[0x5484]),
+ 0x1110, 0, 0, 240, _madsVm->globals()->getQuote(49));
+ dataMap()[0x5484] += 14;
+ }
+
+ if ((anim->getCurrentFrame() == 7) && (dataMap()[0x5482] == 1)) {
+ dataMap()[0x5482]++;
+ _madsVm->scene()->_kernelMessages.add(Common::Point(63, dataMap()[0x5484]),
+ 0x1110, 0, 0, 240, _madsVm->globals()->getQuote(54));
+ dataMap()[0x5484] += 14;
+ }
+
+ if ((anim->getCurrentFrame() == 10) && (dataMap()[0x5482] == 2)) {
+ dataMap()[0x5482]++;
+ _madsVm->scene()->_kernelMessages.add(Common::Point(63, dataMap()[0x5484]),
+ 0x1110, 0, 0, 240, _madsVm->globals()->getQuote(55));
+ dataMap()[0x5484] += 14;
+ }
+
+ if ((anim->getCurrentFrame() == 17) && (dataMap()[0x5482] == 3)) {
+ dataMap()[0x5482]++;
+ _madsVm->scene()->_kernelMessages.add(Common::Point(63, dataMap()[0x5484]),
+ 0x1110, 0, 0, 240, _madsVm->globals()->getQuote(56));
+ dataMap()[0x5484] += 14;
+ }
+
+ if ((anim->getCurrentFrame() == 20) && (dataMap()[0x5482] == 4)) {
+ dataMap()[0x5482]++;
+ _madsVm->scene()->_kernelMessages.add(Common::Point(63, dataMap()[0x5484]),
+ 0x1110, 0, 0, 240, _madsVm->globals()->getQuote(50));
+ dataMap()[0x5484] += 14;
+ }
+ }
}
}
diff --git a/engines/m4/mads_logic.h b/engines/m4/mads_logic.h
index 8c3f41d08b..98d6df6163 100644
--- a/engines/m4/mads_logic.h
+++ b/engines/m4/mads_logic.h
@@ -42,6 +42,8 @@ private:
uint16 startSpriteSequence3(uint16 srcSpriteIdx, int v0, int numTicks, int triggerCountdown, int timeoutTicks, int extraTicks);
void activateHotspot(int idx, bool active);
void lowRoomsEntrySound();
+ void getPlayerSpritesPrefix();
+ void getPlayerSpritesPrefix2();
private:
int _sceneNumber;
int16 _spriteIndexes[50];
@@ -50,6 +52,8 @@ private:
const char *formAnimName(char sepChar, int16 suffixNum);
void getSceneSpriteSet();
void getAnimName();
+
+ IntStorage &dataMap();
public:
void selectScene(int sceneNum);
@@ -59,6 +63,11 @@ public:
void sceneStep();
};
+class MadsGameLogic {
+public:
+ static void initialiseGlobals();
+};
+
}
#endif
diff --git a/engines/m4/mads_menus.cpp b/engines/m4/mads_menus.cpp
index 94894e78be..d7d9cf4150 100644
--- a/engines/m4/mads_menus.cpp
+++ b/engines/m4/mads_menus.cpp
@@ -163,7 +163,7 @@ bool RexMainMenuView::onEvent(M4EventType eventType, int32 param, int x, int y,
if (_highlightedIndex != -1) {
M4Sprite *spr = _menuItem->getFrame(_highlightedIndex);
const Common::Point &pt = _menuItemPosList[_highlightedIndex];
- spr->copyTo(this, pt.x, row + pt.y, 0);
+ spr->copyTo(this, pt.x, row + pt.y, spr->getTransparencyIndex());
}
}
} else {
@@ -211,10 +211,12 @@ void RexMainMenuView::updateState() {
M4Sprite *spr = _menuItem->getFrame(0);
itemSize = _menuItem->getFrame(0)->height();
spr->copyTo(this, _menuItemPosList[_menuItemIndex - 1].x,
- _menuItemPosList[_menuItemIndex - 1].y + row + (itemSize / 2) - (spr->height() / 2), 0);
+ _menuItemPosList[_menuItemIndex - 1].y + row + (itemSize / 2) - (spr->height() / 2),
+ spr->getTransparencyIndex());
delete _menuItem;
- copyTo(_bgSurface, Common::Rect(0, row, width(), row + MADS_SURFACE_HEIGHT), 0, 0);
+ copyTo(_bgSurface, Common::Rect(0, row, width(), row + MADS_SURFACE_HEIGHT), 0, 0,
+ spr->getTransparencyIndex());
}
// Get the next sprite set
@@ -275,7 +277,7 @@ void RexMainMenuView::updateState() {
_bgSurface->copyTo(this, 0, row);
M4Sprite *spr = _menuItem->getFrame(_frameIndex);
spr->copyTo(this, _menuItemPosList[_menuItemIndex - 1].x, _menuItemPosList[_menuItemIndex - 1].y +
- row + (itemSize / 2) - (spr->height() / 2), 0);
+ row + (itemSize / 2) - (spr->height() / 2), spr->getTransparencyIndex());
}
int RexMainMenuView::getHighlightedItem(int x, int y) {
@@ -293,7 +295,7 @@ int RexMainMenuView::getHighlightedItem(int x, int y) {
}
void RexMainMenuView::handleAction(MadsGameAction action) {
- MadsM4Engine *vm = _vm;
+ MadsEngine *vm = (MadsEngine *)_vm;
vm->_mouse->cursorOff();
vm->_viewManager->deleteView(this);
@@ -303,8 +305,7 @@ void RexMainMenuView::handleAction(MadsGameAction action) {
// Load a sample starting scene - note that, currently, calling loadScene automatically
// removes this menu screen from being displayed
vm->_mouse->cursorOn();
- vm->_scene->show();
- vm->_scene->loadScene(101);
+ vm->startScene(101);
return;
case SHOW_INTRO:
@@ -325,7 +326,7 @@ void RexMainMenuView::handleAction(MadsGameAction action) {
// Activate the scene display with the specified scene
bool altAdvert = vm->_random->getRandomNumber(1000) >= 500;
- vm->_scene->loadScene(altAdvert ? 995 : 996);
+ vm->startScene(altAdvert ? 995 : 996);
vm->_viewManager->addView(vm->_scene);
vm->_viewManager->refreshAll();
@@ -532,7 +533,7 @@ void DragonMainMenuView::updateState() {
_itemPalData.push_back(palData);
spr = _menuItem->getFrame(1);
- spr->copyTo(this, spr->xOffset - 140, spr->yOffset - spr->height(), (int)spr->getTransparentColor());
+ spr->copyTo(this, spr->xOffset - 140, spr->yOffset - spr->height(), spr->getTransparencyIndex());
_vm->_mouse->cursorOn();
}
diff --git a/engines/m4/mads_player.cpp b/engines/m4/mads_player.cpp
new file mode 100644
index 0000000000..8531a3ed44
--- /dev/null
+++ b/engines/m4/mads_player.cpp
@@ -0,0 +1,454 @@
+/* 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 "m4/m4.h"
+#include "m4/mads_player.h"
+#include "m4/mads_scene.h"
+
+namespace M4 {
+
+const int MadsPlayer::_directionListIndexes[32] = {
+ 0, 7, 4, 3, 6, 0, 2, 5, 0, 1, 9, 4, 1, 2, 7, 9, 3, 8, 9, 6, 7, 2, 3, 6, 1, 7, 9, 4, 7, 8, 0, 0
+};
+
+MadsPlayer::MadsPlayer() {
+ _playerPos = Common::Point(160, 78);
+ _direction = 0;
+ _newDirection = 0;
+ _forceRefresh = true;
+ _stepEnabled = true;
+ _ticksAmount = 3;
+ _priorTimer = 0;
+ _visible = true;
+ _priorVisible = false;
+ _visible3 = false;
+ _yScale = 0;
+ _moving = false;
+ _spriteListStart = 0;
+ _spriteListIdx = 0;
+ _spritesChanged = true;
+ _currentScale = 0;
+ strcpy(_spritesPrefix, "");
+ for (int idx = 0; idx < 8; ++idx)
+ _spriteSetsPresent[idx] = false;
+ _frameNum = 0;
+ _frameOffset = 0;
+ _unk1 = 0;
+ _frameCount = 0;
+ _frameListIndex = 0;
+ _actionIndex = 0;
+ resetActionList();
+}
+
+/**
+ * Loads the sprite set for the player
+ */
+bool MadsPlayer::loadSprites(const char *prefix) {
+ const char suffixList[8] = { '8', '9', '6', '3', '2', '7', '4', '1' };
+ char setName[80];
+ bool result = true;
+
+ if (prefix)
+ strcpy(_spritesPrefix, prefix);
+
+ _spriteSetCount = 0;
+ int prefixLen = strlen(_spritesPrefix);
+
+ if (prefixLen == 0) {
+ // No player sprites at at all
+ for (int idx = 0; idx < 8; ++idx)
+ _spriteSetsPresent[idx] = false;
+ } else {
+ strcpy(setName, "*");
+ strcat(setName, _spritesPrefix);
+ strcat(setName, "_0.SS");
+
+ char *digitP = strchr(setName, '_') + 1;
+
+ for (int idx = 0; idx < 8; ++idx) {
+ *digitP = suffixList[idx];
+ _spriteSetsPresent[idx] = true;
+
+ int setIndex = _madsVm->scene()->_spriteSlots.addSprites(setName, true, SPRITE_SET_CHAR_INFO);
+ if (setIndex < 0) {
+ if (idx < 5)
+ break;
+ _spriteSetsPresent[idx] = false;
+ } else {
+ ++_spriteSetCount;
+ }
+
+ if (idx == 0)
+ _spriteListStart = setIndex;
+ }
+
+ result = 0;
+ // TODO: Unknown flag
+ _spritesChanged = false;
+ }
+
+ return result;
+}
+
+/**
+ * Called each frame to update the display of the player
+ */
+void MadsPlayer::update() {
+ if (_forceRefresh || (_visible != _priorVisible)) {
+ // If there's an existing player sprite visible, flag it for expiry
+ int slotIndex = getSpriteSlot();
+ if (slotIndex >= 0)
+ _madsVm->scene()->_spriteSlots[slotIndex].spriteType = EXPIRED_SPRITE;
+
+ // Figure out the depth for the sprite
+ int newDepth = 1;
+ int yp = MIN(_playerPos.y, (int16)155);
+
+ for (int idx = 1; idx < 15; ++idx) {
+ if (_madsVm->scene()->getSceneResources().depthTable[newDepth] >= yp)
+ newDepth = idx + 1;
+ }
+ _currentDepth = newDepth;
+
+ // Get the scale
+ int newScale = getScale(_playerPos.y);
+ _currentScale = MIN(newScale, 100);
+
+ if (_visible) {
+ // Player sprite needs to be rendered
+ MadsSpriteSlot slot;
+ slot.spriteType = FOREGROUND_SPRITE;
+ slot.seqIndex = PLAYER_SEQ_INDEX;
+ slot.spriteListIndex = _spriteListStart + _spriteListIdx;
+ slot.frameNumber = _frameOffset + _frameNum;
+ slot.xp = _playerPos.x;
+ slot.yp = _playerPos.y + (_yScale * newScale) / 100;
+ slot.depth = newDepth;
+ slot.scale = newScale;
+
+ if (slotIndex >= 0) {
+ // Check if the existing player slot has the same details, and can be re-used
+ MadsSpriteSlot &s2 = _madsVm->scene()->_spriteSlots[slotIndex];
+ bool equal = (s2.seqIndex == slot.seqIndex) && (s2.spriteListIndex == slot.spriteListIndex)
+ && (s2.frameNumber == slot.frameNumber) && (s2.xp == slot.xp) && (s2.yp == slot.yp)
+ && (s2.depth == slot.depth) && (s2.scale == slot.scale);
+
+ if (equal)
+ // Undo the prior expiry of the player sprite
+ s2.spriteType = SPRITE_ZERO;
+ else
+ slotIndex = -1;
+ }
+
+ if (slotIndex < 0) {
+ // New slot needed, so allocate one and copy the slot data
+ slotIndex = _madsVm->scene()->_spriteSlots.getIndex();
+ _madsVm->scene()->_spriteSlots[slotIndex] = slot;
+ }
+
+ // TODO: Meaning of word_844c0 block
+
+ }
+ }
+
+ _visible3 = _priorVisible = _visible;
+ _forceRefresh = false;
+}
+
+/**
+ * Updates the animation frame for the player
+ */
+void MadsPlayer::updateFrame() {
+ SpriteAsset &spriteSet = _madsVm->scene()->_spriteSlots.getSprite(_spriteListStart + _spriteListIdx);
+ assert(spriteSet._charInfo);
+
+ if (!spriteSet._charInfo->_numEntries) {
+ _frameNum = 1;
+ } else {
+ _frameListIndex = _actionList[_actionIndex];
+
+ if (!_visible) {
+ _unk2 = 0;
+ } else {
+ _unk2 = _actionList2[_actionIndex];
+
+ if (_actionIndex > 0)
+ --_actionIndex;
+ }
+
+ // Set the player frame number
+ int frameIndex = ABS(_frameListIndex);
+ _frameNum = (_frameListIndex <= 0) ? spriteSet._charInfo->_frameList[frameIndex] :
+ spriteSet._charInfo->_frameList2[frameIndex];
+
+ // Set next waiting period in ticks
+ if (frameIndex == 0)
+ setTicksAmount();
+ else
+ _madsVm->_player._ticksAmount = spriteSet._charInfo->_ticksList[frameIndex];
+ }
+}
+
+void MadsPlayer::setupFrame() {
+ resetActionList();
+ _frameOffset = 0;
+ _spriteListIdx = _directionListIndexes[_direction];
+ if (!_spriteSetsPresent[_spriteListIdx]) {
+ // Direction isn't present, so use alternate direction, with entries flipped
+ _spriteListIdx -= 4;
+ _frameOffset = 0x8000;
+ }
+
+ SpriteAsset &spriteSet = _madsVm->scene()->_spriteSlots.getSprite(_spriteListStart + _spriteListIdx);
+ assert(spriteSet._charInfo);
+ _unk1 = MAX(spriteSet._charInfo->_unk1, 100);
+ setTicksAmount();
+
+ _frameCount = spriteSet._charInfo->_totalFrames;
+ if (_frameCount == 0)
+ _frameCount = spriteSet.getCount();
+
+ _yScale = spriteSet._charInfo->_yScale;
+
+ if ((_frameNum <= 0) || (_frameNum > _frameCount))
+ _frameNum = 1;
+ _forceRefresh = true;
+}
+
+void MadsPlayer::step() {
+ if (_visible && _stepEnabled && !_moving && (_direction == _newDirection) && (_madsVm->_currentTimer >= GET_GLOBAL32(2))) {
+ if (_actionIndex == 0) {
+ int randVal = _vm->_random->getRandomNumber(29999);
+
+ if (GET_GLOBAL(0) == SEX_MALE) {
+ switch (_direction) {
+ case 1:
+ case 3:
+ case 7:
+ case 9:
+ if (randVal < 200) {
+ queueAction(-1, 0);
+ queueAction(1, 0);
+ }
+ break;
+
+ case 2:
+ if (randVal < 500) {
+ for (int i = 0; i < 10; ++i)
+ queueAction((randVal < 250) ? 1 : 2, 0);
+ } else if (randVal < 750) {
+ for (int i = 0; i < 5; ++i)
+ queueAction(1, 0);
+ queueAction(0, 0);
+ for (int i = 0; i < 5; ++i)
+ queueAction(2, 0);
+ }
+ break;
+
+ case 4:
+ case 6:
+ if (randVal < 500) {
+ for (int i = 0; i < 10; ++i)
+ queueAction(1, 0);
+ }
+ break;
+
+ case 5:
+ case 8:
+ if (randVal < 200) {
+ queueAction(-1, 0);
+ queueAction(1, 0);
+ }
+ break;
+ }
+ }
+ }
+
+ SET_GLOBAL32(2, GET_GLOBAL32(2) + 6);
+ }
+
+ if (GET_GLOBAL(138) == 1) {
+ uint32 diff = _madsVm->_currentTimer - GET_GLOBAL32(142);
+ if (diff > 60) {
+ SET_GLOBAL32(144, GET_GLOBAL32(144) + 1);
+ } else {
+ SET_GLOBAL32(144, GET_GLOBAL32(144) + diff);
+ }
+
+ SET_GLOBAL32(142, _madsVm->_currentTimer);
+ }
+}
+
+void MadsPlayer::nextFrame() {
+ if (_madsVm->_currentTimer >= (_priorTimer + _ticksAmount)) {
+ _priorTimer = _madsVm->_currentTimer;
+
+ if (_moving)
+ move();
+ else
+ idle();
+
+ // Post update logic
+ if (_moving) {
+ ++_frameNum;
+ if (_frameNum > _frameCount)
+ _frameNum = 1;
+ _forceRefresh = true;
+ } else if (!_forceRefresh) {
+ idle();
+ }
+
+ // Final update
+ update();
+ }
+}
+
+int MadsPlayer::getScale(int yp) {
+ MadsSceneResources &r = _madsVm->scene()->getSceneResources();
+
+ int scale = (r.bandsRange() == 0) ? r._maxScale : (yp - r._yBandsStart) * r.scaleRange() / r.bandsRange()
+ + r._minScale;
+
+ return MIN(scale, 100);
+}
+
+/**
+ * Scans through the scene's sprite slot list to find any sprite displaying the player
+ */
+int MadsPlayer::getSpriteSlot() {
+ MadsSpriteSlots &slots = _madsVm->scene()->_spriteSlots;
+ for (int i = 0; i < slots.startIndex; ++i) {
+ if ((slots[i].seqIndex == PLAYER_SEQ_INDEX) && (slots[i].spriteType >= SPRITE_ZERO))
+ return i;
+ }
+ return -1;
+}
+
+void MadsPlayer::setTicksAmount() {
+ SpriteAsset &spriteSet = _madsVm->scene()->_spriteSlots.getSprite(_spriteListStart + _spriteListIdx);
+ assert(spriteSet._charInfo);
+ _madsVm->_player._ticksAmount = spriteSet._charInfo->_ticksAmount;
+ if (_madsVm->_player._ticksAmount == 0)
+ _madsVm->_player._ticksAmount = 6;
+}
+
+void MadsPlayer::resetActionList() {
+ _actionList[0] = 0;
+ _actionList2[0] = 0;
+ _actionIndex = 0;
+ _unk2 = 0;
+ _unk3 = 0;
+}
+
+int MadsPlayer::queueAction(int action1, int action2) {
+ SpriteAsset &spriteSet = _madsVm->scene()->_spriteSlots.getSprite(_spriteListStart + _spriteListIdx);
+ assert(spriteSet._charInfo);
+
+ if ((action1 < spriteSet._charInfo->_numEntries) && (_actionIndex < 11)) {
+ ++_actionIndex;
+ _actionList[_actionIndex] = action1;
+ _actionList2[_actionIndex] = action2;
+ return false;
+ }
+
+ return true;
+}
+
+void MadsPlayer::idle() {
+ if (_direction != _newDirection) {
+ // The direction has changed, so reset for new direction
+ dirChanged();
+ return;
+ }
+
+ SpriteAsset &spriteSet = _madsVm->scene()->_spriteSlots.getSprite(_spriteListStart + _spriteListIdx);
+ assert(spriteSet._charInfo);
+ if (spriteSet._charInfo->_numEntries == 0)
+ // No entries, so exit immediately
+ return;
+
+ int frameIndex = ABS(_frameListIndex);
+ int direction = (_frameListIndex < 0) ? -1 : 1;
+
+ if (frameIndex >= spriteSet._charInfo->_numEntries)
+ // Reset back to the start of the list
+ _frameListIndex = 0;
+ else {
+ _frameNum += direction;
+ _forceRefresh = true;
+
+ if (spriteSet._charInfo->_frameList2[frameIndex] < _frameNum) {
+ _unk3 = _unk2;
+ updateFrame();
+ }
+ if (spriteSet._charInfo->_frameList[frameIndex] < _frameNum) {
+ _unk3 = _unk2;
+ updateFrame();
+ }
+ }
+}
+
+void MadsPlayer::move() {
+ // TODO: Handle player movement
+}
+
+void MadsPlayer::dirChanged() {
+ int dirIndex = 0, dirIndex2 = 0;
+ int newDir = 0, newDir2 = 0;
+
+ if (_direction != _newDirection) {
+ // Find the index for the given direction in the player direction list
+ int tempDir = _direction;
+ do {
+ ++dirIndex;
+ newDir += tempDir;
+ tempDir = _directionListIndexes[tempDir + 10];
+ } while (tempDir != _newDirection);
+ }
+
+
+ if (_direction != _newDirection) {
+ // Find the index for the given direction in the player direction list
+ int tempDir = _direction;
+ do {
+ ++dirIndex2;
+ newDir2 += tempDir;
+ tempDir = _directionListIndexes[tempDir + 20];
+ } while (tempDir != _newDirection);
+ }
+
+ int diff = dirIndex - dirIndex2;
+ if (diff == 0)
+ diff = newDir - newDir2;
+
+ _direction = (diff >= 0) ? _directionListIndexes[_direction + 20] : _directionListIndexes[_direction + 10];
+ setupFrame();
+ if ((_direction == _newDirection) && !_moving)
+ updateFrame();
+
+ _priorTimer += 1;
+}
+
+} // End of namespace M4
diff --git a/engines/m4/mads_player.h b/engines/m4/mads_player.h
new file mode 100644
index 0000000000..65ed9ef89c
--- /dev/null
+++ b/engines/m4/mads_player.h
@@ -0,0 +1,88 @@
+/* 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 M4_MADS_PLAYER_H
+#define M4_MADS_PLAYER_H
+
+#include "common/scummsys.h"
+
+namespace M4 {
+
+#define PLAYER_SEQ_INDEX -2
+
+class MadsPlayer {
+private:
+ int getScale(int yp);
+ int getSpriteSlot();
+ void setTicksAmount();
+ void resetActionList();
+ int queueAction(int v0, int v1);
+ void idle();
+ void move();
+ void dirChanged();
+public:
+ char _spritesPrefix[16];
+ int _spriteSetCount;
+ bool _spriteSetsPresent[8];
+ Common::Point _playerPos;
+ Common::Point _destPos;
+ uint32 _priorTimer;
+ uint _ticksAmount;
+ int16 _direction, _newDirection;
+ bool _stepEnabled;
+ bool _visible, _priorVisible;
+ bool _visible3;
+ bool _forceRefresh;
+ int16 _currentScale;
+ int16 _yScale;
+ int16 _currentDepth;
+ int16 _spriteListStart, _spriteListIdx;
+ bool _spritesChanged;
+ uint16 _frameOffset, _frameNum;
+ bool _moving;
+ int _unk1;
+ int _frameCount;
+ int _frameListIndex;
+ int _actionIndex;
+ int _actionList[12];
+ int _actionList2[12];
+ int _unk2;
+ int _unk3;
+
+ static const int _directionListIndexes[32];
+public:
+ MadsPlayer();
+
+ bool loadSprites(const char *prefix);
+ void update();
+ void updateFrame();
+ void setupFrame();
+ void step();
+ void nextFrame();
+};
+
+} // End of namespace M4
+
+#endif
diff --git a/engines/m4/mads_scene.cpp b/engines/m4/mads_scene.cpp
index a65224c722..e4f84aeb5a 100644
--- a/engines/m4/mads_scene.cpp
+++ b/engines/m4/mads_scene.cpp
@@ -55,8 +55,6 @@ MadsScene::MadsScene(MadsEngine *vm): _sceneResources(), Scene(vm, &_sceneResour
MadsView::_bgSurface = Scene::_backgroundSurface;
MadsView::_depthSurface = Scene::_walkSurface;
_interfaceSurface = new MadsInterfaceView(vm);
- for (int i = 0; i < 3; ++i)
- actionNouns[i] = 0;
}
MadsScene::~MadsScene() {
@@ -69,8 +67,10 @@ MadsScene::~MadsScene() {
/**
* Secondary scene loading code
*/
-void MadsScene::loadScene2(const char *aaName) {
+void MadsScene::loadScene2(const char *aaName, int sceneNumber) {
// TODO: Completely finish
+ _madsVm->globals()->previousScene = _madsVm->globals()->sceneNumber;
+ _madsVm->globals()->sceneNumber = sceneNumber;
_spriteSlots.clear();
_sequenceList.clear();
@@ -117,25 +117,57 @@ void MadsScene::loadScene(int sceneNumber) {
// Handle common scene setting
Scene::loadScene(sceneNumber);
-
- _madsVm->globals()->previousScene = _madsVm->globals()->sceneNumber;
- _madsVm->globals()->sceneNumber = sceneNumber;
+ _madsVm->globals()->_nextSceneId = sceneNumber;
// Existing ScummVM code that needs to be eventually replaced with MADS code
loadSceneTemporary();
+ _madsVm->_player._spritesChanged = true;
+ _madsVm->globals()->clearQuotes();
+ _dynamicHotspots.reset();
+
// Signal the script engine what scene is to be active
_sceneLogic.selectScene(sceneNumber);
- _sceneLogic.setupScene();
// Add the scene if necessary to the list of scenes that have been visited
_vm->globals()->addVisitedScene(sceneNumber);
+ if (_vm->getGameType() == GType_RexNebular)
+ _sceneLogic.setupScene();
+
+ // TODO: Unknown code
+
// Secondary scene load routine
- loadScene2("*I0.AA");
+ if (_vm->getGameType() == GType_RexNebular)
+ // Secondary scene load routine
+ loadScene2("*I0.AA", sceneNumber);
+
+ _madsVm->_player.loadSprites(NULL);
+
+ switch (_madsVm->globals()->_config.screenFades) {
+ case 0:
+ _abortTimers2 = 2;
+ break;
+ case 2:
+ _abortTimers2 = 21;
+ break;
+ default:
+ _abortTimers2 = 20;
+ break;
+ }
+ _abortTimers = 0;
+ _abortTimersMode2 = ABORTMODE_1;
+
// Do any scene specific setup
- _sceneLogic.enterScene();
+ if (_vm->getGameType() == GType_RexNebular)
+ _sceneLogic.enterScene();
+
+ // Miscellaneous player setup
+ _madsVm->_player._destPos = _madsVm->_player._destPos;
+ _madsVm->_player._newDirection = _madsVm->_player._direction;
+ _madsVm->_player.setupFrame();
+ _madsVm->_player.updateFrame();
// Purge resources
_vm->res()->purge();
@@ -236,12 +268,14 @@ void MadsScene::leftClick(int x, int y) {
}
void MadsScene::rightClick(int x, int y) {
- // ***DEBUG*** - sample dialog display
- int idx = 3; //_madsVm->_globals->messageIndexOf(0x277a);
- const char *msg = _madsVm->globals()->loadMessage(idx);
- Dialog *dlg = new Dialog(_vm, msg, "TEST DIALOG");
- _vm->_viewManager->addView(dlg);
- _vm->_viewManager->moveToFront(dlg);
+ if (_vm->getGameType() == GType_RexNebular) {
+ // ***DEBUG*** - sample dialog display
+ int idx = 3; //_madsVm->_globals->messageIndexOf(0x277a);
+ const char *msg = _madsVm->globals()->loadMessage(idx);
+ Dialog *dlg = new Dialog(_vm, msg, "TEST DIALOG");
+ _vm->_viewManager->addView(dlg);
+ _vm->_viewManager->moveToFront(dlg);
+ }
}
void MadsScene::setAction(int action, int objectId) {
@@ -298,9 +332,20 @@ void MadsScene::update() {
}
void MadsScene::updateState() {
+ _madsVm->_player.update();
+
+ // Step through the scene
_sceneLogic.sceneStep();
- _sequenceList.tick();
+ _madsVm->_player.step();
+ _madsVm->_player._unk3 = 0;
+
+ if (_abortTimersMode == ABORTMODE_1)
+ _abortTimers = 0;
+
+ // Handle updating the player frame
+ _madsVm->_player.nextFrame();
+
if ((_activeAnimation) && !_abortTimers) {
_activeAnimation->update();
if (((MadsAnimation *) _activeAnimation)->freeFlag()) {
@@ -309,9 +354,37 @@ void MadsScene::updateState() {
}
}
- _kernelMessages.update();
+ MadsView::update();
+
+ // Remove the animation if it's been completed
+ if ((_activeAnimation) && ((MadsAnimation *)_activeAnimation)->freeFlag())
+ freeAnimation();
}
+/**
+ * Does extra work at cleaning up the animation, and then deletes it
+ */
+void MadsScene::freeAnimation() {
+ if (!_activeAnimation)
+ return;
+
+ MadsAnimation *anim = (MadsAnimation *)_activeAnimation;
+ if (anim->freeFlag()) {
+ _madsVm->scene()->_spriteSlots.clear();
+ _madsVm->scene()->_spriteSlots.fullRefresh();
+ _madsVm->scene()->_sequenceList.scan();
+ }
+
+ if (_madsVm->_player._visible) {
+ _madsVm->_player._forceRefresh = true;
+ _madsVm->_player.update();
+ }
+
+ delete _activeAnimation;
+ _activeAnimation = NULL;
+}
+
+
int MadsScene::loadSceneSpriteSet(const char *setName) {
char resName[100];
strcpy(resName, setName);
@@ -323,27 +396,6 @@ int MadsScene::loadSceneSpriteSet(const char *setName) {
return _spriteSlots.addSprites(resName);
}
-void MadsScene::loadPlayerSprites(const char *prefix) {
- const char suffixList[8] = { '8', '9', '6', '3', '2', '7', '4', '1' };
- char setName[80];
-
- strcpy(setName, "*");
- strcat(setName, prefix);
- strcat(setName, "_0.SS");
- char *digitP = strchr(setName, '_') + 1;
-
- for (int idx = 0; idx < 8; ++idx) {
- *digitP = suffixList[idx];
-
- if (_vm->res()->resourceExists(setName)) {
- loadSceneSpriteSet(setName);
- return;
- }
- }
-
- error("Couldn't find player sprites");
-}
-
enum boxSprites {
topLeft = 0,
topRight = 1,
@@ -442,12 +494,12 @@ 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) {
+void MadsScene::loadAnimation(const Common::String &animName, int abortTimers) {
if (_activeAnimation)
error("Multiple active animations are not allowed");
MadsAnimation *anim = new MadsAnimation(_vm, this);
- anim->load(animName.c_str(), 0);
+ anim->load(animName.c_str(), abortTimers);
_activeAnimation = anim;
}
@@ -637,7 +689,7 @@ void MadsSceneResources::load(int sceneNumber, const char *resName, int v0, M4Su
if (sceneNumber > 0) {
sceneName = MADSResourceManager::getResourceName(RESPREFIX_RM, sceneNumber, ".DAT");
} else {
- strcat(buffer1, "*");
+ strcpy(buffer1, "*");
strcat(buffer1, resName);
sceneName = buffer1; // TODO: Check whether this needs to be converted to 'HAG form'
}
@@ -649,25 +701,41 @@ void MadsSceneResources::load(int sceneNumber, const char *resName, int v0, M4Su
// Basic scene info
Common::SeekableReadStream *stream = sceneInfo.getItemStream(0);
- int resSceneId = stream->readUint16LE();
- assert(resSceneId == sceneNumber);
- artFileNum = stream->readUint16LE();
- drawStyle = stream->readUint16LE();
- width = stream->readUint16LE();
- height = stream->readUint16LE();
- assert((width == 320) && (height == 156));
+ if (_vm->getGameType() == GType_RexNebular) {
+ int resSceneId = stream->readUint16LE();
+ assert(resSceneId == sceneNumber);
+ } else {
+ char roomFilename[10];
+ char roomFilenameExpected[10];
+ sprintf(roomFilenameExpected, "*RM%d", sceneNumber);
+
+ stream->read(roomFilename, 6);
+ roomFilename[6] = 0;
+ assert(!strcmp(roomFilename, roomFilenameExpected));
+ }
+
+ // TODO: The following is wrong for Phantom/Dragon
+ _artFileNum = stream->readUint16LE();
+ _depthStyle = stream->readUint16LE();
+ _width = stream->readUint16LE();
+ _height = stream->readUint16LE();
stream->skip(24);
int objectCount = stream->readUint16LE();
-
- stream->skip(40);
+ _yBandsEnd = stream->readUint16LE();
+ _yBandsStart = stream->readUint16LE();
+ _maxScale = stream->readSint16LE();
+ _minScale = stream->readSint16LE();
+ for (int i = 0; i < DEPTH_BANDS_SIZE; ++i)
+ _depthBands[i] = stream->readUint16LE();
+ stream->skip(2);
// Load in any scene objects
for (int i = 0; i < objectCount; ++i) {
MadsObject rec;
rec.load(stream);
- objects.push_back(rec);
+ _objects.push_back(rec);
}
for (int i = 0; i < 20 - objectCount; ++i)
stream->skip(48);
@@ -677,25 +745,25 @@ void MadsSceneResources::load(int sceneNumber, const char *resName, int v0, M4Su
for (int i = 0; i < setCount; ++i) {
char buffer2[64];
Common::String s(buffer2, 64);
- setNames.push_back(s);
+ _setNames.push_back(s);
}
-
+
+ delete stream;
+
// 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);
+ surface = new M4Surface(_width, _height);
ssFlag = true;
- }
- int walkSize = gfxSize;
- if (drawStyle == 2) {
- width >>= 2;
- walkSize = width * height;
- }
+ } else if ((_width != surface->width()) || (_height != surface->height()))
+ surface->setSize(_width, _height);
+
if (!depthSurface) {
- depthSurface = new M4Surface(width, height);
+ depthSurface = new M4Surface(_width, _height);
dsFlag = true;
- }
+ } else if ((_width != depthSurface->width()) || (_height != depthSurface->height()))
+ depthSurface->setSize(_width, _height);
+
// For Rex Nebular, read in the scene's compressed walk surface information
if (_vm->getGameType() == GType_RexNebular) {
@@ -708,19 +776,32 @@ void MadsSceneResources::load(int sceneNumber, const char *resName, int v0, M4Su
byte *destP = depthSurface->getBasePtr(0, 0);
const byte *srcP = walkData;
byte runLength;
+
+ // Run length encoded depth data
while ((runLength = *srcP++) != 0) {
- Common::set_to(destP, destP + runLength, *srcP++);
- destP += runLength;
+ if (_depthStyle == 2) {
+ // 2-bit depth pixels
+ byte byteVal = *srcP++;
+ for (int byteCtr = 0; byteCtr < runLength; ++byteCtr) {
+ byte v = byteVal;
+ for (int bitCtr = 0; bitCtr < 4; ++bitCtr, v >>= 2)
+ *destP++ = (((v & 1) + 1) << 3) - 1;
+ }
+ } else {
+ // 8-bit depth pixels
+ Common::set_to(destP, destP + runLength, *srcP++);
+ destP += runLength;
+ }
}
- delete walkData;
+ free(walkData);
delete stream;
}
_vm->_resourceManager->toss(sceneName);
// Load the surface artwork
- surface->loadBackground(sceneNumber);
+ surface->loadBackground(_artFileNum);
// Final cleanup
if (ssFlag)
@@ -915,7 +996,7 @@ void MadsInterfaceView::onRefresh(RectList *rects, M4Surface *destSurface) {
// 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);
+ spr->copyTo(destSurface, INVENTORY_X, INVENTORY_Y, TRANSPARENT_COLOUR_INDEX);
if (!_madsVm->globals()->_config.invObjectsStill && !dialogVisible) {
// If objects need to be animated, move to the next frame
@@ -1111,9 +1192,9 @@ bool MadsInterfaceView::handleKeypress(int32 keycode) {
warning("TODO: Activate sound");
break;
- case Common::KEYCODE_u:
- // Rotate player
- warning("TODO: Rotate player");
+ case Common::KEYCODE_t:
+ // Rotate player - This was Ctrl-U in the original, but in ScummVM Ctrl-U is a global mute key
+ _madsVm->_player._newDirection = _madsVm->_player._directionListIndexes[_madsVm->_player._newDirection + 10];
break;
case Common::KEYCODE_v: {
diff --git a/engines/m4/mads_scene.h b/engines/m4/mads_scene.h
index 0269de75c8..e671dfb194 100644
--- a/engines/m4/mads_scene.h
+++ b/engines/m4/mads_scene.h
@@ -35,22 +35,26 @@ namespace M4 {
#define INTERFACE_HEIGHT 106
class MadsInterfaceView;
+#define DEPTH_BANDS_SIZE 15
+
class MadsSceneResources: public SceneResources {
public:
- int sceneId;
- int artFileNum;
- int drawStyle;
- int width;
- int height;
- Common::Array<MadsObject> objects;
- Common::Array<Common::String> setNames;
-
- Common::Point playerPos;
- int playerDir;
-
- MadsSceneResources() { playerDir = 0; }
+ int _sceneId;
+ int _artFileNum;
+ int _depthStyle;
+ int _width;
+ int _height;
+ Common::Array<MadsObject> _objects;
+ Common::Array<Common::String> _setNames;
+ int _yBandsStart, _yBandsEnd;
+ int _maxScale, _minScale;
+ int _depthBands[DEPTH_BANDS_SIZE];
+
+ MadsSceneResources() {}
~MadsSceneResources() {}
void load(int sceneId, const char *resName, int v0, M4Surface *depthSurface, M4Surface *surface);
+ int bandsRange() const { return _yBandsEnd - _yBandsStart; }
+ int scaleRange() const { return _maxScale - _minScale; }
};
enum MadsActionMode {ACTMODE_NONE = 0, ACTMODE_VERB = 1, ACTMODE_OBJECT = 3, ACTMODE_TALK = 6};
@@ -99,7 +103,7 @@ private:
SpriteAsset *_playerSprites;
void drawElements();
- void loadScene2(const char *aaName);
+ void loadScene2(const char *aaName, int sceneNumber);
void loadSceneTemporary();
void loadSceneHotspots(int sceneNumber);
void clearAction();
@@ -107,7 +111,6 @@ private:
void setAction();
public:
char _aaName[100];
- uint16 actionNouns[3];
public:
MadsScene(MadsEngine *vm);
virtual ~MadsScene();
@@ -126,9 +129,10 @@ public:
virtual void updateState();
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);
+ void loadAnimation(const Common::String &animName, int abortTimers);
+ Animation *activeAnimation() const { return _activeAnimation; }
+ void freeAnimation();
MadsInterfaceView *getInterface() { return (MadsInterfaceView *)_interfaceSurface; }
MadsSceneResources &getSceneResources() { return _sceneResources; }
diff --git a/engines/m4/mads_views.cpp b/engines/m4/mads_views.cpp
index c7b4f76a00..d7e6435b14 100644
--- a/engines/m4/mads_views.cpp
+++ b/engines/m4/mads_views.cpp
@@ -86,10 +86,16 @@ int MadsSpriteSlots::getIndex() {
return startIndex++;
}
-int MadsSpriteSlots::addSprites(const char *resName) {
+int MadsSpriteSlots::addSprites(const char *resName, bool suppressErrors, int flags) {
+ // If errors are suppressed, first check if the resource exists
+ if (suppressErrors) {
+ if (!_vm->res()->resourceExists(resName))
+ return -1;
+ }
+
// Get the sprite set
Common::SeekableReadStream *data = _vm->res()->get(resName);
- SpriteAsset *spriteSet = new SpriteAsset(_vm, data, data->size(), resName);
+ SpriteAsset *spriteSet = new SpriteAsset(_vm, data, data->size(), resName, false, flags);
spriteSet->translate(_madsVm->_palette);
assert(spriteSet != NULL);
@@ -99,6 +105,12 @@ int MadsSpriteSlots::addSprites(const char *resName) {
return _sprites.size() - 1;
}
+int MadsSpriteSlots::addSprites(SpriteAsset *spriteSet) {
+ _sprites.push_back(spriteSet);
+
+ return _sprites.size() - 1;
+}
+
void MadsSpriteSlots::deleteSprites(int listIndex) {
if (listIndex < 0)
return;
@@ -136,17 +148,19 @@ typedef Common::List<DepthEntry> DepthList;
void MadsSpriteSlots::drawBackground() {
// Draw all active sprites onto the background surface
for (int i = 0; i < startIndex; ++i) {
- if (_entries[i].spriteType >= 0) {
+ MadsSpriteSlot &slot = _entries[i];
+
+ if (slot.spriteType >= 0) {
_owner._dirtyAreas[i].active = false;
} else {
_owner._dirtyAreas[i].textActive = true;
- _owner._dirtyAreas.setSpriteSlot(i, _entries[i]);
+ _owner._dirtyAreas.setSpriteSlot(i, slot);
- if (_entries[i].spriteType == BACKGROUND_SPRITE) {
- SpriteAsset &spriteSet = getSprite(_entries[i].spriteListIndex);
- M4Sprite *frame = spriteSet.getFrame((_entries[i].frameNumber & 0x7fff) - 1);
- int xp = _entries[i].xp;
- int yp = _entries[i].yp;
+ if (slot.spriteType == BACKGROUND_SPRITE) {
+ SpriteAsset &spriteSet = getSprite(slot.spriteListIndex);
+ M4Sprite *frame = spriteSet.getFrame((slot.frameNumber & 0x7fff) - 1);
+ int xp = slot.xp;
+ int yp = slot.yp;
if (_entries[i].scale != -1) {
// Adjust position based on frame size
@@ -154,12 +168,13 @@ void MadsSpriteSlots::drawBackground() {
yp -= frame->height() / 2;
}
- if (_entries[i].depth <= 1) {
- // No depth, so simply copy the frame onto the background
- frame->copyTo(_owner._bgSurface, xp, yp, 0);
+ if (slot.depth > 1) {
+ // Draw the frame with depth processing
+ _owner._bgSurface->copyFrom(frame, xp, yp, slot.depth, _owner._depthSurface, 100,
+ frame->getTransparencyIndex());
} else {
- // Depth was specified, so draw frame using scene's depth information
- frame->copyTo(_owner._bgSurface, xp, yp, _entries[i].depth, _owner._depthSurface, 100, 0);
+ // No depth, so simply draw the image
+ frame->copyTo(_owner._bgSurface, xp, yp, frame->getTransparencyIndex());
}
}
}
@@ -170,7 +185,7 @@ void MadsSpriteSlots::drawBackground() {
_owner._dirtyAreas[i].active = false;
}
-void MadsSpriteSlots::drawForeground(View *view, int yOffset) {
+void MadsSpriteSlots::drawForeground(M4Surface *viewport) {
DepthList depthList;
// Get a list of sprite object depths for active objects
@@ -192,14 +207,23 @@ void MadsSpriteSlots::drawForeground(View *view, int yOffset) {
assert(slot.spriteListIndex < (int)_sprites.size());
SpriteAsset &spriteSet = *_sprites[slot.spriteListIndex];
- if (slot.scale < 100) {
+ // Get the sprite frame
+ int frameNumber = slot.frameNumber & 0x7fff;
+ bool flipped = (slot.frameNumber & 0x8000) != 0;
+ M4Sprite *sprite = spriteSet.getFrame(frameNumber - 1);
+
+ M4Surface *spr = sprite;
+ if (flipped) {
+ // Create a flipped copy of the sprite temporarily
+ spr = sprite->flipHorizontal();
+ }
+
+ if ((slot.scale < 100) && (slot.scale != -1)) {
// Minimalised drawing
- assert(slot.spriteListIndex < (int)_sprites.size());
- M4Sprite *spr = spriteSet.getFrame((slot.frameNumber & 0x7fff) - 1);
- spr->copyTo(view, slot.xp, slot.yp + yOffset, slot.depth, _owner._depthSurface, slot.scale, 0);
+ viewport->copyFrom(spr, slot.xp, slot.yp, slot.depth, _owner._depthSurface, slot.scale,
+ sprite->getTransparencyIndex());
} else {
int xp, yp;
- M4Sprite *spr = spriteSet.getFrame(slot.frameNumber - 1);
if (slot.scale == -1) {
xp = slot.xp - _owner._posAdjust.x;
@@ -211,12 +235,16 @@ void MadsSpriteSlots::drawForeground(View *view, int yOffset) {
if (slot.depth > 1) {
// Draw the frame with depth processing
- spr->copyTo(view, xp, yp + yOffset, slot.depth, _owner._depthSurface, 100, 0);
+ viewport->copyFrom(spr, xp, yp, slot.depth, _owner._depthSurface, 100, sprite->getTransparencyIndex());
} else {
// No depth, so simply draw the image
- spr->copyTo(view, xp, yp + yOffset, 0);
+ spr->copyTo(viewport, xp, yp, sprite->getTransparencyIndex());
}
}
+
+ // Free sprite if it was a flipped one
+ if (flipped)
+ delete spr;
}
}
@@ -270,6 +298,7 @@ MadsTextDisplay::MadsTextDisplay(MadsView &owner): _owner(owner) {
for (int i = 0; i < TEXT_DISPLAY_SIZE; ++i) {
MadsTextDisplayEntry rec;
rec.active = false;
+ rec.expire = 0;
_entries.push_back(rec);
}
}
@@ -307,7 +336,7 @@ int MadsTextDisplay::add(int xp, int yp, uint fontColour, int charSpacing, const
void MadsTextDisplay::setDirtyAreas() {
// Determine dirty areas for active text areas
for (uint idx = 0, dirtyIdx = DIRTY_AREAS_TEXT_DISPLAY_IDX; dirtyIdx < DIRTY_AREAS_SIZE; ++idx, ++dirtyIdx) {
- if ((_entries[idx].expire < 0) || !_entries[idx].active)
+ if ((_entries[idx].expire >= 0) || !_entries[idx].active)
_owner._dirtyAreas[dirtyIdx].active = false;
else {
_owner._dirtyAreas[dirtyIdx].textActive = true;
@@ -326,23 +355,15 @@ void MadsTextDisplay::setDirtyAreas2() {
}
}
-void MadsTextDisplay::draw(View *view, int yOffset) {
+void MadsTextDisplay::draw(M4Surface *view) {
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].font->writeString(view, _entries[idx].msg,
- _entries[idx].bounds.left, _entries[idx].bounds.top + yOffset, _entries[idx].bounds.width(),
+ _entries[idx].bounds.left, _entries[idx].bounds.top, _entries[idx].bounds.width(),
_entries[idx].spacing);
}
}
-
- // Clear up any now text display entries that are to be expired
- for (uint idx = 0; idx < _entries.size(); ++idx) {
- if (_entries[idx].expire < 0) {
- _entries[idx].active = false;
- _entries[idx].expire = 0;
- }
- }
}
/**
@@ -403,17 +424,17 @@ int MadsKernelMessageList::add(const Common::Point &pt, uint fontColour, uint8 f
rec.abortMode = _owner._abortTimersMode2;
for (int i = 0; i < 3; ++i)
- rec.actionNouns[i] = _madsVm->scene()->actionNouns[i];
+ rec.actionNouns[i] = _madsVm->globals()->actionNouns[i];
- if (flags & KMSG_OWNER_TIMEOUT)
- rec.frameTimer = _owner._ticksAmount + _owner._newTimeout;
+ if (flags & KMSG_PLAYER_TIMEOUT)
+ rec.frameTimer = _madsVm->_player._ticksAmount + _madsVm->_player._priorTimer;
return idx;
}
int MadsKernelMessageList::addQuote(int quoteId, int abortTimers, uint32 timeout) {
const char *quoteStr = _madsVm->globals()->getQuote(quoteId);
- return add(Common::Point(0, 0), 0x1110, KMSG_OWNER_TIMEOUT | KMSG_CENTER_ALIGN, abortTimers, timeout, quoteStr);
+ return add(Common::Point(0, 0), 0x1110, KMSG_PLAYER_TIMEOUT | KMSG_CENTER_ALIGN, abortTimers, timeout, quoteStr);
}
void MadsKernelMessageList::scrollMessage(int msgIndex, int numTicks, bool quoted) {
@@ -429,8 +450,8 @@ void MadsKernelMessageList::scrollMessage(int msgIndex, int numTicks, bool quote
_entries[msgIndex].asciiChar = *msgP;
_entries[msgIndex].asciiChar2 = *(msgP + 1);
- if (_entries[msgIndex].flags & KMSG_OWNER_TIMEOUT)
- _entries[msgIndex].frameTimer2 = _owner._ticksAmount + _owner._newTimeout;
+ if (_entries[msgIndex].flags & KMSG_PLAYER_TIMEOUT)
+ _entries[msgIndex].frameTimer2 = _madsVm->_player._ticksAmount + _madsVm->_player._priorTimer;
_entries[msgIndex].frameTimer = _entries[msgIndex].frameTimer2;
}
@@ -503,7 +524,7 @@ void MadsKernelMessageList::processText(int msgIndex) {
if (_owner._abortTimersMode != ABORTMODE_1) {
for (int i = 0; i < 3; ++i)
- _madsVm->scene()->actionNouns[i] = msg.actionNouns[i];
+ _madsVm->globals()->actionNouns[i] = msg.actionNouns[i];
}
}
}
@@ -524,7 +545,7 @@ void MadsKernelMessageList::processText(int msgIndex) {
}
}
- if (msg.flags & KMSG_OWNER_TIMEOUT) {
+ if (msg.flags & KMSG_PLAYER_TIMEOUT) {
if (word_8469E != 0) {
// TODO: Figure out various flags
} else {
@@ -732,9 +753,12 @@ void MadsDirtyArea::setArea(int width, int height, int maxWidth, int maxHeight)
--bounds.left;
++width;
}
- int right = bounds.left + width;
+
if (bounds.left < 0)
bounds.left = 0;
+ else if (bounds.left > maxWidth)
+ bounds.left = maxWidth;
+ int right = bounds.left + width;
if (right < 0)
right = 0;
if (right > maxWidth)
@@ -746,6 +770,8 @@ void MadsDirtyArea::setArea(int width, int height, int maxWidth, int maxHeight)
if (bounds.top < 0)
bounds.top = 0;
+ else if (bounds.top > maxHeight)
+ bounds.top = maxHeight;
int bottom = bounds.top + height;
if (bottom < 0)
bottom = 0;
@@ -816,6 +842,7 @@ void MadsDirtyAreas::setTextDisplay(int dirtyIdx, const MadsTextDisplayEntry &te
* @param count Number of entries to process
*/
void MadsDirtyAreas::merge(int startIndex, int count) {
+return;//***DEBUG***
if (startIndex >= count)
return;
@@ -855,13 +882,23 @@ void MadsDirtyAreas::mergeAreas(int idx1, int idx2) {
da1.textActive = true;
}
-void MadsDirtyAreas::copy(M4Surface *dest, M4Surface *src, int yOffset) {
+void MadsDirtyAreas::copy(M4Surface *dest, M4Surface *src, const Common::Point &posAdjust) {
for (uint i = 0; i < _entries.size(); ++i) {
+ const Common::Rect &srcBounds = _entries[i].bounds;
+
+ Common::Rect bounds(srcBounds.left + posAdjust.x, srcBounds.top + posAdjust.y,
+ srcBounds.right + posAdjust.x, srcBounds.bottom + posAdjust.y);
+
if (_entries[i].active && _entries[i].bounds.isValidRect())
- src->copyTo(dest, _entries[i].bounds, _entries[i].bounds.left, _entries[i].bounds.top + yOffset);
+ src->copyTo(dest, bounds, _entries[i].bounds.left, _entries[i].bounds.top);
}
}
+void MadsDirtyAreas::clear() {
+ for (uint i = 0; i < _entries.size(); ++i)
+ _entries[i].active = false;
+}
+
/*--------------------------------------------------------------------------*/
MadsSequenceList::MadsSequenceList(MadsView &owner): _owner(owner) {
@@ -937,7 +974,7 @@ int MadsSequenceList::add(int spriteListIndex, int v0, int frameIndex, int trigg
_entries[seqIndex].abortMode = _owner._abortTimersMode2;
for (int i = 0; i < 3; ++i)
- _entries[seqIndex].actionNouns[i] = _madsVm->scene()->actionNouns[i];
+ _entries[seqIndex].actionNouns[i] = _madsVm->globals()->actionNouns[i];
return seqIndex;
}
@@ -1174,25 +1211,29 @@ MadsView::MadsView(View *view): _view(view), _dynamicHotspots(*this), _sequenceL
_kernelMessages(*this), _spriteSlots(*this), _dirtyAreas(*this), _textDisplay(*this) {
_textSpacing = -1;
- _ticksAmount = 3;
_newTimeout = 0;
_abortTimers = 0;
_abortTimers2 = 0;
_abortTimersMode = ABORTMODE_0;
_abortTimersMode2 = ABORTMODE_0;
- _yOffset = 0;
_depthSurface = NULL;
_bgSurface = NULL;
+ _viewport = NULL;
_sceneAnimation = new MadsAnimation(_vm, this);
}
MadsView::~MadsView() {
delete _sceneAnimation;
+ delete _viewport;
}
void MadsView::refresh() {
+ if (!_viewport)
+ setViewport(_view->bounds());
+
// Draw any sprites
+ _dirtyAreas.clear();
_spriteSlots.drawBackground();
// Process dirty areas
@@ -1202,7 +1243,7 @@ void MadsView::refresh() {
_dirtyAreas.merge(1, DIRTY_AREAS_SIZE);
// Copy dirty areas to the main display surface
- _dirtyAreas.copy(_view, _bgSurface, _yOffset);
+ _dirtyAreas.copy(_viewport, _bgSurface, _posAdjust);
// Handle dirty areas for foreground objects
_spriteSlots.setDirtyAreas();
@@ -1210,10 +1251,10 @@ void MadsView::refresh() {
_dirtyAreas.merge(1, DIRTY_AREAS_SIZE);
// Draw foreground sprites
- _spriteSlots.drawForeground(_view, _yOffset);
+ _spriteSlots.drawForeground(_viewport);
// Draw text elements onto the view
- _textDisplay.draw(_view, _yOffset);
+ _textDisplay.draw(_viewport);
// Remove any sprite slots that are no longer needed
_spriteSlots.cleanUp();
@@ -1222,4 +1263,21 @@ void MadsView::refresh() {
_textDisplay.cleanUp();
}
+void MadsView::update() {
+ _sequenceList.tick();
+ _kernelMessages.update();
+}
+
+void MadsView::clearLists() {
+ _textDisplay.clear();
+ _kernelMessages.clear();
+ _spriteSlots.clear();
+}
+
+void MadsView::setViewport(const Common::Rect &bounds) {
+ delete _viewport;
+ _viewport = new M4Surface(bounds.width(), bounds.height(), _view->getBasePtr(bounds.left, bounds.top),
+ _view->getPitch());
+}
+
} // End of namespace M4
diff --git a/engines/m4/mads_views.h b/engines/m4/mads_views.h
index 29adb7048a..0604ae1ee1 100644
--- a/engines/m4/mads_views.h
+++ b/engines/m4/mads_views.h
@@ -92,13 +92,14 @@ public:
}
int getIndex();
- int addSprites(const char *resName);
+ int addSprites(const char *resName, bool suppressErrors = false, int flags = 0);
+ int addSprites(SpriteAsset *spriteSet);
void deleteSprites(int listIndex);
void clear();
void deleteTimer(int seqIndex);
void drawBackground();
- void drawForeground(View *view, int yOffset);
+ void drawForeground(M4Surface *viewport);
void setDirtyAreas();
void fullRefresh();
void cleanUp();
@@ -139,7 +140,7 @@ public:
int add(int xp, int yp, uint fontColour, int charSpacing, const char *msg, Font *font);
void clear();
- void draw(View *view, int yOffset);
+ void draw(M4Surface *view);
void setDirtyAreas();
void setDirtyAreas2();
void cleanUp();
@@ -148,7 +149,7 @@ public:
#define TIMED_TEXT_SIZE 10
#define INDEFINITE_TIMEOUT 9999999
-enum KernelMessageFlags {KMSG_QUOTED = 1, KMSG_OWNER_TIMEOUT = 2, KMSG_SEQ_ENTRY = 4, KMSG_SCROLL = 8, KMSG_RIGHT_ALIGN = 0x10,
+enum KernelMessageFlags {KMSG_QUOTED = 1, KMSG_PLAYER_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 {
@@ -170,6 +171,10 @@ public:
AbortTimerMode abortMode;
uint16 actionNouns[3];
char msg[100];
+
+ MadsKernelMessageEntry() {
+ flags = 0;
+ }
};
class MadsKernelMessageList {
@@ -289,7 +294,8 @@ 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, int yOffset);
+ void copy(M4Surface *dest, M4Surface *src, const Common::Point &posAdjust);
+ void clear();
};
enum SpriteAnimType {ANIMTYPE_CYCLED = 1, ANIMTYPE_REVERSIBLE = 2};
@@ -367,10 +373,11 @@ protected:
public:
Animation(MadsM4Engine *vm);
virtual ~Animation();
- virtual void initialise(const Common::String &filename, uint16 flags, M4Surface *walkSurface, M4Surface *sceneSurface) = 0;
+ virtual void initialise(const Common::String &filename, uint16 flags, M4Surface *surface, M4Surface *depthSurface) = 0;
virtual void load(const Common::String &filename, int v0) = 0;
virtual void update() = 0;
virtual void setCurrentFrame(int frameNumber) = 0;
+ virtual int getCurrentFrame() = 0;
};
@@ -388,7 +395,6 @@ public:
MadsDirtyAreas _dirtyAreas;
int _textSpacing;
- int _ticksAmount;
uint32 _newTimeout;
int _abortTimers;
int8 _abortTimers2;
@@ -398,12 +404,15 @@ public:
M4Surface *_depthSurface;
M4Surface *_bgSurface;
- int _yOffset;
+ M4Surface *_viewport;
public:
MadsView(View *view);
~MadsView();
void refresh();
+ void update();
+ void clearLists();
+ void setViewport(const Common::Rect &bounds);
};
}
diff --git a/engines/m4/midi.cpp b/engines/m4/midi.cpp
index 78fe0d6bd6..2c767fdf5a 100644
--- a/engines/m4/midi.cpp
+++ b/engines/m4/midi.cpp
@@ -109,7 +109,7 @@ void MidiPlayer::send(uint32 b) {
else if ((b & 0xFFF0) == 0x007BB0) {
//Only respond to All Notes Off if this channel
//has currently been allocated
- if (_channel[b & 0x0F])
+ if (!_channel[b & 0x0F])
return;
}
diff --git a/engines/m4/module.mk b/engines/m4/module.mk
index 1b08ea2188..f60757ba3b 100644
--- a/engines/m4/module.mk
+++ b/engines/m4/module.mk
@@ -22,6 +22,7 @@ MODULE_OBJS = \
mads_anim.o \
mads_logic.o \
mads_menus.o \
+ mads_player.o \
mads_scene.o \
mads_views.o \
midi.o \
diff --git a/engines/m4/scene.cpp b/engines/m4/scene.cpp
index 15c68f276c..e78d7f865e 100644
--- a/engines/m4/scene.cpp
+++ b/engines/m4/scene.cpp
@@ -55,6 +55,7 @@ Scene::Scene(MadsM4Engine *vm, SceneResources *res): View(vm, Common::Rect(0, 0,
Scene::~Scene() {
leaveScene();
+ _vm->_scene = NULL;
}
void Scene::loadScene(int sceneNumber) {
diff --git a/engines/m4/sound.cpp b/engines/m4/sound.cpp
index 69ab8c0516..e0fbd2f7a9 100644
--- a/engines/m4/sound.cpp
+++ b/engines/m4/sound.cpp
@@ -197,20 +197,20 @@ void Sound::loadDSRFile(const char *fileName) {
//printf("DSR has %i entries\n", _dsrFile.entryCount);
for (int i = 0; i < _dsrFile.entryCount; i++) {
- DSREntry* newEntry = new DSREntry();
- newEntry->frequency = fileStream->readUint16LE();
- newEntry->channels = fileStream->readUint32LE();
- newEntry->compSize = fileStream->readUint32LE();
- newEntry->uncompSize = fileStream->readUint32LE();
- newEntry->offset = fileStream->readUint32LE();
+ DSREntry newEntry;
+ newEntry.frequency = fileStream->readUint16LE();
+ newEntry.channels = fileStream->readUint32LE();
+ newEntry.compSize = fileStream->readUint32LE();
+ newEntry.uncompSize = fileStream->readUint32LE();
+ newEntry.offset = fileStream->readUint32LE();
_dsrFile.dsrEntries.push_back(newEntry);
/*
printf("%i: ", i);
printf("frequency: %i ", newEntry->frequency);
printf("channels: %i ", newEntry->channels);
- printf("comp: %i ", newEntry->compSize);
- printf("uncomp: %i ", newEntry->uncompSize);
+ printf("comp: %i ", newEntry.compSize);
+ printf("uncomp: %i ", newEntry.uncompSize);
printf("offset: %i ", newEntry->offset);
printf("\n");
*/
@@ -225,9 +225,7 @@ void Sound::unloadDSRFile() {
if (!_dsrFileLoaded)
return;
- for (int i = 0; i < _dsrFile.entryCount; i++) {
- _dsrFile.dsrEntries.remove_at(0);
- }
+ _dsrFile.dsrEntries.clear();
_dsrFile.entryCount = 0;
strcpy(_dsrFile.fileName, "");
@@ -251,28 +249,28 @@ void Sound::playDSRSound(int soundIndex, int volume, bool loop) {
// Get sound data
FabDecompressor fab;
- byte *compData = new byte[_dsrFile.dsrEntries[soundIndex]->compSize];
- byte *buffer = new byte[_dsrFile.dsrEntries[soundIndex]->uncompSize];
+ byte *compData = new byte[_dsrFile.dsrEntries[soundIndex].compSize];
+ byte *buffer = new byte[_dsrFile.dsrEntries[soundIndex].uncompSize];
Common::SeekableReadStream *fileStream = _vm->res()->get(_dsrFile.fileName);
- fileStream->seek(_dsrFile.dsrEntries[soundIndex]->offset, SEEK_SET);
- fileStream->read(compData, _dsrFile.dsrEntries[soundIndex]->compSize);
+ fileStream->seek(_dsrFile.dsrEntries[soundIndex].offset, SEEK_SET);
+ fileStream->read(compData, _dsrFile.dsrEntries[soundIndex].compSize);
_vm->res()->toss(_dsrFile.fileName);
- fab.decompress(compData, _dsrFile.dsrEntries[soundIndex]->compSize,
- buffer, _dsrFile.dsrEntries[soundIndex]->uncompSize);
+ fab.decompress(compData, _dsrFile.dsrEntries[soundIndex].compSize,
+ buffer, _dsrFile.dsrEntries[soundIndex].uncompSize);
// Play sound
Audio::AudioStream *stream = Audio::makeLoopingAudioStream(
Audio::makeRawStream(buffer,
- _dsrFile.dsrEntries[soundIndex]->uncompSize,
- _dsrFile.dsrEntries[soundIndex]->frequency, Audio::FLAG_UNSIGNED),
+ _dsrFile.dsrEntries[soundIndex].uncompSize,
+ _dsrFile.dsrEntries[soundIndex].frequency, Audio::FLAG_UNSIGNED),
loop ? 0 : 1);
_mixer->playStream(Audio::Mixer::kSFXSoundType, &handle->handle, stream, -1, volume);
/*
// Dump the sound file
FILE *destFile = fopen("sound.raw", "wb");
- fwrite(_dsrFile.dsrEntries[soundIndex]->data, _dsrFile.dsrEntries[soundIndex]->uncompSize, 1, destFile);
+ fwrite(_dsrFile.dsrEntries[soundIndex]->data, _dsrFile.dsrEntries[soundIndex].uncompSize, 1, destFile);
fclose(destFile);
*/
}
diff --git a/engines/m4/sound.h b/engines/m4/sound.h
index 7d442a73cc..5587810506 100644
--- a/engines/m4/sound.h
+++ b/engines/m4/sound.h
@@ -65,7 +65,7 @@ struct DSREntry {
struct DSRFile {
char fileName[20];
int entryCount;
- Common::Array<DSREntry *> dsrEntries;
+ Common::Array<DSREntry> dsrEntries;
};
class MadsM4Engine;
diff --git a/engines/m4/sprite.cpp b/engines/m4/sprite.cpp
index 0ff4bec855..641b93baea 100644
--- a/engines/m4/sprite.cpp
+++ b/engines/m4/sprite.cpp
@@ -121,60 +121,89 @@ void M4Sprite::loadDeltaRle(Common::SeekableReadStream* rleData, int destX, int
// TODO: The sprite outlines (pixel value 0xFD) are not shown
void M4Sprite::loadMadsSprite(Common::SeekableReadStream* source) {
- byte *outp, *lineStart;
- bool newLine = false;
+ bool spriteEnd = false;
- outp = getBasePtr();
- lineStart = getBasePtr();
+ // Set entire sprite contents to transparent pixels
+ fillRect(bounds(), TRANSPARENT_COLOUR_INDEX);
- while (1) {
- byte cmd1, cmd2, count, pixel;
-
- if (newLine) {
- outp = lineStart + w;
- lineStart = outp;
- newLine = false;
- }
-
- cmd1 = source->readByte();
+ // Major line loop
+ for (int yp = 0; yp < h; ++yp) {
+ byte *destP = getBasePtr(0, yp);
+ bool newLine = false;
+ byte cmd = source->readByte();
+ int x2 = 0;
- if (cmd1 == 0xFC)
+ if (cmd == 0xfc) {
+ // End of entire sprite
+ spriteEnd = true;
break;
- else if (cmd1 == 0xFF)
+ } else if (cmd == 0xff) {
+ // The entire line is empty
newLine = true;
- else if (cmd1 == 0xFD) {
- while (!newLine) {
- count = source->readByte();
- if (count == 0xFF) {
+ } else if (cmd == 0xFD) {
+ // Lines contains only run lenghs of pixels
+ while (x2 < w) {
+ cmd = source->readByte();
+ if (cmd == 0xff) {
+ // End of line reached
newLine = true;
- } else {
- pixel = source->readByte();
- while (count--)
- *outp++ = (pixel == 0xFD) ? 0 : pixel;
+ break;
+ }
+
+ byte v = source->readByte();
+ while (cmd-- > 0) {
+ if (x2 < w)
+ *destP++ = (v == 0xFD) ? TRANSPARENT_COLOUR_INDEX : v;
+ ++x2;
}
}
} else {
- while (!newLine) {
- cmd2 = source->readByte();
- if (cmd2 == 0xFF) {
+ // Line intermixes run lengths with individual pixels
+ while (x2 < w) {
+ cmd = source->readByte();
+ if (cmd == 0xff) {
+ // End of line reached
newLine = true;
- } else if (cmd2 == 0xFE) {
- count = source->readByte();
- pixel = source->readByte();
- while (count--)
- *outp++ = (pixel == 0xFD) ? 0 : pixel;
+ break;
+ }
+
+ if (cmd == 0xFE) {
+ // Handle repeated sequence
+ cmd = source->readByte();
+ byte v = source->readByte();
+ while (cmd-- > 0) {
+ if (x2 < w) {
+ *destP++ = (v == 0xFD) ? TRANSPARENT_COLOUR_INDEX : v;
+ }
+ ++x2;
+ }
} else {
- *outp++ = (cmd2 == 0xFD) ? 0 : cmd2;
+ // Handle writing out single pixel
+ *destP++ = (cmd == 0xFD) ? TRANSPARENT_COLOUR_INDEX : cmd;
+ ++x2;
}
}
}
+
+ // Check if we need to scan forward to find the end of the line
+ if (!newLine) {
+ do {
+ if (source->eos()) {
+ warning("M4Sprite::loadMadsSprite: unexpected end of data");
+ break;
+ }
+ } while (source->readByte() != 0xff);
+ }
+ }
+
+ if (!spriteEnd) {
+ byte v = source->readByte();
+ assert(v == 0xFC);
}
}
-byte M4Sprite::getTransparentColor() const {
- // FIXME: We assume that the transparent color is the color of the
- // top left pixel.
- return *getBasePtr(0, 0);
+byte M4Sprite::getTransparencyIndex() const {
+ return TRANSPARENT_COLOUR_INDEX;
}
} // End of namespace M4
diff --git a/engines/m4/sprite.h b/engines/m4/sprite.h
index 49a96a6c4a..d4e5502efd 100644
--- a/engines/m4/sprite.h
+++ b/engines/m4/sprite.h
@@ -115,7 +115,7 @@ public:
void loadDeltaRle(Common::SeekableReadStream* rleData, int destX, int destY);
void loadMadsSprite(Common::SeekableReadStream* source);
- byte getTransparentColor() const;
+ byte getTransparencyIndex() const;
protected:
};
diff --git a/engines/made/detection.cpp b/engines/made/detection.cpp
index 1dfc0c3f83..dd2becd3b8 100644
--- a/engines/made/detection.cpp
+++ b/engines/made/detection.cpp
@@ -493,7 +493,11 @@ static const ADParams detectionParams = {
// Flags
0,
// Additional GUI options (for every game}
- Common::GUIO_NONE
+ Common::GUIO_NONE,
+ // Maximum directory depth
+ 1,
+ // List of directory globs
+ 0
};
class MadeMetaEngine : public AdvancedMetaEngine {
diff --git a/engines/made/made.cpp b/engines/made/made.cpp
index 917141ae04..20b4dc1e1b 100644
--- a/engines/made/made.cpp
+++ b/engines/made/made.cpp
@@ -96,11 +96,11 @@ MadeEngine::MadeEngine(OSystem *syst, const MadeGameDescription *gameDesc) : Eng
_script = new ScriptInterpreter(this);
- MidiDriverType midiDriver = MidiDriver::detectMusicDriver(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MIDI);
- bool native_mt32 = ((midiDriver == MD_MT32) || ConfMan.getBool("native_mt32"));
- //bool adlib = (midiDriver == MD_ADLIB);
+ MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
+ bool native_mt32 = ((MidiDriver::getMusicType(dev) == MT_MT32) || ConfMan.getBool("native_mt32"));
+ //bool adlib = (MidiDriver::getMusicType(dev) == MT_ADLIB);
- MidiDriver *driver = MidiDriver::createMidi(midiDriver);
+ MidiDriver *driver = MidiDriver::createMidi(dev);
if (native_mt32)
driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
diff --git a/engines/mohawk/console.cpp b/engines/mohawk/console.cpp
index 149b6b6eba..5dcfff4f90 100644
--- a/engines/mohawk/console.cpp
+++ b/engines/mohawk/console.cpp
@@ -28,6 +28,7 @@
#include "mohawk/myst_scripts.h"
#include "mohawk/graphics.h"
#include "mohawk/riven.h"
+#include "mohawk/riven_external.h"
#include "mohawk/livingbooks.h"
#include "mohawk/sound.h"
#include "mohawk/video.h"
@@ -307,6 +308,7 @@ RivenConsole::RivenConsole(MohawkEngine_Riven *vm) : GUI::Debugger(), _vm(vm) {
DCmd_Register("dumpScript", WRAP_METHOD(RivenConsole, Cmd_DumpScript));
DCmd_Register("listZipCards", WRAP_METHOD(RivenConsole, Cmd_ListZipCards));
DCmd_Register("getRMAP", WRAP_METHOD(RivenConsole, Cmd_GetRMAP));
+ DCmd_Register("combos", WRAP_METHOD(RivenConsole, Cmd_Combos));
}
RivenConsole::~RivenConsole() {
@@ -556,9 +558,11 @@ bool RivenConsole::Cmd_DumpScript(int argc, const char **argv) {
printf ("==================================\n\n");
Common::SeekableReadStream *cardStream = _vm->getRawData(MKID_BE('CARD'), (uint16)atoi(argv[3]));
cardStream->seek(4);
- RivenScriptList scriptList = RivenScript::readScripts(_vm, cardStream);
- for (uint32 i = 0; i < scriptList.size(); i++)
+ RivenScriptList scriptList = _vm->_scriptMan->readScripts(cardStream, false);
+ for (uint32 i = 0; i < scriptList.size(); i++) {
scriptList[i]->dumpScript(varNames, xNames, 0);
+ delete scriptList[i];
+ }
delete cardStream;
} else if (!scumm_stricmp(argv[2], "HSPT")) {
printf ("\n\nDumping scripts for %s\'s card %d hotspots!\n", argv[1], (uint16)atoi(argv[3]));
@@ -571,9 +575,11 @@ bool RivenConsole::Cmd_DumpScript(int argc, const char **argv) {
for (uint16 i = 0; i < hotspotCount; i++) {
printf ("Hotspot %d:\n", i);
hsptStream->seek(22, SEEK_CUR); // Skip non-script related stuff
- RivenScriptList scriptList = RivenScript::readScripts(_vm, hsptStream);
- for (uint32 j = 0; j < scriptList.size(); j++)
+ RivenScriptList scriptList = _vm->_scriptMan->readScripts(hsptStream, false);
+ for (uint32 j = 0; j < scriptList.size(); j++) {
scriptList[j]->dumpScript(varNames, xNames, 1);
+ delete scriptList[j];
+ }
}
delete hsptStream;
@@ -608,6 +614,33 @@ bool RivenConsole::Cmd_GetRMAP(int argc, const char **argv) {
return true;
}
+bool RivenConsole::Cmd_Combos(int argc, const char **argv) {
+ // In the vain of SCUMM's 'drafts' command, this command will list
+ // out all combinations needed in Riven, decoded from the variables.
+ // You'll need to look up the Rebel Tunnel puzzle on your own; the
+ // solution is constant.
+
+ uint32 teleCombo = *_vm->matchVarToString("tcorrectorder");
+ uint32 prisonCombo = *_vm->matchVarToString("pcorrectorder");
+ uint32 domeCombo = *_vm->matchVarToString("adomecombo");
+
+ DebugPrintf("Telescope Combo:\n ");
+ for (int i = 0; i < 5; i++)
+ DebugPrintf("%d ", _vm->_externalScriptHandler->getComboDigit(teleCombo, i));
+
+ DebugPrintf("\nPrison Combo:\n ");
+ for (int i = 0; i < 5; i++)
+ DebugPrintf("%d ", _vm->_externalScriptHandler->getComboDigit(prisonCombo, i));
+
+ DebugPrintf("\nDome Combo:\n ");
+ for (int i = 1; i <= 25; i++)
+ if (domeCombo & (1 << (25 - i)))
+ DebugPrintf("%d ", i);
+
+ DebugPrintf("\n");
+ return true;
+}
+
LivingBooksConsole::LivingBooksConsole(MohawkEngine_LivingBooks *vm) : GUI::Debugger(), _vm(vm) {
DCmd_Register("playSound", WRAP_METHOD(LivingBooksConsole, Cmd_PlaySound));
DCmd_Register("stopSound", WRAP_METHOD(LivingBooksConsole, Cmd_StopSound));
diff --git a/engines/mohawk/console.h b/engines/mohawk/console.h
index 9a30d46225..1806c61027 100644
--- a/engines/mohawk/console.h
+++ b/engines/mohawk/console.h
@@ -88,6 +88,7 @@ private:
bool Cmd_DumpScript(int argc, const char **argv);
bool Cmd_ListZipCards(int argc, const char **argv);
bool Cmd_GetRMAP(int argc, const char **argv);
+ bool Cmd_Combos(int argc, const char **argv);
};
class LivingBooksConsole : public GUI::Debugger {
diff --git a/engines/mohawk/detection.cpp b/engines/mohawk/detection.cpp
index 7f2e0cb312..f04338239f 100644
--- a/engines/mohawk/detection.cpp
+++ b/engines/mohawk/detection.cpp
@@ -117,882 +117,21 @@ static const PlainGameDescriptor mohawkGames[] = {
{"ruff", "Ruff's Bone"},
{"newkid", "The New Kid on the Block"},
{"arthurrace", "Arthur's Reading Race"},
+ {"arthurbday", "Arthur's Birthday"},
+ {"lilmonster", "Little Monster at School"},
#endif
{0, 0}
};
+#include "mohawk/detection_tables.h"
-namespace Mohawk {
-
-static const MohawkGameDescription gameDescriptions[] = {
- // Myst
- // English Windows 3.11
- // From clone2727
- {
- {
- "myst",
- "",
- AD_ENTRY1("MYST.DAT", "ae3258c9c90128d274aa6a790b3ad181"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_MYST,
- 0,
- 0,
- },
-
- // Myst Demo
- // English Windows 3.11
- // From CD-ROM Today July, 1994
- {
- {
- "myst",
- "Demo",
- AD_ENTRY1("DEMO.DAT", "c39303dd53fb5c4e7f3c23231c606cd0"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_DEMO,
- Common::GUIO_NONE
- },
- GType_MYST,
- GF_DEMO,
- 0,
- },
-
- // Myst
- // German Windows 3.11
- // From clone2727
- {
- {
- "myst",
- "",
- AD_ENTRY1("MYST.DAT", "4beb3366ed3f3b9bfb6e81a14a43bdcc"),
- Common::DE_DEU,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_MYST,
- 0,
- 0,
- },
-
- // Myst
- // German Windows 3.11
- // From LordHoto
- {
- {
- "myst",
- "",
- AD_ENTRY1("MYST.DAT", "e0937cca1ab125e48e30dc3cd5046ddf"),
- Common::DE_DEU,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_MYST,
- 0,
- 0,
- },
-
- // Myst
- // Spanish Windows ?
- // From jvprat
- {
- {
- "myst",
- "",
- AD_ENTRY1("MYST.DAT", "f7e7d7ca69934f1351b5acd4fe4d44c2"),
- Common::ES_ESP,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_MYST,
- 0,
- 0,
- },
-
- // Myst
- // Japanese Windows 3.11
- // From clone2727
- {
- {
- "myst",
- "",
- AD_ENTRY1("MYST.DAT", "032c88e3b7e8db4ca475e7b7db9a66bb"),
- Common::JA_JPN,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_MYST,
- 0,
- 0,
- },
-
- // Myst
- // French Windows 3.11
- // From Strangerke
- {
- {
- "myst",
- "",
- AD_ENTRY1("MYST.DAT", "d631d42567a941c67c78f2e491f4ea58"),
- Common::FR_FRA,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_MYST,
- 0,
- 0,
- },
-
- // Making of Myst
- // English Windows 3.11
- // From clone2727
- {
- {
- "MakingOfMyst",
- "",
- AD_ENTRY1("MAKING.DAT", "f6387e8f0f7b8a3e42c95294315d6a0e"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_MAKINGOF,
- 0,
- 0,
- },
-
- // Making of Myst
- // Japanese Windows 3.11
- // From clone2727
- {
- {
- "MakingOfMyst",
- "",
- AD_ENTRY1("MAKING.DAT", "03ff62607e64419ab2b6ebf7b7bcdf63"),
- Common::JA_JPN,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_MAKINGOF,
- 0,
- 0,
- },
-
- // Myst Masterpiece Edition
- // English Windows
- // From clone2727
- {
- {
- "myst",
- "Masterpiece Edition",
- AD_ENTRY1("MYST.DAT", "c4cae9f143b5947262e6cb2397e1617e"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_MYST,
- GF_ME,
- 0,
- },
-
- // Myst Masterpiece Edition
- // English Windows
- // From clone2727
- {
- {
- "myst",
- "Masterpiece Edition",
- AD_ENTRY1("MYST.DAT", "c4cae9f143b5947262e6cb2397e1617e"),
- Common::EN_ANY,
- Common::kPlatformMacintosh,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_MYST,
- GF_ME,
- 0,
- },
-
- // Myst Masterpiece Edition
- // German Windows
- // From DrMcCoy (Included in "Myst: Die Trilogie")
- {
- {
- "myst",
- "Masterpiece Edition",
- AD_ENTRY1("MYST.DAT", "f88e0ace66dbca78eebdaaa1d3314ceb"),
- Common::DE_DEU,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_MYST,
- GF_ME,
- 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
- {
- {
- "riven",
- "",
- AD_ENTRY1("a_Data.MHK", "71145fdecbd68a0cfc292c2fbddf8e08"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_RIVEN,
- 0,
- 0,
- },
-
- // Riven: The Sequel to Myst
- // Version 1.03 (5CD)
- // From ST
- {
- {
- "riven",
- "",
- AD_ENTRY1("a_Data.MHK", "d8ccae34a0e3c709135a73f449b783be"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_RIVEN,
- 0,
- 0,
- },
-
- // Riven: The Sequel to Myst
- // Version 1.? (5CD)
- // From jvprat
- {
- {
- "riven",
- "",
- AD_ENTRY1("a_Data.MHK", "249e8c995d191b03ee94c892c0eac775"),
- Common::ES_ESP,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_RIVEN,
- 0,
- 0,
- },
-
- // Riven: The Sequel to Myst
- // Version 1.? (DVD, From "Myst 10th Anniversary Edition")
- // From Clone2727
- {
- {
- "riven",
- "DVD",
- AD_ENTRY1("a_Data.MHK", "08fcaa5d5a2a01d7a5a6960f497212fe"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_RIVEN,
- GF_DVD,
- 0,
- },
-
- // Riven: The Sequel to Myst
- // Version 1.0 (DVD, From "Myst: Die Trilogie")
- // From DrMcCoy
- {
- {
- "riven",
- "",
- AD_ENTRY1("a_Data.MHK", "a5fe1c91a6033eb6ee54b287578b74b9"),
- Common::DE_DEU,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_RIVEN,
- GF_DVD,
- 0,
- },
-
- // 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
- {
- {
- "riven",
- "Demo",
- AD_ENTRY1("a_Data.MHK", "bae6b03bd8d6eb350d35fd13f0e3139f"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_DEMO,
- Common::GUIO_NONE
- },
- GType_RIVEN,
- GF_DEMO,
- 0,
- },
-
-#ifdef DETECT_BRODERBUND_TITLES
- {
- {
- "zoombini",
- "",
- AD_ENTRY1("ZOOMBINI.MHK", "98b758fec55104c096cfd129048be9a6"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_ZOOMBINI,
- GF_HASMIDI,
- 0
- },
-
- {
- {
- "csworld",
- "v3.0",
- AD_ENTRY1("C2K.MHK", "605fe88380848031bbd0ff84ade6fe40"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_CSWORLD,
- 0,
- 0
- },
-
- {
- {
- "csworld",
- "v3.5",
- AD_ENTRY1("C2K.MHK", "d4857aeb0f5e2e0c4ac556aa74f38c23"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_CSWORLD,
- 0,
- 0
- },
-
- {
- {
- "csamtrak",
- "",
- AD_ENTRY1("AMTRAK.MHK", "2f95301f0bb950d555bb7b0e3b1b7eb1"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_CSAMTRAK,
- 0,
- 0
- },
-
- {
- {
- "maggiess",
- "",
- AD_ENTRY1("MAGGIESS.MHK", "08f75fc8c0390e68fdada5ddb35d0355"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_MAGGIESS,
- 0,
- 0
- },
-
- {
- {
- "jamesmath",
- "",
- AD_ENTRY1("BRODER.MHK", "007299da8b2c6e8ec1cde9598c243024"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_JAMESMATH,
- GF_HASMIDI,
- 0
- },
-
- // This is in the NEWDATA folder, so I assume it's a newer version ;)
- {
- {
- "jamesmath",
- "",
- AD_ENTRY1("BRODER.MHK", "53c000938a50dca92860fd9b546dd276"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_JAMESMATH,
- GF_HASMIDI,
- 1
- },
-
- {
- {
- "treehouse",
- "",
- AD_ENTRY1("MAINROOM.MHK", "12f51894d7f838af639ea9bf1bc8f45b"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_TREEHOUSE,
- GF_HASMIDI,
- 0
- },
-
- {
- {
- "greeneggs",
- "",
- AD_ENTRY1("GREEN.LB", "5df8438138186f89e71299d7b4f88d06"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_LIVINGBOOKSV3,
- 0,
- 0
- },
-
- // 32-bit version of the previous entry
- {
- {
- "greeneggs",
- "",
- AD_ENTRY1("GREEN32.LB", "5df8438138186f89e71299d7b4f88d06"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_LIVINGBOOKSV3,
- 0,
- 0
- },
-
- {
- {
- "1stdegree",
- "",
- AD_ENTRY1("AL236_1.MHK", "3ba145492a7b8b4dee0ef4222c5639c3"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_1STDEGREE,
- GF_HASMIDI,
- 0
- },
-
- // In The 1st Degree
- // French Windows
- // From Strangerke
- {
- {
- "1stdegree",
- "",
- AD_ENTRY1("AL236_1.MHK", "0e0c70b1b702b6ddca61a1192ada1282"),
- Common::FR_FRA,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_1STDEGREE,
- GF_HASMIDI,
- 0
- },
-
- {
- {
- "csusa",
- "",
- AD_ENTRY1("USAC2K.MHK", "b8c9d3a2586f62bce3a48b50d7a700e9"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_CSUSA,
- 0,
- 0
- },
-
- {
- {
- "tortoise",
- "Demo v1.0",
- AD_ENTRY1("TORTOISE.512", "75d9a2f8339e423604a0c6e8177600a6"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_DEMO,
- Common::GUIO_NONE
- },
- GType_LIVINGBOOKSV1,
- GF_DEMO,
- 0
- },
-
- {
- {
- "tortoise",
- "Demo v1.1",
- AD_ENTRY1("TORTOISE.512", "a38c99360e2bea3bfdec418469aef022"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_DEMO,
- Common::GUIO_NONE
- },
- GType_LIVINGBOOKSV1,
- GF_DEMO,
- 0
- },
-
- {
- {
- "arthur",
- "",
- AD_ENTRY1("PAGES.512", "1550a361454ec452fe7d2328aac2003c"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_LIVINGBOOKSV1,
- 0,
- 0
- },
-
- {
- {
- "arthur",
- "Demo",
- AD_ENTRY1("PAGES.512", "a4d68cef197af1416921ca5b2e0c1e31"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_DEMO,
- Common::GUIO_NONE
- },
- GType_LIVINGBOOKSV1,
- GF_DEMO,
- 0
- },
-
- {
- {
- "arthur",
- "Demo",
- AD_ENTRY1("Bookoutline", "7e2691611ff4c7b89c05221736628059"),
- Common::EN_ANY,
- Common::kPlatformMacintosh,
- ADGF_DEMO,
- Common::GUIO_NONE
- },
- GType_LIVINGBOOKSV1,
- GF_DEMO,
- 0
- },
-
- {
- {
- "grandma",
- "Demo v1.0",
- AD_ENTRY1("PAGES.512", "95d9f4b035bf5d15c57a9189f231b0f8"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_DEMO,
- Common::GUIO_NONE
- },
- GType_LIVINGBOOKSV1,
- GF_DEMO,
- 0
- },
-
- {
- {
- "grandma",
- "Demo v1.1",
- AD_ENTRY1("GRANDMA.512", "72a4d5fb1b3f06b5f75425635d42ce2e"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_DEMO,
- Common::GUIO_NONE
- },
- GType_LIVINGBOOKSV1,
- GF_DEMO,
- 0
- },
-
- {
- {
- "grandma",
- "Demo",
- AD_ENTRY1("Bookoutline", "553c93891b9631d1e1d269599e1efa6c"),
- Common::EN_ANY,
- Common::kPlatformMacintosh,
- ADGF_DEMO,
- Common::GUIO_NONE
- },
- GType_LIVINGBOOKSV1,
- GF_DEMO,
- 0
- },
-
- {
- {
- "ruff",
- "Demo",
- AD_ENTRY1("RUFF.512", "2ba1aa65177c816e156db648c398d362"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_DEMO,
- Common::GUIO_NONE
- },
- GType_LIVINGBOOKSV1,
- GF_DEMO,
- 0
- },
-
- {
- {
- "ruff",
- "Demo",
- AD_ENTRY1("Ruff's Bone Demo", "22553ac2ceb2a166bdf1def6ad348532"),
- Common::EN_ANY,
- Common::kPlatformMacintosh,
- ADGF_DEMO,
- Common::GUIO_NONE
- },
- GType_LIVINGBOOKSV1,
- GF_DEMO,
- 0
- },
-
- {
- {
- "newkid",
- "Demo v1.0",
- AD_ENTRY1("NEWKID.512", "2b9d94763a50d514c04a3af488934f73"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_DEMO,
- Common::GUIO_NONE
- },
- GType_LIVINGBOOKSV1,
- GF_DEMO,
- 0
- },
-
- {
- {
- "newkid",
- "Demo v1.1",
- AD_ENTRY1("NEWKID.512", "41e975b7390c626f8d1058a34f9d9b2e"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_DEMO,
- Common::GUIO_NONE
- },
- GType_LIVINGBOOKSV1,
- GF_DEMO,
- 0
- },
-
- {
- {
- "arthurrace",
- "",
- AD_ENTRY1("RACE.LB", "1645f36bcb36e440d928e920aa48c373"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_LIVINGBOOKSV3,
- 0,
- 0
- },
-
- // 32-bit version of the previous entry
- {
- {
- "arthurrace",
- "",
- AD_ENTRY1("RACE32.LB", "292a05bc48c1dd9583821a4181a02ef2"),
- Common::EN_ANY,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_LIVINGBOOKSV3,
- 0,
- 0
- },
-#endif
-
- { AD_TABLE_END_MARKER, 0, 0, 0 }
+static const char *directoryGlobs[] = {
+ "all",
+ "assets1",
+ "data",
+ 0
};
-//////////////////////////////
-//Fallback detection
-//////////////////////////////
-
-static const MohawkGameDescription fallbackDescs[] = {
- {
- {
- "myst",
- "unknown",
- AD_ENTRY1(0, 0),
- Common::UNK_LANG,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_MYST,
- 0,
- 0
- },
-
- {
- {
- "MakingOfMyst",
- "unknown",
- AD_ENTRY1(0, 0),
- Common::UNK_LANG,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_MAKINGOF,
- 0,
- 0
- },
-
- {
- {
- "myst",
- "unknown (Masterpiece Edition)",
- AD_ENTRY1(0, 0),
- Common::UNK_LANG,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_MYST,
- GF_ME,
- 0
- },
-
- {
- {
- "riven",
- "unknown",
- AD_ENTRY1(0, 0),
- Common::UNK_LANG,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_RIVEN,
- 0,
- 0
- },
-
- {
- {
- "riven",
- "unknown (DVD)",
- AD_ENTRY1(0, 0),
- Common::UNK_LANG,
- Common::kPlatformWindows,
- ADGF_NO_FLAGS,
- Common::GUIO_NONE
- },
- GType_RIVEN,
- GF_DVD,
- 0
- }
-};
-
-static const ADFileBasedFallback fileBased[] = {
- { &fallbackDescs[0], { "MYST.DAT", 0 } },
- { &fallbackDescs[1], { "MAKING.DAT", 0 } },
- { &fallbackDescs[2], { "MYST.DAT", "Help.dat", 0 } }, // Help system doesn't exist in original
- { &fallbackDescs[3], { "a_Data.MHK", 0 } },
- { &fallbackDescs[4], { "a_Data.MHK", "t_Data1.MHK" , 0 } },
- { 0, { 0 } }
-};
-
-} // End of namespace Mohawk
-
static const ADParams detectionParams = {
// Pointer to ADGameDescription or its superset structure
(const byte *)Mohawk::gameDescriptions,
@@ -1011,7 +150,11 @@ static const ADParams detectionParams = {
// Flags
0,
// Additional GUI options (for every game)
- Common::GUIO_NONE
+ Common::GUIO_NONE,
+ // Maximum directory depth
+ 2,
+ // List of directory globs
+ directoryGlobs
};
class MohawkMetaEngine : public AdvancedMetaEngine {
diff --git a/engines/mohawk/detection_tables.h b/engines/mohawk/detection_tables.h
new file mode 100644
index 0000000000..7470dbf1dd
--- /dev/null
+++ b/engines/mohawk/detection_tables.h
@@ -0,0 +1,1030 @@
+/* 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$
+ *
+ */
+
+namespace Mohawk {
+
+static const MohawkGameDescription gameDescriptions[] = {
+ // Myst
+ // English Windows 3.11
+ // From clone2727
+ {
+ {
+ "myst",
+ "",
+ AD_ENTRY1("MYST.DAT", "ae3258c9c90128d274aa6a790b3ad181"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_MYST,
+ 0,
+ 0,
+ },
+
+ // Myst Demo
+ // English Windows 3.11
+ // From CD-ROM Today July, 1994
+ {
+ {
+ "myst",
+ "Demo",
+ AD_ENTRY1("DEMO.DAT", "c39303dd53fb5c4e7f3c23231c606cd0"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_DEMO,
+ Common::GUIO_NONE
+ },
+ GType_MYST,
+ GF_DEMO,
+ 0,
+ },
+
+ // Myst
+ // German Windows 3.11
+ // From clone2727
+ {
+ {
+ "myst",
+ "",
+ AD_ENTRY1("MYST.DAT", "4beb3366ed3f3b9bfb6e81a14a43bdcc"),
+ Common::DE_DEU,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_MYST,
+ 0,
+ 0,
+ },
+
+ // Myst
+ // German Windows 3.11
+ // From LordHoto
+ {
+ {
+ "myst",
+ "",
+ AD_ENTRY1("MYST.DAT", "e0937cca1ab125e48e30dc3cd5046ddf"),
+ Common::DE_DEU,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_MYST,
+ 0,
+ 0,
+ },
+
+ // Myst
+ // Spanish Windows ?
+ // From jvprat
+ {
+ {
+ "myst",
+ "",
+ AD_ENTRY1("MYST.DAT", "f7e7d7ca69934f1351b5acd4fe4d44c2"),
+ Common::ES_ESP,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_MYST,
+ 0,
+ 0,
+ },
+
+ // Myst
+ // Japanese Windows 3.11
+ // From clone2727
+ {
+ {
+ "myst",
+ "",
+ AD_ENTRY1("MYST.DAT", "032c88e3b7e8db4ca475e7b7db9a66bb"),
+ Common::JA_JPN,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_MYST,
+ 0,
+ 0,
+ },
+
+ // Myst
+ // French Windows 3.11
+ // From Strangerke
+ {
+ {
+ "myst",
+ "",
+ AD_ENTRY1("MYST.DAT", "d631d42567a941c67c78f2e491f4ea58"),
+ Common::FR_FRA,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_MYST,
+ 0,
+ 0,
+ },
+
+ // Making of Myst
+ // English Windows 3.11
+ // From clone2727
+ {
+ {
+ "MakingOfMyst",
+ "",
+ AD_ENTRY1("MAKING.DAT", "f6387e8f0f7b8a3e42c95294315d6a0e"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_MAKINGOF,
+ 0,
+ 0,
+ },
+
+ // Making of Myst
+ // Japanese Windows 3.11
+ // From clone2727
+ {
+ {
+ "MakingOfMyst",
+ "",
+ AD_ENTRY1("MAKING.DAT", "03ff62607e64419ab2b6ebf7b7bcdf63"),
+ Common::JA_JPN,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_MAKINGOF,
+ 0,
+ 0,
+ },
+
+ // Myst Masterpiece Edition
+ // English Windows
+ // From clone2727
+ {
+ {
+ "myst",
+ "Masterpiece Edition",
+ AD_ENTRY1("MYST.DAT", "c4cae9f143b5947262e6cb2397e1617e"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_MYST,
+ GF_ME,
+ 0,
+ },
+
+ // Myst Masterpiece Edition
+ // English Windows
+ // From clone2727
+ {
+ {
+ "myst",
+ "Masterpiece Edition",
+ AD_ENTRY1("MYST.DAT", "c4cae9f143b5947262e6cb2397e1617e"),
+ Common::EN_ANY,
+ Common::kPlatformMacintosh,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_MYST,
+ GF_ME,
+ 0,
+ },
+
+ // Myst Masterpiece Edition
+ // German Windows
+ // From DrMcCoy (Included in "Myst: Die Trilogie")
+ {
+ {
+ "myst",
+ "Masterpiece Edition",
+ AD_ENTRY1("MYST.DAT", "f88e0ace66dbca78eebdaaa1d3314ceb"),
+ Common::DE_DEU,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_MYST,
+ GF_ME,
+ 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
+ {
+ {
+ "riven",
+ "",
+ AD_ENTRY1("a_Data.MHK", "71145fdecbd68a0cfc292c2fbddf8e08"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_RIVEN,
+ 0,
+ 0,
+ },
+
+ // Riven: The Sequel to Myst
+ // Version 1.03 (5CD)
+ // From ST
+ {
+ {
+ "riven",
+ "",
+ AD_ENTRY1("a_Data.MHK", "d8ccae34a0e3c709135a73f449b783be"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_RIVEN,
+ 0,
+ 0,
+ },
+
+ // Riven: The Sequel to Myst
+ // Version 1.? (5CD)
+ // From jvprat
+ {
+ {
+ "riven",
+ "",
+ AD_ENTRY1("a_Data.MHK", "249e8c995d191b03ee94c892c0eac775"),
+ Common::ES_ESP,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_RIVEN,
+ 0,
+ 0,
+ },
+
+ // Riven: The Sequel to Myst
+ // Version 1.? (DVD, From "Myst 10th Anniversary Edition")
+ // From Clone2727
+ {
+ {
+ "riven",
+ "DVD",
+ AD_ENTRY1("a_Data.MHK", "08fcaa5d5a2a01d7a5a6960f497212fe"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_RIVEN,
+ GF_DVD,
+ 0,
+ },
+
+ // Riven: The Sequel to Myst
+ // Version 1.0 (DVD, From "Myst: Die Trilogie")
+ // From DrMcCoy
+ {
+ {
+ "riven",
+ "",
+ AD_ENTRY1("a_Data.MHK", "a5fe1c91a6033eb6ee54b287578b74b9"),
+ Common::DE_DEU,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_RIVEN,
+ GF_DVD,
+ 0,
+ },
+
+ // 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
+ {
+ {
+ "riven",
+ "Demo",
+ AD_ENTRY1("a_Data.MHK", "bae6b03bd8d6eb350d35fd13f0e3139f"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_DEMO,
+ Common::GUIO_NONE
+ },
+ GType_RIVEN,
+ GF_DEMO,
+ 0,
+ },
+
+#ifdef DETECT_BRODERBUND_TITLES
+ {
+ {
+ "zoombini",
+ "",
+ AD_ENTRY1("ZOOMBINI.MHK", "98b758fec55104c096cfd129048be9a6"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_ZOOMBINI,
+ GF_HASMIDI,
+ 0
+ },
+
+ {
+ {
+ "csworld",
+ "v3.0",
+ AD_ENTRY1("C2K.MHK", "605fe88380848031bbd0ff84ade6fe40"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_CSWORLD,
+ 0,
+ 0
+ },
+
+ {
+ {
+ "csworld",
+ "v3.5",
+ AD_ENTRY1("C2K.MHK", "d4857aeb0f5e2e0c4ac556aa74f38c23"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_CSWORLD,
+ 0,
+ 0
+ },
+
+ {
+ {
+ "csamtrak",
+ "",
+ AD_ENTRY1("AMTRAK.MHK", "2f95301f0bb950d555bb7b0e3b1b7eb1"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_CSAMTRAK,
+ 0,
+ 0
+ },
+
+ {
+ {
+ "maggiess",
+ "",
+ AD_ENTRY1("MAGGIESS.MHK", "08f75fc8c0390e68fdada5ddb35d0355"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_MAGGIESS,
+ 0,
+ 0
+ },
+
+ {
+ {
+ "jamesmath",
+ "",
+ AD_ENTRY1("BRODER.MHK", "007299da8b2c6e8ec1cde9598c243024"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_JAMESMATH,
+ GF_HASMIDI,
+ 0
+ },
+
+ // This is in the NEWDATA folder, so I assume it's a newer version ;)
+ {
+ {
+ "jamesmath",
+ "",
+ AD_ENTRY1("BRODER.MHK", "53c000938a50dca92860fd9b546dd276"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_JAMESMATH,
+ GF_HASMIDI,
+ 1
+ },
+
+ {
+ {
+ "treehouse",
+ "",
+ AD_ENTRY1("MAINROOM.MHK", "12f51894d7f838af639ea9bf1bc8f45b"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_TREEHOUSE,
+ GF_HASMIDI,
+ 0
+ },
+
+ {
+ {
+ "greeneggs",
+ "",
+ AD_ENTRY1("GREEN.LB", "5df8438138186f89e71299d7b4f88d06"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_LIVINGBOOKSV3,
+ 0,
+ 0
+ },
+
+ // 32-bit version of the previous entry
+ {
+ {
+ "greeneggs",
+ "",
+ AD_ENTRY1("GREEN32.LB", "5df8438138186f89e71299d7b4f88d06"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_LIVINGBOOKSV3,
+ 0,
+ 0
+ },
+
+ {
+ {
+ "1stdegree",
+ "",
+ AD_ENTRY1("AL236_1.MHK", "3ba145492a7b8b4dee0ef4222c5639c3"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_1STDEGREE,
+ GF_HASMIDI,
+ 0
+ },
+
+ // In The 1st Degree
+ // French Windows
+ // From Strangerke
+ {
+ {
+ "1stdegree",
+ "",
+ AD_ENTRY1("AL236_1.MHK", "0e0c70b1b702b6ddca61a1192ada1282"),
+ Common::FR_FRA,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_1STDEGREE,
+ GF_HASMIDI,
+ 0
+ },
+
+ {
+ {
+ "csusa",
+ "",
+ AD_ENTRY1("USAC2K.MHK", "b8c9d3a2586f62bce3a48b50d7a700e9"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_CSUSA,
+ 0,
+ 0
+ },
+
+ {
+ {
+ "tortoise",
+ "Demo v1.0",
+ AD_ENTRY1("TORTOISE.512", "75d9a2f8339e423604a0c6e8177600a6"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_DEMO,
+ Common::GUIO_NONE
+ },
+ GType_LIVINGBOOKSV1,
+ GF_DEMO,
+ 0
+ },
+
+ {
+ {
+ "tortoise",
+ "Demo v1.1",
+ AD_ENTRY1("TORTOISE.512", "a38c99360e2bea3bfdec418469aef022"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_DEMO,
+ Common::GUIO_NONE
+ },
+ GType_LIVINGBOOKSV1,
+ GF_DEMO,
+ 0
+ },
+
+ {
+ {
+ "tortoise",
+ "Demo",
+ AD_ENTRY1("The Tortoise and the Hare Demo", "35d571806838667743c7c15a133e9335"),
+ Common::EN_ANY,
+ Common::kPlatformMacintosh,
+ ADGF_DEMO,
+ Common::GUIO_NONE
+ },
+ GType_LIVINGBOOKSV1,
+ GF_DEMO,
+ 0
+ },
+
+ {
+ {
+ "arthur",
+ "",
+ AD_ENTRY1("PAGES.512", "1550a361454ec452fe7d2328aac2003c"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_LIVINGBOOKSV1,
+ 0,
+ 0
+ },
+
+ {
+ {
+ "arthur",
+ "Demo",
+ AD_ENTRY1("PAGES.512", "a4d68cef197af1416921ca5b2e0c1e31"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_DEMO,
+ Common::GUIO_NONE
+ },
+ GType_LIVINGBOOKSV1,
+ GF_DEMO,
+ 0
+ },
+
+ {
+ {
+ "arthur",
+ "Demo v1.1",
+ AD_ENTRY1("ARTHUR.512", "f19e824e0a2f2745ed698e6aaf44f838"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_DEMO,
+ Common::GUIO_NONE
+ },
+ GType_LIVINGBOOKSV1,
+ GF_DEMO,
+ 0
+ },
+
+ {
+ {
+ "arthur",
+ "Demo",
+ AD_ENTRY1("Bookoutline", "7e2691611ff4c7b89c05221736628059"),
+ Common::EN_ANY,
+ Common::kPlatformMacintosh,
+ ADGF_DEMO,
+ Common::GUIO_NONE
+ },
+ GType_LIVINGBOOKSV1,
+ GF_DEMO,
+ 0
+ },
+
+ {
+ {
+ "arthur",
+ "Demo",
+ AD_ENTRY1("Arthur's Teacher Trouble Demo", "dcbd8af6bf25854df8ad36fd13665d08"),
+ Common::EN_ANY,
+ Common::kPlatformMacintosh,
+ ADGF_DEMO,
+ Common::GUIO_NONE
+ },
+ GType_LIVINGBOOKSV1,
+ GF_DEMO,
+ 0
+ },
+
+ {
+ {
+ "grandma",
+ "Demo v1.0",
+ AD_ENTRY1("PAGES.512", "95d9f4b035bf5d15c57a9189f231b0f8"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_DEMO,
+ Common::GUIO_NONE
+ },
+ GType_LIVINGBOOKSV1,
+ GF_DEMO,
+ 0
+ },
+
+ {
+ {
+ "grandma",
+ "Demo v1.1",
+ AD_ENTRY1("GRANDMA.512", "72a4d5fb1b3f06b5f75425635d42ce2e"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_DEMO,
+ Common::GUIO_NONE
+ },
+ GType_LIVINGBOOKSV1,
+ GF_DEMO,
+ 0
+ },
+
+ {
+ {
+ "grandma",
+ "Demo",
+ AD_ENTRY1("Bookoutline", "553c93891b9631d1e1d269599e1efa6c"),
+ Common::EN_ANY,
+ Common::kPlatformMacintosh,
+ ADGF_DEMO,
+ Common::GUIO_NONE
+ },
+ GType_LIVINGBOOKSV1,
+ GF_DEMO,
+ 0
+ },
+
+ {
+ {
+ "grandma",
+ "Demo",
+ AD_ENTRY1("Just Grandma and Me Demo", "552d8729fa77a4a83c88283c7d79bd31"),
+ Common::EN_ANY,
+ Common::kPlatformMacintosh,
+ ADGF_DEMO,
+ Common::GUIO_NONE
+ },
+ GType_LIVINGBOOKSV1,
+ GF_DEMO,
+ 0
+ },
+
+ {
+ {
+ "ruff",
+ "Demo",
+ AD_ENTRY1("RUFF.512", "2ba1aa65177c816e156db648c398d362"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_DEMO,
+ Common::GUIO_NONE
+ },
+ GType_LIVINGBOOKSV1,
+ GF_DEMO,
+ 0
+ },
+
+ {
+ {
+ "ruff",
+ "Demo",
+ AD_ENTRY1("Ruff's Bone Demo", "22553ac2ceb2a166bdf1def6ad348532"),
+ Common::EN_ANY,
+ Common::kPlatformMacintosh,
+ ADGF_DEMO,
+ Common::GUIO_NONE
+ },
+ GType_LIVINGBOOKSV1,
+ GF_DEMO,
+ 0
+ },
+
+ {
+ {
+ "newkid",
+ "Demo v1.0",
+ AD_ENTRY1("NEWKID.512", "2b9d94763a50d514c04a3af488934f73"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_DEMO,
+ Common::GUIO_NONE
+ },
+ GType_LIVINGBOOKSV1,
+ GF_DEMO,
+ 0
+ },
+
+ {
+ {
+ "newkid",
+ "Demo v1.1",
+ AD_ENTRY1("NEWKID.512", "41e975b7390c626f8d1058a34f9d9b2e"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_DEMO,
+ Common::GUIO_NONE
+ },
+ GType_LIVINGBOOKSV1,
+ GF_DEMO,
+ 0
+ },
+
+ {
+ {
+ "newkid",
+ "Demo",
+ AD_ENTRY1("The New Kid on the Block Demo", "7d33237e0ea452a97f2a3acdfb9e1286"),
+ Common::EN_ANY,
+ Common::kPlatformMacintosh,
+ ADGF_DEMO,
+ Common::GUIO_NONE
+ },
+ GType_LIVINGBOOKSV1,
+ GF_DEMO,
+ 0
+ },
+
+ {
+ {
+ "arthurrace",
+ "",
+ AD_ENTRY1("RACE.LB", "1645f36bcb36e440d928e920aa48c373"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_LIVINGBOOKSV3,
+ 0,
+ 0
+ },
+
+ // 32-bit version of the previous entry
+ {
+ {
+ "arthurrace",
+ "",
+ AD_ENTRY1("RACE32.LB", "292a05bc48c1dd9583821a4181a02ef2"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_LIVINGBOOKSV3,
+ 0,
+ 0
+ },
+
+ {
+ {
+ "arthurbday",
+ "Demo",
+ AD_ENTRY1("BIRTHDAY.512", "fb73e387cfec65c5c930db068a8f468a"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_DEMO,
+ Common::GUIO_NONE
+ },
+ GType_LIVINGBOOKSV1,
+ GF_DEMO,
+ 0
+ },
+
+ {
+ {
+ "arthurbday",
+ "Demo",
+ AD_ENTRY1("Arthur's Birthday Demo", "0d974ec635eea615475368e865f1b1c8"),
+ Common::EN_ANY,
+ Common::kPlatformMacintosh,
+ ADGF_DEMO,
+ Common::GUIO_NONE
+ },
+ GType_LIVINGBOOKSV1,
+ GF_DEMO,
+ 0
+ },
+
+ {
+ {
+ "lilmonster",
+ "",
+ AD_ENTRY1("MONSTER.512", "e7b24bf8f59106b5c4df51b39eb8c0ef"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_LIVINGBOOKSV1,
+ 0,
+ 0
+ },
+
+ {
+ {
+ "lilmonster",
+ "",
+ AD_ENTRY1("BookOutline", "970409f9d967d63c05e63113f8e78fe2"),
+ Common::EN_ANY,
+ Common::kPlatformMacintosh,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_LIVINGBOOKSV1,
+ 0,
+ 0
+ },
+#endif
+
+ { AD_TABLE_END_MARKER, 0, 0, 0 }
+};
+
+//////////////////////////////
+//Fallback detection
+//////////////////////////////
+
+static const MohawkGameDescription fallbackDescs[] = {
+ {
+ {
+ "myst",
+ "unknown",
+ AD_ENTRY1(0, 0),
+ Common::UNK_LANG,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_MYST,
+ 0,
+ 0
+ },
+
+ {
+ {
+ "MakingOfMyst",
+ "unknown",
+ AD_ENTRY1(0, 0),
+ Common::UNK_LANG,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_MAKINGOF,
+ 0,
+ 0
+ },
+
+ {
+ {
+ "myst",
+ "unknown (Masterpiece Edition)",
+ AD_ENTRY1(0, 0),
+ Common::UNK_LANG,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_MYST,
+ GF_ME,
+ 0
+ },
+
+ {
+ {
+ "riven",
+ "unknown",
+ AD_ENTRY1(0, 0),
+ Common::UNK_LANG,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_RIVEN,
+ 0,
+ 0
+ },
+
+ {
+ {
+ "riven",
+ "unknown (DVD)",
+ AD_ENTRY1(0, 0),
+ Common::UNK_LANG,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ Common::GUIO_NONE
+ },
+ GType_RIVEN,
+ GF_DVD,
+ 0
+ }
+};
+
+static const ADFileBasedFallback fileBased[] = {
+ { &fallbackDescs[0], { "MYST.DAT", 0 } },
+ { &fallbackDescs[1], { "MAKING.DAT", 0 } },
+ { &fallbackDescs[2], { "MYST.DAT", "Help.dat", 0 } }, // Help system doesn't exist in original
+ { &fallbackDescs[3], { "a_Data.MHK", 0 } },
+ { &fallbackDescs[4], { "a_Data.MHK", "t_Data1.MHK" , 0 } },
+ { 0, { 0 } }
+};
+
+} // End of Namespace Mohawk
diff --git a/engines/mohawk/dialogs.cpp b/engines/mohawk/dialogs.cpp
index c5327dbeea..c09763cbb3 100644
--- a/engines/mohawk/dialogs.cpp
+++ b/engines/mohawk/dialogs.cpp
@@ -30,6 +30,7 @@
#include "gui/GuiManager.h"
#include "common/savefile.h"
+#include "common/translation.h"
namespace Mohawk {
@@ -77,11 +78,11 @@ enum {
};
MystOptionsDialog::MystOptionsDialog(MohawkEngine_Myst* vm) : GUI::OptionsDialog("", 120, 120, 360, 200), _vm(vm) {
- _zipModeCheckbox = new GUI::CheckboxWidget(this, 15, 10, 300, 15, "Zip Mode Activated", kZipCmd, 'Z');
- _transitionsCheckbox = new GUI::CheckboxWidget(this, 15, 30, 300, 15, "Transitions Enabled", kTransCmd, 'T');
+ _zipModeCheckbox = new GUI::CheckboxWidget(this, 15, 10, 300, 15, _("~Z~ip Mode Activated"), 0, kZipCmd);
+ _transitionsCheckbox = new GUI::CheckboxWidget(this, 15, 30, 300, 15, _("~T~ransitions Enabled"), 0, kTransCmd);
- new GUI::ButtonWidget(this, 95, 160, 120, 25, "OK", GUI::kOKCmd, 'O');
- new GUI::ButtonWidget(this, 225, 160, 120, 25, "Cancel", GUI::kCloseCmd, 'C');
+ new GUI::ButtonWidget(this, 95, 160, 120, 25, _("~O~K"), 0, GUI::kOKCmd);
+ new GUI::ButtonWidget(this, 225, 160, 120, 25, _("~C~ancel"), 0, GUI::kCloseCmd);
}
MystOptionsDialog::~MystOptionsDialog() {
@@ -111,11 +112,11 @@ void MystOptionsDialog::handleCommand(GUI::CommandSender *sender, uint32 cmd, ui
}
RivenOptionsDialog::RivenOptionsDialog(MohawkEngine_Riven* vm) : GUI::OptionsDialog("", 120, 120, 360, 200), _vm(vm) {
- _zipModeCheckbox = new GUI::CheckboxWidget(this, 15, 10, 300, 15, "Zip Mode Activated", kZipCmd, 'Z');
- _waterEffectCheckbox = new GUI::CheckboxWidget(this, 15, 30, 300, 15, "Water Effect Enabled", kWaterCmd, 'W');
+ _zipModeCheckbox = new GUI::CheckboxWidget(this, 15, 10, 300, 15, _("~Z~ip Mode Activated"), 0, kZipCmd);
+ _waterEffectCheckbox = new GUI::CheckboxWidget(this, 15, 30, 300, 15, _("~W~ater Effect Enabled"), 0, kWaterCmd);
- new GUI::ButtonWidget(this, 95, 160, 120, 25, "OK", GUI::kOKCmd, 'O');
- new GUI::ButtonWidget(this, 225, 160, 120, 25, "Cancel", GUI::kCloseCmd, 'C');
+ new GUI::ButtonWidget(this, 95, 160, 120, 25, _("~O~K"), 0, GUI::kOKCmd);
+ new GUI::ButtonWidget(this, 225, 160, 120, 25, _("~C~ancel"), 0, GUI::kCloseCmd);
}
RivenOptionsDialog::~RivenOptionsDialog() {
diff --git a/engines/mohawk/graphics.cpp b/engines/mohawk/graphics.cpp
index 2ddfb47575..1974aec9c2 100644
--- a/engines/mohawk/graphics.cpp
+++ b/engines/mohawk/graphics.cpp
@@ -466,27 +466,22 @@ void RivenGraphics::runScheduledTransition() {
// transitions were found by hacking scripts.
switch (_scheduledTransition) {
+ case 0: // Swipe Left
+ case 1: // Swipe Right
+ case 2: // Swipe Up
+ case 3: // Swipe Down
case 12: // Pan Left
- warning ("STUB: Pan left");
- break;
case 13: // Pan Right
- warning ("STUB: Pan right");
- break;
case 14: // Pan Up
- warning ("STUB: Pan up");
- break;
case 15: // Pan Down
- warning ("STUB: Pan down");
- break;
case 16: // Dissolve
case 17: // Dissolve (tspit CARD 155)
- warning ("STUB: Dissolve");
break;
default:
- if (_scheduledTransition < 12)
- error ("Found unused transition %d", _scheduledTransition);
+ if (_scheduledTransition >= 4 && _scheduledTransition <= 11)
+ error("Found unused transition %d", _scheduledTransition);
else
- error ("Found unknown transition %d", _scheduledTransition);
+ error("Found unknown transition %d", _scheduledTransition);
}
// For now, just copy the image to screen without doing any transition.
@@ -607,19 +602,23 @@ void RivenGraphics::showInventory() {
if (_vm->getFeatures() & GF_DEMO || _vm->getCurStack() == aspit)
return;
- // There are three books and three vars. However, there's only
- // a possible two combinations. Either you have only Atrus'
- // journal or you have all three books.
- // bool hasAtrusBook = *_vm->matchVarToString("aatrusbook") != 0;
+ // There are three books and three vars. We have three different
+ // combinations. At the start you have just Atrus' journal. Later,
+ // you get Catherine's journal and the trap book. Near the end,
+ // you lose the trap book and have just the two journals.
+
bool hasCathBook = *_vm->matchVarToString("acathbook") != 0;
- // bool hasTrapBook = *_vm->matchVarToString("atrapbook") != 0;
+ bool hasTrapBook = *_vm->matchVarToString("atrapbook") != 0;
if (!hasCathBook) {
- drawInventoryImage(101, g_atrusJournalRectSolo);
+ drawInventoryImage(101, g_atrusJournalRect1);
+ } else if (!hasTrapBook) {
+ drawInventoryImage(101, g_atrusJournalRect2);
+ drawInventoryImage(102, g_cathJournalRect2);
} else {
- drawInventoryImage(101, g_atrusJournalRect);
- drawInventoryImage(102, g_cathJournalRect);
- drawInventoryImage(100, g_trapBookRect);
+ drawInventoryImage(101, g_atrusJournalRect3);
+ drawInventoryImage(102, g_cathJournalRect3);
+ drawInventoryImage(100, g_trapBookRect3);
}
_vm->_system->updateScreen();
diff --git a/engines/mohawk/resource.cpp b/engines/mohawk/resource.cpp
index 62a857b90b..74efd6770f 100644
--- a/engines/mohawk/resource.cpp
+++ b/engines/mohawk/resource.cpp
@@ -103,7 +103,7 @@ void MohawkArchive::open(Common::SeekableReadStream *stream) {
else
debug (3, "Type[%02d]: Tag = \'%s\' ResTable Offset = %04x NameTable Offset = %04x", i, tag2str(_types[i].tag), _types[i].resource_table_offset, _types[i].name_table_offset);
- //Resource Table
+ // Resource Table
_mhk->seek(_rsrc.abs_offset + _types[i].resource_table_offset);
_types[i].resTable.resources = _mhk->readUint16BE();
diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp
index c646855bc7..07b08dc220 100644
--- a/engines/mohawk/riven.cpp
+++ b/engines/mohawk/riven.cpp
@@ -29,6 +29,7 @@
#include "common/keyboard.h"
#include "mohawk/graphics.h"
+#include "mohawk/resource.h"
#include "mohawk/riven.h"
#include "mohawk/riven_external.h"
#include "mohawk/riven_saveload.h"
@@ -37,10 +38,12 @@
namespace Mohawk {
-Common::Rect *g_atrusJournalRectSolo;
-Common::Rect *g_atrusJournalRect;
-Common::Rect *g_cathJournalRect;
-Common::Rect *g_trapBookRect;
+Common::Rect *g_atrusJournalRect1;
+Common::Rect *g_atrusJournalRect2;
+Common::Rect *g_cathJournalRect2;
+Common::Rect *g_atrusJournalRect3;
+Common::Rect *g_cathJournalRect3;
+Common::Rect *g_trapBookRect3;
MohawkEngine_Riven::MohawkEngine_Riven(OSystem *syst, const MohawkGameDescription *gamedesc) : MohawkEngine(syst, gamedesc) {
_showHotspots = false;
@@ -49,20 +52,27 @@ MohawkEngine_Riven::MohawkEngine_Riven(OSystem *syst, const MohawkGameDescriptio
_activatedSLST = false;
_ignoreNextMouseUp = false;
_extrasFile = NULL;
+ _curStack = aspit;
+ _hotspots = NULL;
- // Attempt to let game run from the CDs
- // NOTE: assets2 contains higher quality audio than assets1
- const Common::FSNode gameDataDir(ConfMan.get("path"));
+ // NOTE: We can never really support CD swapping. All of the music files
+ // (*_Sounds.mhk) are stored on disc 1. They are copied to the hard drive
+ // during install and used from there. The same goes for the extras.mhk
+ // file. The following directories allow Riven to be played directly
+ // from the DVD.
+ const Common::FSNode gameDataDir(ConfMan.get("path"));
SearchMan.addSubDirectoryMatching(gameDataDir, "all");
SearchMan.addSubDirectoryMatching(gameDataDir, "data");
SearchMan.addSubDirectoryMatching(gameDataDir, "exe");
- SearchMan.addSubDirectoryMatching(gameDataDir, "assets2");
-
- g_atrusJournalRectSolo = new Common::Rect(295, 402, 313, 426);
- g_atrusJournalRect = new Common::Rect(222, 402, 240, 426);
- g_cathJournalRect = new Common::Rect(291, 408, 311, 419);
- g_trapBookRect = new Common::Rect(363, 396, 386, 432);
+ SearchMan.addSubDirectoryMatching(gameDataDir, "assets1");
+
+ g_atrusJournalRect1 = new Common::Rect(295, 402, 313, 426);
+ g_atrusJournalRect2 = new Common::Rect(259, 402, 278, 426);
+ g_cathJournalRect2 = new Common::Rect(328, 408, 348, 419);
+ g_atrusJournalRect3 = new Common::Rect(222, 402, 240, 426);
+ g_cathJournalRect3 = new Common::Rect(291, 408, 311, 419);
+ g_trapBookRect3 = new Common::Rect(363, 396, 386, 432);
}
MohawkEngine_Riven::~MohawkEngine_Riven() {
@@ -71,15 +81,17 @@ MohawkEngine_Riven::~MohawkEngine_Riven() {
delete _externalScriptHandler;
delete _extrasFile;
delete _saveLoad;
+ delete _scriptMan;
delete[] _vars;
- delete _loadDialog;
delete _optionsDialog;
delete _rnd;
- delete g_atrusJournalRectSolo;
- delete g_atrusJournalRect;
- delete g_cathJournalRect;
- delete g_trapBookRect;
- _cardData.scripts.clear();
+ delete[] _hotspots;
+ delete g_atrusJournalRect1;
+ delete g_atrusJournalRect2;
+ delete g_cathJournalRect2;
+ delete g_atrusJournalRect3;
+ delete g_cathJournalRect3;
+ delete g_trapBookRect3;
}
GUI::Debugger *MohawkEngine_Riven::getDebugger() {
@@ -94,9 +106,8 @@ Common::Error MohawkEngine_Riven::run() {
_console = new RivenConsole(this);
_saveLoad = new RivenSaveLoad(this, _saveFileMan);
_externalScriptHandler = new RivenExternal(this);
- _loadDialog = new GUI::SaveLoadChooser("Load Game:", "Load");
- _loadDialog->setSaveMode(false);
_optionsDialog = new RivenOptionsDialog(this);
+ _scriptMan = new RivenScriptManager(this);
_rnd = new Common::RandomSource();
g_eventRec.registerRandomSource(*_rnd, "riven");
@@ -339,13 +350,13 @@ void MohawkEngine_Riven::refreshCard() {
}
void MohawkEngine_Riven::loadCard(uint16 id) {
- // NOTE: Do not clear the card scripts because it may delete a currently running script!
+ // NOTE: The card scripts are cleared by the RivenScriptManager automatically.
Common::SeekableReadStream* inStream = getRawData(ID_CARD, id);
_cardData.name = inStream->readSint16BE();
_cardData.zipModePlace = inStream->readUint16BE();
- _cardData.scripts = RivenScript::readScripts(this, inStream);
+ _cardData.scripts = _scriptMan->readScripts(inStream);
_cardData.hasData = true;
delete inStream;
@@ -363,7 +374,10 @@ void MohawkEngine_Riven::loadCard(uint16 id) {
}
void MohawkEngine_Riven::loadHotspots(uint16 id) {
- // NOTE: Do not clear the hotspots because it may delete a currently running script!
+ // Clear old hotspots
+ delete[] _hotspots;
+
+ // NOTE: The hotspot scripts are cleared by the RivenScriptManager automatically.
Common::SeekableReadStream* inStream = getRawData(ID_HSPT, id);
@@ -405,7 +419,7 @@ void MohawkEngine_Riven::loadHotspots(uint16 id) {
_hotspots[i].zipModeHotspot = inStream->readUint16BE();
// Read in the scripts now
- _hotspots[i].scripts = RivenScript::readScripts(this, inStream);
+ _hotspots[i].scripts = _scriptMan->readScripts(inStream);
}
delete inStream;
@@ -480,26 +494,37 @@ void MohawkEngine_Riven::checkInventoryClick() {
*matchVarToString("returncardid") = _curCard;
// See RivenGraphics::showInventory() for an explanation
- // of why only this variable is used.
+ // of the variables' meanings.
bool hasCathBook = *matchVarToString("acathbook") != 0;
+ bool hasTrapBook = *matchVarToString("atrapbook") != 0;
// Go to the book if a hotspot contains the mouse
if (!hasCathBook) {
- if (g_atrusJournalRectSolo->contains(_mousePos)) {
+ if (g_atrusJournalRect1->contains(_mousePos)) {
_gfx->hideInventory();
changeToStack(aspit);
changeToCard(5);
}
+ } else if (!hasTrapBook) {
+ if (g_atrusJournalRect2->contains(_mousePos)) {
+ _gfx->hideInventory();
+ changeToStack(aspit);
+ changeToCard(5);
+ } else if (g_cathJournalRect2->contains(_mousePos)) {
+ _gfx->hideInventory();
+ changeToStack(aspit);
+ changeToCard(6);
+ }
} else {
- if (g_atrusJournalRect->contains(_mousePos)) {
+ if (g_atrusJournalRect3->contains(_mousePos)) {
_gfx->hideInventory();
changeToStack(aspit);
changeToCard(5);
- } else if (g_cathJournalRect->contains(_mousePos)) {
+ } else if (g_cathJournalRect3->contains(_mousePos)) {
_gfx->hideInventory();
changeToStack(aspit);
changeToCard(6);
- } else if (g_trapBookRect->contains(_mousePos)) {
+ } else if (g_trapBookRect3->contains(_mousePos)) {
_gfx->hideInventory();
changeToStack(aspit);
changeToCard(7);
@@ -583,7 +608,19 @@ void MohawkEngine_Riven::runHotspotScript(uint16 hotspot, uint16 scriptType) {
}
void MohawkEngine_Riven::runLoadDialog() {
- runDialog(*_loadDialog);
+ GUI::SaveLoadChooser slc("Load Game:", "Load");
+ slc.setSaveMode(false);
+
+ Common::String gameId = ConfMan.get("gameid");
+
+ const EnginePlugin *plugin = 0;
+ EngineMan.findGame(gameId, &plugin);
+
+ int slot = slc.runModal(plugin, ConfMan.getActiveDomainName());
+ if (slot >= 0)
+ loadGameState(slot);
+
+ slc.close();
}
Common::Error MohawkEngine_Riven::loadGameState(int slot) {
@@ -618,4 +655,4 @@ bool ZipMode::operator== (const ZipMode &z) const {
return z.name == name && z.id == id;
}
-}
+} // End of namespace Mohawk
diff --git a/engines/mohawk/riven.h b/engines/mohawk/riven.h
index 11c3a4c0cb..631285455e 100644
--- a/engines/mohawk/riven.h
+++ b/engines/mohawk/riven.h
@@ -68,10 +68,12 @@ enum {
// Rects for the inventory object positions (initialized in
// MohawkEngine_Riven's constructor).
-extern Common::Rect *g_atrusJournalRectSolo;
-extern Common::Rect *g_atrusJournalRect;
-extern Common::Rect *g_cathJournalRect;
-extern Common::Rect *g_trapBookRect;
+extern Common::Rect *g_atrusJournalRect1;
+extern Common::Rect *g_atrusJournalRect2;
+extern Common::Rect *g_cathJournalRect2;
+extern Common::Rect *g_atrusJournalRect3;
+extern Common::Rect *g_cathJournalRect3;
+extern Common::Rect *g_trapBookRect3;
struct RivenHotspot {
uint16 blstID;
@@ -111,6 +113,7 @@ public:
RivenGraphics *_gfx;
RivenExternal *_externalScriptHandler;
Common::RandomSource *_rnd;
+ RivenScriptManager *_scriptMan;
Card _cardData;
@@ -126,7 +129,6 @@ private:
MohawkArchive *_extrasFile; // We need a separate handle for the extra data
RivenConsole *_console;
RivenSaveLoad *_saveLoad;
- GUI::SaveLoadChooser *_loadDialog;
RivenOptionsDialog *_optionsDialog;
// Stack/Card-related functions and variables
diff --git a/engines/mohawk/riven_external.cpp b/engines/mohawk/riven_external.cpp
index 4e6bba1c2a..67d621a54c 100644
--- a/engines/mohawk/riven_external.cpp
+++ b/engines/mohawk/riven_external.cpp
@@ -284,9 +284,14 @@ void RivenExternal::xaatrusbookprevpage(uint16 argc, uint16 *argv) {
return;
(*page)--;
- // TODO: Play the page turning sound
+ // Play the page turning sound
+ if (_vm->getFeatures() & GF_DEMO)
+ _vm->_sound->playSound(4, false);
+ else
+ _vm->_sound->playSound(3, false);
// Now update the screen :)
+ _vm->_gfx->scheduleTransition(1);
_vm->_gfx->updateScreen();
}
@@ -299,9 +304,14 @@ void RivenExternal::xaatrusbooknextpage(uint16 argc, uint16 *argv) {
return;
(*page)++;
- // TODO: Play the page turning sound
+ // Play the page turning sound
+ if (_vm->getFeatures() & GF_DEMO)
+ _vm->_sound->playSound(5, false);
+ else
+ _vm->_sound->playSound(4, false);
// Now update the screen :)
+ _vm->_gfx->scheduleTransition(0);
_vm->_gfx->updateScreen();
}
@@ -349,9 +359,11 @@ void RivenExternal::xacathbookprevpage(uint16 argc, uint16 *argv) {
return;
(*page)--;
- // TODO: Play the page turning sound
+ // Play the page turning sound
+ _vm->_sound->playSound(5, false);
// Now update the screen :)
+ _vm->_gfx->scheduleTransition(3);
_vm->_gfx->updateScreen();
}
@@ -364,9 +376,11 @@ void RivenExternal::xacathbooknextpage(uint16 argc, uint16 *argv) {
return;
(*page)++;
- // TODO: Play the page turning sound
+ // Play the page turning sound
+ _vm->_sound->playSound(6, false);
// Now update the screen :)
+ _vm->_gfx->scheduleTransition(2);
_vm->_gfx->updateScreen();
}
@@ -380,12 +394,20 @@ void RivenExternal::xtrapbookback(uint16 argc, uint16 *argv) {
void RivenExternal::xatrapbookclose(uint16 argc, uint16 *argv) {
// Close the trap book
*_vm->matchVarToString("atrap") = 0;
+
+ // Play the page turning sound
+ _vm->_sound->playSound(8, false);
+
_vm->refreshCard();
}
void RivenExternal::xatrapbookopen(uint16 argc, uint16 *argv) {
// Open the trap book
*_vm->matchVarToString("atrap") = 1;
+
+ // Play the page turning sound
+ _vm->_sound->playSound(9, false);
+
_vm->refreshCard();
}
@@ -437,7 +459,11 @@ void RivenExternal::xblabbookprevpage(uint16 argc, uint16 *argv) {
return;
(*page)--;
+ // Play the page turning sound
+ _vm->_sound->playSound(22, false);
+
// Now update the screen :)
+ _vm->_gfx->scheduleTransition(1);
_vm->_gfx->updateScreen();
}
@@ -450,7 +476,11 @@ void RivenExternal::xblabbooknextpage(uint16 argc, uint16 *argv) {
return;
(*page)++;
+ // Play the page turning sound
+ _vm->_sound->playSound(23, false);
+
// Now update the screen :)
+ _vm->_gfx->scheduleTransition(0);
_vm->_gfx->updateScreen();
}
@@ -662,15 +692,17 @@ void RivenExternal::xvalvecontrol(uint16 argc, uint16 *argv) {
// Get the variable for the valve
uint32 *valve = _vm->matchVarToString("bvalve");
- Common::Event event;
int changeX = 0;
int changeY = 0;
+ bool done = false;
// Set the cursor to the closed position
_vm->_gfx->changeCursor(2004);
_vm->_system->updateScreen();
- for (;;) {
+ while (!done) {
+ Common::Event event;
+
while (_vm->_system->getEventManager()->pollEvent(event)) {
switch (event.type) {
case Common::EVENT_MOUSEMOVE:
@@ -682,30 +714,53 @@ void RivenExternal::xvalvecontrol(uint16 argc, uint16 *argv) {
// FIXME: These values for changes in x/y could be tweaked.
if (*valve == 0 && changeY <= -10) {
*valve = 1;
- // TODO: Play movie
+ _vm->_gfx->changeCursor(kRivenHideCursor);
+ _vm->_video->playMovieBlocking(2);
_vm->refreshCard();
} else if (*valve == 1) {
if (changeX >= 0 && changeY >= 10) {
*valve = 0;
- // TODO: Play movie
+ _vm->_gfx->changeCursor(kRivenHideCursor);
+ _vm->_video->playMovieBlocking(3);
_vm->refreshCard();
} else if (changeX <= -10 && changeY <= 10) {
*valve = 2;
- // TODO: Play movie
+ _vm->_gfx->changeCursor(kRivenHideCursor);
+ _vm->_video->playMovieBlocking(1);
_vm->refreshCard();
}
} else if (*valve == 2 && changeX >= 10) {
*valve = 1;
- // TODO: Play movie
+ _vm->_gfx->changeCursor(kRivenHideCursor);
+ _vm->_video->playMovieBlocking(4);
_vm->refreshCard();
}
- return;
+ done = true;
default:
break;
}
}
_vm->_system->delayMillis(10);
}
+
+ // If we changed state and the new state is that the valve is flowing to
+ // the boiler, we need to update the boiler state.
+ if (*valve == 1) {
+ if (*_vm->matchVarToString("bidvlv") == 1) { // Check which way the water is going at the boiler
+ if (*_vm->matchVarToString("bblrarm") == 1) {
+ // If the pipe is open, make sure the water is drained out
+ *_vm->matchVarToString("bheat") = 0;
+ *_vm->matchVarToString("bblrwtr") = 0;
+ } else {
+ // If the pipe is closed, fill the boiler again
+ *_vm->matchVarToString("bheat") = *_vm->matchVarToString("bblrvalve");
+ *_vm->matchVarToString("bblrwtr") = 1;
+ }
+ } else {
+ // Have the grating inside the boiler match the switch outside
+ *_vm->matchVarToString("bblrgrt") = (*_vm->matchVarToString("bblrsw") == 1) ? 0 : 1;
+ }
+ }
}
void RivenExternal::xbchipper(uint16 argc, uint16 *argv) {
@@ -960,33 +1015,60 @@ void RivenExternal::xjtunnel106_pictfix(uint16 argc, uint16 *argv) {
}
void RivenExternal::xvga1300_carriage(uint16 argc, uint16 *argv) {
- // TODO: This function is supposed to do a lot more, something like this (pseudocode):
-
- // Show level pull movie
- // Set transition up
- // Change to up card
- // Show movie of carriage coming down
- // Set transition down
- // Change back to card 276
- // Show movie of carriage coming down
- // if jgallows == 0
- // Set up timer
- // Enter new input loop
- // if you click within the time
- // move forward
- // set transition right
- // change to card right
- // show movie of ascending
- // change to card 263
- // else
- // show movie of carriage ascending only
- // else
- // show movie of carriage ascending only
-
-
- // For now, if the gallows base is closed, assume ascension and move to that card.
- if (*_vm->matchVarToString("jgallows") == 0)
- _vm->changeToCard(_vm->matchRMAPToCard(0x17167));
+ // Run the gallows's carriage
+
+ _vm->_gfx->changeCursor(kRivenHideCursor); // Hide the cursor
+ _vm->_video->playMovieBlocking(1); // Play handle movie
+ _vm->_gfx->scheduleTransition(15); // Set pan down transition
+ _vm->changeToCard(_vm->matchRMAPToCard(0x18e77)); // Change to card facing up
+ _vm->_gfx->changeCursor(kRivenHideCursor); // Hide the cursor (again)
+ _vm->_video->playMovieBlocking(4); // Play carriage beginning to drop
+ _vm->_gfx->scheduleTransition(14); // Set pan up transition
+ _vm->changeToCard(_vm->matchRMAPToCard(0x183a9)); // Change to card looking straight again
+ _vm->_video->playMovieBlocking(2);
+
+ uint32 *gallows = _vm->matchVarToString("jgallows");
+ if (*gallows == 1) {
+ // If the gallows is open, play the up movie and return
+ _vm->_video->playMovieBlocking(3);
+ return;
+ }
+
+ // Give the player 5 seconds to click (anywhere)
+ uint32 startTime = _vm->_system->getMillis();
+ bool gotClick = false;
+ while (_vm->_system->getMillis() - startTime <= 5000 && !gotClick) {
+ Common::Event event;
+ while (_vm->_system->getEventManager()->pollEvent(event)) {
+ switch (event.type) {
+ case Common::EVENT_MOUSEMOVE:
+ _vm->_system->updateScreen();
+ break;
+ case Common::EVENT_LBUTTONUP:
+ gotClick = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ _vm->_system->delayMillis(10);
+ }
+
+ _vm->_gfx->changeCursor(kRivenHideCursor); // Hide the cursor
+
+ if (gotClick) {
+ _vm->_gfx->scheduleTransition(16); // Schedule dissolve transition
+ _vm->changeToCard(_vm->matchRMAPToCard(0x18d4d)); // Move forward
+ _vm->_gfx->changeCursor(kRivenHideCursor); // Hide the cursor
+ _vm->_system->delayMillis(500); // Delay a half second before changing again
+ _vm->_gfx->scheduleTransition(12); // Schedule pan left transition
+ _vm->changeToCard(_vm->matchRMAPToCard(0x18ab5)); // Turn right
+ _vm->_gfx->changeCursor(kRivenHideCursor); // Hide the cursor
+ _vm->_video->playMovieBlocking(1); // Play carriage ride movie
+ _vm->changeToCard(_vm->matchRMAPToCard(0x17167)); // We have arrived at the top
+ } else
+ _vm->_video->playMovieBlocking(3); // Too slow!
}
void RivenExternal::xjdome25_resetsliders(uint16 argc, uint16 *argv) {
@@ -1043,8 +1125,10 @@ int RivenExternal::jspitElevatorLoop() {
void RivenExternal::xhandlecontrolup(uint16 argc, uint16 *argv) {
int changeLevel = jspitElevatorLoop();
+ // If we've moved the handle down, go down a floor
if (changeLevel == -1) {
- // TODO: Run movie
+ _vm->_video->playMovieBlocking(1);
+ _vm->_video->playMovieBlocking(2);
_vm->changeToCard(_vm->matchRMAPToCard(0x1e374));
}
}
@@ -1052,8 +1136,10 @@ void RivenExternal::xhandlecontrolup(uint16 argc, uint16 *argv) {
void RivenExternal::xhandlecontroldown(uint16 argc, uint16 *argv) {
int changeLevel = jspitElevatorLoop();
+ // If we've moved the handle up, go up a floor
if (changeLevel == 1) {
- // TODO: Run movie
+ _vm->_video->playMovieBlocking(1);
+ _vm->_video->playMovieBlocking(2);
_vm->changeToCard(_vm->matchRMAPToCard(0x1e374));
}
}
@@ -1061,11 +1147,29 @@ void RivenExternal::xhandlecontroldown(uint16 argc, uint16 *argv) {
void RivenExternal::xhandlecontrolmid(uint16 argc, uint16 *argv) {
int changeLevel = jspitElevatorLoop();
+ if (changeLevel == 0)
+ return;
+
+ // Play the handle moving video
+ if (changeLevel == 1)
+ _vm->_video->playMovieBlocking(7);
+ else
+ _vm->_video->playMovieBlocking(6);
+
+ // If the whark's mouth is open, close it
+ uint32 *mouthVar = _vm->matchVarToString("jwmouth");
+ if (*mouthVar == 1) {
+ _vm->_video->playMovieBlocking(3);
+ _vm->_video->playMovieBlocking(8);
+ *mouthVar = 0;
+ }
+
+ // Play the elevator video and then change the card
if (changeLevel == 1) {
- // TODO: Run movie
+ _vm->_video->playMovieBlocking(5);
_vm->changeToCard(_vm->matchRMAPToCard(0x1e597));
- } else if (changeLevel == -1) {
- // TODO: Run movie
+ } else {
+ _vm->_video->playMovieBlocking(4);
_vm->changeToCard(_vm->matchRMAPToCard(0x1e29c));
}
}
@@ -1196,9 +1300,11 @@ void RivenExternal::xogehnbookprevpage(uint16 argc, uint16 *argv) {
return;
(*page)--;
- // TODO: Play the page turning sound
+ // Play the page turning sound
+ _vm->_sound->playSound(12, false);
// Now update the screen :)
+ _vm->_gfx->scheduleTransition(1);
_vm->_gfx->updateScreen();
}
@@ -1211,13 +1317,15 @@ void RivenExternal::xogehnbooknextpage(uint16 argc, uint16 *argv) {
return;
(*page)++;
- // TODO: Play the page turning sound
+ // Play the page turning sound
+ _vm->_sound->playSound(13, false);
// Now update the screen :)
+ _vm->_gfx->scheduleTransition(0);
_vm->_gfx->updateScreen();
}
-static uint16 getComboDigit(uint32 correctCombo, uint32 digit) {
+uint16 RivenExternal::getComboDigit(uint32 correctCombo, uint32 digit) {
static const uint32 powers[] = { 100000, 10000, 1000, 100, 10, 1 };
return (correctCombo % powers[digit]) / powers[digit + 1];
}
diff --git a/engines/mohawk/riven_external.h b/engines/mohawk/riven_external.h
index 14bb51340c..bdf3fa01bc 100644
--- a/engines/mohawk/riven_external.h
+++ b/engines/mohawk/riven_external.h
@@ -38,6 +38,7 @@ public:
~RivenExternal();
void runCommand(uint16 argc, uint16 *argv);
+ uint16 getComboDigit(uint32 correctCombo, uint32 digit);
private:
MohawkEngine_Riven *_vm;
diff --git a/engines/mohawk/riven_saveload.cpp b/engines/mohawk/riven_saveload.cpp
index 3c989838da..d73b4ec0dc 100644
--- a/engines/mohawk/riven_saveload.cpp
+++ b/engines/mohawk/riven_saveload.cpp
@@ -23,6 +23,7 @@
*
*/
+#include "mohawk/resource.h"
#include "mohawk/riven.h"
#include "mohawk/riven_saveload.h"
@@ -31,11 +32,9 @@
namespace Mohawk {
RivenSaveLoad::RivenSaveLoad(MohawkEngine_Riven *vm, Common::SaveFileManager *saveFileMan) : _vm(vm), _saveFileMan(saveFileMan) {
- _loadFile = new MohawkArchive();
}
RivenSaveLoad::~RivenSaveLoad() {
- delete _loadFile;
}
Common::StringArray RivenSaveLoad::generateSaveGameList() {
@@ -63,7 +62,8 @@ static uint16 mapOldStackIDToNew(uint16 oldID) {
case 8:
return aspit;
}
- error ("Unknown old stack ID %d", oldID);
+
+ error("Unknown old stack ID %d", oldID);
return 0;
}
@@ -86,7 +86,8 @@ static uint16 mapNewStackIDToOld(uint16 newID) {
case tspit:
return 4;
}
- error ("Unknown new stack ID %d", newID);
+
+ error("Unknown new stack ID %d", newID);
return 0;
}
@@ -94,26 +95,28 @@ bool RivenSaveLoad::loadGame(Common::String filename) {
if (_vm->getFeatures() & GF_DEMO) // Don't load games in the demo
return false;
- Common::InSaveFile *loadFile;
- if (!(loadFile = _saveFileMan->openForLoading(filename.c_str())))
+ Common::InSaveFile *loadFile = _saveFileMan->openForLoading(filename);
+ if (!loadFile)
return false;
- debug (0, "Loading game from \'%s\'", filename.c_str());
- _loadFile->open(loadFile);
+ debug(0, "Loading game from \'%s\'", filename.c_str());
+
+ MohawkArchive *mhk = new MohawkArchive();
+ mhk->open(loadFile);
// First, let's make sure we're using a saved game file from this version of Riven by checking the VERS resource
- Common::SeekableReadStream *vers = _loadFile->getRawData(ID_VERS, 1);
+ Common::SeekableReadStream *vers = mhk->getRawData(ID_VERS, 1);
uint32 saveGameVersion = vers->readUint32BE();
delete vers;
if ((saveGameVersion == kCDSaveGameVersion && (_vm->getFeatures() & GF_DVD))
|| (saveGameVersion == kDVDSaveGameVersion && !(_vm->getFeatures() & GF_DVD))) {
- warning ("Incompatible saved game versions. No support for this yet.");
- delete _loadFile;
+ warning("Incompatible saved game versions. No support for this yet.");
+ delete mhk;
return false;
}
// Now, we'll read in the variable values.
- Common::SeekableReadStream *vars = _loadFile->getRawData(ID_VARS, 1);
+ Common::SeekableReadStream *vars = mhk->getRawData(ID_VARS, 1);
Common::Array<uint32> rawVariables;
while (!vars->eos()) {
@@ -126,7 +129,7 @@ bool RivenSaveLoad::loadGame(Common::String filename) {
// Next, we set the variables based on the name found by the index in the VARS resource.
// TODO: Merge with code in mohawk.cpp for loading names?
- Common::SeekableReadStream *names = _loadFile->getRawData(ID_NAME, 1);
+ Common::SeekableReadStream *names = mhk->getRawData(ID_NAME, 1);
uint16 namesCount = names->readUint16BE();
uint16 *stringOffsets = new uint16[namesCount];
@@ -151,9 +154,10 @@ bool RivenSaveLoad::loadGame(Common::String filename) {
c = (char)names->readByte();
}
- // WORKAROUND: Some versions have two extra variables. However, the saves are
- // still compatible with other saves of the same version. Are these used in the
- // original interpreter anywhere? (They come from DVD v1.1)
+ // TODO: Some versions have two extra variables. However, the saves are
+ // still compatible with other saves of the same version (they come from DVD v1.1).
+ // There are used in the whark number puzzle. I thought jleftpos and jrightpos were
+ // for this purpose.
if (name == "dropLeftStart" || name == "dropRightStart")
continue;
@@ -161,11 +165,11 @@ bool RivenSaveLoad::loadGame(Common::String filename) {
*var = rawVariables[i];
- if (!scumm_stricmp(name.c_str(), "CurrentStackID"))
+ if (name.equalsIgnoreCase("CurrentStackID"))
stackID = mapOldStackIDToNew(rawVariables[i]);
- else if (!scumm_stricmp(name.c_str(), "CurrentCardID"))
+ else if (name.equalsIgnoreCase("CurrentCardID"))
cardID = rawVariables[i];
- else if (!scumm_stricmp(name.c_str(), "ReturnStackID"))
+ else if (name.equalsIgnoreCase("ReturnStackID"))
*var = mapOldStackIDToNew(rawVariables[i]);
}
@@ -179,7 +183,7 @@ bool RivenSaveLoad::loadGame(Common::String filename) {
_vm->_zipModeData.clear();
// Finally, we load in zip mode data.
- Common::SeekableReadStream *zips = _loadFile->getRawData(ID_ZIPS, 1);
+ Common::SeekableReadStream *zips = mhk->getRawData(ID_ZIPS, 1);
uint16 zipsRecordCount = zips->readUint16BE();
for (uint16 i = 0; i < zipsRecordCount; i++) {
ZipMode zip;
@@ -189,10 +193,10 @@ bool RivenSaveLoad::loadGame(Common::String filename) {
zip.id = zips->readUint16BE();
_vm->_zipModeData.push_back(zip);
}
+
delete zips;
+ delete mhk;
- delete _loadFile;
- _loadFile = NULL;
return true;
}
@@ -211,7 +215,14 @@ Common::MemoryWriteStreamDynamic *RivenSaveLoad::genVARSSection() {
for (uint32 i = 0; i < _vm->getVarCount(); i++) {
stream->writeUint32BE(0); // Unknown
stream->writeUint32BE(0); // Unknown
- stream->writeUint32BE(_vm->getGlobalVar(i));
+
+ // Remap returnstackid here because we don't actually want to change
+ // our internal returnstackid.
+ uint32 variable = _vm->getGlobalVar(i);
+ if (_vm->getGlobalVarName(i) == "returnstackid")
+ variable = mapNewStackIDToOld(variable);
+
+ stream->writeUint32BE(variable);
}
return stream;
@@ -257,17 +268,17 @@ bool RivenSaveLoad::saveGame(Common::String filename) {
// Note, this code is still WIP. It works quite well for now.
// Make sure we have the right extension
- if (!filename.hasSuffix(".rvn") && !filename.hasSuffix(".RVN"))
+ if (!filename.matchString("*.rvn", true))
filename += ".rvn";
// Convert class variables to variable numbers
*_vm->matchVarToString("currentstackid") = mapNewStackIDToOld(_vm->getCurStack());
*_vm->matchVarToString("currentcardid") = _vm->getCurCard();
- *_vm->matchVarToString("returnstackid") = mapNewStackIDToOld(*_vm->matchVarToString("returnstackid"));
- Common::OutSaveFile *saveFile;
- if (!(saveFile = _saveFileMan->openForSaving(filename.c_str())))
+ Common::OutSaveFile *saveFile = _saveFileMan->openForSaving(filename);
+ if (!saveFile)
return false;
+
debug (0, "Saving game to \'%s\'", filename.c_str());
Common::MemoryWriteStreamDynamic *versSection = genVERSSection();
@@ -392,7 +403,7 @@ bool RivenSaveLoad::saveGame(Common::String filename) {
void RivenSaveLoad::deleteSave(Common::String saveName) {
debug (0, "Deleting save file \'%s\'", saveName.c_str());
- _saveFileMan->removeSavefile(saveName.c_str());
+ _saveFileMan->removeSavefile(saveName);
}
} // End of namespace Mohawk
diff --git a/engines/mohawk/riven_saveload.h b/engines/mohawk/riven_saveload.h
index c708f9d35d..9cc9bc3737 100644
--- a/engines/mohawk/riven_saveload.h
+++ b/engines/mohawk/riven_saveload.h
@@ -29,8 +29,6 @@
#include "common/savefile.h"
#include "common/str.h"
-#include "mohawk/resource.h"
-
namespace Mohawk {
class MohawkEngine_Riven;
@@ -53,7 +51,6 @@ public:
private:
MohawkEngine_Riven *_vm;
Common::SaveFileManager *_saveFileMan;
- MohawkArchive *_loadFile;
Common::MemoryWriteStreamDynamic *genVERSSection();
Common::MemoryWriteStreamDynamic *genNAMESection();
diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp
index d574a455c6..1fcaba8ac0 100644
--- a/engines/mohawk/riven_scripts.cpp
+++ b/engines/mohawk/riven_scripts.cpp
@@ -35,33 +35,21 @@
namespace Mohawk {
-RivenScript::RivenScript(MohawkEngine_Riven *vm, Common::SeekableReadStream *stream, uint16 scriptType)
- : _vm(vm), _stream(stream), _scriptType(scriptType) {
+RivenScript::RivenScript(MohawkEngine_Riven *vm, Common::SeekableReadStream *stream, uint16 scriptType, uint16 parentStack, uint16 parentCard)
+ : _vm(vm), _stream(stream), _scriptType(scriptType), _parentStack(parentStack), _parentCard(parentCard) {
setupOpcodes();
+ _isRunning = false;
}
RivenScript::~RivenScript() {
delete _stream;
}
-RivenScriptList RivenScript::readScripts(MohawkEngine_Riven *vm, Common::SeekableReadStream *stream) {
- RivenScriptList scriptList;
-
- uint16 scriptCount = stream->readUint16BE();
- for (uint16 i = 0; i < scriptCount; i++) {
- uint16 scriptType = stream->readUint16BE();
- uint32 scriptSize = calculateScriptSize(stream);
- scriptList.push_back(Common::SharedPtr<RivenScript>(new RivenScript(vm, stream->readStream(scriptSize), scriptType)));
- }
-
- return scriptList;
-}
-
uint32 RivenScript::calculateCommandSize(Common::SeekableReadStream* script) {
uint16 command = script->readUint16BE();
uint32 commandSize = 2;
if (command == 8) {
- if (script->readUint16BE() != 2)
+ if (script->readUint16BE() != 2) // Arg count?
warning ("if-then-else unknown value is not 2");
script->readUint16BE(); // variable to check against
uint16 logicBlockCount = script->readUint16BE(); // number of logic blocks
@@ -161,7 +149,7 @@ void RivenScript::setupOpcodes() {
OPCODE(activateFLST),
OPCODE(zipMode),
OPCODE(activateMLST),
- OPCODE(activateSLSTWithVolume)
+ OPCODE(empty) // Activate an SLST with a volume parameter (not used)
};
_opcodes = riven_opcodes;
@@ -239,10 +227,13 @@ void RivenScript::dumpCommands(Common::StringArray varNames, Common::StringArray
}
void RivenScript::runScript() {
+ _isRunning = true;
+
if (_stream->pos() != 0)
_stream->seek(0);
processCommands(true);
+ _isRunning = false;
}
void RivenScript::processCommands(bool runCommands) {
@@ -610,9 +601,46 @@ void RivenScript::activateMLST(uint16 op, uint16 argc, uint16 *argv) {
_vm->_video->activateMLST(argv[0], _vm->getCurCard());
}
-// Command 47: activate SLST record with a volume argument
-void RivenScript::activateSLSTWithVolume(uint16 op, uint16 argc, uint16 *argv) {
- warning("STUB: activateSLSTWithVolume()");
+RivenScriptManager::RivenScriptManager(MohawkEngine_Riven *vm) {
+ _vm = vm;
+}
+
+RivenScriptManager::~RivenScriptManager() {
+ for (uint32 i = 0; i < _currentScripts.size(); i++)
+ delete _currentScripts[i];
+}
+
+RivenScriptList RivenScriptManager::readScripts(Common::SeekableReadStream *stream, bool garbageCollect) {
+ if (garbageCollect)
+ unloadUnusedScripts(); // Garbage collect!
+
+ RivenScriptList scriptList;
+
+ uint16 scriptCount = stream->readUint16BE();
+ for (uint16 i = 0; i < scriptCount; i++) {
+ uint16 scriptType = stream->readUint16BE();
+ uint32 scriptSize = RivenScript::calculateScriptSize(stream);
+ RivenScript *script = new RivenScript(_vm, stream->readStream(scriptSize), scriptType, _vm->getCurStack(), _vm->getCurCard());
+ scriptList.push_back(script);
+
+ // Only add it to the scripts that we will free later if it is requested.
+ // (ie. we don't want to store scripts from the dumpScript console command)
+ if (garbageCollect)
+ _currentScripts.push_back(script);
+ }
+
+ return scriptList;
+}
+
+void RivenScriptManager::unloadUnusedScripts() {
+ // Free any scripts that aren't part of the current card and aren't running
+ for (uint32 i = 0; i < _currentScripts.size(); i++) {
+ if ((_vm->getCurStack() != _currentScripts[i]->getParentStack() || _vm->getCurCard() != _currentScripts[i]->getParentCard()) && !_currentScripts[i]->isRunning()) {
+ delete _currentScripts[i];
+ _currentScripts.remove_at(i);
+ i--;
+ }
+ }
}
} // End of namespace Mohawk
diff --git a/engines/mohawk/riven_scripts.h b/engines/mohawk/riven_scripts.h
index a1512af697..5187bbde08 100644
--- a/engines/mohawk/riven_scripts.h
+++ b/engines/mohawk/riven_scripts.h
@@ -50,19 +50,20 @@ enum {
};
class RivenScript;
-typedef Common::Array<Common::SharedPtr<RivenScript> > RivenScriptList;
class RivenScript {
public:
- RivenScript(MohawkEngine_Riven *vm, Common::SeekableReadStream *stream, uint16 scriptType);
+ RivenScript(MohawkEngine_Riven *vm, Common::SeekableReadStream *stream, uint16 scriptType, uint16 parentStack, uint16 parentCard);
~RivenScript();
void runScript();
void dumpScript(Common::StringArray varNames, Common::StringArray xNames, byte tabs);
uint16 getScriptType() { return _scriptType; }
+ uint16 getParentStack() { return _parentStack; }
+ uint16 getParentCard() { return _parentCard; }
+ bool isRunning() { return _isRunning; }
- // Read in an array of script objects from a stream
- static RivenScriptList readScripts(MohawkEngine_Riven *vm, Common::SeekableReadStream *stream);
+ static uint32 calculateScriptSize(Common::SeekableReadStream *script);
private:
typedef void (RivenScript::*OpcodeProcRiven)(uint16 op, uint16 argc, uint16 *argv);
@@ -70,18 +71,18 @@ private:
OpcodeProcRiven proc;
const char *desc;
};
- const RivenOpcode* _opcodes;
+ const RivenOpcode *_opcodes;
void setupOpcodes();
MohawkEngine_Riven *_vm;
Common::SeekableReadStream *_stream;
- uint16 _scriptType;
+ uint16 _scriptType, _parentStack, _parentCard, _parentHotspot;
+ bool _isRunning;
void dumpCommands(Common::StringArray varNames, Common::StringArray xNames, byte tabs);
void processCommands(bool runCommands);
- static uint32 calculateCommandSize(Common::SeekableReadStream* script);
- static uint32 calculateScriptSize(Common::SeekableReadStream* script);
+ static uint32 calculateCommandSize(Common::SeekableReadStream *script);
DECLARE_OPCODE(empty) { warning ("Unknown Opcode %04x", op); }
@@ -120,7 +121,21 @@ private:
DECLARE_OPCODE(activateFLST);
DECLARE_OPCODE(zipMode);
DECLARE_OPCODE(activateMLST);
- DECLARE_OPCODE(activateSLSTWithVolume);
+};
+
+typedef Common::Array<RivenScript*> RivenScriptList;
+
+class RivenScriptManager {
+public:
+ RivenScriptManager(MohawkEngine_Riven *vm);
+ ~RivenScriptManager();
+
+ RivenScriptList readScripts(Common::SeekableReadStream *stream, bool garbageCollect = true);
+
+private:
+ void unloadUnusedScripts();
+ RivenScriptList _currentScripts;
+ MohawkEngine_Riven *_vm;
};
} // End of namespace Mohawk
diff --git a/engines/mohawk/riven_vars.cpp b/engines/mohawk/riven_vars.cpp
index 3f7cd5a8b6..b6d2dff315 100644
--- a/engines/mohawk/riven_vars.cpp
+++ b/engines/mohawk/riven_vars.cpp
@@ -319,6 +319,8 @@ void MohawkEngine_Riven::initVars() {
*matchVarToString("bheat") = 1;
*matchVarToString("waterenabled") = 1;
*matchVarToString("ogehnpage") = 1;
+ *matchVarToString("bblrsw") = 1;
+ *matchVarToString("ocage") = 1;
// Randomize the telescope combination
uint32 *teleCombo = matchVarToString("tcorrectorder");
diff --git a/engines/mohawk/sound.cpp b/engines/mohawk/sound.cpp
index b84573f011..091bd68021 100644
--- a/engines/mohawk/sound.cpp
+++ b/engines/mohawk/sound.cpp
@@ -27,12 +27,12 @@
#include "common/util.h"
+#include "sound/musicplugin.h"
#include "sound/audiostream.h"
#include "sound/decoders/mp3.h"
#include "sound/decoders/raw.h"
#include "sound/decoders/wave.h"
-
namespace Mohawk {
Sound::Sound(MohawkEngine* vm) : _vm(vm) {
@@ -79,7 +79,7 @@ void Sound::initMidi() {
// Let's get our MIDI parser/driver
_midiParser = MidiParser::createParser_SMF();
- _midiDriver = MidiDriver::createMidi(MidiDriver::detectMusicDriver(MDT_ADLIB|MDT_MIDI));
+ _midiDriver = MidiDriver::createMidi(MidiDriver::detectDevice(MDT_ADLIB|MDT_MIDI));
// Set up everything!
_midiDriver->open();
@@ -233,6 +233,10 @@ void Sound::playSLST(uint16 index, uint16 card) {
if (slstRecord.index == index) {
playSLST(slstRecord);
+ delete[] slstRecord.sound_ids;
+ delete[] slstRecord.volumes;
+ delete[] slstRecord.balances;
+ delete[] slstRecord.u2;
delete slstStream;
return;
}
@@ -244,6 +248,7 @@ void Sound::playSLST(uint16 index, uint16 card) {
}
delete slstStream;
+
// No matching records, assume we need to stop all SLST's
stopAllSLST();
}
@@ -277,8 +282,11 @@ void Sound::playSLST(SLSTRecord slstRecord) {
}
void Sound::stopAllSLST() {
- for (uint16 i = 0; i < _currentSLSTSounds.size(); i++)
+ for (uint16 i = 0; i < _currentSLSTSounds.size(); i++) {
_vm->_mixer->stopHandle(*_currentSLSTSounds[i].handle);
+ delete _currentSLSTSounds[i].handle;
+ }
+
_currentSLSTSounds.clear();
}
@@ -314,6 +322,7 @@ void Sound::playSLSTSound(uint16 id, bool fade, bool loop, uint16 volume, int16
void Sound::stopSLSTSound(uint16 index, bool fade) {
// TODO: Fade out, mixer needs to be extended to get volume on a handle
_vm->_mixer->stopHandle(*_currentSLSTSounds[index].handle);
+ delete _currentSLSTSounds[index].handle;
_currentSLSTSounds.remove_at(index);
}
diff --git a/engines/parallaction/callables_ns.cpp b/engines/parallaction/callables_ns.cpp
index a493523301..cfe6ad8954 100644
--- a/engines/parallaction/callables_ns.cpp
+++ b/engines/parallaction/callables_ns.cpp
@@ -453,14 +453,15 @@ void Parallaction_ns::_c_startIntro(void *parm) {
}
void Parallaction_ns::_c_endIntro(void *parm) {
- // NOTE: suspend command execution queue, to
- // avoid running the QUIT command before
- // credits are displayed. This solves bug
- // #2619824.
- // Execution of the command list will resume
- // as soon as runGameFrame is run.
- _cmdExec->suspend();
-
+ if (getFeatures() & GF_DEMO) {
+ // NOTE: suspend command execution queue, to
+ // avoid running the QUIT command before
+ // credits are displayed. This solves bug
+ // #2619824.
+ // Execution of the command list will resume
+ // as soon as runGameFrame is run.
+ _cmdExec->suspend();
+ }
startCreditSequence();
_intro = false;
}
diff --git a/engines/parallaction/detection.cpp b/engines/parallaction/detection.cpp
index e5e2b22644..e00a087923 100644
--- a/engines/parallaction/detection.cpp
+++ b/engines/parallaction/detection.cpp
@@ -240,7 +240,11 @@ static const ADParams detectionParams = {
// Flags
0,
// Additional GUI options (for every game}
- Common::GUIO_NOLAUNCHLOAD
+ Common::GUIO_NOLAUNCHLOAD,
+ // Maximum directory depth
+ 1,
+ // List of directory globs
+ 0
};
class ParallactionMetaEngine : public AdvancedMetaEngine {
diff --git a/engines/parallaction/exec.cpp b/engines/parallaction/exec.cpp
index 24579286e6..434c4f6ae6 100644
--- a/engines/parallaction/exec.cpp
+++ b/engines/parallaction/exec.cpp
@@ -193,8 +193,10 @@ void CommandExec::runSuspended() {
debugC(3, kDebugExec, "CommandExec::runSuspended()");
_execZone = _suspendedCtxt._zone;
- runList(_suspendedCtxt._first, _suspendedCtxt._last);
+ CommandList::iterator first = _suspendedCtxt._first;
+ CommandList::iterator last = _suspendedCtxt._last;
cleanSuspendedList();
+ runList(first, last);
}
}
diff --git a/engines/parallaction/exec_br.cpp b/engines/parallaction/exec_br.cpp
index e145c0da94..1d8724e2d8 100644
--- a/engines/parallaction/exec_br.cpp
+++ b/engines/parallaction/exec_br.cpp
@@ -293,7 +293,7 @@ DECLARE_COMMAND_OPCODE(text) {
DECLARE_COMMAND_OPCODE(part) {
- warning("Parallaction_br::cmdOp_part not yet implemented");
+ _vm->_nextPart = ctxt._cmd->_counterValue;
}
@@ -527,6 +527,11 @@ DECLARE_INSTRUCTION_OPCODE(endif) {
DECLARE_INSTRUCTION_OPCODE(stop) {
ZonePtr z = ctxt._inst->_z;
+
+ // Prevent execution if zone is missing. The known case is "PART2/insegui.scr", which has
+ // "STOP insegui", which doesn't exist (see ticket #3021744 for the gory details)
+ if (!z) return;
+
if (ACTIONTYPE(z) == kZoneHear) {
warning("Parallaction_br::instOp_stop not yet implemented for HEAR zones");
// TODO: stop music or sound effects generated by a zone.
diff --git a/engines/parallaction/gfxbase.cpp b/engines/parallaction/gfxbase.cpp
index fc6cb28d9e..a1926fc197 100644
--- a/engines/parallaction/gfxbase.cpp
+++ b/engines/parallaction/gfxbase.cpp
@@ -222,10 +222,12 @@ void Gfx::drawGfxObject(GfxObj *obj, Graphics::Surface &surf) {
obj->getRect(obj->frame, rect);
int x = obj->x;
+ int y = obj->y;
if (_overlayMode) {
x += _scrollPosX;
+ y += _scrollPosY;
}
- rect.translate(x, obj->y);
+ rect.translate(x, y);
data = obj->getData(obj->frame);
if (obj->getSize(obj->frame) == obj->getRawSize(obj->frame)) {
@@ -281,30 +283,54 @@ void Gfx::bltMaskScale(const Common::Rect& r, byte *data, Graphics::Surface *sur
return;
}
- Common::Rect q(r);
- Common::Rect clipper(surf->w, surf->h);
- q.clip(clipper);
- if (!q.isValidRect()) return;
-
- uint inc = r.width() * (100 - scale);
- uint thr = r.width() * 100;
- uint xAccum = 0, yAccum = 0;
+ // unscaled rectangle size
+ uint width = r.width();
+ uint height = r.height();
+
+ // scaled rectangle size
+ uint scaledWidth = r.width() * scale / 100;
+ uint scaledHeight = r.height() * scale / 100;
+
+ // scaled rectangle origin
+ uint scaledLeft = r.left + (width - scaledWidth) / 2;
+ uint scaledTop = r.top + (height - scaledHeight);
+
+ // clipped scaled destination rectangle
+ Common::Rect dstRect(scaledWidth, scaledHeight);
+ dstRect.moveTo(scaledLeft, scaledTop);
+
+ Common::Rect clipper(surf->w, surf->h);
+ dstRect.clip(clipper);
+ if (!dstRect.isValidRect()) return;
+
+
+ // clipped source rectangle
+ Common::Rect srcRect;
+ srcRect.left = (dstRect.left - scaledLeft) * 100 / scale;
+ srcRect.top = (dstRect.top - scaledTop) * 100 / scale;
+ srcRect.setWidth(dstRect.width() * 100 / scale);
+ srcRect.setHeight(dstRect.height() * 100 / scale);
+ if (!srcRect.isValidRect()) return;
Common::Point dp;
- dp.x = q.left + (r.width() * (100 - scale)) / 200;
- dp.y = q.top + (r.height() * (100 - scale)) / 100;
- q.translate(-r.left, -r.top);
- byte *s = data + q.left + q.top * r.width();
+ dp.x = dstRect.left;
+ dp.y = dstRect.top;
+
+ byte *s = data + srcRect.left + srcRect.top * width;
byte *d = (byte*)surf->getBasePtr(dp.x, dp.y);
uint line = 0, col = 0;
- for (uint16 i = 0; i < q.height(); i++) {
+ uint xAccum = 0, yAccum = 0;
+ uint inc = width * (100 - scale);
+ uint thr = width * 100;
+
+ for (uint16 i = 0; i < srcRect.height(); i++) {
yAccum += inc;
if (yAccum >= thr) {
yAccum -= thr;
- s += r.width();
+ s += width;
continue;
}
@@ -312,7 +338,7 @@ void Gfx::bltMaskScale(const Common::Rect& r, byte *data, Graphics::Surface *sur
byte *d2 = d;
col = 0;
- for (uint16 j = 0; j < q.width(); j++) {
+ for (uint16 j = 0; j < srcRect.width(); j++) {
xAccum += inc;
if (xAccum >= thr) {
@@ -335,7 +361,7 @@ void Gfx::bltMaskScale(const Common::Rect& r, byte *data, Graphics::Surface *sur
col++;
}
- s += r.width() - q.width();
+ s += width - srcRect.width();
d += surf->w;
line++;
}
diff --git a/engines/parallaction/graphics.cpp b/engines/parallaction/graphics.cpp
index bc1759ecd7..2990d024d2 100644
--- a/engines/parallaction/graphics.cpp
+++ b/engines/parallaction/graphics.cpp
@@ -317,8 +317,10 @@ void Gfx::drawList(Graphics::Surface &surface, GfxObjArray &list) {
void Gfx::copyRectToScreen(const byte *buf, int pitch, int x, int y, int w, int h) {
if (_doubleBuffering) {
- if (_overlayMode)
+ if (_overlayMode) {
x += _scrollPosX;
+ y += _scrollPosY;
+ }
byte *dst = (byte*)_backBuffer.getBasePtr(x, y);
for (int i = 0; i < h; i++) {
@@ -358,7 +360,7 @@ void Gfx::unlockScreen() {
void Gfx::updateScreenIntern() {
if (_doubleBuffering) {
- byte *data = (byte*)_backBuffer.getBasePtr(_scrollPosX, 0);
+ byte *data = (byte*)_backBuffer.getBasePtr(_scrollPosX, _scrollPosY);
_vm->_system->copyRectToScreen(data, _backBuffer.pitch, 0, 0, _vm->_screenWidth, _vm->_screenHeight);
}
@@ -863,6 +865,8 @@ void Gfx::setBackground(uint type, BackgroundInfo *info) {
_minScrollX = 0;
_maxScrollX = MAX<int>(0, _backgroundInfo->width - _vm->_screenWidth);
+ _minScrollY = 0;
+ _maxScrollY = MAX<int>(0, _backgroundInfo->height - _vm->_screenHeight);
}
diff --git a/engines/parallaction/gui_br.cpp b/engines/parallaction/gui_br.cpp
index 7096bbe569..21cb2d8b00 100644
--- a/engines/parallaction/gui_br.cpp
+++ b/engines/parallaction/gui_br.cpp
@@ -98,6 +98,12 @@ public:
}
};
+
+struct LocationPart {
+ int part;
+ const char *location;
+};
+
class MainMenuInputState_BR : public MenuInputState {
Parallaction_br *_vm;
@@ -158,7 +164,7 @@ class MainMenuInputState_BR : public MenuInputState {
const char **_menuStrings;
const MenuOptions *_options;
- static const char *_firstLocation[];
+ static LocationPart _firstLocation[];
int _availItems;
int _selection;
@@ -205,7 +211,8 @@ public:
return this;
}
- switch (_options[_selection]) {
+ int selection = _options[_selection];
+ switch (selection) {
case kMenuQuit: {
_vm->quitGame();
break;
@@ -218,8 +225,10 @@ public:
}
break;
- default:
- _vm->scheduleLocationSwitch(_firstLocation[_options[_selection]]);
+ default:
+ _vm->_nextPart = _firstLocation[selection].part;
+ _vm->scheduleLocationSwitch(_firstLocation[selection].location);
+
}
_vm->_system->showMouse(false);
@@ -262,14 +271,15 @@ public:
};
-const char *MainMenuInputState_BR::_firstLocation[] = {
- "intro.0",
- "museo.1",
- "start.2",
- "bolscoi.3",
- "treno.4"
+LocationPart MainMenuInputState_BR::_firstLocation[] = {
+ { 0, "intro" },
+ { 1, "museo" },
+ { 2, "start" },
+ { 3, "bolscoi" },
+ { 4, "treno" }
};
+
const char *MainMenuInputState_BR::_menuStringsAmiga[NUM_MENULINES] = {
"See the introduction",
"Load a Saved Game",
diff --git a/engines/parallaction/input.cpp b/engines/parallaction/input.cpp
index d6dd9feb19..6d3f6f0b04 100644
--- a/engines/parallaction/input.cpp
+++ b/engines/parallaction/input.cpp
@@ -395,7 +395,7 @@ void Input::exitInventoryMode() {
_vm->dropItem(z->u._mergeObj1);
_vm->dropItem(z->u._mergeObj2);
_vm->addInventoryItem(z->u._mergeObj3);
- _vm->_cmdExec->run(z->_commands);
+ _vm->_cmdExec->run(z->_commands); // commands might set a new _inputMode
}
}
@@ -412,7 +412,11 @@ void Input::exitInventoryMode() {
}
_vm->resumeJobs();
- _inputMode = kInputModeGame;
+ // in case the input mode was not changed by the code above (especially by the commands
+ // executed in case of a merge), then assume we are going back to game mode
+ if (_inputMode == kInputModeInventory) {
+ _inputMode = kInputModeGame;
+ }
}
bool Input::updateInventoryInput() {
diff --git a/engines/parallaction/parallaction.h b/engines/parallaction/parallaction.h
index 151bfd958d..7bbdf79f1c 100644
--- a/engines/parallaction/parallaction.h
+++ b/engines/parallaction/parallaction.h
@@ -548,6 +548,9 @@ public:
const char **_audioCommandsNamesRes;
static const char *_partNames[];
int _part;
+ int _nextPart;
+
+
#if 0 // disabled since I couldn't find any references to lip sync in the scripts
int16 _lipSyncVal;
uint _subtitleLipSync;
diff --git a/engines/parallaction/parallaction_br.cpp b/engines/parallaction/parallaction_br.cpp
index c752c85d4f..470c698a21 100644
--- a/engines/parallaction/parallaction_br.cpp
+++ b/engines/parallaction/parallaction_br.cpp
@@ -61,8 +61,8 @@ Common::Error Parallaction_br::init() {
_disk = new DosDisk_br(this);
}
_disk->setLanguage(2); // NOTE: language is now hardcoded to English. Original used command-line parameters.
- MidiDriverType midiDriver = MidiDriver::detectMusicDriver(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MIDI);
- MidiDriver *driver = MidiDriver::createMidi(midiDriver);
+ MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
+ MidiDriver *driver = MidiDriver::createMidi(dev);
_soundManI = new DosSoundMan_br(this, driver);
} else {
_disk = new AmigaDisk_br(this);
@@ -86,6 +86,7 @@ Common::Error Parallaction_br::init() {
_walker = new PathWalker_BR;
_part = -1;
+ _nextPart = -1;
_subtitle[0] = 0;
_subtitle[1] = 0;
@@ -260,6 +261,12 @@ void Parallaction_br::cleanupGame() {
_globalFlagsNames = 0;
_objectsNames = 0;
_countersNames = 0;
+
+ _numLocations = 0;
+ _globalFlags = 0;
+ memset(_localFlags, 0, sizeof(_localFlags));
+ memset(_locationNames, 0, sizeof(_locationNames));
+ memset(_zoneFlags, 0, sizeof(_zoneFlags));
}
@@ -268,17 +275,16 @@ void Parallaction_br::changeLocation() {
return;
}
- char location[200];
- strcpy(location, _newLocationName.c_str());
-
- char *partStr = strrchr(location, '.');
- if (partStr) {
+ if (_nextPart != -1) {
cleanupGame();
- int n = partStr - location;
- location[n] = '\0';
+ // more cleanup needed for part changes (see also saveload)
+ _globalFlags = 0;
+ cleanInventory(true);
+ strcpy(_characterName1, "null");
+
+ _part = _nextPart;
- _part = atoi(++partStr);
if (getFeatures() & GF_DEMO) {
assert(_part == 1);
} else {
@@ -305,8 +311,8 @@ void Parallaction_br::changeLocation() {
freeLocation(false);
// load new location
- strcpy(_location._name, location);
- parseLocation(location);
+ strcpy(_location._name, _newLocationName.c_str());
+ parseLocation(_location._name);
if (_location._startPosition.x != -1000) {
_char._ani->setFoot(_location._startPosition);
@@ -357,6 +363,7 @@ void Parallaction_br::changeLocation() {
_engineFlags &= ~kEngineChangeLocation;
_newLocationName.clear();
+ _nextPart = -1;
}
// FIXME: Parallaction_br::parseLocation() is now a verbatim copy of the same routine from Parallaction_ns.
diff --git a/engines/parallaction/parallaction_ns.cpp b/engines/parallaction/parallaction_ns.cpp
index c1d6c9367a..f1e7b14583 100644
--- a/engines/parallaction/parallaction_ns.cpp
+++ b/engines/parallaction/parallaction_ns.cpp
@@ -24,7 +24,6 @@
*/
#include "common/system.h"
-
#include "common/config-manager.h"
#include "parallaction/parallaction.h"
@@ -167,8 +166,8 @@ Common::Error Parallaction_ns::init() {
_disk->init();
if (getPlatform() == Common::kPlatformPC) {
- MidiDriverType midiDriver = MidiDriver::detectMusicDriver(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MIDI);
- MidiDriver *driver = MidiDriver::createMidi(midiDriver);
+ MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
+ MidiDriver *driver = MidiDriver::createMidi(dev);
_soundManI = new DosSoundMan_ns(this, driver);
_soundManI->setMusicVolume(ConfMan.getInt("music_volume"));
} else {
diff --git a/engines/parallaction/parser.h b/engines/parallaction/parser.h
index 3e46e99180..5eb26e9fa1 100644
--- a/engines/parallaction/parser.h
+++ b/engines/parallaction/parser.h
@@ -294,6 +294,7 @@ public:
virtual void parseGetData(ZonePtr z);
virtual void parseDoorData(ZonePtr z);
virtual void parseHearData(ZonePtr z);
+ virtual void parseNoneData(ZonePtr z);
protected:
void parseAnswerCounter(Answer *answer);
virtual Answer *parseAnswer();
diff --git a/engines/parallaction/parser_br.cpp b/engines/parallaction/parser_br.cpp
index d3ce1235c1..57259fd637 100644
--- a/engines/parallaction/parser_br.cpp
+++ b/engines/parallaction/parser_br.cpp
@@ -825,6 +825,16 @@ void LocationParser_br::parseHearData(ZonePtr z) {
}
}
+void LocationParser_br::parseNoneData(ZonePtr z) {
+ /* the only case we have to handle here is that of "scende2", which is the only Animation with
+ a command list following the type marker.
+ */
+ if (!scumm_stricmp(_tokens[0], "commands")) {
+ parseCommands(z->_commands);
+ }
+}
+
+
typedef void (LocationParser_br::*ZoneTypeParser)(ZonePtr);
static ZoneTypeParser parsers[] = {
0, // no type
@@ -836,7 +846,7 @@ static ZoneTypeParser parsers[] = {
&LocationParser_br::parseHearData,
0, // feel
&LocationParser_br::parseSpeakData,
- 0, // none
+ &LocationParser_br::parseNoneData,
0, // trap
0, // you
0, // command
@@ -882,7 +892,6 @@ DECLARE_ANIM_PARSER(moveto) {
// ctxt.a->_moveTo.z = atoi(_tokens[3]);
}
-
DECLARE_ANIM_PARSER(endanimation) {
debugC(7, kDebugParser, "ANIM_PARSER(endanimation) ");
diff --git a/engines/parallaction/walk.cpp b/engines/parallaction/walk.cpp
index 8fc916e490..d6df23d415 100644
--- a/engines/parallaction/walk.cpp
+++ b/engines/parallaction/walk.cpp
@@ -450,7 +450,7 @@ void PathWalker_BR::buildPath(State &s, uint16 x, uint16 y) {
Common::Point foot;
s._a->getFoot(foot);
- debugC(1, kDebugWalk, "buildPath: from (%i, %i) to (%i, %i)", foot.x, foot.y, x, y);
+ debugC(1, kDebugWalk, "buildPath: try to build path from (%i, %i) to (%i, %i)", foot.x, foot.y, x, y);
s._walkPath.clear();
// look for easy path first
@@ -465,13 +465,13 @@ void PathWalker_BR::buildPath(State &s, uint16 x, uint16 y) {
ZonePtr z0 = _vm->hitZone(kZonePath, x, y);
if (!z0) {
s._walkPath.push_back(dest);
- debugC(3, kDebugWalk, "buildPath: corner case 0");
+ debugC(3, kDebugWalk, "buildPath: corner case 0 (%i nodes)", s._walkPath.size());
return;
}
ZonePtr z1 = _vm->hitZone(kZonePath, foot.x, foot.y);
if (!z1 || z1 == z0) {
s._walkPath.push_back(dest);
- debugC(3, kDebugWalk, "buildPath: corner case 1");
+ debugC(3, kDebugWalk, "buildPath: corner case 1 (%i nodes)", s._walkPath.size());
return;
}
@@ -480,7 +480,7 @@ void PathWalker_BR::buildPath(State &s, uint16 x, uint16 y) {
if (z1->u._pathLists[id].empty()) {
s._walkPath.clear();
- debugC(3, kDebugWalk, "buildPath: no path");
+ debugC(3, kDebugWalk, "buildPath: no path found");
return;
}
@@ -490,7 +490,7 @@ void PathWalker_BR::buildPath(State &s, uint16 x, uint16 y) {
s._walkPath.push_front(*b);
}
s._walkPath.push_back(dest);
- debugC(3, kDebugWalk, "buildPath: complex path");
+ debugC(3, kDebugWalk, "buildPath: complex path (%i nodes)", s._walkPath.size());
}
@@ -541,8 +541,6 @@ void PathWalker_BR::walk() {
return;
}
- debugC(3, kDebugWalk, "PathWalker_BR::walk()");
-
doWalk(_character);
doWalk(_follower);
@@ -566,8 +564,6 @@ void PathWalker_BR::walk() {
}
_vm->_gfx->initiateScroll(dx, dy);
-
- debugC(3, kDebugWalk, "PathWalker_BR::walk() -> done");
}
void PathWalker_BR::checkTrap(const Common::Point &p) {
@@ -601,8 +597,6 @@ void PathWalker_BR::doWalk(State &s) {
return;
}
- debugC(3, kDebugWalk, "PathWalker_BR::doWalk(%s)", s._a->_name);
-
if (s._walkDelay > 0) {
s._walkDelay--;
if (s._walkDelay == 0 && s._a->_scriptName) {
@@ -619,10 +613,10 @@ void PathWalker_BR::doWalk(State &s) {
if (s._walkPath.empty()) {
finalizeWalk(s);
- debugC(3, kDebugWalk, "PathWalker_BR::doWalk, case 0");
+ debugC(3, kDebugWalk, "PathWalker_BR::doWalk, walk completed (no more nodes)");
return;
} else {
- debugC(3, kDebugWalk, "PathWalker_BR::doWalk, moving to next node");
+ debugC(3, kDebugWalk, "PathWalker_BR::doWalk, reached a walkpath node, %i left", s._walkPath.size());
}
}
@@ -632,6 +626,19 @@ void PathWalker_BR::doWalk(State &s) {
int xStep = (scale * 16) / 100 + 1;
int yStep = (scale * 10) / 100 + 1;
+
+ /* WORKAROUND: in the balloon scene, the position of the balloon (which is implemented as a
+ Character) is controlled by the user (for movement, via this walking code) and by the scripts
+ (to simulate the balloon floating in the air, in a neverending loop that alters the position
+ coordinates).
+ When the two step sizes are equal in magnitude and opposite in direction, then the walk code
+ enters an infinite loop without giving control back to the user (this happens quite frequently
+ when navigating the balloon near the borders of the screen, where the calculated step is
+ forcibly small because of clipping). Since the "floating" script (part1/scripts/mongolo.scr)
+ uses increments of 3 for both x and y, we tweak the calculated steps accordingly here. */
+ if (xStep == 3) xStep--;
+ if (yStep == 3) yStep--;
+
debugC(9, kDebugWalk, "calculated step: (%i, %i)", xStep, yStep);
s._fieldC = 0;
@@ -714,7 +721,7 @@ void PathWalker_BR::doWalk(State &s) {
Common::Point p2;
s._a->getFoot(p2);
checkTrap(p2);
- debugC(3, kDebugWalk, "PathWalker_BR::doWalk, case 1");
+ debugC(3, kDebugWalk, "PathWalker_BR::doWalk, stepped to (%i, %i)", p2.x, p2.y);
return;
}
diff --git a/engines/queen/music.cpp b/engines/queen/music.cpp
index b4b9210616..3d859c8335 100644
--- a/engines/queen/music.cpp
+++ b/engines/queen/music.cpp
@@ -34,6 +34,7 @@
#include "sound/midiparser.h"
+
namespace Queen {
extern MidiDriver *C_Player_CreateAdLibMidiDriver(Audio::Mixer *);
@@ -45,9 +46,9 @@ MidiMusic::MidiMusic(QueenEngine *vm)
_queuePos = _lastSong = _currentSong = 0;
queueClear();
- MidiDriverType midiDriver = MidiDriver::detectMusicDriver(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MIDI);
- _adlib = (midiDriver == MD_ADLIB);
- _nativeMT32 = ((midiDriver == MD_MT32) || ConfMan.getBool("native_mt32"));
+ MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MT32);
+ _adlib = (MidiDriver::getMusicType(dev) == MT_ADLIB);
+ _nativeMT32 = ((MidiDriver::getMusicType(dev) == MT_MT32) || ConfMan.getBool("native_mt32"));
const char *musicDataFile;
if (vm->resource()->isDemo()) {
@@ -72,7 +73,7 @@ MidiMusic::MidiMusic(QueenEngine *vm)
// }
_driver = C_Player_CreateAdLibMidiDriver(vm->_mixer);
} else {
- _driver = MidiDriver::createMidi(midiDriver);
+ _driver = MidiDriver::createMidi(dev);
if (_nativeMT32) {
_driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
}
diff --git a/engines/saga/actor.cpp b/engines/saga/actor.cpp
index c3f5fec83a..8bc8025032 100644
--- a/engines/saga/actor.cpp
+++ b/engines/saga/actor.cpp
@@ -1173,7 +1173,9 @@ void Actor::drawActors() {
return;
}
- if (_vm->_anim->hasCutaway()) {
+ // WORKAROUND
+ // Bug #2928923: 'ITE: Graphic Glitches during racoon death "Cut Scene"'
+ if (_vm->_anim->hasCutaway() || _vm->_scene->currentSceneNumber() == 287 || _vm->_scene->currentSceneNumber() == 286) {
drawSpeech();
return;
}
diff --git a/engines/saga/console.cpp b/engines/saga/console.cpp
index 2c201ac57d..228febfe9c 100644
--- a/engines/saga/console.cpp
+++ b/engines/saga/console.cpp
@@ -40,9 +40,6 @@ Console::Console(SagaEngine *vm) : GUI::Debugger() {
DCmd_Register("continue", WRAP_METHOD(Console, Cmd_Exit));
- // CVAR_Register_I(&_soundEnabled, "sound", NULL, CVAR_CFG, 0, 1);
- // CVAR_Register_I(&_musicEnabled, "music", NULL, CVAR_CFG, 0, 1);
-
// Actor commands
DCmd_Register("actor_walk_to", WRAP_METHOD(Console, cmdActorWalkTo));
diff --git a/engines/saga/detection.cpp b/engines/saga/detection.cpp
index 1c2c6bacff..7913291527 100644
--- a/engines/saga/detection.cpp
+++ b/engines/saga/detection.cpp
@@ -122,7 +122,11 @@ static const ADParams detectionParams = {
// Flags
0,
// Additional GUI options (for every game}
- Common::GUIO_NONE
+ Common::GUIO_NONE,
+ // Maximum directory depth
+ 1,
+ // List of directory globs
+ 0
};
class SagaMetaEngine : public AdvancedMetaEngine {
diff --git a/engines/saga/events.cpp b/engines/saga/events.cpp
index d15f0b2af3..1f4091d07c 100644
--- a/engines/saga/events.cpp
+++ b/engines/saga/events.cpp
@@ -306,7 +306,6 @@ int Events::handleOneShot(Event *event) {
_vm->_sndRes->playVoice(event->param);
break;
case kMusicEvent:
- _vm->_music->stop();
if (event->op == kEventPlay)
_vm->_music->play(event->param, (MusicFlags)event->param2);
break;
diff --git a/engines/saga/font.h b/engines/saga/font.h
index f290384e87..d8b1da30b9 100644
--- a/engines/saga/font.h
+++ b/engines/saga/font.h
@@ -190,7 +190,7 @@ class Font {
}
}
bool valid(FontId fontId) {
- return ((fontId >= 0) && (fontId < _loadedFonts));
+ return (fontId < _loadedFonts);
}
int getByteLen(int numBits) const {
int byteLength = numBits / 8;
diff --git a/engines/saga/interface.cpp b/engines/saga/interface.cpp
index c0d3cee465..c4b4688785 100644
--- a/engines/saga/interface.cpp
+++ b/engines/saga/interface.cpp
@@ -544,8 +544,10 @@ bool Interface::processAscii(Common::KeyState keystate) {
return true;
}
+#ifdef ENABLE_IHNM
if (_vm->_scene->isNonInteractiveIHNMDemoPart())
_vm->_scene->showIHNMDemoSpecialScreen();
+#endif
break;
case kPanelCutaway:
if (keystate.keycode == Common::KEYCODE_ESCAPE) {
@@ -555,8 +557,10 @@ bool Interface::processAscii(Common::KeyState keystate) {
return true;
}
+#ifdef ENABLE_INHM
if (_vm->_scene->isNonInteractiveIHNMDemoPart())
_vm->_scene->showIHNMDemoSpecialScreen();
+#endif
break;
case kPanelVideo:
if (keystate.keycode == Common::KEYCODE_ESCAPE) {
@@ -570,8 +574,10 @@ bool Interface::processAscii(Common::KeyState keystate) {
return true;
}
+#ifdef ENABLE_IHNM
if (_vm->_scene->isNonInteractiveIHNMDemoPart())
_vm->_scene->showIHNMDemoSpecialScreen();
+#endif
break;
case kPanelOption:
// TODO: check input dialog keys
@@ -1855,6 +1861,7 @@ void Interface::update(const Point& mousePoint, int updateFlag) {
if (updateFlag & UPDATE_MOUSECLICK) {
if (!_vm->isIHNMDemo()) {
_vm->_scene->clearPsychicProfile();
+ _vm->_script->wakeUpThreads(kWaitTypeDelay);
} else {
setMode(kPanelConverse);
_vm->_scene->_textList.clear();
@@ -1866,8 +1873,10 @@ void Interface::update(const Point& mousePoint, int updateFlag) {
break;
case kPanelNull:
+#ifdef ENABLE_IHNM
if (_vm->_scene->isNonInteractiveIHNMDemoPart() && (updateFlag & UPDATE_MOUSECLICK))
_vm->_scene->showIHNMDemoSpecialScreen();
+#endif
break;
}
diff --git a/engines/saga/music.cpp b/engines/saga/music.cpp
index 75c5cdffd7..8b7654d689 100644
--- a/engines/saga/music.cpp
+++ b/engines/saga/music.cpp
@@ -42,19 +42,25 @@ namespace Saga {
#define BUFFER_SIZE 4096
#define MUSIC_SUNSPOT 26
-MusicPlayer::MusicPlayer(MidiDriver *driver) : _parser(0), _driver(driver), _looping(false), _isPlaying(false), _passThrough(false), _isGM(false) {
+MusicDriver::MusicDriver() : _isGM(false) {
memset(_channel, 0, sizeof(_channel));
_masterVolume = 0;
+ _nativeMT32 = ConfMan.getBool("native_mt32");
+
+ MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
+ _driver = MidiDriver::createMidi(dev);
+ if (isMT32())
+ _driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
+
this->open();
}
-MusicPlayer::~MusicPlayer() {
- _driver->setTimerCallback(NULL, NULL);
- stopMusic();
+MusicDriver::~MusicDriver() {
this->close();
+ delete _driver;
}
-void MusicPlayer::setVolume(int volume) {
+void MusicDriver::setVolume(int volume) {
volume = CLIP(volume, 0, 255);
if (_masterVolume == volume)
@@ -71,32 +77,7 @@ void MusicPlayer::setVolume(int volume) {
}
}
-int MusicPlayer::open() {
- // Don't ever call open without first setting the output driver!
- if (!_driver)
- return 255;
-
- int ret = _driver->open();
- if (ret)
- return ret;
-
- _driver->setTimerCallback(this, &onTimer);
- return 0;
-}
-
-void MusicPlayer::close() {
- stopMusic();
- if (_driver)
- _driver->close();
- _driver = 0;
-}
-
-void MusicPlayer::send(uint32 b) {
- if (_passThrough) {
- _driver->send(b);
- return;
- }
-
+void MusicDriver::send(uint32 b) {
byte channel = (byte)(b & 0x0F);
if ((b & 0xFFF0) == 0x07B0) {
// Adjust volume changes by master volume
@@ -104,71 +85,86 @@ void MusicPlayer::send(uint32 b) {
_channelVolume[channel] = volume;
volume = volume * _masterVolume / 255;
b = (b & 0xFF00FFFF) | (volume << 16);
- } else if ((b & 0xF0) == 0xC0 && !_isGM && !_nativeMT32) {
+ } else if ((b & 0xF0) == 0xC0 && !_isGM && !isMT32()) {
+ // Remap MT32 instruments to General Midi
b = (b & 0xFFFF00FF) | MidiDriver::_mt32ToGm[(b >> 8) & 0xFF] << 8;
- }
- else if ((b & 0xFFF0) == 0x007BB0) {
- //Only respond to All Notes Off if this channel
- //has currently been allocated
- if (_channel[b & 0x0F])
+ } else if ((b & 0xFFF0) == 0x007BB0) {
+ // Only respond to All Notes Off if this channel
+ // has currently been allocated
+ if (!_channel[channel])
return;
}
if (!_channel[channel])
_channel[channel] = (channel == 9) ? _driver->getPercussionChannel() : _driver->allocateChannel();
-
- if (_channel[channel])
+ else
_channel[channel]->send(b);
}
-void MusicPlayer::metaEvent(byte type, byte *data, uint16 length) {
- // FIXME: The "elkfanfare" is played much too quickly. There are some
- // meta events that we don't handle. Perhaps there is a
- // connection...?
-
- switch (type) {
- case 0x2F: // End of Track
- if (_looping)
- _parser->jumpToTick(0);
- else
- stopMusic();
- break;
- default:
- //warning("Unhandled meta event: %02x", type);
- break;
- }
-}
-
-void MusicPlayer::onTimer(void *refCon) {
- MusicPlayer *music = (MusicPlayer *)refCon;
- Common::StackLock lock(music->_mutex);
-
- if (music->_isPlaying)
- music->_parser->onTimer();
-}
-
-void MusicPlayer::playMusic() {
- _isPlaying = true;
-}
+Music::Music(SagaEngine *vm, Audio::Mixer *mixer) : _vm(vm), _mixer(mixer) {
+ _currentVolume = 0;
+ _driver = new MusicDriver();
-void MusicPlayer::stopMusic() {
- Common::StackLock lock(_mutex);
+ _digitalMusicContext = _vm->_resource->getContext(GAME_DIGITALMUSICFILE);
+ if (!_driver->isAdlib())
+ _musicContext = _vm->_resource->getContext(GAME_MUSICFILE_GM);
+ else
+ _musicContext = _vm->_resource->getContext(GAME_MUSICFILE_FM);
- _isPlaying = false;
- if (_parser) {
- _parser->unloadMusic();
- _parser = NULL;
+ if (!_musicContext) {
+ if (_vm->getGameId() == GID_ITE) {
+ _musicContext = _vm->_resource->getContext(GAME_RESOURCEFILE);
+ } else {
+ // I've listened to music from both the FM and the GM
+ // file, and I've tentatively reached the conclusion
+ // that they are both General MIDI. My guess is that
+ // the FM file has been reorchestrated to sound better
+ // on AdLib and other FM synths.
+ //
+ // Sev says the AdLib music does not sound like in the
+ // original, but I still think assuming General MIDI is
+ // the right thing to do. Some music, like the End
+ // Title (song 0) sound absolutely atrocious when piped
+ // through our MT-32 to GM mapping.
+ //
+ // It is, however, quite possible that the original
+ // used a different GM to FM mapping. If the original
+ // sounded markedly better, perhaps we should add some
+ // way of replacing our stock mapping in adlib.cpp?
+ //
+ // For the composer's own recording of the End Title,
+ // see http://www.johnottman.com/
+
+ // Oddly enough, the intro music (song 1) is very
+ // different in the two files. I have no idea why.
+ // Note that the IHNM demo has only got one music file
+ // (music.rsc). It is assumed that it contains FM music
+ _musicContext = _vm->_resource->getContext(GAME_MUSICFILE_FM);
+ }
}
-}
-Music::Music(SagaEngine *vm, Audio::Mixer *mixer, MidiDriver *driver) : _vm(vm), _mixer(mixer), _adlib(false) {
- _player = new MusicPlayer(driver);
- _currentVolume = 0;
-
- xmidiParser = MidiParser::createParser_XMIDI();
- smfParser = MidiParser::createParser_SMF();
+ // Check if the game is using XMIDI or SMF music
+ if (_vm->getGameId() == GID_IHNM && _vm->isMacResources()) {
+ // Just set an XMIDI parser for Mac IHNM for now
+ _parser = MidiParser::createParser_XMIDI();
+ } else {
+ byte *resourceData;
+ size_t resourceSize;
+ int resourceId = (_vm->getGameId() == GID_ITE ? 9 : 0);
+ _vm->_resource->loadResource(_musicContext, resourceId, resourceData, resourceSize);
+ if (!memcmp(resourceData, "FORM", 4)) {
+ _parser = MidiParser::createParser_XMIDI();
+ // ITE had MT32 mapped instruments
+ _driver->setGM(_vm->getGameId() != GID_ITE);
+ } else {
+ _parser = MidiParser::createParser_SMF();
+ }
+ free(resourceData);
+ }
- _digitalMusicContext = _vm->_resource->getContext(GAME_DIGITALMUSICFILE);
+ _parser->setMidiDriver(_driver);
+ _parser->setTimerRate(_driver->getBaseTempo());
+ _parser->property(MidiParser::mpCenterPitchWheelOnUnload, 1);
_songTableLen = 0;
_songTable = 0;
@@ -180,11 +176,11 @@ Music::Music(SagaEngine *vm, Audio::Mixer *mixer, MidiDriver *driver) : _vm(vm),
Music::~Music() {
_vm->getTimerManager()->removeTimerProc(&musicVolumeGaugeCallback);
_mixer->stopHandle(_musicHandle);
- delete _player;
- xmidiParser->setMidiDriver(NULL);
- smfParser->setMidiDriver(NULL);
- delete xmidiParser;
- delete smfParser;
+ _driver->setTimerCallback(NULL, NULL);
+ _driver->close();
+ delete _driver;
+ _parser->setMidiDriver(NULL);
+ delete _parser;
free(_songTable);
free(_midiMusicData);
@@ -194,6 +190,12 @@ void Music::musicVolumeGaugeCallback(void *refCon) {
((Music *)refCon)->musicVolumeGauge();
}
+void Music::onTimer(void *refCon) {
+ Music *music = (Music *)refCon;
+ Common::StackLock lock(music->_driver->_mutex);
+ music->_parser->onTimer();
+}
+
void Music::musicVolumeGauge() {
int volume;
@@ -209,7 +211,7 @@ void Music::musicVolumeGauge() {
volume = 1;
_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, volume);
- _player->setVolume(volume);
+ _driver->setVolume(volume);
if (_currentVolumePercent == 100) {
_vm->getTimerManager()->removeTimerProc(&musicVolumeGaugeCallback);
@@ -226,23 +228,21 @@ void Music::setVolume(int volume, int time) {
if (time == 1) {
_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, volume);
- _player->setVolume(volume);
+ _driver->setVolume(volume);
_vm->getTimerManager()->removeTimerProc(&musicVolumeGaugeCallback);
_currentVolume = volume;
return;
}
- _vm->getTimerManager()->installTimerProc(&musicVolumeGaugeCallback, time * 100L, this);
+ _vm->getTimerManager()->installTimerProc(&musicVolumeGaugeCallback, time * 3000L, this);
}
bool Music::isPlaying() {
- return _mixer->isSoundHandleActive(_musicHandle) || _player->isPlaying();
+ return _mixer->isSoundHandleActive(_musicHandle) || _parser->isPlaying();
}
void Music::play(uint32 resourceId, MusicFlags flags) {
Audio::SeekableAudioStream *audioStream = NULL;
- MidiParser *parser;
- ResourceContext *context = NULL;
byte *resourceData;
size_t resourceSize;
uint32 loopStart;
@@ -254,8 +254,8 @@ void Music::play(uint32 resourceId, MusicFlags flags) {
}
_trackNumber = resourceId;
- _player->stopMusic();
_mixer->stopHandle(_musicHandle);
+ _parser->unloadMusic();
int realTrackNumber;
@@ -356,55 +356,10 @@ void Music::play(uint32 resourceId, MusicFlags flags) {
return;
}
- if (flags == MUSIC_DEFAULT) {
+ if (flags == MUSIC_DEFAULT)
flags = MUSIC_NORMAL;
- }
// Load MIDI/XMI resource data
-
- if (_vm->getGameId() == GID_ITE) {
- context = _vm->_resource->getContext(GAME_MUSICFILE_GM);
- if (context == NULL) {
- context = _vm->_resource->getContext(GAME_RESOURCEFILE);
- }
- } else if (_vm->getGameId() == GID_IHNM && _vm->isMacResources()) {
- // The music of the Mac version of IHNM is loaded from its
- // associated external file later on
- } else {
- // I've listened to music from both the FM and the GM
- // file, and I've tentatively reached the conclusion
- // that they are both General MIDI. My guess is that
- // the FM file has been reorchestrated to sound better
- // on AdLib and other FM synths.
- //
- // Sev says the AdLib music does not sound like in the
- // original, but I still think assuming General MIDI is
- // the right thing to do. Some music, like the End
- // Title (song 0) sound absolutely atrocious when piped
- // through our MT-32 to GM mapping.
- //
- // It is, however, quite possible that the original
- // used a different GM to FM mapping. If the original
- // sounded markedly better, perhaps we should add some
- // way of replacing our stock mapping in adlib.cpp?
- //
- // For the composer's own recording of the End Title,
- // see http://www.johnottman.com/
-
- // Oddly enough, the intro music (song 1) is very
- // different in the two files. I have no idea why.
- // Note that the IHNM demo has only got one music file
- // (music.rsc). It is assumed that it contains FM music
-
- if (hasAdLib() || _vm->isIHNMDemo()) {
- context = _vm->_resource->getContext(GAME_MUSICFILE_FM);
- } else {
- context = _vm->_resource->getContext(GAME_MUSICFILE_GM);
- }
- }
-
- _player->setGM(true);
-
if (_vm->getGameId() == GID_IHNM && _vm->isMacResources()) {
// Load the external music file for Mac IHNM
#if 0
@@ -422,56 +377,39 @@ void Music::play(uint32 resourceId, MusicFlags flags) {
#endif
return;
} else {
- _vm->_resource->loadResource(context, resourceId, resourceData, resourceSize);
+ _vm->_resource->loadResource(_musicContext, resourceId, resourceData, resourceSize);
}
if (resourceSize < 4) {
error("Music::play() wrong music resource size");
}
- if (xmidiParser->loadMusic(resourceData, resourceSize)) {
- if (_vm->getGameId() == GID_ITE)
- _player->setGM(false);
+ if (!_parser->loadMusic(resourceData, resourceSize))
+ error("Music::play() wrong music resource");
- parser = xmidiParser;
- } else {
- if (smfParser->loadMusic(resourceData, resourceSize)) {
- parser = smfParser;
- } else {
- error("Music::play() wrong music resource");
- }
- }
-
- parser->setTrack(0);
- parser->setMidiDriver(_player);
- parser->setTimerRate(_player->getBaseTempo());
- parser->property(MidiParser::mpCenterPitchWheelOnUnload, 1);
+ _parser->setTrack(0);
+ _driver->setTimerCallback(this, &onTimer);
- _player->_parser = parser;
setVolume(_vm->_musicVolume);
- if (flags & MUSIC_LOOP)
- _player->setLoop(true);
- else
- _player->setLoop(false);
+ // Handle music looping
+ _parser->property(MidiParser::mpAutoLoop, (flags & MUSIC_LOOP) ? 1 : 0);
- _player->playMusic();
free(_midiMusicData);
_midiMusicData = resourceData;
}
void Music::pause() {
- _player->setVolume(-1);
- _player->setPlaying(false);
+ _driver->setTimerCallback(NULL, NULL);
}
void Music::resume() {
- _player->setVolume(_vm->_musicVolume);
- _player->setPlaying(true);
+ _driver->setTimerCallback(this, &onTimer);
}
void Music::stop() {
- _player->stopMusic();
+ _driver->setTimerCallback(NULL, NULL);
+ _parser->unloadMusic();
}
} // End of namespace Saga
diff --git a/engines/saga/music.h b/engines/saga/music.h
index 131078a9c3..f3b0f177b0 100644
--- a/engines/saga/music.h
+++ b/engines/saga/music.h
@@ -44,41 +44,32 @@ enum MusicFlags {
MUSIC_DEFAULT = 0xffff
};
-class MusicPlayer : public MidiDriver {
+class MusicDriver : public MidiDriver {
public:
- MusicPlayer(MidiDriver *driver);
- ~MusicPlayer();
-
- bool isPlaying() { return _isPlaying; }
- void setPlaying(bool playing) { _isPlaying = playing; }
+ MusicDriver();
+ ~MusicDriver();
void setVolume(int volume);
int getVolume() { return _masterVolume; }
- void setNativeMT32(bool b) { _nativeMT32 = b; }
- bool hasNativeMT32() { return _nativeMT32; }
- void playMusic();
- void stopMusic();
- void setLoop(bool loop) { _looping = loop; }
- void setPassThrough(bool b) { _passThrough = b; }
-
+ bool isAdlib() { return _driverType == MT_ADLIB; }
+ bool isMT32() { return _driverType == MT_MT32 || _nativeMT32; }
void setGM(bool isGM) { _isGM = isGM; }
//MidiDriver interface implementation
- int open();
- void close();
+ int open() { return _driver->open(); }
+ void close() { _driver->close(); }
void send(uint32 b);
- void metaEvent(byte type, byte *data, uint16 length);
+ void metaEvent(byte type, byte *data, uint16 length) {}
- void setTimerCallback(void *timerParam, void (*timerProc)(void *)) { }
- uint32 getBaseTempo() { return _driver ? _driver->getBaseTempo() : 0; }
+ void setTimerCallback(void *timerParam, void (*timerProc)(void *)) { _driver->setTimerCallback(timerParam, timerProc); }
+ uint32 getBaseTempo() { return _driver->getBaseTempo(); }
//Channel allocation functions
MidiChannel *allocateChannel() { return 0; }
MidiChannel *getPercussionChannel() { return 0; }
- MidiParser *_parser;
Common::Mutex _mutex;
protected:
@@ -87,14 +78,11 @@ protected:
MidiChannel *_channel[16];
MidiDriver *_driver;
+ MusicType _driverType;
byte _channelVolume[16];
- bool _nativeMT32;
bool _isGM;
- bool _passThrough;
+ bool _nativeMT32;
- bool _isPlaying;
- bool _looping;
- bool _randomLoop;
byte _masterVolume;
byte *_musicData;
@@ -105,13 +93,8 @@ protected:
class Music {
public:
- Music(SagaEngine *vm, Audio::Mixer *mixer, MidiDriver *driver);
+ Music(SagaEngine *vm, Audio::Mixer *mixer);
~Music();
- void setNativeMT32(bool b) { _player->setNativeMT32(b); }
- bool hasNativeMT32() { return _player->hasNativeMT32(); }
- void setAdLib(bool b) { _adlib = b; }
- bool hasAdLib() { return _adlib; }
- void setPassThrough(bool b) { _player->setPassThrough(b); }
bool isPlaying();
bool hasDigitalMusic() { return _digitalMusic; }
@@ -130,24 +113,23 @@ private:
SagaEngine *_vm;
Audio::Mixer *_mixer;
- MusicPlayer *_player;
+ MusicDriver *_driver;
Audio::SoundHandle _musicHandle;
uint32 _trackNumber;
- bool _adlib;
-
int _targetVolume;
int _currentVolume;
int _currentVolumePercent;
bool _digitalMusic;
+ ResourceContext *_musicContext;
ResourceContext *_digitalMusicContext;
- MidiParser *xmidiParser;
- MidiParser *smfParser;
+ MidiParser *_parser;
byte *_midiMusicData;
static void musicVolumeGaugeCallback(void *refCon);
+ static void onTimer(void *refCon);
void musicVolumeGauge();
};
diff --git a/engines/saga/puzzle.cpp b/engines/saga/puzzle.cpp
index 957ab3c8b6..5b13473d77 100644
--- a/engines/saga/puzzle.cpp
+++ b/engines/saga/puzzle.cpp
@@ -411,12 +411,12 @@ void Puzzle::solicitHint() {
switch (_hintRqState) {
case kRQSpeaking:
if (_vm->_actor->isSpeaking()) {
- _vm->getTimerManager()->installTimerProc(&hintTimerCallback, 50000, this);
+ _vm->getTimerManager()->installTimerProc(&hintTimerCallback, 50 * 1000000, this);
break;
}
_hintRqState = _hintNextRqState;
- _vm->getTimerManager()->installTimerProc(&hintTimerCallback, 333333, this);
+ _vm->getTimerManager()->installTimerProc(&hintTimerCallback, 100*1000000/3, this);
break;
case kRQNoHint:
@@ -439,11 +439,11 @@ void Puzzle::solicitHint() {
// Roll to see if Sakka scolds
if (_vm->_rnd.getRandomNumber(1)) {
_hintRqState = kRQSakkaDenies;
- _vm->getTimerManager()->installTimerProc(&hintTimerCallback, 200000, this);
+ _vm->getTimerManager()->installTimerProc(&hintTimerCallback, 200*1000000, this);
} else {
_hintRqState = kRQSpeaking;
_hintNextRqState = kRQHintRequested;
- _vm->getTimerManager()->installTimerProc(&hintTimerCallback, 50000, this);
+ _vm->getTimerManager()->installTimerProc(&hintTimerCallback, 50*1000000, this);
}
break;
@@ -456,7 +456,7 @@ void Puzzle::solicitHint() {
_hintRqState = kRQSpeaking;
_hintNextRqState = kRQHintRequestedStage2;
- _vm->getTimerManager()->installTimerProc(&hintTimerCallback, 50000, this);
+ _vm->getTimerManager()->installTimerProc(&hintTimerCallback, 50*1000000, this);
_vm->_interface->converseClear();
_vm->_interface->converseAddText(optionsStr[_lang][kROAccept], 0, 1, 0, 0 );
diff --git a/engines/saga/render.cpp b/engines/saga/render.cpp
index ec168d296e..dc9334b037 100644
--- a/engines/saga/render.cpp
+++ b/engines/saga/render.cpp
@@ -110,6 +110,12 @@ void Render::drawScene() {
_vm->_actor->drawActors();
}
+ // WORKAROUND
+ // Bug #2886130: "ITE: Graphic Glitches during Cat Tribe Celebration"
+ if (_vm->_scene->currentSceneNumber() == 274) {
+ _vm->_interface->drawStatusBar();
+ }
+
#ifdef SAGA_DEBUG
if (getFlags() & RF_OBJECTMAP_TEST) {
if (_vm->_scene->_objectMap)
diff --git a/engines/saga/saga.cpp b/engines/saga/saga.cpp
index ed8a9055ba..d1ab3bc9d7 100644
--- a/engines/saga/saga.cpp
+++ b/engines/saga/saga.cpp
@@ -83,7 +83,6 @@ SagaEngine::SagaEngine(OSystem *syst, const SAGAGameDescription *gameDesc)
_sndRes = NULL;
_sound = NULL;
_music = NULL;
- _driver = NULL;
_anim = NULL;
_render = NULL;
_isoMap = NULL;
@@ -198,9 +197,6 @@ SagaEngine::~SagaEngine() {
delete _sound;
_sound = NULL;
- delete _driver;
- _driver = NULL;
-
delete _gfx;
_gfx = NULL;
@@ -285,17 +281,7 @@ Common::Error SagaEngine::run() {
_console = new Console(this);
// Graphics should be initialized before music
- MidiDriverType midiDriver = MidiDriver::detectMusicDriver(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MIDI);
- bool native_mt32 = ((midiDriver == MD_MT32) || ConfMan.getBool("native_mt32"));
- bool adlib = (midiDriver == MD_ADLIB);
-
- _driver = MidiDriver::createMidi(midiDriver);
- if (native_mt32)
- _driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
-
- _music = new Music(this, _mixer, _driver);
- _music->setNativeMT32(native_mt32);
- _music->setAdLib(adlib);
+ _music = new Music(this, _mixer);
_render = new Render(this, _system);
if (!_render->initialized()) {
return Common::kUnknownError;
@@ -433,7 +419,7 @@ void SagaEngine::loadStrings(StringsTable &stringsTable, const byte *stringsPoin
offset = scriptS.readUint16();
// In some rooms in IHNM, string offsets can be greater than the maximum value than a 16-bit integer can hold
// We detect this by checking the previous offset, and if it was bigger than the current one, an overflow
- // occured (since the string offsets are sequential), so we're adding the missing part of the number
+ // occurred (since the string offsets are sequential), so we're adding the missing part of the number
// Fixes bug #1895205 - "IHNM: end game text/caption error"
if (prevOffset > offset)
offset += 65536;
diff --git a/engines/saga/saga.h b/engines/saga/saga.h
index 2bef489e05..102d1e5c82 100644
--- a/engines/saga/saga.h
+++ b/engines/saga/saga.h
@@ -525,7 +525,6 @@ public:
SndRes *_sndRes;
Sound *_sound;
Music *_music;
- MidiDriver *_driver;
Anim *_anim;
Render *_render;
IsoMap *_isoMap;
diff --git a/engines/saga/scene.cpp b/engines/saga/scene.cpp
index 40ee1fb720..d7ee037c50 100644
--- a/engines/saga/scene.cpp
+++ b/engines/saga/scene.cpp
@@ -1201,6 +1201,12 @@ void Scene::endScene() {
_vm->_script->abortAllThreads();
_vm->_script->_skipSpeeches = false;
+ // WORKAROUND: Bug #2886151: "ITE: Mouse stops responding at Boar Castle"
+ // This is bug in original engine
+ if (_sceneNumber == 50) {
+ _vm->_interface->activate();
+ }
+
// Copy current screen to render buffer so inset rooms will get proper background
if (!(_sceneDescription.flags & kSceneFlagISO) && !_vm->_scene->isInIntro()) {
BGInfo bgInfo;
@@ -1425,6 +1431,8 @@ void Scene::clearPlacard() {
q_event = _vm->_events->chain(q_event, &event);
}
+#ifdef ENABLE_IHNM
+
void Scene::showPsychicProfile(const char *text) {
int textHeight;
static PalEntry cur_pal[PAL_ENTRIES];
@@ -1440,6 +1448,8 @@ void Scene::showPsychicProfile(const char *text) {
_vm->_interface->setMode(kPanelPlacard);
_vm->_gfx->savePalette();
+ _vm->_events->clearList();
+
event.type = kEvTOneshot;
event.code = kCursorEvent;
event.op = kEventHide;
@@ -1531,4 +1541,6 @@ void Scene::showIHNMDemoSpecialScreen() {
_vm->_scene->changeScene(150, 0, kTransitionFade);
}
+#endif // IHNM
+
} // End of namespace Saga
diff --git a/engines/saga/script.h b/engines/saga/script.h
index f31af7b2ea..21afeb5c44 100644
--- a/engines/saga/script.h
+++ b/engines/saga/script.h
@@ -246,16 +246,19 @@ public:
}
void waitWalk(void *threadObj) {
+ debug(3, "waitWalk()");
wait(kWaitTypeWalk);
_threadObj = threadObj;
}
void waitDelay(int sleepTime) {
+ debug(3, "waitDelay(%d)", sleepTime);
wait(kWaitTypeDelay);
_sleepTime = sleepTime;
}
void waitFrames(int frames) {
+ debug(3, "waitFrames(%d)", frames);
wait(kWaitTypeWaitFrames);
_frameWait = frames;
}
diff --git a/engines/saga/sfuncs.cpp b/engines/saga/sfuncs.cpp
index f98a80acd8..328d4040af 100644
--- a/engines/saga/sfuncs.cpp
+++ b/engines/saga/sfuncs.cpp
@@ -370,12 +370,15 @@ void Script::sfStopBgdAnim(SCRIPTFUNC_PARAMS) {
// reenabled.
// Param1: boolean
void Script::sfLockUser(SCRIPTFUNC_PARAMS) {
- if (thread->pop()) {
+ int16 param = thread->pop();
+
+ if (param != 0) {
_vm->_interface->deactivate();
} else {
_vm->_interface->activate();
}
+ debug(1, "sfLockUser(%d)", param);
}
// Script function #12 (0x0C)
@@ -1153,18 +1156,6 @@ void Script::sfPlacardOff(SCRIPTFUNC_PARAMS) {
_vm->_scene->clearPlacard();
}
-void Script::sfPsychicProfile(SCRIPTFUNC_PARAMS) {
- thread->wait(kWaitTypePlacard);
-
- _vm->_scene->showPsychicProfile(thread->_strings->getString(thread->pop()));
-}
-
-void Script::sfPsychicProfileOff(SCRIPTFUNC_PARAMS) {
- // This is called a while after the psychic profile is
- // opened, to close it automatically
- _vm->_scene->clearPsychicProfile();
-}
-
// Script function #50 (0x32)
void Script::sfSetProtagState(SCRIPTFUNC_PARAMS) {
_vm->_actor->setProtagState(thread->pop());
@@ -1473,6 +1464,8 @@ void Script::sfPlayLoopedSound(SCRIPTFUNC_PARAMS) {
} else {
_vm->_sound->stopSound();
}
+
+ debug(1, "sfPlayLoopedSound(%d)", param);
}
// Script function #72 (0x48)
diff --git a/engines/saga/sfuncs_ihnm.cpp b/engines/saga/sfuncs_ihnm.cpp
index fe586b54ae..b98c1cb852 100644
--- a/engines/saga/sfuncs_ihnm.cpp
+++ b/engines/saga/sfuncs_ihnm.cpp
@@ -440,6 +440,18 @@ void Script::sfDisableAbortSpeeches(SCRIPTFUNC_PARAMS) {
_vm->_interface->disableAbortSpeeches(thread->pop() != 0);
}
+void Script::sfPsychicProfile(SCRIPTFUNC_PARAMS) {
+ thread->wait(kWaitTypePlacard);
+
+ _vm->_scene->showPsychicProfile(thread->_strings->getString(thread->pop()));
+}
+
+void Script::sfPsychicProfileOff(SCRIPTFUNC_PARAMS) {
+ // This is called a while after the psychic profile is
+ // opened, to close it automatically
+ _vm->_scene->clearPsychicProfile();
+}
+
} // End of namespace Saga
#endif
diff --git a/engines/saga/sndres.cpp b/engines/saga/sndres.cpp
index 8ea3fc4003..a27608dcf5 100644
--- a/engines/saga/sndres.cpp
+++ b/engines/saga/sndres.cpp
@@ -159,7 +159,7 @@ void SndRes::playSound(uint32 resourceId, int volume, bool loop) {
return;
}
- _vm->_sound->playSound(buffer, volume, loop);
+ _vm->_sound->playSound(buffer, volume, loop, resourceId);
}
void SndRes::playVoice(uint32 resourceId) {
diff --git a/engines/saga/sound.cpp b/engines/saga/sound.cpp
index 811ee709f7..db979e8104 100644
--- a/engines/saga/sound.cpp
+++ b/engines/saga/sound.cpp
@@ -105,10 +105,20 @@ void Sound::playSoundBuffer(Audio::SoundHandle *handle, const SoundBuffer &buffe
_mixer->playStream(soundType, handle, Audio::makeLoopingAudioStream(stream, loop ? 0 : 1), -1, volume);
}
-void Sound::playSound(SoundBuffer &buffer, int volume, bool loop) {
+void Sound::playSound(SoundBuffer &buffer, int volume, bool loop, int resId) {
+ // WORKAROUND
+ // Prevent playing same looped sound for several times
+ // Fixes bug #2886141: "ITE: Cumulative Snoring sounds in Prince's Bedroom"
+ for (int i = 0; i < SOUND_HANDLES; i++)
+ if (_handles[i].type == kEffectHandle && _handles[i].resId == resId) {
+ debug(1, "Skipped playing SFX #%d", resId);
+ return;
+ }
+
SndHandle *handle = getHandle();
handle->type = kEffectHandle;
+ handle->resId = resId;
playSoundBuffer(&handle->handle, buffer, 2 * volume, handle->type, loop);
}
@@ -129,6 +139,7 @@ void Sound::stopSound() {
if (_handles[i].type == kEffectHandle) {
_mixer->stopHandle(_handles[i].handle);
_handles[i].type = kFreeHandle;
+ _handles[i].resId = -1;
}
}
diff --git a/engines/saga/sound.h b/engines/saga/sound.h
index 9cf8f29767..7ee2765a0f 100644
--- a/engines/saga/sound.h
+++ b/engines/saga/sound.h
@@ -63,6 +63,7 @@ enum sndHandleType {
struct SndHandle {
Audio::SoundHandle handle;
sndHandleType type;
+ int resId;
};
class Sound {
@@ -71,7 +72,7 @@ public:
Sound(SagaEngine *vm, Audio::Mixer *mixer);
~Sound();
- void playSound(SoundBuffer &buffer, int volume, bool loop);
+ void playSound(SoundBuffer &buffer, int volume, bool loop, int resId);
void pauseSound();
void resumeSound();
void stopSound();
diff --git a/engines/saga/sthread.cpp b/engines/saga/sthread.cpp
index c133f8de88..be674e5acd 100644
--- a/engines/saga/sthread.cpp
+++ b/engines/saga/sthread.cpp
@@ -58,6 +58,8 @@ ScriptThread &Script::createThread(uint16 scriptModuleNumber, uint16 scriptEntry
_threadList.push_front(newThread);
+ debug(3, "createThread(). Total threads: %d", _threadList.size());
+
ScriptThread &tmp = *_threadList.begin();
tmp._stackBuf = (int16 *)malloc(ScriptThread::THREAD_STACK_SIZE * sizeof(int16));
tmp._stackTopIndex = ScriptThread::THREAD_STACK_SIZE - 2;
@@ -78,6 +80,8 @@ void Script::wakeUpActorThread(int waitType, void *threadObj) {
void Script::wakeUpThreads(int waitType) {
ScriptThreadList::iterator threadIterator;
+ debug(3, "wakeUpThreads(%d)", waitType);
+
for (threadIterator = _threadList.begin(); threadIterator != _threadList.end(); ++threadIterator) {
ScriptThread &thread = *threadIterator;
if ((thread._flags & kTFlagWaiting) && (thread._waitType == waitType)) {
@@ -89,6 +93,8 @@ void Script::wakeUpThreads(int waitType) {
void Script::wakeUpThreadsDelayed(int waitType, int sleepTime) {
ScriptThreadList::iterator threadIterator;
+ debug(3, "wakeUpThreads(%d, %d)", waitType, sleepTime);
+
for (threadIterator = _threadList.begin(); threadIterator != _threadList.end(); ++threadIterator) {
ScriptThread &thread = *threadIterator;
if ((thread._flags & kTFlagWaiting) && (thread._waitType == waitType)) {
@@ -169,6 +175,8 @@ void Script::executeThreads(uint msec) {
void Script::abortAllThreads() {
ScriptThreadList::iterator threadIterator;
+ debug(3, "abortAllThreads()");
+
threadIterator = _threadList.begin();
while (threadIterator != _threadList.end()) {
diff --git a/engines/sci/console.cpp b/engines/sci/console.cpp
index 814fbff636..33aa5514f2 100644
--- a/engines/sci/console.cpp
+++ b/engines/sci/console.cpp
@@ -31,23 +31,19 @@
#include "sci/event.h"
#include "sci/resource.h"
#include "sci/engine/state.h"
+#include "sci/engine/kernel.h"
#include "sci/engine/selector.h"
#include "sci/engine/savegame.h"
#include "sci/engine/gc.h"
#include "sci/engine/features.h"
-#ifdef USE_OLD_MUSIC_FUNCTIONS
-#include "sci/sound/iterator/songlib.h" // for SongLibrary
-#include "sci/sound/iterator/iterator.h" // for SCI_SONG_ITERATOR_TYPE_SCI0
-#else
+#include "sci/sound/midiparser_sci.h"
#include "sci/sound/music.h"
-#endif
#include "sci/sound/drivers/mididriver.h"
-#include "sci/graphics/gui.h"
-#include "sci/graphics/gui32.h"
#include "sci/graphics/cursor.h"
#include "sci/graphics/screen.h"
#include "sci/graphics/paint.h"
#include "sci/graphics/paint16.h"
+#include "sci/graphics/paint32.h"
#include "sci/graphics/palette.h"
#include "sci/parser/vocabulary.h"
@@ -70,15 +66,15 @@ bool g_debug_track_mouse_clicks = false;
// Refer to the "addresses" command on how to pass address parameters
static int parse_reg_t(EngineState *s, const char *str, reg_t *dest, bool mayBeValue);
-Console::Console(SciEngine *engine) : GUI::Debugger() {
- _engine = engine;
+Console::Console(SciEngine *engine) : GUI::Debugger(),
+ _engine(engine), _debugState(engine->_debugState) {
// Variables
DVar_Register("sleeptime_factor", &g_debug_sleeptime_factor, DVAR_INT, 0);
- DVar_Register("gc_interval", &engine->_gamestate->script_gc_interval, DVAR_INT, 0);
+ DVar_Register("gc_interval", &engine->_gamestate->scriptGCInterval, 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", &_engine->_gamestate->script_abort_flag, DVAR_INT, 0);
+ DVar_Register("script_abort_flag", &_engine->_gamestate->abortScriptProcessing, DVAR_INT, 0);
// General
DCmd_Register("help", WRAP_METHOD(Console, cmdHelp));
@@ -106,6 +102,7 @@ Console::Console(SciEngine *engine) : GUI::Debugger() {
DCmd_Register("list", WRAP_METHOD(Console, cmdList));
DCmd_Register("hexgrep", WRAP_METHOD(Console, cmdHexgrep));
DCmd_Register("verify_scripts", WRAP_METHOD(Console, cmdVerifyScripts));
+ DCmd_Register("show_instruments", WRAP_METHOD(Console, cmdShowInstruments));
// Game
DCmd_Register("save_game", WRAP_METHOD(Console, cmdSaveGame));
DCmd_Register("restore_game", WRAP_METHOD(Console, cmdRestoreGame));
@@ -151,15 +148,17 @@ Console::Console(SciEngine *engine) : GUI::Debugger() {
DCmd_Register("addresses", WRAP_METHOD(Console, cmdAddresses));
DCmd_Register("registers", WRAP_METHOD(Console, cmdRegisters));
DCmd_Register("dissect_script", WRAP_METHOD(Console, cmdDissectScript));
- DCmd_Register("set_acc", WRAP_METHOD(Console, cmdSetAccumulator));
DCmd_Register("backtrace", WRAP_METHOD(Console, cmdBacktrace));
DCmd_Register("bt", WRAP_METHOD(Console, cmdBacktrace)); // alias
- DCmd_Register("step", WRAP_METHOD(Console, cmdStep));
- DCmd_Register("s", WRAP_METHOD(Console, cmdStep)); // alias
+ DCmd_Register("trace", WRAP_METHOD(Console, cmdTrace));
+ DCmd_Register("t", WRAP_METHOD(Console, cmdTrace)); // alias
+ DCmd_Register("s", WRAP_METHOD(Console, cmdTrace)); // alias
+ DCmd_Register("stepover", WRAP_METHOD(Console, cmdStepOver));
+ DCmd_Register("p", WRAP_METHOD(Console, cmdStepOver)); // alias
+ DCmd_Register("step_ret", WRAP_METHOD(Console, cmdStepRet));
+ DCmd_Register("pret", WRAP_METHOD(Console, cmdStepRet)); // alias
DCmd_Register("step_event", WRAP_METHOD(Console, cmdStepEvent));
DCmd_Register("se", WRAP_METHOD(Console, cmdStepEvent)); // alias
- DCmd_Register("step_ret", WRAP_METHOD(Console, cmdStepRet));
- DCmd_Register("sret", WRAP_METHOD(Console, cmdStepRet)); // alias
DCmd_Register("step_global", WRAP_METHOD(Console, cmdStepGlobal));
DCmd_Register("sg", WRAP_METHOD(Console, cmdStepGlobal)); // alias
DCmd_Register("step_callk", WRAP_METHOD(Console, cmdStepCallk));
@@ -171,8 +170,10 @@ Console::Console(SciEngine *engine) : GUI::Debugger() {
// Breakpoints
DCmd_Register("bp_list", WRAP_METHOD(Console, cmdBreakpointList));
DCmd_Register("bplist", WRAP_METHOD(Console, cmdBreakpointList)); // alias
+ DCmd_Register("bl", WRAP_METHOD(Console, cmdBreakpointList)); // alias
DCmd_Register("bp_del", WRAP_METHOD(Console, cmdBreakpointDelete));
DCmd_Register("bpdel", WRAP_METHOD(Console, cmdBreakpointDelete)); // alias
+ DCmd_Register("bc", WRAP_METHOD(Console, cmdBreakpointDelete)); // alias
DCmd_Register("bp_exec_method", WRAP_METHOD(Console, cmdBreakpointExecMethod));
DCmd_Register("bpx", WRAP_METHOD(Console, cmdBreakpointExecMethod)); // alias
DCmd_Register("bp_exec_function", WRAP_METHOD(Console, cmdBreakpointExecFunction));
@@ -180,48 +181,42 @@ Console::Console(SciEngine *engine) : GUI::Debugger() {
// VM
DCmd_Register("script_steps", WRAP_METHOD(Console, cmdScriptSteps));
DCmd_Register("vm_varlist", WRAP_METHOD(Console, cmdVMVarlist));
- DCmd_Register("vmvarlist", WRAP_METHOD(Console, cmdVMVarlist)); // alias
+ DCmd_Register("vmvarlist", WRAP_METHOD(Console, cmdVMVarlist)); // alias
+ DCmd_Register("vl", WRAP_METHOD(Console, cmdVMVarlist)); // alias
DCmd_Register("vm_vars", WRAP_METHOD(Console, cmdVMVars));
- DCmd_Register("vmvars", WRAP_METHOD(Console, cmdVMVars)); // alias
+ DCmd_Register("vmvars", WRAP_METHOD(Console, cmdVMVars)); // alias
+ DCmd_Register("vv", WRAP_METHOD(Console, cmdVMVars)); // alias
DCmd_Register("stack", WRAP_METHOD(Console, cmdStack));
DCmd_Register("value_type", WRAP_METHOD(Console, cmdValueType));
DCmd_Register("view_listnode", WRAP_METHOD(Console, cmdViewListNode));
DCmd_Register("view_reference", WRAP_METHOD(Console, cmdViewReference));
- DCmd_Register("vr", WRAP_METHOD(Console, cmdViewReference)); // alias
+ DCmd_Register("vr", WRAP_METHOD(Console, cmdViewReference)); // alias
DCmd_Register("view_object", WRAP_METHOD(Console, cmdViewObject));
- DCmd_Register("vo", WRAP_METHOD(Console, cmdViewObject)); // alias
+ DCmd_Register("vo", WRAP_METHOD(Console, cmdViewObject)); // alias
DCmd_Register("active_object", WRAP_METHOD(Console, cmdViewActiveObject));
DCmd_Register("acc_object", WRAP_METHOD(Console, cmdViewAccumulatorObject));
- g_debugState.seeking = kDebugSeekNothing;
- g_debugState.seekLevel = 0;
- g_debugState.runningStep = 0;
- g_debugState.stopOnEvent = false;
- g_debugState.debugging = false;
- g_debugState.breakpointWasHit = false;
- g_debugState._breakpoints.clear(); // No breakpoints defined
- g_debugState._activeBreakpointTypes = 0;
+ _debugState.seeking = kDebugSeekNothing;
+ _debugState.seekLevel = 0;
+ _debugState.runningStep = 0;
+ _debugState.stopOnEvent = false;
+ _debugState.debugging = false;
+ _debugState.breakpointWasHit = false;
+ _debugState._breakpoints.clear(); // No breakpoints defined
+ _debugState._activeBreakpointTypes = 0;
}
Console::~Console() {
}
void Console::preEnter() {
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- if (_engine->_gamestate)
- _engine->_gamestate->_sound.sfx_suspend(true);
-#endif
- if (_engine->_gamestate && _engine->_gamestate->_soundCmd)
- _engine->_gamestate->_soundCmd->pauseAll(true);
+ if (g_sci && g_sci->_soundCmd)
+ g_sci->_soundCmd->pauseAll(true);
}
void Console::postEnter() {
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- if (_engine->_gamestate)
- _engine->_gamestate->_sound.sfx_suspend(false);
-#endif
- if (_engine->_gamestate && _engine->_gamestate->_soundCmd)
- _engine->_gamestate->_soundCmd->pauseAll(false);
+ if (g_sci && g_sci->_soundCmd)
+ g_sci->_soundCmd->pauseAll(false);
if (!_videoFile.empty()) {
_engine->_gfxCursor->kernelHide();
@@ -243,8 +238,9 @@ void Console::postEnter() {
if (videoDecoder && videoDecoder->loadFile(_videoFile)) {
uint16 x = (g_system->getWidth() - videoDecoder->getWidth()) / 2;
uint16 y = (g_system->getHeight() - videoDecoder->getHeight()) / 2;
+ bool skipVideo = false;
- while (!g_engine->shouldQuit() && !videoDecoder->endOfVideo()) {
+ while (!g_engine->shouldQuit() && !videoDecoder->endOfVideo() && !skipVideo) {
if (videoDecoder->needsUpdate()) {
Graphics::Surface *frame = videoDecoder->decodeNextFrame();
if (frame) {
@@ -258,8 +254,10 @@ void Console::postEnter() {
}
Common::Event event;
- while (g_system->getEventManager()->pollEvent(event))
- ;
+ while (g_system->getEventManager()->pollEvent(event)) {
+ if ((event.type == Common::EVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_ESCAPE) || event.type == Common::EVENT_LBUTTONUP)
+ skipVideo = true;
+ }
g_system->delayMillis(10);
}
@@ -274,14 +272,6 @@ void Console::postEnter() {
}
}
-
-#if 0
-// Unused
-#define LOOKUP_SPECIES(species) (\
- (species >= 1000) ? species : *(s->_classTable[species].scriptposp) \
- + s->_classTable[species].class_offset)
-#endif
-
bool Console::cmdHelp(int argc, const char **argv) {
DebugPrintf("\n");
DebugPrintf("Variables\n");
@@ -326,6 +316,7 @@ bool Console::cmdHelp(int argc, const char **argv) {
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(" show_instruments - Shows the instruments of a specific song, or all songs\n");
DebugPrintf("\n");
DebugPrintf("Game:\n");
DebugPrintf(" save_game - Saves the current game state to the hard disk\n");
@@ -370,11 +361,11 @@ bool Console::cmdHelp(int argc, const char **argv) {
DebugPrintf(" addresses - Provides information on how to pass addresses\n");
DebugPrintf(" registers - Shows the current register values\n");
DebugPrintf(" dissect_script - Examines a script\n");
- DebugPrintf(" set_acc - Sets the accumulator\n");
DebugPrintf(" backtrace / bt - Dumps the send/self/super/call/calle/callb stack\n");
- DebugPrintf(" step / s - Executes one operation (no parameters) or several operations (specified as a parameter) \n");
+ DebugPrintf(" trace / t / s - Executes one operation (no parameters) or several operations (specified as a parameter) \n");
+ DebugPrintf(" stepover / p - Executes one operation, skips over call/send\n");
+ DebugPrintf(" step_ret / pret - Steps forward until ret is called on the current execution stack level.\n");
DebugPrintf(" step_event / se - Steps forward until a SCI event is received.\n");
- DebugPrintf(" step_ret / sret - Steps forward until ret is called on the current execution stack level.\n");
DebugPrintf(" step_global / sg - Steps until the global variable with the specified index is modified.\n");
DebugPrintf(" step_callk / snk - Steps forward until it hits the next callk operation, or a specific callk (specified as a parameter)\n");
DebugPrintf(" disasm - Disassembles a method by name\n");
@@ -383,15 +374,15 @@ bool Console::cmdHelp(int argc, const char **argv) {
DebugPrintf(" go - Executes the script\n");
DebugPrintf("\n");
DebugPrintf("Breakpoints:\n");
- DebugPrintf(" bp_list / bplist - Lists the current breakpoints\n");
- DebugPrintf(" bp_del / bpdel - Deletes a breakpoint with the specified index\n");
+ DebugPrintf(" bp_list / bplist / bl - Lists the current breakpoints\n");
+ DebugPrintf(" bp_del / bpdel / bc - Deletes a breakpoint with the specified index\n");
DebugPrintf(" bp_exec_method / bpx - Sets a breakpoint on the execution of the specified method\n");
DebugPrintf(" bp_exec_function / bpe - Sets a breakpoint on the execution of the specified exported function\n");
DebugPrintf("\n");
DebugPrintf("VM:\n");
DebugPrintf(" script_steps - Shows the number of executed SCI operations\n");
- DebugPrintf(" vm_varlist / vmvarlist - Shows the addresses of variables in the VM\n");
- DebugPrintf(" vm_vars / vmvars - Displays or changes variables in the VM\n");
+ DebugPrintf(" vm_varlist / vmvarlist / vl - Shows the addresses of variables in the VM\n");
+ DebugPrintf(" vm_vars / vmvars / vv - Displays or changes variables in the VM\n");
DebugPrintf(" stack - Lists the specified number of stack elements\n");
DebugPrintf(" value_type - Determines the type of a value\n");
DebugPrintf(" view_listnode - Examines the list node at the given address\n");
@@ -414,19 +405,12 @@ ResourceType parseResourceType(const char *resid) {
return res;
}
-const char *selector_name(EngineState *s, int selector) {
- if (selector >= 0 && selector < (int)g_sci->getKernel()->getSelectorNamesSize())
- return g_sci->getKernel()->getSelectorName(selector).c_str();
- else
- return "--INVALID--";
-}
-
bool Console::cmdGetVersion(int argc, const char **argv) {
const char *viewTypeDesc[] = { "Unknown", "EGA", "VGA", "VGA SCI1.1", "Amiga" };
bool hasVocab997 = g_sci->getResMan()->testResource(ResourceId(kResourceTypeVocab, VOCAB_RESOURCE_SELECTORS)) ? true : false;
- DebugPrintf("Game ID: %s\n", _engine->getGameID());
+ DebugPrintf("Game ID: %s\n", _engine->getGameIdStr());
DebugPrintf("Emulated interpreter version: %s\n", getSciVersionDesc(getSciVersion()));
DebugPrintf("\n");
DebugPrintf("Detected features:\n");
@@ -434,9 +418,10 @@ bool Console::cmdGetVersion(int argc, const char **argv) {
DebugPrintf("Sound type: %s\n", getSciVersionDesc(_engine->_features->detectDoSoundType()));
DebugPrintf("Graphics functions type: %s\n", getSciVersionDesc(_engine->_features->detectGfxFunctionsType()));
DebugPrintf("Lofs type: %s\n", getSciVersionDesc(_engine->_features->detectLofsType()));
- DebugPrintf("Move count type: %s\n", (_engine->_features->detectMoveCountType() == kIncrementMoveCount) ? "increment" : "ignore");
+ DebugPrintf("Move count type: %s\n", (_engine->_features->handleMoveCount()) ? "increment" : "ignore");
DebugPrintf("SetCursor type: %s\n", getSciVersionDesc(_engine->_features->detectSetCursorType()));
DebugPrintf("View type: %s\n", viewTypeDesc[g_sci->getResMan()->getViewType()]);
+ DebugPrintf("Uses palette merging: %s\n", g_sci->_gfxPalette->isMerging() ? "yes" : "no");
DebugPrintf("Resource volume version: %s\n", g_sci->getResMan()->getVolVersionDesc());
DebugPrintf("Resource map version: %s\n", g_sci->getResMan()->getMapVersionDesc());
DebugPrintf("Contains selector vocabulary (vocab.997): %s\n", hasVocab997 ? "yes" : "no");
@@ -482,14 +467,14 @@ bool Console::cmdSelector(int argc, const char **argv) {
return true;
}
- for (uint seeker = 0; seeker < _engine->getKernel()->getSelectorNamesSize(); seeker++) {
- if (!scumm_stricmp(_engine->getKernel()->getSelectorName(seeker).c_str(), argv[1])) {
- DebugPrintf("Selector %s found at %03x (%d)\n", _engine->getKernel()->getSelectorName(seeker).c_str(), seeker, seeker);
- return true;
- }
+ Common::String name = argv[1];
+ int seeker = _engine->getKernel()->findSelector(name.c_str());
+ if (seeker >= 0) {
+ DebugPrintf("Selector %s found at %03x (%d)\n", name.c_str(), seeker, seeker);
+ return true;
}
- DebugPrintf("Selector %s wasn't found\n", argv[1]);
+ DebugPrintf("Selector %s wasn't found\n", name.c_str());
return true;
}
@@ -597,7 +582,7 @@ 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(s->r_acc), PRINT_REG(s->r_prev), s->restAdjustCur);
+ DebugPrintf("acc=%04x:%04x prev=%04x:%04x &rest=%x\n", PRINT_REG(s->r_acc), PRINT_REG(s->r_prev), s->restAdjust);
if (!s->_executionStack.empty()) {
DebugPrintf("pc=%04x:%04x obj=%04x:%04x fp=ST:%04x sp=ST:%04x\n",
@@ -820,7 +805,7 @@ bool Console::cmdVerifyScripts(int argc, const char **argv) {
}
Common::List<ResourceId> *resources = _engine->getResMan()->listResources(kResourceTypeScript);
- sort(resources->begin(), resources->end(), ResourceIdLess());
+ Common::sort(resources->begin(), resources->end());
Common::List<ResourceId>::iterator itr = resources->begin();
DebugPrintf("%d SCI1.1-SCI2.1 scripts found, performing sanity checks...\n", resources->size());
@@ -829,15 +814,15 @@ bool Console::cmdVerifyScripts(int argc, const char **argv) {
while (itr != resources->end()) {
script = _engine->getResMan()->findResource(*itr, false);
if (!script)
- DebugPrintf("Error: script %d couldn't be loaded\n", itr->number);
+ DebugPrintf("Error: script %d couldn't be loaded\n", itr->getNumber());
- heap = _engine->getResMan()->findResource(*itr, false);
+ heap = _engine->getResMan()->findResource(ResourceId(kResourceTypeHeap, itr->getNumber()), false);
if (!heap)
- DebugPrintf("Error: script %d doesn't have a corresponding heap\n", itr->number);
+ DebugPrintf("Error: script %d doesn't have a corresponding heap\n", itr->getNumber());
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->getNumber(), script->size + heap->size);
++itr;
}
@@ -847,6 +832,167 @@ bool Console::cmdVerifyScripts(int argc, const char **argv) {
return true;
}
+bool Console::cmdShowInstruments(int argc, const char **argv) {
+ int songNumber = -1;
+
+ if (argc == 2)
+ songNumber = atoi(argv[1]);
+
+ SciVersion doSoundVersion = _engine->_features->detectDoSoundType();
+ MidiPlayer *player = MidiPlayer_Midi_create(doSoundVersion);
+ MidiParser_SCI *parser = new MidiParser_SCI(doSoundVersion, 0);
+ parser->setMidiDriver(player);
+
+ Common::List<ResourceId> *resources = _engine->getResMan()->listResources(kResourceTypeSound);
+ Common::sort(resources->begin(), resources->end());
+ Common::List<ResourceId>::iterator itr = resources->begin();
+ int instruments[128];
+ bool instrumentsSongs[128][1000];
+
+ for (int i = 0; i < 128; i++)
+ instruments[i] = 0;
+
+ for (int i = 0; i < 128; i++)
+ for (int j = 0; j < 1000; j++)
+ instrumentsSongs[i][j] = false;
+
+ if (songNumber == -1) {
+ DebugPrintf("%d sounds found, checking their instrument mappings...\n", resources->size());
+ DebugPrintf("Instruments:\n");
+ DebugPrintf("============\n");
+ }
+
+ SoundResource *sound;
+
+ while (itr != resources->end()) {
+ if (songNumber >= 0 && itr->getNumber() != songNumber) {
+ ++itr;
+ continue;
+ }
+
+ sound = new SoundResource(itr->getNumber(), _engine->getResMan(), doSoundVersion);
+ int channelFilterMask = sound->getChannelFilterMask(player->getPlayId(), player->hasRhythmChannel());
+ SoundResource::Track *track = sound->getTrackByType(player->getPlayId());
+ if (track->digitalChannelNr != -1) {
+ // Skip digitized sound effects
+ delete sound;
+ ++itr;
+ continue;
+ }
+
+ parser->loadMusic(track, NULL, channelFilterMask, doSoundVersion);
+ const byte *channelData = parser->getMixedData();
+
+ byte curEvent = 0, prevEvent = 0, command = 0;
+ bool endOfTrack = false;
+ bool firstOneShown = false;
+
+ DebugPrintf("Song %d: ", itr->getNumber());
+
+ do {
+ while (*channelData == 0xF8)
+ channelData++;
+
+ channelData++; // delta
+
+ if ((*channelData & 0xF0) >= 0x80)
+ curEvent = *(channelData++);
+ else
+ curEvent = prevEvent;
+ if (curEvent < 0x80)
+ continue;
+
+ prevEvent = curEvent;
+ command = curEvent >> 4;
+
+ byte channel;
+
+ switch (command) {
+ case 0xC: // program change
+ channel = curEvent & 0x0F;
+ if (channel != 15) { // SCI special
+ byte instrument = *channelData++;
+ if (!firstOneShown)
+ firstOneShown = true;
+ else
+ DebugPrintf(",");
+
+ DebugPrintf(" %d", instrument);
+ instruments[instrument]++;
+ instrumentsSongs[instrument][itr->getNumber()] = true;
+ }
+ break;
+ case 0xD:
+ channelData++; // param1
+ break;
+ case 0xB:
+ channelData++; // param1
+ channelData++; // param2
+ break;
+ case 0x8:
+ case 0x9:
+ case 0xA:
+ case 0xE:
+ channelData++; // param1
+ channelData++; // param2
+ break;
+ case 0xF:
+ if ((curEvent & 0x0F) == 0x2) {
+ channelData++; // param1
+ channelData++; // param2
+ } else if ((curEvent & 0x0F) == 0x3) {
+ channelData++; // param1
+ } else if ((curEvent & 0x0F) == 0xF) { // META
+ byte type = *channelData++;
+ if (type == 0x2F) {// end of track reached
+ endOfTrack = true;
+ } else {
+ // no further processing necessary
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ } while (!endOfTrack);
+
+ DebugPrintf("\n");
+
+ delete sound;
+ ++itr;
+ }
+
+ delete parser;
+ delete player;
+
+ DebugPrintf("\n");
+
+ if (songNumber == -1) {
+ DebugPrintf("Used instruments: ");
+ for (int i = 0; i < 128; i++) {
+ if (instruments[i] > 0)
+ DebugPrintf("%d, ", i);
+ }
+ DebugPrintf("\n\n");
+
+ DebugPrintf("Used instruments in songs:\n");
+ for (int i = 0; i < 128; i++) {
+ if (instruments[i] > 0) {
+ DebugPrintf("Instrument %d: ", i);
+ for (int j = 0; j < 1000; j++) {
+ if (instrumentsSongs[i][j])
+ DebugPrintf("%d, ", j);
+ }
+ DebugPrintf("\n");
+ }
+ }
+
+ DebugPrintf("\n\n");
+ }
+
+ return true;
+}
+
bool Console::cmdList(int argc, const char **argv) {
if (argc < 2) {
DebugPrintf("Lists all the resources of a given type\n");
@@ -870,19 +1016,19 @@ bool Console::cmdList(int argc, const char **argv) {
}
Common::List<ResourceId> *resources = _engine->getResMan()->listResources(res, number);
- sort(resources->begin(), resources->end(), ResourceIdLess());
+ Common::sort(resources->begin(), resources->end());
Common::List<ResourceId>::iterator itr = resources->begin();
int cnt = 0;
while (itr != resources->end()) {
if (number == -1) {
- DebugPrintf("%8i", itr->number);
+ DebugPrintf("%8i", itr->getNumber());
if (++cnt % 10 == 0)
DebugPrintf("\n");
- }
- else if (number == (int)itr->number) {
- DebugPrintf("(%3i, %3i, %3i, %3i) ", (itr->tuple >> 24) & 0xff, (itr->tuple >> 16) & 0xff,
- (itr->tuple >> 8) & 0xff, itr->tuple & 0xff);
+ } else if (number == (int)itr->getNumber()) {
+ const uint32 tuple = itr->getTuple();
+ DebugPrintf("(%3i, %3i, %3i, %3i) ", (tuple >> 24) & 0xff, (tuple >> 16) & 0xff,
+ (tuple >> 8) & 0xff, tuple & 0xff);
if (++cnt % 4 == 0)
DebugPrintf("\n");
}
@@ -920,7 +1066,7 @@ bool Console::cmdSaveGame(int argc, const char **argv) {
}
// TODO: enable custom descriptions? force filename into a specific format?
- if (gamestate_save(_engine->_gamestate, out, "debugging", version)) {
+ if (!gamestate_save(_engine->_gamestate, out, "debugging", version)) {
DebugPrintf("Saving the game state to '%s' failed\n", argv[1]);
} else {
out->finalize();
@@ -957,8 +1103,7 @@ 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;
- _engine->_gamestate->script_abort_flag = 1;
+ _engine->_gamestate->abortScriptProcessing = kAbortRestartGame;;
return false;
}
@@ -1135,7 +1280,11 @@ bool Console::cmdDrawRobot(int argc, const char **argv) {
uint16 resourceId = atoi(argv[1]);
- _engine->_gui32->drawRobot(resourceId);
+ if (_engine->_gfxPaint32) {
+ _engine->_gfxPaint32->debugDrawRobot(resourceId);
+ } else {
+ DebugPrintf("command not available in non-sci32 games");
+ }
return true;
}
#endif
@@ -1217,7 +1366,7 @@ bool Console::cmdPrintSegmentTable(int argc, const char **argv) {
switch (mobj->getType()) {
case SEG_TYPE_SCRIPT:
- DebugPrintf("S script.%03d l:%d ", (*(Script *)mobj)._nr, (*(Script *)mobj).getLockers());
+ DebugPrintf("S script.%03d l:%d ", (*(Script *)mobj).getScriptNumber(), (*(Script *)mobj).getLockers());
break;
case SEG_TYPE_CLONES:
@@ -1252,10 +1401,16 @@ bool Console::cmdPrintSegmentTable(int argc, const char **argv) {
DebugPrintf("M dynmem: %d bytes", (*(DynMem *)mobj)._size);
break;
- case SEG_TYPE_STRING_FRAG:
- DebugPrintf("F string fragments");
+#ifdef ENABLE_SCI32
+ case SEG_TYPE_ARRAY:
+ DebugPrintf("A SCI32 arrays (%d)", (*(ArrayTable *)mobj).entries_used);
break;
+ case SEG_TYPE_STRING:
+ DebugPrintf("T SCI32 strings (%d)", (*(StringTable *)mobj).entries_used);
+ break;
+#endif
+
default:
DebugPrintf("I Invalid (type = %x)", mobj->getType());
break;
@@ -1281,9 +1436,9 @@ 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->getBufSize(), (uint)scr->getBufSize());
+ DebugPrintf("script.%03d locked by %d, bufsize=%d (%x)\n", scr->getScriptNumber(), 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)));
+ DebugPrintf(" Exports: %4d at %d\n", scr->getExportsNr(), (int)(((const byte *)scr->getExportTable()) - ((const byte *)scr->getBuf())));
else
DebugPrintf(" Exports: none\n");
@@ -1394,10 +1549,14 @@ bool Console::segmentInfo(int nr) {
}
break;
- case SEG_TYPE_STRING_FRAG: {
- DebugPrintf("string frags\n");
+#ifdef ENABLE_SCI32
+ case SEG_TYPE_STRING:
+ DebugPrintf("SCI32 strings\n");
break;
- }
+ case SEG_TYPE_ARRAY:
+ DebugPrintf("SCI32 arrays\n");
+ break;
+#endif
default :
DebugPrintf("Invalid type %d\n", mobj->getType());
@@ -1413,7 +1572,7 @@ bool Console::cmdSegmentInfo(int argc, const char **argv) {
DebugPrintf("Provides information on the specified segment(s)\n");
DebugPrintf("Usage: %s <segment number>\n", argv[0]);
DebugPrintf("<segment number> can be a number, which shows the information of the segment with\n");
- DebugPrintf("the specified number, or \"all\" to show information on all active segments");
+ DebugPrintf("the specified number, or \"all\" to show information on all active segments\n");
return true;
}
@@ -1421,9 +1580,11 @@ bool Console::cmdSegmentInfo(int argc, const char **argv) {
for (uint i = 0; i < _engine->_gamestate->_segMan->_heap.size(); i++)
segmentInfo(i);
} else {
- int nr = atoi(argv[1]);
- if (!segmentInfo(nr))
- DebugPrintf("Segment %04x does not exist\n", nr);
+ int segmentNr;
+ if (!parseInteger(argv[1], segmentNr))
+ return true;
+ if (!segmentInfo(segmentNr))
+ DebugPrintf("Segment %04xh does not exist\n", segmentNr);
}
return true;
@@ -1436,21 +1597,23 @@ bool Console::cmdKillSegment(int argc, const char **argv) {
DebugPrintf("Usage: %s <segment number>\n", argv[0]);
return true;
}
-
- _engine->_gamestate->_segMan->getScript(atoi(argv[1]))->setLockers(0);
+ int segmentNumber;
+ if (!parseInteger(argv[1], segmentNumber))
+ return true;
+ _engine->_gamestate->_segMan->getScript(segmentNumber)->setLockers(0);
return true;
}
bool Console::cmdShowMap(int argc, const char **argv) {
if (argc != 2) {
- DebugPrintf("Shows one of the screen maps\n");
+ DebugPrintf("Switches to one of the following screen maps\n");
DebugPrintf("Usage: %s <screen map>\n", argv[0]);
DebugPrintf("Screen maps:\n");
- DebugPrintf("- 0: visual map (back buffer)\n");
- DebugPrintf("- 1: priority map (back buffer)\n");
- DebugPrintf("- 2: control map (static buffer)\n");
- DebugPrintf("- 3: display screen (newgui only)\n");
+ DebugPrintf("- 0: visual map\n");
+ DebugPrintf("- 1: priority map\n");
+ DebugPrintf("- 2: control map\n");
+ DebugPrintf("- 3: display screen\n");
return true;
}
@@ -1473,23 +1636,7 @@ bool Console::cmdShowMap(int argc, const char **argv) {
bool Console::cmdSongLib(int argc, const char **argv) {
DebugPrintf("Song library:\n");
-
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- Song *seeker = _engine->_gamestate->_sound._songlib._lib;
-
- do {
- DebugPrintf(" %p", (void *)seeker);
-
- if (seeker) {
- DebugPrintf("[%04lx,p=%d,s=%d]->", seeker->_handle, seeker->_priority, seeker->_status);
- seeker = seeker->_next;
- }
- DebugPrintf("\n");
- } while (seeker);
- DebugPrintf("\n");
-#else
- _engine->_gamestate->_soundCmd->printPlayList(this);
-#endif
+ g_sci->_soundCmd->printPlayList(this);
return true;
}
@@ -1509,7 +1656,7 @@ bool Console::cmdSongInfo(int argc, const char **argv) {
return true;
}
- _engine->_gamestate->_soundCmd->printSongInfo(addr, this);
+ g_sci->_soundCmd->printSongInfo(addr, this);
return true;
}
@@ -1528,7 +1675,7 @@ bool Console::cmdStartSound(int argc, const char **argv) {
return true;
}
- _engine->_gamestate->_soundCmd->startNewSound(number);
+ g_sci->_soundCmd->startNewSound(number);
return false;
}
@@ -1552,37 +1699,21 @@ bool Console::cmdToggleSound(int argc, const char **argv) {
return true;
}
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- int handle = id.segment << 16 | id.offset; // frobnicate handle
-
- if (id.segment) {
- 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);
- writeSelectorValue(segMan, id, SELECTOR(signal), SIGNAL_OFFSET);
- writeSelectorValue(segMan, id, SELECTOR(nodePtr), 0);
- writeSelectorValue(segMan, id, SELECTOR(handle), 0);
- }
-#else
-
Common::String newState = argv[2];
newState.toLowercase();
if (newState == "play")
- _engine->_gamestate->_soundCmd->playSound(id);
+ g_sci->_soundCmd->processPlaySound(id);
else if (newState == "stop")
- _engine->_gamestate->_soundCmd->stopSound(id);
+ g_sci->_soundCmd->processStopSound(id, false);
else
DebugPrintf("New state can either be 'play' or 'stop'");
-#endif
return true;
}
bool Console::cmdStopAllSounds(int argc, const char **argv) {
-#ifndef USE_OLD_MUSIC_FUNCTIONS
- _engine->_gamestate->_soundCmd->stopAllSounds();
-#endif
+ g_sci->_soundCmd->stopAllSounds();
DebugPrintf("All sounds have been stopped\n");
return true;
@@ -1596,36 +1727,6 @@ bool Console::cmdIsSample(int argc, const char **argv) {
return true;
}
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- Resource *song = _engine->getResMan()->findResource(ResourceId(kResourceTypeSound, atoi(argv[1])), 0);
- SongIterator *songit;
- Audio::AudioStream *data;
-
- if (!song) {
- DebugPrintf("Not a sound resource!\n");
- return true;
- }
-
- songit = songit_new(song->data, song->size, SCI_SONG_ITERATOR_TYPE_SCI0, 0xcaffe /* What do I care about the ID? */);
-
- if (!songit) {
- DebugPrintf("Could not convert to song iterator!\n");
- return true;
- }
-
- data = songit->getAudioStream();
- if (data) {
- // TODO
-/*
- DebugPrintf("\nIs sample (encoding %dHz/%s/%04x)", data->conf.rate, (data->conf.stereo) ?
- ((data->conf.stereo == SFX_PCM_STEREO_LR) ? "stereo-LR" : "stereo-RL") : "mono", data->conf.format);
-*/
- delete data;
- } else
- DebugPrintf("Valid song, but not a sample.\n");
-
- delete songit;
-#else
int16 number = atoi(argv[1]);
if (!_engine->getResMan()->testResource(ResourceId(kResourceTypeSound, number))) {
@@ -1649,7 +1750,6 @@ bool Console::cmdIsSample(int argc, const char **argv) {
DebugPrintf("Sample size: %d, sample rate: %d, channels: %d, digital channel number: %d\n",
track->digitalSampleSize, track->digitalSampleRate, track->channelCount, track->digitalChannelNr);
-#endif
return true;
}
@@ -1661,10 +1761,10 @@ bool Console::cmdGCInvoke(int argc, const char **argv) {
}
bool Console::cmdGCObjects(int argc, const char **argv) {
- reg_t_hash_map *use_map = find_all_used_references(_engine->_gamestate);
+ AddrSet *use_map = findAllActiveReferences(_engine->_gamestate);
DebugPrintf("Reachable object references (normalised):\n");
- for (reg_t_hash_map::iterator i = use_map->begin(); i != use_map->end(); ++i) {
+ for (AddrSet::iterator i = use_map->begin(); i != use_map->end(); ++i) {
DebugPrintf(" - %04x:%04x\n", PRINT_REG(i->_key));
}
@@ -1673,11 +1773,6 @@ bool Console::cmdGCObjects(int argc, const char **argv) {
return true;
}
-void _print_address(void * _, reg_t addr) {
- if (addr.segment)
- g_sci->getSciDebugger()->DebugPrintf(" %04x:%04x\n", PRINT_REG(addr));
-}
-
bool Console::cmdGCShowReachable(int argc, const char **argv) {
if (argc != 2) {
DebugPrintf("Prints all addresses directly reachable from the memory object specified as parameter.\n");
@@ -1701,7 +1796,10 @@ bool Console::cmdGCShowReachable(int argc, const char **argv) {
}
DebugPrintf("Reachable from %04x:%04x:\n", PRINT_REG(addr));
- mobj->listAllOutgoingReferences(addr, NULL, _print_address);
+ const Common::Array<reg_t> tmp = mobj->listAllOutgoingReferences(addr);
+ for (Common::Array<reg_t>::const_iterator it = tmp.begin(); it != tmp.end(); ++it)
+ if (it->segment)
+ g_sci->getSciDebugger()->DebugPrintf(" %04x:%04x\n", PRINT_REG(*it));
return true;
}
@@ -1730,7 +1828,10 @@ bool Console::cmdGCShowFreeable(int argc, const char **argv) {
}
DebugPrintf("Freeable in segment %04x:\n", addr.segment);
- mobj->listAllDeallocatable(addr.segment, NULL, _print_address);
+ const Common::Array<reg_t> tmp = mobj->listAllDeallocatable(addr.segment);
+ for (Common::Array<reg_t>::const_iterator it = tmp.begin(); it != tmp.end(); ++it)
+ if (it->segment)
+ g_sci->getSciDebugger()->DebugPrintf(" %04x:%04x\n", PRINT_REG(*it));
return true;
}
@@ -1772,9 +1873,9 @@ bool Console::cmdVMVarlist(int argc, const char **argv) {
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(s->variables_seg[i], s->variables[i] - s->variables_base[i])));
- if (s->variables_max)
- DebugPrintf(" total %d", s->variables_max[i]);
+ DebugPrintf("%s vars at %04x:%04x ", varnames[i], PRINT_REG(make_reg(s->variablesSegment[i], s->variables[i] - s->variablesBase[i])));
+ if (s->variablesMax)
+ DebugPrintf(" total %d", s->variablesMax[i]);
DebugPrintf("\n");
}
@@ -1782,76 +1883,95 @@ bool Console::cmdVMVarlist(int argc, const char **argv) {
}
bool Console::cmdVMVars(int argc, const char **argv) {
- if (argc < 3) {
+ if (argc < 2) {
DebugPrintf("Displays or changes variables in the VM\n");
DebugPrintf("Usage: %s <type> <varnum> [<value>]\n", argv[0]);
- DebugPrintf("First parameter is either g(lobal), l(ocal), t(emp) or p(aram).\n");
- DebugPrintf("Second parameter is the var number\n");
+ DebugPrintf("First parameter is either g(lobal), l(ocal), t(emp), p(aram) or a(cc).\n");
+ DebugPrintf("Second parameter is the var number (not specified on acc)\n");
DebugPrintf("Third parameter (if specified) is the value to set the variable to, in address form\n");
DebugPrintf("Check the \"addresses\" command on how to use addresses\n");
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]);
- int vartype;
- int idx;
-
- if (!vartype_pre) {
+ const char *varNames[] = {"global", "local", "temp", "param", "acc"};
+ const char *varAbbrev = "gltpa";
+ const char *varType_pre = strchr(varAbbrev, *argv[1]);
+ int varType;
+ int varIndex = 0;
+ reg_t *curValue = NULL;
+ const char *setValue = NULL;
+
+ if (!varType_pre) {
DebugPrintf("Invalid variable type '%c'\n", *argv[1]);
return true;
}
- vartype = vartype_pre - varabbrev;
+ varType = varType_pre - varAbbrev;
- char *endPtr = 0;
- int idxLen = strlen(argv[2]);
- const char *lastChar = argv[2] + idxLen - (idxLen == 0 ? 0 : 1);
+ switch (varType) {
+ case 0:
+ case 1:
+ case 2:
+ case 3: {
+ // for global, local, temp and param, we need an index
+ if (argc < 3) {
+ DebugPrintf("Variable number must be specified for requested type\n");
+ return true;
+ }
+ if (argc > 4) {
+ DebugPrintf("Too many arguments\n");
+ return true;
+ }
- if ((strncmp(argv[2], "0x", 2) == 0) || (*lastChar == 'h')) {
- // hexadecimal number
- idx = strtol(argv[2], &endPtr, 16);
- if ((*endPtr != 0) && (*endPtr != 'h')) {
- DebugPrintf("Invalid hexadecimal index '%s'\n", argv[2]);
+ if (!parseInteger(argv[2], varIndex))
+ return true;
+
+ if (varIndex < 0) {
+ DebugPrintf("Variable number may not be negative\n");
return true;
}
- } else {
- // decimal number
- idx = strtol(argv[2], &endPtr, 10);
- if (*endPtr != 0) {
- DebugPrintf("Invalid decimal index '%s'\n", argv[2]);
+
+ if ((s->variablesMax) && (s->variablesMax[varType] <= varIndex)) {
+ DebugPrintf("Maximum variable number for this type is %d (0x%x)\n", s->variablesMax[varType], s->variablesMax[varType]);
return true;
}
+ curValue = &s->variables[varType][varIndex];
+ if (argc == 4)
+ setValue = argv[3];
+ break;
}
- if (idx < 0) {
- DebugPrintf("Invalid: negative index\n");
- return true;
- }
+ case 4:
+ // acc
+ if (argc > 3) {
+ DebugPrintf("Too many arguments\n");
+ return true;
+ }
+ curValue = &s->r_acc;
+ if (argc == 3)
+ setValue = argv[2];
+ break;
- 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;
+ default:
+ break;
}
- switch (argc) {
- case 3:
- 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], &s->variables[vartype][idx], true)) {
+ if (!setValue) {
+ if (varType == 4)
+ DebugPrintf("%s == %04x:%04x", varNames[varType], PRINT_REG(*curValue));
+ else
+ DebugPrintf("%s var %d == %04x:%04x", varNames[varType], varIndex, PRINT_REG(*curValue));
+ printBasicVarInfo(*curValue);
+ DebugPrintf("\n");
+ } else {
+ if (parse_reg_t(s, setValue, curValue, 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");
return true;
}
- break;
- default:
- DebugPrintf("Too many arguments\n");
}
-
return true;
}
@@ -1901,20 +2021,22 @@ bool Console::cmdValueType(int argc, const char **argv) {
int t = g_sci->getKernel()->findRegType(val);
switch (t) {
- case KSIG_LIST:
+ case SIG_TYPE_LIST:
DebugPrintf("List");
break;
- case KSIG_OBJECT:
+ case SIG_TYPE_OBJECT:
DebugPrintf("Object");
break;
- case KSIG_REF:
+ case SIG_TYPE_REFERENCE:
DebugPrintf("Reference");
break;
- case KSIG_ARITHMETIC:
- DebugPrintf("Arithmetic");
+ case SIG_TYPE_INTEGER:
+ DebugPrintf("Integer");
+ case SIG_TYPE_INTEGER | SIG_TYPE_NULL:
+ DebugPrintf("Null");
break;
default:
- DebugPrintf("Erroneous unknown type %02x(%d decimal)\n", t, t);
+ DebugPrintf("Erroneous unknown type 0x%02x (%d decimal)\n", t, t);
}
return true;
@@ -1978,7 +2100,7 @@ bool Console::cmdViewReference(int argc, const char **argv) {
return true;
}
- if (reg_end.segment != reg.segment) {
+ if (reg_end.segment != reg.segment && reg_end != NULL_REG) {
DebugPrintf("Ending segment different from starting segment. Assuming no bound on dump.\n");
reg_end = NULL_REG;
}
@@ -1994,47 +2116,68 @@ bool Console::cmdViewReference(int argc, const char **argv) {
switch (type) {
case 0:
break;
- case KSIG_LIST: {
- List *l = _engine->_gamestate->_segMan->lookupList(reg);
+ case SIG_TYPE_LIST: {
+ List *list = _engine->_gamestate->_segMan->lookupList(reg);
DebugPrintf("list\n");
- if (l)
- printList(l);
+ if (list)
+ printList(list);
else
DebugPrintf("Invalid list.\n");
}
break;
- case KSIG_NODE:
+ case SIG_TYPE_NODE:
DebugPrintf("list node\n");
printNode(reg);
break;
- case KSIG_OBJECT:
+ case SIG_TYPE_OBJECT:
DebugPrintf("object\n");
printObject(reg);
break;
- case KSIG_REF: {
- int size;
- const SegmentRef block = _engine->_gamestate->_segMan->dereference(reg);
- size = block.maxSize;
+ case SIG_TYPE_REFERENCE: {
+ switch (_engine->_gamestate->_segMan->getSegmentType(reg.segment)) {
+#ifdef ENABLE_SCI32
+ case SEG_TYPE_STRING: {
+ DebugPrintf("SCI32 string\n");
+ const SciString *str = _engine->_gamestate->_segMan->lookupString(reg);
+ Common::hexdump((const byte *) str->getRawData(), str->getSize(), 16, 0);
+ break;
+ }
+ case SEG_TYPE_ARRAY: {
+ DebugPrintf("SCI32 array:\n");
+ const SciArray<reg_t> *array = _engine->_gamestate->_segMan->lookupArray(reg);
+ hexDumpReg(array->getRawData(), array->getSize(), 4, 0, true);
+ break;
+ }
+#endif
+ default: {
+ int size;
+ const SegmentRef block = _engine->_gamestate->_segMan->dereference(reg);
+ size = block.maxSize;
- DebugPrintf("raw data\n");
+ DebugPrintf("raw data\n");
- if (reg_end.segment != 0 && size < reg_end.offset - reg.offset) {
- DebugPrintf("Block end out of bounds (size %d). Resetting.\n", size);
- reg_end = NULL_REG;
- }
+ if (reg_end.segment != 0 && size < reg_end.offset - reg.offset) {
+ DebugPrintf("Block end out of bounds (size %d). Resetting.\n", size);
+ reg_end = NULL_REG;
+ }
- if (reg_end.segment != 0 && (size >= reg_end.offset - reg.offset))
- size = reg_end.offset - reg.offset;
+ if (reg_end.segment != 0 && (size >= reg_end.offset - reg.offset))
+ size = reg_end.offset - reg.offset;
- if (reg_end.segment != 0)
- DebugPrintf("Block size less than or equal to %d\n", size);
+ if (reg_end.segment != 0)
+ DebugPrintf("Block size less than or equal to %d\n", size);
- Common::hexdump(block.raw, size, 16, 0);
+ if (block.isRaw)
+ Common::hexdump(block.raw, size, 16, 0);
+ else
+ hexDumpReg(block.reg, size / 2, 4, 0);
+ }
}
break;
- case KSIG_ARITHMETIC:
+ }
+ case SIG_TYPE_INTEGER:
DebugPrintf("arithmetic value\n %d (%04x)\n", (int16) reg.offset, reg.offset);
break;
default:
@@ -2087,35 +2230,12 @@ bool Console::cmdViewAccumulatorObject(int argc, const char **argv) {
}
bool Console::cmdScriptSteps(int argc, const char **argv) {
- DebugPrintf("Number of executed SCI operations: %d\n", _engine->_gamestate->script_step_counter);
- return true;
-}
-
-bool Console::cmdSetAccumulator(int argc, const char **argv) {
- if (argc != 2) {
- DebugPrintf("Sets the accumulator.\n");
- DebugPrintf("Usage: %s <address>\n", argv[0]);
- DebugPrintf("Check the \"addresses\" command on how to use addresses\n");
- return true;
- }
-
- reg_t val;
-
- if (parse_reg_t(_engine->_gamestate, argv[1], &val, false)) {
- DebugPrintf("Invalid address passed.\n");
- DebugPrintf("Check the \"addresses\" command on how to use addresses\n");
- return true;
- }
-
- _engine->_gamestate->r_acc = val;
-
+ DebugPrintf("Number of executed SCI operations: %d\n", _engine->_gamestate->scriptStepCounter);
return true;
}
bool Console::cmdBacktrace(int argc, const char **argv) {
- DebugPrintf("Dumping the send/self/super/call/calle/callb stack:\n");
-
- DebugPrintf("Call stack (current base: 0x%x):\n", _engine->_gamestate->execution_stack_base);
+ DebugPrintf("Call stack (current base: 0x%x):\n", _engine->_gamestate->executionStackBase);
Common::List<ExecStack>::iterator iter;
uint i = 0;
@@ -2126,20 +2246,25 @@ bool Console::cmdBacktrace(int argc, const char **argv) {
int paramc, totalparamc;
switch (call.type) {
-
- case EXEC_STACK_TYPE_CALL: {// Normal function
- DebugPrintf(" %x:[%x] %s::%s(", i, call.origin, objname, (call.selector == -1) ? "<call[be]?>" :
- selector_name(_engine->_gamestate, call.selector));
- }
- break;
+ case EXEC_STACK_TYPE_CALL: // Normal function
+ if (call.type == EXEC_STACK_TYPE_CALL)
+ DebugPrintf(" %x: script %d - ", i, (*(Script *)_engine->_gamestate->_segMan->_heap[call.addr.pc.segment]).getScriptNumber());
+ if (call.debugSelector != -1) {
+ DebugPrintf("%s::%s(", objname, _engine->getKernel()->getSelectorName(call.debugSelector).c_str());
+ } else if (call.debugExportId != -1) {
+ DebugPrintf("export %d (", call.debugExportId);
+ } else if (call.debugLocalCallOffset != -1) {
+ DebugPrintf("call %x (", call.debugLocalCallOffset);
+ }
+ break;
case EXEC_STACK_TYPE_KERNEL: // Kernel function
- DebugPrintf(" %x:[%x] k%s(", i, call.origin, _engine->getKernel()->getKernelName(call.selector).c_str());
+ DebugPrintf(" %x:[%x] k%s(", i, call.debugOrigin, _engine->getKernel()->getKernelName(call.debugSelector).c_str());
break;
case EXEC_STACK_TYPE_VARSELECTOR:
- DebugPrintf(" %x:[%x] vs%s %s::%s (", i, call.origin, (call.argc) ? "write" : "read",
- objname, _engine->getKernel()->getSelectorName(call.selector).c_str());
+ DebugPrintf(" %x:[%x] vs%s %s::%s (", i, call.debugOrigin, (call.argc) ? "write" : "read",
+ objname, _engine->getKernel()->getSelectorName(call.debugSelector).c_str());
break;
}
@@ -2158,7 +2283,10 @@ bool Console::cmdBacktrace(int argc, const char **argv) {
if (call.argc > 16)
DebugPrintf("...");
- DebugPrintf(")\n obj@%04x:%04x", PRINT_REG(call.objp));
+ DebugPrintf(")\n ");
+ if (call.debugOrigin != -1)
+ DebugPrintf("by %x ", call.debugOrigin);
+ DebugPrintf("obj@%04x:%04x", PRINT_REG(call.objp));
if (call.type == EXEC_STACK_TYPE_CALL) {
DebugPrintf(" pc=%04x:%04x", PRINT_REG(call.addr.pc));
if (call.sp == CALL_SP_CARRY)
@@ -2171,33 +2299,39 @@ bool Console::cmdBacktrace(int argc, const char **argv) {
DebugPrintf(" pc:none");
DebugPrintf(" argp:ST:%04x", (unsigned)(call.variables_argp - _engine->_gamestate->stack_base));
- if (call.type == EXEC_STACK_TYPE_CALL)
- DebugPrintf(" script: %d", (*(Script *)_engine->_gamestate->_segMan->_heap[call.addr.pc.segment])._nr);
DebugPrintf("\n");
}
return true;
}
-bool Console::cmdStep(int argc, const char **argv) {
+bool Console::cmdTrace(int argc, const char **argv) {
if (argc == 2 && atoi(argv[1]) > 0)
- g_debugState.runningStep = atoi(argv[1]) - 1;
- g_debugState.debugging = true;
+ _debugState.runningStep = atoi(argv[1]) - 1;
+ _debugState.debugging = true;
+
+ return false;
+}
+
+bool Console::cmdStepOver(int argc, const char **argv) {
+ _debugState.seeking = kDebugSeekStepOver;
+ _debugState.seekLevel = _engine->_gamestate->_executionStack.size();
+ _debugState.debugging = true;
return false;
}
bool Console::cmdStepEvent(int argc, const char **argv) {
- g_debugState.stopOnEvent = true;
- g_debugState.debugging = true;
+ _debugState.stopOnEvent = true;
+ _debugState.debugging = true;
return false;
}
bool Console::cmdStepRet(int argc, const char **argv) {
- g_debugState.seeking = kDebugSeekLevelRet;
- g_debugState.seekLevel = _engine->_gamestate->_executionStack.size() - 1;
- g_debugState.debugging = true;
+ _debugState.seeking = kDebugSeekLevelRet;
+ _debugState.seekLevel = _engine->_gamestate->_executionStack.size() - 1;
+ _debugState.debugging = true;
return false;
}
@@ -2209,9 +2343,9 @@ bool Console::cmdStepGlobal(int argc, const char **argv) {
return true;
}
- g_debugState.seeking = kDebugSeekGlobal;
- g_debugState.seekSpecial = atoi(argv[1]);
- g_debugState.debugging = true;
+ _debugState.seeking = kDebugSeekGlobal;
+ _debugState.seekSpecial = atoi(argv[1]);
+ _debugState.debugging = true;
return false;
}
@@ -2239,12 +2373,12 @@ bool Console::cmdStepCallk(int argc, const char **argv) {
}
}
- g_debugState.seeking = kDebugSeekSpecialCallk;
- g_debugState.seekSpecial = callk_index;
+ _debugState.seeking = kDebugSeekSpecialCallk;
+ _debugState.seekSpecial = callk_index;
} else {
- g_debugState.seeking = kDebugSeekCallk;
+ _debugState.seeking = kDebugSeekCallk;
}
- g_debugState.debugging = true;
+ _debugState.debugging = true;
return false;
}
@@ -2357,11 +2491,11 @@ bool Console::cmdSend(int argc, const char **argv) {
return true;
}
- const char *selector_name = argv[2];
- int selectorId = _engine->getKernel()->findSelector(selector_name);
+ const char *selectorName = argv[2];
+ int selectorId = _engine->getKernel()->findSelector(selectorName);
if (selectorId < 0) {
- DebugPrintf("Unknown selector: \"%s\"\n", selector_name);
+ DebugPrintf("Unknown selector: \"%s\"\n", selectorName);
return true;
}
@@ -2371,10 +2505,10 @@ bool Console::cmdSend(int argc, const char **argv) {
return true;
}
- SelectorType selector_type = lookupSelector(_engine->_gamestate->_segMan, object, selectorId, 0, 0);
+ SelectorType selector_type = lookupSelector(_engine->_gamestate->_segMan, object, selectorId, NULL, NULL);
if (selector_type == kSelectorNone) {
- DebugPrintf("Object does not support selector: \"%s\"\n", selector_name);
+ DebugPrintf("Object does not support selector: \"%s\"\n", selectorName);
return true;
}
@@ -2426,7 +2560,7 @@ bool Console::cmdSend(int argc, const char **argv) {
bool Console::cmdGo(int argc, const char **argv) {
// CHECKME: is this necessary?
- g_debugState.seeking = kDebugSeekNothing;
+ _debugState.seeking = kDebugSeekNothing;
return Cmd_Exit(argc, argv);
}
@@ -2437,8 +2571,8 @@ bool Console::cmdBreakpointList(int argc, const char **argv) {
DebugPrintf("Breakpoint list:\n");
- Common::List<Breakpoint>::const_iterator bp = g_debugState._breakpoints.begin();
- Common::List<Breakpoint>::const_iterator end = g_debugState._breakpoints.end();
+ Common::List<Breakpoint>::const_iterator bp = _debugState._breakpoints.begin();
+ Common::List<Breakpoint>::const_iterator end = _debugState._breakpoints.end();
for (; bp != end; ++bp) {
DebugPrintf(" #%i: ", i);
switch (bp->type) {
@@ -2454,6 +2588,9 @@ bool Console::cmdBreakpointList(int argc, const char **argv) {
i++;
}
+ if (!i)
+ DebugPrintf(" No breakpoints defined.\n");
+
return true;
}
@@ -2461,14 +2598,21 @@ bool Console::cmdBreakpointDelete(int argc, const char **argv) {
if (argc != 2) {
DebugPrintf("Deletes a breakpoint with the specified index.\n");
DebugPrintf("Usage: %s <breakpoint index>\n", argv[0]);
+ DebugPrintf("<index> * will remove all breakpoints\n");
+ return true;
+ }
+
+ if (strcmp(argv[1], "*") == 0) {
+ _debugState._breakpoints.clear();
+ _debugState._activeBreakpointTypes = 0;
return true;
}
const int idx = atoi(argv[1]);
// Find the breakpoint at index idx.
- Common::List<Breakpoint>::iterator bp = g_debugState._breakpoints.begin();
- const Common::List<Breakpoint>::iterator end = g_debugState._breakpoints.end();
+ Common::List<Breakpoint>::iterator bp = _debugState._breakpoints.begin();
+ const Common::List<Breakpoint>::iterator end = _debugState._breakpoints.end();
for (int i = 0; bp != end && i < idx; ++bp, ++i) {
// do nothing
}
@@ -2479,15 +2623,15 @@ bool Console::cmdBreakpointDelete(int argc, const char **argv) {
}
// Delete it
- g_debugState._breakpoints.erase(bp);
+ _debugState._breakpoints.erase(bp);
// Update EngineState::_activeBreakpointTypes.
int type = 0;
- for (bp = g_debugState._breakpoints.begin(); bp != end; ++bp) {
+ for (bp = _debugState._breakpoints.begin(); bp != end; ++bp) {
type |= bp->type;
}
- g_debugState._activeBreakpointTypes = type;
+ _debugState._activeBreakpointTypes = type;
return true;
}
@@ -2509,8 +2653,8 @@ bool Console::cmdBreakpointExecMethod(int argc, const char **argv) {
bp.type = BREAK_SELECTOR;
bp.name = argv[1];
- g_debugState._breakpoints.push_back(bp);
- g_debugState._activeBreakpointTypes |= BREAK_SELECTOR;
+ _debugState._breakpoints.push_back(bp);
+ _debugState._activeBreakpointTypes |= BREAK_SELECTOR;
return true;
}
@@ -2530,8 +2674,8 @@ bool Console::cmdBreakpointExecFunction(int argc, const char **argv) {
bp.type = BREAK_EXPORT;
bp.address = (atoi(argv[1]) << 16 | atoi(argv[2]));
- g_debugState._breakpoints.push_back(bp);
- g_debugState._activeBreakpointTypes |= BREAK_EXPORT;
+ _debugState._breakpoints.push_back(bp);
+ _debugState._activeBreakpointTypes |= BREAK_EXPORT;
return true;
}
@@ -2731,9 +2875,9 @@ bool Console::cmdQuit(int argc, const char **argv) {
if (!scumm_stricmp(argv[1], "game")) {
// Quit gracefully
- _engine->_gamestate->script_abort_flag = 1; // Terminate VM
- g_debugState.seeking = kDebugSeekNothing;
- g_debugState.runningStep = 0;
+ _engine->_gamestate->abortScriptProcessing = kAbortQuitGame; // Terminate VM
+ _debugState.seeking = kDebugSeekNothing;
+ _debugState.runningStep = 0;
} else if (!scumm_stricmp(argv[1], "now")) {
// Quit ungracefully
@@ -2980,8 +3124,71 @@ static int parse_reg_t(EngineState *s, const char *str, reg_t *dest, bool mayBeV
return 0;
}
-void Console::printList(List *l) {
- reg_t pos = l->first;
+bool Console::parseInteger(const char *argument, int &result) {
+ char *endPtr = 0;
+ int idxLen = strlen(argument);
+ const char *lastChar = argument + idxLen - (idxLen == 0 ? 0 : 1);
+
+ if ((strncmp(argument, "0x", 2) == 0) || (*lastChar == 'h')) {
+ // hexadecimal number
+ result = strtol(argument, &endPtr, 16);
+ if ((*endPtr != 0) && (*endPtr != 'h')) {
+ DebugPrintf("Invalid hexadecimal number '%s'\n", argument);
+ return false;
+ }
+ } else {
+ // decimal number
+ result = strtol(argument, &endPtr, 10);
+ if (*endPtr != 0) {
+ DebugPrintf("Invalid decimal number '%s'\n", argument);
+ return false;
+ }
+ }
+ return true;
+}
+
+void Console::printBasicVarInfo(reg_t variable) {
+ int regType = g_sci->getKernel()->findRegType(variable);
+ int segType = regType;
+ SegManager *segMan = g_sci->getEngineState()->_segMan;
+
+ segType &= SIG_TYPE_INTEGER | SIG_TYPE_OBJECT | SIG_TYPE_REFERENCE | SIG_TYPE_NODE | SIG_TYPE_LIST | SIG_TYPE_UNINITIALIZED | SIG_TYPE_ERROR;
+
+ switch (segType) {
+ case SIG_TYPE_INTEGER: {
+ uint16 content = variable.toUint16();
+ if (content >= 10)
+ DebugPrintf(" (%dd)", content);
+ break;
+ }
+ case SIG_TYPE_OBJECT:
+ DebugPrintf(" (object '%s')", segMan->getObjectName(variable));
+ break;
+ case SIG_TYPE_REFERENCE:
+ DebugPrintf(" (reference)");
+ break;
+ case SIG_TYPE_NODE:
+ DebugPrintf(" (node)");
+ break;
+ case SIG_TYPE_LIST:
+ DebugPrintf(" (list)");
+ break;
+ case SIG_TYPE_UNINITIALIZED:
+ DebugPrintf(" (uninitialized)");
+ break;
+ case SIG_TYPE_ERROR:
+ DebugPrintf(" (error)");
+ break;
+ default:
+ DebugPrintf(" (??\?)");
+ }
+
+ if (regType & SIG_IS_INVALID)
+ DebugPrintf(" IS INVALID!");
+}
+
+void Console::printList(List *list) {
+ reg_t pos = list->first;
reg_t my_prev = NULL_REG;
DebugPrintf("\t<\n");
@@ -3008,9 +3215,9 @@ void Console::printList(List *l) {
pos = node->succ;
}
- if (my_prev != l->last)
+ if (my_prev != list->last)
DebugPrintf(" WARNING: Last node was expected to be %04x:%04x, was %04x:%04x!\n",
- PRINT_REG(l->last), PRINT_REG(my_prev));
+ PRINT_REG(list->last), PRINT_REG(my_prev));
DebugPrintf("\t>\n");
}
@@ -3076,7 +3283,7 @@ int Console::printObject(reg_t pos) {
DebugPrintf(" ");
if (i < var_container->getVarCount()) {
uint16 varSelector = var_container->getVarSelector(i);
- DebugPrintf("[%03x] %s = ", varSelector, selector_name(s, varSelector));
+ DebugPrintf("[%03x] %s = ", varSelector, _engine->getKernel()->getSelectorName(varSelector).c_str());
} else
DebugPrintf("p#%x = ", i);
@@ -3095,12 +3302,66 @@ int Console::printObject(reg_t pos) {
DebugPrintf(" -- methods:\n");
for (i = 0; i < obj->getMethodCount(); i++) {
reg_t fptr = obj->getFunction(i);
- DebugPrintf(" [%03x] %s = %04x:%04x\n", obj->getFuncSelector(i), selector_name(s, obj->getFuncSelector(i)), PRINT_REG(fptr));
+ DebugPrintf(" [%03x] %s = %04x:%04x\n", obj->getFuncSelector(i), _engine->getKernel()->getSelectorName(obj->getFuncSelector(i)).c_str(), PRINT_REG(fptr));
}
if (s->_segMan->_heap[pos.segment]->getType() == SEG_TYPE_SCRIPT)
- DebugPrintf("\nOwner script: %d\n", s->_segMan->getScript(pos.segment)->_nr);
+ DebugPrintf("\nOwner script: %d\n", s->_segMan->getScript(pos.segment)->getScriptNumber());
return 0;
}
+void Console::hexDumpReg(const reg_t *data, int len, int regsPerLine, int startOffset, bool isArray) {
+ // reg_t version of Common::hexdump
+ assert(1 <= regsPerLine && regsPerLine <= 8);
+ int i;
+ byte c;
+ int offset = startOffset;
+ while (len >= regsPerLine) {
+ printf("%06x: ", offset);
+ for (i = 0; i < regsPerLine; i++) {
+ printf("%04x:%04x ", PRINT_REG(data[i]));
+ }
+ printf(" |");
+ for (i = 0; i < regsPerLine; i++) {
+ c = data[i].toUint16() >> 8;
+ if (c < 32 || c >= 127)
+ c = '.';
+ printf("%c", c);
+ c = data[i].toUint16() & 0xff;
+ if (c < 32 || c >= 127)
+ c = '.';
+ printf("%c", c);
+ }
+ printf("|\n");
+ data += regsPerLine;
+ len -= regsPerLine;
+ offset += regsPerLine * (isArray ? 1 : 2);
+ }
+
+ if (len <= 0)
+ return;
+
+ printf("%06x: ", offset);
+ for (i = 0; i < regsPerLine; i++) {
+ if (i < len)
+ printf("%04x:%04x ", PRINT_REG(data[i]));
+ else
+ printf(" ");
+ }
+ printf(" |");
+ for (i = 0; i < len; i++) {
+ c = data[i].toUint16() >> 8;
+ if (c < 32 || c >= 127)
+ c = '.';
+ printf("%c", c);
+ c = data[i].toUint16() & 0xff;
+ if (c < 32 || c >= 127)
+ c = '.';
+ printf("%c", c);
+ }
+ for (; i < regsPerLine; i++)
+ printf(" ");
+ printf("|\n");
+}
+
} // End of namespace Sci
diff --git a/engines/sci/console.h b/engines/sci/console.h
index 2b13e03ef6..51f02d7168 100644
--- a/engines/sci/console.h
+++ b/engines/sci/console.h
@@ -74,6 +74,7 @@ private:
bool cmdList(int argc, const char **argv);
bool cmdHexgrep(int argc, const char **argv);
bool cmdVerifyScripts(int argc, const char **argv);
+ bool cmdShowInstruments(int argc, const char **argv);
// Game
bool cmdSaveGame(int argc, const char **argv);
bool cmdRestoreGame(int argc, const char **argv);
@@ -117,9 +118,9 @@ private:
bool cmdAddresses(int argc, const char **argv);
bool cmdRegisters(int argc, const char **argv);
bool cmdDissectScript(int argc, const char **argv);
- bool cmdSetAccumulator(int argc, const char **argv);
bool cmdBacktrace(int argc, const char **argv);
- bool cmdStep(int argc, const char **argv);
+ bool cmdTrace(int argc, const char **argv);
+ bool cmdStepOver(int argc, const char **argv);
bool cmdStepEvent(int argc, const char **argv);
bool cmdStepRet(int argc, const char **argv);
bool cmdStepGlobal(int argc, const char **argv);
@@ -145,12 +146,18 @@ private:
bool cmdViewActiveObject(int argc, const char **argv);
bool cmdViewAccumulatorObject(int argc, const char **argv);
+ bool parseInteger(const char *argument, int &result);
+
+ void printBasicVarInfo(reg_t variable);
+
bool segmentInfo(int nr);
- void printList(List *l);
+ void printList(List *list);
int printNode(reg_t addr);
+ void hexDumpReg(const reg_t *data, int len, int regsPerLine = 4, int startOffset = 0, bool isArray = false);
private:
SciEngine *_engine;
+ DebugState &_debugState;
bool _mouseVisible;
Common::String _videoFile;
int _videoFrameDelay;
diff --git a/engines/sci/debug.h b/engines/sci/debug.h
index 8383722956..5cf0e38fbc 100644
--- a/engines/sci/debug.h
+++ b/engines/sci/debug.h
@@ -26,8 +26,8 @@
#ifndef SCI_DEBUG_H
#define SCI_DEBUG_H
+#include "common/list.h"
#include "sci/engine/vm_types.h" // for StackPtr
-#include "sci/engine/vm.h" // for ExecStack
namespace Sci {
@@ -57,8 +57,8 @@ enum DebugSeeking {
kDebugSeekCallk = 1, // Step forward until callk is found
kDebugSeekLevelRet = 2, // Step forward until returned from this level
kDebugSeekSpecialCallk = 3, // Step forward until a /special/ callk is found
- kDebugSeekSO = 4, // Step forward until specified PC (after the send command) and stack depth
- kDebugSeekGlobal = 5 // Step forward until one specified global variable is modified
+ kDebugSeekGlobal = 4, // Step forward until one specified global variable is modified
+ kDebugSeekStepOver = 5 // Step forward until we reach same stack-level again
};
struct DebugState {
@@ -79,7 +79,6 @@ struct DebugState {
extern int g_debug_sleeptime_factor;
extern int g_debug_simulated_key;
extern bool g_debug_track_mouse_clicks;
-extern DebugState g_debugState;
} // End of namespace Sci
diff --git a/engines/sci/decompressor.cpp b/engines/sci/decompressor.cpp
index 84af50b596..96c7f24ef6 100644
--- a/engines/sci/decompressor.cpp
+++ b/engines/sci/decompressor.cpp
@@ -574,15 +574,17 @@ void DecompressorLZW::reorderView(byte *src, byte *dest) {
for (c = 0; c < cel_total; c++)
decodeRLE(&rle_ptr, &pix_ptr, cc_pos[c] + 8, cc_lengths[c]);
- *writer++ = 'P';
- *writer++ = 'A';
- *writer++ = 'L';
+ if (pal_offset) {
+ *writer++ = 'P';
+ *writer++ = 'A';
+ *writer++ = 'L';
- for (c = 0; c < 256; c++)
- *writer++ = c;
+ for (c = 0; c < 256; c++)
+ *writer++ = c;
- seeker -= 4; /* The missing four. Don't ask why. */
- memcpy(writer, seeker, 4*256 + 4);
+ seeker -= 4; /* The missing four. Don't ask why. */
+ memcpy(writer, seeker, 4*256 + 4);
+ }
free(cc_pos);
free(cc_lengths);
diff --git a/engines/sci/detection.cpp b/engines/sci/detection.cpp
index 1ccfc6bf02..eb2c989e0d 100644
--- a/engines/sci/detection.cpp
+++ b/engines/sci/detection.cpp
@@ -36,12 +36,11 @@
#include "sci/engine/script.h"
#include "sci/engine/seg_manager.h"
#include "sci/engine/state.h"
-#include "sci/engine/vm.h" // for convertSierraGameId
namespace Sci {
// Titles of the games
-static const PlainGameDescriptor SciGameTitles[] = {
+static const PlainGameDescriptor s_sciGameTitles[] = {
{"sci", "Sierra SCI Game"},
{"sci-fanmade", "Fanmade SCI Game"},
// === SCI0 games =========================================================
@@ -106,6 +105,7 @@ static const PlainGameDescriptor SciGameTitles[] = {
// TODO: Inside The Chest/Behind the Developer's Shield
{"kq7", "King's Quest VII: The Princeless Bride"},
// TODO: King's Questions
+ {"lsl6hires", "Leisure Suit Larry 6: Shape Up or Slip Out!"},
{"phantasmagoria", "Phantasmagoria"},
{"pqswat", "Police Quest: SWAT"},
{"shivers", "Shivers"},
@@ -120,6 +120,81 @@ static const PlainGameDescriptor SciGameTitles[] = {
{0, 0}
};
+struct GameIdStrToEnum {
+ const char *gameidStr;
+ SciGameId gameidEnum;
+};
+
+static const GameIdStrToEnum s_gameIdStrToEnum[] = {
+ { "astrochicken", GID_ASTROCHICKEN },
+ { "camelot", GID_CAMELOT },
+ { "castlebrain", GID_CASTLEBRAIN },
+ { "christmas1988", GID_CHRISTMAS1988 },
+ { "christmas1990", GID_CHRISTMAS1990 },
+ { "christmas1992", GID_CHRISTMAS1992 },
+ { "cnick-kq", GID_CNICK_KQ },
+ { "cnick-laurabow", GID_CNICK_LAURABOW },
+ { "cnick-longbow", GID_CNICK_LONGBOW },
+ { "cnick-lsl", GID_CNICK_LSL },
+ { "cnick-sq", GID_CNICK_SQ },
+ { "ecoquest", GID_ECOQUEST },
+ { "ecoquest2", GID_ECOQUEST2 },
+ { "fairytales", GID_FAIRYTALES },
+ { "freddypharkas", GID_FREDDYPHARKAS },
+ { "funseeker", GID_FUNSEEKER },
+ { "gk1", GID_GK1 },
+ { "gk2", GID_GK2 },
+ { "hoyle1", GID_HOYLE1 },
+ { "hoyle2", GID_HOYLE2 },
+ { "hoyle3", GID_HOYLE3 },
+ { "hoyle4", GID_HOYLE4 },
+ { "iceman", GID_ICEMAN },
+ { "islandbrain", GID_ISLANDBRAIN },
+ { "jones", GID_JONES },
+ { "kq1sci", GID_KQ1 },
+ { "kq4sci", GID_KQ4 },
+ { "kq5", GID_KQ5 },
+ { "kq6", GID_KQ6 },
+ { "kq7", GID_KQ7 },
+ { "laurabow", GID_LAURABOW },
+ { "laurabow2", GID_LAURABOW2 },
+ { "lighthouse", GID_LIGHTHOUSE },
+ { "longbow", GID_LONGBOW },
+ { "lsl1sci", GID_LSL1 },
+ { "lsl2", GID_LSL2 },
+ { "lsl3", GID_LSL3 },
+ { "lsl5", GID_LSL5 },
+ { "lsl6", GID_LSL6 },
+ { "lsl6hires", GID_LSL6HIRES },
+ { "lsl7", GID_LSL7 },
+ { "mothergoose", GID_MOTHERGOOSE },
+ { "msastrochicken", GID_MSASTROCHICKEN },
+ { "pepper", GID_PEPPER },
+ { "phantasmagoria", GID_PHANTASMAGORIA },
+ { "phantasmagoria2", GID_PHANTASMAGORIA2 },
+ { "pq1sci", GID_PQ1 },
+ { "pq2", GID_PQ2 },
+ { "pq3", GID_PQ3 },
+ { "pq4", GID_PQ4 },
+ { "pqswat", GID_PQSWAT },
+ { "qfg1", GID_QFG1 },
+ { "qfg2", GID_QFG2 },
+ { "qfg3", GID_QFG3 },
+ { "qfg4", GID_QFG4 },
+ { "rama", GID_RAMA },
+ { "sci-fanmade", GID_FANMADE }, // FIXME: Do we really need/want this?
+ { "shivers", GID_SHIVERS },
+ { "shivers2", GID_SHIVERS2 },
+ { "slater", GID_SLATER },
+ { "sq1sci", GID_SQ1 },
+ { "sq3", GID_SQ3 },
+ { "sq4", GID_SQ4 },
+ { "sq5", GID_SQ5 },
+ { "sq6", GID_SQ6 },
+ { "torin", GID_TORIN },
+ { NULL, (SciGameId)-1 }
+};
+
struct OldNewIdTableEntry {
const char *oldId;
const char *newId;
@@ -198,6 +273,12 @@ static const OldNewIdTableEntry s_oldNewTable[] = {
{ "", "", SCI_VERSION_NONE }
};
+/**
+ * Converts the builtin Sierra game IDs to the ones we use in ScummVM
+ * @param[in] gameId The internal game ID
+ * @param[in] gameFlags The game's flags, which are adjusted accordingly for demos
+ * @return The equivalent ScummVM game id
+ */
Common::String convertSierraGameId(Common::String sierraId, uint32 *gameFlags, ResourceManager *resMan) {
// Convert the id to lower case, so that we match all upper/lower case variants.
sierraId.toLowercase();
@@ -295,7 +376,7 @@ static const ADParams detectionParams = {
// Number of bytes to compute MD5 sum for
5000,
// List of all engine targets
- SciGameTitles,
+ s_sciGameTitles,
// Structure for autoupgrading obsolete targets
0,
// Name of single gameid (optional)
@@ -305,7 +386,11 @@ static const ADParams detectionParams = {
// Flags
0,
// Additional GUI options (for every game}
- Common::GUIO_NONE
+ Common::GUIO_NONE,
+ // Maximum directory depth
+ 1,
+ // List of directory globs
+ 0
};
class SciMetaEngine : public AdvancedMetaEngine {
@@ -419,7 +504,12 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const Common::FSList &fsl
return 0;
}
- ResourceManager *resMan = new ResourceManager(fslist);
+ ResourceManager *resMan = new ResourceManager();
+ assert(resMan);
+ resMan->addAppropriateSources(fslist);
+ resMan->init();
+ // TODO: Add error handling.
+
ViewType gameViews = resMan->getViewType();
// Have we identified the game views? If not, stop here
@@ -509,12 +599,16 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const Common::FSList &fsl
return (const ADGameDescription *)&s_fallbackDesc;
}
-bool SciMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const {
- const ADGameDescription *desc = (const ADGameDescription *)gd;
-
- *engine = new SciEngine(syst, desc);
+bool SciMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
+ const GameIdStrToEnum *g = s_gameIdStrToEnum;
+ for (; g->gameidStr; ++g) {
+ if (0 == strcmp(desc->gameid, g->gameidStr)) {
+ *engine = new SciEngine(syst, desc, g->gameidEnum);
+ return true;
+ }
+ }
- return true;
+ return false;
}
bool SciMetaEngine::hasFeature(MetaEngineFeature f) const {
@@ -652,7 +746,7 @@ Common::Error SciEngine::saveGameState(int slot, const char *desc) {
return Common::kWritingFailed;
}
- if (gamestate_save(_gamestate, out, desc, version)) {
+ if (!gamestate_save(_gamestate, out, desc, version)) {
warning("Saving the game state to '%s' failed", fileName.c_str());
return Common::kWritingFailed;
} else {
@@ -668,11 +762,11 @@ Common::Error SciEngine::saveGameState(int slot, const char *desc) {
}
bool SciEngine::canLoadGameStateCurrently() {
- return !_gamestate->execution_stack_base;
+ return !_gamestate->executionStackBase;
}
bool SciEngine::canSaveGameStateCurrently() {
- return !_gamestate->execution_stack_base;
+ return !_gamestate->executionStackBase;
}
} // End of namespace Sci
diff --git a/engines/sci/detection_tables.h b/engines/sci/detection_tables.h
index dba4d879aa..aa1e26e0f4 100644
--- a/engines/sci/detection_tables.h
+++ b/engines/sci/detection_tables.h
@@ -753,14 +753,31 @@ static const struct ADGameDescription SciGameDescriptions[] = {
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
// Hoyle 4 - English DOS Demo
+ {"hoyle4", "Demo", {
+ {"resource.map", 0, "60f764020a6b788bbbe415dbc2ccb9f3", 931},
+ {"resource.000", 0, "5fe3670e3ddcd4f85c10013b5453141a", 615522},
+ {NULL, 0, NULL, 0}},
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
+
+ // Hoyle 4 - English DOS Demo
// SCI interpreter version 1.001.200 (just a guess)
+ // Does anyone have this version? -clone2727
{"hoyle4", "Demo", {
{"resource.map", 0, "662087cb383e52e3cc4ae7ecb10e20aa", 938},
{"resource.000", 0, "24c10844792c54d476d272213cbac300", 675252},
{NULL, 0, NULL, 0}},
Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
- // Jones in the Fast Lane - English DOS
+ // Jones in the Fast Lane EGA - English DOS
+ // SCI interpreter version 1.000.172 (not 100% sure FIXME)
+ {"jones", "", {
+ {"resource.map", 0, "be4cf9e8c1e253623ef35ae3b8a1d998", 1800},
+ {"resource.001", 0, "bac3ec6cb3e3920984ab0f32becf5163", 202105},
+ {"resource.002", 0, "b86daa3ba2784d1502da881eedb80d9b", 341771},
+ {NULL, 0, NULL, 0}},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+
+ // Jones in the Fast Lane VGA - English DOS
// SCI interpreter version 1.000.172
{"jones", "", {
{"resource.map", 0, "65cbe19b36fffc71c8e7b2686bd49ad7", 1800},
@@ -1667,7 +1684,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
#ifdef ENABLE_SCI32
// Larry 6 - English/German DOS CD - HIRES
// SCI interpreter version 2.100.002
- {"lsl6", "", {
+ {"lsl6hires", "", {
{"resource.map", 0, "0c0804434ea62278dd15032b1947426c", 8872},
{"resource.000", 0, "9a9f4870504444cda863dd14d077a680", 18520872},
{NULL, 0, NULL, 0}},
@@ -1675,7 +1692,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
// Larry 6 - German DOS CD - HIRES (provided by richiefs in bug report #2670691)
// SCI interpreter version 2.100.002
- {"lsl6", "", {
+ {"lsl6hires", "", {
{"resource.map", 0, "badfdf446ffed569a310d2c63a249421", 8896},
{"resource.000", 0, "bd944d2b06614a5b39f1586906f0ee88", 18534274},
{NULL, 0, NULL, 0}},
@@ -1683,7 +1700,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
// Larry 6 - French DOS CD - HIRES (provided by richiefs in bug report #2670691)
// SCI interpreter version 2.100.002
- {"lsl6", "", {
+ {"lsl6hires", "", {
{"resource.map", 0, "d184e9aa4f2d4b5670ddb3669db82cda", 8896},
{"resource.000", 0, "bd944d2b06614a5b39f1586906f0ee88", 18538987},
{NULL, 0, NULL, 0}},
@@ -2390,7 +2407,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
},
// Quest for Glory 4 1.1 Floppy - German DOS (supplied by markcool in bug report #2723850)
- // SCI interpreter version 2.000.000 (a guess?)
+ // Executable scanning reports "2.000.000", VERSION file reports "1.1"
{"qfg4", "", {
{"resource.map", 0, "9e0abba8746f40565bc7eb5720522ecd", 9301},
{"resource.000", 0, "57f22cdc54eeb35fce1f26b31b5c3ee1", 11076197},
@@ -2407,27 +2424,6 @@ static const struct ADGameDescription SciGameDescriptions[] = {
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH
},
-#if 0
- // NOTE: This version looks to be exactly the same as the English one
- // Perhaps it's the English one?
-
- // Quest for Glory 4 - German DOS/Windows (from PCJoker 2/98)
- {"qfg4", "", {
- {"resource.map", 0, "aba367f2102e81782d961b14fbe3d630", 10246},
- {"resource.000", 0, "263dce4aa34c49d3ad29bec889007b1c", 11571394},
- {NULL, 0, NULL, 0}},
- Common::DE_DEU, Common::kPlatformPC, 0, GUIO_NOSPEECH
- },
-#endif
-
- // Quest for Glory 4 - German DOS/Windows Disk V1.1 (from PCJoker 2/89)
- // SCI interpreter version 2.000.000 (a guess?)
- {"qfg4", "", {
- {"resource.map", 0, "9e0abba8746f40565bc7eb5720522ecd", 9301},
- {"resource.000", 0, "57f22cdc54eeb35fce1f26b31b5c3ee1", 11076197},
- {NULL, 0, NULL, 0}},
- Common::DE_DEU, Common::kPlatformPC, 0, GUIO_NOSPEECH
- },
#endif
// Slater & Charlie go camping
@@ -2572,6 +2568,18 @@ static const struct ADGameDescription SciGameDescriptions[] = {
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH
},
+ // Space Quest 1 VGA Remake - English Mac (from Fingolfin)
+ {"sq1sci", "VGA Remake", {
+ {"resource.map", 0, "5c6ad20407261b544238e8dce87afead", 5895},
+ {"resource.000", 0, "2c414644b23839069c8d1a93b721df16", 1017033},
+ {"resource.001", 0, "8744ae2ea6b316e91e2a35ab1aa301d2", 1024622},
+ {"resource.002", 0, "96860704f7a07ecc10bef223b4b2f153", 1273992},
+ {"resource.003", 0, "ae46e195e66df5a131917f0aa80b5669", 1242794},
+ {"resource.004", 0, "91d58a9eb2187c38424990afe4c12bc6", 1250949},
+ {NULL, 0, NULL, 0}},
+ Common::EN_ANY, Common::kPlatformMacintosh, 0, GUIO_NOSPEECH
+ },
+
// Space Quest 1 VGA Remake - English Non-Interactive Demo (from FRG)
// SCI interpreter version 1.000.181
{"sq1sci", "VGA Remake, Demo", {
@@ -2660,6 +2668,15 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{NULL, 0, NULL, 0}},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+ // Space Quest 3 - English Mac (from Fingolfin)
+ {"sq3", "", {
+ {"resource.map", 0, "5c931675c6e01c4b418faca85d76c92c", 5844},
+ {"resource.001", 0, "0d8dfe42683b46f3131823233a91ce6a", 771917},
+ {"resource.002", 0, "0d8dfe42683b46f3131823233a91ce6a", 794072},
+ {"resource.003", 0, "0d8dfe42683b46f3131823233a91ce6a", 776536},
+ {NULL, 0, NULL, 0}},
+ Common::EN_ANY, Common::kPlatformMacintosh, 0, GUIO_NOSPEECH },
+
// Space Quest 3 - German DOS (from Tobis87)
// SCI interpreter version 0.000.453 (?)
{"sq3", "", {
diff --git a/engines/sci/engine/features.cpp b/engines/sci/engine/features.cpp
index 48f7c2d64f..fee6e69da7 100644
--- a/engines/sci/engine/features.cpp
+++ b/engines/sci/engine/features.cpp
@@ -24,6 +24,7 @@
*/
#include "sci/engine/features.h"
+#include "sci/engine/kernel.h"
#include "sci/engine/script.h"
#include "sci/engine/selector.h"
#include "sci/engine/vm.h"
@@ -51,13 +52,13 @@ reg_t GameFeatures::getDetectionAddr(const Common::String &objName, Selector slc
reg_t addr;
if (objAddr.isNull()) {
- warning("getDetectionAddr: %s object couldn't be found", objName.c_str());
+ error("getDetectionAddr: %s object couldn't be found", objName.c_str());
return NULL_REG;
}
if (methodNum == -1) {
if (lookupSelector(_segMan, objAddr, slc, NULL, &addr) != kSelectorMethod) {
- warning("getDetectionAddr: target selector is not a method of object %s", objName.c_str());
+ error("getDetectionAddr: target selector is not a method of object %s", objName.c_str());
return NULL_REG;
}
} else {
@@ -69,7 +70,7 @@ reg_t GameFeatures::getDetectionAddr(const Common::String &objName, Selector slc
bool GameFeatures::autoDetectSoundType() {
// Look up the script address
- reg_t addr = getDetectionAddr("Sound", _kernel->_selectorCache.play);
+ reg_t addr = getDetectionAddr("Sound", SELECTOR(play));
if (!addr.segment)
return false;
@@ -83,17 +84,17 @@ bool GameFeatures::autoDetectSoundType() {
int16 opparams[4];
byte extOpcode;
byte opcode;
- offset += readPMachineInstruction(script->_buf + offset, extOpcode, opparams);
+ offset += readPMachineInstruction(script->getBuf(offset), extOpcode, opparams);
opcode = extOpcode >> 1;
// Check for end of script
if (opcode == op_ret || offset >= script->getBufSize())
break;
- // The play method of the Sound object pushes the DoSound command
- // that it'll use just before it calls DoSound. We intercept that here
- // in order to check what sound semantics are used, cause the position
- // of the sound commands has changed at some point during SCI1 middle
+ // The play method of the Sound object pushes the DoSound command that
+ // it will use just before it calls DoSound. We intercept that here in
+ // order to check what sound semantics are used, cause the position of
+ // the sound commands has changed at some point during SCI1 middle.
if (opcode == op_pushi) {
// Load the pushi parameter
intParam = opparams[0];
@@ -104,8 +105,8 @@ bool GameFeatures::autoDetectSoundType() {
if (kFuncNum == 6) { // kIsObject (SCI0-SCI11)
foundTarget = true;
} else if (kFuncNum == 45) { // kDoSound (SCI1)
- // First, check which DoSound function is called by the play method of
- // the Sound object
+ // First, check which DoSound function is called by the play
+ // method of the Sound object
switch (intParam) {
case 1:
_doSoundType = SCI_VERSION_0_EARLY;
@@ -118,8 +119,8 @@ bool GameFeatures::autoDetectSoundType() {
break;
default:
// Unknown case... should never happen. We fall back to
- // alternative detection here, which works in general, apart from
- // some transitive games like Jones CD
+ // alternative detection here, which works in general, apart
+ // from some transitive games like Jones CD
_doSoundType = foundTarget ? SCI_VERSION_1_LATE : SCI_VERSION_1_EARLY;
break;
}
@@ -136,9 +137,10 @@ bool GameFeatures::autoDetectSoundType() {
SciVersion GameFeatures::detectDoSoundType() {
if (_doSoundType == SCI_VERSION_NONE) {
if (getSciVersion() == SCI_VERSION_0_EARLY) {
- // This game is using early SCI0 sound code (different headers than SCI0 late)
+ // This game is using early SCI0 sound code (different headers than
+ // SCI0 late)
_doSoundType = SCI_VERSION_0_EARLY;
- } else if (_kernel->_selectorCache.nodePtr == -1) {
+ } else if (SELECTOR(nodePtr) == -1) {
// No nodePtr selector, so this game is definitely using newer
// SCI0 sound code (i.e. SCI_VERSION_0_LATE)
_doSoundType = SCI_VERSION_0_LATE;
@@ -171,14 +173,16 @@ SciVersion GameFeatures::detectSetCursorType() {
// SCI1.1 games always use cursor views
_setCursorType = SCI_VERSION_1_1;
} else { // SCI1 late game, detect cursor semantics
- // If the Cursor object doesn't exist, we're using the SCI0 early kSetCursor semantics.
+ // If the Cursor object doesn't exist, we're using the SCI0 early
+ // kSetCursor semantics.
if (_segMan->findObjectByName("Cursor") == NULL_REG) {
_setCursorType = SCI_VERSION_0_EARLY;
debugC(1, kDebugLevelGraphics, "Detected SetCursor type: %s", getSciVersionDesc(_setCursorType));
return _setCursorType;
}
- // Check for the existence of the handCursor object (first found). This is based on KQ5.
+ // Check for the existence of the handCursor object (first found).
+ // This is based on KQ5.
reg_t objAddr = _segMan->findObjectByName("handCursor", 0);
// If that doesn't exist, we assume it uses SCI1.1 kSetCursor semantics
@@ -188,11 +192,13 @@ SciVersion GameFeatures::detectSetCursorType() {
return _setCursorType;
}
- // Now we check what the number variable holds in the handCursor object.
+ // Now we check what the number variable holds in the handCursor
+ // object.
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.
+ // If the number is 0, it uses views and therefore the SCI1.1
+ // kSetCursor semantics, otherwise it uses the SCI0 early kSetCursor
+ // semantics.
if (number == 0)
_setCursorType = SCI_VERSION_1_1;
else
@@ -219,7 +225,7 @@ bool GameFeatures::autoDetectLofsType(int methodNum) {
int16 opparams[4];
byte extOpcode;
byte opcode;
- offset += readPMachineInstruction(script->_buf + offset, extOpcode, opparams);
+ offset += readPMachineInstruction(script->getBuf(offset), extOpcode, opparams);
opcode = extOpcode >> 1;
// Check for end of script
@@ -293,7 +299,7 @@ SciVersion GameFeatures::detectLofsType() {
bool GameFeatures::autoDetectGfxFunctionsType(int methodNum) {
// Look up the script address
- reg_t addr = getDetectionAddr("Rm", _kernel->_selectorCache.overlay, methodNum);
+ reg_t addr = getDetectionAddr("Rm", SELECTOR(overlay), methodNum);
if (!addr.segment)
return false;
@@ -305,7 +311,7 @@ bool GameFeatures::autoDetectGfxFunctionsType(int methodNum) {
int16 opparams[4];
byte extOpcode;
byte opcode;
- offset += readPMachineInstruction(script->_buf + offset, extOpcode, opparams);
+ offset += readPMachineInstruction(script->getBuf(offset), extOpcode, opparams);
opcode = extOpcode >> 1;
// Check for end of script
@@ -317,10 +323,10 @@ bool GameFeatures::autoDetectGfxFunctionsType(int methodNum) {
uint16 argc = opparams[1];
if (kFuncNum == 8) { // kDrawPic (SCI0 - SCI11)
- // If kDrawPic is called with 6 parameters from the
- // overlay selector, the game is using old graphics functions.
+ // If kDrawPic is called with 6 parameters from the overlay
+ // selector, the game is using old graphics functions.
// Otherwise, if it's called with 8 parameters, it's using new
- // graphics functions
+ // graphics functions.
_gfxFunctionsType = (argc == 8) ? SCI_VERSION_0_LATE : SCI_VERSION_0_EARLY;
return true;
}
@@ -343,33 +349,36 @@ SciVersion GameFeatures::detectGfxFunctionsType() {
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 (SELECTOR(overlay) != -1) {
+ // The game has an overlay selector, check how it calls kDrawPic
+ // to determine the graphics functions type used
+ if (lookupSelector(_segMan, rmObjAddr, SELECTOR(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)
+ // 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
+ // 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
+ // The game doesn't have an overlay selector, so search for it
+ // manually
searchRoomObj = true;
}
if (searchRoomObj) {
- // If requested, check if any method of the Rm object is calling kDrawPic,
- // as the overlay selector might be missing in demos
+ // 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;
const Object *obj = _segMan->getObject(rmObjAddr);
@@ -380,8 +389,9 @@ SciVersion GameFeatures::detectGfxFunctionsType() {
}
if (!found) {
- // No method of the Rm object is calling kDrawPic, thus the game
- // doesn't have overlays and is using older 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;
}
}
@@ -395,8 +405,20 @@ SciVersion GameFeatures::detectGfxFunctionsType() {
#ifdef ENABLE_SCI32
bool GameFeatures::autoDetectSci21KernelType() {
+ // First, check if the Sound object is loaded
+ reg_t soundObjAddr = _segMan->findObjectByName("Sound");
+ if (soundObjAddr.isNull()) {
+ // Usually, this means that the Sound object isn't loaded yet.
+ // This case doesn't occur in early SCI2.1 games, and we've only
+ // seen it happen in the RAMA demo, thus we can assume that the
+ // game is using a SCI2.1 table
+ warning("autoDetectSci21KernelType(): Sound object not loaded, assuming a SCI2.1 table");
+ _sci21KernelType = SCI_VERSION_2_1;
+ return true;
+ }
+
// Look up the script address
- reg_t addr = getDetectionAddr("Sound", _kernel->_selectorCache.play);
+ reg_t addr = getDetectionAddr("Sound", SELECTOR(play));
if (!addr.segment)
return false;
@@ -408,7 +430,7 @@ bool GameFeatures::autoDetectSci21KernelType() {
int16 opparams[4];
byte extOpcode;
byte opcode;
- offset += readPMachineInstruction(script->_buf + offset, extOpcode, opparams);
+ offset += readPMachineInstruction(script->getBuf(offset), extOpcode, opparams);
opcode = extOpcode >> 1;
// Check for end of script
@@ -419,9 +441,11 @@ bool GameFeatures::autoDetectSci21KernelType() {
uint16 kFuncNum = opparams[0];
// Here we check for the kDoSound opcode that's used in SCI2.1.
- // Finding 0x40 as kDoSound in the Sound::play() function means the game is using
- // the modified SCI2 kernel table found in some older SCI2.1 games (GK2 demo, KQ7 v1.4).
- // Finding 0x75 as kDoSound means the game is using the regular SCI2.1 kernel table.
+ // Finding 0x40 as kDoSound in the Sound::play() function means the
+ // game is using the modified SCI2 kernel table found in some older
+ // SCI2.1 games (GK2 demo, KQ7 v1.4).
+ // Finding 0x75 as kDoSound means the game is using the regular
+ // SCI2.1 kernel table.
if (kFuncNum == 0x40) {
_sci21KernelType = SCI_VERSION_2;
return true;
@@ -448,7 +472,7 @@ SciVersion GameFeatures::detectSci21KernelType() {
bool GameFeatures::autoDetectMoveCountType() {
// Look up the script address
- reg_t addr = getDetectionAddr("Motion", _kernel->_selectorCache.doit);
+ reg_t addr = getDetectionAddr("Motion", SELECTOR(doit));
if (!addr.segment)
return false;
@@ -461,7 +485,7 @@ bool GameFeatures::autoDetectMoveCountType() {
int16 opparams[4];
byte extOpcode;
byte opcode;
- offset += readPMachineInstruction(script->_buf + offset, extOpcode, opparams);
+ offset += readPMachineInstruction(script->getBuf(offset), extOpcode, opparams);
opcode = extOpcode >> 1;
// Check for end of script
@@ -491,7 +515,7 @@ MoveCountType GameFeatures::detectMoveCountType() {
_moveCountType = kIncrementMoveCount;
} else {
if (!autoDetectMoveCountType()) {
- warning("Move count autodetection failed");
+ error("Move count autodetection failed");
_moveCountType = kIncrementMoveCount; // Most games do this, so best guess
}
}
diff --git a/engines/sci/engine/features.h b/engines/sci/engine/features.h
index 77c2f0cff7..5b383746d8 100644
--- a/engines/sci/engine/features.h
+++ b/engines/sci/engine/features.h
@@ -31,6 +31,12 @@
namespace Sci {
+enum MoveCountType {
+ kMoveCountUninitialized,
+ kIgnoreMoveCount,
+ kIncrementMoveCount
+};
+
class GameFeatures {
public:
GameFeatures(SegManager *segMan, Kernel *kernel);
diff --git a/engines/sci/engine/game.cpp b/engines/sci/engine/game.cpp
deleted file mode 100644
index bc10099e52..0000000000
--- a/engines/sci/engine/game.cpp
+++ /dev/null
@@ -1,169 +0,0 @@
-/* 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/system.h"
-#include "common/file.h"
-
-#include "engines/advancedDetector.h" // for ADGF_DEMO
-
-#include "sci/sci.h"
-#include "sci/resource.h"
-#include "sci/engine/features.h"
-#include "sci/engine/state.h"
-#include "sci/engine/kernel.h"
-#include "sci/engine/message.h"
-#include "sci/graphics/gui.h"
-#include "sci/graphics/menu.h"
-#include "sci/sound/audio.h"
-#include "sci/sound/music.h"
-
-namespace Sci {
-
-#ifdef USE_OLD_MUSIC_FUNCTIONS
-int game_init_sound(EngineState *s, int sound_flags, SciVersion soundVersion) {
- if (getSciVersion() > SCI_VERSION_0_LATE)
- sound_flags |= SFX_STATE_FLAG_MULTIPLAY;
-
- s->sfx_init_flags = sound_flags;
- s->_sound.sfx_init(g_sci->getResMan(), sound_flags, soundVersion);
-
- return 0;
-}
-#endif
-
-// Architectural stuff: Init/Unintialize engine
-int script_init_engine(EngineState *s) {
- s->_msgState = new MessageState(s->_segMan);
- s->gc_countdown = GC_INTERVAL - 1;
-
- SegmentId script_000_segment = s->_segMan->getScriptSegment(0, SCRIPT_GET_LOCK);
-
- if (script_000_segment <= 0) {
- debug(2, "Failed to instantiate script.000");
- return 1;
- }
-
- s->script_000 = s->_segMan->getScript(script_000_segment);
-
- s->_segMan->initSysStrings();
-
- s->r_acc = s->r_prev = NULL_REG;
- s->restAdjust = 0;
-
- s->_executionStack.clear(); // Start without any execution stack
- s->execution_stack_base = -1; // No vm is running yet
-
- s->restarting_flags = SCI_GAME_IS_NOT_RESTARTING;
-
- debug(2, "Engine initialized");
-
- return 0;
-}
-
-/*************************************************************/
-/* Game instance stuff: Init/Unitialize state-dependant data */
-/*************************************************************/
-
-int game_init(EngineState *s) {
- // FIXME Use new VM instantiation code all over the place
- DataStack *stack;
-
- stack = s->_segMan->allocateStack(VM_STACK_SIZE, NULL);
- s->stack_base = stack->_entries;
- s->stack_top = stack->_entries + stack->_capacity;
-
- if (!script_instantiate(g_sci->getResMan(), s->_segMan, 0)) {
- warning("game_init(): Could not instantiate script 0");
- return 1;
- }
-
- // 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->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
-
- s->_gameObj = g_sci->getResMan()->findGameObject();
-
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- if (s->sfx_init_flags & SFX_STATE_FLAG_NOSOUND)
- game_init_sound(s, 0, g_sci->_features->detectDoSoundType());
-#endif
-
- // Load game language into printLang property of game object
- // FIXME: It's evil to achieve this as a side effect of a getter.
- // Much better to have an explicit init method for this.
- g_sci->getSciLanguage();
-
- return 0;
-}
-
-int game_exit(EngineState *s) {
- 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
- game_init_sound(s, SFX_STATE_FLAG_NOSOUND, g_sci->_features->detectDoSoundType());
-#else
- g_sci->_audio->stopAllAudio();
- s->_soundCmd->clearPlayList();
-#endif
- }
-
- // Note: It's a bad idea to delete the segment manager here
- // when loading a game.
- // This function is called right after a game is loaded, and
- // the segment manager has already been initialized from the
- // save game. Deleting or resetting it here will result in
- // invalidating the loaded save state
- if (s->restarting_flags & SCI_GAME_IS_RESTARTING_NOW)
- s->_segMan->resetSegMan();
-
- // TODO Free parser segment here
-
- // TODO Free scripts here
-
- // Close all opened file handles
- s->_fileHandles.clear();
- s->_fileHandles.resize(5);
-
- return 0;
-}
-
-} // End of namespace Sci
diff --git a/engines/sci/engine/gc.cpp b/engines/sci/engine/gc.cpp
index c2f1c15776..936b83d760 100644
--- a/engines/sci/engine/gc.cpp
+++ b/engines/sci/engine/gc.cpp
@@ -28,11 +28,9 @@
namespace Sci {
-//#define DEBUG_GC
-
struct WorklistManager {
Common::Array<reg_t> _worklist;
- reg_t_hash_map _map;
+ AddrSet _map;
void push(reg_t reg) {
if (!reg.segment) // No numbers
@@ -46,14 +44,19 @@ struct WorklistManager {
_map.setVal(reg, true);
_worklist.push_back(reg);
}
+
+ void pushArray(const Common::Array<reg_t> &tmp) {
+ for (Common::Array<reg_t>::const_iterator it = tmp.begin(); it != tmp.end(); ++it)
+ push(*it);
+ }
};
-static reg_t_hash_map *normalise_hashmap_ptrs(SegManager *segMan, reg_t_hash_map &nonnormal_map) {
- reg_t_hash_map *normal_map = new reg_t_hash_map();
+static AddrSet *normalizeAddresses(SegManager *segMan, const AddrSet &nonnormal_map) {
+ AddrSet *normal_map = new AddrSet();
- for (reg_t_hash_map::iterator i = nonnormal_map.begin(); i != nonnormal_map.end(); ++i) {
+ for (AddrSet::const_iterator i = nonnormal_map.begin(); i != nonnormal_map.end(); ++i) {
reg_t reg = i->_key;
- SegmentObj *mobj = (reg.segment < segMan->_heap.size()) ? segMan->_heap[reg.segment] : NULL;
+ SegmentObj *mobj = segMan->getSegmentObj(reg.segment);
if (mobj) {
reg = mobj->findCanonicAddress(segMan, reg);
@@ -65,14 +68,9 @@ static reg_t_hash_map *normalise_hashmap_ptrs(SegManager *segMan, reg_t_hash_map
}
-void add_outgoing_refs(void *refcon, reg_t addr) {
- WorklistManager *wm = (WorklistManager *)refcon;
- wm->push(addr);
-}
-
-reg_t_hash_map *find_all_used_references(EngineState *s) {
+AddrSet *findAllActiveReferences(EngineState *s) {
SegManager *segMan = s->_segMan;
- reg_t_hash_map *normal_map = NULL;
+ AddrSet *normal_map = NULL;
WorklistManager wm;
uint i;
@@ -84,22 +82,19 @@ reg_t_hash_map *find_all_used_references(EngineState *s) {
wm.push(s->r_prev);
// Init: Value Stack
// We do this one by hand since the stack doesn't know the current execution stack
- Common::List<ExecStack>::iterator iter;
- {
- iter = s->_executionStack.reverse_begin();
+ Common::List<ExecStack>::iterator iter = s->_executionStack.reverse_begin();
- // Skip fake kernel stack frame if it's on top
- if (((*iter).type == EXEC_STACK_TYPE_KERNEL))
- --iter;
+ // Skip fake kernel stack frame if it's on top
+ if (((*iter).type == EXEC_STACK_TYPE_KERNEL))
+ --iter;
- assert((iter != s->_executionStack.end()) && ((*iter).type != EXEC_STACK_TYPE_KERNEL));
+ assert((iter != s->_executionStack.end()) && ((*iter).type != EXEC_STACK_TYPE_KERNEL));
- ExecStack &xs = *iter;
- reg_t *pos;
+ ExecStack &xs = *iter;
+ reg_t *pos;
- for (pos = s->stack_base; pos < xs.sp; pos++)
- wm.push(*pos);
- }
+ for (pos = s->stack_base; pos < xs.sp; pos++)
+ wm.push(*pos);
debugC(2, kDebugLevelGC, "[GC] -- Finished adding value stack");
@@ -118,24 +113,18 @@ reg_t_hash_map *find_all_used_references(EngineState *s) {
debugC(2, kDebugLevelGC, "[GC] -- Finished adding execution stack");
+ const Common::Array<SegmentObj *> &heap = segMan->getSegments();
+
// Init: Explicitly loaded scripts
- for (i = 1; i < segMan->_heap.size(); i++)
- if (segMan->_heap[i]
- && segMan->_heap[i]->getType() == SEG_TYPE_SCRIPT) {
- Script *script = (Script *)segMan->_heap[i];
+ for (i = 1; i < heap.size(); i++) {
+ if (heap[i] && heap[i]->getType() == SEG_TYPE_SCRIPT) {
+ Script *script = (Script *)heap[i];
if (script->getLockers()) { // Explicitly loaded?
- // Locals, if present
- wm.push(make_reg(script->_localsSegment, 0));
-
- // All objects (may be classes, may be indirectly reachable)
- ObjMap::iterator it;
- const ObjMap::iterator end = script->_objects.end();
- for (it = script->_objects.begin(); it != end; ++it) {
- wm.push(it->_value.getPos());
- }
+ wm.pushArray(script->listObjectReferences());
}
}
+ }
debugC(2, kDebugLevelGC, "[GC] -- Finished explicitly loaded scripts, done with root set");
@@ -146,76 +135,64 @@ reg_t_hash_map *find_all_used_references(EngineState *s) {
wm._worklist.pop_back();
if (reg.segment != stack_seg) { // No need to repeat this one
debugC(2, kDebugLevelGC, "[GC] Checking %04x:%04x", PRINT_REG(reg));
- if (reg.segment < segMan->_heap.size() && segMan->_heap[reg.segment])
- segMan->_heap[reg.segment]->listAllOutgoingReferences(reg, &wm, add_outgoing_refs);
+ if (reg.segment < heap.size() && heap[reg.segment]) {
+ // Valid heap object? Find its outgoing references!
+ wm.pushArray(heap[reg.segment]->listAllOutgoingReferences(reg));
+ }
}
}
// Normalise
- normal_map = normalise_hashmap_ptrs(segMan, wm._map);
+ normal_map = normalizeAddresses(segMan, wm._map);
return normal_map;
}
-struct deallocator_t {
- SegManager *segMan;
- SegmentObj *mobj;
-#ifdef DEBUG_GC
- char *segnames[SEG_TYPE_MAX + 1];
- int segcount[SEG_TYPE_MAX + 1];
-#endif
- reg_t_hash_map *use_map;
-};
-
-void free_unless_used(void *refcon, reg_t addr) {
- deallocator_t *deallocator = (deallocator_t *)refcon;
- reg_t_hash_map *use_map = deallocator->use_map;
-
- if (!use_map->contains(addr)) {
- // Not found -> we can free it
- deallocator->mobj->freeAtAddress(deallocator->segMan, addr);
-#ifdef DEBUG_GC
- debugC(2, kDebugLevelGC, "[GC] Deallocating %04x:%04x", PRINT_REG(addr));
- deallocator->segcount[deallocator->mobj->getType()]++;
-#endif
- }
-
-}
-
void run_gc(EngineState *s) {
- uint seg_nr;
- deallocator_t deallocator;
SegManager *segMan = s->_segMan;
-#ifdef DEBUG_GC
+ // Some debug stuff
debugC(2, kDebugLevelGC, "[GC] Running...");
- memset(&(deallocator.segcount), 0, sizeof(int) * (SEG_TYPE_MAX + 1));
-#endif
-
- deallocator.segMan = segMan;
- deallocator.use_map = find_all_used_references(s);
-
- for (seg_nr = 1; seg_nr < segMan->_heap.size(); seg_nr++) {
- if (segMan->_heap[seg_nr] != NULL) {
- deallocator.mobj = segMan->_heap[seg_nr];
-#ifdef DEBUG_GC
- deallocator.segnames[deallocator.mobj->getType()] = deallocator.mobj->type; // FIXME: add a segment "name"
-#endif
- deallocator.mobj->listAllDeallocatable(seg_nr, &deallocator, free_unless_used);
+ const char *segnames[SEG_TYPE_MAX + 1];
+ int segcount[SEG_TYPE_MAX + 1];
+ memset(segnames, 0, sizeof(segnames));
+ memset(segcount, 0, sizeof(segcount));
+
+ // Compute the set of all segments references currently in use.
+ AddrSet *activeRefs = findAllActiveReferences(s);
+
+ // Iterate over all segments, and check for each whether it
+ // contains stuff that can be collected.
+ const Common::Array<SegmentObj *> &heap = segMan->getSegments();
+ for (uint seg = 1; seg < heap.size(); seg++) {
+ SegmentObj *mobj = heap[seg];
+ if (mobj != NULL) {
+ const SegmentType type = mobj->getType();
+ segnames[type] = SegmentObj::getSegmentTypeName(type);
+
+ // Get a list of all deallocatable objects in this segment,
+ // then free any which are not referenced from somewhere.
+ const Common::Array<reg_t> tmp = mobj->listAllDeallocatable(seg);
+ for (Common::Array<reg_t>::const_iterator it = tmp.begin(); it != tmp.end(); ++it) {
+ const reg_t addr = *it;
+ if (!activeRefs->contains(addr)) {
+ // Not found -> we can free it
+ mobj->freeAtAddress(segMan, addr);
+ debugC(2, kDebugLevelGC, "[GC] Deallocating %04x:%04x", PRINT_REG(addr));
+ segcount[type]++;
+ }
+ }
+
}
}
- delete deallocator.use_map;
+ delete activeRefs;
-#ifdef DEBUG_GC
- {
- int i;
- debugC(2, kDebugLevelGC, "[GC] Summary:");
- for (i = 0; i <= SEG_TYPE_MAX; i++)
- if (deallocator.segcount[i])
- debugC(2, kDebugLevelGC, "\t%d\t* %s", deallocator.segcount[i], deallocator.segnames[i]);
- }
-#endif
+ // Output debug summary of garbage collection
+ debugC(2, kDebugLevelGC, "[GC] Summary:");
+ for (int i = 0; i <= SEG_TYPE_MAX; i++)
+ if (segcount[i])
+ debugC(2, kDebugLevelGC, "\t%d\t* %s", segcount[i], segnames[i]);
}
} // End of namespace Sci
diff --git a/engines/sci/engine/gc.h b/engines/sci/engine/gc.h
index 9f9347ca18..f4318a1453 100644
--- a/engines/sci/engine/gc.h
+++ b/engines/sci/engine/gc.h
@@ -33,29 +33,24 @@
namespace Sci {
-struct reg_t_EqualTo {
- bool operator()(const reg_t& x, const reg_t& y) const {
- return (x.segment == y.segment) && (x.offset == y.offset);
- }
-};
-
struct reg_t_Hash {
uint operator()(const reg_t& x) const {
- return (x.segment << 3) | x.offset;
+ return (x.segment << 3) ^ x.offset ^ (x.offset << 16);
}
};
/*
- * The reg_t_hash_map is actually really a hashset
+ * The AddrSet is a "set" of reg_t values.
+ * We don't have a HashSet type, so we abuse a HashMap for this.
*/
-typedef Common::HashMap<reg_t, bool, reg_t_Hash, reg_t_EqualTo> reg_t_hash_map;
+typedef Common::HashMap<reg_t, bool, reg_t_Hash> AddrSet;
/**
* Finds all used references and normalises them to their memory addresses
* @param s The state to gather all information from
* @return A hash map containing entries for all used references
*/
-reg_t_hash_map *find_all_used_references(EngineState *s);
+AddrSet *findAllActiveReferences(EngineState *s);
/**
* Runs garbage collection on the current system state
diff --git a/engines/sci/engine/kernel.cpp b/engines/sci/engine/kernel.cpp
index 6ebee2dfbd..beb1d3ce35 100644
--- a/engines/sci/engine/kernel.cpp
+++ b/engines/sci/engine/kernel.cpp
@@ -33,10 +33,28 @@
namespace Sci {
-// Default kernel name table
-#define SCI_KNAMES_DEFAULT_ENTRIES_NR 0x89
-
-static const char *sci_default_knames[SCI_KNAMES_DEFAULT_ENTRIES_NR] = {
+// Uncompiled kernel signatures are formed from a string of letters.
+// each corresponding to a type of a parameter (see below).
+// Use small letters to indicate end of sum type.
+// Use capital letters for sum types, e.g.
+// "LNoLr" for a function which takes two arguments:
+// (1) list, node or object
+// (2) list or ref
+#define KSIG_SPEC_LIST 'l'
+#define KSIG_SPEC_NODE 'n'
+#define KSIG_SPEC_OBJECT 'o'
+#define KSIG_SPEC_REF 'r' // Said Specs and strings
+#define KSIG_SPEC_ARITHMETIC 'i'
+#define KSIG_SPEC_NULL 'z'
+#define KSIG_SPEC_ANY '.'
+#define KSIG_SPEC_ELLIPSIS '*' // Arbitrarily more TYPED arguments
+
+#define KSIG_SPEC_SUM_DONE ('a' - 'A')
+
+
+
+/** Default kernel name table. */
+static const char *s_defaultKernelNames[] = {
/*0x00*/ "Load",
/*0x01*/ "UnLoad",
/*0x02*/ "ScriptID",
@@ -152,18 +170,18 @@ static const char *sci_default_knames[SCI_KNAMES_DEFAULT_ENTRIES_NR] = {
/*0x6c*/ "Graph",
/*0x6d*/ "Joystick",
// End of kernel function table for SCI0
- /*0x6e*/ "ShiftScreen",
+ /*0x6e*/ "Dummy", // ShiftScreen
/*0x6f*/ "Palette",
/*0x70*/ "MemorySegment",
/*0x71*/ "Intersections", // MoveCursor (SCI1 late), PalVary (SCI1.1)
/*0x72*/ "Memory",
- /*0x73*/ "ListOps",
+ /*0x73*/ "Dummy", // ListOps
/*0x74*/ "FileIO",
/*0x75*/ "DoAudio",
/*0x76*/ "DoSync",
/*0x77*/ "AvoidPath",
/*0x78*/ "Sort", // StrSplit (SCI01)
- /*0x79*/ "ATan",
+ /*0x79*/ "Dummy", // ATan
/*0x7a*/ "Lock",
/*0x7b*/ "StrSplit",
/*0x7c*/ "GetMessage", // Message (SCI1.1)
@@ -181,227 +199,456 @@ static const char *sci_default_knames[SCI_KNAMES_DEFAULT_ENTRIES_NR] = {
/*0x88*/ "Dummy" // DbugStr
};
-struct SciKernelFunction {
+reg_t kStub(EngineState *s, int argc, reg_t *argv) {
+ Kernel *kernel = g_sci->getKernel();
+ int kernelCallNr = -1;
+
+ Common::List<ExecStack>::iterator callIterator = s->_executionStack.end();
+ if (callIterator != s->_executionStack.begin()) {
+ callIterator--;
+ ExecStack lastCall = *callIterator;
+ kernelCallNr = lastCall.debugSelector;
+ }
+
+ Common::String warningMsg = "Dummy function k" + kernel->getKernelName(kernelCallNr) +
+ Common::String::printf("[%x]", kernelCallNr) +
+ " invoked. Params: " +
+ Common::String::printf("%d", argc) + " (";
+
+ for (int i = 0; i < argc; i++) {
+ warningMsg += Common::String::printf("%04x:%04x", PRINT_REG(argv[i]));
+ warningMsg += (i == argc - 1 ? ")" : ", ");
+ }
+
+ warning("%s", warningMsg.c_str());
+ return s->r_acc;
+}
+
+reg_t kStubNull(EngineState *s, int argc, reg_t *argv) {
+ kStub(s, argc, argv);
+ return NULL_REG;
+}
+
+reg_t kDummy(EngineState *s, int argc, reg_t *argv) {
+ kStub(s, argc, argv);
+ error("Kernel function was called, which was considered to be unused - see log for details");
+}
+
+// [io] -> either integer or object
+// (io) -> optionally integer AND an object
+// (i) -> optional integer
+// . -> any type
+// i* -> optional multiple integers
+// .* -> any parameters afterwards (or none)
+
+// gameID, scriptNr,lvl, object-name, method-name, call, index, replace
+static const SciWorkaroundEntry kAbs_workarounds[] = {
+ { GID_HOYLE1, 1, 0, "room1", "doit", -1, 0, { 2, 0x3e9 } }, // crazy eights - called with objects instead of integers
+ { GID_HOYLE1, 2, 0, "room2", "doit", -1, 0, { 2, 0x3e9 } }, // old maid - called with objects instead of integers
+ { GID_HOYLE1, 3, 0, "room3", "doit", -1, 0, { 2, 0x3e9 } }, // hearts - called with objects instead of integers
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, scriptNr,lvl, object-name, method-name, call, index, replace
+static const SciWorkaroundEntry kDisposeScript_workarounds[] = {
+ { GID_QFG1, 64, 0, "rm64", "dispose", -1, 0, { 1, 0 } }, // when leaving graveyard, parameter 0 is an object
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, scriptNr,lvl, object-name, method-name, call, index, replace
+static const SciWorkaroundEntry kDoSoundFade_workarounds[] = {
+ { GID_KQ6, 989, 0, "globalSound", "fade", -1, 0, { 0, 0 } }, // during intro, parameter 4 is an object
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, scriptNr,lvl, object-name, method-name, call, index, replace
+static const SciWorkaroundEntry kGraphRestoreBox_workarounds[] = {
+ { GID_LSL6, 85, 0, "rScroller", "hide", -1, 0, { 0, 0 } }, // happens when restoring (sometimes), same as the one below
+ { GID_LSL6, 85, 0, "lScroller", "hide", -1, 0, { 0, 0 } }, // happens when restoring (sometimes), same as the one below
+ { GID_LSL6, 86, 0, "LL6Inv", "show", -1, 0, { 0, 0 } }, // happens when restoring, is called with hunk segment, but hunk is not allocated at that time
+ // ^^ TODO: check, if this is really a script error or an issue with our restore code
+ { GID_LSL6, 86, 0, "LL6Inv", "hide", -1, 0, { 0, 0 } }, // happens during the game, gets called with 1 extra parameter
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, scriptNr,lvl, object-name, method-name, call, index, replace
+static const SciWorkaroundEntry kGraphFillBoxForeground_workarounds[] = {
+ { GID_LSL6, 0, 0, "LSL6", "hideControls", -1, 0, { 0, 0 } }, // happens when giving the bungee key to merrily - gets called with additional 5th parameter
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, scriptNr,lvl, object-name, method-name, call, index, replace
+static const SciWorkaroundEntry kGraphFillBoxAny_workarounds[] = {
+ { GID_SQ4, 818, 0, "iconTextSwitch", "show", -1, 0, { 0, 0 } }, // game menu "text/speech" display - parameter 5 is missing, but the right color number is on the stack
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, scriptNr,lvl, object-name, method-name, call, index, replace
+static const SciWorkaroundEntry kUnLoad_workarounds[] = {
+ { GID_LSL6, 130, 0, "recruitLarryScr", "changeState", -1, 0, { 1, 0 } }, // during intro, a 3rd parameter is passed by accident
+ { GID_LSL6, 740, 0, "showCartoon", "changeState", -1, 0, { 1, 0 } }, // during ending, 4 additional parameters are passed by accident
+ { GID_SQ1, 303, 0, "slotGuy", "dispose", -1, 0, { 1, 0 } }, // when leaving ulence flats bar, parameter 1 is not passed - script error
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+struct SciKernelMapSubEntry {
+ SciVersion fromVersion;
+ SciVersion toVersion;
+
+ uint16 id;
+
const char *name;
- KernelFunc *fun; /* The actual function */
- const char *signature; /* kfunct signature */
+ KernelFunctionCall *function;
+
+ const char *signature;
+ const SciWorkaroundEntry *workarounds;
+};
+
+#define SCI_SUBOPENTRY_TERMINATOR { SCI_VERSION_NONE, SCI_VERSION_NONE, 0, NULL, NULL, NULL, NULL }
+
+
+#define SIG_SCIALL SCI_VERSION_NONE, SCI_VERSION_NONE
+#define SIG_SCI0 SCI_VERSION_NONE, SCI_VERSION_01
+#define SIG_SCI1 SCI_VERSION_1_EGA, SCI_VERSION_1_LATE
+#define SIG_SCI11 SCI_VERSION_1_1, SCI_VERSION_1_1
+#define SIG_SCI16 SCI_VERSION_NONE, SCI_VERSION_1_1
+#define SIG_SCI32 SCI_VERSION_2, SCI_VERSION_NONE
+
+// SCI-Sound-Version
+#define SIG_SOUNDSCI0 SCI_VERSION_0_EARLY, SCI_VERSION_0_LATE
+#define SIG_SOUNDSCI1EARLY SCI_VERSION_1_EARLY, SCI_VERSION_1_EARLY
+#define SIG_SOUNDSCI1LATE SCI_VERSION_1_LATE, SCI_VERSION_1_LATE
+
+#define SIGFOR_ALL 0x3f
+#define SIGFOR_DOS 1 << 0
+#define SIGFOR_PC98 1 << 1
+#define SIGFOR_WIN 1 << 2
+#define SIGFOR_MAC 1 << 3
+#define SIGFOR_AMIGA 1 << 4
+#define SIGFOR_ATARI 1 << 5
+#define SIGFOR_PC SIGFOR_DOS|SIGFOR_WIN
+
+#define SIG_EVERYWHERE SIG_SCIALL, SIGFOR_ALL
+
+#define MAP_CALL(_name_) #_name_, k##_name_
+
+// version, subId, function-mapping, signature, workarounds
+static const SciKernelMapSubEntry kDoSound_subops[] = {
+ { SIG_SOUNDSCI0, 0, MAP_CALL(DoSoundInit), "o", NULL },
+ { SIG_SOUNDSCI0, 1, MAP_CALL(DoSoundPlay), "o", NULL },
+ { SIG_SOUNDSCI0, 2, MAP_CALL(DoSoundDummy), "o", NULL },
+ { SIG_SOUNDSCI0, 3, MAP_CALL(DoSoundDispose), "o", NULL },
+ { SIG_SOUNDSCI0, 4, MAP_CALL(DoSoundMute), "(i)", NULL },
+ { SIG_SOUNDSCI0, 5, MAP_CALL(DoSoundStop), "o", NULL },
+ { SIG_SOUNDSCI0, 6, MAP_CALL(DoSoundPause), "i", NULL },
+ { SIG_SOUNDSCI0, 7, MAP_CALL(DoSoundResume), "", NULL },
+ { SIG_SOUNDSCI0, 8, MAP_CALL(DoSoundMasterVolume), "(i)", NULL },
+ { SIG_SOUNDSCI0, 9, MAP_CALL(DoSoundUpdate), "o", NULL },
+ { SIG_SOUNDSCI0, 10, MAP_CALL(DoSoundFade), "o", NULL },
+ { SIG_SOUNDSCI0, 11, MAP_CALL(DoSoundGetPolyphony), "", NULL },
+ { SIG_SOUNDSCI0, 12, MAP_CALL(DoSoundStopAll), "", NULL },
+ { SIG_SOUNDSCI1EARLY, 0, MAP_CALL(DoSoundMasterVolume), NULL, NULL },
+ { SIG_SOUNDSCI1EARLY, 1, MAP_CALL(DoSoundMute), NULL, NULL },
+ { SIG_SOUNDSCI1EARLY, 2, MAP_CALL(DoSoundDummy), NULL, NULL },
+ { SIG_SOUNDSCI1EARLY, 3, MAP_CALL(DoSoundGetPolyphony), NULL, NULL },
+ { SIG_SOUNDSCI1EARLY, 4, MAP_CALL(DoSoundUpdate), NULL, NULL },
+ { SIG_SOUNDSCI1EARLY, 5, MAP_CALL(DoSoundInit), NULL, NULL },
+ { SIG_SOUNDSCI1EARLY, 6, MAP_CALL(DoSoundDispose), NULL, NULL },
+ { SIG_SOUNDSCI1EARLY, 7, MAP_CALL(DoSoundPlay), "oi", NULL },
+ { SIG_SOUNDSCI1EARLY, 8, MAP_CALL(DoSoundStop), NULL, NULL },
+ { SIG_SOUNDSCI1EARLY, 9, MAP_CALL(DoSoundPause), "[o0]i", NULL },
+ { SIG_SOUNDSCI1EARLY, 10, MAP_CALL(DoSoundFade), "oiiii", NULL },
+ { SIG_SOUNDSCI1EARLY, 11, MAP_CALL(DoSoundUpdateCues), "o", NULL },
+ { SIG_SOUNDSCI1EARLY, 12, MAP_CALL(DoSoundSendMidi), "oiiii", NULL },
+ { SIG_SOUNDSCI1EARLY, 13, MAP_CALL(DoSoundReverb), "oi", NULL },
+ { SIG_SOUNDSCI1EARLY, 14, MAP_CALL(DoSoundSetHold), "oi", NULL },
+ { SIG_SOUNDSCI1EARLY, 15, MAP_CALL(DoSoundDummy), "", NULL },
+ // ^^ Longbow demo
+ { SIG_SOUNDSCI1LATE, 0, MAP_CALL(DoSoundMasterVolume), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 1, MAP_CALL(DoSoundMute), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 2, MAP_CALL(DoSoundDummy), "", NULL },
+ { SIG_SOUNDSCI1LATE, 3, MAP_CALL(DoSoundGetPolyphony), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 4, MAP_CALL(DoSoundGetAudioCapability), "", NULL },
+ { SIG_SOUNDSCI1LATE, 5, MAP_CALL(DoSoundSuspend), "i", NULL },
+ { SIG_SOUNDSCI1LATE, 6, MAP_CALL(DoSoundInit), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 7, MAP_CALL(DoSoundDispose), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 8, MAP_CALL(DoSoundPlay), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 9, MAP_CALL(DoSoundStop), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 10, MAP_CALL(DoSoundPause), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 11, MAP_CALL(DoSoundFade), "oiiii(i)", kDoSoundFade_workarounds },
+ { SIG_SOUNDSCI1LATE, 12, MAP_CALL(DoSoundSetHold), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 13, MAP_CALL(DoSoundDummy), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 14, MAP_CALL(DoSoundSetVolume), "oi", NULL },
+ { SIG_SOUNDSCI1LATE, 15, MAP_CALL(DoSoundSetPriority), "oi", NULL },
+ { SIG_SOUNDSCI1LATE, 16, MAP_CALL(DoSoundSetLoop), "oi", NULL },
+ { SIG_SOUNDSCI1LATE, 17, MAP_CALL(DoSoundUpdateCues), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 18, MAP_CALL(DoSoundSendMidi), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 19, MAP_CALL(DoSoundReverb), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 20, MAP_CALL(DoSoundUpdate), NULL, NULL },
+ SCI_SUBOPENTRY_TERMINATOR
};
-#define DEFUN(name, fun, sig) {name, fun, sig}
-
-SciKernelFunction kfunct_mappers[] = {
- /*00*/ DEFUN("Load", kLoad, "iii*"),
- /*01*/ DEFUN("UnLoad", kUnLoad, "i.*"), // Work around SQ1 bug, when exiting the Ulence flats bar
- /*02*/ DEFUN("ScriptID", kScriptID, "Ioi*"),
- /*03*/ DEFUN("DisposeScript", kDisposeScript, "Oii*"), // Work around QfG1 bug
- /*04*/ DEFUN("Clone", kClone, "o"),
- /*05*/ DEFUN("DisposeClone", kDisposeClone, "o"),
- /*06*/ DEFUN("IsObject", kIsObject, "."),
- /*07*/ DEFUN("RespondsTo", kRespondsTo, ".i"),
- /*08*/ DEFUN("DrawPic", kDrawPic, "i*"),
-
- /*0a*/ DEFUN("PicNotValid", kPicNotValid, "i*"),
- /*0b*/ DEFUN("Animate", kAnimate, "LI*"), // More like (li?)?
- /*0c*/ DEFUN("SetNowSeen", kSetNowSeen, "oi*"), // The second parameter is ignored
- /*0d*/ DEFUN("NumLoops", kNumLoops, "o"),
- /*0e*/ DEFUN("NumCels", kNumCels, "o"),
- /*0f*/ DEFUN("CelWide", kCelWide, "iOi*"),
- /*10*/ DEFUN("CelHigh", kCelHigh, "iOi*"),
- /*11*/ DEFUN("DrawCel", kDrawCel, "iiiiii*i*r*"),
- /*12*/ DEFUN("AddToPic", kAddToPic, "Il*"),
- // FIXME: signature check removed (set to .*) as kNewWindow is different in Mac versions
- /*13*/ DEFUN("NewWindow", kNewWindow, "*."),
- ///*13*/ DEFUN("NewWindow", kNewWindow, "iiiiZRi*"),
- /*14*/ DEFUN("GetPort", kGetPort, ""),
- /*15*/ DEFUN("SetPort", kSetPort, "ii*"),
- /*16*/ DEFUN("DisposeWindow", kDisposeWindow, "ii*"),
- /*17*/ DEFUN("DrawControl", kDrawControl, "o"),
- /*18*/ DEFUN("HiliteControl", kHiliteControl, "o"),
- /*19*/ DEFUN("EditControl", kEditControl, "ZoZo"),
- /*1a*/ DEFUN("TextSize", kTextSize, "rZrii*r*"),
- /*1b*/ DEFUN("Display", kDisplay, ".*"),
- /*1c*/ DEFUN("GetEvent", kGetEvent, "ioi*"), // Mac versions pass an extra 3rd parameter (ignored - always 0?)
- /*1d*/ DEFUN("GlobalToLocal", kGlobalToLocal, "oo*"),
- /*1e*/ DEFUN("LocalToGlobal", kLocalToGlobal, "oo*"),
- /*1f*/ DEFUN("MapKeyToDir", kMapKeyToDir, "o"),
- /*20*/ DEFUN("DrawMenuBar", kDrawMenuBar, "i"),
- /*21*/ DEFUN("MenuSelect", kMenuSelect, "oi*"),
- /*22*/ DEFUN("AddMenu", kAddMenu, "rr"),
- /*23*/ DEFUN("DrawStatus", kDrawStatus, "Zri*"),
- /*24*/ DEFUN("Parse", kParse, "ro"),
- /*25*/ DEFUN("Said", kSaid, "Zr"),
- /*26*/ DEFUN("SetSynonyms", kSetSynonyms, "o"),
- /*27*/ DEFUN("HaveMouse", kHaveMouse, ""),
- /*28*/ DEFUN("SetCursor", kSetCursor, "i*"),
- // FIXME: The number 0x28 occurs twice :-)
- /*28*/ DEFUN("MoveCursor", kMoveCursor, "ii"),
- /*29*/ DEFUN("FOpen", kFOpen, "ri"),
- /*2a*/ DEFUN("FPuts", kFPuts, "ir"),
- /*2b*/ DEFUN("FGets", kFGets, "rii"),
- /*2c*/ DEFUN("FClose", kFClose, "i"),
- /*2d*/ DEFUN("SaveGame", kSaveGame, "rirr*"),
- /*2e*/ DEFUN("RestoreGame", kRestoreGame, "rir*"),
- /*2f*/ DEFUN("RestartGame", kRestartGame, ""),
- /*30*/ DEFUN("GameIsRestarting", kGameIsRestarting, "i*"),
- /*31*/ DEFUN("DoSound", kDoSound, "iIo*"),
- /*32*/ DEFUN("NewList", kNewList, ""),
- /*33*/ DEFUN("DisposeList", kDisposeList, "l"),
- /*34*/ DEFUN("NewNode", kNewNode, ".."),
- /*35*/ DEFUN("FirstNode", kFirstNode, "Zl"),
- /*36*/ DEFUN("LastNode", kLastNode, "l"),
- /*37*/ DEFUN("EmptyList", kEmptyList, "l"),
- /*38*/ DEFUN("NextNode", kNextNode, "n"),
- /*39*/ DEFUN("PrevNode", kPrevNode, "n"),
- /*3a*/ DEFUN("NodeValue", kNodeValue, "Zn"),
- /*3b*/ DEFUN("AddAfter", kAddAfter, "lnn"),
- /*3c*/ DEFUN("AddToFront", kAddToFront, "ln"),
- /*3d*/ DEFUN("AddToEnd", kAddToEnd, "ln"),
- /*3e*/ DEFUN("FindKey", kFindKey, "l."),
- /*3f*/ DEFUN("DeleteKey", kDeleteKey, "l."),
- /*40*/ DEFUN("Random", kRandom, "i*"),
- /*41*/ DEFUN("Abs", kAbs, "Oi"),
- /*42*/ DEFUN("Sqrt", kSqrt, "i"),
- /*43*/ DEFUN("GetAngle", kGetAngle, "iiiii*"), // occasionally KQ6 passes a 5th argument by mistake
- /*44*/ DEFUN("GetDistance", kGetDistance, "iiiii*"),
- /*45*/ DEFUN("Wait", kWait, "i"),
- /*46*/ DEFUN("GetTime", kGetTime, "i*"),
- /*47*/ DEFUN("StrEnd", kStrEnd, "r"),
- /*48*/ DEFUN("StrCat", kStrCat, "rr"),
- /*49*/ DEFUN("StrCmp", kStrCmp, "rri*"),
- /*4a*/ DEFUN("StrLen", kStrLen, "Zr"),
- /*4b*/ DEFUN("StrCpy", kStrCpy, "rZri*"),
- /*4c*/ DEFUN("Format", kFormat, "r.*"),
- /*4d*/ DEFUN("GetFarText", kGetFarText, "iiZr"),
- /*4e*/ DEFUN("ReadNumber", kReadNumber, "r"),
- /*4f*/ DEFUN("BaseSetter", kBaseSetter, "o"),
- /*50*/ DEFUN("DirLoop", kDirLoop, "oi"),
- // Opcode 51 is defined twice for a reason: In older SCI versions
- // it is CanBeHere, whereas in newer version it is CantBeHere
- /*51*/ DEFUN("CanBeHere", kCanBeHere, "ol*"),
- /*51*/ DEFUN("CantBeHere", kCantBeHere, "ol*"),
- /*52*/ DEFUN("OnControl", kOnControl, "i*"),
- /*53*/ DEFUN("InitBresen", kInitBresen, "oi*"),
- /*54*/ DEFUN("DoBresen", kDoBresen, "o"),
- /*55*/ DEFUN("DoAvoider", kDoAvoider, "o"),
- /*56*/ DEFUN("SetJump", kSetJump, "oiii"),
- /*57*/ DEFUN("SetDebug", kSetDebug, "i*"),
- /*5c*/ DEFUN("MemoryInfo", kMemoryInfo, "i"),
- /*5f*/ DEFUN("GetMenu", kGetMenu, "i."),
- /*60*/ DEFUN("SetMenu", kSetMenu, "i.*"),
- /*61*/ DEFUN("GetSaveFiles", kGetSaveFiles, "rrr"),
- /*62*/ DEFUN("GetCWD", kGetCWD, "r"),
- /*63*/ DEFUN("CheckFreeSpace", kCheckFreeSpace, "r.*"),
- /*64*/ DEFUN("ValidPath", kValidPath, "r"),
- /*65*/ DEFUN("CoordPri", kCoordPri, "ii*"),
- /*66*/ DEFUN("StrAt", kStrAt, "rii*"),
- /*67*/ DEFUN("DeviceInfo", kDeviceInfo, "i.*"),
- /*68*/ DEFUN("GetSaveDir", kGetSaveDir, ".*"), // accepts a parameter in SCI2+ games
- /*69*/ DEFUN("CheckSaveGame", kCheckSaveGame, ".*"),
- /*6a*/ DEFUN("ShakeScreen", kShakeScreen, "ii*"),
- /*6b*/ DEFUN("FlushResources", kFlushResources, "i"),
- /*6c*/ DEFUN("TimesSin", kTimesSin, "ii"),
- /*6d*/ DEFUN("TimesCos", kTimesCos, "ii"),
- /*6e*/ DEFUN("6e", kTimesSin, "ii"),
- /*6f*/ DEFUN("6f", kTimesCos, "ii"),
- /*70*/ DEFUN("Graph", kGraph, ".*"),
- /*71*/ DEFUN("Joystick", kJoystick, ".*"),
-
- // Experimental functions
- /*74*/ DEFUN("FileIO", kFileIO, "i.*"),
- /*(?)*/ DEFUN("Memory", kMemory, "i.*"),
- /*(?)*/ DEFUN("Sort", kSort, "ooo"),
- /*(?)*/ DEFUN("AvoidPath", kAvoidPath, "ii.*"),
- /*(?)*/ DEFUN("Lock", kLock, "iii*"),
- /*(?)*/ DEFUN("Palette", kPalette, "i.*"),
- /*(?)*/ DEFUN("IsItSkip", kIsItSkip, "iiiii"),
- /*7b*/ DEFUN("StrSplit", kStrSplit, "rrZr"),
-
- // Non-experimental functions without a fixed ID
- DEFUN("CosMult", kTimesCos, "ii"),
- DEFUN("SinMult", kTimesSin, "ii"),
-
- // Misc functions
- /*(?)*/ DEFUN("CosDiv", kCosDiv, "ii"),
- /*(?)*/ DEFUN("PriCoord", kPriCoord, "i"),
- /*(?)*/ DEFUN("SinDiv", kSinDiv, "ii"),
- /*(?)*/ DEFUN("TimesCot", kTimesCot, "ii"),
- /*(?)*/ DEFUN("TimesTan", kTimesTan, "ii"),
- DEFUN("Message", kMessage, ".*"),
- DEFUN("GetMessage", kGetMessage, "iiir"),
- DEFUN("DoAudio", kDoAudio, ".*"),
- 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, ".*"),
- DEFUN("TextColors", kTextColors, ".*"),
- DEFUN("TextFonts", kTextFonts, ".*"),
- DEFUN("Portrait", kPortrait, ".*"),
+// version, subId, function-mapping, signature, workarounds
+static const SciKernelMapSubEntry kGraph_subops[] = {
+ { SIG_SCI32, 1, MAP_CALL(StubNull), "", NULL }, // called by gk1 sci32 right at the start
+ { SIG_SCIALL, 2, MAP_CALL(GraphGetColorCount), "", NULL },
+ // 3 - set palette via resource
+ { SIG_SCIALL, 4, MAP_CALL(GraphDrawLine), "iiiii(i)(i)", NULL },
+ // 5 - nop
+ // 6 - draw pattern
+ { SIG_SCIALL, 7, MAP_CALL(GraphSaveBox), "iiiii", NULL },
+ { SIG_SCIALL, 8, MAP_CALL(GraphRestoreBox), "[r0!]", kGraphRestoreBox_workarounds },
+ // ^ this may get called with invalid references, we check them within restoreBits() and sierra sci behaves the same
+ { SIG_SCIALL, 9, MAP_CALL(GraphFillBoxBackground), "iiii", NULL },
+ { SIG_SCIALL, 10, MAP_CALL(GraphFillBoxForeground), "iiii", kGraphFillBoxForeground_workarounds },
+ { SIG_SCIALL, 11, MAP_CALL(GraphFillBoxAny), "iiiiii(i)(i)", kGraphFillBoxAny_workarounds },
+ { SIG_SCI11, 12, MAP_CALL(GraphUpdateBox), "iiii(i)(r0)", NULL }, // kq6 hires
+ { SIG_SCIALL, 12, MAP_CALL(GraphUpdateBox), "iiii(i)", NULL },
+ { SIG_SCIALL, 13, MAP_CALL(GraphRedrawBox), "iiii", NULL },
+ { SIG_SCIALL, 14, MAP_CALL(GraphAdjustPriority), "ii", NULL },
+ { SIG_SCI11, 15, MAP_CALL(GraphSaveUpscaledHiresBox), "iiii", NULL }, // kq6 hires
+ SCI_SUBOPENTRY_TERMINATOR
+};
-#ifdef ENABLE_SCI32
- // SCI2 Kernel Functions
- DEFUN("IsHiRes", kIsHiRes, ""),
- DEFUN("Array", kArray, ".*"),
- DEFUN("ListAt", kListAt, "li"),
- DEFUN("String", kString, ".*"),
- DEFUN("AddScreenItem", kAddScreenItem, "o"),
- DEFUN("UpdateScreenItem", kUpdateScreenItem, "o"),
- DEFUN("DeleteScreenItem", kDeleteScreenItem, "o"),
- DEFUN("AddPlane", kAddPlane, "o"),
- DEFUN("DeletePlane", kDeletePlane, "o"),
- DEFUN("UpdatePlane", kUpdatePlane, "o"),
- DEFUN("RepaintPlane", kRepaintPlane, "o"),
- DEFUN("GetHighPlanePri", kGetHighPlanePri, ""),
- DEFUN("FrameOut", kFrameOut, ""),
- DEFUN("ListEachElementDo", kListEachElementDo, "li.*"),
- DEFUN("ListFirstTrue", kListFirstTrue, "li.*"),
- DEFUN("ListAllTrue", kListAllTrue, "li.*"),
- DEFUN("ListIndexOf", kListIndexOf, "lZo"),
- DEFUN("OnMe", kOnMe, "iio.*"),
- DEFUN("InPolygon", kInPolygon, "iio"),
- DEFUN("CreateTextBitmap", kCreateTextBitmap, "i.*"),
-
- // SCI2.1 Kernel Functions
- DEFUN("Save", kSave, ".*"),
- DEFUN("List", kList, ".*"),
- DEFUN("Robot", kRobot, ".*"),
- DEFUN("IsOnMe", kOnMe, "iio.*"), // TODO: this seems right, but verify...
+// version, subId, function-mapping, signature, workarounds
+static const SciKernelMapSubEntry kPalVary_subops[] = {
+ { SIG_SCIALL, 0, MAP_CALL(PalVaryInit), "ii(i)(i)", NULL },
+ { SIG_SCIALL, 1, MAP_CALL(PalVaryReverse), "(i)(i)(i)", NULL },
+ { SIG_SCIALL, 2, MAP_CALL(PalVaryGetCurrentStep), "", NULL },
+ { SIG_SCIALL, 3, MAP_CALL(PalVaryDeinit), "", NULL },
+ { SIG_SCIALL, 4, MAP_CALL(PalVaryChangeTarget), "i", NULL },
+ { SIG_SCIALL, 5, MAP_CALL(PalVaryChangeTicks), "i", NULL },
+ { SIG_SCIALL, 6, MAP_CALL(PalVaryPauseResume), "i", NULL },
+ { SIG_SCI32, 8, MAP_CALL(PalVaryUnknown), "", NULL },
+ SCI_SUBOPENTRY_TERMINATOR
+};
-#endif
+// version, subId, function-mapping, signature, workarounds
+static const SciKernelMapSubEntry kPalette_subops[] = {
+ { SIG_SCIALL, 1, MAP_CALL(PaletteSetFromResource), "i(i)", NULL },
+ { SIG_SCIALL, 2, MAP_CALL(PaletteSetFlag), "iii", NULL },
+ { SIG_SCIALL, 3, MAP_CALL(PaletteUnsetFlag), "iii", NULL },
+ { SIG_SCIALL, 4, MAP_CALL(PaletteSetIntensity), "iii(i)", NULL },
+ { SIG_SCIALL, 5, MAP_CALL(PaletteFindColor), "iii", NULL },
+ { SIG_SCIALL, 6, MAP_CALL(PaletteAnimate), "i*", NULL },
+ { SIG_SCIALL, 7, MAP_CALL(PaletteSave), "", NULL },
+ { SIG_SCIALL, 8, MAP_CALL(PaletteRestore), "i", NULL },
+ SCI_SUBOPENTRY_TERMINATOR
+};
- // its a stub, but its needed for Pharkas to work
- DEFUN("PalVary", kPalVary, "ii*"),
- DEFUN("AssertPalette", kAssertPalette, "i"),
-
-#if 0
- // Stub functions
- /*09*/ DEFUN("Show", kShow, "i"),
- DEFUN("ShiftScreen", kShiftScreen, ".*"),
- DEFUN("ListOps", kListOps, ".*"),
- DEFUN("ATan", kATan, ".*"),
- DEFUN("Record", kRecord, ".*"),
- DEFUN("PlayBack", kPlayBack, ".*"),
- DEFUN("DbugStr", kDbugStr, ".*"),
-#endif
+struct SciKernelMapEntry {
+ const char *name;
+ KernelFunctionCall *function;
+
+ SciVersion fromVersion;
+ SciVersion toVersion;
+ byte forPlatform;
+
+ const char *signature;
+ const SciKernelMapSubEntry *subFunctions;
+ const SciWorkaroundEntry *workarounds;
+};
+
+// name, version/platform, signature, sub-signatures, workarounds
+static SciKernelMapEntry s_kernelMap[] = {
+ { MAP_CALL(Abs), SIG_EVERYWHERE, "i", NULL, kAbs_workarounds },
+ { MAP_CALL(AddAfter), SIG_EVERYWHERE, "lnn", NULL, NULL },
+ { MAP_CALL(AddMenu), SIG_EVERYWHERE, "rr", NULL, NULL },
+ { MAP_CALL(AddToEnd), SIG_EVERYWHERE, "ln", NULL, NULL },
+ { MAP_CALL(AddToFront), SIG_EVERYWHERE, "ln", NULL, NULL },
+ { MAP_CALL(AddToPic), SIG_EVERYWHERE, "[il](iiiiii)", NULL, NULL },
+ { MAP_CALL(Animate), SIG_EVERYWHERE, "(l0)(i)", NULL, NULL },
+ { MAP_CALL(AssertPalette), SIG_EVERYWHERE, "i", NULL, NULL },
+ { MAP_CALL(AvoidPath), SIG_EVERYWHERE, "ii(.*)", NULL, NULL },
+ { MAP_CALL(BaseSetter), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(CanBeHere), SIG_EVERYWHERE, "o(l)", NULL, NULL },
+ { MAP_CALL(CantBeHere), SIG_EVERYWHERE, "o(l)", NULL, NULL },
+ { MAP_CALL(CelHigh), SIG_EVERYWHERE, "ii(i)", NULL, NULL },
+ { MAP_CALL(CelWide), SIG_EVERYWHERE, "ii(i)", NULL, NULL },
+ { MAP_CALL(CheckFreeSpace), SIG_SCI32, SIGFOR_ALL, "r.*", NULL, NULL },
+ { MAP_CALL(CheckFreeSpace), SIG_EVERYWHERE, "r", NULL, NULL },
+ { MAP_CALL(CheckSaveGame), SIG_EVERYWHERE, ".*", NULL, NULL },
+ { MAP_CALL(Clone), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(CoordPri), SIG_EVERYWHERE, "i(i)", NULL, NULL },
+ { MAP_CALL(CosDiv), SIG_EVERYWHERE, "ii", NULL, NULL },
+ { MAP_CALL(DeleteKey), SIG_EVERYWHERE, "l.", NULL, NULL },
+ { MAP_CALL(DeviceInfo), SIG_EVERYWHERE, "i(r)(r)(i)", NULL, NULL }, // subop
+ { MAP_CALL(Display), SIG_EVERYWHERE, "[ir]([ir!]*)", NULL, NULL },
+ // ^ we allow invalid references here, because kDisplay gets called with those in e.g. pq3 during intro
+ // restoreBits() checks and skips invalid handles, so that's fine. Sierra SCI behaved the same
+ { MAP_CALL(DirLoop), SIG_EVERYWHERE, "oi", NULL, NULL },
+ { MAP_CALL(DisposeClone), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(DisposeList), SIG_EVERYWHERE, "l", NULL, NULL },
+ { MAP_CALL(DisposeScript), SIG_EVERYWHERE, "i(i*)", NULL, kDisposeScript_workarounds },
+ { MAP_CALL(DisposeWindow), SIG_EVERYWHERE, "i(i)", NULL, NULL },
+ { MAP_CALL(DoAudio), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop
+ { MAP_CALL(DoAvoider), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(DoBresen), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(DoSound), SIG_EVERYWHERE, "i([io])(i)(ii[io])(i)", kDoSound_subops, NULL },
+ { MAP_CALL(DoSync), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop
+ { MAP_CALL(DrawCel), SIG_SCI11, SIGFOR_PC, "iiiii(i)(i)(r0)", NULL, NULL }, // for kq6 hires
+ { MAP_CALL(DrawCel), SIG_EVERYWHERE, "iiiii(i)(i)", NULL, NULL },
+ { MAP_CALL(DrawControl), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(DrawMenuBar), SIG_EVERYWHERE, "i", NULL, NULL },
+ { MAP_CALL(DrawPic), SIG_EVERYWHERE, "i(i)(i)(i)", NULL, NULL },
+ { MAP_CALL(DrawStatus), SIG_EVERYWHERE, "[r0](i)(i)", NULL, NULL },
+ { MAP_CALL(EditControl), SIG_EVERYWHERE, "[o0][o0]", NULL, NULL },
+ { MAP_CALL(Empty), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(EmptyList), SIG_EVERYWHERE, "l", NULL, NULL },
+ { MAP_CALL(FClose), SIG_EVERYWHERE, "i", NULL, NULL },
+ { MAP_CALL(FGets), SIG_EVERYWHERE, "rii", NULL, NULL },
+ { MAP_CALL(FOpen), SIG_EVERYWHERE, "ri", NULL, NULL },
+ { MAP_CALL(FPuts), SIG_EVERYWHERE, "ir", NULL, NULL },
+ { MAP_CALL(FileIO), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop
+ { MAP_CALL(FindKey), SIG_EVERYWHERE, "l.", NULL, NULL },
+ { MAP_CALL(FirstNode), SIG_EVERYWHERE, "[l0]", NULL, NULL },
+ { MAP_CALL(FlushResources), SIG_EVERYWHERE, "i", NULL, NULL },
+ { MAP_CALL(Format), SIG_EVERYWHERE, "r(.*)", NULL, NULL },
+ { MAP_CALL(GameIsRestarting), SIG_EVERYWHERE, "(i)", NULL, NULL },
+ { MAP_CALL(GetAngle), SIG_EVERYWHERE, "iiii", NULL, NULL },
+ // ^^ FIXME - occasionally KQ6 passes a 5th argument by mistake
+ { MAP_CALL(GetCWD), SIG_EVERYWHERE, "r", NULL, NULL },
+ { MAP_CALL(GetDistance), SIG_EVERYWHERE, "ii(i)(i)(i)(i)", NULL, NULL },
+ { MAP_CALL(GetEvent), SIG_SCIALL, SIGFOR_MAC, "io(i*)", NULL, NULL },
+ { MAP_CALL(GetEvent), SIG_EVERYWHERE, "io", NULL, NULL },
+ { MAP_CALL(GetFarText), SIG_EVERYWHERE, "ii[r0]", NULL, NULL },
+ { MAP_CALL(GetMenu), SIG_EVERYWHERE, "i.", NULL, NULL },
+ { MAP_CALL(GetMessage), SIG_EVERYWHERE, "iiir", NULL, NULL },
+ { MAP_CALL(GetPort), SIG_EVERYWHERE, "", NULL, NULL },
+ { MAP_CALL(GetSaveDir), SIG_SCI32, SIGFOR_ALL, "(r*)", NULL, NULL },
+ { MAP_CALL(GetSaveDir), SIG_EVERYWHERE, "", NULL, NULL },
+ { MAP_CALL(GetSaveFiles), SIG_EVERYWHERE, "rrr", NULL, NULL },
+ { MAP_CALL(GetTime), SIG_EVERYWHERE, "(i)", NULL, NULL },
+ { MAP_CALL(GlobalToLocal), SIG_SCI32, SIGFOR_ALL, "oo", NULL, NULL },
+ { MAP_CALL(GlobalToLocal), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(Graph), SIG_EVERYWHERE, NULL, kGraph_subops, NULL },
+ { MAP_CALL(HaveMouse), SIG_EVERYWHERE, "", NULL, NULL },
+ { MAP_CALL(HiliteControl), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(InitBresen), SIG_EVERYWHERE, "o(i)", NULL, NULL },
+ { MAP_CALL(Intersections), SIG_EVERYWHERE, "iiiiriiiri", NULL, NULL },
+ { MAP_CALL(IsItSkip), SIG_EVERYWHERE, "iiiii", NULL, NULL },
+ { MAP_CALL(IsObject), SIG_EVERYWHERE, ".", NULL, NULL },
+ { MAP_CALL(Joystick), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop
+ { MAP_CALL(LastNode), SIG_EVERYWHERE, "l", NULL, NULL },
+ { MAP_CALL(Load), SIG_EVERYWHERE, "ii(i*)", NULL, NULL },
+ { MAP_CALL(LocalToGlobal), SIG_SCI32, SIGFOR_ALL, "oo", NULL, NULL },
+ { MAP_CALL(LocalToGlobal), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(Lock), SIG_EVERYWHERE, "ii(i)", NULL, NULL },
+ { MAP_CALL(MapKeyToDir), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(Memory), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop
+ { MAP_CALL(MemoryInfo), SIG_EVERYWHERE, "i", NULL, NULL },
+ { MAP_CALL(MemorySegment), SIG_EVERYWHERE, "ir(i)", NULL, NULL }, // subop
+ { MAP_CALL(MenuSelect), SIG_EVERYWHERE, "o(i)", NULL, NULL },
+ { MAP_CALL(MergePoly), SIG_EVERYWHERE, "rli", NULL, NULL },
+ { MAP_CALL(Message), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop
+ { MAP_CALL(MoveCursor), SIG_EVERYWHERE, "ii", NULL, NULL },
+ { MAP_CALL(NewList), SIG_EVERYWHERE, "", NULL, NULL },
+ { MAP_CALL(NewNode), SIG_EVERYWHERE, "..", NULL, NULL },
+ { MAP_CALL(NewWindow), SIG_SCIALL, SIGFOR_MAC, ".*", NULL, NULL },
+ { MAP_CALL(NewWindow), SIG_SCI0, SIGFOR_ALL, "iiii[r0]i(i)(i)(i)", NULL, NULL },
+ { MAP_CALL(NewWindow), SIG_SCI1, SIGFOR_ALL, "iiii[ir]i(i)(i)([ir])(i)(i)(i)(i)", NULL, NULL },
+ { MAP_CALL(NewWindow), SIG_SCI11, SIGFOR_ALL, "iiiiiiii[r0]i(i)(i)(i)", NULL, NULL },
+ { MAP_CALL(NextNode), SIG_EVERYWHERE, "n", NULL, NULL },
+ { MAP_CALL(NodeValue), SIG_EVERYWHERE, "[n0]", NULL, NULL },
+ { MAP_CALL(NumCels), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(NumLoops), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(OnControl), SIG_EVERYWHERE, "ii(i)(i)(i)", NULL, NULL },
+ { MAP_CALL(PalVary), SIG_EVERYWHERE, "i(i*)", kPalVary_subops, NULL },
+ { MAP_CALL(Palette), SIG_EVERYWHERE, "i(.*)", kPalette_subops, NULL },
+ { MAP_CALL(Parse), SIG_EVERYWHERE, "ro", NULL, NULL },
+ { MAP_CALL(PicNotValid), SIG_EVERYWHERE, "(i)", NULL, NULL },
+ { MAP_CALL(Platform), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(Portrait), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop
+ { MAP_CALL(PrevNode), SIG_EVERYWHERE, "n", NULL, NULL },
+ { MAP_CALL(PriCoord), SIG_EVERYWHERE, "i", NULL, NULL },
+ { MAP_CALL(Random), SIG_EVERYWHERE, "ii", NULL, NULL },
+ { MAP_CALL(ReadNumber), SIG_EVERYWHERE, "r", NULL, NULL },
+ { MAP_CALL(ResCheck), SIG_EVERYWHERE, "ii(iiii)", NULL, NULL },
+ { MAP_CALL(RespondsTo), SIG_EVERYWHERE, ".i", NULL, NULL },
+ { MAP_CALL(RestartGame), SIG_EVERYWHERE, "", NULL, NULL },
+ { MAP_CALL(RestoreGame), SIG_EVERYWHERE, "rir", NULL, NULL },
+ { MAP_CALL(Said), SIG_EVERYWHERE, "[r0]", NULL, NULL },
+ { MAP_CALL(SaveGame), SIG_EVERYWHERE, "rir(r)", NULL, NULL },
+ { MAP_CALL(ScriptID), SIG_EVERYWHERE, "[io](i)", NULL, NULL },
+ { MAP_CALL(SetCursor), SIG_EVERYWHERE, "i(i*)", NULL, NULL },
+ { MAP_CALL(SetDebug), SIG_EVERYWHERE, "(i*)", NULL, NULL },
+ { MAP_CALL(SetJump), SIG_EVERYWHERE, "oiii", NULL, NULL },
+ { MAP_CALL(SetMenu), SIG_EVERYWHERE, "i(.*)", NULL, NULL },
+ { MAP_CALL(SetNowSeen), SIG_EVERYWHERE, "o(i)", NULL, NULL },
+ { MAP_CALL(SetPort), SIG_EVERYWHERE, "i(iii)(i)(i)(i)", NULL, NULL },
+ { MAP_CALL(SetQuitStr), SIG_EVERYWHERE, "r", NULL, NULL },
+ { MAP_CALL(SetSynonyms), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(SetVideoMode), SIG_EVERYWHERE, "i", NULL, NULL },
+ { MAP_CALL(ShakeScreen), SIG_EVERYWHERE, "(i)(i)", NULL, NULL },
+ { MAP_CALL(ShowMovie), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(SinDiv), SIG_EVERYWHERE, "ii", NULL, NULL },
+ { MAP_CALL(Sort), SIG_EVERYWHERE, "ooo", NULL, NULL },
+ { MAP_CALL(Sqrt), SIG_EVERYWHERE, "i", NULL, NULL },
+ { MAP_CALL(StrAt), SIG_EVERYWHERE, "ri(i)", NULL, NULL },
+ { MAP_CALL(StrCat), SIG_EVERYWHERE, "rr", NULL, NULL },
+ { MAP_CALL(StrCmp), SIG_EVERYWHERE, "rr(i)", NULL, NULL },
+ { MAP_CALL(StrCpy), SIG_EVERYWHERE, "[r0]r(i)", NULL, NULL },
+ { MAP_CALL(StrEnd), SIG_EVERYWHERE, "r", NULL, NULL },
+ { MAP_CALL(StrLen), SIG_EVERYWHERE, "[r0]", NULL, NULL },
+ { MAP_CALL(StrSplit), SIG_EVERYWHERE, "rr[r0]", NULL, NULL },
+ { MAP_CALL(TextColors), SIG_EVERYWHERE, "(i*)", NULL, NULL },
+ { MAP_CALL(TextFonts), SIG_EVERYWHERE, "(i*)", NULL, NULL },
+ { MAP_CALL(TextSize), SIG_EVERYWHERE, "r[r0]i(i)(r0)", NULL, NULL },
+ { MAP_CALL(TimesCos), SIG_EVERYWHERE, "ii", NULL, NULL },
+ { "CosMult", kTimesCos, SIG_EVERYWHERE, "ii", NULL, NULL },
+ { MAP_CALL(TimesCot), SIG_EVERYWHERE, "ii", NULL, NULL },
+ { MAP_CALL(TimesSin), SIG_EVERYWHERE, "ii", NULL, NULL },
+ { "SinMult", kTimesSin, SIG_EVERYWHERE, "ii", NULL, NULL },
+ { MAP_CALL(TimesTan), SIG_EVERYWHERE, "ii", NULL, NULL },
+ { MAP_CALL(UnLoad), SIG_EVERYWHERE, "i[ri]", NULL, kUnLoad_workarounds },
+ { MAP_CALL(ValidPath), SIG_EVERYWHERE, "r", NULL, NULL },
+ { MAP_CALL(Wait), SIG_EVERYWHERE, "i", NULL, NULL },
- {NULL, NULL, NULL} // Terminator
+#ifdef ENABLE_SCI32
+ // SCI2 Kernel Functions
+ { MAP_CALL(AddPlane), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(AddScreenItem), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(Array), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(CreateTextBitmap), SIG_EVERYWHERE, "i(.*)", NULL, NULL },
+ { MAP_CALL(DeletePlane), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(DeleteScreenItem), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(FrameOut), SIG_EVERYWHERE, "", NULL, NULL },
+ { MAP_CALL(GetHighPlanePri), SIG_EVERYWHERE, "", NULL, NULL },
+ { MAP_CALL(InPolygon), SIG_EVERYWHERE, "iio", NULL, NULL },
+ { MAP_CALL(IsHiRes), SIG_EVERYWHERE, "", NULL, NULL },
+ { MAP_CALL(ListAllTrue), SIG_EVERYWHERE, "li(.*)", NULL, NULL },
+ { MAP_CALL(ListAt), SIG_EVERYWHERE, "li", NULL, NULL },
+ { MAP_CALL(ListEachElementDo), SIG_EVERYWHERE, "li(.*)", NULL, NULL },
+ { MAP_CALL(ListFirstTrue), SIG_EVERYWHERE, "li(.*)", NULL, NULL },
+ { MAP_CALL(ListIndexOf), SIG_EVERYWHERE, "l[o0]", NULL, NULL },
+ { MAP_CALL(OnMe), SIG_EVERYWHERE, "iio(.*)", NULL, NULL },
+ { MAP_CALL(RepaintPlane), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(String), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(UpdatePlane), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(UpdateScreenItem), SIG_EVERYWHERE, "o", NULL, NULL },
+
+ // SCI2.1 Kernel Functions
+ { MAP_CALL(CD), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(IsOnMe), SIG_EVERYWHERE, "iio(.*)", NULL, NULL },
+ { MAP_CALL(List), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(MulDiv), SIG_EVERYWHERE, "iii", NULL, NULL },
+ { MAP_CALL(PlayVMD), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(Robot), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(Save), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(Text), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { NULL, NULL, SIG_EVERYWHERE, NULL, NULL, NULL }
+#endif
};
-Kernel::Kernel(ResourceManager *resMan, SegManager *segMan) : _resMan(resMan), _segMan(segMan) {
+Kernel::Kernel(ResourceManager *resMan, SegManager *segMan)
+ : _resMan(resMan), _segMan(segMan), _invalid("<invalid>") {
loadSelectorNames();
mapSelectors(); // Map a few special selectors for later use
}
Kernel::~Kernel() {
- for (KernelFuncsContainer::iterator i = _kernelFuncs.begin(); i != _kernelFuncs.end(); ++i)
- // TODO: Doing a const_cast is not that nice actually... But since KernelFuncWithSignature
- // keeps the signature member as "const char *" there is no way around it.
- // Think of a clever way to avoid this.
- free(const_cast<char *>(i->signature));
+ for (KernelFunctionArray::iterator i = _kernelFuncs.begin(); i != _kernelFuncs.end(); ++i)
+ free(i->signature);
}
uint Kernel::getSelectorNamesSize() const {
@@ -409,6 +656,8 @@ uint Kernel::getSelectorNamesSize() const {
}
const Common::String &Kernel::getSelectorName(uint selector) const {
+ if (selector >= _selectorNames.size())
+ return _invalid;
return _selectorNames[selector];
}
@@ -417,12 +666,10 @@ uint Kernel::getKernelNamesSize() const {
}
const Common::String &Kernel::getKernelName(uint number) const {
- // FIXME: The following check is a temporary workaround for
- // an issue leading to crashes when using the debugger's backtrace
- // command.
- static const Common::String invalid = "(invalid)";
+ // FIXME: The following check is a temporary workaround for an issue
+ // leading to crashes when using the debugger's backtrace command.
if (number >= _kernelNames.size())
- return invalid;
+ return _invalid;
return _kernelNames[number];
}
@@ -477,165 +724,241 @@ void Kernel::loadSelectorNames() {
}
}
-static void kernel_compile_signature(const char **s) {
- const char *src = *s;
- char *result;
- int ellipsis = 0;
- char v;
- int index = 0;
-
- if (!src)
- return; // NULL signature: Nothing to do
-
- result = (char *)malloc(strlen(*s) + 1);
-
- while (*src) {
- char c;
- v = 0;
-
- if (ellipsis) {
- error("Failed compiling kernel function signature '%s': non-terminal ellipsis '%c'", *s, *src);
- }
-
- do {
- char cc;
- cc = c = *src++;
- if (c >= 'A' || c <= 'Z')
- cc = c | KSIG_SPEC_SUM_DONE;
-
- switch (cc) {
- case KSIG_SPEC_LIST:
- v |= KSIG_LIST;
- break;
-
- case KSIG_SPEC_NODE:
- v |= KSIG_NODE;
- break;
-
- case KSIG_SPEC_REF:
- v |= KSIG_REF;
- break;
-
- case KSIG_SPEC_OBJECT:
- v |= KSIG_OBJECT;
- break;
-
- case KSIG_SPEC_ARITHMETIC:
- v |= KSIG_ARITHMETIC;
- break;
-
- case KSIG_SPEC_NULL:
- v |= KSIG_NULL;
- break;
-
- case KSIG_SPEC_ANY:
- v |= KSIG_ANY;
- break;
-
- case KSIG_SPEC_ELLIPSIS:
- v |= KSIG_ELLIPSIS;
- ellipsis = 1;
- break;
-
- default:
- error("INTERNAL ERROR when compiling kernel function signature '%s': (%02x) not understood (aka"
- " '%c')\n", *s, c, c);
+// this parses a written kernel signature into an internal memory format
+// [io] -> either integer or object
+// (io) -> optionally integer AND an object
+// (i) -> optional integer
+// . -> any type
+// i* -> optional multiple integers
+// .* -> any parameters afterwards (or none)
+static uint16 *parseKernelSignature(const char *kernelName, const char *writtenSig) {
+ const char *curPos;
+ char curChar;
+ uint16 *result = NULL;
+ uint16 *writePos = NULL;
+ int size = 0;
+ bool validType = false;
+ bool optionalType = false;
+ bool eitherOr = false;
+ bool optional = false;
+ bool hadOptional = false;
+
+ // No signature given? no signature out
+ if (!writtenSig)
+ return NULL;
+
+ // First, we check how many bytes the result will be
+ // we also check, if the written signature makes any sense
+ curPos = writtenSig;
+ while (*curPos) {
+ curChar = *curPos;
+ switch (curChar) {
+ case '[': // either or
+ if (eitherOr)
+ error("signature for k%s: '[' used within '[]'", kernelName);
+ eitherOr = true;
+ validType = false;
+ break;
+ case ']': // either or end
+ if (!eitherOr)
+ error("signature for k%s: ']' used without leading '['", kernelName);
+ if (!validType)
+ error("signature for k%s: '[]' does not surround valid type(s)", kernelName);
+ eitherOr = false;
+ validType = false;
+ size++;
+ break;
+ case '(': // optional
+ if (optional)
+ error("signature for k%s: '(' used within '()' brackets", kernelName);
+ if (eitherOr)
+ error("signature for k%s: '(' used within '[]' brackets", kernelName);
+ optional = true;
+ validType = false;
+ optionalType = false;
+ break;
+ case ')': // optional end
+ if (!optional)
+ error("signature for k%s: ')' used without leading '('", kernelName);
+ if (!optionalType)
+ error("signature for k%s: '()' does not to surround valid type(s)", kernelName);
+ optional = false;
+ validType = false;
+ hadOptional = true;
+ break;
+ case '0': // allowed types
+ case 'i':
+ case 'o':
+ case 'r':
+ case 'l':
+ case 'n':
+ case '.':
+ case '!':
+ if ((hadOptional) & (!optional))
+ error("signature for k%s: non-optional type may not follow optional type", kernelName);
+ validType = true;
+ if (optional)
+ optionalType = true;
+ if (!eitherOr)
+ size++;
+ break;
+ case '*': // accepts more of the same parameter (must be last char)
+ if (!validType) {
+ if ((writtenSig == curPos) || (*(curPos - 1) != ']'))
+ error("signature for k%s: a valid type must be in front of '*'", kernelName);
}
- } while (*src && (*src == KSIG_SPEC_ELLIPSIS || (c < 'a' && c != KSIG_SPEC_ANY)));
-
- // To handle sum types
- result[index++] = v;
+ if (eitherOr)
+ error("signature for k%s: '*' may not be inside '[]'", kernelName);
+ if (optional) {
+ if ((*(curPos + 1) != ')') || (*(curPos + 2) != 0))
+ error("signature for k%s: '*' may only be used for last type", kernelName);
+ } else {
+ if (*(curPos + 1) != 0)
+ error("signature for k%s: '*' may only be used for last type", kernelName);
+ }
+ break;
+ default:
+ error("signature for k%s: '%c' unknown", kernelName, *curPos);
+ }
+ curPos++;
}
- result[index] = 0;
- *s = result; // Write back
-}
-
-void Kernel::mapFunctions() {
- int mapped = 0;
- int ignored = 0;
- uint functions_nr = _kernelNames.size();
-
- _kernelFuncs.resize(functions_nr);
-
- for (uint functnr = 0; functnr < functions_nr; functnr++) {
- int found = -1;
-
- // First, get the name, if known, of the kernel function with number functnr
- Common::String sought_name = _kernelNames[functnr];
-
- // Reset the table entry
- _kernelFuncs[functnr].fun = NULL;
- _kernelFuncs[functnr].signature = NULL;
- _kernelFuncs[functnr].orig_name = sought_name;
-
- if (sought_name.empty()) {
- // No name was given -> must be an unknown opcode
- warning("Kernel function %x unknown", functnr);
- _kernelFuncs[functnr].isDummy = true;
- continue;
+ uint16 signature = 0;
+
+ // Now we allocate buffer with required size and fill it
+ result = new uint16[size + 1];
+ writePos = result;
+ curPos = writtenSig;
+ do {
+ curChar = *curPos;
+ if (!eitherOr) {
+ // not within either-or, check if next character forces output
+ switch (curChar) {
+ case 0:
+ case '[':
+ case '(':
+ case ')':
+ case 'i':
+ case 'o':
+ case 'r':
+ case 'l':
+ case 'n':
+ case '.':
+ case '!':
+ // and we also got some signature pending?
+ if (signature) {
+ if (!(signature & SIG_MAYBE_ANY))
+ error("signature for k%s: invalid ('!') may only get used in combination with a real type", kernelName);
+ if ((signature & SIG_IS_INVALID) && ((signature & SIG_MAYBE_ANY) == (SIG_TYPE_NULL | SIG_TYPE_INTEGER)))
+ error("signature for k%s: invalid ('!') should not be used on exclusive null/integer type", kernelName);
+ if (optional) {
+ signature |= SIG_IS_OPTIONAL;
+ if (curChar != ')')
+ signature |= SIG_NEEDS_MORE;
+ }
+ *writePos = signature;
+ writePos++;
+ signature = 0;
+ }
+ }
}
-
- // Don't map dummy functions - they will never be called
- if (sought_name == "Dummy") {
- _kernelFuncs[functnr].isDummy = true;
- continue;
+ switch (curChar) {
+ case '[': // either or
+ eitherOr = true;
+ break;
+ case ']': // either or end
+ eitherOr = false;
+ break;
+ case '(': // optional
+ optional = true;
+ break;
+ case ')': // optional end
+ optional = false;
+ break;
+ case '0':
+ if (signature & SIG_TYPE_NULL)
+ error("signature for k%s: NULL ('0') specified more than once", kernelName);
+ signature |= SIG_TYPE_NULL;
+ break;
+ case 'i':
+ if (signature & SIG_TYPE_INTEGER)
+ error("signature for k%s: integer ('i') specified more than once", kernelName);
+ signature |= SIG_TYPE_INTEGER | SIG_TYPE_NULL;
+ break;
+ case 'o':
+ if (signature & SIG_TYPE_OBJECT)
+ error("signature for k%s: object ('o') specified more than once", kernelName);
+ signature |= SIG_TYPE_OBJECT;
+ break;
+ case 'r':
+ if (signature & SIG_TYPE_REFERENCE)
+ error("signature for k%s: reference ('r') specified more than once", kernelName);
+ signature |= SIG_TYPE_REFERENCE;
+ break;
+ case 'l':
+ if (signature & SIG_TYPE_LIST)
+ error("signature for k%s: list ('l') specified more than once", kernelName);
+ signature |= SIG_TYPE_LIST;
+ break;
+ case 'n':
+ if (signature & SIG_TYPE_NODE)
+ error("signature for k%s: node ('n') specified more than once", kernelName);
+ signature |= SIG_TYPE_NODE;
+ break;
+ case '.':
+ if (signature & SIG_MAYBE_ANY)
+ error("signature for k%s: maybe-any ('.') shouldn't get specified with other types in front of it", kernelName);
+ signature |= SIG_MAYBE_ANY;
+ break;
+ case '!':
+ if (signature & SIG_IS_INVALID)
+ error("signature for k%s: invalid ('!') specified more than once", kernelName);
+ signature |= SIG_IS_INVALID;
+ break;
+ case '*': // accepts more of the same parameter
+ signature |= SIG_MORE_MAY_FOLLOW;
+ break;
+ default:
+ break;
}
+ curPos++;
+ } while (curChar);
- // If the name is known, look it up in kfunct_mappers. This table
- // maps kernel func names to actual function (pointers).
- for (uint seeker = 0; (found == -1) && kfunct_mappers[seeker].name; seeker++)
- if (sought_name == kfunct_mappers[seeker].name)
- found = seeker; // Found a kernel function with the correct name!
-
- if (found == -1) {
- // No match but a name was given -> stub
- warning("Kernel function %s[%x] unmapped", sought_name.c_str(), functnr);
- _kernelFuncs[functnr].isDummy = true;
- } else {
- // A match in kfunct_mappers was found
- if (kfunct_mappers[found].fun) {
- _kernelFuncs[functnr].fun = kfunct_mappers[found].fun;
- _kernelFuncs[functnr].signature = kfunct_mappers[found].signature;
- _kernelFuncs[functnr].isDummy = false;
- kernel_compile_signature(&(_kernelFuncs[functnr].signature));
- ++mapped;
- } else {
- //warning("Ignoring function %s\n", kfunct_mappers[found].name);
- ++ignored;
- }
- }
- } // for all functions requesting to be mapped
+ // Write terminator
+ *writePos = 0;
- debugC(2, kDebugLevelVM, "Handled %d/%d kernel functions, mapping %d and ignoring %d.",
- mapped + ignored, _kernelNames.size(), mapped, ignored);
-
- return;
+ return result;
}
-int Kernel::findRegType(reg_t reg) {
- // No segment? Must be arithmetic
+uint16 Kernel::findRegType(reg_t reg) {
+ // No segment? Must be integer
if (!reg.segment)
- return reg.offset ? KSIG_ARITHMETIC : KSIG_ARITHMETIC | KSIG_NULL;
+ return SIG_TYPE_INTEGER | (reg.offset ? 0 : SIG_TYPE_NULL);
+
+ if (reg.segment == 0xFFFF)
+ return SIG_TYPE_UNINITIALIZED;
// Otherwise it's an object
SegmentObj *mobj = _segMan->getSegmentObj(reg.segment);
if (!mobj)
- return 0; // Invalid
+ return SIG_TYPE_ERROR;
+ uint16 result = 0;
if (!mobj->isValidOffset(reg.offset))
- warning("[KERN] ref %04x:%04x is invalid", PRINT_REG(reg));
+ result |= SIG_IS_INVALID;
switch (mobj->getType()) {
case SEG_TYPE_SCRIPT:
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;
+ RAW_IS_OBJECT((*(Script *)mobj).getBuf(reg.offset)) ) {
+ result |= ((Script *)mobj)->getObject(reg.offset) ? SIG_TYPE_OBJECT : SIG_TYPE_REFERENCE;
} else
- return KSIG_REF;
+ result |= SIG_TYPE_REFERENCE;
+ break;
case SEG_TYPE_CLONES:
- return KSIG_OBJECT;
+ result |= SIG_TYPE_OBJECT;
+ break;
case SEG_TYPE_LOCALS:
case SEG_TYPE_STACK:
case SEG_TYPE_SYS_STRINGS:
@@ -645,54 +968,286 @@ int Kernel::findRegType(reg_t reg) {
case SEG_TYPE_ARRAY:
case SEG_TYPE_STRING:
#endif
- return KSIG_REF;
+ result |= SIG_TYPE_REFERENCE;
+ break;
case SEG_TYPE_LISTS:
- return KSIG_LIST;
+ result |= SIG_TYPE_LIST;
+ break;
case SEG_TYPE_NODES:
- return KSIG_NODE;
+ result |= SIG_TYPE_NODE;
+ break;
default:
- return 0;
+ return SIG_TYPE_ERROR;
}
+ return result;
}
-bool Kernel::signatureMatch(const char *sig, int argc, const reg_t *argv) {
- // Always "match" if no signature is given
- if (!sig)
- return true;
+struct SignatureDebugType {
+ uint16 typeCheck;
+ const char *text;
+};
- while (*sig && argc) {
- if ((*sig & KSIG_ANY) != KSIG_ANY) {
- int type = findRegType(*argv);
+static const SignatureDebugType signatureDebugTypeList[] = {
+ { SIG_TYPE_NULL, "null" },
+ { SIG_TYPE_INTEGER, "integer" },
+ { SIG_TYPE_UNINITIALIZED, "uninitialized" },
+ { SIG_TYPE_OBJECT, "object" },
+ { SIG_TYPE_REFERENCE, "reference" },
+ { SIG_TYPE_LIST, "list" },
+ { SIG_TYPE_NODE, "node" },
+ { SIG_TYPE_ERROR, "error" },
+ { SIG_IS_INVALID, "invalid" },
+ { 0, NULL }
+};
- if (!type) {
- warning("[KERN] Could not determine type of ref %04x:%04x; failing signature check", PRINT_REG(*argv));
- return false;
- }
+static void kernelSignatureDebugType(const uint16 type) {
+ bool firstPrint = true;
- if (!(type & *sig)) {
- warning("kernel_matches_signature: %d args left, is %d, should be %d", argc, type, *sig);
- return false;
- }
+ const SignatureDebugType *list = signatureDebugTypeList;
+ while (list->typeCheck) {
+ if (type & list->typeCheck) {
+ if (!firstPrint)
+ printf(", ");
+ printf("%s", list->text);
+ firstPrint = false;
+ }
+ list++;
+ }
+}
+// Shows kernel call signature and current arguments for debugging purposes
+void Kernel::signatureDebug(const uint16 *sig, int argc, const reg_t *argv) {
+ int argnr = 0;
+ while (*sig || argc) {
+ printf("parameter %d: ", argnr++);
+ if (argc) {
+ reg_t parameter = *argv;
+ printf("%04x:%04x (", PRINT_REG(parameter));
+ int regType = findRegType(parameter);
+ if (regType)
+ kernelSignatureDebugType(regType);
+ else
+ printf("unknown type of %04x:%04x", PRINT_REG(parameter));
+ printf(")");
+ argv++;
+ argc--;
+ } else {
+ printf("not passed");
}
- if (!(*sig & KSIG_ELLIPSIS))
- ++sig;
- ++argv;
- --argc;
+ if (*sig) {
+ const uint16 signature = *sig;
+ if ((signature & SIG_MAYBE_ANY) == SIG_MAYBE_ANY) {
+ printf(", may be any");
+ } else {
+ printf(", should be ");
+ kernelSignatureDebugType(signature);
+ }
+ if (signature & SIG_IS_OPTIONAL)
+ printf(" (optional)");
+ if (signature & SIG_NEEDS_MORE)
+ printf(" (needs more)");
+ if (signature & SIG_MORE_MAY_FOLLOW)
+ printf(" (more may follow)");
+ sig++;
+ }
+ printf("\n");
}
+}
+
+bool Kernel::signatureMatch(const uint16 *sig, int argc, const reg_t *argv) {
+ uint16 nextSig = *sig;
+ uint16 curSig = nextSig;
+ while (nextSig && argc) {
+ curSig = nextSig;
+ int type = findRegType(*argv);
+
+ if ((type & SIG_IS_INVALID) && (!(curSig & SIG_IS_INVALID)))
+ return false; // pointer is invalid and signature doesn't allow that?
+
+ if (!((type & ~SIG_IS_INVALID) & curSig))
+ return false; // type mismatch
- if (argc) {
- warning("kernel_matches_signature: too many arguments");
- return false; // Too many arguments
+ if (!(curSig & SIG_MORE_MAY_FOLLOW)) {
+ sig++;
+ nextSig = *sig;
+ } else {
+ nextSig |= SIG_IS_OPTIONAL; // more may follow -> assumes followers are optional
+ }
+ argv++;
+ argc--;
}
- if (*sig == 0 || (*sig & KSIG_ELLIPSIS))
+
+ // Too many arguments?
+ if (argc)
+ return false;
+ // Signature end reached?
+ if (nextSig == 0)
return true;
- warning("kernel_matches_signature: too few arguments");
+ // current parameter is optional?
+ if (curSig & SIG_IS_OPTIONAL) {
+ // yes, check if nothing more is required
+ if (!(curSig & SIG_NEEDS_MORE))
+ return true;
+ } else {
+ // no, check if next parameter is optional
+ if (nextSig & SIG_IS_OPTIONAL)
+ return true;
+ }
+ // Too few arguments or more optional arguments required
return false;
}
-void Kernel::setDefaultKernelNames(Common::String gameId) {
- _kernelNames = Common::StringArray(sci_default_knames, SCI_KNAMES_DEFAULT_ENTRIES_NR);
+void Kernel::mapFunctions() {
+ int mapped = 0;
+ int ignored = 0;
+ uint functionCount = _kernelNames.size();
+ byte platformMask = 0;
+ SciVersion myVersion = getSciVersion();
+
+ switch (g_sci->getPlatform()) {
+ case Common::kPlatformPC:
+ platformMask = SIGFOR_DOS;
+ break;
+ case Common::kPlatformPC98:
+ platformMask = SIGFOR_PC98;
+ break;
+ case Common::kPlatformWindows:
+ platformMask = SIGFOR_WIN;
+ break;
+ case Common::kPlatformMacintosh:
+ platformMask = SIGFOR_MAC;
+ break;
+ case Common::kPlatformAmiga:
+ platformMask = SIGFOR_AMIGA;
+ break;
+ case Common::kPlatformAtariST:
+ platformMask = SIGFOR_ATARI;
+ break;
+ default:
+ break;
+ }
+
+ _kernelFuncs.resize(functionCount);
+
+ for (uint id = 0; id < functionCount; id++) {
+ // First, get the name, if known, of the kernel function with number functnr
+ Common::String kernelName = _kernelNames[id];
+
+ // Reset the table entry
+ _kernelFuncs[id].function = NULL;
+ _kernelFuncs[id].signature = NULL;
+ _kernelFuncs[id].name = NULL;
+ _kernelFuncs[id].workarounds = NULL;
+ _kernelFuncs[id].subFunctions = NULL;
+ _kernelFuncs[id].subFunctionCount = 0;
+ _kernelFuncs[id].debugCalls = false;
+ if (kernelName.empty()) {
+ // No name was given -> must be an unknown opcode
+ warning("Kernel function %x unknown", id);
+ continue;
+ }
+
+ // Don't map dummy functions - they will never be called
+ if (kernelName == "Dummy") {
+ _kernelFuncs[id].function = kDummy;
+ continue;
+ }
+
+ // If the name is known, look it up in s_kernelMap. This table
+ // maps kernel func names to actual function (pointers).
+ SciKernelMapEntry *kernelMap = s_kernelMap;
+ bool nameMatch = false;
+ while (kernelMap->name) {
+ if (kernelName == kernelMap->name) {
+ if ((kernelMap->fromVersion == SCI_VERSION_NONE) || (kernelMap->fromVersion <= myVersion))
+ if ((kernelMap->toVersion == SCI_VERSION_NONE) || (kernelMap->toVersion >= myVersion))
+ if (platformMask & kernelMap->forPlatform)
+ break;
+ nameMatch = true;
+ }
+ kernelMap++;
+ }
+
+ if (kernelMap->name) {
+ // A match was found
+ _kernelFuncs[id].function = kernelMap->function;
+ _kernelFuncs[id].name = kernelMap->name;
+ _kernelFuncs[id].signature = parseKernelSignature(kernelMap->name, kernelMap->signature);
+ _kernelFuncs[id].workarounds = kernelMap->workarounds;
+ if (kernelMap->subFunctions) {
+ // Get version for subfunction identification
+ SciVersion mySubVersion = (SciVersion)kernelMap->function(NULL, 0, NULL).offset;
+ // Now check whats the highest subfunction-id for this version
+ const SciKernelMapSubEntry *kernelSubMap = kernelMap->subFunctions;
+ uint16 subFunctionCount = 0;
+ while (kernelSubMap->function) {
+ if ((kernelSubMap->fromVersion == SCI_VERSION_NONE) || (kernelSubMap->fromVersion <= mySubVersion))
+ if ((kernelSubMap->toVersion == SCI_VERSION_NONE) || (kernelSubMap->toVersion >= mySubVersion))
+ if (subFunctionCount <= kernelSubMap->id)
+ subFunctionCount = kernelSubMap->id + 1;
+ kernelSubMap++;
+ }
+ if (!subFunctionCount)
+ error("k%s[%x]: no subfunctions found for requested version", kernelName.c_str(), id);
+ // Now allocate required memory and go through it again
+ _kernelFuncs[id].subFunctionCount = subFunctionCount;
+ KernelSubFunction *subFunctions = new KernelSubFunction[subFunctionCount];
+ _kernelFuncs[id].subFunctions = subFunctions;
+ memset(subFunctions, 0, sizeof(KernelSubFunction) * subFunctionCount);
+ // And fill this info out
+ kernelSubMap = kernelMap->subFunctions;
+ uint kernelSubNr = 0;
+ while (kernelSubMap->function) {
+ if ((kernelSubMap->fromVersion == SCI_VERSION_NONE) || (kernelSubMap->fromVersion <= mySubVersion))
+ if ((kernelSubMap->toVersion == SCI_VERSION_NONE) || (kernelSubMap->toVersion >= mySubVersion)) {
+ uint subId = kernelSubMap->id;
+ if (!subFunctions[subId].function) {
+ subFunctions[subId].function = kernelSubMap->function;
+ subFunctions[subId].name = kernelSubMap->name;
+ subFunctions[subId].workarounds = kernelSubMap->workarounds;
+ if (kernelSubMap->signature) {
+ subFunctions[subId].signature = parseKernelSignature(kernelSubMap->name, kernelSubMap->signature);
+ } else {
+ // we go back the submap to find the previous signature for that kernel call
+ const SciKernelMapSubEntry *kernelSubMapBack = kernelSubMap;
+ uint kernelSubLeft = kernelSubNr;
+ while (kernelSubLeft) {
+ kernelSubLeft--;
+ kernelSubMapBack--;
+ if (kernelSubMapBack->name == kernelSubMap->name) {
+ if (kernelSubMapBack->signature) {
+ subFunctions[subId].signature = parseKernelSignature(kernelSubMap->name, kernelSubMapBack->signature);
+ break;
+ }
+ }
+ }
+ if (!subFunctions[subId].signature)
+ error("k%s: no previous signatures", kernelSubMap->name);
+ }
+ }
+ }
+ kernelSubMap++;
+ kernelSubNr++;
+ }
+ }
+ ++mapped;
+ } else {
+ if (nameMatch)
+ error("k%s[%x]: not found for this version/platform", kernelName.c_str(), id);
+ // No match but a name was given -> stub
+ warning("k%s[%x]: unmapped", kernelName.c_str(), id);
+ _kernelFuncs[id].function = kStub;
+ }
+ } // for all functions requesting to be mapped
+
+ debugC(2, kDebugLevelVM, "Handled %d/%d kernel functions, mapping %d and ignoring %d.",
+ mapped + ignored, _kernelNames.size(), mapped, ignored);
+
+ return;
+}
+
+void Kernel::setDefaultKernelNames() {
+ _kernelNames = Common::StringArray(s_defaultKernelNames, ARRAYSIZE(s_defaultKernelNames));
// Some (later) SCI versions replaced CanBeHere by CantBeHere
if (_selectorCache.cantBeHere != -1)
@@ -727,14 +1282,19 @@ void Kernel::setDefaultKernelNames(Common::String gameId) {
break;
case SCI_VERSION_1_1:
- // In KQ6 CD, the empty kSetSynonyms function has been replaced
- // with kPortrait. In KQ6 Mac, kPlayBack has been replaced by
- // kShowMovie.
- if (gameId == "kq6") {
- if (g_sci->getPlatform() == Common::kPlatformMacintosh)
- _kernelNames[0x84] = "ShowMovie";
- else
+ // In SCI1.1, kSetSynonyms is an empty function
+ _kernelNames[0x26] = "Empty";
+
+ if (g_sci->getGameId() == GID_KQ6) {
+ // In the Windows version of KQ6 CD, the empty kSetSynonyms
+ // function has been replaced with kPortrait. In KQ6 Mac,
+ // kPlayBack has been replaced by kShowMovie.
+ if (g_sci->getPlatform() == Common::kPlatformWindows)
_kernelNames[0x26] = "Portrait";
+ else if (g_sci->getPlatform() == Common::kPlatformMacintosh)
+ _kernelNames[0x84] = "ShowMovie";
+ } else if (g_sci->getGameId() == GID_QFG4 && g_sci->isDemo()) {
+ _kernelNames[0x7b] = "RemapColors"; // QFG4 Demo has this SCI2 function instead of StrSplit
}
_kernelNames[0x71] = "PalVary";
@@ -747,20 +1307,19 @@ void Kernel::setDefaultKernelNames(Common::String gameId) {
}
}
-bool Kernel::loadKernelNames(Common::String gameId) {
+void Kernel::loadKernelNames(GameFeatures *features) {
_kernelNames.clear();
#ifdef ENABLE_SCI32
if (getSciVersion() >= SCI_VERSION_2_1)
- setKernelNamesSci21();
+ setKernelNamesSci21(features);
else if (getSciVersion() == SCI_VERSION_2)
setKernelNamesSci2();
else
#endif
- setDefaultKernelNames(gameId);
+ setDefaultKernelNames();
mapFunctions();
- return true;
}
Common::String Kernel::lookupText(reg_t address, int index) {
@@ -769,31 +1328,28 @@ Common::String Kernel::lookupText(reg_t address, int index) {
if (address.segment)
return _segMan->getString(address);
- else {
- int textlen;
- int _index = index;
- textres = _resMan->findResource(ResourceId(kResourceTypeText, address.offset), 0);
-
- if (!textres) {
- error("text.%03d not found", address.offset);
- return NULL; /* Will probably segfault */
- }
- textlen = textres->size;
- seeker = (char *) textres->data;
+ int textlen;
+ int _index = index;
+ textres = _resMan->findResource(ResourceId(kResourceTypeText, address.offset), 0);
- while (index--)
- while ((textlen--) && (*seeker++))
- ;
+ if (!textres) {
+ error("text.%03d not found", address.offset);
+ return NULL; /* Will probably segfault */
+ }
- if (textlen)
- return seeker;
- else {
- error("Index %d out of bounds in text.%03d", _index, address.offset);
- return NULL;
- }
+ textlen = textres->size;
+ seeker = (char *) textres->data;
- }
+ while (index--)
+ while ((textlen--) && (*seeker++))
+ ;
+
+ if (textlen)
+ return seeker;
+
+ error("Index %d out of bounds in text.%03d", _index, address.offset);
+ return NULL;
}
} // End of namespace Sci
diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h
index 8f8f34f74e..fa206e8053 100644
--- a/engines/sci/engine/kernel.h
+++ b/engines/sci/engine/kernel.h
@@ -31,7 +31,7 @@
#include "common/rect.h"
#include "common/str-array.h"
-#include "sci/sci.h" // for USE_OLD_MUSIC_FUNCTIONS
+#include "sci/engine/selector.h"
#include "sci/engine/vm_types.h" // for reg_t
#include "sci/engine/vm.h"
@@ -39,6 +39,7 @@ namespace Sci {
struct Node; // from segment.h
struct List; // from segment.h
+struct SelectorCache; // from selector.h
/**
* @defgroup VocabularyResources Vocabulary resources in SCI
@@ -92,59 +93,67 @@ struct List; // from segment.h
//@{
//#define DEBUG_PARSER // enable for parser debugging
-//#define DISABLE_VALIDATIONS // enable to stop validation checks
// ---- Kernel signatures -----------------------------------------------------
-#define KSIG_TERMINATOR 0
-
-// Uncompiled signatures
-#define KSIG_SPEC_ARITMETIC 'i'
-#define KSIG_SPEC_LIST 'l'
-#define KSIG_SPEC_NODE 'n'
-#define KSIG_SPEC_OBJECT 'o'
-#define KSIG_SPEC_REF 'r' // Said Specs and strings
-#define KSIG_SPEC_ARITHMETIC 'i'
-#define KSIG_SPEC_NULL 'z'
-#define KSIG_SPEC_ANY '.'
-#define KSIG_SPEC_ELLIPSIS '*' // Arbitrarily more TYPED arguments
-
-#define KSIG_SPEC_SUM_DONE ('a' - 'A') // Use small letters to indicate end of sum type
-/* Use capital letters for sum types, e.g.
-** "LNoLr" for a function which takes two arguments:
-** (1) list, node or object
-** (2) list or ref
-*/
-
-// Compiled signatures
-#define KSIG_LIST 0x01
-#define KSIG_NODE 0x02
-#define KSIG_OBJECT 0x04
-#define KSIG_REF 0x08
-#define KSIG_ARITHMETIC 0x10
-
-#define KSIG_NULL 0x40
-#define KSIG_ANY 0x5f
-#define KSIG_ELLIPSIS 0x80
+
+// internal kernel signature data
+enum {
+ SIG_TYPE_NULL = 0x01, // may be 0:0 [0]
+ SIG_TYPE_INTEGER = 0x02, // may be 0:* [i], automatically also allows null
+ SIG_TYPE_UNINITIALIZED = 0x04, // may be FFFF:* -> not allowable, only used for comparsion
+ SIG_TYPE_OBJECT = 0x08, // may be object [o]
+ SIG_TYPE_REFERENCE = 0x10, // may be reference [r]
+ SIG_TYPE_LIST = 0x20, // may be list [l]
+ SIG_TYPE_NODE = 0x40, // may be node [n]
+ SIG_TYPE_ERROR = 0x80, // happens, when there is a identification error - only used for comparsion
+ SIG_IS_INVALID = 0x100, // ptr is invalid [!] -> invalid offset
+ SIG_IS_OPTIONAL = 0x200, // is optional
+ SIG_NEEDS_MORE = 0x400, // needs at least one additional parameter following
+ SIG_MORE_MAY_FOLLOW = 0x800 // may have more parameters of the same type following
+};
+
+// this does not include SIG_TYPE_UNINITIALIZED, because we can not allow uninitialized values anywhere
+#define SIG_MAYBE_ANY (SIG_TYPE_NULL | SIG_TYPE_INTEGER | SIG_TYPE_OBJECT | SIG_TYPE_REFERENCE | SIG_TYPE_LIST | SIG_TYPE_NODE)
+
// ----------------------------------------------------------------------------
/* Generic description: */
-typedef reg_t KernelFunc(EngineState *s, int argc, reg_t *argv);
+typedef reg_t KernelFunctionCall(EngineState *s, int argc, reg_t *argv);
+
+struct SciWorkaroundEntry {
+ SciGameId gameId;
+ int scriptNr;
+ int16 inheritanceLevel;
+ const char *objectName;
+ const char *methodName;
+ int localCallOffset;
+ int index;
+ reg_t newValue;
+};
+
+#define SCI_WORKAROUNDENTRY_TERMINATOR { (SciGameId)0, -1, 0, NULL, NULL, -1, 0, { 0, 0 } }
-struct KernelFuncWithSignature {
- KernelFunc *fun; /**< The actual function */
- const char *signature; /**< KernelFunc signature */
- Common::String orig_name; /**< Original name, in case we couldn't map it */
- bool isDummy;
+struct KernelSubFunction {
+ KernelFunctionCall *function;
+ const char *name;
+ uint16 *signature;
+ const SciWorkaroundEntry *workarounds;
};
-enum AutoDetectedFeatures {
- kFeatureOldScriptHeader = 1 << 0
+struct KernelFunction {
+ KernelFunctionCall *function;
+ const char *name;
+ uint16 *signature;
+ const SciWorkaroundEntry *workarounds;
+ const KernelSubFunction *subFunctions;
+ uint16 subFunctionCount;
+ bool debugCalls;
};
class Kernel {
public:
/**
- * Initializes the SCI kernel
+ * Initializes the SCI kernel.
*/
Kernel(ResourceManager *resMan, SegManager *segMan);
~Kernel();
@@ -156,18 +165,7 @@ public:
const Common::String &getKernelName(uint number) const;
/**
- * Loads the kernel function names.
- *
- * This function reads the kernel function name table from resource_map,
- * and fills the _kernelNames array with them.
- * The resulting list has the same format regardless of the format of the
- * name table of the resource (the format changed between version 0 and 1).
- * @return true on success, false on failure
- */
- bool loadKernelNames(Common::String gameId);
-
- /**
- * Determines the selector ID of a selector by its name
+ * Determines the selector ID of a selector by its name.
* @param selectorName Name of the selector to look up
* @return The appropriate selector ID, or -1 on error
*/
@@ -178,22 +176,24 @@ public:
void dumpScriptObject(char *data, int seeker, int objsize);
void dumpScriptClass(char *data, int seeker, int objsize);
- SelectorCache _selectorCache; /**< Shortcut list for important selectors */
- typedef Common::Array<KernelFuncWithSignature> KernelFuncsContainer;
- KernelFuncsContainer _kernelFuncs; /**< Table of kernel functions */
+ SelectorCache _selectorCache; /**< Shortcut list for important selectors. */
+ typedef Common::Array<KernelFunction> KernelFunctionArray;
+ KernelFunctionArray _kernelFuncs; /**< Table of kernel functions. */
/**
* Determines whether a list of registers matches a given signature.
* If no signature is given (i.e., if sig is NULL), this is always
* treated as a match.
*
- * @param segMan pointer to the segment manager
* @param sig signature to test against
* @param argc number of arguments to test
* @param argv argument list
* @return true if the signature was matched, false otherwise
*/
- bool signatureMatch(const char *sig, int argc, const reg_t *argv);
+ bool signatureMatch(const uint16 *sig, int argc, const reg_t *argv);
+
+ // Prints out debug information in case a signature check fails
+ void signatureDebug(const uint16 *sig, int argc, const reg_t *argv);
/**
* Determines the type of the object indicated by reg.
@@ -202,15 +202,15 @@ public:
* KSIG_INVALID set if the type of reg can be determined, but is invalid.
* 0 on error.
*/
- int findRegType(reg_t reg);
+ uint16 findRegType(reg_t reg);
/******************** Text functionality ********************/
/**
- * Looks up text referenced by scripts
- * SCI uses two values to reference to text: An address, and an index. The address
- * determines whether the text should be read from a resource file, or from the heap,
- * while the index either refers to the number of the string in the specified source,
- * or to a relative position inside the text.
+ * Looks up text referenced by scripts.
+ * SCI uses two values to reference to text: An address, and an index. The
+ * address determines whether the text should be read from a resource file,
+ * or from the heap, while the index either refers to the number of the
+ * string in the specified source, or to a relative position inside the text.
*
* @param address The address to look up
* @param index The relative index
@@ -218,22 +218,32 @@ public:
*/
Common::String lookupText(reg_t address, int index);
+ /**
+ * Loads the kernel function names.
+ *
+ * This function reads the kernel function name table from resource_map,
+ * and fills the _kernelNames array with them.
+ * The resulting list has the same format regardless of the format of the
+ * name table of the resource (the format changed between version 0 and 1).
+ */
+ void loadKernelNames(GameFeatures *features);
+
private:
/**
- * Sets the default kernel function names, based on the SCI version used
+ * Sets the default kernel function names, based on the SCI version used.
*/
- void setDefaultKernelNames(Common::String gameId);
+ void setDefaultKernelNames();
#ifdef ENABLE_SCI32
/**
- * Sets the default kernel function names to the SCI2 kernel functions
+ * Sets the default kernel function names to the SCI2 kernel functions.
*/
void setKernelNamesSci2();
/**
- * Sets the default kernel function names to the SCI2.1 kernel functions
+ * Sets the default kernel function names to the SCI2.1 kernel functions.
*/
- void setKernelNamesSci21();
+ void setKernelNamesSci21(GameFeatures *features);
#endif
/**
@@ -248,36 +258,26 @@ private:
Common::StringArray checkStaticSelectorNames();
/**
- * Maps special selectors
+ * Maps special selectors.
*/
void mapSelectors();
/**
- * Maps kernel functions
+ * Maps kernel functions.
*/
void mapFunctions();
ResourceManager *_resMan;
SegManager *_segMan;
- uint32 features;
// Kernel-related lists
Common::StringArray _selectorNames;
Common::StringArray _kernelNames;
+
+ const Common::String _invalid;
};
-#ifdef USE_OLD_MUSIC_FUNCTIONS
-/******************** Misc functions ********************/
-
-/**
- * Get all sound events, apply their changes to the heap
- */
-void process_sound_events(EngineState *s);
-
-/******************** Constants ********************/
-#endif
-
-/* Maximum length of a savegame name (including terminator character) */
+/* Maximum length of a savegame name (including terminator character). */
#define SCI_MAX_SAVENAME_LENGTH 0x24
/******************** Kernel functions ********************/
@@ -423,6 +423,7 @@ reg_t kStrSplit(EngineState *s, int argc, reg_t *argv);
reg_t kPlatform(EngineState *s, int argc, reg_t *argv);
reg_t kTextColors(EngineState *s, int argc, reg_t *argv);
reg_t kTextFonts(EngineState *s, int argc, reg_t *argv);
+reg_t kEmpty(EngineState *s, int argc, reg_t *argv);
#ifdef ENABLE_SCI32
// SCI2 Kernel Functions
@@ -430,6 +431,7 @@ reg_t kIsHiRes(EngineState *s, int argc, reg_t *argv);
reg_t kArray(EngineState *s, int argc, reg_t *argv);
reg_t kListAt(EngineState *s, int argc, reg_t *argv);
reg_t kString(EngineState *s, int argc, reg_t *argv);
+reg_t kMulDiv(EngineState *s, int argc, reg_t *argv);
// "Screen items" in SCI32 are views
reg_t kAddScreenItem(EngineState *s, int argc, reg_t *argv);
reg_t kUpdateScreenItem(EngineState *s, int argc, reg_t *argv);
@@ -451,12 +453,69 @@ reg_t kOnMe(EngineState *s, int argc, reg_t *argv);
reg_t kInPolygon(EngineState *s, int argc, reg_t *argv);
// SCI2.1 Kernel Functions
+reg_t kText(EngineState *s, int argc, reg_t *argv);
reg_t kSave(EngineState *s, int argc, reg_t *argv);
reg_t kList(EngineState *s, int argc, reg_t *argv);
reg_t kRobot(EngineState *s, int argc, reg_t *argv);
+reg_t kPlayVMD(EngineState *s, int argc, reg_t *argv);
+reg_t kIsOnMe(EngineState *s, int argc, reg_t *argv);
+reg_t kCD(EngineState *s, int argc, reg_t *argv);
#endif
+reg_t kDoSoundInit(EngineState *s, int argc, reg_t *argv);
+reg_t kDoSoundPlay(EngineState *s, int argc, reg_t *argv);
+reg_t kDoSoundDummy(EngineState *s, int argc, reg_t *argv);
+reg_t kDoSoundDispose(EngineState *s, int argc, reg_t *argv);
+reg_t kDoSoundMute(EngineState *s, int argc, reg_t *argv);
+reg_t kDoSoundStop(EngineState *s, int argc, reg_t *argv);
+reg_t kDoSoundStopAll(EngineState *s, int argc, reg_t *argv);
+reg_t kDoSoundPause(EngineState *s, int argc, reg_t *argv);
+reg_t kDoSoundResume(EngineState *s, int argc, reg_t *argv);
+reg_t kDoSoundMasterVolume(EngineState *s, int argc, reg_t *argv);
+reg_t kDoSoundUpdate(EngineState *s, int argc, reg_t *argv);
+reg_t kDoSoundFade(EngineState *s, int argc, reg_t *argv);
+reg_t kDoSoundGetPolyphony(EngineState *s, int argc, reg_t *argv);
+reg_t kDoSoundUpdateCues(EngineState *s, int argc, reg_t *argv);
+reg_t kDoSoundSendMidi(EngineState *s, int argc, reg_t *argv);
+reg_t kDoSoundReverb(EngineState *s, int argc, reg_t *argv);
+reg_t kDoSoundSetHold(EngineState *s, int argc, reg_t *argv);
+reg_t kDoSoundGetAudioCapability(EngineState *s, int argc, reg_t *argv);
+reg_t kDoSoundSuspend(EngineState *s, int argc, reg_t *argv);
+reg_t kDoSoundSetVolume(EngineState *s, int argc, reg_t *argv);
+reg_t kDoSoundSetPriority(EngineState *s, int argc, reg_t *argv);
+reg_t kDoSoundSetLoop(EngineState *s, int argc, reg_t *argv);
+
+reg_t kGraphGetColorCount(EngineState *s, int argc, reg_t *argv);
+reg_t kGraphDrawLine(EngineState *s, int argc, reg_t *argv);
+reg_t kGraphSaveBox(EngineState *s, int argc, reg_t *argv);
+reg_t kGraphRestoreBox(EngineState *s, int argc, reg_t *argv);
+reg_t kGraphFillBoxBackground(EngineState *s, int argc, reg_t *argv);
+reg_t kGraphFillBoxForeground(EngineState *s, int argc, reg_t *argv);
+reg_t kGraphFillBoxAny(EngineState *s, int argc, reg_t *argv);
+reg_t kGraphUpdateBox(EngineState *s, int argc, reg_t *argv);
+reg_t kGraphRedrawBox(EngineState *s, int argc, reg_t *argv);
+reg_t kGraphAdjustPriority(EngineState *s, int argc, reg_t *argv);
+reg_t kGraphSaveUpscaledHiresBox(EngineState *s, int argc, reg_t *argv);
+
+reg_t kPalVaryInit(EngineState *s, int argc, reg_t *argv);
+reg_t kPalVaryReverse(EngineState *s, int argc, reg_t *argv);
+reg_t kPalVaryGetCurrentStep(EngineState *s, int argc, reg_t *argv);
+reg_t kPalVaryDeinit(EngineState *s, int argc, reg_t *argv);
+reg_t kPalVaryChangeTarget(EngineState *s, int argc, reg_t *argv);
+reg_t kPalVaryChangeTicks(EngineState *s, int argc, reg_t *argv);
+reg_t kPalVaryPauseResume(EngineState *s, int argc, reg_t *argv);
+reg_t kPalVaryUnknown(EngineState *s, int argc, reg_t *argv);
+
+reg_t kPaletteSetFromResource(EngineState *s, int argc, reg_t *argv);
+reg_t kPaletteSetFlag(EngineState *s, int argc, reg_t *argv);
+reg_t kPaletteUnsetFlag(EngineState *s, int argc, reg_t *argv);
+reg_t kPaletteSetIntensity(EngineState *s, int argc, reg_t *argv);
+reg_t kPaletteFindColor(EngineState *s, int argc, reg_t *argv);
+reg_t kPaletteAnimate(EngineState *s, int argc, reg_t *argv);
+reg_t kPaletteSave(EngineState *s, int argc, reg_t *argv);
+reg_t kPaletteRestore(EngineState *s, int argc, reg_t *argv);
+
//@}
} // End of namespace Sci
diff --git a/engines/sci/engine/kernel32.cpp b/engines/sci/engine/kernel32.cpp
index 0afdc3f2eb..eab1b90139 100644
--- a/engines/sci/engine/kernel32.cpp
+++ b/engines/sci/engine/kernel32.cpp
@@ -30,9 +30,8 @@
#include "sci/engine/segment.h"
#include "sci/engine/state.h"
#include "sci/engine/selector.h"
-#include "sci/graphics/gui.h"
-#include "sci/graphics/gui32.h"
#include "sci/graphics/frameout.h"
+#include "sci/graphics/screen.h"
#include "common/system.h"
@@ -337,7 +336,7 @@ static const char *sci21_default_knames[] = {
/*0x80*/ "Dummy",
/*0x81*/ "Dummy",
/*0x82*/ "Dummy",
- /*0x83*/ "Dummy",
+ /*0x83*/ "PrintDebug", // used by the Shivers 2 demo
/*0x84*/ "Dummy",
/*0x85*/ "Dummy",
/*0x86*/ "Dummy",
@@ -378,15 +377,17 @@ void Kernel::setKernelNamesSci2() {
_kernelNames = Common::StringArray(sci2_default_knames, kKernelEntriesSci2);
}
-void Kernel::setKernelNamesSci21() {
- // Some SCI games use a modified SCI2 kernel table instead of the SCI2.1/SCI3 kernel table.
- // The GK2 demo does this as well as at least one version of KQ7. We detect which version
- // to use based on where kDoSound is called from Sound::play().
+void Kernel::setKernelNamesSci21(GameFeatures *features) {
+ // Some SCI games use a modified SCI2 kernel table instead of the
+ // SCI2.1/SCI3 kernel table. The GK2 demo does this as well as at
+ // least one version of KQ7. We detect which version to use based on
+ // where kDoSound is called from Sound::play().
- // This is interesting because they all have the same interpreter version (2.100.002), yet
- // they would not be compatible with other games of the same interpreter.
+ // This is interesting because they all have the same interpreter
+ // version (2.100.002), yet they would not be compatible with other
+ // games of the same interpreter.
- if (g_sci->_features->detectSci21KernelType() == SCI_VERSION_2) {
+ if (features->detectSci21KernelType() == SCI_VERSION_2) {
_kernelNames = Common::StringArray(sci2_default_knames, kKernelEntriesGk2Demo);
// OnMe is IsOnMe here, but they should be compatible
_kernelNames[0x23] = "Robot"; // Graph in SCI2
@@ -400,7 +401,8 @@ void Kernel::setKernelNamesSci21() {
// SCI2 Kernel Functions
reg_t kIsHiRes(EngineState *s, int argc, reg_t *argv) {
- // Returns 0 if the screen width or height is less than 640 or 400, respectively.
+ // Returns 0 if the screen width or height is less than 640 or 400,
+ // respectively.
if (g_system->getWidth() < 640 || g_system->getHeight() < 400)
return make_reg(0, 0);
@@ -461,6 +463,13 @@ reg_t kArray(EngineState *s, int argc, reg_t *argv) {
return argv[1];
}
case 6: { // Cpy
+ if (s->_segMan->getSegmentObj(argv[1].segment)->getType() != SEG_TYPE_ARRAY ||
+ s->_segMan->getSegmentObj(argv[3].segment)->getType() != SEG_TYPE_ARRAY) {
+ // Happens in the RAMA demo
+ warning("kArray(Cpy): Request to copy a segment which isn't an array, ignoring");
+ return NULL_REG;
+ }
+
SciArray<reg_t> *array1 = s->_segMan->lookupArray(argv[1]);
SciArray<reg_t> *array2 = s->_segMan->lookupArray(argv[3]);
uint32 index1 = argv[2].toUint16();
@@ -509,6 +518,19 @@ reg_t kArray(EngineState *s, int argc, reg_t *argv) {
return NULL_REG;
}
+reg_t kText(EngineState *s, int argc, reg_t *argv) {
+ switch (argv[0].toUint16()) {
+ case 0:
+ return kTextSize(s, argc - 1, argv + 1);
+ default:
+ // TODO: Other subops here too, perhaps kTextColors and kTextFonts
+ warning("kText(%d)", argv[0].toUint16());
+ break;
+ }
+
+ return s->r_acc;
+}
+
reg_t kString(EngineState *s, int argc, reg_t *argv) {
switch (argv[0].toUint16()) {
case 0: { // New
@@ -594,21 +616,24 @@ reg_t kString(EngineState *s, int argc, reg_t *argv) {
if (argv[1].segment == s->_segMan->getSysStringsSegment()) {
// Resize if necessary
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));
+ SystemString *sysString = s->_segMan->getSystemString(sysStringId);
+ assert(sysString);
+ if ((uint32)sysString->_maxSize < index1 + count) {
+ free(sysString->_value);
+ sysString->_maxSize = index1 + count;
+ sysString->_value = (char *)calloc(index1 + count, sizeof(char));
}
- strncpy(s->_segMan->sysStrings->_strings[sysStringId]._value + index1, string2 + index2, count);
+ strncpy(sysString->_value + index1, string2 + index2, count);
} else {
SciString *string1 = s->_segMan->lookupString(argv[1]);
if (string1->getSize() < index1 + count)
string1->setSize(index1 + count);
- // 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].
+ // 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[i + index2]);
}
@@ -791,8 +816,76 @@ reg_t kOnMe(EngineState *s, int argc, reg_t *argv) {
nsRect.top = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsTop));
nsRect.right = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsRight));
nsRect.bottom = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsBottom));
+ uint16 itemX = readSelectorValue(s->_segMan, targetObject, SELECTOR(x));
+ uint16 itemY = readSelectorValue(s->_segMan, targetObject, SELECTOR(y));
+
+ // If top and left are negative, we need to adjust coordinates by
+ // the item's x and y (e.g. happens in GK1, day 1, with detective
+ // Mosely's hotspot in his office)
- //warning("kOnMe: (%d, %d) on object %04x:%04x, parameter %d", argv[0].toUint16(), argv[1].toUint16(), PRINT_REG(argv[2]), argv[3].toUint16());
+ if (nsRect.left < 0)
+ nsRect.translate(itemX, 0);
+
+ if (nsRect.top < 0)
+ nsRect.translate(0, itemY);
+
+ // HACK: nsLeft and nsTop can be invalid, so try and fix them here
+ // using x and y (e.g. with the inventory screen in GK1)
+ if (nsRect.left == itemY && nsRect.top == itemX) {
+ // Swap the values, as they're inversed (eh???)
+ nsRect.left = itemX;
+ nsRect.top = itemY;
+ }
+
+ /*
+ warning("kOnMe: (%d, %d) on object %04x:%04x (%s), rect (%d, %d, %d, %d), parameter %d",
+ argv[0].toUint16(), argv[1].toUint16(), PRINT_REG(argv[2]), s->_segMan->getObjectName(argv[2]),
+ nsRect.left, nsRect.top, nsRect.right, nsRect.bottom,
+ argv[3].toUint16());
+ */
+
+ return make_reg(0, nsRect.contains(x, y));
+}
+
+reg_t kIsOnMe(EngineState *s, int argc, reg_t *argv) {
+ // Tests if the cursor is on the passed object, after adjusting the
+ // coordinates of the object according to the object's plane
+
+ uint16 x = argv[0].toUint16();
+ uint16 y = argv[1].toUint16();
+ reg_t targetObject = argv[2];
+ // TODO: argv[3] - it's usually 0
+ Common::Rect nsRect;
+
+ // Get the bounding rectangle of the object
+ 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));
+
+ // Get the object's plane
+ reg_t planeObject = readSelector(s->_segMan, targetObject, SELECTOR(plane));
+ if (!planeObject.isNull()) {
+ uint16 itemX = readSelectorValue(s->_segMan, targetObject, SELECTOR(x));
+ uint16 itemY = readSelectorValue(s->_segMan, targetObject, SELECTOR(y));
+ uint16 planeResY = readSelectorValue(s->_segMan, planeObject, SELECTOR(resY));
+ uint16 planeResX = readSelectorValue(s->_segMan, planeObject, SELECTOR(resX));
+ uint16 planeTop = readSelectorValue(s->_segMan, planeObject, SELECTOR(top));
+ uint16 planeLeft = readSelectorValue(s->_segMan, planeObject, SELECTOR(left));
+ planeTop = (planeTop * g_sci->_gfxScreen->getHeight()) / planeResY;
+ planeLeft = (planeLeft * g_sci->_gfxScreen->getWidth()) / planeResX;
+
+ // Adjust the bounding rectangle of the object by the object's
+ // actual X, Y coordinates
+ itemY = ((itemY * g_sci->_gfxScreen->getHeight()) / planeResY);
+ itemX = ((itemX * g_sci->_gfxScreen->getWidth()) / planeResX);
+ itemY += planeTop;
+ itemX += planeLeft;
+
+ nsRect.translate(itemX, itemY);
+ }
+
+ //warning("kIsOnMe: (%d, %d) on object %04x:%04x, parameter %d", argv[0].toUint16(), argv[1].toUint16(), PRINT_REG(argv[2]), argv[3].toUint16());
return make_reg(0, nsRect.contains(x, y));
}
@@ -805,7 +898,7 @@ 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
switch (argv[0].toUint16()) {
- case 0:
+ case 0: {
if (argc != 4) {
warning("kCreateTextBitmap(0): expected 4 arguments, got %i", argc);
return NULL_REG;
@@ -814,6 +907,22 @@ reg_t kCreateTextBitmap(EngineState *s, int argc, reg_t *argv) {
Common::String text = s->_segMan->getString(readSelector(s->_segMan, object, SELECTOR(text)));
debug("kCreateTextBitmap: %s", text.c_str());
}
+ default:
+ warning("CreateTextBitmap(%d)", argv[0].toUint16());
+ }
+
+ return NULL_REG;
+}
+
+reg_t kCD(EngineState *s, int argc, reg_t *argv) {
+ // TODO: Stub
+ switch (argv[0].toUint16()) {
+ case 0:
+ // Return whether the contents of disc argv[1] is available.
+ return TRUE_REG;
+ default:
+ warning("CD(%d)", argv[0].toUint16());
+ }
return NULL_REG;
}
diff --git a/engines/sci/engine/kevent.cpp b/engines/sci/engine/kevent.cpp
index fd7711f196..7547ad5ab6 100644
--- a/engines/sci/engine/kevent.cpp
+++ b/engines/sci/engine/kevent.cpp
@@ -31,8 +31,6 @@
#include "sci/console.h"
#include "sci/debug.h" // for g_debug_simulated_key
#include "sci/event.h"
-#include "sci/graphics/gui.h"
-#include "sci/graphics/gui32.h"
#include "sci/graphics/coordadjuster.h"
#include "sci/graphics/cursor.h"
@@ -43,7 +41,7 @@ namespace Sci {
reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) {
int mask = argv[0].toUint16();
reg_t obj = argv[1];
- sciEvent curEvent;
+ SciEvent curEvent;
int oldx, oldy;
int modifier_mask = getSciVersion() <= SCI_VERSION_01 ? SCI_KEYMOD_ALL : SCI_KEYMOD_NO_FOOLOCK;
SegManager *segMan = s->_segMan;
@@ -67,7 +65,7 @@ reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) {
oldx = mousePos.x;
oldy = mousePos.y;
- curEvent = s->_event->get(mask);
+ curEvent = g_sci->getEventManager()->getSciEvent(mask);
if (g_sci->getVocabulary())
g_sci->getVocabulary()->parser_event = NULL_REG; // Invalidate parser event
@@ -79,7 +77,9 @@ reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) {
switch (curEvent.type) {
case SCI_EVENT_QUIT:
- quit_vm(s);
+ s->abortScriptProcessing = kAbortQuitGame; // Terminate VM
+ g_sci->_debugState.seeking = kDebugSeekNothing;
+ g_sci->_debugState.runningStep = 0;
break;
case SCI_EVENT_KEYBOARD:
@@ -124,12 +124,12 @@ reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) {
s->r_acc = NULL_REG; // Unknown or no event
}
- if ((s->r_acc.offset) && (g_debugState.stopOnEvent)) {
- g_debugState.stopOnEvent = false;
+ if ((s->r_acc.offset) && (g_sci->_debugState.stopOnEvent)) {
+ g_sci->_debugState.stopOnEvent = false;
- // A SCI event occured, and we have been asked to stop, so open the debug console
+ // A SCI event occurred, and we have been asked to stop, so open the debug console
Console *con = g_sci->getSciDebugger();
- con->DebugPrintf("SCI event occured: ");
+ con->DebugPrintf("SCI event occurred: ");
switch (curEvent.type) {
case SCI_EVENT_QUIT:
con->DebugPrintf("quit event\n");
@@ -149,16 +149,14 @@ reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) {
con->onFrame();
}
-#ifndef USE_OLD_MUSIC_FUNCTIONS
if (g_sci->_features->detectDoSoundType() <= SCI_VERSION_0_LATE) {
// If we're running a SCI0 game, update the sound cues, to compensate
// for the fact that SCI0 does not poll to update the sound cues itself,
// like SCI01 and later do with cmdUpdateSoundCues. kGetEvent is called
// quite often, so emulate the SCI01 behavior of cmdUpdateSoundCues with
// this call
- s->_soundCmd->updateSci0Cues();
+ g_sci->_soundCmd->updateSci0Cues();
}
-#endif
return s->r_acc;
}
@@ -203,7 +201,10 @@ reg_t kMapKeyToDir(EngineState *s, int argc, reg_t *argv) {
}
if (mover >= 0) {
- writeSelectorValue(segMan, obj, SELECTOR(type), SCI_EVENT_JOYSTICK);
+ if (g_sci->getEventManager()->getUsesNewKeyboardDirectionType())
+ writeSelectorValue(segMan, obj, SELECTOR(type), SCI_EVENT_KEYBOARD | SCI_EVENT_DIRECTION);
+ else
+ writeSelectorValue(segMan, obj, SELECTOR(type), SCI_EVENT_DIRECTION);
writeSelectorValue(segMan, obj, SELECTOR(message), mover);
return make_reg(0, 1);
} else
diff --git a/engines/sci/engine/kfile.cpp b/engines/sci/engine/kfile.cpp
index 3e0ecd1a28..807edc63a5 100644
--- a/engines/sci/engine/kfile.cpp
+++ b/engines/sci/engine/kfile.cpp
@@ -36,12 +36,9 @@
namespace Sci {
-enum {
- MAX_SAVEGAME_NR = 20 /**< Maximum number of savegames */
-};
-
struct SavegameDesc {
- int id;
+ uint id;
+ int virtualId; // straight numbered, according to id but w/o gaps
int date;
int time;
int version;
@@ -115,15 +112,17 @@ void file_open(EngineState *s, const char *filename, int mode) {
if (mode == _K_FILE_MODE_OPEN_OR_FAIL) {
// Try to open file, abort if not possible
inFile = saveFileMan->openForLoading(wrappedName);
- // If no matching savestate exists: fall back to reading from a regular file
+ // If no matching savestate exists: fall back to reading from a regular
+ // file
if (!inFile)
inFile = SearchMan.createReadStreamForMember(englishName);
- // Special case for LSL3: It tries to create a new dummy file, LARRY3.DRV
- // Apparently, if the file doesn't exist here, it should be created. The game
- // scripts then go ahead and fill its contents with data. It seems to be a similar
- // case as the dummy MEMORY.DRV file in LSL5, but LSL5 creates the file if it can't
- // find it with a separate call to file_open()
+ // Special case for LSL3: It tries to create a new dummy file,
+ // LARRY3.DRV. Apparently, if the file doesn't exist here, it should be
+ // created. The game scripts then go ahead and fill its contents with
+ // data. It seems to be a similar case as the dummy MEMORY.DRV file in
+ // LSL5, but LSL5 creates the file if it can't find it with a separate
+ // call to file_open().
if (!inFile && englishName == "LARRY3.DRV") {
outFile = saveFileMan->openForSaving(wrappedName);
outFile->finalize();
@@ -133,27 +132,28 @@ void file_open(EngineState *s, const char *filename, int mode) {
}
if (!inFile)
- warning("file_open(_K_FILE_MODE_OPEN_OR_FAIL) failed to open file '%s'", englishName.c_str());
+ warning(" -> file_open(_K_FILE_MODE_OPEN_OR_FAIL): failed to open file '%s'", englishName.c_str());
} else if (mode == _K_FILE_MODE_CREATE) {
// Create the file, destroying any content it might have had
outFile = saveFileMan->openForSaving(wrappedName);
if (!outFile)
- warning("file_open(_K_FILE_MODE_CREATE) failed to create file '%s'", englishName.c_str());
+ warning(" -> file_open(_K_FILE_MODE_CREATE): failed to create file '%s'", englishName.c_str());
} else if (mode == _K_FILE_MODE_OPEN_OR_CREATE) {
// Try to open file, create it if it doesn't exist
outFile = saveFileMan->openForSaving(wrappedName);
if (!outFile)
- warning("file_open(_K_FILE_MODE_CREATE) failed to create file '%s'", englishName.c_str());
- // QfG1 opens the character export file with _K_FILE_MODE_CREATE first, closes it immediately and opens it again
- // with this here
- // Perhaps other games use this for read access as well
- // I guess changing this whole code into using virtual files and writing them after close would be more appropriate
+ warning(" -> file_open(_K_FILE_MODE_CREATE): failed to create file '%s'", englishName.c_str());
+ // QfG1 opens the character export file with _K_FILE_MODE_CREATE first,
+ // closes it immediately and opens it again with this here. Perhaps
+ // other games use this for read access as well. I guess changing this
+ // whole code into using virtual files and writing them after close
+ // would be more appropriate.
} else {
error("file_open: unsupported mode %d (filename '%s')", mode, englishName.c_str());
}
if (!inFile && !outFile) { // Failed
- debug(3, "file_open() failed");
+ debugC(2, kDebugLevelFile, " -> file_open() failed");
s->r_acc = SIGNAL_REG;
return;
}
@@ -163,7 +163,8 @@ void file_open(EngineState *s, const char *filename, int mode) {
while ((handle < s->_fileHandles.size()) && s->_fileHandles[handle].isOpen())
handle++;
- if (handle == s->_fileHandles.size()) { // Hit size limit => Allocate more space
+ if (handle == s->_fileHandles.size()) {
+ // Hit size limit => Allocate more space
s->_fileHandles.resize(s->_fileHandles.size() + 1);
}
@@ -173,14 +174,14 @@ void file_open(EngineState *s, const char *filename, int mode) {
s->r_acc = make_reg(0, handle);
- debug(3, " -> opened file '%s' with handle %d", englishName.c_str(), handle);
+ debugC(2, kDebugLevelFile, " -> opened file '%s' with handle %d", englishName.c_str(), handle);
}
reg_t kFOpen(EngineState *s, int argc, reg_t *argv) {
Common::String name = s->_segMan->getString(argv[0]);
int mode = argv[1].toUint16();
- debug(3, "kFOpen(%s,0x%x)", name.c_str(), mode);
+ debugC(2, kDebugLevelFile, "kFOpen(%s,0x%x)", name.c_str(), mode);
file_open(s, name.c_str(), mode);
return s->r_acc;
}
@@ -200,7 +201,7 @@ static FileHandle *getFileFromHandle(EngineState *s, uint handle) {
}
reg_t kFClose(EngineState *s, int argc, reg_t *argv) {
- debug(3, "kFClose(%d)", argv[0].toUint16());
+ debugC(2, kDebugLevelFile, "kFClose(%d)", argv[0].toUint16());
if (argv[0] != SIGNAL_REG) {
FileHandle *f = getFileFromHandle(s, argv[0].toUint16());
if (f)
@@ -221,8 +222,6 @@ reg_t kFPuts(EngineState *s, int argc, reg_t *argv) {
}
static void fgets_wrapper(EngineState *s, char *dest, int maxsize, int handle) {
- debugC(2, kDebugLevelFile, "FGets'ing %d bytes from handle %d", maxsize, handle);
-
FileHandle *f = getFileFromHandle(s, handle);
if (!f)
return;
@@ -244,66 +243,7 @@ static void fgets_wrapper(EngineState *s, char *dest, int maxsize, int handle) {
*dest = f->_in->readByte();
}
- debugC(2, kDebugLevelFile, "FGets'ed \"%s\"", dest);
-}
-
-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) {
- Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager();
-
- // Load all saves
- Common::StringArray saveNames = saveFileMan->listSavefiles(g_sci->getSavegamePattern());
-
- for (Common::StringArray::const_iterator iter = saveNames.begin(); iter != saveNames.end(); ++iter) {
- Common::String filename = *iter;
- Common::SeekableReadStream *in;
- if ((in = saveFileMan->openForLoading(filename))) {
- SavegameMetadata meta;
- if (!get_savegame_metadata(in, &meta) || meta.savegame_name.empty()) {
- // invalid
- delete in;
- continue;
- }
- delete in;
-
- SavegameDesc desc;
- desc.id = strtol(filename.end() - 3, NULL, 10);
- desc.date = meta.savegame_date;
- // 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);
- }
- }
-
- // Sort the list by creation date of the saves
- Common::sort(saves.begin(), saves.end(), _savegame_index_struct_compare);
-}
-
-bool Console::cmdListSaves(int argc, const char **argv) {
- Common::Array<SavegameDesc> saves;
- listSavegames(saves);
-
- for (uint i = 0; i < saves.size(); i++) {
- Common::String filename = g_sci->getSavegameName(saves[i].id);
- DebugPrintf("%s: '%s'\n", filename.c_str(), saves[i].name);
- }
-
- return true;
+ debugC(2, kDebugLevelFile, " -> FGets'ed \"%s\"", dest);
}
reg_t kFGets(EngineState *s, int argc, reg_t *argv) {
@@ -311,7 +251,7 @@ reg_t kFGets(EngineState *s, int argc, reg_t *argv) {
char *buf = new char[maxsize];
int handle = argv[2].toUint16();
- debug(3, "kFGets(%d,%d)", handle, maxsize);
+ debugC(2, kDebugLevelFile, "kFGets(%d, %d)", handle, maxsize);
fgets_wrapper(s, buf, maxsize, handle);
s->_segMan->memcpy(argv[0], (const byte*)buf, maxsize);
return argv[0];
@@ -326,11 +266,14 @@ reg_t kGetCWD(EngineState *s, int argc, reg_t *argv) {
// TODO/FIXME: Is "/" a good value? Maybe "" or "." or "C:\" are better?
s->_segMan->strcpy(argv[0], "/");
- debug(3, "kGetCWD() -> %s", "/");
+ debugC(2, kDebugLevelFile, "kGetCWD() -> %s", "/");
return argv[0];
}
+static void listSavegames(Common::Array<SavegameDesc> &saves);
+static int findSavegame(Common::Array<SavegameDesc> &saves, uint saveId);
+
enum {
K_DEVICE_INFO_GET_DEVICE = 0,
K_DEVICE_INFO_GET_CURRENT_DEVICE = 1,
@@ -389,21 +332,25 @@ reg_t kDeviceInfo(EngineState *s, int argc, reg_t *argv) {
break;
case K_DEVICE_INFO_GET_SAVEFILE_NAME: {
Common::String game_prefix = s->_segMan->getString(argv[2]);
- int savegame_id = argv[3].toUint16();
+ uint virtualId = argv[3].toUint16();
s->_segMan->strcpy(argv[1], "__throwaway");
- debug(3, "K_DEVICE_INFO_GET_SAVEFILE_NAME(%s,%d) -> %s", game_prefix.c_str(), savegame_id, "__throwaway");
+ debug(3, "K_DEVICE_INFO_GET_SAVEFILE_NAME(%s,%d) -> %s", game_prefix.c_str(), virtualId, "__throwaway");
+ if ((virtualId < SAVEGAMEID_OFFICIALRANGE_START) || (virtualId > SAVEGAMEID_OFFICIALRANGE_END))
+ error("kDeviceInfo(deleteSave): invalid savegame-id specified");
+ uint savegameId = virtualId - SAVEGAMEID_OFFICIALRANGE_START;
Common::Array<SavegameDesc> saves;
listSavegames(saves);
- int savedir_nr = saves[savegame_id].id;
- Common::String filename = g_sci->getSavegameName(savedir_nr);
- Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager();
- saveFileMan->removeSavefile(filename);
+ if (findSavegame(saves, savegameId) != -1) {
+ // Confirmed that this id still lives...
+ Common::String filename = g_sci->getSavegameName(savegameId);
+ Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager();
+ saveFileMan->removeSavefile(filename);
}
break;
+ }
default:
- // TODO: Not all sub-commands are handled. E.g. KQ5CD calls sub-command 5
- warning("Unknown DeviceInfo() sub-command: %d", mode);
+ error("Unknown DeviceInfo() sub-command: %d", mode);
break;
}
@@ -430,27 +377,108 @@ reg_t kCheckFreeSpace(EngineState *s, int argc, reg_t *argv) {
Common::String path = s->_segMan->getString(argv[0]);
debug(3, "kCheckFreeSpace(%s)", path.c_str());
- // We simply always pretend that there is enough space.
- // The alternative would be to write a big test file, which is not nice
- // on systems where doing so is very slow.
+ // We simply always pretend that there is enough space. The alternative
+ // would be to write a big test file, which is not nice on systems where
+ // doing so is very slow.
return make_reg(0, 1);
}
+static bool _savegame_sort_byDate(const SavegameDesc &l, const SavegameDesc &r) {
+ if (l.date != r.date)
+ return (l.date > r.date);
+ return (l.time > r.time);
+}
+
+// Create a sorted array containing all found savedgames
+static void listSavegames(Common::Array<SavegameDesc> &saves) {
+ Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager();
+
+ // Load all saves
+ Common::StringArray saveNames = saveFileMan->listSavefiles(g_sci->getSavegamePattern());
+
+ for (Common::StringArray::const_iterator iter = saveNames.begin(); iter != saveNames.end(); ++iter) {
+ Common::String filename = *iter;
+ Common::SeekableReadStream *in;
+ if ((in = saveFileMan->openForLoading(filename))) {
+ SavegameMetadata meta;
+ if (!get_savegame_metadata(in, &meta) || meta.savegame_name.empty()) {
+ // invalid
+ delete in;
+ continue;
+ }
+ delete in;
+
+ SavegameDesc desc;
+ desc.id = strtol(filename.end() - 3, NULL, 10);
+ desc.date = meta.savegame_date;
+ // We need to fix date in here, because we save DDMMYYYY instead of
+ // YYYYMMDD, so sorting wouldn't 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);
+ }
+ }
+
+ // Sort the list by creation date of the saves
+ Common::sort(saves.begin(), saves.end(), _savegame_sort_byDate);
+}
+
+// Find a savedgame according to virtualId and return the position within our array
+static int findSavegame(Common::Array<SavegameDesc> &saves, uint savegameId) {
+ for (uint saveNr = 0; saveNr < saves.size(); saveNr++) {
+ if (saves[saveNr].id == savegameId)
+ return saveNr;
+ }
+ return -1;
+}
+
+// The scripts get IDs ranging from 1000->1999, because the scripts require us to assign unique ids THAT EVEN STAY BETWEEN
+// SAVES and the scripts also use "saves-count + 1" to create a new savedgame slot.
+// SCI1.1 actually recycles ids, in that case we will currently get "0".
+// This behaviour is required especially for LSL6. In this game, it's possible to quick save. The scripts will use
+// the last-used id for that feature. If we don't assign sticky ids, the feature will overwrite different saves all the
+// time. And sadly we can't just use the actual filename ids directly, because of the creation method for new slots.
+
+bool Console::cmdListSaves(int argc, const char **argv) {
+ Common::Array<SavegameDesc> saves;
+ listSavegames(saves);
+
+ for (uint i = 0; i < saves.size(); i++) {
+ Common::String filename = g_sci->getSavegameName(saves[i].id);
+ DebugPrintf("%s: '%s'\n", filename.c_str(), saves[i].name);
+ }
+
+ return true;
+}
+
reg_t kCheckSaveGame(EngineState *s, int argc, reg_t *argv) {
Common::String game_id = s->_segMan->getString(argv[0]);
- uint16 savedir_nr = argv[1].toUint16();
+ uint16 virtualId = argv[1].toUint16();
- debug(3, "kCheckSaveGame(%s, %d)", game_id.c_str(), savedir_nr);
+ debug(3, "kCheckSaveGame(%s, %d)", game_id.c_str(), virtualId);
Common::Array<SavegameDesc> saves;
listSavegames(saves);
- // Check for savegame slot being out of range
- if (savedir_nr >= saves.size())
+ // Find saved-game
+ if ((virtualId < SAVEGAMEID_OFFICIALRANGE_START) || (virtualId > SAVEGAMEID_OFFICIALRANGE_END))
+ error("kCheckSaveGame: called with invalid savegameId!");
+ uint savegameId = virtualId - SAVEGAMEID_OFFICIALRANGE_START;
+ int savegameNr = findSavegame(saves, savegameId);
+ if (savegameNr == -1)
return NULL_REG;
// Check for compatible savegame version
- int ver = saves[savedir_nr].version;
+ int ver = saves[savegameNr].version;
if (ver < MINIMUM_SAVEGAME_VERSION || ver > CURRENT_SAVEGAME_VERSION)
return NULL_REG;
@@ -463,9 +491,12 @@ reg_t kGetSaveFiles(EngineState *s, int argc, reg_t *argv) {
debug(3, "kGetSaveFiles(%s)", game_id.c_str());
+ // Scripts ask for current save files, we can assume that if afterwards they ask us to create a new slot they really
+ // mean new slot instead of overwriting the old one
+ s->_lastSaveVirtualId = SAVEGAMEID_OFFICIALRANGE_START;
+
Common::Array<SavegameDesc> saves;
listSavegames(saves);
-
uint totalSaves = MIN<uint>(saves.size(), MAX_SAVEGAME_NR);
reg_t *slot = s->_segMan->derefRegPtr(argv[2], totalSaves);
@@ -480,7 +511,7 @@ reg_t kGetSaveFiles(EngineState *s, int argc, reg_t *argv) {
char *saveNamePtr = saveNames;
for (uint i = 0; i < totalSaves; i++) {
- *slot++ = make_reg(0, i); // Store slot
+ *slot++ = make_reg(0, saves[i].id + SAVEGAMEID_OFFICIALRANGE_START); // Store the virtual savegame-id ffs. see above
strcpy(saveNamePtr, saves[i].name);
saveNamePtr += SCI_MAX_SAVENAME_LENGTH;
}
@@ -495,46 +526,51 @@ reg_t kGetSaveFiles(EngineState *s, int argc, reg_t *argv) {
reg_t kSaveGame(EngineState *s, int argc, reg_t *argv) {
Common::String game_id = s->_segMan->getString(argv[0]);
- int savedir_nr = argv[1].toUint16();
- int savedir_id; // Savegame ID, derived from savedir_nr and the savegame ID list
+ uint virtualId = argv[1].toUint16();
Common::String game_description = s->_segMan->getString(argv[2]);
Common::String version;
if (argc > 3)
version = s->_segMan->getString(argv[3]);
- debug(3, "kSaveGame(%s,%d,%s,%s)", game_id.c_str(), savedir_nr, game_description.c_str(), version.c_str());
+ debug(3, "kSaveGame(%s,%d,%s,%s)", game_id.c_str(), virtualId, game_description.c_str(), version.c_str());
Common::Array<SavegameDesc> saves;
listSavegames(saves);
- if (savedir_nr >= 0 && (uint)savedir_nr < saves.size()) {
- // Overwrite
- savedir_id = saves[savedir_nr].id;
- } else if (savedir_nr >= 0 && savedir_nr < MAX_SAVEGAME_NR) {
- uint i = 0;
-
- savedir_id = 0;
-
- // First, look for holes
- while (i < saves.size()) {
- if (saves[i].id == savedir_id) {
- ++savedir_id;
- i = 0;
- } else
- ++i;
- }
- if (savedir_id >= MAX_SAVEGAME_NR) {
- warning("Internal error: Free savegame ID is %d, shouldn't happen", savedir_id);
+ uint savegameId;
+ if ((virtualId >= SAVEGAMEID_OFFICIALRANGE_START) && (virtualId <= SAVEGAMEID_OFFICIALRANGE_END)) {
+ // savegameId is an actual Id, so search for it just to make sure
+ savegameId = virtualId - SAVEGAMEID_OFFICIALRANGE_START;
+ if (findSavegame(saves, savegameId) != -1)
return NULL_REG;
+ } else if (virtualId < SAVEGAMEID_OFFICIALRANGE_START) {
+ // virtualId is low, we assume that scripts expect us to create new slot
+ if (virtualId == s->_lastSaveVirtualId) {
+ // if last virtual id is the same as this one, we assume that caller wants to overwrite last save
+ savegameId = s->_lastSaveNewId;
+ } else {
+ uint savegameNr;
+ // savegameId is in lower range, scripts expect us to create a new slot
+ for (savegameId = 0; savegameId < SAVEGAMEID_OFFICIALRANGE_START; savegameId++) {
+ for (savegameNr = 0; savegameNr < saves.size(); savegameNr++) {
+ if (savegameId == saves[savegameNr].id)
+ break;
+ }
+ if (savegameNr == saves.size())
+ break;
+ }
+ if (savegameId == SAVEGAMEID_OFFICIALRANGE_START)
+ error("kSavegame: no more savegame slots available");
}
-
- // This loop terminates when savedir_id is not in [x | ex. n. saves [n].id = x]
} else {
- warning("Savegame ID %d is not allowed", savedir_nr);
- return NULL_REG;
+ error("kSaveGame: invalid savegameId used");
}
- Common::String filename = g_sci->getSavegameName(savedir_id);
+ // Save in case caller wants to overwrite last newly created save
+ s->_lastSaveVirtualId = virtualId;
+ s->_lastSaveNewId = savegameId;
+
+ Common::String filename = g_sci->getSavegameName(savegameId);
Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager();
Common::OutSaveFile *out;
if (!(out = saveFileMan->openForSaving(filename))) {
@@ -543,7 +579,7 @@ reg_t kSaveGame(EngineState *s, int argc, reg_t *argv) {
return NULL_REG;
}
- if (gamestate_save(s, out, game_description.c_str(), version.c_str())) {
+ if (!gamestate_save(s, out, game_description.c_str(), version.c_str())) {
warning("Saving the game failed.");
s->r_acc = NULL_REG;
} else {
@@ -563,35 +599,37 @@ reg_t kSaveGame(EngineState *s, int argc, reg_t *argv) {
reg_t kRestoreGame(EngineState *s, int argc, reg_t *argv) {
Common::String game_id = !argv[0].isNull() ? s->_segMan->getString(argv[0]) : "";
- int savedir_nr = argv[1].toUint16();
+ uint savegameId = argv[1].toUint16();
- debug(3, "kRestoreGame(%s,%d)", game_id.c_str(), savedir_nr);
+ debug(3, "kRestoreGame(%s,%d)", game_id.c_str(), savegameId);
- if (!argv[0].isNull()) {
- Common::Array<SavegameDesc> saves;
- listSavegames(saves);
+ if ((savegameId < 1000) || (savegameId > 1999)) {
+ warning("Savegame ID %d is not allowed", savegameId);
+ return TRUE_REG;
+ }
+ savegameId -= 1000;
- savedir_nr = saves[savedir_nr].id;
- } else {
- // Loading from launcher, no change necessary
+ Common::Array<SavegameDesc> saves;
+ listSavegames(saves);
+ if (findSavegame(saves, savegameId) == -1) {
+ warning("Savegame ID %d not found", savegameId);
+ return TRUE_REG;
}
- if (savedir_nr > -1) {
- 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
+ Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager();
+ Common::String filename = g_sci->getSavegameName(savegameId);
+ Common::SeekableReadStream *in;
+ if ((in = saveFileMan->openForLoading(filename))) {
+ // found a savegame file
- gamestate_restore(s, in);
- delete in;
+ gamestate_restore(s, in);
+ delete in;
- return s->r_acc;
- }
+ return s->r_acc;
}
- s->r_acc = make_reg(0, 1);
- warning("Savegame #%d not found", savedir_nr);
+ s->r_acc = TRUE_REG;
+ warning("Savegame #%d not found", savegameId);
return s->r_acc;
}
@@ -642,7 +680,8 @@ reg_t DirSeeker::firstFile(const Common::String &mask, reg_t buffer, SegManager
Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager();
_savefiles = saveFileMan->listSavefiles(wrappedMask);
- // Reset the list iterator and write the first match to the output buffer, if any.
+ // Reset the list iterator and write the first match to the output buffer,
+ // if any.
_iter = _savefiles.begin();
return nextFile(segMan);
}
@@ -672,7 +711,8 @@ reg_t kFileIO(EngineState *s, int argc, reg_t *argv) {
case K_FILEIO_OPEN : {
Common::String name = s->_segMan->getString(argv[1]);
- // SCI32 can call K_FILEIO_OPEN with only two arguments. It seems to just be checking if it exists.
+ // SCI32 can call K_FILEIO_OPEN with only two arguments. It seems to
+ // just be checking if it exists.
int mode = (argc < 3) ? (int)_K_FILE_MODE_OPEN_OR_FAIL : argv[2].toUint16();
// SQ4 floppy prepends /\ to the filenames
@@ -681,10 +721,10 @@ reg_t kFileIO(EngineState *s, int argc, reg_t *argv) {
name.deleteChar(0);
}
- // SQ4 floppy attempts to update the savegame index file sq4sg.dir
- // when deleting saved games. We don't use an index file for saving
- // or loading, so just stop the game from modifying the file here
- // in order to avoid having it saved in the ScummVM save directory
+ // SQ4 floppy attempts to update the savegame index file sq4sg.dir when
+ // deleting saved games. We don't use an index file for saving or
+ // loading, so just stop the game from modifying the file here in order
+ // to avoid having it saved in the ScummVM save directory.
if (name == "sq4sg.dir") {
debugC(2, kDebugLevelFile, "Not opening unused file sq4sg.dir");
return SIGNAL_REG;
@@ -694,12 +734,12 @@ reg_t kFileIO(EngineState *s, int argc, reg_t *argv) {
warning("Attempted to open a file with an empty filename");
return SIGNAL_REG;
}
+ debugC(2, kDebugLevelFile, "kFileIO(open): %s, 0x%x", name.c_str(), mode);
file_open(s, name.c_str(), mode);
- debug(3, "K_FILEIO_OPEN(%s,0x%x)", name.c_str(), mode);
break;
}
case K_FILEIO_CLOSE : {
- debug(3, "K_FILEIO_CLOSE(%d)", argv[1].toUint16());
+ debugC(2, kDebugLevelFile, "kFileIO(close): %d", argv[1].toUint16());
FileHandle *f = getFileFromHandle(s, argv[1].toUint16());
if (f)
@@ -710,8 +750,7 @@ reg_t kFileIO(EngineState *s, int argc, reg_t *argv) {
int handle = argv[1].toUint16();
int size = argv[3].toUint16();
char *buf = new char[size];
- debug(3, "K_FILEIO_READ_RAW(%d,%d)", handle, size);
-
+ debugC(2, kDebugLevelFile, "kFileIO(readRaw): %d, %d", handle, size);
FileHandle *f = getFileFromHandle(s, handle);
if (f) {
@@ -727,7 +766,7 @@ reg_t kFileIO(EngineState *s, int argc, reg_t *argv) {
int size = argv[3].toUint16();
char *buf = new char[size];
s->_segMan->memcpy((byte*)buf, argv[2], size);
- debug(3, "K_FILEIO_WRITE_RAW(%d,%d)", handle, size);
+ debugC(2, kDebugLevelFile, "kFileIO(writeRaw): %d, %d", handle, size);
FileHandle *f = getFileFromHandle(s, handle);
if (f)
@@ -745,11 +784,13 @@ reg_t kFileIO(EngineState *s, int argc, reg_t *argv) {
name.deleteChar(0);
}
- // Special case for SQ4 floppy: This game has hardcoded names for all of its
- // savegames, and they are all named "sq4sg.xxx", where xxx is the slot. We just
- // take the slot number here, and delete the appropriate save game
+ // Special case for SQ4 floppy: This game has hardcoded names for all of
+ // its savegames, and they are all named "sq4sg.xxx", where xxx is the
+ // slot. We just take the slot number here, and delete the appropriate
+ // save game.
if (name.hasPrefix("sq4sg.")) {
- // Special handling for SQ4... get the slot number and construct the save game name
+ // Special handling for SQ4... get the slot number and construct the
+ // save game name.
int slotNum = atoi(name.c_str() + name.size() - 3);
Common::Array<SavegameDesc> saves;
listSavegames(saves);
@@ -761,7 +802,7 @@ reg_t kFileIO(EngineState *s, int argc, reg_t *argv) {
saveFileMan->removeSavefile(wrappedName);
}
- debug(3, "K_FILEIO_UNLINK(%s)", name.c_str());
+ debugC(2, kDebugLevelFile, "kFileIO(unlink): %s", name.c_str());
// TODO/FIXME: Should we return something (like, a bool indicating
// whether deleting the save succeeded or failed)?
@@ -771,7 +812,7 @@ reg_t kFileIO(EngineState *s, int argc, reg_t *argv) {
int size = argv[2].toUint16();
char *buf = new char[size];
int handle = argv[3].toUint16();
- debug(3, "K_FILEIO_READ_STRING(%d,%d)", handle, size);
+ debugC(2, kDebugLevelFile, "kFileIO(readString): %d, %d", handle, size);
fgets_wrapper(s, buf, size, handle);
s->_segMan->memcpy(argv[1], (const byte*)buf, size);
@@ -782,7 +823,7 @@ reg_t kFileIO(EngineState *s, int argc, reg_t *argv) {
int handle = argv[1].toUint16();
int size = argv[3].toUint16();
Common::String str = s->_segMan->getString(argv[2]);
- debug(3, "K_FILEIO_WRITE_STRING(%d,%d)", handle, size);
+ debugC(2, kDebugLevelFile, "kFileIO(writeString): %d, %d", handle, size);
// CHECKME: Is the size parameter used at all?
// In the LSL5 password protection it is zero, and we should
@@ -798,7 +839,7 @@ reg_t kFileIO(EngineState *s, int argc, reg_t *argv) {
int handle = argv[1].toUint16();
int offset = argv[2].toUint16();
int whence = argv[3].toUint16();
- debug(3, "K_FILEIO_SEEK(%d,%d,%d)", handle, offset, whence);
+ debugC(2, kDebugLevelFile, "kFileIO(seek): %d, %d, %d", handle, offset, whence);
FileHandle *f = getFileFromHandle(s, handle);
if (f)
@@ -809,7 +850,7 @@ reg_t kFileIO(EngineState *s, int argc, reg_t *argv) {
Common::String mask = s->_segMan->getString(argv[1]);
reg_t buf = argv[2];
int attr = argv[3].toUint16(); // We won't use this, Win32 might, though...
- debug(3, "K_FILEIO_FIND_FIRST(%s,0x%x)", mask.c_str(), attr);
+ debugC(2, kDebugLevelFile, "kFileIO(findFirst): %s, 0x%x", mask.c_str(), attr);
// We remove ".*". mask will get prefixed, so we will return all additional files for that gameid
if (mask == "*.*")
@@ -827,7 +868,7 @@ reg_t kFileIO(EngineState *s, int argc, reg_t *argv) {
break;
}
case K_FILEIO_FIND_NEXT : {
- debug(3, "K_FILEIO_FIND_NEXT()");
+ debugC(2, kDebugLevelFile, "kFileIO(findNext)");
s->r_acc = s->_dirseeker.nextFile(s->_segMan);
break;
}
@@ -849,14 +890,17 @@ reg_t kFileIO(EngineState *s, int argc, reg_t *argv) {
delete inFile;
}
- // Special case for non-English versions of LSL5: The English version of LSL5 calls
- // kFileIO(), case K_FILEIO_OPEN for reading to check if memory.drv exists (which is
- // where the game's password is stored). If it's not found, it calls kFileIO() again,
- // case K_FILEIO_OPEN for writing and creates a new file. Non-English versions call
- // kFileIO(), case K_FILEIO_FILE_EXISTS instead, and fail if memory.drv can't be found.
- // We create a default memory.drv file with no password, so that the game can continue
+ // Special case for non-English versions of LSL5: The English version of
+ // LSL5 calls kFileIO(), case K_FILEIO_OPEN for reading to check if
+ // memory.drv exists (which is where the game's password is stored). If
+ // it's not found, it calls kFileIO() again, case K_FILEIO_OPEN for
+ // writing and creates a new file. Non-English versions call kFileIO(),
+ // case K_FILEIO_FILE_EXISTS instead, and fail if memory.drv can't be
+ // found. We create a default memory.drv file with no password, so that
+ // the game can continue.
if (!exists && name == "memory.drv") {
- // Create a new file, and write the bytes for the empty password string inside
+ // Create a new file, and write the bytes for the empty password
+ // string inside
byte defaultContent[] = { 0xE9, 0xE9, 0xEB, 0xE1, 0x0D, 0x0A, 0x31, 0x30, 0x30, 0x30 };
Common::WriteStream *outFile = saveFileMan->openForSaving(wrappedName);
for (int i = 0; i < 10; i++)
@@ -866,15 +910,15 @@ reg_t kFileIO(EngineState *s, int argc, reg_t *argv) {
exists = true;
}
- debug(3, "K_FILEIO_FILE_EXISTS(%s) -> %d", name.c_str(), exists);
+ debugC(2, kDebugLevelFile, "kFileIO(fileExists) %s -> %d", name.c_str(), exists);
return make_reg(0, exists);
}
case K_FILEIO_RENAME: {
Common::String oldName = s->_segMan->getString(argv[1]);
Common::String newName = s->_segMan->getString(argv[2]);
- // SCI1.1 returns 0 on success and a DOS error code on fail. SCI32 returns -1 on fail.
- // We just return -1 for all versions.
+ // SCI1.1 returns 0 on success and a DOS error code on fail. SCI32
+ // returns -1 on fail. We just return -1 for all versions.
if (g_engine->getSaveFileManager()->renameSavefile(oldName, newName))
return NULL_REG;
else
@@ -914,7 +958,7 @@ reg_t kFileIO(EngineState *s, int argc, reg_t *argv) {
break;
#endif
default:
- error("Unknown FileIO() sub-command: %d", func_nr);
+ error("kFileIO(): unknown sub-command: %d", func_nr);
}
return s->r_acc;
diff --git a/engines/sci/engine/kgraphics.cpp b/engines/sci/engine/kgraphics.cpp
index abe55455de..29f7565ef9 100644
--- a/engines/sci/engine/kgraphics.cpp
+++ b/engines/sci/engine/kgraphics.cpp
@@ -37,8 +37,6 @@
#include "sci/engine/state.h"
#include "sci/engine/selector.h"
#include "sci/engine/kernel.h"
-#include "sci/graphics/gui.h"
-#include "sci/graphics/gui32.h"
#include "sci/graphics/animate.h"
#include "sci/graphics/cache.h"
#include "sci/graphics/compare.h"
@@ -48,7 +46,11 @@
#include "sci/graphics/paint16.h"
#include "sci/graphics/ports.h"
#include "sci/graphics/screen.h"
+#include "sci/graphics/text16.h"
#include "sci/graphics/view.h"
+#ifdef ENABLE_SCI32
+#include "sci/video/vmd_decoder.h"
+#endif
namespace Sci {
@@ -171,7 +173,7 @@ static reg_t kSetCursorSci11(EngineState *s, int argc, reg_t *argv) {
g_sci->_gfxCursor->kernelSetView(argv[0].toUint16(), argv[1].toUint16(), argv[2].toUint16(), hotspot);
break;
default :
- warning("kSetCursor: Unhandled case: %d arguments given", argc);
+ error("kSetCursor: Unhandled case: %d arguments given", argc);
break;
}
return s->r_acc;
@@ -184,7 +186,7 @@ reg_t kSetCursor(EngineState *s, int argc, reg_t *argv) {
case SCI_VERSION_1_1:
return kSetCursorSci11(s, argc, argv);
default:
- warning("Unknown SetCursor type");
+ error("Unknown SetCursor type");
return NULL_REG;
}
}
@@ -205,121 +207,108 @@ reg_t kPicNotValid(EngineState *s, int argc, reg_t *argv) {
return make_reg(0, g_sci->_gfxScreen->kernelPicNotValid(newPicNotValid));
}
-Common::Rect kGraphCreateRect(int16 x, int16 y, int16 x1, int16 y1) {
+static Common::Rect getGraphRect(reg_t *argv) {
+ int16 x = argv[1].toSint16();
+ int16 y = argv[0].toSint16();
+ int16 x1 = argv[3].toSint16();
+ int16 y1 = argv[2].toSint16();
if (x > x1) SWAP(x, x1);
if (y > y1) SWAP(y, y1);
return Common::Rect(x, y, x1, y1);
}
-// Graph subfunctions
-enum {
- K_GRAPH_GET_COLORS_NR = 2,
- // 3 - SET PALETTE VIA RESOURCE
- K_GRAPH_DRAW_LINE = 4,
- // 5 - NOP
- // 6 - DRAW PATTERN
- K_GRAPH_SAVE_BOX = 7,
- K_GRAPH_RESTORE_BOX = 8,
- K_GRAPH_FILL_BOX_BACKGROUND = 9,
- K_GRAPH_FILL_BOX_FOREGROUND = 10,
- K_GRAPH_FILL_BOX_ANY = 11,
- K_GRAPH_UPDATE_BOX = 12,
- K_GRAPH_REDRAW_BOX = 13,
- K_GRAPH_ADJUST_PRIORITY = 14,
- K_GRAPH_SAVE_UPSCALEDHIRES_BOX = 15 // KQ6CD Windows version
-};
+static Common::Point getGraphPoint(reg_t *argv) {
+ int16 x = argv[1].toSint16();
+ int16 y = argv[0].toSint16();
+ return Common::Point(x, y);
+}
reg_t kGraph(EngineState *s, int argc, reg_t *argv) {
- int16 x = 0, y = 0, x1 = 0, y1 = 0;
- uint16 screenMask;
- int16 priority, control, color, colorMask;
- Common::Rect rect;
-
- if (argc >= 5) {
- x = argv[2].toSint16();
- y = argv[1].toSint16();
- x1 = argv[4].toSint16();
- y1 = argv[3].toSint16();
- }
-
- switch (argv[0].toSint16()) {
- case K_GRAPH_GET_COLORS_NR:
- if (g_sci->getResMan()->isAmiga32color())
- return make_reg(0, 32);
- return make_reg(0, !g_sci->getResMan()->isVGA() ? 16 : 256);
-
- case K_GRAPH_DRAW_LINE:
- priority = (argc > 6) ? argv[6].toSint16() : -1;
- control = (argc > 7) ? argv[7].toSint16() : -1;
- color = argv[5].toSint16();
-
- // TODO: Find out why we get >15 for color in EGA
- if (!g_sci->getResMan()->isVGA() && !g_sci->getResMan()->isAmiga32color())
- color &= 0x0F;
+ if (!s)
+ return make_reg(0, getSciVersion());
+ error("not supposed to call this");
+}
- g_sci->_gfxPaint16->kernelGraphDrawLine(Common::Point(x, y), Common::Point(x1, y1), color, priority, control);
- break;
+reg_t kGraphGetColorCount(EngineState *s, int argc, reg_t *argv) {
+ if (g_sci->getResMan()->isAmiga32color())
+ return make_reg(0, 32);
+ return make_reg(0, !g_sci->getResMan()->isVGA() ? 16 : 256);
+}
- case K_GRAPH_SAVE_BOX:
- rect = kGraphCreateRect(x, y, x1, y1);
- screenMask = (argc > 5) ? argv[5].toUint16() : 0;
- return g_sci->_gfxPaint16->kernelGraphSaveBox(rect, screenMask);
+reg_t kGraphDrawLine(EngineState *s, int argc, reg_t *argv) {
+ int16 color = argv[4].toSint16();
+ int16 priority = (argc > 5) ? argv[5].toSint16() : -1;
+ int16 control = (argc > 6) ? argv[6].toSint16() : -1;
- case K_GRAPH_RESTORE_BOX:
- // This may be called with a memoryhandle from SAVE_BOX or SAVE_UPSCALEDHIRES_BOX
- g_sci->_gfxPaint16->kernelGraphRestoreBox(argv[1]);
- break;
+ // TODO: Find out why we get >15 for color in EGA
+ if (!g_sci->getResMan()->isVGA() && !g_sci->getResMan()->isAmiga32color())
+ color &= 0x0F;
- case K_GRAPH_FILL_BOX_BACKGROUND:
- rect = kGraphCreateRect(x, y, x1, y1);
- g_sci->_gfxPaint16->kernelGraphFillBoxBackground(rect);
- break;
+ g_sci->_gfxPaint16->kernelGraphDrawLine(getGraphPoint(argv), getGraphPoint(argv + 2), color, priority, control);
+ return s->r_acc;
+}
- case K_GRAPH_FILL_BOX_FOREGROUND:
- rect = kGraphCreateRect(x, y, x1, y1);
- g_sci->_gfxPaint16->kernelGraphFillBoxForeground(rect);
- break;
+reg_t kGraphSaveBox(EngineState *s, int argc, reg_t *argv) {
+ Common::Rect rect = getGraphRect(argv);
+ uint16 screenMask = (argc > 4) ? argv[4].toUint16() : 0;
+ return g_sci->_gfxPaint16->kernelGraphSaveBox(rect, screenMask);
+}
- case K_GRAPH_FILL_BOX_ANY:
- priority = (argc > 7) ? argv[7].toSint16() : -1;
- control = (argc > 8) ? argv[8].toSint16() : -1;
- color = argv[6].toSint16();
- colorMask = argv[5].toUint16();
+reg_t kGraphRestoreBox(EngineState *s, int argc, reg_t *argv) {
+ // This may be called with a memoryhandle from SAVE_BOX or SAVE_UPSCALEDHIRES_BOX
+ g_sci->_gfxPaint16->kernelGraphRestoreBox(argv[0]);
+ return s->r_acc;
+}
- rect = kGraphCreateRect(x, y, x1, y1);
- g_sci->_gfxPaint16->kernelGraphFillBox(rect, colorMask, color, priority, control);
- break;
+reg_t kGraphFillBoxBackground(EngineState *s, int argc, reg_t *argv) {
+ Common::Rect rect = getGraphRect(argv);
+ g_sci->_gfxPaint16->kernelGraphFillBoxBackground(rect);
+ return s->r_acc;
+}
- case K_GRAPH_UPDATE_BOX: {
- rect = kGraphCreateRect(x, y, x1, y1);
- bool hiresMode = (argc > 6) ? true : false;
- // argc == 7 on upscaled hires
- g_sci->_gfxPaint16->kernelGraphUpdateBox(rect, hiresMode);
- break;
- }
+reg_t kGraphFillBoxForeground(EngineState *s, int argc, reg_t *argv) {
+ Common::Rect rect = getGraphRect(argv);
+ g_sci->_gfxPaint16->kernelGraphFillBoxForeground(rect);
+ return s->r_acc;
+}
- case K_GRAPH_REDRAW_BOX:
- rect = kGraphCreateRect(x, y, x1, y1);
- g_sci->_gfxPaint16->kernelGraphRedrawBox(rect);
- break;
+reg_t kGraphFillBoxAny(EngineState *s, int argc, reg_t *argv) {
+ Common::Rect rect = getGraphRect(argv);
+ int16 colorMask = argv[4].toUint16();
+ int16 color = argv[5].toSint16();
+ int16 priority = (argc > 6) ? argv[6].toSint16() : -1;
+ int16 control = (argc > 7) ? argv[7].toSint16() : -1;
- case K_GRAPH_ADJUST_PRIORITY:
- // Seems to be only implemented for SCI0/SCI01 games
- debugC(2, kDebugLevelGraphics, "adjust_priority(%d, %d)", argv[1].toUint16(), argv[2].toUint16());
- g_sci->_gfxPorts->kernelGraphAdjustPriority(argv[1].toUint16(), argv[2].toUint16());
- break;
+ g_sci->_gfxPaint16->kernelGraphFillBox(rect, colorMask, color, priority, control);
+ return s->r_acc;
+}
- case K_GRAPH_SAVE_UPSCALEDHIRES_BOX:
- rect = kGraphCreateRect(x, y, x1, y1);
- return g_sci->_gfxPaint16->kernelGraphSaveUpscaledHiresBox(rect);
+reg_t kGraphUpdateBox(EngineState *s, int argc, reg_t *argv) {
+ Common::Rect rect = getGraphRect(argv);
+ // argv[4] is the map (1 for visual, etc.)
+ // argc == 6 on upscaled hires
+ bool hiresMode = (argc > 5) ? true : false;
+ g_sci->_gfxPaint16->kernelGraphUpdateBox(rect, hiresMode);
+ return s->r_acc;
+}
- default:
- warning("Unsupported kGraph() operation %04x", argv[0].toSint16());
- }
+reg_t kGraphRedrawBox(EngineState *s, int argc, reg_t *argv) {
+ Common::Rect rect = getGraphRect(argv);
+ g_sci->_gfxPaint16->kernelGraphRedrawBox(rect);
+ return s->r_acc;
+}
+// Seems to be only implemented for SCI0/SCI01 games
+reg_t kGraphAdjustPriority(EngineState *s, int argc, reg_t *argv) {
+ g_sci->_gfxPorts->kernelGraphAdjustPriority(argv[0].toUint16(), argv[1].toUint16());
return s->r_acc;
}
+reg_t kGraphSaveUpscaledHiresBox(EngineState *s, int argc, reg_t *argv) {
+ Common::Rect rect = getGraphRect(argv);
+ return g_sci->_gfxPaint16->kernelGraphSaveUpscaledHiresBox(rect);
+}
+
reg_t kTextSize(EngineState *s, int argc, reg_t *argv) {
int16 textWidth, textHeight;
Common::String text = s->_segMan->getString(argv[1]);
@@ -343,13 +332,14 @@ reg_t kTextSize(EngineState *s, int argc, reg_t *argv) {
}
textWidth = dest[3].toUint16(); textHeight = dest[2].toUint16();
-
+
#ifdef ENABLE_SCI32
- if (g_sci->_gui32)
- g_sci->_gui32->textSize(g_sci->strSplit(text.c_str(), sep).c_str(), font_nr, maxwidth, &textWidth, &textHeight);
- else
+ if (!g_sci->_gfxText16) {
+ // TODO: Implement this
+ textWidth = 0; textHeight = 0;
+ } else
#endif
- g_sci->_gui->textSize(g_sci->strSplit(text.c_str(), sep).c_str(), font_nr, maxwidth, &textWidth, &textHeight);
+ g_sci->_gfxText16->kernelTextSize(g_sci->strSplit(text.c_str(), sep).c_str(), font_nr, maxwidth, &textWidth, &textHeight);
debugC(2, kDebugLevelStrings, "GetTextSize '%s' -> %dx%d", text.c_str(), textWidth, textHeight);
dest[2] = make_reg(0, textHeight);
@@ -531,7 +521,7 @@ 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) {
+ if (s->currentRoomNumber() == 300 && g_sci->getGameId() == GID_LSL1) {
int top = readSelectorValue(s->_segMan, object, SELECTOR(brTop));
writeSelectorValue(s->_segMan, object, SELECTOR(brTop), top + 2);
}
@@ -545,64 +535,72 @@ reg_t kSetNowSeen(EngineState *s, int argc, reg_t *argv) {
return s->r_acc;
}
+// we are called on EGA/amiga games as well, this doesnt make sense.
+// doing this would actually break the system EGA/amiga palette
reg_t kPalette(EngineState *s, int argc, reg_t *argv) {
- // we are called on EGA/amiga games as well, this doesnt make sense.
- // doing this would actually break the system EGA/amiga palette
- if (!g_sci->getResMan()->isVGA())
- return s->r_acc;
+ if (!s)
+ return make_reg(0, getSciVersion());
+ error("not supposed to call this");
+}
- switch (argv[0].toUint16()) {
- case 1: // Set resource palette
- if (argc==3) {
- GuiResourceId resourceId = argv[1].toUint16();
- bool force = argv[2].toUint16() == 2 ? true : false;
- g_sci->_gfxPalette->kernelSetFromResource(resourceId, force);
- } else {
- warning("kPalette(1) called with %d parameters", argc);
- }
- break;
- case 2: { // Set palette-flag(s)
- uint16 fromColor = CLIP<uint16>(argv[1].toUint16(), 1, 255);
- uint16 toColor = CLIP<uint16>(argv[2].toUint16(), 1, 255);
- uint16 flags = argv[3].toUint16();
+reg_t kPaletteSetFromResource(EngineState *s, int argc, reg_t *argv) {
+ if (g_sci->getResMan()->isVGA()) {
+ GuiResourceId resourceId = argv[0].toUint16();
+ bool force = false;
+ if (argc == 2)
+ force = argv[1].toUint16() == 2 ? true : false;
+ g_sci->_gfxPalette->kernelSetFromResource(resourceId, force);
+ }
+ return s->r_acc;
+}
+
+reg_t kPaletteSetFlag(EngineState *s, int argc, reg_t *argv) {
+ if (g_sci->getResMan()->isVGA()) {
+ uint16 fromColor = CLIP<uint16>(argv[0].toUint16(), 1, 255);
+ uint16 toColor = CLIP<uint16>(argv[1].toUint16(), 1, 255);
+ uint16 flags = argv[2].toUint16();
g_sci->_gfxPalette->kernelSetFlag(fromColor, toColor, flags);
- break;
}
- case 3: { // Remove palette-flag(s)
- uint16 fromColor = CLIP<uint16>(argv[1].toUint16(), 1, 255);
- uint16 toColor = CLIP<uint16>(argv[2].toUint16(), 1, 255);
- uint16 flags = argv[3].toUint16();
+ return s->r_acc;
+}
+
+reg_t kPaletteUnsetFlag(EngineState *s, int argc, reg_t *argv) {
+ if (g_sci->getResMan()->isVGA()) {
+ uint16 fromColor = CLIP<uint16>(argv[0].toUint16(), 1, 255);
+ uint16 toColor = CLIP<uint16>(argv[1].toUint16(), 1, 255);
+ uint16 flags = argv[2].toUint16();
g_sci->_gfxPalette->kernelUnsetFlag(fromColor, toColor, flags);
- break;
}
- case 4: { // Set palette intensity
- switch (argc) {
- case 4:
- case 5: {
- uint16 fromColor = CLIP<uint16>(argv[1].toUint16(), 1, 255);
- uint16 toColor = CLIP<uint16>(argv[2].toUint16(), 1, 255);
- uint16 intensity = argv[3].toUint16();
- bool setPalette = (argc < 5) ? true : (argv[4].isNull()) ? true : false;
-
- g_sci->_gfxPalette->kernelSetIntensity(fromColor, toColor, intensity, setPalette);
- break;
- }
- default:
- warning("kPalette(4) called with %d parameters", argc);
- }
- break;
+ return s->r_acc;
+}
+
+reg_t kPaletteSetIntensity(EngineState *s, int argc, reg_t *argv) {
+ if (g_sci->getResMan()->isVGA()) {
+ uint16 fromColor = CLIP<uint16>(argv[0].toUint16(), 1, 255);
+ uint16 toColor = CLIP<uint16>(argv[1].toUint16(), 1, 255);
+ uint16 intensity = argv[2].toUint16();
+ bool setPalette = (argc < 4) ? true : (argv[3].isNull()) ? true : false;
+
+ g_sci->_gfxPalette->kernelSetIntensity(fromColor, toColor, intensity, setPalette);
}
- case 5: { // Find closest color
- uint16 r = argv[1].toUint16();
- uint16 g = argv[2].toUint16();
- uint16 b = argv[3].toUint16();
+ return s->r_acc;
+}
+reg_t kPaletteFindColor(EngineState *s, int argc, reg_t *argv) {
+ if (g_sci->getResMan()->isVGA()) {
+ uint16 r = argv[0].toUint16();
+ uint16 g = argv[1].toUint16();
+ uint16 b = argv[2].toUint16();
return make_reg(0, g_sci->_gfxPalette->kernelFindColor(r, g, b));
}
- case 6: { // Animate
+ return NULL_REG;
+}
+
+reg_t kPaletteAnimate(EngineState *s, int argc, reg_t *argv) {
+ if (g_sci->getResMan()->isVGA()) {
int16 argNr;
bool paletteChanged = false;
- for (argNr = 1; argNr < argc; argNr += 3) {
+ for (argNr = 0; argNr < argc; argNr += 3) {
uint16 fromColor = argv[argNr].toUint16();
uint16 toColor = argv[argNr + 1].toUint16();
int16 speed = argv[argNr + 2].toSint16();
@@ -611,80 +609,82 @@ reg_t kPalette(EngineState *s, int argc, reg_t *argv) {
}
if (paletteChanged)
g_sci->_gfxPalette->kernelAnimateSet();
- break;
}
- case 7: { // Save palette to heap
+ return s->r_acc;
+}
+
+reg_t kPaletteSave(EngineState *s, int argc, reg_t *argv) {
+ if (g_sci->getResMan()->isVGA()) {
warning("kPalette(7), save palette to heap STUB");
- break;
- }
- case 8: { // Restore palette from heap
- warning("kPalette(8), set stored palette STUB");
- break;
- }
- default:
- warning("kPalette(%d), not implemented", argv[0].toUint16());
}
+ return NULL_REG;
+}
+reg_t kPaletteRestore(EngineState *s, int argc, reg_t *argv) {
+ if (g_sci->getResMan()->isVGA()) {
+ warning("kPalette(8), restore palette from heap STUB");
+ }
return s->r_acc;
}
-// This here is needed to make Pharkas work
reg_t kPalVary(EngineState *s, int argc, reg_t *argv) {
- uint16 operation = argv[0].toUint16();
+ if (!s)
+ return make_reg(0, getSciVersion());
+ error("not supposed to call this");
+}
- if (!g_sci->_gui)
- return s->r_acc;
+reg_t kPalVaryInit(EngineState *s, int argc, reg_t *argv) {
+ GuiResourceId paletteId = argv[0].toUint16();
+ uint16 ticks = argv[1].toUint16();
+ uint16 stepStop = argc >= 3 ? argv[2].toUint16() : 64;
+ uint16 direction = argc >= 4 ? argv[3].toUint16() : 1;
+ if (g_sci->_gfxPalette->kernelPalVaryInit(paletteId, ticks, stepStop, direction))
+ return SIGNAL_REG;
+ return NULL_REG;
+}
- switch (operation) {
- case 0: { // Init
- GuiResourceId paletteId;
- uint16 time;
- if (argc == 3) {
- paletteId = argv[1].toUint16();
- time = argv[2].toUint16();
- g_sci->_gfxPalette->startPalVary(paletteId, time);
- warning("kPalVary(init) called with paletteId = %d, time = %d", paletteId, time);
- } else {
- warning("kPalVary(init) called with unsupported argc %d", argc);
- }
- break;
- }
- case 1: { // Unknown
- warning("kPalVary(1) called with parameter %d (argc %d)", argv[1].toUint16(), argc);
- break;
- }
- case 3: { // DeInit
- if (argc == 1) {
- g_sci->_gfxPalette->stopPalVary();
- warning("kPalVary(deinit)");
- } else {
- warning("kPalVary(deinit) called with unsupported argc %d", argc);
- }
- break;
- }
- case 4: { // Unknown
- warning("kPalVary(4) called with parameter %d (argc %d)", argv[1].toUint16(), argc);
- break;
- }
- case 6: { // Pause
- bool pauseState;
- if (argc == 2) {
- pauseState = argv[1].isNull() ? false : true;
- g_sci->_gfxPalette->togglePalVary(pauseState);
- warning("kPalVary(pause) called with state = %d", pauseState);
- } else {
- warning("kPalVary(pause) called with unsupported argc %d", argc);
- }
- break;
- }
- default:
- warning("kPalVary(%d), not implemented (argc = %d)", operation, argc);
- }
+reg_t kPalVaryReverse(EngineState *s, int argc, reg_t *argv) {
+ int16 ticks = argc >= 1 ? argv[0].toUint16() : -1;
+ int16 stepStop = argc >= 2 ? argv[1].toUint16() : 0;
+ int16 direction = argc >= 3 ? argv[2].toSint16() : -1;
+
+ return make_reg(0, g_sci->_gfxPalette->kernelPalVaryReverse(ticks, stepStop, direction));
+}
+
+reg_t kPalVaryGetCurrentStep(EngineState *s, int argc, reg_t *argv) {
+ return make_reg(0, g_sci->_gfxPalette->kernelPalVaryGetCurrentStep());
+}
+
+reg_t kPalVaryDeinit(EngineState *s, int argc, reg_t *argv) {
+ g_sci->_gfxPalette->kernelPalVaryDeinit();
+ return NULL_REG;
+}
+
+reg_t kPalVaryChangeTarget(EngineState *s, int argc, reg_t *argv) {
+ GuiResourceId paletteId = argv[0].toUint16();
+ int16 currentStep = g_sci->_gfxPalette->kernelPalVaryChangeTarget(paletteId);
+ return make_reg(0, currentStep);
+}
+
+reg_t kPalVaryChangeTicks(EngineState *s, int argc, reg_t *argv) {
+ uint16 ticks = argv[0].toUint16();
+ g_sci->_gfxPalette->kernelPalVaryChangeTicks(ticks);
+ return NULL_REG;
+}
+
+reg_t kPalVaryPauseResume(EngineState *s, int argc, reg_t *argv) {
+ bool pauseState = !argv[0].isNull();
+ g_sci->_gfxPalette->kernelPalVaryPause(pauseState);
+ return NULL_REG;
+}
+
+reg_t kPalVaryUnknown(EngineState *s, int argc, reg_t *argv) {
+ // Unknown (seems to be SCI32 exclusive)
return NULL_REG;
}
reg_t kAssertPalette(EngineState *s, int argc, reg_t *argv) {
- GuiResourceId paletteId = argv[1].toUint16();
+ GuiResourceId paletteId = argv[0].toUint16();
g_sci->_gfxPalette->kernelAssertPalette(paletteId);
return s->r_acc;
@@ -698,9 +698,9 @@ reg_t kPortrait(EngineState *s, int argc, reg_t *argv) {
case 0: { // load
if (argc == 2) {
Common::String resourceName = s->_segMan->getString(argv[1]);
- s->r_acc = g_sci->_gui->portraitLoad(resourceName);
+ s->r_acc = g_sci->_gfxPaint16->kernelPortraitLoad(resourceName);
} else {
- warning("kPortrait(loadResource) called with unsupported argc %d", argc);
+ error("kPortrait(loadResource) called with unsupported argc %d", argc);
}
break;
}
@@ -715,24 +715,24 @@ reg_t kPortrait(EngineState *s, int argc, reg_t *argv) {
uint seq = argv[8].toUint16() & 0xff;
// argv[9] is usually 0??!!
- g_sci->_gui->portraitShow(resourceName, position, resourceNum, noun, verb, cond, seq);
+ g_sci->_gfxPaint16->kernelPortraitShow(resourceName, position, resourceNum, noun, verb, cond, seq);
return SIGNAL_REG;
} else {
- warning("kPortrait(show) called with unsupported argc %d", argc);
+ error("kPortrait(show) called with unsupported argc %d", argc);
}
break;
}
case 2: { // unload
if (argc == 2) {
uint16 portraitId = argv[1].toUint16();
- g_sci->_gui->portraitUnload(portraitId);
+ g_sci->_gfxPaint16->kernelPortraitUnload(portraitId);
} else {
- warning("kPortrait(unload) called with unsupported argc %d", argc);
+ error("kPortrait(unload) called with unsupported argc %d", argc);
}
break;
}
default:
- warning("kPortrait(%d), not implemented (argc = %d)", operation, argc);
+ error("kPortrait(%d), not implemented (argc = %d)", operation, argc);
}
return s->r_acc;
@@ -790,6 +790,11 @@ void _k_GenericDrawControl(EngineState *s, reg_t controlObject, bool hilite) {
mode = readSelectorValue(s->_segMan, controlObject, SELECTOR(mode));
maxChars = readSelectorValue(s->_segMan, controlObject, SELECTOR(max));
cursorPos = readSelectorValue(s->_segMan, controlObject, SELECTOR(cursor));
+ if (cursorPos > (int)text.size()) {
+ // if cursor is outside of text, adjust accordingly
+ cursorPos = text.size();
+ writeSelectorValue(s->_segMan, controlObject, SELECTOR(cursor), cursorPos);
+ }
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;
@@ -801,11 +806,8 @@ void _k_GenericDrawControl(EngineState *s, reg_t controlObject, bool hilite) {
loopNo = (l & 0x80) ? l - 256 : l;
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"))
+ // Check if the control object specifies a priority selector (like in Jones)
+ if (lookupSelector(s->_segMan, controlObject, SELECTOR(priority), NULL, NULL) == kSelectorVariable)
priority = readSelectorValue(s->_segMan, controlObject, SELECTOR(priority));
else
priority = -1;
@@ -821,12 +823,12 @@ void _k_GenericDrawControl(EngineState *s, reg_t controlObject, bool hilite) {
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) {
+ if (SELECTOR(topString) != -1) {
// Games from early SCI1 onwards use topString
upperOffset = readSelectorValue(s->_segMan, controlObject, SELECTOR(topString));
} else {
// Earlier games use lsTop or brTop
- if (lookupSelector(s->_segMan, controlObject, g_sci->getKernel()->_selectorCache.brTop, NULL, NULL) == kSelectorVariable)
+ if (lookupSelector(s->_segMan, controlObject, SELECTOR(brTop), NULL, NULL) == kSelectorVariable)
upperOffset = readSelectorValue(s->_segMan, controlObject, SELECTOR(brTop));
else
upperOffset = readSelectorValue(s->_segMan, controlObject, SELECTOR(lsTop));
@@ -883,6 +885,18 @@ reg_t kDrawControl(EngineState *s, int argc, reg_t *argv) {
int state = readSelectorValue(s->_segMan, controlObject, SELECTOR(state));
writeSelectorValue(s->_segMan, controlObject, SELECTOR(state), (state | SCI_CONTROLS_STYLE_DISABLED) & ~SCI_CONTROLS_STYLE_ENABLED);
}
+ if (objName == "DEdit") {
+ reg_t textReference = readSelector(s->_segMan, controlObject, SELECTOR(text));
+ if (!textReference.isNull()) {
+ Common::String text = s->_segMan->getString(textReference);
+ if (text == "a:hq1_hero.sav") {
+ // Remove "a:" from hero quest export default filename
+ text.deleteChar(0);
+ text.deleteChar(0);
+ s->_segMan->strcpy(textReference, text.c_str());
+ }
+ }
+ }
_k_GenericDrawControl(s, controlObject, false);
return NULL_REG;
@@ -989,24 +1003,6 @@ reg_t kDrawCel(EngineState *s, int argc, reg_t *argv) {
bool hiresMode = (argc > 7) ? true : false;
reg_t upscaledHiresHandle = (argc > 7) ? argv[7] : NULL_REG;
- if (!strcmp(g_sci->getGameID(), "freddypharkas") || !strcmp(g_sci->getGameID(), "freddypharkas-demo")) {
- // WORKAROUND
- // Script 24 contains code that draws the game menu on screen. It uses a temp variable for setting priority that
- // is not set. in Sierra sci this happens to be 8250h. In our sci temporary variables are initialized thus we would
- // get 0 here resulting in broken menus.
- if ((viewId == 995) && (loopNo == 0) && (celNo == 0) && (priority == 0)) // game menu
- priority = 15;
- if ((viewId == 992) && (loopNo == 0) && (celNo == 0) && (priority == 0)) // quit game
- priority = 15;
- }
-
- if (!strcmp(g_sci->getGameID(), "laurabow2")) {
- // WORKAROUND
- // see the one above
- if ((viewId == 995) && (priority == 0))
- priority = 15;
- }
-
g_sci->_gfxPaint16->kernelDrawCel(viewId, loopNo, celNo, x, y, priority, paletteNo, hiresMode, upscaledHiresHandle);
return s->r_acc;
@@ -1049,10 +1045,6 @@ reg_t kAnimate(EngineState *s, int argc, reg_t *argv) {
reg_t castListReference = (argc > 0) ? argv[0] : NULL_REG;
bool cycle = (argc > 1) ? ((argv[1].toUint16()) ? true : false) : false;
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- // Take care of incoming events (kAnimate is called semi-regularly)
- process_sound_events(s);
-#endif
g_sci->_gfxAnimate->kernelAnimate(castListReference, cycle, argc, argv);
return s->r_acc;
@@ -1083,6 +1075,58 @@ reg_t kDisplay(EngineState *s, int argc, reg_t *argv) {
return g_sci->_gfxPaint16->kernelDisplay(g_sci->strSplit(text.c_str()).c_str(), argc, argv);
}
+void playVideo(Graphics::VideoDecoder *videoDecoder) {
+ if (!videoDecoder)
+ return;
+
+ byte *scaleBuffer = 0;
+ uint16 width = videoDecoder->getWidth();
+ uint16 height = videoDecoder->getHeight();
+ uint16 screenWidth = g_system->getWidth();
+ uint16 screenHeight = g_system->getHeight();
+
+ if (screenWidth == 640 && width <= 320 && height <= 240) {
+ assert(videoDecoder->getPixelFormat().bytesPerPixel == 1);
+ width *= 2;
+ height *= 2;
+ scaleBuffer = new byte[width * height];
+ }
+
+ uint16 x = (screenWidth - width) / 2;
+ uint16 y = (screenHeight - height) / 2;
+ bool skipVideo = false;
+
+ while (!g_engine->shouldQuit() && !videoDecoder->endOfVideo() && !skipVideo) {
+ if (videoDecoder->needsUpdate()) {
+ Graphics::Surface *frame = videoDecoder->decodeNextFrame();
+ if (frame) {
+ if (scaleBuffer) {
+ // TODO: Probably should do aspect ratio correction in e.g. GK1 Windows
+ g_sci->_gfxScreen->scale2x((byte *)frame->pixels, scaleBuffer, videoDecoder->getWidth(), videoDecoder->getHeight());
+ g_system->copyRectToScreen(scaleBuffer, width, x, y, width, height);
+ } else
+ g_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, x, y, width, height);
+
+ if (videoDecoder->hasDirtyPalette())
+ videoDecoder->setSystemPalette();
+
+ g_system->updateScreen();
+ }
+ }
+
+ Common::Event event;
+ while (g_system->getEventManager()->pollEvent(event)) {
+ if ((event.type == Common::EVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_ESCAPE) || event.type == Common::EVENT_LBUTTONUP)
+ skipVideo = true;
+ }
+
+ g_system->delayMillis(10);
+ }
+
+ delete[] scaleBuffer;
+ delete videoDecoder;
+}
+
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.
@@ -1106,7 +1150,7 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) {
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.");
+ error("This video requires >8bpp color to be displayed, but could not switch to RGB color mode.");
return NULL_REG;
}
@@ -1159,37 +1203,16 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) {
}
if (videoDecoder) {
- uint16 x = (screenWidth - videoDecoder->getWidth()) / 2;
- uint16 y = (screenHeight - videoDecoder->getHeight()) / 2;
-
- while (!g_engine->shouldQuit() && !videoDecoder->endOfVideo()) {
- if (videoDecoder->needsUpdate()) {
- Graphics::Surface *frame = videoDecoder->decodeNextFrame();
- if (frame) {
- g_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, x, y, frame->w, frame->h);
-
- if (videoDecoder->hasDirtyPalette())
- videoDecoder->setSystemPalette();
-
- g_system->updateScreen();
- }
- }
-
- Common::Event event;
- while (g_system->getEventManager()->pollEvent(event))
- ;
-
- g_system->delayMillis(10);
- }
+ playVideo(videoDecoder);
// 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
+ else {
g_sci->_gfxScreen->kernelSyncWithFramebuffer();
-
- delete videoDecoder;
+ g_sci->_gfxPalette->kernelSyncScreenPalette();
+ }
}
if (reshowCursor)
@@ -1213,6 +1236,9 @@ reg_t kRobot(EngineState *s, int argc, reg_t *argv) {
warning("kRobot(init), id %d, obj %04x:%04x, flag %d, x=%d, y=%d", id, PRINT_REG(obj), flag, x, y);
}
break;
+ case 1: // LSL6 hires (startup)
+ // TODO
+ return NULL_REG; // an integer is expected
case 4: { // start
int id = argv[1].toUint16();
warning("kRobot(start), id %d", id);
@@ -1228,30 +1254,140 @@ reg_t kRobot(EngineState *s, int argc, reg_t *argv) {
return s->r_acc;
}
+
+reg_t kPlayVMD(EngineState *s, int argc, reg_t *argv) {
+ uint16 operation = argv[0].toUint16();
+ Graphics::VideoDecoder *videoDecoder = 0;
+ bool reshowCursor = g_sci->_gfxCursor->isVisible();
+ Common::String fileName, warningMsg;
+
+ switch (operation) {
+ case 0: // init
+ // This is actually meant to init the video file, but we play it instead
+ fileName = s->_segMan->derefString(argv[1]);
+ // TODO: argv[2] (usually null). When it exists, it points to an "Event" object,
+ // that holds no data initially (e.g. in the intro of Phantasmagoria 1 demo).
+ // Perhaps it's meant for syncing
+ if (argv[2] != NULL_REG)
+ warning("kPlayVMD: third parameter isn't 0 (it's %04x:%04x - %s)", PRINT_REG(argv[2]), s->_segMan->getObjectName(argv[2]));
+
+ videoDecoder = new VMDDecoder(g_system->getMixer());
+
+ if (reshowCursor)
+ g_sci->_gfxCursor->kernelHide();
+
+ if (videoDecoder && videoDecoder->loadFile(fileName))
+ playVideo(videoDecoder);
+
+ if (reshowCursor)
+ g_sci->_gfxCursor->kernelShow();
+ break;
+ case 1:
+ {
+ // Set VMD parameters. Called with a maximum of 6 parameters:
+ //
+ // x, y, flags, gammaBoost, gammaFirst, gammaLast
+ //
+ // Flags are as follows:
+ // bit 0 doubled
+ // bit 1 "drop frames"?
+ // bit 2 insert black lines
+ // bit 3 unknown
+ // bit 4 gamma correction
+ // bit 5 hold black frame
+ // bit 6 hold last frame
+ // bit 7 unknown
+ // bit 8 stretch
+
+ // gammaBoost boosts palette colors in the range gammaFirst to
+ // gammaLast, but only if bit 4 in flags is set. Percent value such that
+ // 0% = no amplification These three parameters are optional if bit 4 is
+ // clear. Also note that the x, y parameters play subtle games if used
+ // with subfx 21. The subtleness has to do with creation of temporary
+ // planes and positioning relative to such planes.
+
+ int flags = argv[3].offset;
+ Common::String flagspec;
+
+ if (argc > 3) {
+ if (flags & 1)
+ flagspec += "doubled ";
+ if (flags & 2)
+ flagspec += "dropframes ";
+ if (flags & 4)
+ flagspec += "blacklines ";
+ if (flags & 8)
+ flagspec += "bit3 ";
+ if (flags & 16)
+ flagspec += "gammaboost ";
+ if (flags & 32)
+ flagspec += "holdblack ";
+ if (flags & 64)
+ flagspec += "holdlast ";
+ if (flags & 128)
+ flagspec += "bit7 ";
+ if (flags & 256)
+ flagspec += "stretch";
+
+ warning("VMDFlags: %s", flagspec.c_str());
+ }
+
+ warning("x, y: %d, %d", argv[1].offset, argv[2].offset);
+
+ if (argc > 4 && flags & 16)
+ warning("gammaBoost: %d%% between palette entries %d and %d", argv[4].offset, argv[5].offset, argv[6].offset);
+ break;
+ }
+ case 6:
+ // Play, perhaps? Or stop? This is the last call made, and takes no extra parameters
+ case 14:
+ // Takes an additional integer parameter (e.g. 3)
+ case 16:
+ // Takes an additional parameter, usually 0
+ case 21:
+ // Looks to be setting the video size and position. Called with 4 extra integer
+ // parameters (e.g. 86, 41, 235, 106)
+ default:
+ warningMsg = "PlayVMD - unsupported subop. Params: " +
+ Common::String::printf("%d", argc) + " (";
+
+ for (int i = 0; i < argc; i++) {
+ warningMsg += Common::String::printf("%04x:%04x", PRINT_REG(argv[i]));
+ warningMsg += (i == argc - 1 ? ")" : ", ");
+ }
+
+ warning("%s", warningMsg.c_str());
+ break;
+ }
+
+ return s->r_acc;
+}
+
#endif
reg_t kSetVideoMode(EngineState *s, int argc, reg_t *argv) {
- // This call is used for KQ6's intro. It has one parameter, which is
- // 1 when the intro begins, and 0 when it ends. It is suspected that
- // this is actually a flag to enable video planar memory access, as
- // the video decoder in KQ6 is specifically written for the planar
- // memory model. Planar memory mode access was used for VGA "Mode X"
- // (320x240 resolution, although the intro in KQ6 is 320x200).
+ // This call is used for KQ6's intro. It has one parameter, which is 1 when
+ // the intro begins, and 0 when it ends. It is suspected that this is
+ // actually a flag to enable video planar memory access, as the video
+ // decoder in KQ6 is specifically written for the planar memory model.
+ // Planar memory mode access was used for VGA "Mode X" (320x240 resolution,
+ // although the intro in KQ6 is 320x200).
// Refer to http://en.wikipedia.org/wiki/Mode_X
//warning("STUB: SetVideoMode %d", argv[0].toUint16());
return s->r_acc;
}
-// New calls for SCI11. Using those is only needed when using text-codes so that one is able to change
-// font and/or color multiple times during kDisplay and kDrawControl
+// New calls for SCI11. Using those is only needed when using text-codes so that
+// one is able to change font and/or color multiple times during kDisplay and
+// kDrawControl
reg_t kTextFonts(EngineState *s, int argc, reg_t *argv) {
- g_sci->_gui->textFonts(argc, argv);
+ g_sci->_gfxText16->kernelTextFonts(argc, argv);
return s->r_acc;
}
reg_t kTextColors(EngineState *s, int argc, reg_t *argv) {
- g_sci->_gui->textColors(argc, argv);
+ g_sci->_gfxText16->kernelTextColors(argc, argv);
return s->r_acc;
}
diff --git a/engines/sci/engine/klists.cpp b/engines/sci/engine/klists.cpp
index f06f3eec77..0701883a9b 100644
--- a/engines/sci/engine/klists.cpp
+++ b/engines/sci/engine/klists.cpp
@@ -37,12 +37,12 @@ static bool isSaneNodePointer(SegManager *segMan, reg_t addr) {
Node *node = segMan->lookupNode(addr);
if (!node) {
- warning("isSaneNodePointer: Node at %04x:%04x wasn't found", PRINT_REG(addr));
+ error("isSaneNodePointer: Node at %04x:%04x wasn't found", PRINT_REG(addr));
return false;
}
if (havePrev && node->pred != prev) {
- warning("isSaneNodePointer: Node at %04x:%04x points to invalid predecessor %04x:%04x (should be %04x:%04x)",
+ error("isSaneNodePointer: Node at %04x:%04x points to invalid predecessor %04x:%04x (should be %04x:%04x)",
PRINT_REG(addr), PRINT_REG(node->pred), PRINT_REG(prev));
//node->pred = prev; // fix the problem in the node
@@ -58,33 +58,33 @@ static bool isSaneNodePointer(SegManager *segMan, reg_t addr) {
}
static void checkListPointer(SegManager *segMan, reg_t addr) {
- List *l = segMan->lookupList(addr);
+ List *list = segMan->lookupList(addr);
- if (!l) {
- warning("isSaneListPointer (list %04x:%04x): The requested list wasn't found",
+ if (!list) {
+ error("checkListPointer (list %04x:%04x): The requested list wasn't found",
PRINT_REG(addr));
return;
}
- if (l->first.isNull() && l->last.isNull()) {
+ if (list->first.isNull() && list->last.isNull()) {
// Empty list is fine
- } else if (!l->first.isNull() && !l->last.isNull()) {
+ } else if (!list->first.isNull() && !list->last.isNull()) {
// Normal list
- Node *node_a = segMan->lookupNode(l->first);
- Node *node_z = segMan->lookupNode(l->last);
+ Node *node_a = segMan->lookupNode(list->first);
+ Node *node_z = segMan->lookupNode(list->last);
if (!node_a) {
- warning("isSaneListPointer (list %04x:%04x): missing first node", PRINT_REG(addr));
+ error("checkListPointer (list %04x:%04x): missing first node", PRINT_REG(addr));
return;
}
if (!node_z) {
- warning("isSaneListPointer (list %04x:%04x): missing last node", PRINT_REG(addr));
+ error("checkListPointer (list %04x:%04x): missing last node", PRINT_REG(addr));
return;
}
if (!node_a->pred.isNull()) {
- warning("isSaneListPointer (list %04x:%04x): First node of the list points to a predecessor node",
+ error("checkListPointer (list %04x:%04x): First node of the list points to a predecessor node",
PRINT_REG(addr));
//node_a->pred = NULL_REG; // fix the problem in the node
@@ -93,7 +93,7 @@ static void checkListPointer(SegManager *segMan, reg_t addr) {
}
if (!node_z->succ.isNull()) {
- warning("isSaneListPointer (list %04x:%04x): Last node of the list points to a successor node",
+ error("checkListPointer (list %04x:%04x): Last node of the list points to a successor node",
PRINT_REG(addr));
//node_z->succ = NULL_REG; // fix the problem in the node
@@ -101,66 +101,42 @@ static void checkListPointer(SegManager *segMan, reg_t addr) {
return;
}
- isSaneNodePointer(segMan, l->first);
+ isSaneNodePointer(segMan, list->first);
} else {
// Not sane list... it's missing pointers to the first or last element
- if (l->first.isNull())
- warning("isSaneListPointer (list %04x:%04x): missing pointer to first element",
+ if (list->first.isNull())
+ error("checkListPointer (list %04x:%04x): missing pointer to first element",
PRINT_REG(addr));
- if (l->last.isNull())
- warning("isSaneListPointer (list %04x:%04x): missing pointer to last element",
+ if (list->last.isNull())
+ error("checkListPointer (list %04x:%04x): missing pointer to last element",
PRINT_REG(addr));
}
}
reg_t kNewList(EngineState *s, int argc, reg_t *argv) {
- reg_t listbase;
- List *l;
- l = s->_segMan->allocateList(&listbase);
- l->first = l->last = NULL_REG;
- debugC(2, kDebugLevelNodes, "New listbase at %04x:%04x", PRINT_REG(listbase));
+ reg_t listRef;
+ List *list = s->_segMan->allocateList(&listRef);
+ list->first = list->last = NULL_REG;
+ debugC(2, kDebugLevelNodes, "New listRef at %04x:%04x", PRINT_REG(listRef));
- return listbase; // Return list base address
+ return listRef; // Return list base address
}
reg_t kDisposeList(EngineState *s, int argc, reg_t *argv) {
// This function is not needed in ScummVM. The garbage collector
// cleans up unused objects automatically
-#if 0
- List *l = s->_segMan->lookupList(argv[0]);
-
- if (!l) {
- // FIXME: This should be an error, but it's turned to a warning for now
- warning("Attempt to dispose non-list at %04x:%04x", PRINT_REG(argv[0]));
- return NULL_REG;
- }
-
- checkListPointer(s->_segMan, argv[0]);
-
- if (!l->first.isNull()) {
- reg_t n_addr = l->first;
-
- while (!n_addr.isNull()) { // Free all nodes
- Node *n = s->_segMan->lookupNode(n_addr);
- n_addr = n->succ;
-
- //s->_segMan->free_Node(n_addr); // TODO
- }
- }
-
- //s->_segMan->free_list(argv[0]); // TODO
-#endif
-
return s->r_acc;
}
reg_t kNewNode(EngineState *s, int argc, reg_t *argv) {
reg_t nodeValue = argv[0];
- reg_t nodeKey = (argc == 2) ? argv[1] : NULL_REG;
+ // Some SCI32 games call this with 1 parameter (e.g. the demo of Phantasmagoria).
+ // Set the key to be the same as the value in this case
+ reg_t nodeKey = (argc == 2) ? argv[1] : argv[0];
s->r_acc = s->_segMan->newNode(nodeValue, nodeKey);
- debugC(2, kDebugLevelNodes, "New nodebase at %04x:%04x", PRINT_REG(s->r_acc));
+ debugC(2, kDebugLevelNodes, "New nodeRef at %04x:%04x", PRINT_REG(s->r_acc));
return s->r_acc;
}
@@ -169,11 +145,11 @@ reg_t kFirstNode(EngineState *s, int argc, reg_t *argv) {
if (argv[0].isNull())
return NULL_REG;
- List *l = s->_segMan->lookupList(argv[0]);
+ List *list = s->_segMan->lookupList(argv[0]);
- if (l) {
+ if (list) {
checkListPointer(s->_segMan, argv[0]);
- return l->first;
+ return list->first;
} else {
return NULL_REG;
}
@@ -183,11 +159,11 @@ reg_t kLastNode(EngineState *s, int argc, reg_t *argv) {
if (argv[0].isNull())
return NULL_REG;
- List *l = s->_segMan->lookupList(argv[0]);
+ List *list = s->_segMan->lookupList(argv[0]);
- if (l) {
+ if (list) {
checkListPointer(s->_segMan, argv[0]);
- return l->last;
+ return list->last;
} else {
return NULL_REG;
}
@@ -197,56 +173,56 @@ reg_t kEmptyList(EngineState *s, int argc, reg_t *argv) {
if (argv[0].isNull())
return NULL_REG;
- List *l = s->_segMan->lookupList(argv[0]);
+ List *list = s->_segMan->lookupList(argv[0]);
checkListPointer(s->_segMan, argv[0]);
- return make_reg(0, ((l) ? l->first.isNull() : 0));
+ return make_reg(0, ((list) ? list->first.isNull() : 0));
}
-static void _k_add_to_front(EngineState *s, reg_t listbase, reg_t nodebase) {
- List *l = s->_segMan->lookupList(listbase);
- Node *new_n = s->_segMan->lookupNode(nodebase);
+static void addToFront(EngineState *s, reg_t listRef, reg_t nodeRef) {
+ List *list = s->_segMan->lookupList(listRef);
+ Node *newNode = s->_segMan->lookupNode(nodeRef);
+
+ debugC(2, kDebugLevelNodes, "Adding node %04x:%04x to end of list %04x:%04x", PRINT_REG(nodeRef), PRINT_REG(listRef));
- debugC(2, kDebugLevelNodes, "Adding node %04x:%04x to end of list %04x:%04x", PRINT_REG(nodebase), PRINT_REG(listbase));
+ if (!newNode)
+ error("Attempt to add non-node (%04x:%04x) to list at %04x:%04x", PRINT_REG(nodeRef), PRINT_REG(listRef));
+ checkListPointer(s->_segMan, listRef);
- // FIXME: This should be an error, but it's turned to a warning for now
- if (!new_n)
- warning("Attempt to add non-node (%04x:%04x) to list at %04x:%04x", PRINT_REG(nodebase), PRINT_REG(listbase));
- checkListPointer(s->_segMan, listbase);
+ newNode->pred = NULL_REG;
+ newNode->succ = list->first;
- new_n->succ = l->first;
- new_n->pred = NULL_REG;
// Set node to be the first and last node if it's the only node of the list
- if (l->first.isNull())
- l->last = nodebase;
+ if (list->first.isNull())
+ list->last = nodeRef;
else {
- Node *old_n = s->_segMan->lookupNode(l->first);
- old_n->pred = nodebase;
+ Node *oldNode = s->_segMan->lookupNode(list->first);
+ oldNode->pred = nodeRef;
}
- l->first = nodebase;
+ list->first = nodeRef;
}
-static void _k_add_to_end(EngineState *s, reg_t listbase, reg_t nodebase) {
- List *l = s->_segMan->lookupList(listbase);
- Node *new_n = s->_segMan->lookupNode(nodebase);
+static void addToEnd(EngineState *s, reg_t listRef, reg_t nodeRef) {
+ List *list = s->_segMan->lookupList(listRef);
+ Node *newNode = s->_segMan->lookupNode(nodeRef);
- debugC(2, kDebugLevelNodes, "Adding node %04x:%04x to end of list %04x:%04x", PRINT_REG(nodebase), PRINT_REG(listbase));
+ debugC(2, kDebugLevelNodes, "Adding node %04x:%04x to end of list %04x:%04x", PRINT_REG(nodeRef), PRINT_REG(listRef));
- // FIXME: This should be an error, but it's turned to a warning for now
- if (!new_n)
- warning("Attempt to add non-node (%04x:%04x) to list at %04x:%04x", PRINT_REG(nodebase), PRINT_REG(listbase));
- checkListPointer(s->_segMan, listbase);
+ if (!newNode)
+ error("Attempt to add non-node (%04x:%04x) to list at %04x:%04x", PRINT_REG(nodeRef), PRINT_REG(listRef));
+ checkListPointer(s->_segMan, listRef);
+
+ newNode->pred = list->last;
+ newNode->succ = NULL_REG;
- new_n->succ = NULL_REG;
- new_n->pred = l->last;
// Set node to be the first and last node if it's the only node of the list
- if (l->last.isNull())
- l->first = nodebase;
+ if (list->last.isNull())
+ list->first = nodeRef;
else {
- Node *old_n = s->_segMan->lookupNode(l->last);
- old_n->succ = nodebase;
+ Node *old_n = s->_segMan->lookupNode(list->last);
+ old_n->succ = nodeRef;
}
- l->last = nodebase;
+ list->last = nodeRef;
}
reg_t kNextNode(EngineState *s, int argc, reg_t *argv) {
@@ -274,28 +250,30 @@ reg_t kNodeValue(EngineState *s, int argc, reg_t *argv) {
}
reg_t kAddToFront(EngineState *s, int argc, reg_t *argv) {
- _k_add_to_front(s, argv[0], argv[1]);
+ addToFront(s, argv[0], argv[1]);
return s->r_acc;
}
reg_t kAddAfter(EngineState *s, int argc, reg_t *argv) {
- List *l = s->_segMan->lookupList(argv[0]);
+ List *list = s->_segMan->lookupList(argv[0]);
Node *firstnode = argv[1].isNull() ? NULL : s->_segMan->lookupNode(argv[1]);
Node *newnode = s->_segMan->lookupNode(argv[2]);
checkListPointer(s->_segMan, argv[0]);
- // FIXME: This should be an error, but it's turned to a warning for now
if (!newnode) {
- warning("New 'node' %04x:%04x is not a node", PRINT_REG(argv[2]));
+ error("New 'node' %04x:%04x is not a node", PRINT_REG(argv[2]));
return NULL_REG;
}
- if (argc != 3) {
- warning("kAddAfter: Haven't got 3 arguments, aborting");
+ if (argc != 3 && argc != 4) {
+ error("kAddAfter: Haven't got 3 or 4 arguments, aborting");
return NULL_REG;
}
+ if (argc == 4)
+ newnode->key = argv[3];
+
if (firstnode) { // We're really appending after
reg_t oldnext = firstnode->succ;
@@ -305,19 +283,19 @@ reg_t kAddAfter(EngineState *s, int argc, reg_t *argv) {
if (oldnext.isNull()) // Appended after last node?
// Set new node as last list node
- l->last = argv[2];
+ list->last = argv[2];
else
s->_segMan->lookupNode(oldnext)->pred = argv[2];
} else { // !firstnode
- _k_add_to_front(s, argv[0], argv[2]); // Set as initial list node
+ addToFront(s, argv[0], argv[2]); // Set as initial list node
}
return s->r_acc;
}
reg_t kAddToEnd(EngineState *s, int argc, reg_t *argv) {
- _k_add_to_end(s, argv[0], argv[1]);
+ addToEnd(s, argv[0], argv[1]);
return s->r_acc;
}
@@ -352,23 +330,27 @@ reg_t kFindKey(EngineState *s, int argc, reg_t *argv) {
reg_t kDeleteKey(EngineState *s, int argc, reg_t *argv) {
reg_t node_pos = kFindKey(s, 2, argv);
Node *n;
- List *l = s->_segMan->lookupList(argv[0]);
+ List *list = s->_segMan->lookupList(argv[0]);
if (node_pos.isNull())
return NULL_REG; // Signal failure
n = s->_segMan->lookupNode(node_pos);
- if (l->first == node_pos)
- l->first = n->succ;
- if (l->last == node_pos)
- l->last = n->pred;
+ if (list->first == node_pos)
+ list->first = n->succ;
+ if (list->last == node_pos)
+ list->last = n->pred;
if (!n->pred.isNull())
s->_segMan->lookupNode(n->pred)->succ = n->succ;
if (!n->succ.isNull())
s->_segMan->lookupNode(n->succ)->pred = n->pred;
- //s->_segMan->free_Node(node_pos); // TODO
+ // Erase references to the predecessor and successor nodes, as the game
+ // scripts could reference the node itself again.
+ // Happens in the intro of QFG1 and in Longbow, when exiting the cave.
+ n->pred = NULL_REG;
+ n->succ = NULL_REG;
return make_reg(0, 1); // Signal success
}
@@ -398,8 +380,6 @@ reg_t kSort(EngineState *s, int argc, reg_t *argv) {
reg_t order_func = argv[2];
int input_size = (int16)readSelectorValue(segMan, source, SELECTOR(size));
- int i;
-
reg_t input_data = readSelector(segMan, source, SELECTOR(elements));
reg_t output_data = readSelector(segMan, dest, SELECTOR(elements));
@@ -422,9 +402,10 @@ reg_t kSort(EngineState *s, int argc, reg_t *argv) {
sort_temp_t *temp_array = (sort_temp_t *)malloc(sizeof(sort_temp_t) * input_size);
- i = 0;
+ int i = 0;
while (node) {
- invokeSelector(INV_SEL(s, order_func, doit, kStopOnInvalidSelector), 1, node->value);
+ reg_t params[1] = { node->value };
+ invokeSelector(s, order_func, SELECTOR(doit), argc, argv, 1, params);
temp_array[i].key = node->key;
temp_array[i].value = node->value;
temp_array[i].order = s->r_acc;
@@ -436,7 +417,7 @@ reg_t kSort(EngineState *s, int argc, reg_t *argv) {
for (i = 0;i < input_size;i++) {
reg_t lNode = s->_segMan->newNode(temp_array[i].value, temp_array[i].key);
- _k_add_to_end(s, output_data, lNode);
+ addToEnd(s, output_data, lNode);
}
free(temp_array);
@@ -449,14 +430,14 @@ reg_t kSort(EngineState *s, int argc, reg_t *argv) {
reg_t kListAt(EngineState *s, int argc, reg_t *argv) {
if (argc != 2) {
- warning("kListAt called with %d parameters", argc);
+ error("kListAt called with %d parameters", argc);
return NULL_REG;
}
List *list = s->_segMan->lookupList(argv[0]);
reg_t curAddress = list->first;
if (list->first.isNull()) {
- warning("kListAt tried to reference empty list (%04x:%04x)", PRINT_REG(argv[0]));
+ error("kListAt tried to reference empty list (%04x:%04x)", PRINT_REG(argv[0]));
return NULL_REG;
}
Node *curNode = s->_segMan->lookupNode(curAddress);
@@ -518,12 +499,12 @@ reg_t kListEachElementDo(EngineState *s, int argc, reg_t *argv) {
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);
+ error("kListEachElementDo: Attempted to modify a variable selector with %d params", argc);
} else {
writeSelector(s->_segMan, curObject, slc, argv[2]);
}
} else {
- invokeSelectorArgv(s, curObject, slc, kContinueOnInvalidSelector, argc, argv, argc - 2, argv + 2);
+ invokeSelector(s, curObject, slc, argc, argv, argc - 2, argv + 2);
}
curNode = s->_segMan->lookupNode(nextNode);
@@ -550,9 +531,9 @@ reg_t kListFirstTrue(EngineState *s, int argc, reg_t *argv) {
// First, check if the target selector is a variable
if (lookupSelector(s->_segMan, curObject, slc, &address, NULL) == kSelectorVariable) {
// Can this happen with variable selectors?
- warning("kListFirstTrue: Attempted to access a variable selector");
+ error("kListFirstTrue: Attempted to access a variable selector");
} else {
- invokeSelectorArgv(s, curObject, slc, kContinueOnInvalidSelector, argc, argv, argc - 2, argv + 2);
+ invokeSelector(s, curObject, slc, argc, argv, argc - 2, argv + 2);
// Check if the result is true
if (!s->r_acc.isNull())
@@ -584,9 +565,9 @@ reg_t kListAllTrue(EngineState *s, int argc, reg_t *argv) {
// First, check if the target selector is a variable
if (lookupSelector(s->_segMan, curObject, slc, &address, NULL) == kSelectorVariable) {
// Can this happen with variable selectors?
- warning("kListAllTrue: Attempted to access a variable selector");
+ error("kListAllTrue: Attempted to access a variable selector");
} else {
- invokeSelectorArgv(s, curObject, slc, kContinueOnInvalidSelector, argc, argv, argc - 2, argv + 2);
+ invokeSelector(s, curObject, slc, argc, argv, argc - 2, argv + 2);
// Check if the result isn't true
if (s->r_acc.isNull())
@@ -627,15 +608,15 @@ reg_t kList(EngineState *s, int argc, reg_t *argv) {
case 11:
return kAddToEnd(s, argc - 1, argv + 1);
case 12:
- warning("kList: unimplemented subfunction kAddBefore");
+ error("kList: unimplemented subfunction kAddBefore");
//return kAddBefore(s, argc - 1, argv + 1);
return NULL_REG;
case 13:
- warning("kList: unimplemented subfunction kMoveToFront");
+ error("kList: unimplemented subfunction kMoveToFront");
//return kMoveToFront(s, argc - 1, argv + 1);
return NULL_REG;
case 14:
- warning("kList: unimplemented subfunction kMoveToEnd");
+ error("kList: unimplemented subfunction kMoveToEnd");
//return kMoveToEnd(s, argc - 1, argv + 1);
return NULL_REG;
case 15:
@@ -655,7 +636,7 @@ reg_t kList(EngineState *s, int argc, reg_t *argv) {
case 22:
return kSort(s, argc - 1, argv + 1);
default:
- warning("kList: Unhandled case %d", argv[0].toUint16());
+ error("kList: Unhandled case %d", argv[0].toUint16());
return NULL_REG;
}
}
diff --git a/engines/sci/engine/kmath.cpp b/engines/sci/engine/kmath.cpp
index dbf317860f..eab964d624 100644
--- a/engines/sci/engine/kmath.cpp
+++ b/engines/sci/engine/kmath.cpp
@@ -36,9 +36,6 @@ reg_t kRandom(EngineState *s, int argc, reg_t *argv) {
}
reg_t kAbs(EngineState *s, int argc, reg_t *argv) {
- // This is a hack, but so is the code in Hoyle1 that needs it.
- if (argv[0].segment)
- return make_reg(0, 0x3e9); // Yes people, this is an object
return make_reg(0, abs(argv[0].toSint16()));
}
@@ -112,7 +109,7 @@ reg_t kCosDiv(EngineState *s, int argc, reg_t *argv) {
double cosval = cos(angle * PI / 180.0);
if ((cosval < 0.0001) && (cosval > -0.0001)) {
- warning("kCosDiv: Attempted division by zero");
+ error("kCosDiv: Attempted division by zero");
return SIGNAL_REG;
} else
return make_reg(0, (int16)(value / cosval));
@@ -124,7 +121,7 @@ reg_t kSinDiv(EngineState *s, int argc, reg_t *argv) {
double sinval = sin(angle * PI / 180.0);
if ((sinval < 0.0001) && (sinval > -0.0001)) {
- warning("kSinDiv: Attempted division by zero");
+ error("kSinDiv: Attempted division by zero");
return SIGNAL_REG;
} else
return make_reg(0, (int16)(value / sinval));
@@ -136,7 +133,7 @@ reg_t kTimesTan(EngineState *s, int argc, reg_t *argv) {
param -= 90;
if ((param % 90) == 0) {
- warning("kTimesTan: Attempted tan(pi/2)");
+ error("kTimesTan: Attempted tan(pi/2)");
return SIGNAL_REG;
} else
return make_reg(0, (int16) - (tan(param * PI / 180.0) * scale));
@@ -147,10 +144,28 @@ reg_t kTimesCot(EngineState *s, int argc, reg_t *argv) {
int scale = (argc > 1) ? argv[1].toSint16() : 1;
if ((param % 90) == 0) {
- warning("kTimesCot: Attempted tan(pi/2)");
+ error("kTimesCot: Attempted tan(pi/2)");
return SIGNAL_REG;
} else
return make_reg(0, (int16)(tan(param * PI / 180.0) * scale));
}
+#ifdef ENABLE_SCI32
+
+reg_t kMulDiv(EngineState *s, int argc, reg_t *argv) {
+ int16 multiplicant = argv[0].toSint16();
+ int16 multiplier = argv[1].toSint16();
+ int16 denominator = argv[2].toSint16();
+
+ // Sanity check...
+ if (!denominator) {
+ error("kMulDiv: attempt to divide by zero (%d * %d / %d", multiplicant, multiplier, denominator);
+ return NULL_REG;
+ }
+
+ return make_reg(0, multiplicant * multiplier / denominator);
+}
+
+#endif
+
} // End of namespace Sci
diff --git a/engines/sci/engine/kmenu.cpp b/engines/sci/engine/kmenu.cpp
index a5f2f01297..c8a6e03556 100644
--- a/engines/sci/engine/kmenu.cpp
+++ b/engines/sci/engine/kmenu.cpp
@@ -27,7 +27,6 @@
#include "sci/resource.h"
#include "sci/engine/state.h"
#include "sci/engine/kernel.h"
-#include "sci/graphics/gui.h"
#include "sci/graphics/cursor.h"
#include "sci/graphics/menu.h"
@@ -74,7 +73,7 @@ reg_t kDrawStatus(EngineState *s, int argc, reg_t *argv) {
int16 colorBack = (argc > 2) ? argv[2].toSint16() : g_sci->getResMan()->isVGA() ? 255 : 15;
if (!textReference.isNull()) {
- // Sometimes this is called without giving text, if thats the case dont process it
+ // Sometimes this is called without giving text, if thats the case dont process it.
text = s->_segMan->getString(textReference);
g_sci->_gfxMenu->kernelDrawStatus(g_sci->strSplit(text.c_str(), NULL).c_str(), colorPen, colorBack);
@@ -91,10 +90,9 @@ reg_t kDrawMenuBar(EngineState *s, int argc, reg_t *argv) {
reg_t kMenuSelect(EngineState *s, int argc, reg_t *argv) {
reg_t eventObject = argv[0];
- //bool pauseSound = argc > 1 ? (argv[1].isNull() ? false : true) : false;
+ bool pauseSound = argc > 1 ? (argv[1].isNull() ? false : true) : true;
- // TODO: pauseSound implementation
- return g_sci->_gfxMenu->kernelSelect(eventObject);
+ return g_sci->_gfxMenu->kernelSelect(eventObject, pauseSound);
}
} // End of namespace Sci
diff --git a/engines/sci/engine/kmisc.cpp b/engines/sci/engine/kmisc.cpp
index f91ba0fd82..b8c62210f9 100644
--- a/engines/sci/engine/kmisc.cpp
+++ b/engines/sci/engine/kmisc.cpp
@@ -31,17 +31,14 @@
#include "sci/engine/state.h"
#include "sci/engine/kernel.h"
#include "sci/engine/gc.h"
-#include "sci/graphics/gui.h"
#include "sci/graphics/maciconbar.h"
namespace Sci {
reg_t kRestartGame(EngineState *s, int argc, reg_t *argv) {
- s->restarting_flags |= SCI_GAME_IS_RESTARTING_NOW;
-
s->shrinkStackToBase();
- s->script_abort_flag = 1; // Force vm to abort ASAP
+ s->abortScriptProcessing = kAbortRestartGame; // Force vm to abort ASAP
return NULL_REG;
}
@@ -49,47 +46,28 @@ reg_t kRestartGame(EngineState *s, int argc, reg_t *argv) {
** Returns the restarting_flag in acc
*/
reg_t kGameIsRestarting(EngineState *s, int argc, reg_t *argv) {
- s->r_acc = make_reg(0, (s->restarting_flags & SCI_GAME_WAS_RESTARTED));
+ s->r_acc = make_reg(0, s->gameWasRestarted);
if (argc) { // Only happens during replay
if (!argv[0].toUint16()) // Set restarting flag
- s->restarting_flags &= ~SCI_GAME_WAS_RESTARTED;
+ s->gameWasRestarted = false;
}
uint32 neededSleep = 30;
- // WORKAROUND:
- // LSL3 calculates a machinespeed variable during game startup (right after the filthy questions)
- // This one would go through w/o throttling resulting in having to do 1000 pushups or something
- // Another way of handling this would be delaying incrementing of "machineSpeed" selector
- if (!strcmp(g_sci->getGameID(), "lsl3") && s->currentRoomNumber() == 290)
+ // WORKAROUND: LSL3 calculates a machinespeed variable during game startup
+ // (right after the filthy questions). This one would go through w/o
+ // throttling resulting in having to do 1000 pushups or something. Another
+ // way of handling this would be delaying incrementing of "machineSpeed"
+ // selector.
+ if (g_sci->getGameId() == GID_LSL3 && s->currentRoomNumber() == 290)
s->_throttleTrigger = true;
- if (!strcmp(g_sci->getGameID(), "iceman") && s->currentRoomNumber() == 27) {
+ else if (g_sci->getGameId() == GID_ICEMAN && s->currentRoomNumber() == 27) {
s->_throttleTrigger = true;
neededSleep = 60;
}
- if (s->_throttleTrigger) {
- // Some games seem to get the duration of main loop initially and then switch of animations for the whole game
- // based on that (qfg2, iceman). We are now running full speed initially to avoid that.
- // It seems like we dont need to do that anymore
- //if (s->_throttleCounter < 50) {
- // s->_throttleCounter++;
- // return s->r_acc;
- //}
-
- uint32 curTime = g_system->getMillis();
- uint32 duration = curTime - s->_throttleLastTime;
-
- if (duration < neededSleep) {
- s->_event->sleep(neededSleep - duration);
- s->_throttleLastTime = g_system->getMillis();
- } else {
- s->_throttleLastTime = curTime;
- }
- s->_throttleTrigger = false;
- }
-
+ s->speedThrottler(neededSleep);
return s->r_acc;
}
@@ -106,7 +84,11 @@ enum kMemoryInfoFunc {
};
reg_t kMemoryInfo(EngineState *s, int argc, reg_t *argv) {
- const uint16 size = 0x7fff; // Must not be 0xffff, or some memory calculations will overflow
+ // The free heap size returned must not be 0xffff, or some memory
+ // calculations will overflow. Crazy Nick's games handle up to 32746
+ // bytes (0x7fea), otherwise they throw a warning that the memory is
+ // fragmented
+ const uint16 size = 0x7fea;
switch (argv[0].offset) {
case K_MEMORYINFO_LARGEST_HEAP_BLOCK:
@@ -120,7 +102,7 @@ reg_t kMemoryInfo(EngineState *s, int argc, reg_t *argv) {
return make_reg(0, size);
default:
- warning("Unknown MemoryInfo operation: %04x", argv[0].offset);
+ error("Unknown MemoryInfo operation: %04x", argv[0].offset);
}
return NULL_REG;
@@ -172,8 +154,8 @@ reg_t kFlushResources(EngineState *s, int argc, reg_t *argv) {
reg_t kSetDebug(EngineState *s, int argc, reg_t *argv) {
printf("Debug mode activated\n");
- g_debugState.seeking = kDebugSeekNothing;
- g_debugState.runningStep = 0;
+ g_sci->_debugState.seeking = kDebugSeekNothing;
+ g_sci->_debugState.runningStep = 0;
return s->r_acc;
}
@@ -190,12 +172,12 @@ reg_t kGetTime(EngineState *s, int argc, reg_t *argv) {
int retval = 0; // Avoid spurious warning
g_system->getTimeAndDate(loc_time);
- elapsedTime = g_system->getMillis() - s->game_start_time;
+ elapsedTime = g_system->getMillis() - s->gameStartTime;
int mode = (argc > 0) ? argv[0].toUint16() : 0;
if (getSciVersion() <= SCI_VERSION_0_LATE && mode > 1)
- warning("kGetTime called in SCI0 with mode %d (expected 0 or 1)", mode);
+ error("kGetTime called in SCI0 with mode %d (expected 0 or 1)", mode);
switch (mode) {
case K_NEW_GETTIME_TICKS :
@@ -215,7 +197,7 @@ reg_t kGetTime(EngineState *s, int argc, reg_t *argv) {
debugC(2, kDebugLevelTime, "GetTime(date) returns %d", retval);
break;
default:
- warning("Attempt to use unknown GetTime mode %d", mode);
+ error("Attempt to use unknown GetTime mode %d", mode);
break;
}
@@ -261,7 +243,7 @@ reg_t kMemory(EngineState *s, int argc, reg_t *argv) {
SegmentRef ref = s->_segMan->dereference(argv[1]);
if (!ref.isValid() || ref.maxSize < 2) {
- warning("Attempt to peek invalid memory at %04x:%04x", PRINT_REG(argv[1]));
+ error("Attempt to peek invalid memory at %04x:%04x", PRINT_REG(argv[1]));
return s->r_acc;
}
if (ref.isRaw)
@@ -277,7 +259,7 @@ reg_t kMemory(EngineState *s, int argc, reg_t *argv) {
SegmentRef ref = s->_segMan->dereference(argv[1]);
if (!ref.isValid() || ref.maxSize < 2) {
- warning("Attempt to poke invalid memory at %04x:%04x", PRINT_REG(argv[1]));
+ error("Attempt to poke invalid memory at %04x:%04x", PRINT_REG(argv[1]));
return s->r_acc;
}
@@ -335,11 +317,12 @@ reg_t kPlatform(EngineState *s, int argc, reg_t *argv) {
bool isWindows = g_sci->getPlatform() == Common::kPlatformWindows;
if (argc == 0 && getSciVersion() < SCI_VERSION_2) {
- // This is called in KQ5CD with no parameters, where it seems to do some graphics
- // driver check. This kernel function didn't have subfunctions then. If 0 is
- // returned, the game functions normally, otherwise all the animations show up
- // like a slideshow (e.g. in the intro). So we return 0. However, the behavior
- // changed for kPlatform with no parameters in SCI32.
+ // This is called in KQ5CD with no parameters, where it seems to do some
+ // graphics driver check. This kernel function didn't have subfunctions
+ // then. If 0 is returned, the game functions normally, otherwise all
+ // the animations show up like a slideshow (e.g. in the intro). So we
+ // return 0. However, the behavior changed for kPlatform with no
+ // parameters in SCI32.
return NULL_REG;
}
@@ -371,10 +354,18 @@ reg_t kPlatform(EngineState *s, int argc, reg_t *argv) {
case kPlatformIsItWindows:
return make_reg(0, isWindows);
default:
- warning("Unsupported kPlatform operation %d", operation);
+ error("Unsupported kPlatform operation %d", operation);
}
return NULL_REG;
}
+reg_t kEmpty(EngineState *s, int argc, reg_t *argv) {
+ // Placeholder for empty kernel functions which are still called from the
+ // engine scripts (like the empty kSetSynonyms function in SCI1.1). This
+ // differs from dummy functions because it does nothing and never throws a
+ // warning when it is called.
+ return s->r_acc;
+}
+
} // End of namespace Sci
diff --git a/engines/sci/engine/kmovement.cpp b/engines/sci/engine/kmovement.cpp
index 499aeabcc6..ccef3d862a 100644
--- a/engines/sci/engine/kmovement.cpp
+++ b/engines/sci/engine/kmovement.cpp
@@ -33,42 +33,43 @@
namespace Sci {
-/*
-Compute "velocity" vector (xStep,yStep)=(vx,vy) for a jump from (0,0) to (dx,dy), with gravity gy.
-The gravity is assumed to be non-negative.
-
-If this was ordinary continuous physics, we would compute the desired (floating point!)
-velocity vector (vx,vy) as follows, under the assumption that vx and vy are linearly correlated
-by some constant factor c, i.e. vy = c * vx:
- dx = t * vx
- dy = t * vy + gy * t^2 / 2
-=> dy = c * dx + gy * (dx/vx)^2 / 2
-=> |vx| = sqrt( gy * dx^2 / (2 * (dy - c * dx)) )
-Here, the sign of vx must be chosen equal to the sign of dx, obviously.
-
-Clearly, this square root only makes sense in our context if the denominator is positive,
-or equivalently, (dy - c * dx) must be positive. For simplicity and by symmetry
-along the x-axis, we assume dx to be positive for all computations, and only adjust for
-its sign in the end. Switching the sign of c appropriately, we set tmp := (dy + c * dx)
-and compute c so that this term becomes positive.
-
-Remark #1: If the jump is straight up, i.e. dx == 0, then we should not assume the above
-linear correlation vy = c * vx of the velocities (as vx will be 0, but vy shouldn't be,
-unless we drop).
-
-
-Remark #2: We are actually in a discrete setup. The motion is computed iteratively: each iteration,
-we add vx and vy to the position, then add gy to vy. So the real formula is the following
-(where t is ideally close to an int):
-
- dx = t * vx
- dy = t * vy + gy * t*(t-1) / 2
-
-But the solution resulting from that is a lot more complicated, so we use the above approximation instead.
-
-Still, what we compute in the end is of course not a real velocity anymore, but an integer approximation,
-used in an iterative stepping algorithm
-*/
+/**
+ * Compute "velocity" vector (xStep,yStep)=(vx,vy) for a jump from (0,0) to
+ * (dx,dy), with gravity constant gy. The gravity is assumed to be non-negative.
+ *
+ * If this was ordinary continuous physics, we would compute the desired
+ * (floating point!) velocity vector (vx,vy) as follows, under the assumption
+ * that vx and vy are linearly correlated by a constant c, i.e., vy = c * vx:
+ * dx = t * vx
+ * dy = t * vy + gy * t^2 / 2
+ * => dy = c * dx + gy * (dx/vx)^2 / 2
+ * => |vx| = sqrt( gy * dx^2 / (2 * (dy - c * dx)) )
+ * Here, the sign of vx must be chosen equal to the sign of dx, obviously.
+ *
+ * This square root only makes sense in our context if the denominator is
+ * positive, or equivalently, (dy - c * dx) must be positive. For simplicity
+ * and by symmetry along the x-axis, we assume dx to be positive for all
+ * computations, and only adjust for its sign in the end. Switching the sign of
+ * c appropriately, we set tmp := (dy + c * dx) and compute c so that this term
+ * becomes positive.
+ *
+ * Remark #1: If the jump is straight up, i.e. dx == 0, then we should not
+ * assume the above linear correlation vy = c * vx of the velocities (as vx
+ * will be 0, but vy shouldn't be, unless we drop down).
+ *
+ * Remark #2: We are actually in a discrete setup. The motion is computed
+ * iteratively: each iteration, we add vx and vy to the position, then add gy
+ * to vy. So the real formula is the following (where t ideally is close to an int):
+ *
+ * dx = t * vx
+ * dy = t * vy + gy * t*(t-1) / 2
+ *
+ * But the solution resulting from that is a lot more complicated, so we use
+ * the above approximation instead.
+ *
+ * Still, what we compute in the end is of course not a real velocity anymore,
+ * but an integer approximation, used in an iterative stepping algorithm.
+ */
reg_t kSetJump(EngineState *s, int argc, reg_t *argv) {
SegManager *segMan = s->_segMan;
// Input data
@@ -115,7 +116,7 @@ reg_t kSetJump(EngineState *s, int argc, reg_t *argv) {
//tmp = dx * 3 / 2; // ALMOST the resulting value, except for obvious rounding issues
// FIXME: Where is the 3 coming from? Maybe they hard/coded, by "accident", that usually gy=3 ?
- // Then this choice of will make t equal to roughly sqrt(dx)
+ // Then this choice of scalar will make t equal to roughly sqrt(dx)
}
}
// POST: c >= 1
@@ -315,11 +316,11 @@ reg_t kDoBresen(EngineState *s, int argc, reg_t *argv) {
debugC(2, kDebugLevelBresen, "New data: (x,y)=(%d,%d), di=%d", x, y, bdi);
- if (g_sci->getKernel()->_selectorCache.cantBeHere != -1) {
- invokeSelector(INV_SEL(s, client, cantBeHere, kStopOnInvalidSelector), 0);
+ if (SELECTOR(cantBeHere) != -1) {
+ invokeSelector(s, client, SELECTOR(cantBeHere), argc, argv);
s->r_acc = make_reg(0, !s->r_acc.offset);
} else {
- invokeSelector(INV_SEL(s, client, canBeHere, kStopOnInvalidSelector), 0);
+ invokeSelector(s, client, SELECTOR(canBeHere), argc, argv);
}
if (!s->r_acc.offset) { // Contains the return value
@@ -335,7 +336,7 @@ reg_t kDoBresen(EngineState *s, int argc, reg_t *argv) {
if ((getSciVersion() >= SCI_VERSION_1_EGA))
if (completed)
- invokeSelector(INV_SEL(s, mover, moveDone, kStopOnInvalidSelector), 0);
+ invokeSelector(s, mover, SELECTOR(moveDone), argc, argv);
return make_reg(0, completed);
}
@@ -373,14 +374,14 @@ reg_t kDoAvoider(EngineState *s, int argc, reg_t *argv) {
s->r_acc = SIGNAL_REG;
if (!s->_segMan->isHeapObject(avoider)) {
- warning("DoAvoider() where avoider %04x:%04x is not an object", PRINT_REG(avoider));
+ error("DoAvoider() where avoider %04x:%04x is not an object", PRINT_REG(avoider));
return NULL_REG;
}
client = readSelector(segMan, avoider, SELECTOR(client));
if (!s->_segMan->isHeapObject(client)) {
- warning("DoAvoider() where client %04x:%04x is not an object", PRINT_REG(client));
+ error("DoAvoider() where client %04x:%04x is not an object", PRINT_REG(client));
return NULL_REG;
}
@@ -389,7 +390,7 @@ reg_t kDoAvoider(EngineState *s, int argc, reg_t *argv) {
if (!s->_segMan->isHeapObject(mover)) {
if (mover.segment) {
- warning("DoAvoider() where mover %04x:%04x is not an object", PRINT_REG(mover));
+ error("DoAvoider() where mover %04x:%04x is not an object", PRINT_REG(mover));
}
return s->r_acc;
}
@@ -399,20 +400,13 @@ reg_t kDoAvoider(EngineState *s, int argc, reg_t *argv) {
debugC(2, kDebugLevelBresen, "Doing avoider %04x:%04x (dest=%d,%d)", PRINT_REG(avoider), destx, desty);
- 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;
- }
+ invokeSelector(s, mover, SELECTOR(doit), argc, argv);
mover = readSelector(segMan, client, SELECTOR(mover));
if (!mover.segment) // Mover has been disposed?
return s->r_acc; // Return gracefully.
- 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;
- }
+ invokeSelector(s, client, SELECTOR(isBlocked), argc, argv);
dx = destx - readSelectorValue(segMan, client, SELECTOR(x));
dy = desty - readSelectorValue(segMan, client, SELECTOR(y));
@@ -439,11 +433,7 @@ reg_t kDoAvoider(EngineState *s, int argc, reg_t *argv) {
debugC(2, kDebugLevelBresen, "Pos (%d,%d): Trying angle %d; delta=(%d,%d)", oldx, oldy, angle, move_x, move_y);
- 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;
- }
+ invokeSelector(s, client, SELECTOR(canBeHere), argc, argv);
writeSelectorValue(segMan, client, SELECTOR(x), oldx);
writeSelectorValue(segMan, client, SELECTOR(y), oldy);
@@ -461,7 +451,7 @@ reg_t kDoAvoider(EngineState *s, int argc, reg_t *argv) {
angle -= 360;
}
- warning("DoAvoider failed for avoider %04x:%04x", PRINT_REG(avoider));
+ error("DoAvoider failed for avoider %04x:%04x", PRINT_REG(avoider));
} else {
int heading = readSelectorValue(segMan, client, SELECTOR(heading));
@@ -472,12 +462,11 @@ reg_t kDoAvoider(EngineState *s, int argc, reg_t *argv) {
s->r_acc = make_reg(0, angle);
+ reg_t params[2] = { make_reg(0, angle), client };
+
if (looper.segment) {
- 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
- return s->r_acc;
+ invokeSelector(s, looper, SELECTOR(doit), argc, argv, 2, params);
+ return s->r_acc;
} else {
// No looper? Fall back to DirLoop
_k_dirloop(client, (uint16)angle, s, argc, argv);
diff --git a/engines/sci/engine/kparse.cpp b/engines/sci/engine/kparse.cpp
index 785ff39d22..45493a95d2 100644
--- a/engines/sci/engine/kparse.cpp
+++ b/engines/sci/engine/kparse.cpp
@@ -94,8 +94,8 @@ reg_t kParse(EngineState *s, int argc, reg_t *argv) {
ResultWordList words;
reg_t event = argv[1];
Vocabulary *voc = g_sci->getVocabulary();
-
voc->parser_event = event;
+ reg_t params[2] = { voc->parser_base, stringpos };
bool res = voc->tokenizeString(words, string.c_str(), &error);
voc->parserIsValid = false; /* not valid */
@@ -118,7 +118,7 @@ reg_t kParse(EngineState *s, int argc, reg_t *argv) {
s->r_acc = make_reg(0, 1);
writeSelectorValue(segMan, event, SELECTOR(claimed), 1);
- invokeSelector(INV_SEL(s, s->_gameObj, syntaxFail, kStopOnInvalidSelector), 2, voc->parser_base, stringpos);
+ invokeSelector(s, g_sci->getGameObject(), SELECTOR(syntaxFail), argc, argv, 2, params);
/* Issue warning */
debugC(2, kDebugLevelParser, "Tree building failed");
@@ -141,7 +141,7 @@ reg_t kParse(EngineState *s, int argc, reg_t *argv) {
debugC(2, kDebugLevelParser, "Word unknown: %s", error);
/* Issue warning: */
- invokeSelector(INV_SEL(s, s->_gameObj, wordFail, kStopOnInvalidSelector), 2, voc->parser_base, stringpos);
+ invokeSelector(s, g_sci->getGameObject(), SELECTOR(wordFail), argc, argv, 2, params);
free(error);
return make_reg(0, 1); /* Tell them that it didn't work */
}
diff --git a/engines/sci/engine/kpathing.cpp b/engines/sci/engine/kpathing.cpp
index 857ccc2a08..fdaae3e121 100644
--- a/engines/sci/engine/kpathing.cpp
+++ b/engines/sci/engine/kpathing.cpp
@@ -436,33 +436,41 @@ static void print_input(EngineState *s, reg_t poly_list, Common::Point start, Co
}
}
+/**
+ * Computes the area of a triangle
+ * Parameters: (const Common::Point &) a, b, c: The points of the triangle
+ * Returns : (int) The area multiplied by two
+ */
static int area(const Common::Point &a, const Common::Point &b, const Common::Point &c) {
- // Computes the area of a triangle
- // Parameters: (const Common::Point &) a, b, c: The points of the triangle
- // Returns : (int) The area multiplied by two
return (b.x - a.x) * (a.y - c.y) - (c.x - a.x) * (a.y - b.y);
}
+/**
+ * Determines whether or not a point is to the left of a directed line
+ * Parameters: (const Common::Point &) a, b: The directed line (a, b)
+ * (const Common::Point &) c: The query point
+ * Returns : (int) true if c is to the left of (a, b), false otherwise
+ */
static bool left(const Common::Point &a, const Common::Point &b, const Common::Point &c) {
- // Determines whether or not a point is to the left of a directed line
- // Parameters: (const Common::Point &) a, b: The directed line (a, b)
- // (const Common::Point &) c: The query point
- // Returns : (int) true if c is to the left of (a, b), false otherwise
return area(a, b, c) > 0;
}
+/**
+ * Determines whether or not three points are collinear
+ * Parameters: (const Common::Point &) a, b, c: The three points
+ * Returns : (int) true if a, b, and c are collinear, false otherwise
+ */
static bool collinear(const Common::Point &a, const Common::Point &b, const Common::Point &c) {
- // Determines whether or not three points are collinear
- // Parameters: (const Common::Point &) a, b, c: The three points
- // Returns : (int) true if a, b, and c are collinear, false otherwise
return area(a, b, c) == 0;
}
+/**
+ * Determines whether or not a point lies on a line segment
+ * Parameters: (const Common::Point &) a, b: The line segment (a, b)
+ * (const Common::Point &) c: The query point
+ * Returns : (int) true if c lies on (a, b), false otherwise
+ */
static bool between(const Common::Point &a, const Common::Point &b, const Common::Point &c) {
- // Determines whether or not a point lies on a line segment
- // Parameters: (const Common::Point &) a, b: The line segment (a, b)
- // (const Common::Point &) c: The query point
- // Returns : (int) true if c lies on (a, b), false otherwise
if (!collinear(a, b, c))
return false;
@@ -473,25 +481,29 @@ static bool between(const Common::Point &a, const Common::Point &b, const Common
return ((a.y <= c.y) && (c.y <= b.y)) || ((a.y >= c.y) && (c.y >= b.y));
}
+/**
+ * Determines whether or not two line segments properly intersect
+ * Parameters: (const Common::Point &) a, b: The line segment (a, b)
+ * (const Common::Point &) c, d: The line segment (c, d)
+ * Returns : (int) true if (a, b) properly intersects (c, d), false otherwise
+ */
static bool intersect_proper(const Common::Point &a, const Common::Point &b, const Common::Point &c, const Common::Point &d) {
- // Determines whether or not two line segments properly intersect
- // Parameters: (const Common::Point &) a, b: The line segment (a, b)
- // (const Common::Point &) c, d: The line segment (c, d)
- // Returns : (int) true if (a, b) properly intersects (c, d), false otherwise
int ab = (left(a, b, c) && left(b, a, d)) || (left(a, b, d) && left(b, a, c));
int cd = (left(c, d, a) && left(d, c, b)) || (left(c, d, b) && left(d, c, a));
return ab && cd;
}
+/**
+ * Polygon containment test
+ * Parameters: (const Common::Point &) p: The point
+ * (Polygon *) polygon: The polygon
+ * Returns : (int) CONT_INSIDE if p is strictly contained in polygon,
+ * CONT_ON_EDGE if p lies on an edge of polygon,
+ * CONT_OUTSIDE otherwise
+ * Number of ray crossing left and right
+ */
static int contained(const Common::Point &p, Polygon *polygon) {
- // Polygon containment test
- // Parameters: (const Common::Point &) p: The point
- // (Polygon *) polygon: The polygon
- // Returns : (int) CONT_INSIDE if p is strictly contained in polygon,
- // CONT_ON_EDGE if p lies on an edge of polygon,
- // CONT_OUTSIDE otherwise
- // Number of ray crossing left and right
int lcross = 0, rcross = 0;
Vertex *vertex;
@@ -549,10 +561,12 @@ static int contained(const Common::Point &p, Polygon *polygon) {
return CONT_OUTSIDE;
}
+/**
+ * Computes polygon area
+ * Parameters: (Polygon *) polygon: The polygon
+ * Returns : (int) The area multiplied by two
+ */
static int polygon_area(Polygon *polygon) {
- // Computes polygon area
- // Parameters: (Polygon *) polygon: The polygon
- // Returns : (int) The area multiplied by two
Vertex *first = polygon->vertices.first();
Vertex *v;
int size = 0;
@@ -567,11 +581,13 @@ static int polygon_area(Polygon *polygon) {
return size;
}
+/**
+ * Fixes the vertex order of a polygon if incorrect. Contained access
+ * polygons should have their vertices ordered clockwise, all other types
+ * anti-clockwise
+ * Parameters: (Polygon *) polygon: The polygon
+ */
static void fix_vertex_order(Polygon *polygon) {
- // Fixes the vertex order of a polygon if incorrect. Contained access
- // polygons should have their vertices ordered clockwise, all other types
- // anti-clockwise
- // Parameters: (Polygon *) polygon: The polygon
int area = polygon_area(polygon);
// When the polygon area is positive the vertices are ordered
@@ -584,13 +600,15 @@ static void fix_vertex_order(Polygon *polygon) {
}
}
+/**
+ * Determines whether or not a line from a point to a vertex intersects the
+ * interior of the polygon, locally at that vertex
+ * Parameters: (Common::Point) p: The point
+ * (Vertex *) vertex: The vertex
+ * Returns : (int) 1 if the line (p, vertex->v) intersects the interior of
+ * the polygon, locally at the vertex. 0 otherwise
+ */
static int inside(const Common::Point &p, Vertex *vertex) {
- // Determines whether or not a line from a point to a vertex intersects the
- // interior of the polygon, locally at that vertex
- // Parameters: (Common::Point) p: The point
- // (Vertex *) vertex: The vertex
- // Returns : (int) 1 if the line (p, vertex->v) intersects the interior of
- // the polygon, locally at the vertex. 0 otherwise
// Check that it's not a single-vertex polygon
if (VERTEX_HAS_EDGES(vertex)) {
const Common::Point &prev = CLIST_PREV(vertex)->v;
@@ -655,28 +673,34 @@ static VertexList *visible_vertices(PathfindingState *s, Vertex *vertex_cur) {
return visVerts;
}
+/**
+ * Determines if a point lies on the screen border
+ * Parameters: (const Common::Point &) p: The point
+ * Returns : (int) true if p lies on the screen border, false otherwise
+ */
bool PathfindingState::pointOnScreenBorder(const Common::Point &p) {
- // Determines if a point lies on the screen border
- // Parameters: (const Common::Point &) p: The point
- // Returns : (int) true if p lies on the screen border, false otherwise
return (p.x == 0) || (p.x == _width - 1) || (p.y == 0) || (p.y == _height - 1);
}
+/**
+ * Determines if an edge lies on the screen border
+ * Parameters: (const Common::Point &) p, q: The edge (p, q)
+ * Returns : (int) true if (p, q) lies on the screen border, false otherwise
+ */
bool PathfindingState::edgeOnScreenBorder(const Common::Point &p, const Common::Point &q) {
- // Determines if an edge lies on the screen border
- // Parameters: (const Common::Point &) p, q: The edge (p, q)
- // Returns : (int) true if (p, q) lies on the screen border, false otherwise
return ((p.x == 0 && q.x == 0) || (p.y == 0 && q.y == 0)
|| ((p.x == _width - 1) && (q.x == _width - 1))
|| ((p.y == _height - 1) && (q.y == _height - 1)));
}
+/**
+ * Searches for a nearby point that is not contained in a polygon
+ * Parameters: (FloatPoint) f: The pointf to search nearby
+ * (Polygon *) polygon: The polygon
+ * Returns : (int) PF_OK on success, PF_FATAL otherwise
+ * (Common::Point) *ret: The non-contained point on success
+ */
static int find_free_point(FloatPoint f, Polygon *polygon, Common::Point *ret) {
- // Searches for a nearby point that is not contained in a polygon
- // Parameters: (FloatPoint) f: The pointf to search nearby
- // (Polygon *) polygon: The polygon
- // Returns : (int) PF_OK on success, PF_FATAL otherwise
- // (Common::Point) *ret: The non-contained point on success
Common::Point p;
// Try nearest point first
@@ -706,12 +730,14 @@ static int find_free_point(FloatPoint f, Polygon *polygon, Common::Point *ret) {
return PF_OK;
}
+/**
+ * Computes the near point of a point contained in a polygon
+ * Parameters: (const Common::Point &) p: The point
+ * (Polygon *) polygon: The polygon
+ * Returns : (int) PF_OK on success, PF_FATAL otherwise
+ * (Common::Point) *ret: The near point of p in polygon on success
+ */
int PathfindingState::findNearPoint(const Common::Point &p, Polygon *polygon, Common::Point *ret) {
- // Computes the near point of a point contained in a polygon
- // Parameters: (const Common::Point &) p: The point
- // (Polygon *) polygon: The polygon
- // Returns : (int) PF_OK on success, PF_FATAL otherwise
- // (Common::Point) *ret: The near point of p in polygon on success
Vertex *vertex;
FloatPoint near_p;
uint32 dist = HUGE_DISTANCE;
@@ -751,13 +777,15 @@ int PathfindingState::findNearPoint(const Common::Point &p, Polygon *polygon, Co
return find_free_point(near_p, polygon, ret);
}
+/**
+ * Computes the intersection point of a line segment and an edge (not
+ * including the vertices themselves)
+ * Parameters: (const Common::Point &) a, b: The line segment (a, b)
+ * (Vertex *) vertex: The first vertex of the edge
+ * Returns : (int) FP_OK on success, PF_ERROR otherwise
+ * (FloatPoint) *ret: The intersection point
+ */
static int intersection(const Common::Point &a, const Common::Point &b, Vertex *vertex, FloatPoint *ret) {
- // Computes the intersection point of a line segment and an edge (not
- // including the vertices themselves)
- // Parameters: (const Common::Point &) a, b: The line segment (a, b)
- // (Vertex *) vertex: The first vertex of the edge
- // Returns : (int) FP_OK on success, PF_ERROR otherwise
- // (FloatPoint) *ret: The intersection point
// Parameters of parametric equations
float s, t;
// Numerator and denominator of equations
@@ -790,16 +818,18 @@ static int intersection(const Common::Point &a, const Common::Point &b, Vertex *
return PF_ERROR;
}
+/**
+ * Computes the nearest intersection point of a line segment and the polygon
+ * set. Intersection points that are reached from the inside of a polygon
+ * are ignored as are improper intersections which do not obstruct
+ * visibility
+ * Parameters: (PathfindingState *) s: The pathfinding state
+ * (const Common::Point &) p, q: The line segment (p, q)
+ * Returns : (int) PF_OK on success, PF_ERROR when no intersections were
+ * found, PF_FATAL otherwise
+ * (Common::Point) *ret: On success, the closest intersection point
+ */
static int nearest_intersection(PathfindingState *s, const Common::Point &p, const Common::Point &q, Common::Point *ret) {
- // Computes the nearest intersection point of a line segment and the polygon
- // set. Intersection points that are reached from the inside of a polygon
- // are ignored as are improper intersections which do not obstruct
- // visibility
- // Parameters: (PathfindingState *) s: The pathfinding state
- // (const Common::Point &) p, q: The line segment (p, q)
- // Returns : (int) PF_OK on success, PF_ERROR when no intersections were
- // found, PF_FATAL otherwise
- // (Common::Point) *ret: On success, the closest intersection point
Polygon *polygon = 0;
FloatPoint isec;
Polygon *ipolygon = 0;
@@ -981,14 +1011,16 @@ static Common::Point *fixup_end_point(PathfindingState *s, const Common::Point &
return new_end;
}
+/**
+ * Merges a point into the polygon set. A new vertex is allocated for this
+ * point, unless a matching vertex already exists. If the point is on an
+ * already existing edge that edge is split up into two edges connected by
+ * the new vertex
+ * Parameters: (PathfindingState *) s: The pathfinding state
+ * (const Common::Point &) v: The point to merge
+ * Returns : (Vertex *) The vertex corresponding to v
+ */
static Vertex *merge_point(PathfindingState *s, const Common::Point &v) {
- // Merges a point into the polygon set. A new vertex is allocated for this
- // point, unless a matching vertex already exists. If the point is on an
- // already existing edge that edge is split up into two edges connected by
- // the new vertex
- // Parameters: (PathfindingState *) s: The pathfinding state
- // (const Common::Point &) v: The point to merge
- // Returns : (Vertex *) The vertex corresponding to v
Vertex *vertex;
Vertex *v_new;
Polygon *polygon;
@@ -1029,11 +1061,13 @@ static Vertex *merge_point(PathfindingState *s, const Common::Point &v) {
return v_new;
}
+/**
+ * Converts an SCI polygon into a Polygon
+ * Parameters: (EngineState *) s: The game state
+ * (reg_t) polygon: The SCI polygon to convert
+ * Returns : (Polygon *) The converted polygon, or NULL on error
+ */
static Polygon *convert_polygon(EngineState *s, reg_t polygon) {
- // Converts an SCI polygon into a Polygon
- // Parameters: (EngineState *) s: The game state
- // (reg_t) polygon: The SCI polygon to convert
- // Returns : (Polygon *) The converted polygon, or NULL on error
SegManager *segMan = s->_segMan;
int i;
reg_t points = readSelector(segMan, polygon, SELECTOR(points));
@@ -1056,7 +1090,7 @@ static Polygon *convert_polygon(EngineState *s, reg_t polygon) {
// WORKAROUND: broken polygon in lsl1sci, room 350, after opening elevator
// Polygon has 17 points but size is set to 19
- if ((size == 19) && !strcmp(g_sci->getGameID(), "lsl1sci")) {
+ if ((size == 19) && g_sci->getGameId() == GID_LSL1) {
if ((s->currentRoomNumber() == 350)
&& (read_point(segMan, points, 18) == Common::Point(108, 137))) {
debug(1, "Applying fix for broken polygon in lsl1sci, room 350");
@@ -1074,11 +1108,13 @@ static Polygon *convert_polygon(EngineState *s, reg_t polygon) {
return poly;
}
+/**
+ * Changes the polygon list for optimization level 0 (used for keyboard
+ * support). Totally accessible polygons are removed and near-point
+ * accessible polygons are changed into totally accessible polygons.
+ * Parameters: (PathfindingState *) s: The pathfinding state
+ */
static void change_polygons_opt_0(PathfindingState *s) {
- // Changes the polygon list for optimization level 0 (used for keyboard
- // support). Totally accessible polygons are removed and near-point
- // accessible polygons are changed into totally accessible polygons.
- // Parameters: (PathfindingState *) s: The pathfinding state
PolygonList::iterator it = s->polygons.begin();
while (it != s->polygons.end()) {
@@ -1096,15 +1132,17 @@ static void change_polygons_opt_0(PathfindingState *s) {
}
}
+/**
+ * Converts the SCI input data for pathfinding
+ * Parameters: (EngineState *) s: The game state
+ * (reg_t) poly_list: Polygon list
+ * (Common::Point) start: The start point
+ * (Common::Point) end: The end point
+ * (int) opt: Optimization level (0, 1 or 2)
+ * Returns : (PathfindingState *) On success a newly allocated pathfinding state,
+ * NULL otherwise
+ */
static PathfindingState *convert_polygon_set(EngineState *s, reg_t poly_list, Common::Point start, Common::Point end, int width, int height, int opt) {
- // Converts the SCI input data for pathfinding
- // Parameters: (EngineState *) s: The game state
- // (reg_t) poly_list: Polygon list
- // (Common::Point) start: The start point
- // (Common::Point) end: The end point
- // (int) opt: Optimization level (0, 1 or 2)
- // Returns : (PathfindingState *) On success a newly allocated pathfinding state,
- // NULL otherwise
SegManager *segMan = s->_segMan;
Polygon *polygon;
int err;
@@ -1174,7 +1212,7 @@ static PathfindingState *convert_polygon_set(EngineState *s, reg_t poly_list, Co
// WORKAROUND LSL5 room 660. Priority glitch due to us choosing a different path
// than SSCI. Happens when Patti walks to the control room.
- if (!strcmp(g_sci->getGameID(), "lsl5") && (s->currentRoomNumber() == 660) && (Common::Point(67, 131) == *new_start) && (Common::Point(229, 101) == *new_end)) {
+ if (g_sci->getGameId() == GID_LSL5 && (s->currentRoomNumber() == 660) && (Common::Point(67, 131) == *new_start) && (Common::Point(229, 101) == *new_end)) {
debug(1, "[avoidpath] Applying fix for priority problem in LSL5, room 660");
pf_s->_prependPoint = new_start;
new_start = new Common::Point(77, 107);
@@ -1297,11 +1335,13 @@ static reg_t allocateOutputArray(SegManager *segMan, int size) {
return addr;
}
+/**
+ * Stores the final path in newly allocated dynmem
+ * Parameters: (PathfindingState *) p: The pathfinding state
+ * (EngineState *) s: The game state
+ * Returns : (reg_t) Pointer to dynmem containing path
+ */
static reg_t output_path(PathfindingState *p, EngineState *s) {
- // Stores the final path in newly allocated dynmem
- // Parameters: (PathfindingState *) p: The pathfinding state
- // (EngineState *) s: The game state
- // Returns : (reg_t) Pointer to dynmem containing path
int path_len = 0;
reg_t output;
Vertex *vertex = p->vertex_end;
@@ -1694,17 +1734,14 @@ 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.
+/**
+ * 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) {
+#if 0
// 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
@@ -1723,9 +1760,14 @@ reg_t kMergePoly(EngineState *s, int argc, reg_t *argv) {
}
#endif
+ // TODO: actually merge the polygon. We return an empty polygon for now.
+ // In QFG1VGA, you can walk over enemy bodies after killing them, since
+ // this is a stub.
+ reg_t output = allocateOutputArray(s->_segMan, 1);
+ SegmentRef arrayRef = s->_segMan->dereference(output);
+ writePoint(arrayRef, 0, Common::Point(POLY_LAST_POINT, POLY_LAST_POINT));
warning("Stub: kMergePoly");
-
- return polygonData;
+ return output;
}
} // End of namespace Sci
diff --git a/engines/sci/engine/kscripts.cpp b/engines/sci/engine/kscripts.cpp
index 722d0175d1..029943b070 100644
--- a/engines/sci/engine/kscripts.cpp
+++ b/engines/sci/engine/kscripts.cpp
@@ -25,16 +25,20 @@
#include "sci/sci.h"
#include "sci/resource.h"
+#include "sci/engine/seg_manager.h"
+#include "sci/engine/script.h"
#include "sci/engine/state.h"
#include "sci/engine/selector.h"
#include "sci/engine/kernel.h"
+#include "common/file.h"
+
namespace Sci {
// Loads arbitrary resources of type 'restype' with resource numbers 'resnrs'
// This implementation ignores all resource numbers except the first one.
reg_t kLoad(EngineState *s, int argc, reg_t *argv) {
- int restype = argv[0].toUint16();
+ ResourceType restype = (ResourceType)(argv[0].toUint16() & 0x7f);
int resnr = argv[1].toUint16();
// Request to dynamically allocate hunk memory for later use
@@ -44,6 +48,29 @@ reg_t kLoad(EngineState *s, int argc, reg_t *argv) {
return make_reg(0, ((restype << 11) | resnr)); // Return the resource identifier as handle
}
+// Unloads an arbitrary resource of type 'restype' with resource numbber 'resnr'
+// behaviour of this call didn't change between sci0->sci1.1 parameter wise, which means getting called with
+// 1 or 3+ parameters is not right according to sierra sci
+reg_t kUnLoad(EngineState *s, int argc, reg_t *argv) {
+ if (argc >= 2) {
+ ResourceType restype = (ResourceType)(argv[0].toUint16() & 0x7f);
+ reg_t resnr = argv[1];
+
+ // WORKAROUND for a broken script in room 320 in Castle of Dr. Brain.
+ // Script 377 tries to free the hunk memory allocated for the saved area
+ // (underbits) beneath the pop up window, which results in having the
+ // window stay on screen even when it's closed. Ignore this request here.
+ if (restype == kResourceTypeMemory && g_sci->getGameId() == GID_CASTLEBRAIN &&
+ s->currentRoomNumber() == 320)
+ return s->r_acc;
+
+ if (restype == kResourceTypeMemory)
+ s->_segMan->freeHunkEntry(resnr);
+ }
+
+ return s->r_acc;
+}
+
reg_t kLock(EngineState *s, int argc, reg_t *argv) {
int state = argc > 2 ? argv[2].toUint16() : 1;
ResourceType type = (ResourceType)(argv[0].toUint16() & 0x7f);
@@ -56,43 +83,46 @@ reg_t kLock(EngineState *s, int argc, reg_t *argv) {
g_sci->getResMan()->findResource(id, 1);
break;
case 0 :
- which = g_sci->getResMan()->findResource(id, 0);
-
- if (which)
- g_sci->getResMan()->unlockResource(which);
- else {
- if (id.type == kResourceTypeInvalid)
- warning("[resMan] Attempt to unlock resource %i of invalid type %i", id.number, type);
- else
- warning("[resMan] Attempt to unlock non-existant resource %s", id.toString().c_str());
+ if (id.getNumber() == 0xFFFF) {
+ // Unlock all resources of the requested type
+ Common::List<ResourceId> *resources = g_sci->getResMan()->listResources(type);
+ Common::List<ResourceId>::iterator itr = resources->begin();
+
+ while (itr != resources->end()) {
+ Resource *res = g_sci->getResMan()->testResource(*itr);
+ if (res->isLocked())
+ g_sci->getResMan()->unlockResource(res);
+ ++itr;
+ }
+
+ } else {
+ which = g_sci->getResMan()->findResource(id, 0);
+
+ if (which)
+ g_sci->getResMan()->unlockResource(which);
+ else {
+ if (id.getType() == kResourceTypeInvalid)
+ warning("[resMan] Attempt to unlock resource %i of invalid type %i", id.getNumber(), type);
+ else
+ // Happens in CD games (e.g. LSL6CD) with the message resource
+ warning("[resMan] Attempt to unlock non-existant resource %s", id.toString().c_str());
+ }
}
break;
}
return s->r_acc;
}
-// Unloads an arbitrary resource of type 'restype' with resource numbber 'resnr'
-reg_t kUnLoad(EngineState *s, int argc, reg_t *argv) {
- if (argc >= 2) {
- int restype = argv[0].toUint16();
- reg_t resnr = argv[1];
-
- if (restype == kResourceTypeMemory)
- s->_segMan->freeHunkEntry(resnr);
-
- if (argc > 2)
- warning("kUnload called with more than 2 parameters (%d)", argc);
- } else {
- warning("kUnload called with less than 2 parameters (%d) - ignoring", argc);
- }
-
- return s->r_acc;
-}
-
reg_t kResCheck(EngineState *s, int argc, reg_t *argv) {
Resource *res = NULL;
ResourceType restype = (ResourceType)(argv[0].toUint16() & 0x7f);
+ if (restype == kResourceTypeVMD) {
+ char fileName[10];
+ sprintf(fileName, "%d.vmd", argv[1].toUint16());
+ return make_reg(0, Common::File::exists(fileName));
+ }
+
if ((restype == kResourceTypeAudio36) || (restype == kResourceTypeSync36)) {
if (argc >= 6) {
uint noun = argv[2].toUint16() & 0xff;
@@ -155,24 +185,10 @@ reg_t kDisposeClone(EngineState *s, int argc, reg_t *argv) {
}
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 (readSelectorValue(victim_addr, underBits))
- // warning("Clone %04x:%04x was cleared with underBits set", PRINT_REG(victim_addr));
-
-#if 0
- if (s->dyn_views) { // Free any widget associated with the clone
- GfxWidget *widget = gfxw_set_id(gfxw_remove_ID(s->dyn_views, offset), GFXW_NO_ID);
-
- if (widget && s->bg_widgets)
- s->bg_widgets->add(GFXWC(s->bg_widgets), widget);
- }
-#endif
-
victim_obj->markAsFreed();
return s->r_acc;
@@ -198,9 +214,10 @@ reg_t kScriptID(EngineState *s, int argc, reg_t *argv) {
// 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
+ // as no return value is expected. If an export is requested, then
+ // it will most certainly fail with OOB access.
if (argc == 2)
- warning("Script 0x%x does not have a dispatch table and export %d "
+ error("Script 0x%x does not have a dispatch table and export %d "
"was requested from it", script, index);
return NULL_REG;
}
@@ -222,10 +239,6 @@ reg_t kScriptID(EngineState *s, int argc, reg_t *argv) {
reg_t kDisposeScript(EngineState *s, int argc, reg_t *argv) {
int script = argv[0].offset;
- // Work around QfG1 graveyard bug
- if (argv[0].segment)
- return s->r_acc;
-
SegmentId id = s->_segMan->getScriptSegment(script);
Script *scr = s->_segMan->getScriptIfLoaded(id);
if (scr) {
@@ -233,13 +246,13 @@ reg_t kDisposeScript(EngineState *s, int argc, reg_t *argv) {
scr->setLockers(1);
}
- script_uninstantiate(s->_segMan, script);
+ s->_segMan->uninstantiateScript(script);
if (argc != 2) {
return s->r_acc;
} else {
- // This exists in the KQ5CD and GK1 interpreter. We know it is used when GK1 starts
- // up, before the Sierra logo.
+ // This exists in the KQ5CD and GK1 interpreter. We know it is used
+ // when GK1 starts up, before the Sierra logo.
warning("kDisposeScript called with 2 parameters, still untested");
return argv[1];
}
diff --git a/engines/sci/engine/ksound.cpp b/engines/sci/engine/ksound.cpp
index 367a89005c..69ae68674b 100644
--- a/engines/sci/engine/ksound.cpp
+++ b/engines/sci/engine/ksound.cpp
@@ -39,12 +39,38 @@ namespace Sci {
* Used for synthesized music playback
*/
reg_t kDoSound(EngineState *s, int argc, reg_t *argv) {
- return s->_soundCmd->parseCommand(argc, argv, s->r_acc);
+ if (!s)
+ return make_reg(0, g_sci->_features->detectDoSoundType());
+ error("not supposed to call this");
}
+#define CREATE_DOSOUND_FORWARD(_name_) reg_t k##_name_(EngineState *s, int argc, reg_t *argv) { return g_sci->_soundCmd->k##_name_(argc, argv, s->r_acc); }
+
+CREATE_DOSOUND_FORWARD(DoSoundInit)
+CREATE_DOSOUND_FORWARD(DoSoundPlay)
+CREATE_DOSOUND_FORWARD(DoSoundDummy)
+CREATE_DOSOUND_FORWARD(DoSoundDispose)
+CREATE_DOSOUND_FORWARD(DoSoundMute)
+CREATE_DOSOUND_FORWARD(DoSoundStop)
+CREATE_DOSOUND_FORWARD(DoSoundStopAll)
+CREATE_DOSOUND_FORWARD(DoSoundPause)
+CREATE_DOSOUND_FORWARD(DoSoundResume)
+CREATE_DOSOUND_FORWARD(DoSoundMasterVolume)
+CREATE_DOSOUND_FORWARD(DoSoundUpdate)
+CREATE_DOSOUND_FORWARD(DoSoundFade)
+CREATE_DOSOUND_FORWARD(DoSoundGetPolyphony)
+CREATE_DOSOUND_FORWARD(DoSoundUpdateCues)
+CREATE_DOSOUND_FORWARD(DoSoundSendMidi)
+CREATE_DOSOUND_FORWARD(DoSoundReverb)
+CREATE_DOSOUND_FORWARD(DoSoundSetHold)
+CREATE_DOSOUND_FORWARD(DoSoundGetAudioCapability)
+CREATE_DOSOUND_FORWARD(DoSoundSuspend)
+CREATE_DOSOUND_FORWARD(DoSoundSetVolume)
+CREATE_DOSOUND_FORWARD(DoSoundSetPriority)
+CREATE_DOSOUND_FORWARD(DoSoundSetLoop)
+
reg_t kDoCdAudio(EngineState *s, int argc, reg_t *argv) {
switch (argv[0].toUint16()) {
- case kSciAudioWPlay:
case kSciAudioPlay: {
if (argc < 2)
return NULL_REG;
@@ -72,6 +98,7 @@ reg_t kDoCdAudio(EngineState *s, int argc, reg_t *argv) {
break;
case kSciAudioPosition:
return make_reg(0, g_sci->_audio->audioCdPosition());
+ case kSciAudioWPlay: // CD Audio can't be preloaded
case kSciAudioRate: // No need to set the audio rate
case kSciAudioVolume: // The speech setting isn't used by CD Audio
case kSciAudioLanguage: // No need to set the language
@@ -80,7 +107,7 @@ reg_t kDoCdAudio(EngineState *s, int argc, reg_t *argv) {
// Init
return make_reg(0, 1);
default:
- warning("kCdDoAudio: Unhandled case %d", argv[0].toUint16());
+ error("kCdDoAudio: Unhandled case %d", argv[0].toUint16());
}
return s->r_acc;
@@ -110,8 +137,10 @@ reg_t kDoAudio(EngineState *s, int argc, reg_t *argv) {
number = argv[1].toUint16();
} else if (argc == 6 || argc == 8) {
module = argv[1].toUint16();
- number = ((argv[2].toUint16() & 0xff) << 24) | ((argv[3].toUint16() & 0xff) << 16) |
- ((argv[4].toUint16() & 0xff) << 8) | (argv[5].toUint16() & 0xff);
+ number = ((argv[2].toUint16() & 0xff) << 24) |
+ ((argv[3].toUint16() & 0xff) << 16) |
+ ((argv[4].toUint16() & 0xff) << 8) |
+ (argv[5].toUint16() & 0xff);
if (argc == 8)
warning("kDoAudio: Play called with SQ6 extra parameters");
} else {
@@ -119,46 +148,91 @@ reg_t kDoAudio(EngineState *s, int argc, reg_t *argv) {
return NULL_REG;
}
- return make_reg(0, g_sci->_audio->startAudio(module, number)); // return sample length in ticks
+ debugC(2, kDebugLevelSound, "kDoAudio: play sample %d, module %d", number, module);
+
+ // return sample length in ticks
+ if (argv[0].toUint16() == kSciAudioWPlay)
+ return make_reg(0, g_sci->_audio->wPlayAudio(module, number));
+ else
+ return make_reg(0, g_sci->_audio->startAudio(module, number));
}
case kSciAudioStop:
+ debugC(2, kDebugLevelSound, "kDoAudio: stop");
g_sci->_audio->stopAudio();
break;
case kSciAudioPause:
+ debugC(2, kDebugLevelSound, "kDoAudio: pause");
g_sci->_audio->pauseAudio();
break;
case kSciAudioResume:
+ debugC(2, kDebugLevelSound, "kDoAudio: resume");
g_sci->_audio->resumeAudio();
break;
case kSciAudioPosition:
+ //debugC(2, kDebugLevelSound, "kDoAudio: get position"); // too verbose
return make_reg(0, g_sci->_audio->getAudioPosition());
case kSciAudioRate:
+ debugC(2, kDebugLevelSound, "kDoAudio: set audio rate to %d", argv[1].toUint16());
g_sci->_audio->setAudioRate(argv[1].toUint16());
break;
case kSciAudioVolume: {
int16 volume = argv[1].toUint16();
volume = CLIP<int16>(volume, 0, AUDIO_VOLUME_MAX);
+ debugC(2, kDebugLevelSound, "kDoAudio: set volume to %d", volume);
+#ifdef ENABLE_SCI32
+ if (getSciVersion() >= SCI_VERSION_2_1) {
+ int16 volumePrev = mixer->getVolumeForSoundType(Audio::Mixer::kSpeechSoundType) / 2;
+ volumePrev = CLIP<int16>(volumePrev, 0, AUDIO_VOLUME_MAX);
+ mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, volume * 2);
+ return make_reg(0, volumePrev);
+ } else
+#endif
mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, volume * 2);
- break;
}
case kSciAudioLanguage:
// In SCI1.1: tests for digital audio support
- if (getSciVersion() == SCI_VERSION_1_1)
+ if (getSciVersion() == SCI_VERSION_1_1) {
+ debugC(2, kDebugLevelSound, "kDoAudio: audio capability test");
return make_reg(0, 1);
- else {
+ } else {
int16 language = argv[1].toSint16();
+ debugC(2, kDebugLevelSound, "kDoAudio: set language to %d", language);
if (language != -1)
g_sci->getResMan()->setAudioLanguage(language);
- return make_reg(0, g_sci->getSciLanguage());
+ kLanguage kLang = g_sci->getSciLanguage();
+ g_sci->setSciLanguage(kLang);
+
+ return make_reg(0, kLang);
}
break;
case kSciAudioCD:
- return kDoCdAudio(s, argc - 1, argv + 1);
- // TODO: There are 3 more functions used in Freddy Pharkas (11, 12 and 13) and new within sierra sci
- // Details currently unknown
- // kDoAudio sits at seg026:038C
+
+ if (getSciVersion() <= SCI_VERSION_1_1) {
+ debugC(2, kDebugLevelSound, "kDoAudio: CD audio subop");
+ return kDoCdAudio(s, argc - 1, argv + 1);
+#ifdef ENABLE_SCI32
+ } else {
+ // TODO: This isn't CD Audio in SCI32 anymore
+ warning("kDoAudio: Unhandled case 10, %d extra arguments passed", argc - 1);
+ break;
+#endif
+ }
+
+ // 3 new subops in Pharkas. kDoAudio in Pharkas sits at seg026:038C
+ case 11:
+ warning("kDoAudio: Unhandled case 11, %d extra arguments passed", argc - 1);
+ break;
+ case 12:
+ // Seems to be audio sync, used in Pharkas. Silenced the warning due to
+ // the high level of spam it produces.
+ //warning("kDoAudio: Unhandled case 12, %d extra arguments passed", argc - 1);
+ break;
+ case 13:
+ // Used in Pharkas whenever a speech sample starts
+ warning("kDoAudio: Unhandled case 13, %d extra arguments passed", argc - 1);
+ break;
default:
warning("kDoAudio: Unhandled case %d, %d extra arguments passed", argv[0].toUint16(), argc - 1);
}
@@ -195,7 +269,7 @@ reg_t kDoSync(EngineState *s, int argc, reg_t *argv) {
g_sci->_audio->stopSoundSync();
break;
default:
- warning("DoSync: Unhandled subfunction %d", argv[0].toUint16());
+ error("DoSync: Unhandled subfunction %d", argv[0].toUint16());
}
return s->r_acc;
diff --git a/engines/sci/engine/kstring.cpp b/engines/sci/engine/kstring.cpp
index 2681b612e9..f2f9543ad2 100644
--- a/engines/sci/engine/kstring.cpp
+++ b/engines/sci/engine/kstring.cpp
@@ -115,12 +115,14 @@ reg_t kStrAt(EngineState *s, int argc, reg_t *argv) {
if (argc > 2) { /* Request to modify this char */
tmp.offset &= 0xff00;
tmp.offset |= newvalue;
+ tmp.segment = 0;
}
} else {
value = tmp.offset >> 8;
if (argc > 2) { /* Request to modify this char */
tmp.offset &= 0x00ff;
tmp.offset |= newvalue << 8;
+ tmp.segment = 0;
}
}
}
@@ -141,19 +143,22 @@ reg_t kReadNumber(EngineState *s, int argc, reg_t *argv) {
int16 result = 0;
if (*source == '$') {
- // hexadecimal input
+ // 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!
+ // Decimal input. We can not use strtol/atoi in here, because while
+ // Sierra used atoi, it was a non standard compliant atoi, that didn't
+ // 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)
+ // Sierra's atoi stopped processing at anything which is not
+ // a digit. Sometimes the input has a trailing space, that's
+ // fine (example: lsl3)
if (*source != ' ') {
// TODO: this happens in lsl5 right in the intro -> we get '1' '3' 0xCD 0xCD 0xCD 0xCD 0xCD
// find out why this happens and fix it
@@ -423,15 +428,16 @@ reg_t kGetFarText(EngineState *s, int argc, reg_t *argv) {
seeker = (char *)textres->data;
- // The second parameter (counter) determines the number of the string inside the text
- // resource.
+ // The second parameter (counter) determines the number of the string
+ // inside the text resource.
while (counter--) {
while (*seeker++)
;
}
- // If the third argument is NULL, allocate memory for the destination. This occurs in
- // SCI1 Mac games. The memory will later be freed by the game's scripts.
+ // If the third argument is NULL, allocate memory for the destination. This
+ // occurs in SCI1 Mac games. The memory will later be freed by the game's
+ // scripts.
if (argv[2] == NULL_REG)
s->_segMan->allocDynmem(strlen(seeker) + 1, "Mac FarText", &argv[2]);
@@ -561,7 +567,7 @@ reg_t kMessage(EngineState *s, int argc, reg_t *argv) {
reg_t kSetQuitStr(EngineState *s, int argc, reg_t *argv) {
Common::String quitStr = s->_segMan->getString(argv[0]);
- debug("Setting quit string to '%s'", quitStr.c_str());
+ //debug("Setting quit string to '%s'", quitStr.c_str());
return s->r_acc;
}
@@ -578,7 +584,8 @@ reg_t kStrSplit(EngineState *s, int argc, reg_t *argv) {
// Make sure target buffer is large enough
SegmentRef buf_r = s->_segMan->dereference(argv[0]);
if (!buf_r.isValid() || buf_r.maxSize < (int)str.size() + 1) {
- warning("StrSplit: buffer %04x:%04x invalid or too small to hold the following text of %i bytes: '%s'", PRINT_REG(argv[0]), str.size() + 1, str.c_str());
+ warning("StrSplit: buffer %04x:%04x invalid or too small to hold the following text of %i bytes: '%s'",
+ PRINT_REG(argv[0]), str.size() + 1, str.c_str());
return NULL_REG;
}
s->_segMan->strcpy(argv[0], str.c_str());
diff --git a/engines/sci/engine/message.cpp b/engines/sci/engine/message.cpp
index 07f8792471..18e60eb521 100644
--- a/engines/sci/engine/message.cpp
+++ b/engines/sci/engine/message.cpp
@@ -161,7 +161,7 @@ bool MessageState::getRecord(CursorStack &stack, bool recurse, MessageRecord &re
reader = new MessageReaderV4(res->data, res->size);
break;
default:
- warning("Message: unsupported resource version %d", version);
+ error("Message: unsupported resource version %d", version);
return false;
}
@@ -259,7 +259,7 @@ void MessageState::popCursorStack() {
if (!_cursorStackStack.empty())
_cursorStack = _cursorStackStack.pop();
else
- warning("Message: attempt to pop from empty stack");
+ error("Message: attempt to pop from empty stack");
}
int MessageState::hexDigitToInt(char h) {
@@ -330,7 +330,8 @@ bool MessageState::stringStage(Common::String &outstr, const Common::String &inS
}
// If we find a lowercase character or a digit, it's not a stage direction
- if (((inStr[i] >= 'a') && (inStr[i] <= 'z')) || ((inStr[i] >= '0') && (inStr[i] <= '9')))
+ // SCI32 seems to support having digits in stage directions
+ if (((inStr[i] >= 'a') && (inStr[i] <= 'z')) || ((inStr[i] >= '0') && (inStr[i] <= '9') && (getSciVersion() < SCI_VERSION_2)))
return false;
}
diff --git a/engines/sci/engine/savegame.cpp b/engines/sci/engine/savegame.cpp
index 9bf23dedf5..0fe5f2088a 100644
--- a/engines/sci/engine/savegame.cpp
+++ b/engines/sci/engine/savegame.cpp
@@ -33,24 +33,18 @@
#include "sci/event.h"
#include "sci/engine/features.h"
+#include "sci/engine/kernel.h"
#include "sci/engine/state.h"
#include "sci/engine/message.h"
#include "sci/engine/savegame.h"
+#include "sci/engine/selector.h"
#include "sci/engine/vm_types.h"
#include "sci/engine/script.h" // for SCI_OBJ_EXPORTS and SCI_OBJ_SYNONYMS
-#include "sci/graphics/gui.h"
#include "sci/graphics/ports.h"
#include "sci/sound/audio.h"
-#ifdef USE_OLD_MUSIC_FUNCTIONS
-#include "sci/sound/iterator/core.h"
-#include "sci/sound/iterator/iterator.h"
-#else
#include "sci/sound/music.h"
-#endif
-#ifdef ENABLE_SCI32
-#include "sci/graphics/gui32.h"
-#endif
+#include "gui/message.h"
namespace Sci {
@@ -62,47 +56,11 @@ namespace Sci {
const uint32 INTMAPPER_MAGIC_KEY = 0xDEADBEEF;
-#ifdef USE_OLD_MUSIC_FUNCTIONS
-// from ksound.cpp:
-SongIterator *build_iterator(ResourceManager *resMan, int song_nr, SongIteratorType type, songit_id_t id);
-#endif
-
-
#pragma mark -
// TODO: Many of the following sync_*() methods should be turned into member funcs
// of the classes they are syncing.
-#ifdef USE_OLD_MUSIC_FUNCTIONS
-static void sync_songlib(Common::Serializer &s, SongLibrary &obj);
-#endif
-
-static void sync_reg_t(Common::Serializer &s, reg_t &obj) {
- s.syncAsUint16LE(obj.segment);
- s.syncAsUint16LE(obj.offset);
-}
-
-#ifdef USE_OLD_MUSIC_FUNCTIONS
-static void syncSong(Common::Serializer &s, Song &obj) {
- s.syncAsSint32LE(obj._handle);
- s.syncAsSint32LE(obj._resourceNum);
- s.syncAsSint32LE(obj._priority);
- s.syncAsSint32LE(obj._status);
- s.syncAsSint32LE(obj._restoreBehavior);
- s.syncAsSint32LE(obj._restoreTime);
- s.syncAsSint32LE(obj._loops);
- s.syncAsSint32LE(obj._hold);
-
- if (s.isLoading()) {
- obj._it = 0;
- obj._delay = 0;
- obj._next = 0;
- obj._nextPlaying = 0;
- obj._nextStopping = 0;
- }
-}
-#else
-
#define DEFROBNICATE_HANDLE(handle) (make_reg((handle >> 16) & 0xffff, handle & 0xffff))
void MusicEntry::saveLoadWithSerializer(Common::Serializer &s) {
@@ -132,7 +90,7 @@ void MusicEntry::saveLoadWithSerializer(Common::Serializer &s) {
fadeTickerStep = 0;
} else {
// A bit more optimized saving
- sync_reg_t(s, soundObj);
+ soundObj.saveLoadWithSerializer(s);
s.syncAsSint16LE(resourceId);
s.syncAsSint16LE(dataInc);
s.syncAsSint16LE(ticker);
@@ -156,7 +114,6 @@ void MusicEntry::saveLoadWithSerializer(Common::Serializer &s) {
pStreamAud = 0;
}
}
-#endif
// Experimental hack: Use syncWithSerializer to sync. By default, this assume
// the object to be synced is a subclass of Serializable and thus tries to invoke
@@ -217,31 +174,18 @@ void syncArray(Common::Serializer &s, Common::Array<T> &arr) {
template <>
void syncWithSerializer(Common::Serializer &s, reg_t &obj) {
- sync_reg_t(s, obj);
+ obj.saveLoadWithSerializer(s);
}
void SegManager::saveLoadWithSerializer(Common::Serializer &s) {
- s.skip(4, VER(9), VER(9)); // OBSOLETE: Used to be reserved_id
- s.skip(4, VER(9), VER(18)); // OBSOLETE: Used to be _exportsAreWide
- s.skip(4, VER(9), VER(9)); // OBSOLETE: Used to be gc_mark_bits
+ if (s.isLoading())
+ resetSegMan();
+
+ s.skip(4, VER(12), VER(18)); // OBSOLETE: Used to be _exportsAreWide
if (s.isLoading()) {
// Reset _scriptSegMap, to be restored below
_scriptSegMap.clear();
-
- if (s.getVersion() <= 9) {
- // OBSOLETE: Skip over the old id_seg_map when loading (we now
- // regenerate the equivalent data, in _scriptSegMap, from scratch).
-
- s.skip(4); // base_value
- while (true) {
- uint32 key = 0;
- s.syncAsSint32LE(key);
- if (key == INTMAPPER_MAGIC_KEY)
- break;
- s.skip(4); // idx
- }
- }
}
@@ -257,57 +201,38 @@ void SegManager::saveLoadWithSerializer(Common::Serializer &s) {
// If we were saving and mobj == 0, or if we are loading and this is an
// entry marked as empty -> skip to next
- if (type == SEG_TYPE_INVALID) {
+ if (type == SEG_TYPE_INVALID)
continue;
- }
-
- s.skip(4, VER(9), VER(9)); // OBSOLETE: Used to be _segManagerId
// Don't save or load HunkTable segments
- if (type == SEG_TYPE_HUNK) {
+ if (type == SEG_TYPE_HUNK)
continue;
- }
-
- // Handle the OBSOLETE type SEG_TYPE_STRING_FRAG -- just ignore it
- if (s.isLoading() && type == SEG_TYPE_STRING_FRAG) {
- continue;
- }
-
- if (s.isLoading()) {
+ if (s.isLoading())
mobj = SegmentObj::createSegmentObj(type);
- }
+
assert(mobj);
// Let the object sync custom data
mobj->saveLoadWithSerializer(s);
// If we are loading a script, hook it up in the script->segment map.
- if (s.isLoading() && type == SEG_TYPE_SCRIPT) {
- _scriptSegMap[((Script *)mobj)->_nr] = i;
- }
+ if (s.isLoading() && type == SEG_TYPE_SCRIPT)
+ _scriptSegMap[((Script *)mobj)->getScriptNumber()] = i;
}
- s.syncAsSint32LE(Clones_seg_id);
- s.syncAsSint32LE(Lists_seg_id);
- s.syncAsSint32LE(Nodes_seg_id);
-}
-
-static void sync_SegManagerPtr(Common::Serializer &s, SegManager *&obj) {
- s.skip(1, VER(9), VER(9)); // obsolete: used to be a flag indicating if we got sci11 or not
-
- if (s.isLoading())
- obj->resetSegMan();
+ s.syncAsSint32LE(_clonesSegId);
+ s.syncAsSint32LE(_listsSegId);
+ s.syncAsSint32LE(_nodesSegId);
- obj->saveLoadWithSerializer(s);
+ syncArray<Class>(s, _classTable);
}
-
template <>
void syncWithSerializer(Common::Serializer &s, Class &obj) {
s.syncAsSint32LE(obj.script);
- sync_reg_t(s, obj.reg);
+ obj.reg.saveLoadWithSerializer(s);
}
static void sync_SavegameMetadata(Common::Serializer &s, SavegameMetadata &obj) {
@@ -318,17 +243,20 @@ static void sync_SavegameMetadata(Common::Serializer &s, SavegameMetadata &obj)
s.syncVersion(CURRENT_SAVEGAME_VERSION);
obj.savegame_version = s.getVersion();
s.syncString(obj.game_version);
- s.skip(4, VER(9), VER(9)); // obsolete: used to be game version
s.syncAsSint32LE(obj.savegame_date);
s.syncAsSint32LE(obj.savegame_time);
+ if (s.getVersion() < 22) {
+ obj.game_object_offset = 0;
+ obj.script0_size = 0;
+ } else {
+ s.syncAsUint16LE(obj.game_object_offset);
+ s.syncAsUint16LE(obj.script0_size);
+ }
}
void EngineState::saveLoadWithSerializer(Common::Serializer &s) {
- s.skip(4, VER(9), VER(9)); // OBSOLETE: Used to be savegame_version
-
Common::String tmp;
- s.syncString(tmp); // OBSOLETE: Used to be game_version
- s.skip(4, VER(9), VER(9)); // OBSOLETE: Used to be version
+ s.syncString(tmp, VER(12), VER(23)); // OBSOLETE: Used to be game_version
// OBSOLETE: Saved menus. Skip all of the saved data
if (s.getVersion() < 14) {
@@ -347,7 +275,6 @@ void EngineState::saveLoadWithSerializer(Common::Serializer &s) {
for (int j = 0; j < menuLength; j++) {
s.skip(4, VER(12), VER(12)); // OBSOLETE: Used to be _type
s.syncString(tmp); // OBSOLETE: Used to be _keytext
- s.skip(4, VER(9), VER(9)); // OBSOLETE: Used to be keytext_size
s.skip(4, VER(12), VER(12)); // OBSOLETE: Used to be _flags
s.skip(64, VER(12), VER(12)); // OBSOLETE: Used to be MENU_SAID_SPEC_SIZE
@@ -362,16 +289,14 @@ void EngineState::saveLoadWithSerializer(Common::Serializer &s) {
s.skip(4, VER(12), VER(12)); // obsolete: used to be status_bar_foreground
s.skip(4, VER(12), VER(12)); // obsolete: used to be status_bar_background
- if (s.getVersion() >= 13 && g_sci->_gui) {
- // Save/Load picPort as well (cause sierra sci also does this)
+ if (s.getVersion() >= 13 && getSciVersion() <= SCI_VERSION_1_1) {
+ // Save/Load picPort as well for SCI0-SCI1.1. Necessary for Castle of Dr. Brain,
+ // as the picPort has been changed when loading during the intro
int16 picPortTop, picPortLeft;
Common::Rect picPortRect;
- if (s.isSaving()) {
- // FIXME: _gfxPorts is 0 when using SCI32 code
- assert(g_sci->_gfxPorts);
+ if (s.isSaving())
picPortRect = g_sci->_gfxPorts->kernelGetPicWindow(picPortTop, picPortLeft);
- }
s.syncAsSint16LE(picPortRect.top);
s.syncAsSint16LE(picPortRect.left);
@@ -379,17 +304,14 @@ void EngineState::saveLoadWithSerializer(Common::Serializer &s) {
s.syncAsSint16LE(picPortRect.right);
s.syncAsSint16LE(picPortTop);
s.syncAsSint16LE(picPortLeft);
- }
- sync_SegManagerPtr(s, _segMan);
+ if (s.isLoading())
+ g_sci->_gfxPorts->kernelSetPicWindow(picPortRect, picPortTop, picPortLeft, false);
+ }
- syncArray<Class>(s, _segMan->_classTable);
+ _segMan->saveLoadWithSerializer(s);
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- sync_songlib(s, _sound._songlib);
-#else
- _soundCmd->syncPlayList(s);
-#endif
+ g_sci->_soundCmd->syncPlayList(s);
}
void LocalVariables::saveLoadWithSerializer(Common::Serializer &s) {
@@ -400,8 +322,8 @@ void LocalVariables::saveLoadWithSerializer(Common::Serializer &s) {
void Object::saveLoadWithSerializer(Common::Serializer &s) {
s.syncAsSint32LE(_flags);
- sync_reg_t(s, _pos);
- s.skip(4, VER(9), VER(12)); // OBSOLETE: Used to be variable_names_nr
+ _pos.saveLoadWithSerializer(s);
+ s.skip(4, VER(12), VER(12)); // OBSOLETE: Used to be variable_names_nr
s.syncAsSint32LE(_methodCount); // that's actually a uint16
syncArray<reg_t>(s, _variables);
@@ -418,18 +340,18 @@ template <>
void syncWithSerializer(Common::Serializer &s, Table<List>::Entry &obj) {
s.syncAsSint32LE(obj.next_free);
- sync_reg_t(s, obj.first);
- sync_reg_t(s, obj.last);
+ obj.first.saveLoadWithSerializer(s);
+ obj.last.saveLoadWithSerializer(s);
}
template <>
void syncWithSerializer(Common::Serializer &s, Table<Node>::Entry &obj) {
s.syncAsSint32LE(obj.next_free);
- sync_reg_t(s, obj.pred);
- sync_reg_t(s, obj.succ);
- sync_reg_t(s, obj.key);
- sync_reg_t(s, obj.value);
+ obj.pred.saveLoadWithSerializer(s);
+ obj.succ.saveLoadWithSerializer(s);
+ obj.key.saveLoadWithSerializer(s);
+ obj.value.saveLoadWithSerializer(s);
}
#ifdef ENABLE_SCI32
@@ -463,7 +385,7 @@ void syncWithSerializer(Common::Serializer &s, Table<SciArray<reg_t> >::Entry &o
if (s.isSaving())
value = obj.getValue(i);
- sync_reg_t(s, value);
+ value.saveLoadWithSerializer(s);
if (s.isLoading())
obj.setValue(i, value);
@@ -524,25 +446,15 @@ void HunkTable::saveLoadWithSerializer(Common::Serializer &s) {
void Script::saveLoadWithSerializer(Common::Serializer &s) {
s.syncAsSint32LE(_nr);
- s.syncAsUint32LE(_bufSize);
- s.syncAsUint32LE(_scriptSize);
- s.syncAsUint32LE(_heapSize);
-
- if (s.getVersion() <= 10) {
- assert((s.isLoading()));
- // OBSOLETE: Skip over the old _objIndices data when loading
- s.skip(4); // base_value
- while (true) {
- uint32 key = 0;
- s.syncAsSint32LE(key);
- if (key == INTMAPPER_MAGIC_KEY)
- break;
- s.skip(4); // idx
- }
- }
- s.skip(4, VER(9), VER(19)); // OBSOLETE: Used to be _numExports
- s.skip(4, VER(9), VER(19)); // OBSOLETE: Used to be _numSynonyms
+ if (s.isLoading())
+ init(_nr, g_sci->getResMan());
+ s.skip(4, VER(12), VER(22)); // OBSOLETE: Used to be _bufSize
+ s.skip(4, VER(12), VER(22)); // OBSOLETE: Used to be _scriptSize
+ s.skip(4, VER(12), VER(22)); // OBSOLETE: Used to be _heapSize
+
+ s.skip(4, VER(12), VER(19)); // OBSOLETE: Used to be _numExports
+ s.skip(4, VER(12), VER(19)); // OBSOLETE: Used to be _numSynonyms
s.syncAsSint32LE(_lockers);
// Sync _objects. This is a hashmap, and we use the following on disk format:
@@ -570,7 +482,7 @@ void Script::saveLoadWithSerializer(Common::Serializer &s) {
}
}
- s.syncAsSint32LE(_localsOffset);
+ s.skip(4, VER(12), VER(20)); // OBSOLETE: Used to be _localsOffset
s.syncAsSint32LE(_localsSegment);
s.syncAsSint32LE(_markedAsDeleted);
@@ -622,30 +534,6 @@ void DataStack::saveLoadWithSerializer(Common::Serializer &s) {
#pragma mark -
-#ifdef USE_OLD_MUSIC_FUNCTIONS
-static void sync_songlib(Common::Serializer &s, SongLibrary &obj) {
- int songcount = 0;
- if (s.isSaving())
- songcount = obj.countSongs();
- s.syncAsUint32LE(songcount);
-
- if (s.isLoading()) {
- obj._lib = 0;
- while (songcount--) {
- Song *newsong = new Song;
- syncSong(s, *newsong);
- obj.addSong(newsong);
- }
- } else {
- Song *seeker = obj._lib;
- while (seeker) {
- seeker->_restoreTime = seeker->_it->getTimepos();
- syncSong(s, *seeker);
- seeker = seeker->_next;
- }
- }
-}
-#else
void SciMusic::saveLoadWithSerializer(Common::Serializer &s) {
// Sync song lib data. When loading, the actual song lib will be initialized
// afterwards in gamestate_restore()
@@ -694,7 +582,35 @@ void SciMusic::saveLoadWithSerializer(Common::Serializer &s) {
}
}
}
-#endif
+
+void SoundCommandParser::syncPlayList(Common::Serializer &s) {
+ _music->saveLoadWithSerializer(s);
+}
+
+void SoundCommandParser::reconstructPlayList(int savegame_version) {
+ Common::StackLock lock(_music->_mutex);
+
+ const MusicList::iterator end = _music->getPlayListEnd();
+ for (MusicList::iterator i = _music->getPlayListStart(); i != end; ++i) {
+ if ((*i)->resourceId && _resMan->testResource(ResourceId(kResourceTypeSound, (*i)->resourceId))) {
+ (*i)->soundRes = new SoundResource((*i)->resourceId, _resMan, _soundVersion);
+ _music->soundInitSnd(*i);
+ } else {
+ (*i)->soundRes = 0;
+ }
+ if ((*i)->status == kSoundPlaying) {
+ if (savegame_version < 14) {
+ (*i)->dataInc = readSelectorValue(_segMan, (*i)->soundObj, SELECTOR(dataInc));
+ (*i)->signal = readSelectorValue(_segMan, (*i)->soundObj, SELECTOR(signal));
+
+ if (_soundVersion >= SCI_VERSION_1_LATE)
+ (*i)->volume = readSelectorValue(_segMan, (*i)->soundObj, SELECTOR(vol));
+ }
+
+ processPlaySound((*i)->soundObj);
+ }
+ }
+}
#ifdef ENABLE_SCI32
void ArrayTable::saveLoadWithSerializer(Common::Serializer &ser) {
@@ -712,43 +628,13 @@ void StringTable::saveLoadWithSerializer(Common::Serializer &ser) {
}
#endif
-#pragma mark -
-
-
-int gamestate_save(EngineState *s, Common::WriteStream *fh, const char* savename, const char *version) {
- TimeDate curTime;
- g_system->getTimeAndDate(curTime);
-
- SavegameMetadata meta;
- meta.savegame_version = CURRENT_SAVEGAME_VERSION;
- meta.savegame_name = savename;
- meta.game_version = version;
- meta.savegame_date = ((curTime.tm_mday & 0xFF) << 24) | (((curTime.tm_mon + 1) & 0xFF) << 16) | ((curTime.tm_year + 1900) & 0xFFFF);
- meta.savegame_time = ((curTime.tm_hour & 0xFF) << 16) | (((curTime.tm_min) & 0xFF) << 8) | ((curTime.tm_sec) & 0xFF);
-
- if (s->execution_stack_base) {
- warning("Cannot save from below kernel function");
- return 1;
- }
-
- Common::Serializer ser(0, fh);
- sync_SavegameMetadata(ser, meta);
- Graphics::saveThumbnail(*fh);
- s->saveLoadWithSerializer(ser); // FIXME: Error handling?
-
- return 0;
+void SegManager::reconstructStack(EngineState *s) {
+ DataStack *stack = (DataStack *)(_heap[findSegmentByType(SEG_TYPE_STACK)]);
+ s->stack_base = stack->_entries;
+ s->stack_top = s->stack_base + stack->_capacity;
}
-// 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);
- DataStack *stack = (DataStack *)(retval->_segMan->_heap[stack_seg]);
-
- retval->stack_base = stack->_entries;
- retval->stack_top = stack->_entries + stack->_capacity;
-}
-
-// TODO: Move thie function to a more appropriate place, such as vm.cpp or script.cpp
+// TODO: Move this function to a more appropriate place, such as vm.cpp or script.cpp
void SegManager::reconstructScripts(EngineState *s) {
uint i;
@@ -761,7 +647,7 @@ void SegManager::reconstructScripts(EngineState *s) {
scr->_localsBlock = (scr->_localsSegment == 0) ? NULL : (LocalVariables *)(_heap[scr->_localsSegment]);
for (ObjMap::iterator it = scr->_objects.begin(); it != scr->_objects.end(); ++it)
- it->_value._baseObj = scr->_buf + it->_value.getPos().offset;
+ it->_value._baseObj = scr->getBuf(it->_value.getPos().offset);
}
for (i = 0; i < _heap.size(); i++) {
@@ -776,6 +662,8 @@ void SegManager::reconstructScripts(EngineState *s) {
if (getSciVersion() < SCI_VERSION_1_1) {
if (!obj->initBaseObject(this, addr, false)) {
+ // TODO/FIXME: This should not be happening at all. It might indicate a possible issue
+ // with the garbage collector. It happens for example in LSL5 (German, perhaps English too).
warning("Failed to locate base object for object at %04X:%04X; skipping", PRINT_REG(addr));
scr->scriptObjRemove(addr);
}
@@ -784,47 +672,70 @@ void SegManager::reconstructScripts(EngineState *s) {
}
}
-#ifdef USE_OLD_MUSIC_FUNCTIONS
-static void reconstruct_sounds(EngineState *s) {
- Song *seeker;
- SongIteratorType it_type;
+void SegManager::reconstructClones() {
+ for (uint i = 0; i < _heap.size(); i++) {
+ SegmentObj *mobj = _heap[i];
+ if (mobj && mobj->getType() == SEG_TYPE_CLONES) {
+ CloneTable *ct = (CloneTable *)mobj;
+
+ for (uint j = 0; j < ct->_table.size(); j++) {
+ // Check if the clone entry is used
+ uint entryNum = (uint)ct->first_free;
+ bool isUsed = true;
+ while (entryNum != ((uint) CloneTable::HEAPENTRY_INVALID)) {
+ if (entryNum == j) {
+ isUsed = false;
+ break;
+ }
+ entryNum = ct->_table[entryNum].next_free;
+ }
+
+ if (!isUsed)
+ continue;
- if (getSciVersion() > SCI_VERSION_01)
- it_type = SCI_SONG_ITERATOR_TYPE_SCI1;
- else
- it_type = SCI_SONG_ITERATOR_TYPE_SCI0;
+ CloneTable::Entry &seeker = ct->_table[j];
+ const Object *baseObj = getObject(seeker.getSpeciesSelector());
+ seeker.cloneFromObject(baseObj);
+ if (!baseObj)
+ error("Clone entry without a base class: %d", j);
+ } // end for
+ } // end if
+ } // end for
+}
- seeker = s->_sound._songlib._lib;
- while (seeker) {
- SongIterator *base, *ff = 0;
- int oldstatus;
- SongIterator::Message msg;
+#pragma mark -
- base = ff = build_iterator(g_sci->getResMan(), seeker->_resourceNum, it_type, seeker->_handle);
- if (seeker->_restoreBehavior == RESTORE_BEHAVIOR_CONTINUE)
- ff = new_fast_forward_iterator(base, seeker->_restoreTime);
- ff->init();
- msg = SongIterator::Message(seeker->_handle, SIMSG_SET_LOOPS(seeker->_loops));
- songit_handle_message(&ff, msg);
- msg = SongIterator::Message(seeker->_handle, SIMSG_SET_HOLD(seeker->_hold));
- songit_handle_message(&ff, msg);
+bool gamestate_save(EngineState *s, Common::WriteStream *fh, const char* savename, const char *version) {
+ TimeDate curTime;
+ g_system->getTimeAndDate(curTime);
- oldstatus = seeker->_status;
- seeker->_status = SOUND_STATUS_STOPPED;
- seeker->_it = ff;
- s->_sound.sfx_song_set_status(seeker->_handle, oldstatus);
- seeker = seeker->_next;
+ SavegameMetadata meta;
+ meta.savegame_version = CURRENT_SAVEGAME_VERSION;
+ meta.savegame_name = savename;
+ meta.game_version = version;
+ meta.savegame_date = ((curTime.tm_mday & 0xFF) << 24) | (((curTime.tm_mon + 1) & 0xFF) << 16) | ((curTime.tm_year + 1900) & 0xFFFF);
+ meta.savegame_time = ((curTime.tm_hour & 0xFF) << 16) | (((curTime.tm_min) & 0xFF) << 8) | ((curTime.tm_sec) & 0xFF);
+
+ Resource *script0 = g_sci->getResMan()->findResource(ResourceId(kResourceTypeScript, 0), false);
+ meta.script0_size = script0->size;
+ meta.game_object_offset = g_sci->getGameObject().offset;
+
+ if (s->executionStackBase) {
+ warning("Cannot save from below kernel function");
+ return false;
}
+
+ Common::Serializer ser(0, fh);
+ sync_SavegameMetadata(ser, meta);
+ Graphics::saveThumbnail(*fh);
+ s->saveLoadWithSerializer(ser); // FIXME: Error handling?
+
+ return true;
}
-#endif
void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) {
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- SongLibrary temp;
-#endif
-
SavegameMetadata meta;
Common::Serializer ser(fh, 0);
@@ -837,85 +748,62 @@ void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) {
if ((meta.savegame_version < MINIMUM_SAVEGAME_VERSION) ||
(meta.savegame_version > CURRENT_SAVEGAME_VERSION)) {
+ /*
if (meta.savegame_version < MINIMUM_SAVEGAME_VERSION)
- warning("Old savegame version detected- can't load");
+ warning("Old savegame version detected, unable to load it");
else
- warning("Savegame version is %d- maximum supported is %0d", meta.savegame_version, CURRENT_SAVEGAME_VERSION);
+ warning("Savegame version is %d, maximum supported is %0d", meta.savegame_version, CURRENT_SAVEGAME_VERSION);
+ */
+
+ GUI::MessageDialog dialog("The format of this saved game is obsolete, unable to load it", "OK");
+ dialog.runModal();
s->r_acc = make_reg(0, 1); // signal failure
return;
}
- if (meta.savegame_version >= 12) {
- // We don't need the thumbnail here, so just read it and discard it
- Graphics::Surface *thumbnail = new Graphics::Surface();
- assert(thumbnail);
- Graphics::loadThumbnail(*fh, *thumbnail);
- delete thumbnail;
- thumbnail = 0;
+ if (meta.game_object_offset > 0 && meta.script0_size > 0) {
+ Resource *script0 = g_sci->getResMan()->findResource(ResourceId(kResourceTypeScript, 0), false);
+ if (script0->size != meta.script0_size || g_sci->getGameObject().offset != meta.game_object_offset) {
+ //warning("This saved game was created with a different version of the game, unable to load it");
+
+ GUI::MessageDialog dialog("This saved game was created with a different version of the game, unable to load it", "OK");
+ dialog.runModal();
+
+ s->r_acc = make_reg(0, 1); // signal failure
+ return;
+ }
}
+ // We don't need the thumbnail here, so just read it and discard it
+ Graphics::Surface *thumbnail = new Graphics::Surface();
+ assert(thumbnail);
+ Graphics::loadThumbnail(*fh, *thumbnail);
+ delete thumbnail;
+ thumbnail = 0;
+
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
- s->execution_stack_base = 0;
-
// Now copy all current state information
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- 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(s);
+ s->_segMan->reconstructStack(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;
+ s->initGlobals();
+ s->gcCountDown = GC_INTERVAL - 1;
// Time state:
- s->last_wait_time = g_system->getMillis();
- s->game_start_time = g_system->getMillis();
-
- s->restoring = false;
-
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- s->_sound._it = NULL;
- s->_sound._flags = s->_sound._flags;
- s->_sound._song = NULL;
- s->_sound._suspended = s->_sound._suspended;
- reconstruct_sounds(s);
-#else
- s->_soundCmd->reconstructPlayList(meta.savegame_version);
-#endif
+ s->lastWaitTime = g_system->getMillis();
+ s->gameStartTime = g_system->getMillis();
+ s->_screenUpdateTime = g_system->getMillis();
+
+ g_sci->_soundCmd->reconstructPlayList(meta.savegame_version);
// Message state:
s->_msgState = new MessageState(s->_segMan);
-#ifdef ENABLE_SCI32
- if (g_sci->_gui32) {
- g_sci->_gui32->init();
- } else {
-#endif
- g_sci->_gui->resetEngineState(s);
- g_sci->_gui->init(g_sci->_features->usesOldGfxFunctions());
-#ifdef ENABLE_SCI32
- }
-#endif
-
-
- s->restoring = true;
- s->script_abort_flag = 2; // Abort current game with replay
+ s->abortScriptProcessing = kAbortLoadGame;
s->shrinkStackToBase();
}
diff --git a/engines/sci/engine/savegame.h b/engines/sci/engine/savegame.h
index 7be05381da..9a882f2bf7 100644
--- a/engines/sci/engine/savegame.h
+++ b/engines/sci/engine/savegame.h
@@ -36,8 +36,8 @@ namespace Sci {
struct EngineState;
enum {
- CURRENT_SAVEGAME_VERSION = 20,
- MINIMUM_SAVEGAME_VERSION = 9
+ CURRENT_SAVEGAME_VERSION = 23,
+ MINIMUM_SAVEGAME_VERSION = 12
};
// Savegame metadata
@@ -47,6 +47,8 @@ struct SavegameMetadata {
Common::String game_version;
int savegame_date;
int savegame_time;
+ uint16 game_object_offset;
+ uint16 script0_size;
};
@@ -57,7 +59,7 @@ struct SavegameMetadata {
* @param savename The description of the savegame
* @return 0 on success, 1 otherwise
*/
-int gamestate_save(EngineState *s, Common::WriteStream *save, const char *savename, const char *version);
+bool gamestate_save(EngineState *s, Common::WriteStream *save, const char *savename, const char *version);
/**
* Restores a game state from a directory.
diff --git a/engines/sci/engine/script.cpp b/engines/sci/engine/script.cpp
index 1f32e50b67..a293f81d2f 100644
--- a/engines/sci/engine/script.cpp
+++ b/engines/sci/engine/script.cpp
@@ -35,431 +35,553 @@
namespace Sci {
-#define END Script_None
-
-opcode_format g_opcode_formats[128][4] = {
- /*00*/
- {Script_None}, {Script_None}, {Script_None}, {Script_None},
- /*04*/
- {Script_None}, {Script_None}, {Script_None}, {Script_None},
- /*08*/
- {Script_None}, {Script_None}, {Script_None}, {Script_None},
- /*0C*/
- {Script_None}, {Script_None}, {Script_None}, {Script_None},
- /*10*/
- {Script_None}, {Script_None}, {Script_None}, {Script_None},
- /*14*/
- {Script_None}, {Script_None}, {Script_None}, {Script_SRelative, END},
- /*18*/
- {Script_SRelative, END}, {Script_SRelative, END}, {Script_SVariable, END}, {Script_None},
- /*1C*/
- {Script_SVariable, END}, {Script_None}, {Script_None}, {Script_Variable, END},
- /*20*/
- {Script_SRelative, Script_Byte, END}, {Script_Variable, Script_Byte, END}, {Script_Variable, Script_Byte, END}, {Script_Variable, Script_SVariable, Script_Byte, END},
- /*24 (24=ret)*/
- {Script_End}, {Script_Byte, END}, {Script_Invalid}, {Script_Invalid},
- /*28*/
- {Script_Variable, END}, {Script_Invalid}, {Script_Byte, END}, {Script_Variable, Script_Byte, END},
- /*2C*/
- {Script_SVariable, END}, {Script_SVariable, Script_Variable, END}, {Script_None}, {Script_Invalid},
- /*30*/
- {Script_None}, {Script_Property, END}, {Script_Property, END}, {Script_Property, END},
- /*34*/
- {Script_Property, END}, {Script_Property, END}, {Script_Property, END}, {Script_Property, END},
- /*38*/
- {Script_Property, END}, {Script_SRelative, END}, {Script_SRelative, END}, {Script_None},
- /*3C*/
- {Script_None}, {Script_None}, {Script_None}, {Script_Word},
- /*40-4F*/
- {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
- {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
- {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
- {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
- /*50-5F*/
- {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
- {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
- {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
- {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
- /*60-6F*/
- {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
- {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
- {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
- {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
- /*70-7F*/
- {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
- {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
- {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
- {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}
-};
-#undef END
-
-// TODO: script_adjust_opcode_formats should probably be part of the
-// constructor (?) of a VirtualMachine or a ScriptManager class.
-void script_adjust_opcode_formats(EngineState *s) {
- // TODO: Check that this is correct
- if (g_sci->_features->detectLofsType() != SCI_VERSION_0_EARLY) {
- g_opcode_formats[op_lofsa][0] = Script_Offset;
- g_opcode_formats[op_lofss][0] = Script_Offset;
- }
+Script::Script() : SegmentObj(SEG_TYPE_SCRIPT) {
+ _nr = 0;
+ _buf = NULL;
+ _bufSize = 0;
+ _scriptSize = 0;
+ _heapSize = 0;
+
+ _synonyms = NULL;
+ _heapStart = NULL;
+ _exportTable = NULL;
+
+ _localsOffset = 0;
+ _localsSegment = 0;
+ _localsBlock = NULL;
+ _localsCount = 0;
+
+ _markedAsDeleted = false;
+}
+
+Script::~Script() {
+ freeScript();
+}
+
+void Script::freeScript() {
+ free(_buf);
+ _buf = NULL;
+ _bufSize = 0;
-#ifdef ENABLE_SCI32
- // In SCI32, some arguments are now words instead of bytes
- if (getSciVersion() >= SCI_VERSION_2) {
- g_opcode_formats[op_calle][2] = Script_Word;
- g_opcode_formats[op_callk][1] = Script_Word;
- g_opcode_formats[op_super][1] = Script_Word;
- g_opcode_formats[op_send][0] = Script_Word;
- g_opcode_formats[op_self][0] = Script_Word;
- g_opcode_formats[op_call][1] = Script_Word;
- g_opcode_formats[op_callb][1] = Script_Word;
+ _objects.clear();
+}
+
+void Script::init(int script_nr, ResourceManager *resMan) {
+ Resource *script = resMan->findResource(ResourceId(kResourceTypeScript, script_nr), 0);
+
+ _localsOffset = 0;
+ _localsBlock = NULL;
+ _localsCount = 0;
+
+ _markedAsDeleted = false;
+
+ _nr = script_nr;
+ _buf = 0;
+ _heapStart = 0;
+
+ _scriptSize = script->size;
+ _bufSize = script->size;
+ _heapSize = 0;
+
+ _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.
+ if (script->size & 2) {
+ _bufSize++;
+ _scriptSize++;
+ }
+
+ // As mentioned above, the script and the heap together should not exceed 64KB
+ if (script->size + heap->size > 65535)
+ error("Script and heap sizes combined exceed 64K. This means a fundamental "
+ "design bug was made regarding SCI1.1 and newer games.\n"
+ "Please report this error to the ScummVM team");
}
-#endif
}
-void SegManager::createClassTable() {
- Resource *vocab996 = _resMan->findResource(ResourceId(kResourceTypeVocab, 996), 1);
+void Script::load(ResourceManager *resMan) {
+ Resource *script = resMan->findResource(ResourceId(kResourceTypeScript, _nr), 0);
+ assert(script != 0);
- if (!vocab996)
- error("SegManager: failed to open vocab 996");
+ _buf = (byte *)malloc(_bufSize);
+ assert(_buf);
- int totalClasses = vocab996->size >> 2;
- _classTable.resize(totalClasses);
+ 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);
- for (uint16 classNr = 0; classNr < totalClasses; classNr++) {
- uint16 scriptNr = READ_SCI11ENDIAN_UINT16(vocab996->data + classNr * 4 + 2);
+ _heapStart = _buf + _scriptSize;
- _classTable[classNr].reg = NULL_REG;
- _classTable[classNr].script = scriptNr;
+ assert(_bufSize - _scriptSize <= heap->size);
+ memcpy(_heapStart, heap->data, heap->size);
}
- _resMan->unlockResource(vocab996);
-}
+ _exportTable = 0;
+ _numExports = 0;
+ _synonyms = 0;
+ _numSynonyms = 0;
+
+ if (getSciVersion() >= SCI_VERSION_1_1) {
+ if (READ_LE_UINT16(_buf + 1 + 5) > 0) { // does the script have an export table?
+ _exportTable = (const uint16 *)(_buf + 1 + 5 + 2);
+ _numExports = READ_SCI11ENDIAN_UINT16(_exportTable - 1);
+ }
+ _localsOffset = _scriptSize + 4;
+ _localsCount = READ_SCI11ENDIAN_UINT16(_buf + _localsOffset - 2);
+ } 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
+ }
+ const byte* localsBlock = findBlock(SCI_OBJ_LOCALVARS);
+ if (localsBlock) {
+ _localsOffset = localsBlock - _buf + 4;
+ _localsCount = (READ_LE_UINT16(_buf + _localsOffset - 2) - 4) >> 1; // half block size
+ }
+ }
-reg_t SegManager::getClassAddress(int classnr, ScriptLoadType lock, reg_t caller) {
- if (classnr == 0xffff)
- return NULL_REG;
+ if (getSciVersion() > SCI_VERSION_0_EARLY) {
+ // Does the script actually have locals? If not, set the locals offset to 0
+ if (!_localsCount)
+ _localsOffset = 0;
- 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;
+ if (_localsOffset + _localsCount * 2 + 1 >= (int)_bufSize) {
+ error("Locals extend beyond end of script: offset %04x, count %d vs size %d", _localsOffset, _localsCount, _bufSize);
+ _localsCount = (_bufSize - _localsOffset) >> 1;
+ }
} else {
- Class *the_class = &_classTable[classnr];
- if (!the_class->reg.segment) {
- getScriptSegment(the_class->script, lock);
+ // 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.
+ _localsCount = READ_LE_UINT16(_buf);
+ _localsOffset = -_localsCount * 2; // Make sure it's invalid
+ }
+}
- if (!the_class->reg.segment) {
- error("[VM] Trying to instantiate class %x by instantiating script 0x%x (%03d) failed;", classnr, the_class->script, the_class->script);
- return NULL_REG;
- }
- } else
- if (caller.segment != the_class->reg.segment)
- getScript(the_class->reg.segment)->incrementLockers();
+Object *Script::getObject(uint16 offset) {
+ if (_objects.contains(offset))
+ return &_objects[offset];
+ else
+ return 0;
+}
- return the_class->reg;
- }
+const Object *Script::getObject(uint16 offset) const {
+ if (_objects.contains(offset))
+ return &_objects[offset];
+ else
+ return 0;
}
-void SegManager::scriptInitialiseLocalsZero(SegmentId seg, int count) {
- Script *scr = getScript(seg);
+Object *Script::scriptObjInit(reg_t obj_pos, bool fullObjectInit) {
+ if (getSciVersion() < SCI_VERSION_1_1 && fullObjectInit)
+ obj_pos.offset += 8; // magic offset (SCRIPT_OBJECT_MAGIC_OFFSET)
- scr->_localsOffset = -count * 2; // Make sure it's invalid
+ VERIFY(obj_pos.offset < _bufSize, "Attempt to initialize object beyond end of script\n");
- LocalVariables *locals = allocLocalsSegment(scr, count);
- if (locals) {
- for (int i = 0; i < count; i++)
- locals->_locals[i] = NULL_REG;
- }
+ VERIFY(obj_pos.offset + kOffsetFunctionArea < (int)_bufSize, "Function area pointer stored beyond end of script\n");
+
+ // Get the object at the specified position and init it. This will
+ // automatically "allocate" space for it in the _objects map if necessary.
+ Object *obj = &_objects[obj_pos.offset];
+ obj->init(_buf, obj_pos, fullObjectInit);
+
+ return obj;
}
-void SegManager::scriptInitialiseLocals(reg_t location) {
- Script *scr = getScript(location.segment);
- unsigned int count;
+void Script::scriptObjRemove(reg_t obj_pos) {
+ if (getSciVersion() < SCI_VERSION_1_1)
+ obj_pos.offset += 8;
- VERIFY(location.offset + 1 < (uint16)scr->getBufSize(), "Locals beyond end of script\n");
+ _objects.erase(obj_pos.toUint16());
+}
- if (getSciVersion() >= SCI_VERSION_1_1)
- count = READ_SCI11ENDIAN_UINT16(scr->_buf + location.offset - 2);
- else
- count = (READ_LE_UINT16(scr->_buf + location.offset - 2) - 4) >> 1;
- // half block size
+// This helper function is used by Script::relocateLocal and Object::relocate
+// Duplicate in segment.cpp and script.cpp
+static bool relocateBlock(Common::Array<reg_t> &block, int block_location, SegmentId segment, int location, size_t scriptSize) {
+ int rel = location - block_location;
- scr->_localsOffset = location.offset;
+ if (rel < 0)
+ return false;
- 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;
- }
+ uint idx = rel >> 1;
- LocalVariables *locals = allocLocalsSegment(scr, count);
- if (locals) {
- uint i;
- const byte *base = (const byte *)(scr->_buf + location.offset);
+ if (idx >= block.size())
+ return false;
- for (i = 0; i < count; i++)
- locals->_locals[i] = make_reg(0, READ_SCI11ENDIAN_UINT16(base + i * 2));
+ if (rel & 1) {
+ error("Attempt to relocate odd variable #%d.5e (relative to %04x)\n", idx, block_location);
+ return false;
}
+ block[idx].segment = segment; // Perform relocation
+ if (getSciVersion() >= SCI_VERSION_1_1)
+ block[idx].offset += scriptSize;
+
+ return true;
}
-void SegManager::scriptInitialiseObjectsSci11(SegmentId seg) {
- Script *scr = getScript(seg);
- const byte *seeker = scr->_heapStart + 4 + READ_SCI11ENDIAN_UINT16(scr->_heapStart + 2) * 2;
+bool Script::relocateLocal(SegmentId segment, int location) {
+ if (_localsBlock)
+ return relocateBlock(_localsBlock->_locals, _localsOffset, segment, location, _scriptSize);
+ else
+ return false;
+}
- while (READ_SCI11ENDIAN_UINT16(seeker) == SCRIPT_OBJECT_MAGIC_NUMBER) {
- 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()) {
- error("Invalid species %d(0x%x) not in interval [0,%d) while instantiating script %d",
- species, species, _classTable.size(), scr->_nr);
- return;
- }
+void Script::relocate(reg_t block) {
+ const byte *heap = _buf;
+ uint16 heapSize = (uint16)_bufSize;
+ uint16 heapOffset = 0;
- _classTable[species].reg.segment = seg;
- _classTable[species].reg.offset = classpos;
- }
- seeker += READ_SCI11ENDIAN_UINT16(seeker + 2) * 2;
+ if (getSciVersion() >= SCI_VERSION_1_1) {
+ heap = _heapStart;
+ heapSize = (uint16)_heapSize;
+ heapOffset = _scriptSize;
}
- seeker = scr->_heapStart + 4 + READ_SCI11ENDIAN_UINT16(scr->_heapStart + 2) * 2;
- while (READ_SCI11ENDIAN_UINT16(seeker) == SCRIPT_OBJECT_MAGIC_NUMBER) {
- reg_t reg = make_reg(seg, seeker - scr->_buf);
- Object *obj = scr->scriptObjInit(reg);
-
- // 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());
+ VERIFY(block.offset < (uint16)heapSize && READ_SCI11ENDIAN_UINT16(heap + block.offset) * 2 + block.offset < (uint16)heapSize,
+ "Relocation block outside of script\n");
+
+ int count = READ_SCI11ENDIAN_UINT16(heap + block.offset);
+ int exportIndex = 0;
+ int pos = 0;
+
+ for (int i = 0; i < count; i++) {
+ pos = READ_SCI11ENDIAN_UINT16(heap + block.offset + 2 + (exportIndex * 2)) + heapOffset;
+ // This occurs in SCI01/SCI1 games where 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");
}
- // 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
- // uses this selector together with -propDict- to compare classes.
- // For the purpose of Obj::isKindOf, using the script number appears
- // to be sufficient.
- obj->setClassScriptSelector(make_reg(0, scr->_nr));
+ // In SCI0-SCI1, script local variables, objects and code are relocated.
+ // We only relocate locals and objects here, and ignore relocation of
+ // code blocks. In SCI1.1 and newer versions, only locals and objects
+ // are relocated.
+ if (!relocateLocal(block.segment, pos)) {
+ // Not a local? It's probably an object or code block. If it's an object, relocate it.
+ const ObjMap::iterator end = _objects.end();
+ for (ObjMap::iterator it = _objects.begin(); it != end; ++it)
+ if (it->_value.relocate(block.segment, pos, _scriptSize))
+ break;
+ }
- seeker += READ_SCI11ENDIAN_UINT16(seeker + 2) * 2;
+ exportIndex++;
}
}
-void script_instantiate_sci0(Script *scr, int segmentId, SegManager *segMan) {
- int objType;
- uint32 objLength = 0;
- bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY);
- uint16 curOffset = oldScriptHeader ? 2 : 0;
+void Script::incrementLockers() {
+ _lockers++;
+}
- 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 localsCount = READ_LE_UINT16(scr->_buf);
- if (localsCount)
- segMan->scriptInitialiseLocalsZero(segmentId, localsCount);
- }
+void Script::decrementLockers() {
+ if (_lockers > 0)
+ _lockers--;
+}
- // Now do a first pass through the script objects to find the
- // local variable blocks
+int Script::getLockers() const {
+ return _lockers;
+}
- do {
- objType = scr->getHeap(curOffset);
- if (!objType)
- break;
+void Script::setLockers(int lockers) {
+ _lockers = lockers;
+}
- objLength = scr->getHeap(curOffset + 2);
- curOffset += 4; // skip header
+uint16 Script::validateExportFunc(int pubfunct) {
+ bool exportsAreWide = (g_sci->_features->detectLofsType() == SCI_VERSION_1_MIDDLE);
- switch (objType) {
- case SCI_OBJ_LOCALVARS:
- 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->classTableSize()) {
- if (species == (int)segMan->classTableSize()) {
- // Happens in the LSL2 demo
- warning("Applying workaround for an off-by-one invalid species access");
- segMan->resizeClassTable(segMan->classTableSize() + 1);
- } else {
- error("Invalid species %d(0x%x) not in interval "
- "[0,%d) while instantiating script at segment %d\n",
- species, species, segMan->classTableSize(),
- segmentId);
- return;
- }
- }
+ if (_numExports <= pubfunct) {
+ error("validateExportFunc(): pubfunct is invalid");
+ return 0;
+ }
- segMan->setClassOffset(species, make_reg(segmentId, classpos));
- // Set technical class position-- into the block allocated for it
- }
- break;
+ if (exportsAreWide)
+ pubfunct *= 2;
+ uint16 offset = READ_SCI11ENDIAN_UINT16(_exportTable + pubfunct);
+ VERIFY(offset < _bufSize, "invalid export function pointer");
- default:
- break;
- }
+ return offset;
+}
- curOffset += objLength - 4;
- } while (objType != 0 && curOffset < scr->getScriptSize() - 2);
+byte *Script::findBlock(int type) {
+ byte *buf = _buf;
+ bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY);
- // And now a second pass to adjust objects and class pointers, and the general pointers
- objLength = 0;
- curOffset = oldScriptHeader ? 2 : 0;
+ if (oldScriptHeader)
+ buf += 2;
do {
- objType = scr->getHeap(curOffset);
- if (!objType)
+ int seekerType = READ_LE_UINT16(buf);
+
+ if (seekerType == 0)
break;
+ if (seekerType == type)
+ return buf;
- objLength = scr->getHeap(curOffset + 2);
- curOffset += 4; // skip header
+ int seekerSize = READ_LE_UINT16(buf + 2);
+ assert(seekerSize > 0);
+ buf += seekerSize;
+ } while (1);
- reg_t addr = make_reg(segmentId, curOffset);
+ return NULL;
+}
- switch (objType) {
- case SCI_OBJ_CODE:
- scr->scriptAddCodeBlock(addr);
- break;
- case SCI_OBJ_OBJECT:
- case SCI_OBJ_CLASS: { // object or class?
- Object *obj = scr->scriptObjInit(addr);
- obj->initSpecies(segMan, addr);
+// memory operations
- 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;
- default:
- break;
- }
+void Script::mcpyInOut(int dst, const void *src, size_t n) {
+ if (_buf) {
+ assert(dst + n <= _bufSize);
+ memcpy(_buf + dst, src, n);
+ }
+}
+
+bool Script::isValidOffset(uint16 offset) const {
+ return offset < _bufSize;
+}
+
+SegmentRef Script::dereference(reg_t pointer) {
+ if (pointer.offset > _bufSize) {
+ error("Script::dereference(): Attempt to dereference invalid pointer %04x:%04x into script segment (script size=%d)",
+ PRINT_REG(pointer), (uint)_bufSize);
+ return SegmentRef();
+ }
- curOffset += objLength - 4;
- } while (objType != 0 && curOffset < scr->getScriptSize() - 2);
+ SegmentRef ret;
+ ret.isRaw = true;
+ ret.maxSize = _bufSize - pointer.offset;
+ ret.raw = _buf + pointer.offset;
+ return ret;
}
-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;
+void Script::initialiseLocals(SegManager *segMan) {
+ LocalVariables *locals = segMan->allocLocalsSegment(this);
+ if (locals) {
+ if (getSciVersion() > SCI_VERSION_0_EARLY) {
+ const byte *base = (const byte *)(_buf + getLocalsOffset());
+
+ for (uint16 i = 0; i < getLocalsCount(); i++)
+ locals->_locals[i] = make_reg(0, READ_SCI11ENDIAN_UINT16(base + i * 2));
} else {
- scr->freeScript();
+ // In SCI0 early, locals are set at run time, thus zero them all here
+ for (uint16 i = 0; i < getLocalsCount(); i++)
+ locals->_locals[i] = NULL_REG;
}
- } else {
- scr = segMan->allocateScript(scriptNum, &segmentId);
}
+}
- scr->init(scriptNum, resMan);
- scr->load(resMan);
-
+void Script::initialiseClasses(SegManager *segMan) {
+ const byte *seeker = 0;
+ uint16 mult = 0;
+
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)));
+ seeker = _heapStart + 4 + READ_SCI11ENDIAN_UINT16(_heapStart + 2) * 2;
+ mult = 2;
} 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));
+ seeker = findBlock(SCI_OBJ_CLASS);
+ mult = 1;
}
- return segmentId;
-}
-
-void script_uninstantiate_sci0(SegManager *segMan, int script_nr, SegmentId seg) {
- bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY);
- reg_t reg = make_reg(seg, oldScriptHeader ? 2 : 0);
- int objType, objLength = 0;
- Script *scr = segMan->getScript(seg);
+ if (!seeker)
+ return;
- // Make a pass over the object in order uninstantiate all superclasses
+ uint16 marker;
+ bool isClass;
+ uint16 classpos;
+ int16 species = 0;
- do {
- reg.offset += objLength; // Step over the last checked object
+ while (true) {
+ // In SCI0-SCI1, this is the segment type. In SCI11, it's a marker (0x1234)
+ marker = READ_SCI11ENDIAN_UINT16(seeker);
+ classpos = seeker - _buf;
- objType = scr->getHeap(reg.offset);
- if (!objType)
+ if (!marker)
break;
- objLength = scr->getHeap(reg.offset + 2); // use SEG_UGET_HEAP ??
- reg.offset += 4; // Step over header
+ if (getSciVersion() >= SCI_VERSION_1_1) {
+ isClass = (READ_SCI11ENDIAN_UINT16(seeker + 14) & kInfoFlagClass); // -info- selector
+ species = READ_SCI11ENDIAN_UINT16(seeker + 10);
+ } else {
+ isClass = (marker == SCI_OBJ_CLASS);
+ if (isClass)
+ species = READ_SCI11ENDIAN_UINT16(seeker + 12);
+ classpos += 12;
+ }
- if ((objType == SCI_OBJ_OBJECT) || (objType == SCI_OBJ_CLASS)) { // object or class?
- int superclass;
+ if (isClass) {
+ // WORKAROUND for an invalid species access in the demo of LSL2
+ if (g_sci->getGameId() == GID_LSL2 && g_sci->isDemo() && species == (int)segMan->classTableSize())
+ segMan->resizeClassTable(segMan->classTableSize() + 1);
- reg.offset -= SCRIPT_OBJECT_MAGIC_OFFSET;
+ if (species < 0 || species >= (int)segMan->classTableSize())
+ error("Invalid species %d(0x%x) not in interval [0,%d) while instantiating script %d\n",
+ species, species, segMan->classTableSize(), _nr);
- superclass = scr->getHeap(reg.offset + SCRIPT_SUPERCLASS_OFFSET); // Get superclass...
+ SegmentId segmentId = segMan->getScriptSegment(_nr);
+ segMan->setClassOffset(species, make_reg(segmentId, classpos));
+ }
+
+ seeker += READ_SCI11ENDIAN_UINT16(seeker + 2) * mult;
+ }
+}
- if (superclass >= 0) {
- int superclass_script = segMan->getClass(superclass).script;
+void Script::initialiseObjectsSci0(SegManager *segMan, SegmentId segmentId) {
+ bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY);
+ const byte *seeker = _buf + (oldScriptHeader ? 2 : 0);
+
+ do {
+ uint16 objType = READ_SCI11ENDIAN_UINT16(seeker);
+ if (!objType)
+ break;
- if (superclass_script == script_nr) {
- if (scr->getLockers())
- scr->decrementLockers(); // Decrease lockers if this is us ourselves
- } else
- script_uninstantiate(segMan, superclass_script);
- // Recurse to assure that the superclass lockers number gets decreased
+ switch (objType) {
+ case SCI_OBJ_OBJECT:
+ case SCI_OBJ_CLASS:
+ {
+ reg_t addr = make_reg(segmentId, seeker - _buf + 4);
+ Object *obj = scriptObjInit(addr);
+ obj->initSpecies(segMan, addr);
+
+ if (!obj->initBaseObject(segMan, addr)) {
+ if (_nr == 202 && g_sci->getGameId() == GID_KQ5 && g_sci->getSciLanguage() == K_LANG_FRENCH) {
+ // Script 202 of KQ5 French has an invalid object. This is non-fatal.
+ } else {
+ error("Failed to locate base object for object at %04X:%04X; skipping", PRINT_REG(addr));
+ }
+ scriptObjRemove(addr);
+ }
}
+ break;
- reg.offset += SCRIPT_OBJECT_MAGIC_OFFSET;
- } // if object or class
+ default:
+ break;
+ }
- reg.offset -= 4; // Step back on header
+ seeker += READ_SCI11ENDIAN_UINT16(seeker + 2);
+ } while ((uint32)(seeker - _buf) < getScriptSize() - 2);
- } while (objType != 0);
+ byte *relocationBlock = findBlock(SCI_OBJ_POINTERS);
+ if (relocationBlock)
+ relocate(make_reg(segmentId, relocationBlock - getBuf() + 4));
}
-void script_uninstantiate(SegManager *segMan, int script_nr) {
- SegmentId segment = segMan->getScriptSegment(script_nr);
- Script *scr = segMan->getScriptIfLoaded(segment);
+void Script::initialiseObjectsSci11(SegManager *segMan, SegmentId segmentId) {
+ const byte *seeker = _heapStart + 4 + READ_SCI11ENDIAN_UINT16(_heapStart + 2) * 2;
- if (!scr) { // Is it already loaded?
- //warning("unloading script 0x%x requested although not loaded", script_nr);
- // This is perfectly valid SCI behaviour
- return;
- }
+ while (READ_SCI11ENDIAN_UINT16(seeker) == SCRIPT_OBJECT_MAGIC_NUMBER) {
+ reg_t reg = make_reg(segmentId, seeker - _buf);
+ Object *obj = scriptObjInit(reg);
- scr->decrementLockers(); // One less locker
+ // Copy base from species class, as we need its selector IDs
+ obj->setSuperClassSelector(
+ segMan->getClassAddress(obj->getSuperClassSelector().offset, SCRIPT_GET_LOCK, NULL_REG));
- if (scr->getLockers() > 0)
- return;
+ // 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();
+ const Object *classObj = segMan->getObject(classObject);
+ obj->setPropDictSelector(classObj->getPropDictSelector());
+ }
- // Free all classtable references to this script
- for (uint i = 0; i < segMan->classTableSize(); i++)
- if (segMan->getClass(i).reg.segment == segment)
- segMan->setClassOffset(i, NULL_REG);
+ // 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
+ // uses this selector together with -propDict- to compare classes.
+ // For the purpose of Obj::isKindOf, using the script number appears
+ // to be sufficient.
+ obj->setClassScriptSelector(make_reg(0, _nr));
- if (getSciVersion() < SCI_VERSION_1_1)
- script_uninstantiate_sci0(segMan, script_nr, segment);
- // FIXME: Add proper script uninstantiation for SCI 1.1
+ seeker += READ_SCI11ENDIAN_UINT16(seeker + 2) * 2;
+ }
- if (scr->getLockers())
- return; // if xxx.lockers > 0
+ relocate(make_reg(segmentId, READ_SCI11ENDIAN_UINT16(_heapStart)));
+}
+
+reg_t Script::findCanonicAddress(SegManager *segMan, reg_t addr) const {
+ addr.offset = 0;
+ return addr;
+}
- // Otherwise unload it completely
- // Explanation: I'm starting to believe that this work is done by SCI itself.
- scr->markDeleted();
+void Script::freeAtAddress(SegManager *segMan, reg_t addr) {
+ /*
+ debugC(2, kDebugLevelGC, "[GC] Freeing script %04x:%04x", PRINT_REG(addr));
+ if (_localsSegment)
+ debugC(2, kDebugLevelGC, "[GC] Freeing locals %04x:0000", _localsSegment);
+ */
+
+ if (_markedAsDeleted)
+ segMan->deallocateScript(_nr);
+}
- debugC(kDebugLevelScripts, "Unloaded script 0x%x.", script_nr);
+Common::Array<reg_t> Script::listAllDeallocatable(SegmentId segId) const {
+ const reg_t r = make_reg(segId, 0);
+ return Common::Array<reg_t>(&r, 1);
+}
- return;
+Common::Array<reg_t> Script::listAllOutgoingReferences(reg_t addr) const {
+ Common::Array<reg_t> tmp;
+ if (addr.offset <= _bufSize && addr.offset >= -SCRIPT_OBJECT_MAGIC_OFFSET && RAW_IS_OBJECT(_buf + addr.offset)) {
+ const Object *obj = getObject(addr.offset);
+ if (obj) {
+ // Note all local variables, if we have a local variable environment
+ if (_localsSegment)
+ tmp.push_back(make_reg(_localsSegment, 0));
+
+ for (uint i = 0; i < obj->getVarCount(); i++)
+ tmp.push_back(obj->getVariable(i));
+ } else {
+ error("Request for outgoing script-object reference at %04x:%04x failed", PRINT_REG(addr));
+ }
+ } else {
+ /* warning("Unexpected request for outgoing script-object references at %04x:%04x", PRINT_REG(addr));*/
+ /* Happens e.g. when we're looking into strings */
+ }
+ return tmp;
}
+Common::Array<reg_t> Script::listObjectReferences() const {
+ Common::Array<reg_t> tmp;
+
+ // Locals, if present
+ if (_localsSegment)
+ tmp.push_back(make_reg(_localsSegment, 0));
+
+ // All objects (may be classes, may be indirectly reachable)
+ ObjMap::iterator it;
+ const ObjMap::iterator end = _objects.end();
+ for (it = _objects.begin(); it != end; ++it) {
+ tmp.push_back(it->_value.getPos());
+ }
+
+ return tmp;
+}
} // End of namespace Sci
diff --git a/engines/sci/engine/script.h b/engines/sci/engine/script.h
index e94e9f64e6..3817f8aae1 100644
--- a/engines/sci/engine/script.h
+++ b/engines/sci/engine/script.h
@@ -27,14 +27,13 @@
#define SCI_ENGINE_SCRIPT_H
#include "common/str.h"
+#include "sci/engine/segment.h"
namespace Sci {
struct EngineState;
class ResourceManager;
-#define SCI_SCRIPTS_NR 1000
-
enum ScriptObjectTypes {
SCI_OBJ_TERMINATOR,
SCI_OBJ_OBJECT,
@@ -49,160 +48,214 @@ enum ScriptObjectTypes {
SCI_OBJ_LOCALVARS
};
-// Opcode formats
-enum opcode_format {
- Script_Invalid = -1,
- Script_None = 0,
- Script_Byte,
- Script_SByte,
- Script_Word,
- Script_SWord,
- Script_Variable,
- Script_SVariable,
- Script_SRelative,
- Script_Property,
- Script_Global,
- Script_Local,
- Script_Temp,
- Script_Param,
- Script_Offset,
- Script_End
-};
+typedef Common::HashMap<uint16, Object> ObjMap;
-enum sci_opcodes {
- op_bnot = 0x00, // 000
- op_add = 0x01, // 001
- op_sub = 0x02, // 002
- op_mul = 0x03, // 003
- op_div = 0x04, // 004
- op_mod = 0x05, // 005
- op_shr = 0x06, // 006
- op_shl = 0x07, // 007
- op_xor = 0x08, // 008
- op_and = 0x09, // 009
- op_or = 0x0a, // 010
- op_neg = 0x0b, // 011
- op_not = 0x0c, // 012
- op_eq_ = 0x0d, // 013
- op_ne_ = 0x0e, // 014
- op_gt_ = 0x0f, // 015
- op_ge_ = 0x10, // 016
- op_lt_ = 0x11, // 017
- op_le_ = 0x12, // 018
- op_ugt_ = 0x13, // 019
- op_uge_ = 0x14, // 020
- op_ult_ = 0x15, // 021
- op_ule_ = 0x16, // 022
- op_bt = 0x17, // 023
- op_bnt = 0x18, // 024
- op_jmp = 0x19, // 025
- op_ldi = 0x1a, // 026
- op_push = 0x1b, // 027
- op_pushi = 0x1c, // 028
- op_toss = 0x1d, // 029
- op_dup = 0x1e, // 030
- op_link = 0x1f, // 031
- op_call = 0x20, // 032
- op_callk = 0x21, // 033
- op_callb = 0x22, // 034
- op_calle = 0x23, // 035
- op_ret = 0x24, // 036
- op_send = 0x25, // 037
- // dummy 0x26, // 038
- // dummy 0x27, // 039
- op_class = 0x28, // 040
- // dummy 0x29, // 041
- op_self = 0x2a, // 042
- op_super = 0x2b, // 043
- op_rest = 0x2c, // 044
- op_lea = 0x2d, // 045
- op_selfID = 0x2e, // 046
- // dummy 0x2f // 047
- op_pprev = 0x30, // 048
- op_pToa = 0x31, // 049
- op_aTop = 0x32, // 050
- op_pTos = 0x33, // 051
- op_sTop = 0x34, // 052
- op_ipToa = 0x35, // 053
- op_dpToa = 0x36, // 054
- op_ipTos = 0x37, // 055
- op_dpTos = 0x38, // 056
- op_lofsa = 0x39, // 057
- op_lofss = 0x3a, // 058
- op_push0 = 0x3b, // 059
- op_push1 = 0x3c, // 060
- op_push2 = 0x3d, // 061
- op_pushSelf = 0x3e, // 062
- op_line = 0x3f, // 063
- op_lag = 0x40, // 064
- op_lal = 0x41, // 065
- op_lat = 0x42, // 066
- op_lap = 0x43, // 067
- op_lsg = 0x44, // 068
- op_lsl = 0x45, // 069
- op_lst = 0x46, // 070
- op_lsp = 0x47, // 071
- op_lagi = 0x48, // 072
- op_lali = 0x49, // 073
- op_lati = 0x4a, // 074
- op_lapi = 0x4b, // 075
- op_lsgi = 0x4c, // 076
- op_lsli = 0x4d, // 077
- op_lsti = 0x4e, // 078
- op_lspi = 0x4f, // 079
- op_sag = 0x50, // 080
- op_sal = 0x51, // 081
- op_sat = 0x52, // 082
- op_sap = 0x53, // 083
- op_ssg = 0x54, // 084
- op_ssl = 0x55, // 085
- op_sst = 0x56, // 086
- op_ssp = 0x57, // 087
- op_sagi = 0x58, // 088
- op_sali = 0x59, // 089
- op_sati = 0x5a, // 090
- op_sapi = 0x5b, // 091
- op_ssgi = 0x5c, // 092
- op_ssli = 0x5d, // 093
- op_ssti = 0x5e, // 094
- op_sspi = 0x5f, // 095
- op_plusag = 0x60, // 096
- op_plusal = 0x61, // 097
- op_plusat = 0x62, // 098
- op_plusap = 0x63, // 099
- op_plussg = 0x64, // 100
- op_plussl = 0x65, // 101
- op_plusst = 0x66, // 102
- op_plussp = 0x67, // 103
- op_plusagi = 0x68, // 104
- op_plusali = 0x69, // 105
- op_plusati = 0x6a, // 106
- op_plusapi = 0x6b, // 107
- op_plussgi = 0x6c, // 108
- op_plussli = 0x6d, // 109
- op_plussti = 0x6e, // 110
- op_plusspi = 0x6f, // 111
- op_minusag = 0x70, // 112
- op_minusal = 0x71, // 113
- op_minusat = 0x72, // 114
- op_minusap = 0x73, // 115
- op_minussg = 0x74, // 116
- op_minussl = 0x75, // 117
- op_minusst = 0x76, // 118
- op_minussp = 0x77, // 119
- op_minusagi = 0x78, // 120
- op_minusali = 0x79, // 121
- op_minusati = 0x7a, // 122
- op_minusapi = 0x7b, // 123
- op_minussgi = 0x7c, // 124
- op_minussli = 0x7d, // 125
- op_minussti = 0x7e, // 126
- op_minusspi = 0x7f // 127
-};
+class Script : public SegmentObj {
+private:
+ int _nr; /**< Script number */
+ byte *_buf; /**< Static data buffer, or NULL if not used */
+ byte *_heapStart; /**< Start of heap if SCI1.1, NULL otherwise */
+
+ int _lockers; /**< Number of classes and objects that require this script */
+ size_t _scriptSize;
+ size_t _heapSize;
+ uint16 _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 */
+
+ int _localsOffset;
+ uint16 _localsCount;
+
+ bool _markedAsDeleted;
+
+public:
+ /**
+ * Table for objects, contains property variables.
+ * Indexed by the TODO offset.
+ */
+ ObjMap _objects;
+ SegmentId _localsSegment; /**< The local variable segment */
+ LocalVariables *_localsBlock;
+
+public:
+ int getLocalsOffset() const { return _localsOffset; }
+ uint16 getLocalsCount() const { return _localsCount; }
+
+ uint32 getScriptSize() const { return _scriptSize; }
+ uint32 getHeapSize() const { return _heapSize; }
+ uint32 getBufSize() const { return _bufSize; }
+ const byte *getBuf(uint offset = 0) const { return _buf + offset; }
+
+ int getScriptNumber() const { return _nr; }
+
+public:
+ Script();
+ ~Script();
+
+ void freeScript();
+ 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) const;
+ virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr);
+ virtual Common::Array<reg_t> listAllDeallocatable(SegmentId segId) const;
+ virtual Common::Array<reg_t> listAllOutgoingReferences(reg_t object) const;
+
+ /**
+ * Return a list of all references to objects in this script
+ * (and also to the locals segment, if any).
+ * Used by the garbage collector.
+ * @return a list of outgoing references within the object
+ */
+ Common::Array<reg_t> listObjectReferences() const;
+
+ virtual void saveLoadWithSerializer(Common::Serializer &ser);
+
+ Object *getObject(uint16 offset);
+ const Object *getObject(uint16 offset) const;
+
+ /**
+ * Initializes an object within the segment manager
+ * @param obj_pos Location (segment, offset) of the object. It must
+ * point to the beginning of the script/class block
+ * (as opposed to what the VM considers to be the
+ * object location)
+ * @returns A newly created Object describing the object,
+ * stored within the relevant script
+ */
+ Object *scriptObjInit(reg_t obj_pos, bool fullObjectInit = true);
+
+ /**
+ * Removes a script object
+ * @param obj_pos Location (segment, offset) of the object.
+ */
+ void scriptObjRemove(reg_t obj_pos);
-extern opcode_format g_opcode_formats[128][4];
+ /**
+ * Initializes the script's local variables
+ * @param segMan A reference to the segment manager
+ */
+ void initialiseLocals(SegManager *segMan);
-void script_adjust_opcode_formats(EngineState *s);
+ /**
+ * Adds the script's classes to the segment manager's class table
+ * @param segMan A reference to the segment manager
+ */
+ void initialiseClasses(SegManager *segMan);
+
+ /**
+ * Initializes the script's objects (SCI0)
+ * @param segMan A reference to the segment manager
+ * @param segmentId The script's segment id
+ */
+ void initialiseObjectsSci0(SegManager *segMan, SegmentId segmentId);
+
+ /**
+ * Initializes the script's objects (SCI1.1+)
+ * @param segMan A reference to the segment manager
+ * @param segmentId The script's segment id
+ */
+ void initialiseObjectsSci11(SegManager *segMan, SegmentId segmentId);
+
+ // script lock operations
+
+ /** Increments the number of lockers of this script by one. */
+ void incrementLockers();
+
+ /** Decrements the number of lockers of this script by one. */
+ void decrementLockers();
+
+ /**
+ * Retrieves the number of locks held on this script.
+ * @return the number of locks held on the previously identified script
+ */
+ int getLockers() const;
+
+ /** Sets the number of locks held on this script. */
+ 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.
+ */
+ const byte *getSynonyms() const { return _synonyms; }
+
+ /**
+ * Retrieves the number of synonyms associated with this script.
+ * @return the number of synonyms associated with this script
+ */
+ uint16 getSynonymsNr() const { return _numSynonyms; }
+
+ /**
+ * Validate whether the specified public function is exported by
+ * the script in the specified segment.
+ * @param pubfunct Index of the function to validate
+ * @return NULL if the public function is invalid, its
+ * offset into the script's segment otherwise
+ */
+ uint16 validateExportFunc(int pubfunct);
+
+ /**
+ * 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
+ * either unreachable (resulting in its eventual deletion) or reloaded (resulting
+ * in its data being updated).
+ */
+ void markDeleted() {
+ _markedAsDeleted = true;
+ }
+
+ /**
+ * Determines whether the script is marked as being deleted.
+ */
+ bool isMarkedAsDeleted() const {
+ return _markedAsDeleted;
+ }
+
+ /**
+ * Copies a byte string into a script's heap representation.
+ * @param dst script-relative offset of the destination area
+ * @param src pointer to the data source location
+ * @param n number of bytes to copy
+ */
+ void mcpyInOut(int dst, const void *src, size_t n);
+
+ /**
+ * Finds the pointer where a block of a specific type starts from
+ */
+ byte *findBlock(int type);
+
+private:
+ /**
+ * Processes a relocation block witin a script
+ * This function is idempotent, but it must only be called after all
+ * objects have been instantiated, or a run-time error will occur.
+ * @param obj_pos Location (segment, offset) of the block
+ * @return Location of the relocation block
+ */
+ void relocate(reg_t block);
+
+ bool relocateLocal(SegmentId segment, int location);
+};
} // End of namespace Sci
diff --git a/engines/sci/engine/scriptdebug.cpp b/engines/sci/engine/scriptdebug.cpp
index b465ab3d4e..915a6fa994 100644
--- a/engines/sci/engine/scriptdebug.cpp
+++ b/engines/sci/engine/scriptdebug.cpp
@@ -63,15 +63,11 @@ const char *opcodeNames[] = {
"-sli", "-sti", "-spi"
};
-extern const char *selector_name(EngineState *s, int selector);
-
-DebugState g_debugState;
-
// 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);
Script *script_entity = NULL;
- byte *scr;
+ const byte *scr;
int scr_size;
reg_t retval = make_reg(pos.segment, pos.offset + 1);
uint16 param_value;
@@ -84,7 +80,7 @@ reg_t disassemble(EngineState *s, reg_t pos, int print_bw_tag, int print_bytecod
} else
script_entity = (Script *)mobj;
- scr = script_entity->_buf;
+ scr = script_entity->getBuf();
scr_size = script_entity->getBufSize();
if (pos.offset >= scr_size) {
@@ -197,7 +193,7 @@ reg_t disassemble(EngineState *s, reg_t pos, int print_bw_tag, int print_bytecod
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(" (%s)", g_sci->getKernel()->getSelectorName(obj->propertyOffsetToId(s->_segMan, scr[pos.offset + 1])).c_str());
}
}
@@ -205,12 +201,12 @@ reg_t disassemble(EngineState *s, reg_t pos, int print_bw_tag, int print_bytecod
if (pos == s->xs->addr.pc) { // Extra information if debugging the current opcode
if (opcode == op_callk) {
- int stackframe = (scr[pos.offset + 2] >> 1) + (s->restAdjustCur);
+ int stackframe = (scr[pos.offset + 2] >> 1) + (s->restAdjust);
int argc = ((s->xs->sp)[- stackframe - 1]).offset;
bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY);
if (!oldScriptHeader)
- argc += (s->restAdjustCur);
+ argc += (s->restAdjust);
printf(" Kernel params: (");
@@ -221,7 +217,7 @@ reg_t disassemble(EngineState *s, reg_t pos, int print_bw_tag, int print_bytecod
}
printf(")\n");
} else if ((opcode == op_send) || (opcode == op_self)) {
- int restmod = s->restAdjustCur;
+ int restmod = s->restAdjust;
int stackframe = (scr[pos.offset + 1] >> 1) + restmod;
reg_t *sb = s->xs->sp;
uint16 selector;
@@ -244,7 +240,7 @@ reg_t disassemble(EngineState *s, reg_t pos, int print_bw_tag, int print_bytecod
if (!name)
name = "<invalid>";
- printf(" %s::%s[", name, (selector > kernel->getSelectorNamesSize()) ? "<invalid>" : selector_name(s, selector));
+ printf(" %s::%s[", name, g_sci->getKernel()->getSelectorName(selector).c_str());
switch (lookupSelector(s->_segMan, called_obj_addr, selector, 0, &fun_ref)) {
case kSelectorMethod:
@@ -279,90 +275,76 @@ reg_t disassemble(EngineState *s, reg_t pos, int print_bw_tag, int print_bytecod
}
-void script_debug(EngineState *s) {
- // Do we support a separate console?
+void SciEngine::scriptDebug() {
+ EngineState *s = _gamestate;
+ if (_debugState.seeking && !_debugState.breakpointWasHit) { // Are we looking for something special?
+ if (_debugState.seeking == kDebugSeekStepOver) {
+ // are we above seek-level? resume then
+ if (_debugState.seekLevel < (int)s->_executionStack.size())
+ return;
+ _debugState.seeking = kDebugSeekNothing;
+ }
-#if 0
- if (sci_debug_flags & _DEBUG_FLAG_LOGGING) {
- printf("%d: acc=%04x:%04x ", script_step_counter, PRINT_REG(s->r_acc));
- 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
+ if (_debugState.seeking != kDebugSeekNothing) {
+ const reg_t pc = s->xs->addr.pc;
+ SegmentObj *mobj = s->_segMan->getSegment(pc.segment, SEG_TYPE_SCRIPT);
+
+ if (mobj) {
+ Script *scr = (Script *)mobj;
+ const byte *code_buf = scr->getBuf();
+ int code_buf_size = scr->getBufSize();
+ int opcode = pc.offset >= code_buf_size ? 0 : code_buf[pc.offset];
+ int op = opcode >> 1;
+ int paramb1 = pc.offset + 1 >= code_buf_size ? 0 : code_buf[pc.offset + 1];
+ int paramf1 = (opcode & 1) ? paramb1 : (pc.offset + 2 >= code_buf_size ? 0 : (int16)READ_SCI11ENDIAN_UINT16(code_buf + pc.offset + 1));
+
+ switch (_debugState.seeking) {
+ case kDebugSeekSpecialCallk:
+ if (paramb1 != _debugState.seekSpecial)
+ return;
+
+ case kDebugSeekCallk:
+ if (op != op_callk)
+ return;
+ break;
-#if 0
- if (!g_debugState.debugging)
- return;
-#endif
-
- if (g_debugState.seeking && !g_debugState.breakpointWasHit) { // Are we looking for something special?
- 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->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 = 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:
- if (paramb1 != g_debugState.seekSpecial)
- return;
-
- case kDebugSeekCallk: {
- if (op != op_callk)
- return;
- break;
- }
+ case kDebugSeekLevelRet:
+ if ((op != op_ret) || (_debugState.seekLevel < (int)s->_executionStack.size()-1))
+ return;
+ break;
- case kDebugSeekLevelRet: {
- if ((op != op_ret) || (g_debugState.seekLevel < (int)s->_executionStack.size()-1))
- return;
- break;
- }
+ case kDebugSeekGlobal:
+ if (op < op_sag)
+ return;
+ if ((op & 0x3) > 1)
+ return; // param or temp
+ if ((op & 0x3) && s->_executionStack.back().local_segment > 0)
+ return; // locals and not running in script.000
+ if (paramf1 != _debugState.seekSpecial)
+ return; // CORRECT global?
+ break;
- case kDebugSeekGlobal:
- if (op < op_sag)
- return;
- if ((op & 0x3) > 1)
- return; // param or temp
- if ((op & 0x3) && s->_executionStack.back().local_segment > 0)
- return; // locals and not running in script.000
- if (paramf1 != g_debugState.seekSpecial)
- return; // CORRECT global?
- break;
-
- case kDebugSeekSO:
- // FIXME: Unhandled?
- break;
-
- case kDebugSeekNothing:
- // We seek nothing, so just continue
- break;
- }
+ default:
+ break;
+ }
- g_debugState.seeking = kDebugSeekNothing;
- // OK, found whatever we were looking for
+ _debugState.seeking = kDebugSeekNothing;
+ }
}
+ // OK, found whatever we were looking for
}
- printf("Step #%d\n", s->script_step_counter);
+ printf("Step #%d\n", s->scriptStepCounter);
disassemble(s, s->xs->addr.pc, 0, 1);
- if (g_debugState.runningStep) {
- g_debugState.runningStep--;
+ if (_debugState.runningStep) {
+ _debugState.runningStep--;
return;
}
- g_debugState.debugging = false;
+ _debugState.debugging = false;
- Console *con = ((Sci::SciEngine*)g_engine)->getSciDebugger();
- con->attach();
+ _console->attach();
}
void Kernel::dumpScriptObject(char *data, int seeker, int objsize) {
@@ -491,19 +473,17 @@ void Kernel::dissectScript(int scriptNumber, Vocabulary *vocab) {
dumpScriptObject((char *)script->data, seeker, objsize);
break;
- case SCI_OBJ_CODE: {
+ case SCI_OBJ_CODE:
printf("Code\n");
Common::hexdump(script->data + seeker, objsize - 4, 16, seeker);
- };
- break;
+ break;
- case 3: {
+ case 3:
printf("<unknown>\n");
Common::hexdump(script->data + seeker, objsize - 4, 16, seeker);
- };
- break;
+ break;
- case SCI_OBJ_SAID: {
+ case SCI_OBJ_SAID:
printf("Said\n");
Common::hexdump(script->data + seeker, objsize - 4, 16, seeker);
@@ -551,46 +531,40 @@ void Kernel::dissectScript(int scriptNumber, Vocabulary *vocab) {
}
}
printf("\n");
- }
- break;
+ break;
- case SCI_OBJ_STRINGS: {
+ case SCI_OBJ_STRINGS:
printf("Strings\n");
while (script->data [seeker]) {
printf("%04x: %s\n", seeker, script->data + seeker);
seeker += strlen((char *)script->data + seeker) + 1;
}
seeker++; // the ending zero byte
- };
- break;
+ break;
case SCI_OBJ_CLASS:
dumpScriptClass((char *)script->data, seeker, objsize);
break;
- case SCI_OBJ_EXPORTS: {
+ case SCI_OBJ_EXPORTS:
printf("Exports\n");
Common::hexdump((unsigned char *)script->data + seeker, objsize - 4, 16, seeker);
- };
- break;
+ break;
- case SCI_OBJ_POINTERS: {
+ case SCI_OBJ_POINTERS:
printf("Pointers\n");
Common::hexdump(script->data + seeker, objsize - 4, 16, seeker);
- };
- break;
+ break;
- case 9: {
+ case 9:
printf("<unknown>\n");
Common::hexdump(script->data + seeker, objsize - 4, 16, seeker);
- };
- break;
+ break;
- case SCI_OBJ_LOCALVARS: {
+ case SCI_OBJ_LOCALVARS:
printf("Local vars\n");
Common::hexdump(script->data + seeker, objsize - 4, 16, seeker);
- };
- break;
+ break;
default:
printf("Unsupported!\n");
diff --git a/engines/sci/engine/seg_manager.cpp b/engines/sci/engine/seg_manager.cpp
index 4d3e6f754e..ef2279e492 100644
--- a/engines/sci/engine/seg_manager.cpp
+++ b/engines/sci/engine/seg_manager.cpp
@@ -26,6 +26,7 @@
#include "sci/sci.h"
#include "sci/engine/seg_manager.h"
#include "sci/engine/state.h"
+#include "sci/engine/script.h"
namespace Sci {
@@ -39,14 +40,14 @@ enum {
SegManager::SegManager(ResourceManager *resMan) {
_heap.push_back(0);
- Clones_seg_id = 0;
- Lists_seg_id = 0;
- Nodes_seg_id = 0;
- Hunks_seg_id = 0;
+ _clonesSegId = 0;
+ _listsSegId = 0;
+ _nodesSegId = 0;
+ _hunksSegId = 0;
#ifdef ENABLE_SCI32
- Arrays_seg_id = 0;
- String_seg_id = 0;
+ _arraysSegId = 0;
+ _stringSegId = 0;
#endif
_resMan = resMan;
@@ -70,10 +71,10 @@ void SegManager::resetSegMan() {
// And reinitialize
_heap.push_back(0);
- Clones_seg_id = 0;
- Lists_seg_id = 0;
- Nodes_seg_id = 0;
- Hunks_seg_id = 0;
+ _clonesSegId = 0;
+ _listsSegId = 0;
+ _nodesSegId = 0;
+ _hunksSegId = 0;
// Reinitialize class table
_classTable.clear();
@@ -81,10 +82,10 @@ void SegManager::resetSegMan() {
}
void SegManager::initSysStrings() {
- sysStrings = (SystemStrings *)allocSegment(new SystemStrings(), &sysStringsSegment);
+ _sysStrings = (SystemStrings *)allocSegment(new SystemStrings(), &_sysStringsSegId);
// Allocate static buffer for savegame and CWD directories
- SystemString *strSaveDir = &sysStrings->_strings[SYS_STRING_SAVEDIR];
+ SystemString *strSaveDir = getSystemString(SYS_STRING_SAVEDIR);
strSaveDir->_name = "savedir";
strSaveDir->_maxSize = MAX_SAVE_DIR_SIZE;
strSaveDir->_value = (char *)calloc(MAX_SAVE_DIR_SIZE, sizeof(char));
@@ -93,23 +94,17 @@ void SegManager::initSysStrings() {
::strcpy(strSaveDir->_value, "");
// Allocate static buffer for the parser base
- SystemString *strParserBase = &sysStrings->_strings[SYS_STRING_PARSER_BASE];
+ SystemString *strParserBase = getSystemString(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.
- // Optimizations are possible and easy, but I refrain from doing any until this
- // refactoring is complete.
- // One very simple yet probably effective approach would be to add
- // "int firstFreeSegment", which is updated when allocating/freeing segments.
- // There are some scenarios where it performs badly, but we should first check
- // whether those occur at all. If so, there are easy refinements that deal
- // with this. Finally, we could switch to a more elaborate scheme,
- // such as keeping track of all free segments. But I doubt this is necessary.
+ // The following 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. Optimizations are possible and easy, but I'll refrain
+ // from attempting any until we determine we actually need it.
uint seg = 1;
while (seg < _heap.size() && _heap[seg]) {
++seg;
@@ -162,7 +157,7 @@ int SegManager::deallocate(SegmentId seg, bool recursive) {
if (mobj->getType() == SEG_TYPE_SCRIPT) {
Script *scr = (Script *)mobj;
- _scriptSegMap.erase(scr->_nr);
+ _scriptSegMap.erase(scr->getScriptNumber());
if (recursive && scr->_localsSegment)
deallocate(scr->_localsSegment, recursive);
}
@@ -173,12 +168,12 @@ int SegManager::deallocate(SegmentId seg, bool recursive) {
return 1;
}
-bool SegManager::isHeapObject(reg_t pos) {
+bool SegManager::isHeapObject(reg_t pos) const {
const Object *obj = getObject(pos);
if (obj == NULL || (obj && obj->isFreed()))
return false;
Script *scr = getScriptIfLoaded(pos.segment);
- return !(scr && scr->_markedAsDeleted);
+ return !(scr && scr->isMarkedAsDeleted());
}
void SegManager::deallocateScript(int script_nr) {
@@ -199,36 +194,36 @@ Script *SegManager::getScript(const SegmentId seg) {
return (Script *)_heap[seg];
}
-Script *SegManager::getScriptIfLoaded(const SegmentId seg) {
+Script *SegManager::getScriptIfLoaded(const SegmentId seg) const {
if (seg < 1 || (uint)seg >= _heap.size() || !_heap[seg] || _heap[seg]->getType() != SEG_TYPE_SCRIPT)
return 0;
return (Script *)_heap[seg];
}
-SegmentId SegManager::findSegmentByType(int type) {
+SegmentId SegManager::findSegmentByType(int type) const {
for (uint i = 0; i < _heap.size(); i++)
if (_heap[i] && _heap[i]->getType() == type)
return i;
return 0;
}
-SegmentObj *SegManager::getSegmentObj(SegmentId seg) {
+SegmentObj *SegManager::getSegmentObj(SegmentId seg) const {
if (seg < 1 || (uint)seg >= _heap.size() || !_heap[seg])
return 0;
return _heap[seg];
}
-SegmentType SegManager::getSegmentType(SegmentId seg) {
+SegmentType SegManager::getSegmentType(SegmentId seg) const {
if (seg < 1 || (uint)seg >= _heap.size() || !_heap[seg])
return SEG_TYPE_INVALID;
return _heap[seg]->getType();
}
-SegmentObj *SegManager::getSegment(SegmentId seg, SegmentType type) {
+SegmentObj *SegManager::getSegment(SegmentId seg, SegmentType type) const {
return getSegmentType(seg) == type ? _heap[seg] : NULL;
}
-Object *SegManager::getObject(reg_t pos) {
+Object *SegManager::getObject(reg_t pos) const {
SegmentObj *mobj = getSegmentObj(pos.segment);
Object *obj = NULL;
@@ -242,7 +237,7 @@ Object *SegManager::getObject(reg_t pos) {
} else if (mobj->getType() == SEG_TYPE_SCRIPT) {
Script *scr = (Script *)mobj;
if (pos.offset <= scr->getBufSize() && pos.offset >= -SCRIPT_OBJECT_MAGIC_OFFSET
- && RAW_IS_OBJECT(scr->_buf + pos.offset)) {
+ && RAW_IS_OBJECT(scr->getBuf(pos.offset))) {
obj = scr->getObject(pos.offset);
}
}
@@ -268,78 +263,55 @@ const char *SegManager::getObjectName(reg_t pos) {
}
reg_t SegManager::findObjectByName(const Common::String &name, int index) {
- reg_t retVal = NULL_REG;
+ Common::Array<reg_t> result;
+ uint i;
// Now all values are available; iterate over all objects.
- int timesFound = 0;
- for (uint i = 0; i < _heap.size(); i++) {
- SegmentObj *mobj = _heap[i];
- int idx = 0;
- int max_index = 0;
- ObjMap::iterator it;
- Script *scr = 0;
- CloneTable *ct = 0;
-
- if (mobj) {
- if (mobj->getType() == SEG_TYPE_SCRIPT) {
- scr = (Script *)mobj;
- max_index = scr->_objects.size();
- it = scr->_objects.begin();
- } else if (mobj->getType() == SEG_TYPE_CLONES) {
- ct = (CloneTable *)mobj;
- max_index = ct->_table.size();
- }
- }
+ for (i = 0; i < _heap.size(); i++) {
+ const SegmentObj *mobj = _heap[i];
+
+ if (!mobj)
+ continue;
+
+ reg_t objpos = make_reg(i, 0);
- // It's a script or a clone table, scan all objects in it
- for (; idx < max_index; ++idx) {
- const Object *obj = NULL;
- reg_t objpos;
- objpos.offset = 0;
- objpos.segment = i;
-
- if (mobj->getType() == SEG_TYPE_SCRIPT) {
- obj = &(it->_value);
- objpos.offset = obj->getPos().offset;
- ++it;
- } else if (mobj->getType() == SEG_TYPE_CLONES) {
+ if (mobj->getType() == SEG_TYPE_SCRIPT) {
+ // It's a script, scan all objects in it
+ const Script *scr = (const Script *)mobj;
+ for (ObjMap::const_iterator it = scr->_objects.begin(); it != scr->_objects.end(); ++it) {
+ objpos.offset = it->_value.getPos().offset;
+ if (name == getObjectName(objpos))
+ result.push_back(objpos);
+ }
+ } else if (mobj->getType() == SEG_TYPE_CLONES) {
+ // It's clone table, scan all objects in it
+ const CloneTable *ct = (const CloneTable *)mobj;
+ for (uint idx = 0; idx < ct->_table.size(); ++idx) {
if (!ct->isValidEntry(idx))
continue;
- obj = &(ct->_table[idx]);
- objpos.offset = idx;
- }
- const char *objname = getObjectName(objpos);
- if (name == objname) {
- // Found a match!
- if ((index < 0) && (timesFound > 0)) {
- if (timesFound == 1) {
- // First time we realized the ambiguity
- printf("Ambiguous:\n");
- printf(" %3x: [%04x:%04x] %s\n", 0, PRINT_REG(retVal), name.c_str());
- }
- printf(" %3x: [%04x:%04x] %s\n", timesFound, PRINT_REG(objpos), name.c_str());
- }
- if (index < 0 || timesFound == index)
- retVal = objpos;
- ++timesFound;
+ objpos.offset = idx;
+ if (name == getObjectName(objpos))
+ result.push_back(objpos);
}
}
-
}
- if (!timesFound)
+ if (result.empty())
return NULL_REG;
- if (timesFound > 1 && index < 0) {
- printf("Ambiguous: Aborting.\n");
+ if (result.size() > 1 && index < 0) {
+ printf("Ambiguous:\n");
+ for (i = 0; i < result.size(); i++)
+ printf(" %3x: [%04x:%04x] %s\n", i, PRINT_REG(result[i]), name.c_str());
return NULL_REG; // Ambiguous
}
- if (timesFound <= index)
+ if (index < 0)
+ return result[0];
+ else if (result.size() <= (uint)index)
return NULL_REG; // Not found
-
- return retVal;
+ return result[index];
}
// validate the seg
@@ -366,7 +338,7 @@ SegmentId SegManager::getScriptSegment(int script_nr, ScriptLoadType load) {
SegmentId segment;
if ((load & SCRIPT_GET_LOAD) == SCRIPT_GET_LOAD)
- script_instantiate(_resMan, this, script_nr);
+ instantiateScript(script_nr);
segment = getScriptSegment(script_nr);
@@ -377,8 +349,8 @@ SegmentId SegManager::getScriptSegment(int script_nr, ScriptLoadType load) {
return segment;
}
-LocalVariables *SegManager::allocLocalsSegment(Script *scr, int count) {
- if (!count) { // No locals
+LocalVariables *SegManager::allocLocalsSegment(Script *scr) {
+ if (!scr->getLocalsCount()) { // No locals
scr->_localsSegment = 0;
scr->_localsBlock = NULL;
return NULL;
@@ -389,13 +361,13 @@ LocalVariables *SegManager::allocLocalsSegment(Script *scr, int count) {
locals = (LocalVariables *)_heap[scr->_localsSegment];
VERIFY(locals != NULL, "Re-used locals segment was NULL'd out");
VERIFY(locals->getType() == SEG_TYPE_LOCALS, "Re-used locals segment did not consist of local variables");
- VERIFY(locals->script_id == scr->_nr, "Re-used locals segment belonged to other script");
+ VERIFY(locals->script_id == scr->getScriptNumber(), "Re-used locals segment belonged to other script");
} else
locals = (LocalVariables *)allocSegment(new LocalVariables(), &scr->_localsSegment);
scr->_localsBlock = locals;
- locals->script_id = scr->_nr;
- locals->_locals.resize(count);
+ locals->script_id = scr->getScriptNumber();
+ locals->_locals.resize(scr->getLocalsCount());
return locals;
}
@@ -408,6 +380,12 @@ DataStack *SegManager::allocateStack(int size, SegmentId *segid) {
retval->_entries = (reg_t *)calloc(size, sizeof(reg_t));
retval->_capacity = size;
+ // SSCI initializes the stack with "S" characters (uppercase S in SCI0-SCI1,
+ // lowercase s in SCI0 and SCI11) - probably stands for "stack"
+ byte filler = (getSciVersion() >= SCI_VERSION_01 && getSciVersion() <= SCI_VERSION_1_LATE) ? 'S' : 's';
+ for (int i = 0; i < size; i++)
+ retval->_entries[i] = make_reg(0, filler);
+
return retval;
}
@@ -431,13 +409,13 @@ reg_t SegManager::allocateHunkEntry(const char *hunk_type, int size) {
HunkTable *table;
int offset;
- if (!Hunks_seg_id)
- allocSegment(new HunkTable(), &(Hunks_seg_id));
- table = (HunkTable *)_heap[Hunks_seg_id];
+ if (!_hunksSegId)
+ allocSegment(new HunkTable(), &(_hunksSegId));
+ table = (HunkTable *)_heap[_hunksSegId];
offset = table->allocEntry();
- reg_t addr = make_reg(Hunks_seg_id, offset);
+ reg_t addr = make_reg(_hunksSegId, offset);
Hunk *h = &(table->_table[offset]);
if (!h)
@@ -454,7 +432,7 @@ byte *SegManager::getHunkPointer(reg_t addr) {
HunkTable *ht = (HunkTable *)getSegment(addr.segment, SEG_TYPE_HUNK);
if (!ht || !ht->isValidEntry(addr.offset)) {
- warning("getHunkPointer() with invalid handle");
+ // Valid SCI behavior, e.g. when loading/quitting
return NULL;
}
@@ -465,61 +443,28 @@ Clone *SegManager::allocateClone(reg_t *addr) {
CloneTable *table;
int offset;
- if (!Clones_seg_id)
- table = (CloneTable *)allocSegment(new CloneTable(), &(Clones_seg_id));
+ if (!_clonesSegId)
+ table = (CloneTable *)allocSegment(new CloneTable(), &(_clonesSegId));
else
- table = (CloneTable *)_heap[Clones_seg_id];
+ table = (CloneTable *)_heap[_clonesSegId];
offset = table->allocEntry();
- *addr = make_reg(Clones_seg_id, offset);
+ *addr = make_reg(_clonesSegId, offset);
return &(table->_table[offset]);
}
-void SegManager::reconstructClones() {
- for (uint i = 0; i < _heap.size(); i++) {
- if (_heap[i]) {
- SegmentObj *mobj = _heap[i];
- if (mobj->getType() == SEG_TYPE_CLONES) {
- CloneTable *ct = (CloneTable *)mobj;
-
- for (uint j = 0; j < ct->_table.size(); j++) {
- // Check if the clone entry is used
- uint entryNum = (uint)ct->first_free;
- bool isUsed = true;
- while (entryNum != ((uint) CloneTable::HEAPENTRY_INVALID)) {
- if (entryNum == j) {
- isUsed = false;
- break;
- }
- entryNum = ct->_table[entryNum].next_free;
- }
-
- if (!isUsed)
- continue;
-
- CloneTable::Entry &seeker = ct->_table[j];
- const Object *baseObj = getObject(seeker.getSpeciesSelector());
- seeker.cloneFromObject(baseObj);
- if (!baseObj)
- warning("Clone entry without a base class: %d", j);
- } // end for
- } // end if
- } // end if
- } // end for
-}
-
List *SegManager::allocateList(reg_t *addr) {
ListTable *table;
int offset;
- if (!Lists_seg_id)
- allocSegment(new ListTable(), &(Lists_seg_id));
- table = (ListTable *)_heap[Lists_seg_id];
+ if (!_listsSegId)
+ allocSegment(new ListTable(), &(_listsSegId));
+ table = (ListTable *)_heap[_listsSegId];
offset = table->allocEntry();
- *addr = make_reg(Lists_seg_id, offset);
+ *addr = make_reg(_listsSegId, offset);
return &(table->_table[offset]);
}
@@ -527,36 +472,36 @@ Node *SegManager::allocateNode(reg_t *addr) {
NodeTable *table;
int offset;
- if (!Nodes_seg_id)
- allocSegment(new NodeTable(), &(Nodes_seg_id));
- table = (NodeTable *)_heap[Nodes_seg_id];
+ if (!_nodesSegId)
+ allocSegment(new NodeTable(), &(_nodesSegId));
+ table = (NodeTable *)_heap[_nodesSegId];
offset = table->allocEntry();
- *addr = make_reg(Nodes_seg_id, offset);
+ *addr = make_reg(_nodesSegId, offset);
return &(table->_table[offset]);
}
reg_t SegManager::newNode(reg_t value, reg_t key) {
- reg_t nodebase;
- Node *n = allocateNode(&nodebase);
+ reg_t nodeRef;
+ Node *n = allocateNode(&nodeRef);
n->pred = n->succ = NULL_REG;
n->key = key;
n->value = value;
- return nodebase;
+ return nodeRef;
}
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));
+ error("Attempt to use non-list %04x:%04x as list", PRINT_REG(addr));
return NULL;
}
ListTable *lt = (ListTable *)_heap[addr.segment];
if (!lt->isValidEntry(addr.offset)) {
- warning("Attempt to use non-list %04x:%04x as list", PRINT_REG(addr));
+ error("Attempt to use non-list %04x:%04x as list", PRINT_REG(addr));
return NULL;
}
@@ -567,18 +512,17 @@ Node *SegManager::lookupNode(reg_t addr) {
if (addr.isNull())
return NULL; // Non-error null
- if (getSegmentType(addr.segment) != SEG_TYPE_NODES) {
- // FIXME: This occurs right at the beginning of SQ4, when walking north from the first screen. It doesn't
- // seem to have any apparent ill-effects, though, so it's been changed to non-fatal, for now
- //error("%s, L%d: Attempt to use non-node %04x:%04x as list node", __FILE__, __LINE__, PRINT_REG(addr));
- warning("Attempt to use non-node %04x:%04x as list node", PRINT_REG(addr));
+ SegmentType type = getSegmentType(addr.segment);
+
+ if (type != SEG_TYPE_NODES) {
+ error("Attempt to use non-node %04x:%04x (type %d) as list node", PRINT_REG(addr), type);
return NULL;
}
NodeTable *nt = (NodeTable *)_heap[addr.segment];
if (!nt->isValidEntry(addr.offset)) {
- warning("Attempt to use non-node %04x:%04x as list node", PRINT_REG(addr));
+ error("Attempt to use invalid or discarded reference %04x:%04x as list node", PRINT_REG(addr));
return NULL;
}
@@ -606,7 +550,7 @@ static void *derefPtr(SegManager *segMan, reg_t pointer, int entries, bool wantR
if (ret.isRaw != wantRaw) {
warning("Dereferencing pointer %04x:%04x (type %d) which is %s, but expected %s", PRINT_REG(pointer),
- segMan->_heap[pointer.segment]->getType(),
+ segMan->getSegmentType(pointer.segment),
ret.isRaw ? "raw" : "not raw",
wantRaw ? "raw" : "not raw");
}
@@ -646,8 +590,12 @@ static inline char getChar(const SegmentRef &ref, uint offset) {
reg_t val = ref.reg[offset / 2];
+ // segment 0xFFFF means that the scripts are using uninitialized temp-variable space
+ // we can safely ignore this, if it isn't one of the first 2 chars.
+ // foreign lsl3 uses kFileIO(readraw) and then immediately uses kReadNumber right at the start
if (val.segment != 0)
- warning("Attempt to read character from non-raw data");
+ if (!((val.segment == 0xFFFF) && (offset > 1)))
+ warning("Attempt to read character from non-raw data");
return (offset & 1 ? val.offset >> 8 : val.offset & 0xff);
}
@@ -695,6 +643,14 @@ void SegManager::strncpy(reg_t dest, const char* src, size_t n) {
}
void SegManager::strncpy(reg_t dest, reg_t src, size_t n) {
+ if (src.isNull()) {
+ // Clear target string instead.
+ if (n > 0)
+ strcpy(dest, "");
+
+ return; // empty text
+ }
+
SegmentRef dest_r = dereference(dest);
const SegmentRef src_r = dereference(src);
if (!src_r.isValid()) {
@@ -822,6 +778,9 @@ void SegManager::memcpy(byte *dest, reg_t src, size_t n) {
}
size_t SegManager::strlen(reg_t str) {
+ if (str.isNull())
+ return 0; // empty text
+
SegmentRef str_r = dereference(str);
if (!str_r.isValid()) {
warning("Attempt to call strlen on invalid pointer %04x:%04x", PRINT_REG(str));
@@ -841,6 +800,9 @@ size_t SegManager::strlen(reg_t str) {
Common::String SegManager::getString(reg_t pointer, int entries) {
Common::String ret;
+ if (pointer.isNull())
+ return ret; // empty text
+
SegmentRef src_r = dereference(pointer);
if (!src_r.isValid()) {
warning("SegManager::getString(): Attempt to dereference invalid pointer %04x:%04x", PRINT_REG(pointer));
@@ -867,7 +829,6 @@ Common::String SegManager::getString(reg_t pointer, int entries) {
return ret;
}
-
byte *SegManager::allocDynmem(int size, const char *descr, reg_t *addr) {
SegmentId seg;
SegmentObj *mobj = allocSegment(new DynMem(), &seg);
@@ -901,14 +862,14 @@ SciArray<reg_t> *SegManager::allocateArray(reg_t *addr) {
ArrayTable *table;
int offset;
- if (!Arrays_seg_id) {
- table = (ArrayTable *)allocSegment(new ArrayTable(), &(Arrays_seg_id));
+ if (!_arraysSegId) {
+ table = (ArrayTable *)allocSegment(new ArrayTable(), &(_arraysSegId));
} else
- table = (ArrayTable *)_heap[Arrays_seg_id];
+ table = (ArrayTable *)_heap[_arraysSegId];
offset = table->allocEntry();
- *addr = make_reg(Arrays_seg_id, offset);
+ *addr = make_reg(_arraysSegId, offset);
return &(table->_table[offset]);
}
@@ -941,14 +902,14 @@ SciString *SegManager::allocateString(reg_t *addr) {
StringTable *table;
int offset;
- if (!String_seg_id) {
- table = (StringTable *)allocSegment(new StringTable(), &(String_seg_id));
+ if (!_stringSegId) {
+ table = (StringTable *)allocSegment(new StringTable(), &(_stringSegId));
} else
- table = (StringTable *)_heap[String_seg_id];
+ table = (StringTable *)_heap[_stringSegId];
offset = table->allocEntry();
- *addr = make_reg(String_seg_id, offset);
+ *addr = make_reg(_stringSegId, offset);
return &(table->_table[offset]);
}
@@ -979,4 +940,148 @@ void SegManager::freeString(reg_t addr) {
#endif
+void SegManager::createClassTable() {
+ Resource *vocab996 = _resMan->findResource(ResourceId(kResourceTypeVocab, 996), 1);
+
+ if (!vocab996)
+ error("SegManager: failed to open vocab 996");
+
+ int totalClasses = vocab996->size >> 2;
+ _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;
+ }
+
+ _resMan->unlockResource(vocab996);
+}
+
+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());
+ return NULL_REG;
+ } else {
+ Class *the_class = &_classTable[classnr];
+ if (!the_class->reg.segment) {
+ getScriptSegment(the_class->script, lock);
+
+ if (!the_class->reg.segment) {
+ error("[VM] Trying to instantiate class %x by instantiating script 0x%x (%03d) failed;", classnr, the_class->script, the_class->script);
+ return NULL_REG;
+ }
+ } else
+ if (caller.segment != the_class->reg.segment)
+ getScript(the_class->reg.segment)->incrementLockers();
+
+ return the_class->reg;
+ }
+}
+
+int SegManager::instantiateScript(int scriptNum) {
+ SegmentId segmentId = getScriptSegment(scriptNum);
+ Script *scr = getScriptIfLoaded(segmentId);
+ if (scr) {
+ if (!scr->isMarkedAsDeleted()) {
+ scr->incrementLockers();
+ return segmentId;
+ } else {
+ scr->freeScript();
+ }
+ } else {
+ scr = allocateScript(scriptNum, &segmentId);
+ }
+
+ scr->init(scriptNum, _resMan);
+ scr->load(_resMan);
+ scr->initialiseLocals(this);
+ scr->initialiseClasses(this);
+
+ if (getSciVersion() >= SCI_VERSION_1_1) {
+ scr->initialiseObjectsSci11(this, segmentId);
+ } else {
+ scr->initialiseObjectsSci0(this, segmentId);
+ }
+
+ return segmentId;
+}
+
+void SegManager::uninstantiateScript(int script_nr) {
+ SegmentId segmentId = getScriptSegment(script_nr);
+ Script *scr = getScriptIfLoaded(segmentId);
+
+ if (!scr) { // Is it already unloaded?
+ //warning("unloading script 0x%x requested although not loaded", script_nr);
+ // This is perfectly valid SCI behaviour
+ return;
+ }
+
+ scr->decrementLockers(); // One less locker
+
+ if (scr->getLockers() > 0)
+ return;
+
+ // Free all classtable references to this script
+ for (uint i = 0; i < classTableSize(); i++)
+ if (getClass(i).reg.segment == segmentId)
+ setClassOffset(i, NULL_REG);
+
+ if (getSciVersion() < SCI_VERSION_1_1)
+ uninstantiateScriptSci0(script_nr);
+ // FIXME: Add proper script uninstantiation for SCI 1.1
+
+ if (!scr->getLockers()) {
+ // The actual script deletion seems to be done by SCI scripts themselves
+ scr->markDeleted();
+ debugC(kDebugLevelScripts, "Unloaded script 0x%x.", script_nr);
+ }
+}
+
+void SegManager::uninstantiateScriptSci0(int script_nr) {
+ bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY);
+ SegmentId segmentId = getScriptSegment(script_nr);
+ Script *scr = getScript(segmentId);
+ reg_t reg = make_reg(segmentId, oldScriptHeader ? 2 : 0);
+ int objType, objLength = 0;
+
+ // Make a pass over the object in order uninstantiate all superclasses
+
+ do {
+ reg.offset += objLength; // Step over the last checked object
+
+ objType = READ_SCI11ENDIAN_UINT16(scr->getBuf(reg.offset));
+ if (!objType)
+ break;
+ objLength = READ_SCI11ENDIAN_UINT16(scr->getBuf(reg.offset + 2));
+
+ reg.offset += 4; // Step over header
+
+ if ((objType == SCI_OBJ_OBJECT) || (objType == SCI_OBJ_CLASS)) { // object or class?
+ reg.offset += 8; // magic offset (SCRIPT_OBJECT_MAGIC_OFFSET)
+ int16 superclass = READ_SCI11ENDIAN_UINT16(scr->getBuf(reg.offset + 2));
+
+ if (superclass >= 0) {
+ int superclass_script = getClass(superclass).script;
+
+ if (superclass_script == script_nr) {
+ if (scr->getLockers())
+ scr->decrementLockers(); // Decrease lockers if this is us ourselves
+ } else
+ uninstantiateScript(superclass_script);
+ // Recurse to assure that the superclass lockers number gets decreased
+ }
+
+ reg.offset += SCRIPT_OBJECT_MAGIC_OFFSET;
+ } // if object or class
+
+ reg.offset -= 4; // Step back on header
+
+ } while (objType != 0);
+}
+
} // End of namespace Sci
diff --git a/engines/sci/engine/seg_manager.h b/engines/sci/engine/seg_manager.h
index 9312f51f9d..a7f5f8517f 100644
--- a/engines/sci/engine/seg_manager.h
+++ b/engines/sci/engine/seg_manager.h
@@ -28,7 +28,9 @@
#include "common/scummsys.h"
#include "common/serializer.h"
+#include "sci/engine/script.h"
#include "sci/engine/vm.h"
+#include "sci/engine/vm_types.h"
#include "sci/engine/segment.h"
namespace Sci {
@@ -42,8 +44,6 @@ namespace Sci {
error("%s, line, %d, %s", __FILE__, __LINE__, msg); \
}
-
-
/**
* Parameters for getScriptSegment().
*/
@@ -53,6 +53,7 @@ enum ScriptLoadType {
SCRIPT_GET_LOCK = 3 /**< Load, if neccessary, and lock */
};
+class Script;
class SegManager : public Common::Serializable {
friend class Console;
@@ -96,6 +97,11 @@ public:
void reconstructScripts(EngineState *s);
/**
+ * Reconstructs the stack. Used when restoring saved games
+ */
+ void reconstructStack(EngineState *s);
+
+ /**
* Determines the segment occupied by a certain script, if any.
* @param script_nr Number of the script to look up
* @return The script's segment ID, or 0 on failure
@@ -111,6 +117,30 @@ public:
*/
SegmentId getScriptSegment(int script_nr, ScriptLoadType load);
+ /**
+ * Makes sure that a script and its superclasses get loaded to the heap.
+ * If the script already has been loaded, only the number of lockers is
+ * increased. All scripts containing superclasses of this script are loaded
+ * recursively as well, unless 'recursive' is set to zero. The
+ * complementary function is "uninstantiateScript()" below.
+ * @param[in] script_nr The script number to load
+ * @return The script's segment ID or 0 if out of heap
+ */
+ int instantiateScript(int script_nr);
+
+ /**
+ * Decreases the numer of lockers of a script and unloads it if that number
+ * reaches zero.
+ * This function will recursively unload scripts containing its
+ * superclasses, if those aren't locked by other scripts as well.
+ * @param[in] script_nr The script number that is requestet to be unloaded
+ */
+ void uninstantiateScript(int script_nr);
+
+private:
+ void uninstantiateScriptSci0(int script_nr);
+
+public:
// TODO: document this
reg_t getClassAddress(int classnr, ScriptLoadType lock, reg_t caller);
@@ -131,28 +161,7 @@ public:
* @param seg ID of the script segment to check for
* @return A pointer to the Script object, or NULL
*/
- Script *getScriptIfLoaded(SegmentId seg);
-
-
- // 1b. Script Initialisation
-
- // The set of functions below are intended
- // to be used during script instantiation,
- // i.e. loading and linking.
-
- /**
- * Initializes a script's local variable block
- * All variables are initialized to zero.
- * @param seg Segment containing the script to initialize
- * @param nr Number of local variables to allocate
- */
- void scriptInitialiseLocalsZero(SegmentId seg, int nr);
-
- /**
- * Initializes a script's local variable block according to a prototype
- * @param location Location to initialize from
- */
- void scriptInitialiseLocals(reg_t location);
+ Script *getScriptIfLoaded(SegmentId seg) const;
// 2. Clones
@@ -378,39 +387,39 @@ public:
* @param type The type of the segment to find
* @return The segment number, or -1 if the segment wasn't found
*/
- SegmentId findSegmentByType(int type);
+ SegmentId findSegmentByType(int type) const;
// TODO: document this
- SegmentObj *getSegmentObj(SegmentId seg);
+ SegmentObj *getSegmentObj(SegmentId seg) const;
// TODO: document this
- SegmentType getSegmentType(SegmentId seg);
+ SegmentType getSegmentType(SegmentId seg) const;
// TODO: document this
- SegmentObj *getSegment(SegmentId seg, SegmentType type);
+ SegmentObj *getSegment(SegmentId seg, SegmentType type) const;
/**
* Retrieves an object from the specified location
* @param[in] offset Location (segment, offset) of the object
* @return The object in question, or NULL if there is none
*/
- Object *getObject(reg_t pos);
+ Object *getObject(reg_t pos) const;
/**
* Checks whether a heap address contains an object
* @parm obj The address to check
* @return True if it is an object, false otherwise
*/
- bool isObject(reg_t obj) { return getObject(obj) != NULL; }
+ bool isObject(reg_t obj) const { return getObject(obj) != NULL; }
// TODO: document this
- bool isHeapObject(reg_t pos);
+ bool isHeapObject(reg_t pos) const;
/**
* Determines the name of an object
* @param[in] pos Location (segment, offset) of the object
* @return A name for that object, or a string describing an error
- * that occured while looking it up. The string is stored
+ * that occurred while looking it up. The string is stored
* in a static buffer and need not be freed (neither may
* it be modified).
*/
@@ -429,22 +438,27 @@ public:
*/
reg_t findObjectByName(const Common::String &name, int index = -1);
- void scriptInitialiseObjectsSci11(SegmentId seg);
-
- uint32 classTableSize() { return _classTable.size(); }
- Class getClass(int index) { return _classTable[index]; }
+ uint32 classTableSize() const { return _classTable.size(); }
+ Class getClass(int index) const { 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; }
+ SegmentId getSysStringsSegment() { return _sysStringsSegId; }
-public: // TODO: make private
- Common::Array<SegmentObj *> _heap;
- // Only accessible from saveLoadWithSerializer()
- Common::Array<Class> _classTable; /**< Table of all classes */
+ /**
+ * Get a pointer to the system string with the specified index,
+ * or NULL if that index is invalid.
+ *
+ * This method is currently only used by kString().
+ */
+ SystemString *getSystemString(uint idx) const {
+ if (idx >= SYS_STRINGS_MAX)
+ return NULL;
+ return &_sysStrings->_strings[idx];
+ }
#ifdef ENABLE_SCI32
SciArray<reg_t> *allocateArray(reg_t *addr);
@@ -453,35 +467,35 @@ public: // TODO: make private
SciString *allocateString(reg_t *addr);
SciString *lookupString(reg_t addr);
void freeString(reg_t addr);
- SegmentId getStringSegmentId() { return String_seg_id; }
+ SegmentId getStringSegmentId() { return _stringSegId; }
#endif
+ const Common::Array<SegmentObj *> &getSegments() const { return _heap; }
+
private:
+ Common::Array<SegmentObj *> _heap;
+ Common::Array<Class> _classTable; /**< Table of all classes */
/** Map script ids to segment ids. */
Common::HashMap<int, SegmentId> _scriptSegMap;
ResourceManager *_resMan;
- SegmentId Clones_seg_id; ///< ID of the (a) clones segment
- SegmentId Lists_seg_id; ///< ID of the (a) list segment
- SegmentId Nodes_seg_id; ///< ID of the (a) node segment
- SegmentId Hunks_seg_id; ///< ID of the (a) hunk segment
+ SegmentId _clonesSegId; ///< ID of the (a) clones segment
+ SegmentId _listsSegId; ///< ID of the (a) list segment
+ SegmentId _nodesSegId; ///< ID of the (a) node segment
+ SegmentId _hunksSegId; ///< ID of the (a) hunk segment
/* System strings */
- SegmentId sysStringsSegment;
-public: // TODO: make private. Only kString() needs direct access
- SystemStrings *sysStrings;
-
-private:
+ SegmentId _sysStringsSegId;
+ SystemStrings *_sysStrings;
#ifdef ENABLE_SCI32
- SegmentId Arrays_seg_id;
- SegmentId String_seg_id;
+ SegmentId _arraysSegId;
+ SegmentId _stringSegId;
#endif
private:
SegmentObj *allocSegment(SegmentObj *mem, SegmentId *segid);
- LocalVariables *allocLocalsSegment(Script *scr, int count);
int deallocate(SegmentId seg, bool recursive);
void createClassTable();
@@ -494,6 +508,9 @@ private:
* 'seg' is a valid segment
*/
bool check(SegmentId seg);
+
+public:
+ LocalVariables *allocLocalsSegment(Script *scr);
};
} // End of namespace Sci
diff --git a/engines/sci/engine/segment.cpp b/engines/sci/engine/segment.cpp
index 0e0a759d4b..cb908979a3 100644
--- a/engines/sci/engine/segment.cpp
+++ b/engines/sci/engine/segment.cpp
@@ -86,175 +86,52 @@ SegmentObj *SegmentObj::createSegmentObj(SegmentType type) {
return mem;
}
-Script::Script() : SegmentObj(SEG_TYPE_SCRIPT) {
- _nr = 0;
- _buf = NULL;
- _bufSize = 0;
- _scriptSize = 0;
- _heapSize = 0;
-
- _synonyms = NULL;
- _heapStart = NULL;
- _exportTable = NULL;
-
- _localsOffset = 0;
- _localsSegment = 0;
- _localsBlock = NULL;
-
- _markedAsDeleted = false;
-}
-
-Script::~Script() {
- freeScript();
-}
-
-void Script::freeScript() {
- free(_buf);
- _buf = NULL;
- _bufSize = 0;
-
- _objects.clear();
- _codeBlocks.clear();
-}
-
-void Script::init(int script_nr, ResourceManager *resMan) {
- Resource *script = resMan->findResource(ResourceId(kResourceTypeScript, script_nr), 0);
-
- _localsOffset = 0;
- _localsBlock = NULL;
-
- _codeBlocks.clear();
-
- _markedAsDeleted = false;
-
- _nr = script_nr;
- _buf = 0;
- _heapStart = 0;
-
- _scriptSize = script->size;
- _bufSize = script->size;
- _heapSize = 0;
-
- _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.
- if (script->size & 2) {
- _bufSize++;
- _scriptSize++;
- }
-
- // 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
- }
+const char *SegmentObj::getSegmentTypeName(SegmentType type) {
+ switch (type) {
+ case SEG_TYPE_SCRIPT:
+ return "script";
+ break;
+ case SEG_TYPE_CLONES:
+ return "clones";
+ break;
+ case SEG_TYPE_LOCALS:
+ return "locals";
+ break;
+ case SEG_TYPE_SYS_STRINGS:
+ return "strings";
+ break;
+ case SEG_TYPE_STACK:
+ return "stack";
+ break;
+ case SEG_TYPE_HUNK:
+ return "hunk";
+ break;
+ case SEG_TYPE_LISTS:
+ return "lists";
+ break;
+ case SEG_TYPE_NODES:
+ return "nodes";
+ break;
+ case SEG_TYPE_DYNMEM:
+ return "dynmem";
+ break;
+#ifdef ENABLE_SCI32
+ case SEG_TYPE_ARRAY:
+ return "array";
+ break;
+ case SEG_TYPE_STRING:
+ return "string";
+ break;
+#endif
+ default:
+ error("Unknown SegmentObj type %d", type);
+ break;
}
-}
-
-Object *Script::allocateObject(uint16 offset) {
- return &_objects[offset];
-}
-
-Object *Script::getObject(uint16 offset) {
- if (_objects.contains(offset))
- return &_objects[offset];
- else
- return 0;
-}
-
-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 && 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 + kOffsetFunctionArea < (int)_bufSize, "Function area pointer stored beyond end of script\n");
-
- obj->init(_buf, obj_pos, fullObjectInit);
-
- return obj;
-}
-
-void Script::scriptObjRemove(reg_t obj_pos) {
- if (getSciVersion() < SCI_VERSION_1_1)
- obj_pos.offset += 8;
-
- _objects.erase(obj_pos.toUint16());
+ return NULL;
}
// This helper function is used by Script::relocateLocal and Object::relocate
+// Duplicate in segment.cpp and script.cpp
static bool relocateBlock(Common::Array<reg_t> &block, int block_location, SegmentId segment, int location, size_t scriptSize) {
int rel = location - block_location;
@@ -267,7 +144,7 @@ static bool relocateBlock(Common::Array<reg_t> &block, int block_location, Segme
return false;
if (rel & 1) {
- warning("Attempt to relocate odd variable #%d.5e (relative to %04x)\n", idx, block_location);
+ error("Attempt to relocate odd variable #%d.5e (relative to %04x)\n", idx, block_location);
return false;
}
block[idx].segment = segment; // Perform relocation
@@ -277,182 +154,12 @@ static bool relocateBlock(Common::Array<reg_t> &block, int block_location, Segme
return true;
}
-bool Script::relocateLocal(SegmentId segment, int location) {
- if (_localsBlock)
- return relocateBlock(_localsBlock->_locals, _localsOffset, segment, location, _scriptSize);
- else
- return false;
-}
-
-void Script::scriptAddCodeBlock(reg_t location) {
- CodeBlock cb;
- cb.pos = location;
- cb.size = READ_SCI11ENDIAN_UINT16(_buf + location.offset - 2);
- _codeBlocks.push_back(cb);
-}
-
-void Script::relocate(reg_t block) {
- byte *heap = _buf;
- uint16 heapSize = (uint16)_bufSize;
- uint16 heapOffset = 0;
-
- if (getSciVersion() >= SCI_VERSION_1_1) {
- heap = _heapStart;
- heapSize = (uint16)_heapSize;
- heapOffset = _scriptSize;
- }
-
- VERIFY(block.offset < (uint16)heapSize && READ_SCI11ENDIAN_UINT16(heap + block.offset) * 2 + block.offset < (uint16)heapSize,
- "Relocation block outside of script\n");
-
- int count = READ_SCI11ENDIAN_UINT16(heap + block.offset);
- int exportIndex = 0;
-
- for (int i = 0; i < count; i++) {
- 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;
- uint k;
-
- ObjMap::iterator it;
- const ObjMap::iterator end = _objects.end();
- for (it = _objects.begin(); !done && it != end; ++it) {
- 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) {
- 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)
- debug("- locals: %d at %04x\n", _localsBlock->_locals.size(), _localsOffset);
- else
- debug("- No locals\n");
- for (it = _objects.begin(), k = 0; it != end; ++it, ++k)
- debug("- obj#%d at %04x w/ %d vars\n", k, it->_value.getPos().offset, it->_value.getVarCount());
- debug("Trying to continue anyway...\n");
- }
- }
-
- exportIndex++;
- }
-}
-
-void Script::incrementLockers() {
- _lockers++;
-}
-
-void Script::decrementLockers() {
- if (_lockers > 0)
- _lockers--;
-}
-
-int Script::getLockers() const {
- return _lockers;
-}
-
-void Script::setLockers(int lockers) {
- _lockers = lockers;
-}
-
-uint16 Script::validateExportFunc(int pubfunct) {
- bool exportsAreWide = (g_sci->_features->detectLofsType() == SCI_VERSION_1_MIDDLE);
-
- if (_numExports <= pubfunct) {
- warning("validateExportFunc(): pubfunct is invalid");
- return 0;
- }
-
- if (exportsAreWide)
- pubfunct *= 2;
- uint16 offset = READ_SCI11ENDIAN_UINT16(_exportTable + pubfunct);
- VERIFY(offset < _bufSize, "invalid export function pointer");
-
- return offset;
-}
-
-byte *Script::findBlock(int type) {
- byte *buf = _buf;
- bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY);
-
- if (oldScriptHeader)
- buf += 2;
-
- do {
- int seekerType = READ_LE_UINT16(buf);
-
- if (seekerType == 0)
- break;
- if (seekerType == type)
- return buf;
-
- 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) {
- if (_buf) {
- assert(dst + n <= _bufSize);
- memcpy(_buf + dst, src, n);
- }
-}
-
-int16 Script::getHeap(uint16 offset) const {
- assert(offset + 1 < (int)_bufSize);
- return READ_SCI11ENDIAN_UINT16(_buf + offset);
-// return (_buf[offset] | (_buf[offset+1]) << 8);
-}
-
SegmentRef SegmentObj::dereference(reg_t pointer) {
error("Error: Trying to dereference pointer %04x:%04x to inappropriate segment",
PRINT_REG(pointer));
return SegmentRef();
}
-bool Script::isValidOffset(uint16 offset) const {
- return offset < _bufSize;
-}
-
-SegmentRef Script::dereference(reg_t pointer) {
- if (pointer.offset > _bufSize) {
- warning("Script::dereference(): Attempt to dereference invalid pointer %04x:%04x into script segment (script size=%d)",
- PRINT_REG(pointer), (uint)_bufSize);
- return SegmentRef();
- }
-
- SegmentRef ret;
- ret.isRaw = true;
- ret.maxSize = _bufSize - pointer.offset;
- ret.raw = _buf + pointer.offset;
- return ret;
-}
bool LocalVariables::isValidOffset(uint16 offset) const {
return offset < _locals.size() * 2;
@@ -471,7 +178,16 @@ SegmentRef LocalVariables::dereference(reg_t pointer) {
if (ret.maxSize > 0) {
ret.reg = &_locals[pointer.offset / 2];
} else {
- warning("LocalVariables::dereference: Offset at end or out of bounds %04x:%04x", PRINT_REG(pointer));
+ if ((g_sci->getEngineState()->currentRoomNumber() == 660 || g_sci->getEngineState()->currentRoomNumber() == 660)
+ && g_sci->getGameId() == GID_LAURABOW2) {
+ // Happens in two places during the intro of LB2CD, both from kMemory(peek):
+ // - room 160: Heap 160 has 83 local variables (0-82), and the game
+ // asks for variables at indices 83 - 90 too.
+ // - room 220: Heap 220 has 114 local variables (0-113), and the
+ // game asks for variables at indices 114-120 too.
+ } else {
+ error("LocalVariables::dereference: Offset at end or out of bounds %04x:%04x", PRINT_REG(pointer));
+ }
ret.reg = 0;
}
return ret;
@@ -518,58 +234,21 @@ SegmentRef SystemStrings::dereference(reg_t pointer) {
if (isValidOffset(pointer.offset))
ret.raw = (byte *)(_strings[pointer.offset]._value);
else {
- // This occurs in KQ5CD when interacting with certain objects
- warning("SystemStrings::dereference(): Attempt to dereference invalid pointer %04x:%04x", PRINT_REG(pointer));
- }
-
- return ret;
-}
-
-
-//-------------------- script --------------------
-reg_t Script::findCanonicAddress(SegManager *segMan, reg_t addr) const {
- addr.offset = 0;
- return addr;
-}
-
-void Script::freeAtAddress(SegManager *segMan, reg_t addr) {
- /*
- debugC(2, kDebugLevelGC, "[GC] Freeing script %04x:%04x", PRINT_REG(addr));
- if (_localsSegment)
- debugC(2, kDebugLevelGC, "[GC] Freeing locals %04x:0000", _localsSegment);
- */
-
- if (_markedAsDeleted)
- segMan->deallocateScript(_nr);
-}
-
-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) const {
- if (addr.offset <= _bufSize && addr.offset >= -SCRIPT_OBJECT_MAGIC_OFFSET && RAW_IS_OBJECT(_buf + addr.offset)) {
- const Object *obj = getObject(addr.offset);
- if (obj) {
- // Note all local variables, if we have a local variable environment
- if (_localsSegment)
- (*note)(param, make_reg(_localsSegment, 0));
-
- for (uint i = 0; i < obj->getVarCount(); i++)
- (*note)(param, obj->getVariable(i));
+ if (g_sci->getGameId() == GID_KQ5) {
+ // This occurs in KQ5CD when interacting with certain objects
} else {
- warning("Request for outgoing script-object reference at %04x:%04x failed", PRINT_REG(addr));
+ error("SystemStrings::dereference(): Attempt to dereference invalid pointer %04x:%04x", PRINT_REG(pointer));
}
- } else {
- /* warning("Unexpected request for outgoing script-object references at %04x:%04x", PRINT_REG(addr));*/
- /* Happens e.g. when we're looking into strings */
}
+
+ return ret;
}
//-------------------- clones --------------------
-void CloneTable::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) const {
+Common::Array<reg_t> CloneTable::listAllOutgoingReferences(reg_t addr) const {
+ Common::Array<reg_t> tmp;
// assert(addr.segment == _segId);
if (!isValidEntry(addr.offset)) {
@@ -580,20 +259,20 @@ void CloneTable::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback
// Emit all member variables (including references to the 'super' delegate)
for (uint i = 0; i < clone->getVarCount(); i++)
- (*note)(param, clone->getVariable(i));
+ tmp.push_back(clone->getVariable(i));
// Note that this also includes the 'base' object, which is part of the script and therefore also emits the locals.
- (*note)(param, clone->getPos());
+ tmp.push_back(clone->getPos());
//debugC(2, kDebugLevelGC, "[GC] Reporting clone-pos %04x:%04x", PRINT_REG(clone->pos));
+
+ return tmp;
}
void CloneTable::freeAtAddress(SegManager *segMan, reg_t addr) {
#ifdef GC_DEBUG
- Object *victim_obj;
-
-// assert(addr.segment == _segId);
+ // assert(addr.segment == _segId);
- victim_obj = &(_table[addr.offset]);
+ Object *victim_obj = &(_table[addr.offset]);
if (!(victim_obj->_flags & OBJECT_FLAG_FREED))
warning("[GC] Clone %04x:%04x not reachable and not freed (freeing now)", PRINT_REG(addr));
@@ -620,11 +299,14 @@ reg_t LocalVariables::findCanonicAddress(SegManager *segMan, reg_t addr) const {
return make_reg(owner_seg, 0);
}
-void LocalVariables::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) const {
+Common::Array<reg_t> LocalVariables::listAllOutgoingReferences(reg_t addr) const {
+ Common::Array<reg_t> tmp;
// assert(addr.segment == _segId);
for (uint i = 0; i < _locals.size(); i++)
- (*note)(param, _locals[i]);
+ tmp.push_back(_locals[i]);
+
+ return tmp;
}
@@ -634,11 +316,14 @@ reg_t DataStack::findCanonicAddress(SegManager *segMan, reg_t addr) const {
return addr;
}
-void DataStack::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) const {
+Common::Array<reg_t> DataStack::listAllOutgoingReferences(reg_t object) const {
+ Common::Array<reg_t> tmp;
fprintf(stderr, "Emitting %d stack entries\n", _capacity);
for (int i = 0; i < _capacity; i++)
- (*note)(param, _entries[i]);
+ tmp.push_back(_entries[i]);
fprintf(stderr, "DONE");
+
+ return tmp;
}
@@ -647,18 +332,20 @@ void ListTable::freeAtAddress(SegManager *segMan, reg_t sub_addr) {
freeEntry(sub_addr.offset);
}
-void ListTable::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) const {
+Common::Array<reg_t> ListTable::listAllOutgoingReferences(reg_t addr) const {
+ Common::Array<reg_t> tmp;
if (!isValidEntry(addr.offset)) {
- warning("Invalid list referenced for outgoing references: %04x:%04x", PRINT_REG(addr));
- return;
+ error("Invalid list referenced for outgoing references: %04x:%04x", PRINT_REG(addr));
}
const List *list = &(_table[addr.offset]);
- note(param, list->first);
- note(param, list->last);
+ tmp.push_back(list->first);
+ tmp.push_back(list->last);
// We could probably get away with just one of them, but
// let's be conservative here.
+
+ return tmp;
}
@@ -667,19 +354,21 @@ void NodeTable::freeAtAddress(SegManager *segMan, reg_t sub_addr) {
freeEntry(sub_addr.offset);
}
-void NodeTable::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) const {
+Common::Array<reg_t> NodeTable::listAllOutgoingReferences(reg_t addr) const {
+ Common::Array<reg_t> tmp;
if (!isValidEntry(addr.offset)) {
- warning("Invalid node referenced for outgoing references: %04x:%04x", PRINT_REG(addr));
- return;
+ error("Invalid node referenced for outgoing references: %04x:%04x", PRINT_REG(addr));
}
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
- note(param, node->pred);
- note(param, node->succ);
- note(param, node->key);
- note(param, node->value);
+ tmp.push_back(node->pred);
+ tmp.push_back(node->succ);
+ tmp.push_back(node->key);
+ tmp.push_back(node->value);
+
+ return tmp;
}
@@ -743,7 +432,7 @@ 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])",
+ error("Applied propertyOffsetToId to invalid property offset %x (property #%d not in [0..%d])",
propertyOffset, propertyOffset >> 1, selectors - 1);
return -1;
}
@@ -800,8 +489,9 @@ reg_t DynMem::findCanonicAddress(SegManager *segMan, reg_t addr) const {
return addr;
}
-void DynMem::listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) const {
- (*note)(param, make_reg(segId, 0));
+Common::Array<reg_t> DynMem::listAllDeallocatable(SegmentId segId) const {
+ const reg_t r = make_reg(segId, 0);
+ return Common::Array<reg_t>(&r, 1);
}
#ifdef ENABLE_SCI32
@@ -819,10 +509,10 @@ void ArrayTable::freeAtAddress(SegManager *segMan, reg_t sub_addr) {
freeEntry(sub_addr.offset);
}
-void ArrayTable::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) const {
+Common::Array<reg_t> ArrayTable::listAllOutgoingReferences(reg_t addr) const {
+ Common::Array<reg_t> tmp;
if (!isValidEntry(addr.offset)) {
- warning("Invalid array referenced for outgoing references: %04x:%04x", PRINT_REG(addr));
- return;
+ error("Invalid array referenced for outgoing references: %04x:%04x", PRINT_REG(addr));
}
const SciArray<reg_t> *array = &(_table[addr.offset]);
@@ -830,8 +520,10 @@ void ArrayTable::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback
for (uint32 i = 0; i < array->getSize(); i++) {
reg_t value = array->getValue(i);
if (value.segment != 0)
- note(param, value);
+ tmp.push_back(value);
}
+
+ return tmp;
}
Common::String SciString::toString() const {
diff --git a/engines/sci/engine/segment.h b/engines/sci/engine/segment.h
index f1b6dccaa2..2465576302 100644
--- a/engines/sci/engine/segment.h
+++ b/engines/sci/engine/segment.h
@@ -67,7 +67,7 @@ enum SegmentType {
SEG_TYPE_NODES = 7,
SEG_TYPE_HUNK = 8,
SEG_TYPE_DYNMEM = 9,
- SEG_TYPE_STRING_FRAG = 10, // obsolete, we keep it to be able to load old saves
+ // 10 used to be string fragments, now obsolete
#ifdef ENABLE_SCI32
SEG_TYPE_ARRAY = 11,
@@ -80,10 +80,9 @@ enum SegmentType {
struct SegmentObj : public Common::Serializable {
SegmentType _type;
- typedef void (*NoteCallback)(void *param, reg_t addr); // FIXME: Bad choice of name
-
public:
static SegmentObj *createSegmentObj(SegmentType type);
+ static const char *getSegmentTypeName(SegmentType type);
public:
SegmentObj(SegmentType type) : _type(type) {}
@@ -106,6 +105,7 @@ public:
/**
* Finds the canonic address associated with sub_reg.
+ * Used by the garbage collector.
*
* For each valid address a, there exists a canonic address c(a) such that c(a) = c(c(a)).
* This address "governs" a in the sense that deallocating c(a) will deallocate a.
@@ -116,25 +116,31 @@ public:
/**
* Deallocates all memory associated with the specified address.
+ * Used by the garbage collector.
* @param sub_addr address (within the given segment) to deallocate
*/
virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr) {}
/**
- * Iterates over and reports all addresses within the current segment.
- * @param note Invoked for each address on which free_at_address() makes sense
- * @param param parameter passed to 'note'
+ * Iterates over and reports all addresses within the segment.
+ * Used by the garbage collector.
+ * @return a list of addresses within the segment
*/
- virtual void listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) const {}
+ virtual Common::Array<reg_t> listAllDeallocatable(SegmentId segId) const {
+ return Common::Array<reg_t>();
+ }
/**
* Iterates over all references reachable from the specified object.
- * @param object object (within the current segment) to analyse
- * @param param parameter passed to 'note'
- * @param note Invoked for each outgoing reference within the object
- * Note: This function may also choose to report numbers (segment 0) as adresses
+ * Used by the garbage collector.
+ * @param object object (within the current segment) to analyse
+ * @return a list of outgoing references 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) const {}
+ virtual Common::Array<reg_t> listAllOutgoingReferences(reg_t object) const {
+ return Common::Array<reg_t>();
+ }
};
@@ -195,7 +201,7 @@ public:
virtual bool isValidOffset(uint16 offset) const;
virtual SegmentRef dereference(reg_t pointer);
virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr) const;
- virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note) const;
+ virtual Common::Array<reg_t> listAllOutgoingReferences(reg_t object) const;
virtual void saveLoadWithSerializer(Common::Serializer &ser);
};
@@ -331,199 +337,6 @@ private:
reg_t _pos; /**< Object offset within its script; for clones, this is their base */
};
-struct CodeBlock {
- reg_t pos;
- int size;
-};
-
-typedef Common::HashMap<uint16, Object> ObjMap;
-
-class Script : public SegmentObj {
-public:
- int _nr; /**< Script number */
- byte *_buf; /**< Static data buffer, or NULL if not used */
- byte *_heapStart; /**< Start of heap if SCI1.1, NULL otherwise */
-
- 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.
- * Indexed by the TODO offset.
- */
- ObjMap _objects;
-
- int _localsOffset;
- SegmentId _localsSegment; /**< The local variable segment */
- LocalVariables *_localsBlock;
-
- bool _markedAsDeleted;
-
-public:
- Script();
- ~Script();
-
- void freeScript();
- 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) const;
- virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr);
- 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
- * @param location Start of block to relocate
- */
- void scriptAddCodeBlock(reg_t location);
-
- /**
- * Initializes an object within the segment manager
- * @param obj_pos Location (segment, offset) of the object. It must
- * point to the beginning of the script/class block
- * (as opposed to what the VM considers to be the
- * object location)
- * @returns A newly created Object describing the object,
- * stored within the relevant script
- */
- Object *scriptObjInit(reg_t obj_pos, bool fullObjectInit = true);
-
- /**
- * Removes a script object
- * @param obj_pos Location (segment, offset) of the object.
- */
- void scriptObjRemove(reg_t obj_pos);
-
- /**
- * Processes a relocation block witin a script
- * This function is idempotent, but it must only be called after all
- * objects have been instantiated, or a run-time error will occur.
- * @param obj_pos Location (segment, offset) of the block
- * @return Location of the relocation block
- */
- void relocate(reg_t block);
-
-private:
- bool relocateLocal(SegmentId segment, int location);
-
-public:
- // script lock operations
-
- /** Increments the number of lockers of this script by one. */
- void incrementLockers();
-
- /** Decrements the number of lockers of this script by one. */
- void decrementLockers();
-
- /**
- * Retrieves the number of locks held on this script.
- * @return the number of locks held on the previously identified script
- */
- int getLockers() const;
-
- /** Sets the number of locks held on this script. */
- 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.
- */
- const byte *getSynonyms() const { return _synonyms; }
-
- /**
- * Retrieves the number of synonyms associated with this script.
- * @return the number of synonyms associated with this script
- */
- uint16 getSynonymsNr() const { return _numSynonyms; }
-
- /**
- * Validate whether the specified public function is exported by
- * the script in the specified segment.
- * @param pubfunct Index of the function to validate
- * @return NULL if the public function is invalid, its
- * offset into the script's segment otherwise
- */
- uint16 validateExportFunc(int pubfunct);
-
- /**
- * 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
- * either unreachable (resulting in its eventual deletion) or reloaded (resulting
- * in its data being updated).
- */
- void markDeleted() {
- _markedAsDeleted = true;
- }
-
- /**
- * Determines whether the script is marked as being deleted.
- */
- bool isMarkedAsDeleted() const {
- return _markedAsDeleted;
- }
-
- /**
- * Copies a byte string into a script's heap representation.
- * @param dst script-relative offset of the destination area
- * @param src pointer to the data source location
- * @param n number of bytes to copy
- */
- void mcpyInOut(int dst, const void *src, size_t n);
-
-
- /**
- * Retrieves a 16 bit value from within a script's heap representation.
- * @param offset offset to read from
- * @return the value read from the specified location
- */
- int16 getHeap(uint16 offset) const;
-
- /**
- * Finds the pointer where a block of a specific type starts from
- */
- byte *findBlock(int type);
-};
-
/** Data stack */
struct DataStack : SegmentObj {
int _capacity; /**< Number of stack entries */
@@ -542,7 +355,7 @@ public:
virtual bool isValidOffset(uint16 offset) const;
virtual SegmentRef dereference(reg_t pointer);
virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr) const;
- virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note) const;
+ virtual Common::Array<reg_t> listAllOutgoingReferences(reg_t object) const;
virtual void saveLoadWithSerializer(Common::Serializer &ser);
};
@@ -630,10 +443,12 @@ public:
entries_used--;
}
- virtual void listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) const {
+ virtual Common::Array<reg_t> listAllDeallocatable(SegmentId segId) const {
+ Common::Array<reg_t> tmp;
for (uint i = 0; i < _table.size(); i++)
if (isValidEntry(i))
- (*note)(param, make_reg(segId, i));
+ tmp.push_back(make_reg(segId, i));
+ return tmp;
}
};
@@ -643,7 +458,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) const;
+ virtual Common::Array<reg_t> listAllOutgoingReferences(reg_t object) const;
virtual void saveLoadWithSerializer(Common::Serializer &ser);
};
@@ -654,7 +469,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) const;
+ virtual Common::Array<reg_t> listAllOutgoingReferences(reg_t object) const;
virtual void saveLoadWithSerializer(Common::Serializer &ser);
};
@@ -665,7 +480,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) const;
+ virtual Common::Array<reg_t> listAllOutgoingReferences(reg_t object) const;
virtual void saveLoadWithSerializer(Common::Serializer &ser);
};
@@ -678,7 +493,10 @@ struct HunkTable : public Table<Hunk> {
virtual void freeEntry(int idx) {
Table<Hunk>::freeEntry(idx);
+ if (!_table[idx].mem)
+ warning("Attempt to free an already freed hunk");
free(_table[idx].mem);
+ _table[idx].mem = 0;
}
virtual void saveLoadWithSerializer(Common::Serializer &ser);
@@ -701,7 +519,7 @@ public:
virtual bool isValidOffset(uint16 offset) const;
virtual SegmentRef dereference(reg_t pointer);
virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr) const;
- virtual void listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) const;
+ virtual Common::Array<reg_t> listAllDeallocatable(SegmentId segId) const;
virtual void saveLoadWithSerializer(Common::Serializer &ser);
};
@@ -811,6 +629,7 @@ public:
byte getType() const { return _type; }
uint32 getSize() const { return _size; }
T *getRawData() { return _data; }
+ const T *getRawData() const { return _data; }
protected:
int8 _type;
@@ -834,7 +653,7 @@ 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) const;
+ virtual Common::Array<reg_t> listAllOutgoingReferences(reg_t object) 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 04a1b8fbba..00480743cc 100644
--- a/engines/sci/engine/selector.cpp
+++ b/engines/sci/engine/selector.cpp
@@ -24,6 +24,7 @@
*/
#include "sci/sci.h"
+#include "sci/engine/kernel.h"
#include "sci/engine/state.h"
#include "sci/engine/selector.h"
@@ -157,6 +158,9 @@ void Kernel::mapSelectors() {
FIND_SELECTOR(scaleSignal);
FIND_SELECTOR(scaleX);
FIND_SELECTOR(scaleY);
+ FIND_SELECTOR(maxScale);
+ FIND_SELECTOR(vanishingX);
+ FIND_SELECTOR(vanishingY);
FIND_SELECTOR(iconIndex);
#ifdef ENABLE_SCI32
@@ -188,42 +192,37 @@ void writeSelector(SegManager *segMan, reg_t object, Selector selectorId, reg_t
ObjVarRef address;
if ((selectorId < 0) || (selectorId > (int)g_sci->getKernel()->getSelectorNamesSize())) {
- warning("Attempt to write to invalid selector %d of"
+ error("Attempt to write to invalid selector %d of"
" object at %04x:%04x.", selectorId, PRINT_REG(object));
return;
}
if (lookupSelector(segMan, object, selectorId, &address, NULL) != kSelectorVariable)
- warning("Selector '%s' of object at %04x:%04x could not be"
+ error("Selector '%s' of object at %04x:%04x could not be"
" written to", g_sci->getKernel()->getSelectorName(selectorId).c_str(), PRINT_REG(object));
else
*address.getPointer(segMan) = value;
}
-int invokeSelectorArgv(EngineState *s, reg_t object, int selectorId, SelectorInvocation noinvalid,
+void invokeSelector(EngineState *s, reg_t object, int selectorId,
int k_argc, StackPtr k_argp, int argc, const reg_t *argv) {
int i;
int framesize = 2 + 1 * argc;
- reg_t address;
int slc_type;
StackPtr stackframe = k_argp + k_argc;
stackframe[0] = make_reg(0, selectorId); // The selector we want to call
stackframe[1] = make_reg(0, argc); // Argument count
- slc_type = lookupSelector(s->_segMan, object, selectorId, NULL, &address);
+ slc_type = lookupSelector(s->_segMan, object, selectorId, NULL, NULL);
if (slc_type == kSelectorNone) {
- warning("Selector '%s' of object at %04x:%04x could not be invoked",
+ error("Selector '%s' of object at %04x:%04x could not be invoked",
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",
+ error("Attempting to invoke variable selector %s of object %04x:%04x",
g_sci->getKernel()->getSelectorName(selectorId).c_str(), PRINT_REG(object));
- return 0;
}
for (i = 0; i < argc; i++)
@@ -238,24 +237,6 @@ int invokeSelectorArgv(EngineState *s, reg_t object, int selectorId, SelectorInv
xstack->fp += argc + 2;
run_vm(s, false); // Start a new vm
-
- return 0;
-}
-
-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];
-
- va_start(argp, argc);
- for (int i = 0; i < argc; i++)
- args[i] = va_arg(argp, reg_t);
- va_end(argp);
-
- int retval = invokeSelectorArgv(s, object, selectorId, noinvalid, k_argc, k_argp, argc, args);
-
- delete[] args;
- return retval;
}
SelectorType lookupSelector(SegManager *segMan, reg_t obj_location, Selector selectorId, ObjVarRef *varp, reg_t *fptr) {
diff --git a/engines/sci/engine/selector.h b/engines/sci/engine/selector.h
index f50b9ab1b3..acb7912d8d 100644
--- a/engines/sci/engine/selector.h
+++ b/engines/sci/engine/selector.h
@@ -30,13 +30,118 @@
#include "sci/engine/vm_types.h" // for reg_t
#include "sci/engine/vm.h"
-#include "sci/engine/kernel.h" // for Kernel::_selectorCache
namespace Sci {
-enum SelectorInvocation {
- kStopOnInvalidSelector = 0,
- kContinueOnInvalidSelector = 1
+/** Contains selector IDs for a few selected selectors */
+struct SelectorCache {
+ SelectorCache() {
+ memset(this, 0, sizeof(*this));
+ }
+
+ // Statically defined selectors, (almost the) same in all SCI versions
+ Selector y;
+ Selector x;
+ Selector view, loop, cel; ///< Description of a specific image
+ Selector underBits; ///< Used by the graphics subroutines to store backupped BG pic data
+ Selector nsTop, nsLeft, nsBottom, nsRight; ///< View boundaries ('now seen')
+ Selector lsTop, lsLeft, lsBottom, lsRight; ///< Used by Animate() subfunctions and scroll list controls
+ Selector signal; ///< Used by Animate() to control a view's behaviour
+ Selector illegalBits; ///< Used by CanBeHere
+ Selector brTop, brLeft, brBottom, brRight; ///< Bounding Rectangle
+ // name, key, time
+ Selector text; ///< Used by controls
+ Selector elements; ///< Used by SetSynonyms()
+ // color, back
+ Selector mode; ///< Used by text controls (-> DrawControl())
+ // style
+ Selector state, font, type;///< Used by controls
+ // window
+ Selector cursor, max; ///< Used by EditControl
+ // mark, who
+ Selector message; ///< Used by GetEvent
+ // edit
+ Selector play; ///< Play function (first function to be called)
+ Selector number;
+ Selector handle; ///< Replaced by nodePtr in SCI1+
+ Selector nodePtr; ///< Replaces handle in SCI1+
+ Selector client; ///< The object that wants to be moved
+ Selector dx, dy; ///< Deltas
+ Selector b_movCnt, b_i1, b_i2, b_di, b_xAxis, b_incr; ///< Various Bresenham vars
+ Selector xStep, yStep; ///< BR adjustments
+ Selector moveSpeed; ///< Used for DoBresen
+ Selector canBeHere; ///< Funcselector: Checks for movement validity in SCI0
+ Selector heading, mover; ///< Used in DoAvoider
+ Selector doit; ///< Called (!) by the Animate() system call
+ Selector isBlocked, looper; ///< Used in DoAvoider
+ Selector priority;
+ Selector modifiers; ///< Used by GetEvent
+ Selector replay; ///< Replay function
+ // setPri, at, next, done, width
+ Selector wordFail, syntaxFail; ///< Used by Parse()
+ // semanticFail, pragmaFail
+ // said
+ Selector claimed; ///< Used generally by the event mechanism
+ // value, save, restore, title, button, icon, draw
+ Selector delete_; ///< Called by Animate() to dispose a view object
+ Selector z;
+
+ // SCI1+ static selectors
+ Selector parseLang;
+ Selector printLang; ///< Used for i18n
+ Selector subtitleLang;
+ Selector size;
+ Selector points; ///< Used by AvoidPath()
+ Selector palette;
+ Selector dataInc;
+ // handle (in SCI1)
+ Selector min; ///< SMPTE time format
+ Selector sec;
+ Selector frame;
+ Selector vol;
+ Selector pri;
+ // perform
+ Selector moveDone; ///< used for DoBresen
+
+ // SCI1 selectors which have been moved a bit in SCI1.1, but otherwise static
+ Selector cantBeHere; ///< Checks for movement avoidance in SCI1+. Replaces canBeHere
+ Selector topString; ///< SCI1 scroll lists use this instead of lsTop
+ Selector flags;
+
+ // SCI1+ audio sync related selectors, not static. They're used for lip syncing in
+ // CD talkie games
+ Selector syncCue; ///< Used by DoSync()
+ Selector syncTime;
+
+ // SCI1.1 specific selectors
+ Selector scaleSignal; //< Used by kAnimate() for cel scaling (SCI1.1+)
+ Selector scaleX, scaleY; ///< SCI1.1 view scaling
+ Selector maxScale; ///< SCI1.1 view scaling, limit for cel, when using global scaling
+ Selector vanishingX; ///< SCI1.1 view scaling, used by global scaling
+ Selector vanishingY; ///< SCI1.1 view scaling, used by global scaling
+
+ // Used for auto detection purposes
+ Selector overlay; ///< Used to determine if a game is using old gfx functions or not
+
+ // SCI1.1 Mac icon bar selectors
+ Selector iconIndex; ///< Used to index icon bar objects
+
+#ifdef ENABLE_SCI32
+ Selector data; // Used by Array()/String()
+ Selector picture; // Used to hold the picture ID for SCI32 pictures
+
+ Selector plane;
+ Selector top;
+ Selector left;
+ Selector bottom;
+ Selector right;
+ Selector resX;
+ Selector resY;
+
+ Selector fore;
+ Selector back;
+ Selector dimmed;
+#endif
};
/**
@@ -71,18 +176,8 @@ void writeSelector(SegManager *segMan, reg_t object, Selector selectorId, reg_t
/**
* 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 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
-
+void invokeSelector(EngineState *s, reg_t object, int selectorId,
+ int k_argc, StackPtr k_argp, int argc = 0, const reg_t *argv = 0);
} // End of namespace Sci
diff --git a/engines/sci/engine/state.cpp b/engines/sci/engine/state.cpp
index b4a04f8826..36b03c0ad9 100644
--- a/engines/sci/engine/state.cpp
+++ b/engines/sci/engine/state.cpp
@@ -29,6 +29,7 @@
#include "sci/debug.h" // for g_debug_sleeptime_factor
#include "sci/event.h"
+#include "sci/engine/kernel.h"
#include "sci/engine/state.h"
#include "sci/engine/selector.h"
#include "sci/engine/vm.h"
@@ -80,73 +81,91 @@ EngineState::~EngineState() {
}
void EngineState::reset(bool isRestoring) {
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- sfx_init_flags = 0;
-#endif
-
if (!isRestoring) {
- script_000 = 0;
- _gameObj = NULL_REG;
-
_memorySegmentSize = 0;
- _soundCmd = 0;
- restarting_flags = 0;
+ _fileHandles.resize(5);
- execution_stack_base = 0;
- _executionStackPosChanged = false;
+ abortScriptProcessing = kAbortNone;
+ }
- _fileHandles.resize(5);
+ executionStackBase = 0;
+ _executionStackPosChanged = false;
+ stack_base = 0;
+ stack_top = 0;
- r_acc = NULL_REG;
- restAdjust = 0;
- r_prev = NULL_REG;
+ restAdjust = 0;
- stack_base = 0;
- stack_top = 0;
- }
+ r_acc = NULL_REG;
+ r_prev = NULL_REG;
- last_wait_time = 0;
+ lastWaitTime = 0;
- gc_countdown = 0;
+ gcCountDown = 0;
_throttleCounter = 0;
_throttleLastTime = 0;
_throttleTrigger = false;
- script_abort_flag = 0;
- script_step_counter = 0;
- script_gc_interval = GC_INTERVAL;
+ _lastSaveVirtualId = SAVEGAMEID_OFFICIALRANGE_START;
+ _lastSaveNewId = 0;
- restoring = false;
+ scriptStepCounter = 0;
+ scriptGCInterval = GC_INTERVAL;
}
-void EngineState::wait(int16 ticks) {
- uint32 time;
+void EngineState::speedThrottler(uint32 neededSleep) {
+ if (_throttleTrigger) {
+ uint32 curTime = g_system->getMillis();
+ uint32 duration = curTime - _throttleLastTime;
+
+ if (duration < neededSleep) {
+ g_sci->sleep(neededSleep - duration);
+ _throttleLastTime = g_system->getMillis();
+ } else {
+ _throttleLastTime = curTime;
+ }
+ _throttleTrigger = false;
+ }
+}
- time = g_system->getMillis();
- r_acc = make_reg(0, ((long)time - (long)last_wait_time) * 60 / 1000);
- last_wait_time = time;
+void EngineState::wait(int16 ticks) {
+ uint32 time = g_system->getMillis();
+ r_acc = make_reg(0, ((long)time - (long)lastWaitTime) * 60 / 1000);
+ lastWaitTime = time;
ticks *= g_debug_sleeptime_factor;
- _event->sleep(ticks * 1000 / 60);
+ g_sci->sleep(ticks * 1000 / 60);
+}
+
+void EngineState::initGlobals() {
+ Script *script_000 = _segMan->getScript(1);
+
+ if (!script_000->_localsBlock)
+ error("Script 0 has no locals block");
+
+ variablesSegment[VAR_GLOBAL] = script_000->_localsSegment;
+ variablesBase[VAR_GLOBAL] = variables[VAR_GLOBAL] = script_000->_localsBlock->_locals.begin();
+ variablesMax[VAR_GLOBAL] = script_000->_localsBlock->_locals.size();
}
uint16 EngineState::currentRoomNumber() const {
- return script_000->_localsBlock->_locals[13].toUint16();
+ return variables[VAR_GLOBAL][13].toUint16();
}
void EngineState::setRoomNumber(uint16 roomNumber) {
- script_000->_localsBlock->_locals[13] = make_reg(0, roomNumber);
+ variables[VAR_GLOBAL][13] = make_reg(0, roomNumber);
}
void EngineState::shrinkStackToBase() {
- uint size = execution_stack_base + 1;
- assert(_executionStack.size() >= size);
- Common::List<ExecStack>::iterator iter = _executionStack.begin();
- for (uint i = 0; i < size; ++i)
- ++iter;
- _executionStack.erase(iter, _executionStack.end());
+ if (_executionStack.size() > 0) {
+ uint size = executionStackBase + 1;
+ assert(_executionStack.size() >= size);
+ Common::List<ExecStack>::iterator iter = _executionStack.begin();
+ for (uint i = 0; i < size; ++i)
+ ++iter;
+ _executionStack.erase(iter, _executionStack.end());
+ }
}
static kLanguage charToLanguage(const char c) {
@@ -204,7 +223,7 @@ Common::String SciEngine::getSciLanguageString(const char *str, kLanguage lang,
// Copy double-byte character
char c2 = *(++seeker);
if (!c2) {
- warning("SJIS character %02X is missing second byte", c);
+ error("SJIS character %02X is missing second byte", c);
break;
}
fullWidth += c;
@@ -231,8 +250,8 @@ kLanguage SciEngine::getSciLanguage() {
lang = K_LANG_ENGLISH;
- if (_kernel->_selectorCache.printLang != -1) {
- lang = (kLanguage)readSelectorValue(_gamestate->_segMan, _gamestate->_gameObj, SELECTOR(printLang));
+ if (SELECTOR(printLang) != -1) {
+ lang = (kLanguage)readSelectorValue(_gamestate->_segMan, _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.
@@ -265,21 +284,27 @@ kLanguage SciEngine::getSciLanguage() {
default:
lang = K_LANG_ENGLISH;
}
-
- // Store language in printLang selector
- writeSelectorValue(_gamestate->_segMan, _gamestate->_gameObj, SELECTOR(printLang), lang);
}
}
return lang;
}
+void SciEngine::setSciLanguage(kLanguage lang) {
+ if (SELECTOR(printLang) != -1)
+ writeSelectorValue(_gamestate->_segMan, _gameObj, SELECTOR(printLang), lang);
+}
+
+void SciEngine::setSciLanguage() {
+ setSciLanguage(getSciLanguage());
+}
+
Common::String SciEngine::strSplit(const char *str, const char *sep) {
kLanguage lang = getSciLanguage();
kLanguage subLang = K_LANG_NONE;
- if (_kernel->_selectorCache.subtitleLang != -1) {
- subLang = (kLanguage)readSelectorValue(_gamestate->_segMan, _gamestate->_gameObj, SELECTOR(subtitleLang));
+ if (SELECTOR(subtitleLang) != -1) {
+ subLang = (kLanguage)readSelectorValue(_gamestate->_segMan, _gameObj, SELECTOR(subtitleLang));
}
kLanguage secondLang;
diff --git a/engines/sci/engine/state.h b/engines/sci/engine/state.h
index bcdf66d6ef..243a460645 100644
--- a/engines/sci/engine/state.h
+++ b/engines/sci/engine/state.h
@@ -41,17 +41,21 @@ namespace Common {
#include "sci/parser/vocabulary.h"
-#ifdef USE_OLD_MUSIC_FUNCTIONS
-#include "sci/sound/iterator/core.h"
-#endif
#include "sci/sound/soundcmd.h"
namespace Sci {
-class SciEvent;
+class EventManager;
class MessageState;
class SoundCommandParser;
+enum AbortGameState {
+ kAbortNone = 0,
+ kAbortLoadGame = 1,
+ kAbortRestartGame = 2,
+ kAbortQuitGame = 3
+};
+
class DirSeeker {
protected:
reg_t _outbuffer;
@@ -72,11 +76,15 @@ enum {
MAX_SAVE_DIR_SIZE = MAXPATHLEN
};
-/** values for EngineState.restarting_flag */
enum {
- SCI_GAME_IS_NOT_RESTARTING = 0,
- SCI_GAME_WAS_RESTARTED = 1,
- SCI_GAME_IS_RESTARTING_NOW = 2
+ MAX_SAVEGAME_NR = 20 /**< Maximum number of savegames */
+};
+
+// We assume that scripts give us savegameId 0->999 for creating a new save slot
+// and savegameId 1000->1999 for existing save slots ffs. kfile.cpp
+enum {
+ SAVEGAMEID_OFFICIALRANGE_START = 1000,
+ SAVEGAMEID_OFFICIALRANGE_END = 1999
};
class FileHandle {
@@ -105,19 +113,11 @@ public:
/* Non-VM information */
- SciEvent *_event; // Event handling
-
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- SfxState _sound; /**< sound subsystem */
- int sfx_init_flags; /**< flags the sfx subsystem was initialised with */
-#endif
- SoundCommandParser *_soundCmd;
-
- byte restarting_flags; /**< Flags used for restarting */
-
- uint32 game_start_time; /**< The time at which the interpreter was started */
- uint32 last_wait_time; /**< The last time the game invoked Wait() */
+ uint32 gameStartTime; /**< The time at which the interpreter was started */
+ uint32 lastWaitTime; /**< The last time the game invoked Wait() */
+ uint32 _screenUpdateTime; /**< The last time the game updated the screen */
+ void speedThrottler(uint32 neededSleep);
void wait(int16 ticks);
uint32 _throttleCounter; /**< total times kAnimate was invoked */
@@ -130,6 +130,9 @@ public:
DirSeeker _dirseeker;
+ uint _lastSaveVirtualId; // last virtual id fed to kSaveGame, if no kGetSaveFiles was called inbetween
+ uint _lastSaveNewId; // last newly created filename-id by kSaveGame
+
public:
/* VM Information */
@@ -138,12 +141,11 @@ public:
* When called from kernel functions, the vm is re-started recursively on
* the same stack. This variable contains the stack base for the current vm.
*/
- int execution_stack_base;
+ int executionStackBase;
bool _executionStackPosChanged; /**< Set to true if the execution stack position should be re-evaluated by the vm */
reg_t r_acc; /**< Accumulator */
- int16 restAdjust; /**< &rest register (only used for save games) */
- int16 restAdjustCur; /**< current &rest register (only used for save games) */
+ int16 restAdjust; /**< current &rest register */
reg_t r_prev; /**< previous comparison result */
StackPtr stack_base; /**< Pointer to the least stack element */
@@ -152,35 +154,31 @@ public:
// 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
+ reg_t *variablesBase[4]; ///< Used for referencing VM ops
+ SegmentId variablesSegment[4]; ///< Same as above, contains segment IDs
+ int variablesMax[4]; ///< Max. values for all variables
- Script *script_000; /**< script 000, e.g. for globals */
+ AbortGameState abortScriptProcessing;
+ bool gameWasRestarted;
- 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
+ int scriptStepCounter; // Counts the number of steps executed
+ int scriptGCInterval; // Number of steps in between gcs
uint16 currentRoomNumber() const;
void setRoomNumber(uint16 roomNumber);
/**
+ * Sets global variables from script 0
+ */
+ void initGlobals();
+
+ /**
* Shrink execution stack to size.
- * Contains an assert it is not already smaller.
+ * Contains an assert if it is not already smaller.
*/
void shrinkStackToBase();
- reg_t _gameObj; /**< Pointer to the game object */
-
- int gc_countdown; /**< Number of kernel calls until next gc */
+ int gcCountDown; /**< Number of kernel calls until next gc */
public:
MessageState *_msgState;
@@ -197,8 +195,6 @@ public:
* 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/static_selectors.cpp b/engines/sci/engine/static_selectors.cpp
index 0f558f2dc8..85089e74c8 100644
--- a/engines/sci/engine/static_selectors.cpp
+++ b/engines/sci/engine/static_selectors.cpp
@@ -100,13 +100,24 @@ static const SelectorRemap sciSelectorRemap[] = {
{ SCI_VERSION_0_EARLY, SCI_VERSION_0_LATE, "moveDone", 170 },
{ SCI_VERSION_0_EARLY, SCI_VERSION_0_LATE, "points", 316 },
{ SCI_VERSION_0_EARLY, SCI_VERSION_0_LATE, "flags", 368 },
- { SCI_VERSION_1_EARLY, SCI_VERSION_1_1, "nodePtr", 44 },
- { SCI_VERSION_1_LATE, SCI_VERSION_1_1, "cantBeHere", 57 },
- { SCI_VERSION_1_EARLY, SCI_VERSION_1_1, "topString", 101 },
- { SCI_VERSION_1_EARLY, SCI_VERSION_1_1, "flags", 102 },
+ { SCI_VERSION_1_EARLY, SCI_VERSION_1_LATE, "nodePtr", 44 },
+ { SCI_VERSION_1_LATE, SCI_VERSION_1_LATE, "cantBeHere", 57 },
+ { SCI_VERSION_1_EARLY, SCI_VERSION_1_LATE, "topString", 101 },
+ { SCI_VERSION_1_EARLY, SCI_VERSION_1_LATE, "flags", 102 },
+ // SCI1.1
+ { SCI_VERSION_1_1, SCI_VERSION_1_1, "nodePtr", 41 },
+ { SCI_VERSION_1_1, SCI_VERSION_1_1, "cantBeHere", 54 },
+ { SCI_VERSION_1_1, SCI_VERSION_1_1, "topString", 98 },
+ { SCI_VERSION_1_1, SCI_VERSION_1_1, "flags", 99 },
+ // quitGame
+ // restart
+ // hide
{ SCI_VERSION_1_1, SCI_VERSION_1_1,"scaleSignal", 103 },
{ SCI_VERSION_1_1, SCI_VERSION_1_1, "scaleX", 104 },
{ SCI_VERSION_1_1, SCI_VERSION_1_1, "scaleY", 105 },
+ { SCI_VERSION_1_1, SCI_VERSION_1_1, "maxScale", 106 },
+ { SCI_VERSION_1_1, SCI_VERSION_1_1, "vanishingX", 107 },
+ { SCI_VERSION_1_1, SCI_VERSION_1_1, "vanishingY", 108 },
{ SCI_VERSION_NONE, SCI_VERSION_NONE, 0, 0 }
};
@@ -139,16 +150,26 @@ Common::StringArray Kernel::checkStaticSelectorNames() {
}
for (const SelectorRemap *selectorRemap = sciSelectorRemap; selectorRemap->slot; ++selectorRemap) {
- uint32 slot = selectorRemap->slot;
- if (selectorRemap->slot >= names.size())
- names.resize(selectorRemap->slot + 1);
if (getSciVersion() >= selectorRemap->minVersion && getSciVersion() <= selectorRemap->maxVersion) {
- // The SCI1 selectors we use exist in SCI1.1 too, offset by 3
- if (selectorRemap->minVersion >= SCI_VERSION_1_EARLY && getSciVersion() == SCI_VERSION_1_1)
- slot -= 3;
+ const uint32 slot = selectorRemap->slot;
+ if (slot >= names.size())
+ names.resize(slot + 1);
names[slot] = selectorRemap->name;
}
}
+
+ if (g_sci->getGameId() == GID_HOYLE4) {
+ // The demo of Hoyle 4 is one of the few demos with lip syncing and no selector vocabulary.
+ // This needs two selectors, "syncTime" and "syncCue", which keep changing positions in each
+ // game. Usually, games with speech and lip sync have a selector vocabulary, so we don't need
+ // to set these two selectors, but we need for Hoyle...
+ if (names.size() < 276)
+ names.resize(276);
+
+ names[274] = "syncTime";
+ names[275] = "syncCue";
+ }
+
#ifdef ENABLE_SCI32
} else {
// SCI2+
diff --git a/engines/sci/engine/vm.cpp b/engines/sci/engine/vm.cpp
index 1dcdf450ba..8108440102 100644
--- a/engines/sci/engine/vm.cpp
+++ b/engines/sci/engine/vm.cpp
@@ -30,24 +30,104 @@
#include "sci/sci.h"
#include "sci/console.h"
-#include "sci/debug.h" // for g_debugState
#include "sci/resource.h"
#include "sci/engine/features.h"
#include "sci/engine/state.h"
#include "sci/engine/kernel.h"
-#include "sci/engine/seg_manager.h"
#include "sci/engine/script.h"
+#include "sci/engine/seg_manager.h"
+#include "sci/engine/selector.h" // for SELECTOR
#include "sci/engine/gc.h"
namespace Sci {
const reg_t NULL_REG = {0, 0};
const reg_t SIGNAL_REG = {0, SIGNAL_OFFSET};
-
+const reg_t TRUE_REG = {0, 1};
//#define VM_DEBUG_SEND
#define SCI_XS_CALLEE_LOCALS ((SegmentId)-1)
+#define END Script_None
+
+opcode_format g_opcode_formats[128][4] = {
+ /*00*/
+ {Script_None}, {Script_None}, {Script_None}, {Script_None},
+ /*04*/
+ {Script_None}, {Script_None}, {Script_None}, {Script_None},
+ /*08*/
+ {Script_None}, {Script_None}, {Script_None}, {Script_None},
+ /*0C*/
+ {Script_None}, {Script_None}, {Script_None}, {Script_None},
+ /*10*/
+ {Script_None}, {Script_None}, {Script_None}, {Script_None},
+ /*14*/
+ {Script_None}, {Script_None}, {Script_None}, {Script_SRelative, END},
+ /*18*/
+ {Script_SRelative, END}, {Script_SRelative, END}, {Script_SVariable, END}, {Script_None},
+ /*1C*/
+ {Script_SVariable, END}, {Script_None}, {Script_None}, {Script_Variable, END},
+ /*20*/
+ {Script_SRelative, Script_Byte, END}, {Script_Variable, Script_Byte, END}, {Script_Variable, Script_Byte, END}, {Script_Variable, Script_SVariable, Script_Byte, END},
+ /*24 (24=ret)*/
+ {Script_End}, {Script_Byte, END}, {Script_Invalid}, {Script_Invalid},
+ /*28*/
+ {Script_Variable, END}, {Script_Invalid}, {Script_Byte, END}, {Script_Variable, Script_Byte, END},
+ /*2C*/
+ {Script_SVariable, END}, {Script_SVariable, Script_Variable, END}, {Script_None}, {Script_Invalid},
+ /*30*/
+ {Script_None}, {Script_Property, END}, {Script_Property, END}, {Script_Property, END},
+ /*34*/
+ {Script_Property, END}, {Script_Property, END}, {Script_Property, END}, {Script_Property, END},
+ /*38*/
+ {Script_Property, END}, {Script_SRelative, END}, {Script_SRelative, END}, {Script_None},
+ /*3C*/
+ {Script_None}, {Script_None}, {Script_None}, {Script_Word},
+ /*40-4F*/
+ {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
+ {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
+ {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
+ {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
+ /*50-5F*/
+ {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
+ {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
+ {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
+ {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
+ /*60-6F*/
+ {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
+ {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
+ {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
+ {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
+ /*70-7F*/
+ {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
+ {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
+ {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
+ {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}
+};
+#undef END
+
+// TODO: script_adjust_opcode_formats should probably be part of the
+// constructor (?) of a VirtualMachine or a ScriptManager class.
+void script_adjust_opcode_formats() {
+ if (g_sci->_features->detectLofsType() != SCI_VERSION_0_EARLY) {
+ g_opcode_formats[op_lofsa][0] = Script_Offset;
+ g_opcode_formats[op_lofss][0] = Script_Offset;
+ }
+
+#ifdef ENABLE_SCI32
+ // In SCI32, some arguments are now words instead of bytes
+ if (getSciVersion() >= SCI_VERSION_2) {
+ g_opcode_formats[op_calle][2] = Script_Word;
+ g_opcode_formats[op_callk][1] = Script_Word;
+ g_opcode_formats[op_super][1] = Script_Word;
+ g_opcode_formats[op_send][0] = Script_Word;
+ g_opcode_formats[op_self][0] = Script_Word;
+ g_opcode_formats[op_call][1] = Script_Word;
+ g_opcode_formats[op_callb][1] = Script_Word;
+ }
+#endif
+}
+
/**
* Adds an entry to the top of the execution stack.
*
@@ -59,6 +139,8 @@ const reg_t SIGNAL_REG = {0, SIGNAL_OFFSET};
* @param[in] argp Heap pointer to the first parameter
* @param[in] selector The selector by which it was called or
* NULL_SELECTOR if n.a. For debugging.
+ * @param[in] exportId The exportId by which it was called or
+ * -1 if n.a. For debugging.
* @param[in] sendp Pointer to the object which the message was
* sent to. Equal to objp for anything but super.
* @param[in] origin Number of the execution stack element this
@@ -69,7 +151,7 @@ const reg_t SIGNAL_REG = {0, SIGNAL_OFFSET};
* @return A pointer to the new exec stack TOS entry
*/
static ExecStack *add_exec_stack_entry(Common::List<ExecStack> &execStack, reg_t pc, StackPtr sp,
- reg_t objp, int argc, StackPtr argp, Selector selector,
+ reg_t objp, int argc, StackPtr argp, Selector selector, int exportId, int localCallOffset,
reg_t sendp, int origin, SegmentId local_segment);
@@ -94,20 +176,24 @@ static ExecStack *add_exec_stack_varselector(Common::List<ExecStack> &execStack,
// validation functionality
-#ifndef DISABLE_VALIDATIONS
-
static reg_t &validate_property(Object *obj, int index) {
// A static dummy reg_t, which we return if obj or index turn out to be
// invalid. Note that we cannot just return NULL_REG, because client code
// may modify the value of the returned reg_t.
static reg_t dummyReg = NULL_REG;
+ // FIXME/TODO: Where does this occur? Returning a dummy reg here could lead
+ // to all sorts of issues! Turned it into an error for now...
+ // If this occurs, it means there's probably something wrong with the garbage
+ // collector, so don't hide it with fake return values
if (!obj) {
- debugC(2, kDebugLevelVM, "[VM] Sending to disposed object!");
- return dummyReg;
+ error("validate_property: Sending to disposed object");
+ //return dummyReg;
}
if (index < 0 || (uint)index >= obj->getVarCount()) {
+ // This is same way sierra does it and there are some games, that contain such scripts like
+ // iceman script 998 (fred::canBeHere, executed right at the start)
debugC(2, kDebugLevelVM, "[VM] Invalid property #%d (out of [0..%d]) requested!",
index, obj->getVarCount());
return dummyReg;
@@ -127,7 +213,10 @@ static StackPtr validate_stack_addr(EngineState *s, StackPtr sp) {
static int validate_arithmetic(reg_t reg) {
if (reg.segment) {
- warning("[VM] Attempt to read arithmetic value from non-zero segment [%04x]", reg.segment);
+ // The results of this are likely unpredictable... It most likely means that a kernel function is returning something wrong.
+ // If such an error occurs, we usually need to find the last kernel function called and check its return value. Check
+ // callKernelFunc() below
+ error("[VM] Attempt to read arithmetic value from non-zero segment [%04x]. Address: %04x:%04x", reg.segment, PRINT_REG(reg));
return 0;
}
@@ -135,15 +224,10 @@ static int validate_arithmetic(reg_t reg) {
}
static int signed_validate_arithmetic(reg_t reg) {
- if (reg.segment) {
- warning("[VM] Attempt to read arithmetic value from non-zero segment [%04x]", reg.segment);
- return 0;
- }
-
- return (int16)reg.offset;
+ return (int16)validate_arithmetic(reg);
}
-static bool validate_variable(reg_t *r, reg_t *stack_base, int type, int max, int index, int line) {
+static bool validate_variable(reg_t *r, reg_t *stack_base, int type, int max, int index) {
const char *names[4] = {"global", "local", "temp", "param"};
if (index < 0 || index >= max) {
@@ -158,10 +242,15 @@ static bool validate_variable(reg_t *r, reg_t *stack_base, int type, int max, in
if (type == VAR_PARAM || type == VAR_TEMP) {
int total_offset = r - stack_base;
if (total_offset < 0 || total_offset >= VM_STACK_SIZE) {
- warning("%s", txt.c_str());
- warning("[VM] Access would be outside even of the stack (%d); access denied", total_offset);
+ // Fatal, as the game is trying to do an OOB access
+ error("%s. [VM] Access would be outside even of the stack (%d); access denied", txt.c_str(), total_offset);
return false;
} else {
+ // WORKAROUND: Mixed-Up Mother Goose tries to use an invalid parameter in Event::new().
+ // Just skip around it here so we don't error out in validate_arithmetic.
+ if (g_sci->getGameId() == GID_MOTHERGOOSE && getSciVersion() <= SCI_VERSION_1_1 && type == VAR_PARAM && index == 1)
+ return false;
+
debugC(2, kDebugLevelVM, "%s", txt.c_str());
debugC(2, kDebugLevelVM, "[VM] Access within stack boundaries; access granted.");
return true;
@@ -173,15 +262,139 @@ static bool validate_variable(reg_t *r, reg_t *stack_base, int type, int max, in
return true;
}
-static reg_t validate_read_var(reg_t *r, reg_t *stack_base, int type, int max, int index, int line, reg_t default_value) {
- if (validate_variable(r, stack_base, type, max, index, line))
+struct SciTrackOriginReply {
+ int scriptNr;
+ Common::String objectName;
+ Common::String methodName;
+ int localCallOffset;
+};
+
+static reg_t trackOriginAndFindWorkaround(int index, const SciWorkaroundEntry *workaroundList, SciTrackOriginReply *trackOrigin) {
+ EngineState *state = g_sci->getEngineState();
+ ExecStack *lastCall = state->xs;
+ Script *local_script = state->_segMan->getScriptIfLoaded(lastCall->local_segment);
+ int curScriptNr = local_script->getScriptNumber();
+
+ if (lastCall->debugLocalCallOffset != -1) {
+ // if lastcall was actually a local call search back for a real call
+ Common::List<ExecStack>::iterator callIterator = state->_executionStack.end();
+ while (callIterator != state->_executionStack.begin()) {
+ callIterator--;
+ ExecStack loopCall = *callIterator;
+ if ((loopCall.debugSelector != -1) || (loopCall.debugExportId != -1)) {
+ lastCall->debugSelector = loopCall.debugSelector;
+ lastCall->debugExportId = loopCall.debugExportId;
+ break;
+ }
+ }
+ }
+
+ Common::String curObjectName = state->_segMan->getObjectName(lastCall->sendp);
+ Common::String curMethodName;
+ const SciGameId gameId = g_sci->getGameId();
+
+ if (lastCall->type == EXEC_STACK_TYPE_CALL) {
+ if (lastCall->debugSelector != -1) {
+ curMethodName = g_sci->getKernel()->getSelectorName(lastCall->debugSelector);
+ } else if (lastCall->debugExportId != -1) {
+ curObjectName = "";
+ curMethodName = curMethodName.printf("export %d", lastCall->debugExportId);
+ }
+ }
+
+ if (workaroundList) {
+ // Search if there is a workaround for this one
+ const SciWorkaroundEntry *workaround;
+ int16 inheritanceLevel = 0;
+ Common::String searchObjectName = curObjectName;
+ reg_t searchObject = lastCall->sendp;
+ do {
+ workaround = workaroundList;
+ while (workaround->objectName) {
+ if (workaround->gameId == gameId && workaround->scriptNr == curScriptNr
+ && ((workaround->inheritanceLevel == -1) || (workaround->inheritanceLevel == inheritanceLevel))
+ && (workaround->objectName == searchObjectName)
+ && workaround->methodName == curMethodName && workaround->localCallOffset == lastCall->debugLocalCallOffset && workaround->index == index) {
+ // Workaround found
+ return workaround->newValue;
+ }
+ workaround++;
+ }
+
+ // Go back to the parent
+ inheritanceLevel++;
+ searchObject = state->_segMan->getObject(searchObject)->getSuperClassSelector();
+ if (!searchObject.isNull())
+ searchObjectName = state->_segMan->getObjectName(searchObject);
+ } while (!searchObject.isNull()); // no parent left?
+ }
+
+ // give caller origin data
+ trackOrigin->objectName = curObjectName;
+ trackOrigin->methodName = curMethodName;
+ trackOrigin->scriptNr = curScriptNr;
+ trackOrigin->localCallOffset = lastCall->debugLocalCallOffset;
+ return make_reg(0xFFFF, 0xFFFF);
+}
+
+// gameID, scriptNr,lvl, object-name, method-name, call, index, replace
+static const SciWorkaroundEntry uninitializedReadWorkarounds[] = {
+ { GID_LAURABOW2, 24, 0, "gcWin", "open", -1, 5, { 0, 0xf } }, // is used as priority for game menu
+ { GID_FREDDYPHARKAS, 24, 0, "gcWin", "open", -1, 5, { 0, 0xf } }, // is used as priority for game menu
+ { GID_FREDDYPHARKAS, 31, 0, "quitWin", "open", -1, 5, { 0, 0xf } }, // is used as priority for game menu
+ { GID_GK2, 11, 0, "", "export 10", -1, 3, { 0, 0 } }, // called when the game starts
+ { GID_JONES, 232, 0, "weekendText", "draw", 0x3d3, 0, { 0, 0 } }, // jones/cd only - gets called during the game
+ { GID_JONES, 255, 0, "", "export 0", -1, 13, { 0, 0 } }, // jones/ega&vga only - called when the game starts
+ { GID_JONES, 255, 0, "", "export 0", -1, 14, { 0, 0 } }, // jones/ega&vga only - called when the game starts
+ { GID_LSL1, 720, 0, "rm720", "init", -1, 0, { 0, 0 } }, // age check room
+ { GID_LSL3, 997, 0, "TheMenuBar", "handleEvent", -1, 1, { 0, 0xf } }, // when setting volume the first time, this temp is used to set volume on entry (normally it would have been initialized to 's')
+ { GID_LSL6, 928, -1, "Narrator", "startText", -1, 0, { 0, 0 } }, // used by various objects that are even translated in foreign versions, that's why we use the base-class
+ { GID_ISLANDBRAIN, 140, 0, "piece", "init", -1, 3, { 0, 1 } }, // first puzzle right at the start, some initialization variable. bnt is done on it, and it should be non-0
+ { GID_ISLANDBRAIN, 268, 0, "anElement", "select", -1, 0, { 0, 0 } }, // elements puzzle, gets used before super TextIcon
+ { GID_KQ5, 0, 0, "", "export 29", -1, 3, { 0, 0 } }, // called when playing harp for the harpies, is used for kDoAudio
+ { GID_KQ5, 25, 0, "rm025", "doit", -1, 0, { 0, 0 } }, // inside witch forest, where the walking rock is
+ { GID_KQ6, 903, 0, "controlWin", "open", -1, 4, { 0, 0 } }, // when opening the controls window (save, load etc)
+ { GID_KQ6, 500, 0, "rm500", "init", -1, 0, { 0, 0 } }, // going to island of the beast
+ { GID_KQ6, 520, 0, "rm520", "init", -1, 0, { 0, 0 } }, // going to boiling water trap on beast isle
+ { GID_KQ6, 30, 0, "rats", "changeState", -1, 0, { 0, 0 } }, // rats in the catacombs
+ { GID_LSL6, 85, 0, "washcloth", "doVerb", -1, 0, { 0, 0 } }, // washcloth in inventory
+ { GID_SQ1, 703, 0, "", "export 1", -1, 0, { 0, 0 } }, // sub that's called from several objects while on sarien battle cruiser
+ { GID_SQ1, 703, 0, "firePulsar", "changeState", 0x18a, 0, { 0, 0 } }, // export 1, but called locally (when shooting at aliens)
+ { GID_SQ4, 928, 0, "Narrator", "startText", -1, 1000, { 0, 1 } }, // sq4cd: method returns this to the caller
+ { GID_SQ6, 0, 0, "SQ6", "init", -1, 2, { 0, 0 } }, // called when the game starts
+ { GID_SQ6, 64950, 0, "View", "handleEvent", -1, 0, { 0, 0 } }, // called when pressing "Start game" in the main menu
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+static reg_t validate_read_var(reg_t *r, reg_t *stack_base, int type, int max, int index, reg_t default_value) {
+ if (validate_variable(r, stack_base, type, max, index)) {
+ if (r[index].segment == 0xffff) {
+ switch (type) {
+ case VAR_TEMP: {
+ // Uninitialized read on a temp
+ // We need to find correct replacements for each situation manually
+ SciTrackOriginReply originReply;
+ r[index] = trackOriginAndFindWorkaround(index, uninitializedReadWorkarounds, &originReply);
+ if ((r[index].segment == 0xFFFF) && (r[index].offset == 0xFFFF))
+ error("Uninitialized read for temp %d from method %s::%s (script %d, localCall %x)", index, originReply.objectName.c_str(), originReply.methodName.c_str(), originReply.scriptNr, originReply.localCallOffset);
+ break;
+ }
+ case VAR_PARAM:
+ // Out-of-bounds read for a parameter that goes onto stack and hits an uninitialized temp
+ // We return 0 currently in that case
+ warning("Read for a parameter goes out-of-bounds, onto the stack and gets uninitialized temp");
+ return NULL_REG;
+ default:
+ break;
+ }
+ }
return r[index];
- else
+ } else
return default_value;
}
-static void validate_write_var(reg_t *r, reg_t *stack_base, int type, int max, int index, int line, reg_t value, SegManager *segMan, Kernel *kernel) {
- if (validate_variable(r, stack_base, type, max, index, line)) {
+static void validate_write_var(reg_t *r, reg_t *stack_base, int type, int max, int index, reg_t value, SegManager *segMan, Kernel *kernel) {
+ if (validate_variable(r, stack_base, type, max, index)) {
// WORKAROUND: This code is needed to work around a probable script bug, or a
// limitation of the original SCI engine, which can be observed in LSL5.
@@ -200,44 +413,34 @@ static void validate_write_var(reg_t *r, reg_t *stack_base, int type, int max, i
// stopGroop object, which points to ego, to the new ego object. If this is not
// done, ego's movement will not be updated properly, so the result is
// unpredictable (for example in LSL5, Patti spins around instead of walking).
- if (index == 0 && type == VAR_GLOBAL) { // global 0 is ego
+ if (index == 0 && type == VAR_GLOBAL && getSciVersion() > SCI_VERSION_0_EARLY) { // global 0 is ego
reg_t stopGroopPos = segMan->findObjectByName("stopGroop");
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 (lookupSelector(segMan, stopGroopPos, kernel->_selectorCache.client, &varp, NULL) == kSelectorVariable) {
+ if (lookupSelector(segMan, stopGroopPos, SELECTOR(client), &varp, NULL) == kSelectorVariable) {
reg_t *clientVar = varp.getPointer(segMan);
*clientVar = value;
}
}
}
+ // If we are writing an uninitialized value into a temp, we remove the uninitialized segment
+ // this happens at least in sq1/room 44 (slot-machine), because a send is missing parameters, then
+ // those parameters are taken from uninitialized stack and afterwards they are copied back into temps
+ // if we don't remove the segment, we would get false-positive uninitialized reads later
+ if (type == VAR_TEMP && value.segment == 0xffff)
+ value.segment = 0;
+
r[index] = value;
}
}
-#else
-// Non-validating alternatives
-
-# define validate_stack_addr(s, sp) sp
-# define validate_arithmetic(r) ((r).offset)
-# define signed_validate_arithmetic(r) ((int16)(r).offset)
-# define validate_variable(r, sb, t, m, i, l)
-# define validate_read_var(r, sb, t, m, i, l, dv) ((r)[i])
-# define validate_write_var(r, sb, t, m, i, l, v, sm, k) ((r)[i] = (v))
-# define validate_property(o, p) ((o)->_variables[p])
-
-#endif
-
-#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 READ_VAR(type, index) validate_read_var(s->variables[type], s->stack_base, type, s->variablesMax[type], index, s->r_acc)
+#define WRITE_VAR(type, index, value) validate_write_var(s->variables[type], s->stack_base, type, s->variablesMax[type], index, 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)))
-#define ACC_AUX_LOAD() aux_acc = signed_validate_arithmetic(s->r_acc)
-#define ACC_AUX_STORE() s->r_acc = make_reg(0, aux_acc)
-
-#define OBJ_PROPERTY(o, p) (validate_property(o, p))
// Operating on the stack
// 16 bit:
@@ -247,12 +450,32 @@ static void validate_write_var(reg_t *r, reg_t *stack_base, int type, int max, i
#define PUSH32(a) (*(validate_stack_addr(s, (s->xs->sp)++)) = (a))
#define POP32() (*(validate_stack_addr(s, --(s->xs->sp))))
+bool SciEngine::checkExportBreakpoint(uint16 script, uint16 pubfunct) {
+ if (_debugState._activeBreakpointTypes & BREAK_EXPORT) {
+ uint32 bpaddress;
+
+ bpaddress = (script << 16 | pubfunct);
+
+ Common::List<Breakpoint>::const_iterator bp;
+ for (bp = _debugState._breakpoints.begin(); bp != _debugState._breakpoints.end(); ++bp) {
+ if (bp->type == BREAK_EXPORT && bp->address == bpaddress) {
+ _console->DebugPrintf("Break on script %d, export %d\n", script, pubfunct);
+ _debugState.debugging = true;
+ _debugState.breakpointWasHit = true;
+ return true;;
+ }
+ }
+ }
+
+ return false;
+}
+
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);
Script *scr = s->_segMan->getScriptIfLoaded(seg);
if (!scr || scr->isMarkedAsDeleted()) { // Script not present yet?
- seg = script_instantiate(g_sci->getResMan(), s->_segMan, script);
+ seg = s->_segMan->instantiateScript(script);
scr = s->_segMan->getScript(seg);
}
@@ -270,24 +493,9 @@ ExecStack *execute_method(EngineState *s, uint16 script, uint16 pubfunct, StackP
}
// Check if a breakpoint is set on this method
- if (g_debugState._activeBreakpointTypes & BREAK_EXPORT) {
- uint32 bpaddress;
-
- bpaddress = (script << 16 | pubfunct);
-
- Common::List<Breakpoint>::const_iterator bp;
- for (bp = g_debugState._breakpoints.begin(); bp != g_debugState._breakpoints.end(); ++bp) {
- if (bp->type == BREAK_EXPORT && bp->address == bpaddress) {
- Console *con = g_sci->getSciDebugger();
- con->DebugPrintf("Break on script %d, export %d\n", script, pubfunct);
- g_debugState.debugging = true;
- g_debugState.breakpointWasHit = true;
- break;
- }
- }
- }
+ g_sci->checkExportBreakpoint(script, pubfunct);
- return add_exec_stack_entry(s->_executionStack, make_reg(seg, temp), sp, calling_obj, argc, argp, -1, calling_obj, s->_executionStack.size()-1, seg);
+ return add_exec_stack_entry(s->_executionStack, make_reg(seg, temp), sp, calling_obj, argc, argp, -1, pubfunct, -1, calling_obj, s->_executionStack.size()-1, seg);
}
@@ -297,7 +505,7 @@ static void _exec_varselectors(EngineState *s) {
ExecStack &xs = s->_executionStack.back();
reg_t *var = xs.getVarPointer(s->_segMan);
if (!var) {
- warning("Invalid varselector exec stack entry");
+ error("Invalid varselector exec stack entry");
} else {
// varselector access?
if (xs.argc) { // write?
@@ -325,6 +533,30 @@ struct CallsStruct {
int type; /**< Same as ExecStack.type */
};
+bool SciEngine::checkSelectorBreakpoint(reg_t send_obj, int selector) {
+ if (_debugState._activeBreakpointTypes & BREAK_SELECTOR) {
+ char method_name[256];
+
+ sprintf(method_name, "%s::%s", _gamestate->_segMan->getObjectName(send_obj), getKernel()->getSelectorName(selector).c_str());
+
+ Common::List<Breakpoint>::const_iterator bp;
+ for (bp = _debugState._breakpoints.begin(); bp != _debugState._breakpoints.end(); ++bp) {
+ int cmplen = bp->name.size();
+ if (bp->name.lastChar() != ':')
+ cmplen = 256;
+
+ if (bp->type == BREAK_SELECTOR && !strncmp(bp->name.c_str(), method_name, cmplen)) {
+ _console->DebugPrintf("Break on %s (in [%04x:%04x])\n", method_name, PRINT_REG(send_obj));
+ _debugState.debugging = true;
+ _debugState.breakpointWasHit = true;
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPtr sp, int framesize, StackPtr argp) {
// send_obj and work_obj are equal for anything but 'super'
// Returns a pointer to the TOS exec_stack element
@@ -349,27 +581,7 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt
}
// Check if a breakpoint is set on this method
- if (g_debugState._activeBreakpointTypes & BREAK_SELECTOR) {
- char method_name[256];
-
- sprintf(method_name, "%s::%s", s->_segMan->getObjectName(send_obj), g_sci->getKernel()->getSelectorName(selector).c_str());
-
- Common::List<Breakpoint>::const_iterator bp;
- for (bp = g_debugState._breakpoints.begin(); bp != g_debugState._breakpoints.end(); ++bp) {
- int cmplen = bp->name.size();
- if (bp->name.lastChar() != ':')
- cmplen = 256;
-
- 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));
- printSendActions = true;
- g_debugState.debugging = true;
- g_debugState.breakpointWasHit = true;
- break;
- }
- }
- }
+ printSendActions = g_sci->checkSelectorBreakpoint(send_obj, selector);
#ifdef VM_DEBUG_SEND
printf("Send to %04x:%04x, selector %04x (%s):", PRINT_REG(send_obj), selector, g_sci->getKernel()->getSelectorName(selector).c_str());
@@ -405,19 +617,30 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt
}
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));
+ // argc can indeed be bigger than 1 in some cases, and it's usually the
+ // result of a script bug
+
+ const char *objectName = s->_segMan->getObjectName(send_obj);
+
+ if (!strcmp(objectName, "Sq4GlobalNarrator") && selector == 606) {
+ // SQ4 has a script bug in the Sq4GlobalNarrator object when invoking the
+ // returnVal selector, which doesn't affect gameplay, thus don't diplay it
+ } else if (!strcmp(objectName, "longSong") && selector == 3 && g_sci->getGameId() == GID_QFG1) {
+ // QFG1VGA has a script bug in the longSong object when invoking the
+ // loop selector, which doesn't affect gameplay, thus don't diplay it
+ } else if (!strcmp(objectName, "PuzPiece") && selector == 77 && g_sci->getGameId() == GID_CASTLEBRAIN) {
+ // Castle of Dr. Brain has a script bug in the PuzPiece object when invoking
+ // the value selector, which doesn't affect gameplay, thus don't display it
+ } else {
+ // Unknown script bug, show it. Usually these aren't fatal.
+ reg_t oldReg = *varp.getPointer(s->_segMan);
+ reg_t newReg = argp[1];
+ const char *selectorName = g_sci->getKernel()->getSelectorName(selector).c_str();
+ warning("send_selector(): argc = %d while modifying variable selector "
+ "%x (%s) of object %04x:%04x (%s) from %04x:%04x to %04x:%04x",
+ argc, selector, selectorName, PRINT_REG(send_obj),
+ objectName, PRINT_REG(oldReg), PRINT_REG(newReg));
+ }
}
{
@@ -444,7 +667,28 @@ 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 (printSendActions) {
- debug("[invoke selector]\n");
+ printf("[invoke selector]");
+#ifndef VM_DEBUG_SEND
+ int displaySize = 0;
+ for (int argNr = 1; argNr <= argc; argNr++) {
+ if (argNr == 1)
+ printf(" - ");
+ reg_t curParam = argp[argNr];
+ if (curParam.segment) {
+ printf("[%04x:%04x] ", PRINT_REG(curParam));
+ displaySize += 12;
+ } else {
+ printf("[%04x] ", curParam.offset);
+ displaySize += 7;
+ }
+ if (displaySize > 50) {
+ if (argNr < argc)
+ printf("...");
+ break;
+ }
+ }
+#endif
+ printf("\n");
printSendActions = false;
}
@@ -477,7 +721,7 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt
else
add_exec_stack_entry(s->_executionStack, call.address.func, call.sp, work_obj,
call.argc, call.argp,
- call.selector, send_obj, origin, SCI_XS_CALLEE_LOCALS);
+ call.selector, -1, -1, send_obj, origin, SCI_XS_CALLEE_LOCALS);
}
_exec_varselectors(s);
@@ -486,7 +730,7 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt
}
static ExecStack *add_exec_stack_varselector(Common::List<ExecStack> &execStack, reg_t objp, int argc, StackPtr argp, Selector selector, const ObjVarRef& address, int origin) {
- ExecStack *xstack = add_exec_stack_entry(execStack, NULL_REG, 0, objp, argc, argp, selector, objp, origin, SCI_XS_CALLEE_LOCALS);
+ ExecStack *xstack = add_exec_stack_entry(execStack, NULL_REG, 0, objp, argc, argp, selector, -1, -1, objp, origin, SCI_XS_CALLEE_LOCALS);
// Store selector address in sp
xstack->addr.varp = address;
@@ -496,7 +740,7 @@ static ExecStack *add_exec_stack_varselector(Common::List<ExecStack> &execStack,
}
static ExecStack *add_exec_stack_entry(Common::List<ExecStack> &execStack, reg_t pc, StackPtr sp, reg_t objp, int argc,
- StackPtr argp, Selector selector, reg_t sendp, int origin, SegmentId _localsSegment) {
+ StackPtr argp, Selector selector, int exportId, int localCallOffset, reg_t sendp, int origin, SegmentId _localsSegment) {
// Returns new TOS element for the execution stack
// _localsSegment may be -1 if derived from the called object
@@ -520,8 +764,10 @@ static ExecStack *add_exec_stack_entry(Common::List<ExecStack> &execStack, reg_t
*argp = make_reg(0, argc); // SCI code relies on the zeroeth argument to equal argc
// Additional debug information
- xstack.selector = selector;
- xstack.origin = origin;
+ xstack.debugSelector = selector;
+ xstack.debugExportId = exportId;
+ xstack.debugLocalCallOffset = localCallOffset;
+ xstack.debugOrigin = origin;
xstack.type = EXEC_STACK_TYPE_CALL; // Normal call
@@ -529,10 +775,6 @@ static ExecStack *add_exec_stack_entry(Common::List<ExecStack> &execStack, reg_t
return &(execStack.back());
}
-#ifdef DISABLE_VALIDATIONS
-# define kernel_matches_signature(a, b, c, d) 1
-#endif
-
static reg_t pointer_add(EngineState *s, reg_t base, int offset) {
SegmentObj *mobj = s->_segMan->getSegmentObj(base.segment);
@@ -560,84 +802,114 @@ static reg_t pointer_add(EngineState *s, reg_t base, int offset) {
}
}
-static void callKernelFunc(EngineState *s, int kernelFuncNum, int argc) {
-
- if (kernelFuncNum >= (int)g_sci->getKernel()->_kernelFuncs.size())
- error("Invalid kernel function 0x%x requested", kernelFuncNum);
+static void addKernelCallToExecStack(EngineState *s, int kernelCallNr, int argc, reg_t *argv) {
+ // Add stack frame to indicate we're executing a callk.
+ // This is useful in debugger backtraces if this
+ // kernel function calls a script itself.
+ ExecStack *xstack;
+ xstack = add_exec_stack_entry(s->_executionStack, NULL_REG, NULL, NULL_REG, argc, argv - 1, 0, -1, -1, NULL_REG,
+ s->_executionStack.size()-1, SCI_XS_CALLEE_LOCALS);
+ xstack->debugSelector = kernelCallNr;
+ xstack->type = EXEC_STACK_TYPE_KERNEL;
+}
- const KernelFuncWithSignature &kernelFunc = g_sci->getKernel()->_kernelFuncs[kernelFuncNum];
+static void callKernelFunc(EngineState *s, int kernelCallNr, int argc) {
+ Kernel *kernel = g_sci->getKernel();
- if (kernelFunc.signature
- && !g_sci->getKernel()->signatureMatch(kernelFunc.signature, argc, s->xs->sp + 1)) {
- error("[VM] Invalid arguments to kernel call %x", kernelFuncNum);
- }
+ if (kernelCallNr >= (int)kernel->_kernelFuncs.size())
+ error("Invalid kernel function 0x%x requested", kernelCallNr);
+ const KernelFunction &kernelCall = kernel->_kernelFuncs[kernelCallNr];
reg_t *argv = s->xs->sp + 1;
- if (!kernelFunc.isDummy) {
- // Add stack frame to indicate we're executing a callk.
- // This is useful in debugger backtraces if this
- // kernel function calls a script itself.
- ExecStack *xstack;
- xstack = add_exec_stack_entry(s->_executionStack, NULL_REG, NULL, NULL_REG, argc, argv - 1, 0, NULL_REG,
- s->_executionStack.size()-1, SCI_XS_CALLEE_LOCALS);
- xstack->selector = kernelFuncNum;
- xstack->type = EXEC_STACK_TYPE_KERNEL;
-
- //warning("callk %s", kernelFunc.orig_name.c_str());
-
- // 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);
+ if (kernelCall.signature
+ && !kernel->signatureMatch(kernelCall.signature, argc, argv)) {
+ // signature mismatch, check if a workaround is available
+ SciTrackOriginReply originReply;
+ reg_t workaround;
+ workaround = trackOriginAndFindWorkaround(0, kernelCall.workarounds, &originReply);
+ if ((workaround.segment == 0xFFFF) && (workaround.offset == 0xFFFF)) {
+ kernel->signatureDebug(kernelCall.signature, argc, argv);
+ error("[VM] k%s[%x]: signature mismatch via method %s::%s (script %d, localCall %x)", kernelCall.name, kernelCallNr, originReply.objectName.c_str(), originReply.methodName.c_str(), originReply.scriptNr, originReply.localCallOffset);
}
+ // FIXME: implement some real workaround type logic - ignore call, still do call etc.
+ if (workaround.segment == 2)
+ s->r_acc = make_reg(0, workaround.offset);
+ if (workaround.segment)
+ return;
+ }
- // Remove callk stack frame again
- s->_executionStack.pop_back();
+
+ // Call kernel function
+ if (!kernelCall.subFunctionCount) {
+ addKernelCallToExecStack(s, kernelCallNr, argc, argv);
+ s->r_acc = kernelCall.function(s, argc, argv);
} else {
- Common::String warningMsg = "Dummy function " + kernelFunc.orig_name +
- Common::String::printf("[0x%x]", kernelFuncNum) +
- " invoked - ignoring. Params: " +
- Common::String::printf("%d", argc) + " (";
+ // Sub-functions available, check signature and call that one directly
+ if (argc < 1)
+ error("[VM] k%s[%x]: no subfunction-id parameter given", kernelCall.name, kernelCallNr);
+ if (argv[0].segment)
+ error("[VM] k%s[%x]: given subfunction-id is actually a pointer", kernelCall.name, kernelCallNr);
+ const uint16 subId = argv[0].toUint16();
+ // Skip over subfunction-id
+ argc--;
+ argv++;
+ if (subId >= kernelCall.subFunctionCount)
+ error("[VM] k%s: subfunction-id %d requested, but not available", kernelCall.name, subId);
+ const KernelSubFunction &kernelSubCall = kernelCall.subFunctions[subId];
+ if (kernelSubCall.signature && !kernel->signatureMatch(kernelSubCall.signature, argc, argv)) {
+ // Signature mismatch
+ SciTrackOriginReply originReply;
+ reg_t workaround;
+ workaround = trackOriginAndFindWorkaround(0, kernelSubCall.workarounds, &originReply);
+ if ((workaround.segment == 0xFFFF) && (workaround.offset == 0xFFFF)) {
+ kernel->signatureDebug(kernelSubCall.signature, argc, argv);
+ int callNameLen = strlen(kernelCall.name);
+ if (strncmp(kernelCall.name, kernelSubCall.name, callNameLen) == 0) {
+ const char *subCallName = kernelSubCall.name + callNameLen;
+ error("[VM] k%s(%s): signature mismatch via method %s::%s (script %d, localCall %x)", kernelCall.name, subCallName, originReply.objectName.c_str(), originReply.methodName.c_str(), originReply.scriptNr, originReply.localCallOffset);
+ }
+ error("[VM] k%s: signature mismatch via method %s::%s (script %d, localCall %x)", kernelSubCall.name, originReply.objectName.c_str(), originReply.methodName.c_str(), originReply.scriptNr, originReply.localCallOffset);
+ }
+ // FIXME: implement some real workaround type logic - ignore call, still do call etc.
+ if (workaround.segment == 2)
+ s->r_acc = make_reg(0, workaround.offset);
+ if (workaround.segment)
+ return;
+ }
+ if (!kernelSubCall.function)
+ error("[VM] k%s: subfunction-id %d requested, but not available", kernelCall.name, subId);
+ addKernelCallToExecStack(s, kernelCallNr, argc, argv);
+ s->r_acc = kernelSubCall.function(s, argc, argv);
+ }
+
+#if 0
+ // Used for debugging
+ Common::String debugMsg = Common::String::printf("%s [0x%x]", kernelCall.name, kernelCallNr) +
+ Common::String::printf(", %d params: ", argc) +
+ " (";
for (int i = 0; i < argc; i++) {
- warningMsg += Common::String::printf("%04x:%04x", PRINT_REG(argv[i]));
- warningMsg += (i == argc - 1 ? ")" : ", ");
+ debugMsg += Common::String::printf("%04x:%04x", PRINT_REG(argv[i]));
+ debugMsg += (i == argc - 1 ? ")" : ", ");
}
- warning("%s", warningMsg.c_str());
- }
+ debugMsg += ", result: " + Common::String::printf("%04x:%04x", PRINT_REG(s->r_acc));
+ debug("%s", debugMsg.c_str());
+#endif
+
+ // Remove callk stack frame again, if there's still an execution stack
+ if (s->_executionStack.begin() != s->_executionStack.end())
+ s->_executionStack.pop_back();
}
-static void gc_countdown(EngineState *s) {
- if (s->gc_countdown-- <= 0) {
- s->gc_countdown = s->script_gc_interval;
+static void gcCountDown(EngineState *s) {
+ if (s->gcCountDown-- <= 0) {
+ s->gcCountDown = s->scriptGCInterval;
run_gc(s);
}
}
-static const byte _fake_return_buffer[2] = {op_ret << 1, op_ret << 1};
-
-
int readPMachineInstruction(const byte *src, byte &extOpcode, int16 opparams[4]) {
uint offset = 0;
extOpcode = src[offset++]; // Get "extended" opcode (lower bit has special meaning)
@@ -709,50 +981,29 @@ int readPMachineInstruction(const byte *src, byte &extOpcode, int16 opparams[4])
void run_vm(EngineState *s, bool restoring) {
assert(s);
-#ifndef DISABLE_VALIDATIONS
- unsigned int code_buf_size = 0 ; // (Avoid spurious warning)
-#endif
int temp;
- int16 aux_acc; // Auxiliary 16 bit accumulator
reg_t r_temp; // Temporary register
StackPtr s_temp; // Temporary stack pointer
int16 opparams[4]; // opcode parameters
- s->restAdjustCur = s->restAdjust;
- // &rest adjusts the parameter count by this value
+ s->restAdjust = 0; // &rest adjusts the parameter count by this value
// Current execution data:
s->xs = &(s->_executionStack.back());
ExecStack *xs_new = NULL;
Object *obj = s->_segMan->getObject(s->xs->objp);
+ Script *scr = 0;
Script *local_script = s->_segMan->getScriptIfLoaded(s->xs->local_segment);
- int old_execution_stack_base = s->execution_stack_base;
+ int old_executionStackBase = s->executionStackBase;
// Used to detect the stack bottom, for "physical" returns
- const byte *code_buf = NULL; // (Avoid spurious warning)
- if (!local_script) {
+ if (!local_script)
error("run_vm(): program counter gone astray (local_script pointer is null)");
- }
if (!restoring)
- s->execution_stack_base = s->_executionStack.size() - 1;
-
-#ifndef DISABLE_VALIDATIONS
- // Initialize maximum variable count
- if (s->script_000->_localsBlock)
- s->variables_max[VAR_GLOBAL] = s->script_000->_localsBlock->_locals.size();
- else
- s->variables_max[VAR_GLOBAL] = 0;
-#endif
-
- 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;
+ s->executionStackBase = s->_executionStack.size() - 1;
- // SCI code reads the zeroth argument to determine argc
- if (s->script_000->_localsBlock)
- s->variables_base[VAR_GLOBAL] = s->variables[VAR_GLOBAL] = s->script_000->_localsBlock->_locals.begin();
- else
- s->variables_base[VAR_GLOBAL] = s->variables[VAR_GLOBAL] = NULL;
+ s->variablesSegment[VAR_TEMP] = s->variablesSegment[VAR_PARAM] = s->_segMan->findSegmentByType(SEG_TYPE_STACK);
+ s->variablesBase[VAR_TEMP] = s->variablesBase[VAR_PARAM] = s->stack_base;
s->_executionStackPosChanged = true; // Force initialization
@@ -760,101 +1011,90 @@ void run_vm(EngineState *s, bool restoring) {
int var_type; // See description below
int var_number;
- g_debugState.old_pc_offset = s->xs->addr.pc.offset;
- g_debugState.old_sp = s->xs->sp;
+ g_sci->_debugState.old_pc_offset = s->xs->addr.pc.offset;
+ g_sci->_debugState.old_sp = s->xs->sp;
+
+ if (s->abortScriptProcessing != kAbortNone || g_engine->shouldQuit())
+ return; // Stop processing
if (s->_executionStackPosChanged) {
- Script *scr;
+ scr = s->_segMan->getScriptIfLoaded(s->xs->addr.pc.segment);
+ if (!scr)
+ error("No script in segment %d", s->xs->addr.pc.segment);
s->xs = &(s->_executionStack.back());
s->_executionStackPosChanged = false;
- 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", s->xs->addr.pc.segment);
- code_buf = _fake_return_buffer;
-#ifndef DISABLE_VALIDATIONS
- code_buf_size = 2;
-#endif
- s->xs->addr.pc.offset = 1;
-
- scr = NULL;
- obj = NULL;
+ obj = s->_segMan->getObject(s->xs->objp);
+ local_script = s->_segMan->getScriptIfLoaded(s->xs->local_segment);
+ if (!local_script) {
+ // FIXME: Why does this happen? Is the script not loaded yet at this point?
+ warning("Could not find local script from segment %x", s->xs->local_segment);
+ local_script = NULL;
+ s->variablesBase[VAR_LOCAL] = s->variables[VAR_LOCAL] = NULL;
+ s->variablesMax[VAR_LOCAL] = 0;
} else {
- obj = s->_segMan->getObject(s->xs->objp);
- code_buf = scr->_buf;
-#ifndef DISABLE_VALIDATIONS
- code_buf_size = scr->getBufSize();
-#endif
- local_script = s->_segMan->getScriptIfLoaded(s->xs->local_segment);
- if (!local_script) {
- warning("Could not find local script from segment %x", s->xs->local_segment);
- local_script = NULL;
- s->variables_base[VAR_LOCAL] = s->variables[VAR_LOCAL] = NULL;
-#ifndef DISABLE_VALIDATIONS
- s->variables_max[VAR_LOCAL] = 0;
-#endif
- } else {
-
- s->variables_seg[VAR_LOCAL] = local_script->_localsSegment;
- if (local_script->_localsBlock)
- s->variables_base[VAR_LOCAL] = s->variables[VAR_LOCAL] = local_script->_localsBlock->_locals.begin();
- else
- s->variables_base[VAR_LOCAL] = s->variables[VAR_LOCAL] = NULL;
-#ifndef DISABLE_VALIDATIONS
- if (local_script->_localsBlock)
- s->variables_max[VAR_LOCAL] = local_script->_localsBlock->_locals.size();
- else
- 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
- }
- s->variables[VAR_TEMP] = s->xs->fp;
- s->variables[VAR_PARAM] = s->xs->variables_argp;
+ s->variablesSegment[VAR_LOCAL] = local_script->_localsSegment;
+ if (local_script->_localsBlock)
+ s->variablesBase[VAR_LOCAL] = s->variables[VAR_LOCAL] = local_script->_localsBlock->_locals.begin();
+ else
+ s->variablesBase[VAR_LOCAL] = s->variables[VAR_LOCAL] = NULL;
+ if (local_script->_localsBlock)
+ s->variablesMax[VAR_LOCAL] = local_script->_localsBlock->_locals.size();
+ else
+ s->variablesMax[VAR_LOCAL] = 0;
+ s->variablesMax[VAR_TEMP] = s->xs->sp - s->xs->fp;
+ s->variablesMax[VAR_PARAM] = s->xs->argc + 1;
}
-
+ s->variables[VAR_TEMP] = s->xs->fp;
+ s->variables[VAR_PARAM] = s->xs->variables_argp;
}
- if (s->script_abort_flag || g_engine->shouldQuit())
- return; // Emergency
+ if (s->abortScriptProcessing != kAbortNone || g_engine->shouldQuit())
+ return; // Stop processing
// Debug if this has been requested:
// TODO: re-implement sci_debug_flags
- if (g_debugState.debugging /* sci_debug_flags*/) {
- script_debug(s);
- g_debugState.breakpointWasHit = false;
+ if (g_sci->_debugState.debugging /* sci_debug_flags*/) {
+ g_sci->scriptDebug();
+ g_sci->_debugState.breakpointWasHit = false;
}
Console *con = g_sci->getSciDebugger();
if (con->isAttached()) {
con->onFrame();
}
-#ifndef DISABLE_VALIDATIONS
if (s->xs->sp < s->xs->fp)
- error("run_vm(): stack underflow, sp: %04x:%04x, fp: %04x:%04x",
+ error("run_vm(): stack underflow, sp: %04x:%04x, fp: %04x:%04x",
PRINT_REG(*s->xs->sp), PRINT_REG(*s->xs->fp));
- s->variables_max[VAR_TEMP] = s->xs->sp - s->xs->fp;
+ s->variablesMax[VAR_TEMP] = s->xs->sp - s->xs->fp;
- 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
+ if (s->xs->addr.pc.offset >= scr->getBufSize())
+ error("run_vm(): program counter gone astray, addr: %d, code buffer size: %d",
+ s->xs->addr.pc.offset, scr->getBufSize());
// Get opcode
byte extOpcode;
- s->xs->addr.pc.offset += readPMachineInstruction(code_buf + s->xs->addr.pc.offset, extOpcode, opparams);
+ s->xs->addr.pc.offset += readPMachineInstruction(scr->getBuf() + s->xs->addr.pc.offset, extOpcode, opparams);
const byte opcode = extOpcode >> 1;
switch (opcode) {
case op_bnot: // 0x00 (00)
+ // Binary not
s->r_acc = ACC_ARITHMETIC_L(0xffff ^ /*acc*/);
break;
case op_add: // 0x01 (01)
r_temp = POP32();
+
+ // Happens in SQ1, room 28, when throwing the water at Orat
+ if (s->r_acc.segment == 0xFFFF) {
+ // WORKAROUND: init uninitialized variable to 0
+ warning("op_add: attempt to write to uninitialized variable");
+ s->r_acc = NULL_REG;
+ }
+
if (r_temp.segment || s->r_acc.segment) {
reg_t r_ptr = NULL_REG;
int offset;
@@ -912,23 +1152,23 @@ void run_vm(EngineState *s, bool restoring) {
s->r_acc = ACC_ARITHMETIC_L(((int16)POP()) * (int16)/*acc*/);
break;
- case op_div: // 0x04 (04)
- ACC_AUX_LOAD();
- aux_acc = aux_acc != 0 ? ((int16)POP()) / aux_acc : 0;
- ACC_AUX_STORE();
+ case op_div: { // 0x04 (04)
+ int16 divisor = signed_validate_arithmetic(s->r_acc);
+ s->r_acc = make_reg(0, (divisor != 0 ? ((int16)POP()) / divisor : 0));
break;
-
- case op_mod: // 0x05 (05)
- ACC_AUX_LOAD();
- aux_acc = aux_acc != 0 ? ((int16)POP()) % aux_acc : 0;
- ACC_AUX_STORE();
+ }
+ case op_mod: { // 0x05 (05)
+ int16 modulo = signed_validate_arithmetic(s->r_acc);
+ s->r_acc = make_reg(0, (modulo != 0 ? ((int16)POP()) % modulo : 0));
break;
-
+ }
case op_shr: // 0x06 (06)
+ // Shift right logical
s->r_acc = ACC_ARITHMETIC_L(((uint16)POP()) >> /*acc*/);
break;
case op_shl: // 0x07 (07)
+ // Shift left logical
s->r_acc = ACC_ARITHMETIC_L(((uint16)POP()) << /*acc*/);
break;
@@ -954,6 +1194,7 @@ void run_vm(EngineState *s, bool restoring) {
break;
case op_eq_: // 0x0d (13)
+ // ==
s->r_prev = s->r_acc;
r_temp = POP32();
s->r_acc = make_reg(0, r_temp == s->r_acc);
@@ -961,6 +1202,7 @@ void run_vm(EngineState *s, bool restoring) {
break;
case op_ne_: // 0x0e (14)
+ // !=
s->r_prev = s->r_acc;
r_temp = POP32();
s->r_acc = make_reg(0, r_temp != s->r_acc);
@@ -968,6 +1210,7 @@ void run_vm(EngineState *s, bool restoring) {
break;
case op_gt_: // 0x0f (15)
+ // >
s->r_prev = s->r_acc;
r_temp = POP32();
if (r_temp.segment && s->r_acc.segment) {
@@ -975,11 +1218,18 @@ void run_vm(EngineState *s, bool restoring) {
if (r_temp.segment != s->r_acc.segment)
warning("[VM] Comparing pointers in different segments (%04x:%04x vs. %04x:%04x)", PRINT_REG(r_temp), PRINT_REG(s->r_acc));
s->r_acc = make_reg(0, (r_temp.segment == s->r_acc.segment) && r_temp.offset > s->r_acc.offset);
+ } else if (r_temp.segment && !s->r_acc.segment) {
+ if (s->r_acc.offset >= 1000)
+ error("[VM] op_gt: comparsion between a pointer and number");
+ // Pseudo-WORKAROUND: sierra allows any pointer <-> value comparsion
+ // Happens in SQ1, room 28, when throwing the water at Orat
+ s->r_acc = make_reg(0, 1);
} else
s->r_acc = ACC_ARITHMETIC_L(signed_validate_arithmetic(r_temp) > (int16)/*acc*/);
break;
case op_ge_: // 0x10 (16)
+ // >=
s->r_prev = s->r_acc;
r_temp = POP32();
if (r_temp.segment && s->r_acc.segment) {
@@ -991,17 +1241,25 @@ void run_vm(EngineState *s, bool restoring) {
break;
case op_lt_: // 0x11 (17)
+ // <
s->r_prev = s->r_acc;
r_temp = POP32();
if (r_temp.segment && s->r_acc.segment) {
if (r_temp.segment != s->r_acc.segment)
warning("[VM] Comparing pointers in different segments (%04x:%04x vs. %04x:%04x)", PRINT_REG(r_temp), PRINT_REG(s->r_acc));
s->r_acc = make_reg(0, (r_temp.segment == s->r_acc.segment) && r_temp.offset < s->r_acc.offset);
+ } else if (r_temp.segment && !s->r_acc.segment) {
+ if (s->r_acc.offset >= 1000)
+ error("[VM] op_lt: comparsion between a pointer and number");
+ // Pseudo-WORKAROUND: sierra allows any pointer <-> value comparsion
+ // Happens in SQ1, room 58, when giving id-card to robot
+ s->r_acc = make_reg(0, 1);
} else
s->r_acc = ACC_ARITHMETIC_L(signed_validate_arithmetic(r_temp) < (int16)/*acc*/);
break;
case op_le_: // 0x12 (18)
+ // <=
s->r_prev = s->r_acc;
r_temp = POP32();
if (r_temp.segment && s->r_acc.segment) {
@@ -1013,6 +1271,7 @@ void run_vm(EngineState *s, bool restoring) {
break;
case op_ugt_: // 0x13 (19)
+ // > (unsigned)
s->r_prev = s->r_acc;
r_temp = POP32();
@@ -1024,8 +1283,8 @@ void run_vm(EngineState *s, bool restoring) {
// (Print "foo") // Pointer to a string
// (Print 420 5) // Reference to the fifth message in text resource 420
- // It works because in those games, the maximum resource number is 999,
- // so any parameter value above that threshold must be a pointer.
+ // It works because in those games, the maximum resource number is 999,
+ // so any parameter value above that threshold must be a pointer.
if (r_temp.segment && (s->r_acc == make_reg(0, 1000)))
s->r_acc = make_reg(0, 1);
else if (r_temp.segment && s->r_acc.segment)
@@ -1035,6 +1294,7 @@ void run_vm(EngineState *s, bool restoring) {
break;
case op_uge_: // 0x14 (20)
+ // >= (unsigned)
s->r_prev = s->r_acc;
r_temp = POP32();
@@ -1048,6 +1308,7 @@ void run_vm(EngineState *s, bool restoring) {
break;
case op_ult_: // 0x15 (21)
+ // < (unsigned)
s->r_prev = s->r_acc;
r_temp = POP32();
@@ -1061,6 +1322,7 @@ void run_vm(EngineState *s, bool restoring) {
break;
case op_ule_: // 0x16 (22)
+ // <= (unsigned)
s->r_prev = s->r_acc;
r_temp = POP32();
@@ -1074,11 +1336,13 @@ void run_vm(EngineState *s, bool restoring) {
break;
case op_bt: // 0x17 (23)
+ // Branch relative if true
if (s->r_acc.offset || s->r_acc.segment)
s->xs->addr.pc.offset += opparams[0];
break;
case op_bnt: // 0x18 (24)
+ // Branch relative if not true
if (!(s->r_acc.offset || s->r_acc.segment))
s->xs->addr.pc.offset += opparams[0];
break;
@@ -1088,45 +1352,56 @@ void run_vm(EngineState *s, bool restoring) {
break;
case op_ldi: // 0x1a (26)
+ // Load data immediate
s->r_acc = make_reg(0, opparams[0]);
break;
case op_push: // 0x1b (27)
+ // Push to stack
PUSH32(s->r_acc);
break;
case op_pushi: // 0x1c (28)
+ // Push immediate
PUSH(opparams[0]);
break;
case op_toss: // 0x1d (29)
+ // TOS (Top Of Stack) subtract
s->xs->sp--;
break;
case op_dup: // 0x1e (30)
+ // Duplicate TOD (Top Of Stack) element
r_temp = s->xs->sp[-1];
PUSH32(r_temp);
break;
case op_link: // 0x1f (31)
+ // We shouldn't initialize temp variables at all
+ // We put special segment 0xFFFF in there, so that uninitialized reads can get detected
for (int i = 0; i < opparams[0]; i++)
- s->xs->sp[i] = NULL_REG;
+ s->xs->sp[i] = make_reg(0xffff, 0);
+
s->xs->sp += opparams[0];
break;
case op_call: { // 0x20 (32)
+ // Call a script subroutine
int argc = (opparams[1] >> 1) // Given as offset, but we need count
- + 1 + s->restAdjustCur;
+ + 1 + s->restAdjust;
StackPtr call_base = s->xs->sp - argc;
- s->xs->sp[1].offset += s->restAdjustCur;
+ s->xs->sp[1].offset += s->restAdjust;
+
+ uint16 localCallOffset = s->xs->addr.pc.offset + opparams[0];
xs_new = add_exec_stack_entry(s->_executionStack, make_reg(s->xs->addr.pc.segment,
- s->xs->addr.pc.offset + opparams[0]),
+ localCallOffset),
s->xs->sp, s->xs->objp,
- (validate_arithmetic(*call_base)) + s->restAdjustCur,
- call_base, NULL_SELECTOR, s->xs->objp,
+ (validate_arithmetic(*call_base)) + s->restAdjust,
+ call_base, NULL_SELECTOR, -1, localCallOffset, s->xs->objp,
s->_executionStack.size()-1, s->xs->local_segment);
- s->restAdjustCur = 0; // Used up the &rest adjustment
+ s->restAdjust = 0; // Used up the &rest adjustment
s->xs->sp = call_base;
s->_executionStackPosChanged = true;
@@ -1134,74 +1409,80 @@ void run_vm(EngineState *s, bool restoring) {
}
case op_callk: { // 0x21 (33)
- gc_countdown(s);
+ // Call kernel function
+ gcCountDown(s);
s->xs->sp -= (opparams[1] >> 1) + 1;
bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY);
- if (!oldScriptHeader) {
- s->xs->sp -= s->restAdjustCur;
- s->restAdjust = 0; // We just used up the s->restAdjustCur, remember?
- }
+ if (!oldScriptHeader)
+ s->xs->sp -= s->restAdjust;
int argc = validate_arithmetic(s->xs->sp[0]);
if (!oldScriptHeader)
- argc += s->restAdjustCur;
+ argc += s->restAdjust;
callKernelFunc(s, opparams[0], argc);
if (!oldScriptHeader)
- s->restAdjustCur = s->restAdjust;
+ s->restAdjust = 0;
// Calculate xs again: The kernel function might
// have spawned a new VM
xs_new = &(s->_executionStack.back());
s->_executionStackPosChanged = true;
+
+ // If a game is being loaded, stop processing
+ if (s->abortScriptProcessing != kAbortNone || g_engine->shouldQuit())
+ return; // Stop processing
+
break;
}
case op_callb: // 0x22 (34)
- temp = ((opparams[1] >> 1) + s->restAdjustCur + 1);
+ // Call base script
+ temp = ((opparams[1] >> 1) + s->restAdjust + 1);
s_temp = s->xs->sp;
s->xs->sp -= temp;
- s->xs->sp[0].offset += s->restAdjustCur;
+ s->xs->sp[0].offset += s->restAdjust;
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
+ s->restAdjust = 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) + s->restAdjustCur + 1);
+ // Call external script
+ temp = ((opparams[2] >> 1) + s->restAdjust + 1);
s_temp = s->xs->sp;
s->xs->sp -= temp;
- s->xs->sp[0].offset += s->restAdjustCur;
+ s->xs->sp[0].offset += s->restAdjust;
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
+ s->restAdjust = 0; // Used up the &rest adjustment
if (xs_new) // in case of error, keep old stack
s->_executionStackPosChanged = true;
break;
case op_ret: // 0x24 (36)
+ // Return from an execution loop started by call, calle, callb, send, self or super
do {
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?
- s->execution_stack_base = old_execution_stack_base; // Restore stack base
+ if ((int)s->_executionStack.size() - 1 == s->executionStackBase) { // Have we reached the base?
+ s->executionStackBase = old_executionStackBase; // Restore stack base
s->_executionStack.pop_back();
s->_executionStackPosChanged = true;
- s->restAdjust = s->restAdjustCur; // Update &rest
return; // "Hard" return
}
@@ -1233,17 +1514,18 @@ void run_vm(EngineState *s, bool restoring) {
break;
case op_send: // 0x25 (37)
+ // Send for one or more selectors
s_temp = s->xs->sp;
- s->xs->sp -= ((opparams[0] >> 1) + s->restAdjustCur); // Adjust stack
+ s->xs->sp -= ((opparams[0] >> 1) + s->restAdjust); // Adjust stack
- s->xs->sp[1].offset += s->restAdjustCur;
+ s->xs->sp[1].offset += s->restAdjust;
xs_new = send_selector(s, s->r_acc, s->r_acc, s_temp,
- (int)(opparams[0] >> 1) + (uint16)s->restAdjustCur, s->xs->sp);
+ (int)(opparams[0] >> 1) + (uint16)s->restAdjust, s->xs->sp);
if (xs_new && xs_new != s->xs)
s->_executionStackPosChanged = true;
- s->restAdjustCur = 0;
+ s->restAdjust = 0;
break;
@@ -1253,6 +1535,7 @@ void run_vm(EngineState *s, bool restoring) {
break;
case op_class: // 0x28 (40)
+ // Get class address
s->r_acc = s->_segMan->getClassAddress((unsigned)opparams[0], SCRIPT_GET_LOCK,
s->xs->addr.pc);
break;
@@ -1262,45 +1545,48 @@ void run_vm(EngineState *s, bool restoring) {
break;
case op_self: // 0x2a (42)
+ // Send to self
s_temp = s->xs->sp;
- s->xs->sp -= ((opparams[0] >> 1) + s->restAdjustCur); // Adjust stack
+ s->xs->sp -= ((opparams[0] >> 1) + s->restAdjust); // Adjust stack
- s->xs->sp[1].offset += s->restAdjustCur;
+ s->xs->sp[1].offset += s->restAdjust;
xs_new = send_selector(s, s->xs->objp, s->xs->objp,
- s_temp, (int)(opparams[0] >> 1) + (uint16)s->restAdjustCur,
+ s_temp, (int)(opparams[0] >> 1) + (uint16)s->restAdjust,
s->xs->sp);
if (xs_new && xs_new != s->xs)
s->_executionStackPosChanged = true;
- s->restAdjustCur = 0;
+ s->restAdjust = 0;
break;
case op_super: // 0x2b (43)
+ // Send to any class
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 = s->xs->sp;
- s->xs->sp -= ((opparams[1] >> 1) + s->restAdjustCur); // Adjust stack
+ s->xs->sp -= ((opparams[1] >> 1) + s->restAdjust); // Adjust stack
- s->xs->sp[1].offset += s->restAdjustCur;
+ s->xs->sp[1].offset += s->restAdjust;
xs_new = send_selector(s, r_temp, s->xs->objp, s_temp,
- (int)(opparams[1] >> 1) + (uint16)s->restAdjustCur,
+ (int)(opparams[1] >> 1) + (uint16)s->restAdjust,
s->xs->sp);
if (xs_new && xs_new != s->xs)
s->_executionStackPosChanged = true;
- s->restAdjustCur = 0;
+ s->restAdjust = 0;
}
break;
case op_rest: // 0x2c (44)
+ // Pushes all or part of the parameter variable list on the stack
temp = (uint16) opparams[0]; // First argument
- s->restAdjustCur = MAX<int16>(s->xs->argc - temp + 1, 0); // +1 because temp counts the paramcount while argc doesn't
+ s->restAdjust = MAX<int16>(s->xs->argc - temp + 1, 0); // +1 because temp counts the paramcount while argc doesn't
for (; temp <= s->xs->argc; temp++)
PUSH32(s->xs->variables_argp[temp]);
@@ -1308,12 +1594,13 @@ void run_vm(EngineState *s, bool restoring) {
break;
case op_lea: // 0x2d (45)
+ // Load Effective Address
temp = (uint16) opparams[0] >> 1;
var_number = temp & 0x03; // Get variable type
// Get variable block offset
- r_temp.segment = s->variables_seg[var_number];
- r_temp.offset = s->variables[var_number] - s->variables_base[var_number];
+ r_temp.segment = s->variablesSegment[var_number];
+ r_temp.offset = s->variables[var_number] - s->variablesBase[var_number];
if (temp & 0x08) // Add accumulator offset if requested
r_temp.offset += signed_validate_arithmetic(s->r_acc);
@@ -1326,6 +1613,7 @@ void run_vm(EngineState *s, bool restoring) {
case op_selfID: // 0x2e (46)
+ // Get 'self' identity
s->r_acc = s->xs->objp;
break;
@@ -1334,66 +1622,60 @@ void run_vm(EngineState *s, bool restoring) {
break;
case op_pprev: // 0x30 (48)
+ // Pushes the value of the prev register, set by the last comparison
+ // bytecode (eq?, lt?, etc.), on the stack
PUSH32(s->r_prev);
break;
case op_pToa: // 0x31 (49)
- s->r_acc = OBJ_PROPERTY(obj, (opparams[0] >> 1));
+ // Property To Accumulator
+ s->r_acc = validate_property(obj, (opparams[0] >> 1));
break;
case op_aTop: // 0x32 (50)
- OBJ_PROPERTY(obj, (opparams[0] >> 1)) = s->r_acc;
+ // Accumulator To Property
+ validate_property(obj, (opparams[0] >> 1)) = s->r_acc;
break;
case op_pTos: // 0x33 (51)
- PUSH32(OBJ_PROPERTY(obj, opparams[0] >> 1));
+ // Property To Stack
+ PUSH32(validate_property(obj, opparams[0] >> 1));
break;
case op_sTop: // 0x34 (52)
- OBJ_PROPERTY(obj, (opparams[0] >> 1)) = POP32();
+ // Stack To Property
+ validate_property(obj, (opparams[0] >> 1)) = POP32();
break;
case op_ipToa: // 0x35 (53)
- s->r_acc = OBJ_PROPERTY(obj, (opparams[0] >> 1));
- s->r_acc = OBJ_PROPERTY(obj, (opparams[0] >> 1)) = ACC_ARITHMETIC_L(1 + /*acc*/);
+ // Incement Property and copy To Accumulator
+ s->r_acc = validate_property(obj, (opparams[0] >> 1));
+ s->r_acc = validate_property(obj, (opparams[0] >> 1)) = ACC_ARITHMETIC_L(1 + /*acc*/);
break;
case op_dpToa: { // 0x36 (54)
- s->r_acc = OBJ_PROPERTY(obj, (opparams[0] >> 1));
-#if 0
- // Speed throttling is possible here as well
- // although this opens other issues like mud wrestling in lsl5 uses another local variable for delays
- Object *var_container = obj;
- if (!(obj->getInfoSelector().offset & SCRIPT_INFO_CLASS))
- var_container = s->_segMan->getObject(obj->getSuperClassSelector());
- uint16 varSelector = var_container->getVarSelector(opparams[0] >> 1);
-// printf("%X\n", varSelector);
-// printf("%s\n", g_sci->getKernel()->getSelectorName(varSelector).c_str());
- if ((varSelector == 0x84) || (varSelector == 0x92))) {
- // selectors cycles, cycleCnt from lsl5 hardcoded
- uint32 curTime = g_system->getMillis();
- if (s->_lastAnimateTime + 30 > curTime)
- break;
- s->_lastAnimateTime = curTime;
- }
-#endif
- s->r_acc = OBJ_PROPERTY(obj, (opparams[0] >> 1)) = ACC_ARITHMETIC_L(-1 + /*acc*/);
+ // Decrement Property and copy To Accumulator
+ s->r_acc = validate_property(obj, (opparams[0] >> 1));
+ s->r_acc = validate_property(obj, (opparams[0] >> 1)) = ACC_ARITHMETIC_L(-1 + /*acc*/);
break;
}
case op_ipTos: // 0x37 (55)
- validate_arithmetic(OBJ_PROPERTY(obj, (opparams[0] >> 1)));
- temp = ++OBJ_PROPERTY(obj, (opparams[0] >> 1)).offset;
+ // Increment Property and push to Stack
+ validate_arithmetic(validate_property(obj, (opparams[0] >> 1)));
+ temp = ++validate_property(obj, (opparams[0] >> 1)).offset;
PUSH(temp);
break;
case op_dpTos: // 0x38 (56)
- validate_arithmetic(OBJ_PROPERTY(obj, (opparams[0] >> 1)));
- temp = --OBJ_PROPERTY(obj, (opparams[0] >> 1)).offset;
+ // Decrement Property and push to Stack
+ validate_arithmetic(validate_property(obj, (opparams[0] >> 1)));
+ temp = --validate_property(obj, (opparams[0] >> 1)).offset;
PUSH(temp);
break;
case op_lofsa: // 0x39 (57)
+ // Load Offset to Accumulator
s->r_acc.segment = s->xs->addr.pc.segment;
switch (g_sci->_features->detectLofsType()) {
@@ -1407,15 +1689,14 @@ void run_vm(EngineState *s, bool restoring) {
s->r_acc.offset = s->xs->addr.pc.offset + opparams[0];
}
-#ifndef DISABLE_VALIDATIONS
- if (s->r_acc.offset >= code_buf_size) {
+ if (s->r_acc.offset >= scr->getBufSize()) {
error("VM: lofsa operation overflowed: %04x:%04x beyond end"
- " of script (at %04x)\n", PRINT_REG(s->r_acc), code_buf_size);
+ " of script (at %04x)\n", PRINT_REG(s->r_acc), scr->getBufSize());
}
-#endif
break;
case op_lofss: // 0x3a (58)
+ // Load Offset to Stack
r_temp.segment = s->xs->addr.pc.segment;
switch (g_sci->_features->detectLofsType()) {
@@ -1429,12 +1710,10 @@ void run_vm(EngineState *s, bool restoring) {
r_temp.offset = s->xs->addr.pc.offset + opparams[0];
}
-#ifndef DISABLE_VALIDATIONS
- if (r_temp.offset >= code_buf_size) {
+ if (r_temp.offset >= scr->getBufSize()) {
error("VM: lofss operation overflowed: %04x:%04x beyond end"
- " of script (at %04x)", PRINT_REG(r_temp), code_buf_size);
+ " of script (at %04x)", PRINT_REG(r_temp), scr->getBufSize());
}
-#endif
PUSH32(r_temp);
break;
@@ -1455,6 +1734,7 @@ void run_vm(EngineState *s, bool restoring) {
PUSH32(s->xs->objp);
} else {
// Debug opcode op_file, skip null-terminated string (file name)
+ const byte *code_buf = scr->getBuf();
while (code_buf[s->xs->addr.pc.offset++]) ;
}
break;
@@ -1467,42 +1747,49 @@ void run_vm(EngineState *s, bool restoring) {
case op_lal: // 0x41 (65)
case op_lat: // 0x42 (66)
case op_lap: // 0x43 (67)
+ // Load global, local, temp or param variable into the accumulator
var_type = opcode & 0x3; // Gets the variable type: g, l, t or p
var_number = opparams[0];
- s->r_acc = READ_VAR(var_type, var_number, s->r_acc);
+ s->r_acc = READ_VAR(var_type, var_number);
break;
case op_lsg: // 0x44 (68)
case op_lsl: // 0x45 (69)
case op_lst: // 0x46 (70)
case op_lsp: // 0x47 (71)
+ // Load global, local, temp or param variable into the stack
var_type = opcode & 0x3; // Gets the variable type: g, l, t or p
var_number = opparams[0];
- PUSH32(READ_VAR(var_type, var_number, s->r_acc));
+ PUSH32(READ_VAR(var_type, var_number));
break;
case op_lagi: // 0x48 (72)
case op_lali: // 0x49 (73)
case op_lati: // 0x4a (74)
case op_lapi: // 0x4b (75)
+ // Load global, local, temp or param variable into the accumulator,
+ // using the accumulator as an additional index
var_type = opcode & 0x3; // Gets the variable type: g, l, t or p
var_number = opparams[0] + signed_validate_arithmetic(s->r_acc);
- s->r_acc = READ_VAR(var_type, var_number, s->r_acc);
+ s->r_acc = READ_VAR(var_type, var_number);
break;
case op_lsgi: // 0x4c (76)
case op_lsli: // 0x4d (77)
case op_lsti: // 0x4e (78)
case op_lspi: // 0x4f (79)
+ // Load global, local, temp or param variable into the stack,
+ // using the accumulator as an additional index
var_type = opcode & 0x3; // Gets the variable type: g, l, t or p
var_number = opparams[0] + signed_validate_arithmetic(s->r_acc);
- PUSH32(READ_VAR(var_type, var_number, s->r_acc));
+ PUSH32(READ_VAR(var_type, var_number));
break;
case op_sag: // 0x50 (80)
case op_sal: // 0x51 (81)
case op_sat: // 0x52 (82)
case op_sap: // 0x53 (83)
+ // Save the accumulator into the global, local, temp or param variable
var_type = opcode & 0x3; // Gets the variable type: g, l, t or p
var_number = opparams[0];
WRITE_VAR(var_type, var_number, s->r_acc);
@@ -1512,6 +1799,7 @@ void run_vm(EngineState *s, bool restoring) {
case op_ssl: // 0x55 (85)
case op_sst: // 0x56 (86)
case op_ssp: // 0x57 (87)
+ // Save the stack into the global, local, temp or param variable
var_type = opcode & 0x3; // Gets the variable type: g, l, t or p
var_number = opparams[0];
WRITE_VAR(var_type, var_number, POP32());
@@ -1521,6 +1809,9 @@ void run_vm(EngineState *s, bool restoring) {
case op_sali: // 0x59 (89)
case op_sati: // 0x5a (90)
case op_sapi: // 0x5b (91)
+ // Save the accumulator into the global, local, temp or param variable,
+ // using the accumulator as an additional index
+
// Special semantics because it wouldn't really make a whole lot
// of sense otherwise, with acc being used for two things
// simultaneously...
@@ -1534,6 +1825,8 @@ void run_vm(EngineState *s, bool restoring) {
case op_ssli: // 0x5d (93)
case op_ssti: // 0x5e (94)
case op_sspi: // 0x5f (95)
+ // Save the stack into the global, local, temp or param variable,
+ // using the accumulator as an additional index
var_type = opcode & 0x3; // Gets the variable type: g, l, t or p
var_number = opparams[0] + signed_validate_arithmetic(s->r_acc);
WRITE_VAR(var_type, var_number, POP32());
@@ -1543,9 +1836,11 @@ void run_vm(EngineState *s, bool restoring) {
case op_plusal: // 0x61 (97)
case op_plusat: // 0x62 (98)
case op_plusap: // 0x63 (99)
+ // Increment the global, local, temp or param variable and save it
+ // to the accumulator
var_type = opcode & 0x3; // Gets the variable type: g, l, t or p
var_number = opparams[0];
- r_temp = READ_VAR(var_type, var_number, s->r_acc);
+ r_temp = READ_VAR(var_type, var_number);
if (r_temp.segment) {
// Pointer arithmetics!
s->r_acc = pointer_add(s, r_temp, 1);
@@ -1558,9 +1853,11 @@ void run_vm(EngineState *s, bool restoring) {
case op_plussl: // 0x65 (101)
case op_plusst: // 0x66 (102)
case op_plussp: // 0x67 (103)
+ // Increment the global, local, temp or param variable and save it
+ // to the stack
var_type = opcode & 0x3; // Gets the variable type: g, l, t or p
var_number = opparams[0];
- r_temp = READ_VAR(var_type, var_number, s->r_acc);
+ r_temp = READ_VAR(var_type, var_number);
if (r_temp.segment) {
// Pointer arithmetics!
r_temp = pointer_add(s, r_temp, 1);
@@ -1574,9 +1871,11 @@ void run_vm(EngineState *s, bool restoring) {
case op_plusali: // 0x69 (105)
case op_plusati: // 0x6a (106)
case op_plusapi: // 0x6b (107)
+ // Increment the global, local, temp or param variable and save it
+ // to the accumulator, using the accumulator as an additional index
var_type = opcode & 0x3; // Gets the variable type: g, l, t or p
var_number = opparams[0] + signed_validate_arithmetic(s->r_acc);
- r_temp = READ_VAR(var_type, var_number, s->r_acc);
+ r_temp = READ_VAR(var_type, var_number);
if (r_temp.segment) {
// Pointer arithmetics!
s->r_acc = pointer_add(s, r_temp, 1);
@@ -1589,9 +1888,11 @@ void run_vm(EngineState *s, bool restoring) {
case op_plussli: // 0x6d (109)
case op_plussti: // 0x6e (110)
case op_plusspi: // 0x6f (111)
+ // Increment the global, local, temp or param variable and save it
+ // to the stack, using the accumulator as an additional index
var_type = opcode & 0x3; // Gets the variable type: g, l, t or p
var_number = opparams[0] + signed_validate_arithmetic(s->r_acc);
- r_temp = READ_VAR(var_type, var_number, s->r_acc);
+ r_temp = READ_VAR(var_type, var_number);
if (r_temp.segment) {
// Pointer arithmetics!
r_temp = pointer_add(s, r_temp, 1);
@@ -1605,9 +1906,11 @@ void run_vm(EngineState *s, bool restoring) {
case op_minusal: // 0x71 (113)
case op_minusat: // 0x72 (114)
case op_minusap: // 0x73 (115)
+ // Decrement the global, local, temp or param variable and save it
+ // to the accumulator
var_type = opcode & 0x3; // Gets the variable type: g, l, t or p
var_number = opparams[0];
- r_temp = READ_VAR(var_type, var_number, s->r_acc);
+ r_temp = READ_VAR(var_type, var_number);
if (r_temp.segment) {
// Pointer arithmetics!
s->r_acc = pointer_add(s, r_temp, -1);
@@ -1620,9 +1923,11 @@ void run_vm(EngineState *s, bool restoring) {
case op_minussl: // 0x75 (117)
case op_minusst: // 0x76 (118)
case op_minussp: // 0x77 (119)
+ // Decrement the global, local, temp or param variable and save it
+ // to the stack
var_type = opcode & 0x3; // Gets the variable type: g, l, t or p
var_number = opparams[0];
- r_temp = READ_VAR(var_type, var_number, s->r_acc);
+ r_temp = READ_VAR(var_type, var_number);
if (r_temp.segment) {
// Pointer arithmetics!
r_temp = pointer_add(s, r_temp, -1);
@@ -1636,9 +1941,11 @@ void run_vm(EngineState *s, bool restoring) {
case op_minusali: // 0x79 (121)
case op_minusati: // 0x7a (122)
case op_minusapi: // 0x7b (123)
+ // Decrement the global, local, temp or param variable and save it
+ // to the accumulator, using the accumulator as an additional index
var_type = opcode & 0x3; // Gets the variable type: g, l, t or p
var_number = opparams[0] + signed_validate_arithmetic(s->r_acc);
- r_temp = READ_VAR(var_type, var_number, s->r_acc);
+ r_temp = READ_VAR(var_type, var_number);
if (r_temp.segment) {
// Pointer arithmetics!
s->r_acc = pointer_add(s, r_temp, -1);
@@ -1651,9 +1958,11 @@ void run_vm(EngineState *s, bool restoring) {
case op_minussli: // 0x7d (125)
case op_minussti: // 0x7e (126)
case op_minusspi: // 0x7f (127)
+ // Decrement the global, local, temp or param variable and save it
+ // to the stack, using the accumulator as an additional index
var_type = opcode & 0x3; // Gets the variable type: g, l, t or p
var_number = opparams[0] + signed_validate_arithmetic(s->r_acc);
- r_temp = READ_VAR(var_type, var_number, s->r_acc);
+ r_temp = READ_VAR(var_type, var_number);
if (r_temp.segment) {
// Pointer arithmetics!
r_temp = pointer_add(s, r_temp, -1);
@@ -1671,100 +1980,15 @@ void run_vm(EngineState *s, bool restoring) {
if (s->_executionStackPosChanged) // Force initialization
s->xs = xs_new;
-//#ifndef DISABLE_VALIDATIONS
if (s->xs != &(s->_executionStack.back())) {
- warning("xs is stale (%p vs %p); last command was %02x",
+ error("xs is stale (%p vs %p); last command was %02x",
(void *)s->xs, (void *)&(s->_executionStack.back()),
opcode);
}
-//#endif
- ++s->script_step_counter;
+ ++s->scriptStepCounter;
}
}
-static void _init_stack_base_with_selector(EngineState *s, Selector selector) {
- s->stack_base[0] = make_reg(0, (uint16)selector);
- s->stack_base[1] = NULL_REG;
-}
-
-static EngineState *_game_run(EngineState *&s) {
- bool restoring = false;
-
- if (DebugMan.isDebugChannelEnabled(kDebugLevelOnStartup))
- g_sci->getSciDebugger()->attach();
-
- do {
- s->_executionStackPosChanged = false;
- run_vm(s, restoring);
- if (s->restarting_flags & SCI_GAME_IS_RESTARTING_NOW) { // Restart was requested?
- restoring = false;
- s->_executionStack.clear();
- s->_executionStackPosChanged = false;
-
- game_exit(s);
- script_init_engine(s);
- game_init(s);
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- s->_sound.sfx_reset_player();
-#endif
- _init_stack_base_with_selector(s, g_sci->getKernel()->_selectorCache.play);
-
- send_selector(s, s->_gameObj, s->_gameObj, s->stack_base, 2, s->stack_base);
-
- s->script_abort_flag = 0;
- s->restarting_flags = SCI_GAME_WAS_RESTARTED;
-
- } else {
- restoring = s->restoring;
- if (restoring) {
- game_exit(s);
- s->restoring = false;
- if (s->script_abort_flag == 2) {
- debugC(2, kDebugLevelVM, "Restarting with replay()");
- s->_executionStack.clear(); // Restart with replay
-
- _init_stack_base_with_selector(s, g_sci->getKernel()->_selectorCache.replay);
-
- send_selector(s, s->_gameObj, s->_gameObj, s->stack_base, 2, s->stack_base);
- }
-
- s->script_abort_flag = 0;
-
- } else
- break; // exit loop
- }
- } while (true);
-
- return s;
-}
-
-int game_run(EngineState **_s) {
- EngineState *s = *_s;
-
- debugC(2, kDebugLevelVM, "Calling %s::play()", g_sci->getGameID());
- _init_stack_base_with_selector(s, g_sci->getKernel()->_selectorCache.play); // Call the play selector
-
- // Now: Register the first element on the execution stack-
- if (!send_selector(s, s->_gameObj, s->_gameObj, s->stack_base, 2, s->stack_base)) {
- Console *con = g_sci->getSciDebugger();
- con->printObject(s->_gameObj);
- warning("Failed to run the game! Aborting...");
- return 1;
- }
- // and ENGAGE!
- _game_run(*_s);
-
- debugC(2, kDebugLevelVM, "Game::play() finished.");
-
- return 0;
-}
-
-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 {
Object *o = segMan->getObject(obj);
return o ? &o->getVariableRef(varindex) : 0;
diff --git a/engines/sci/engine/vm.h b/engines/sci/engine/vm.h
index 67a6bd0dc3..81ec4f1c61 100644
--- a/engines/sci/engine/vm.h
+++ b/engines/sci/engine/vm.h
@@ -49,11 +49,6 @@ class ResourceManager;
/** Offset of this identifier */
#define SCRIPT_OBJECT_MAGIC_OFFSET (getSciVersion() < SCI_VERSION_1_1 ? -8 : 0)
-/** Script-relative offset of the species ID */
-#define SCRIPT_SPECIES_OFFSET 8 -8
-
-#define SCRIPT_SUPERCLASS_OFFSET (getSciVersion() < SCI_VERSION_1_1 ? 10 -8 : 12)
-
/** Stack pointer value: Use predecessor's value */
#define CALL_SP_CARRY NULL
@@ -69,115 +64,7 @@ struct Class {
reg_t reg; ///< offset; script-relative offset, segment: 0 if not instantiated
};
-#define RAW_IS_OBJECT(datablock) (READ_SCI11ENDIAN_UINT16(((byte *) datablock) + SCRIPT_OBJECT_MAGIC_OFFSET) == SCRIPT_OBJECT_MAGIC_NUMBER)
-
-/** Contains selector IDs for a few selected selectors */
-struct SelectorCache {
- SelectorCache() {
- memset(this, 0, sizeof(*this));
- }
-
- // Statically defined selectors, (almost the) same in all SCI versions
- Selector y;
- Selector x;
- Selector view, loop, cel; ///< Description of a specific image
- Selector underBits; ///< Used by the graphics subroutines to store backupped BG pic data
- Selector nsTop, nsLeft, nsBottom, nsRight; ///< View boundaries ('now seen')
- Selector lsTop, lsLeft, lsBottom, lsRight; ///< Used by Animate() subfunctions and scroll list controls
- Selector signal; ///< Used by Animate() to control a view's behaviour
- Selector illegalBits; ///< Used by CanBeHere
- Selector brTop, brLeft, brBottom, brRight; ///< Bounding Rectangle
- // name, key, time
- Selector text; ///< Used by controls
- Selector elements; ///< Used by SetSynonyms()
- // color, back
- Selector mode; ///< Used by text controls (-> DrawControl())
- // style
- Selector state, font, type;///< Used by controls
- // window
- Selector cursor, max; ///< Used by EditControl
- // mark, who
- Selector message; ///< Used by GetEvent
- // edit
- Selector play; ///< Play function (first function to be called)
- Selector number;
- Selector handle; ///< Replaced by nodePtr in SCI1+
- Selector nodePtr; ///< Replaces handle in SCI1+
- Selector client; ///< The object that wants to be moved
- Selector dx, dy; ///< Deltas
- Selector b_movCnt, b_i1, b_i2, b_di, b_xAxis, b_incr; ///< Various Bresenham vars
- Selector xStep, yStep; ///< BR adjustments
- Selector moveSpeed; ///< Used for DoBresen
- Selector canBeHere; ///< Funcselector: Checks for movement validity in SCI0
- Selector heading, mover; ///< Used in DoAvoider
- Selector doit; ///< Called (!) by the Animate() system call
- Selector isBlocked, looper; ///< Used in DoAvoider
- Selector priority;
- Selector modifiers; ///< Used by GetEvent
- Selector replay; ///< Replay function
- // setPri, at, next, done, width
- Selector wordFail, syntaxFail; ///< Used by Parse()
- // semanticFail, pragmaFail
- // said
- Selector claimed; ///< Used generally by the event mechanism
- // value, save, restore, title, button, icon, draw
- Selector delete_; ///< Called by Animate() to dispose a view object
- Selector z;
-
- // SCI1+ static selectors
- Selector parseLang;
- Selector printLang; ///< Used for i18n
- Selector subtitleLang;
- Selector size;
- Selector points; ///< Used by AvoidPath()
- Selector palette;
- Selector dataInc;
- // handle (in SCI1)
- Selector min; ///< SMPTE time format
- Selector sec;
- Selector frame;
- Selector vol;
- Selector pri;
- // perform
- Selector moveDone; ///< used for DoBresen
-
- // SCI1 selectors which have been moved a bit in SCI1.1, but otherwise static
- Selector cantBeHere; ///< Checks for movement avoidance in SCI1+. Replaces canBeHere
- Selector topString; ///< SCI1 scroll lists use this instead of lsTop
- Selector flags;
-
- // SCI1+ audio sync related selectors, not static. They're used for lip syncing in
- // CD talkie games
- Selector syncCue; ///< Used by DoSync()
- Selector syncTime;
-
- // SCI1.1 specific selectors
- Selector scaleSignal; // < Used by Animate() for cel scaling (SCI1.1+)
- Selector scaleX, scaleY; ///< SCI1.1 view scaling
-
- // Used for auto detection purposes
- Selector overlay; ///< Used to determine if a game is using old gfx functions or not
-
- // SCI1.1 Mac icon bar selectors
- Selector iconIndex; ///< Used to index icon bar objects
-
-#ifdef ENABLE_SCI32
- Selector data; // Used by Array()/String()
- Selector picture; // Used to hold the picture ID for SCI32 pictures
-
- Selector plane;
- Selector top;
- Selector left;
- Selector bottom;
- Selector right;
- Selector resX;
- Selector resY;
-
- Selector fore;
- Selector back;
- Selector dimmed;
-#endif
-};
+#define RAW_IS_OBJECT(datablock) (READ_SCI11ENDIAN_UINT16(((const byte *) datablock) + SCRIPT_OBJECT_MAGIC_OFFSET) == SCRIPT_OBJECT_MAGIC_NUMBER)
// A reference to an object's variable.
// The object is stored as a reg_t, the variable as an index into _variables
@@ -210,8 +97,10 @@ struct ExecStack {
StackPtr variables_argp; // Argument pointer
SegmentId local_segment; // local variables etc
- Selector selector; // The selector which was used to call or -1 if not applicable
- int origin; // The stack frame position the call was made from, or -1 if it was the initial call
+ Selector debugSelector; // The selector which was used to call or -1 if not applicable
+ int debugExportId; // The exportId which was called or -1 if not applicable
+ int debugLocalCallOffset; // Local call offset or -1 if not applicable
+ int debugOrigin; // The stack frame position the call was made from, or -1 if it was the initial call
ExecStackType type;
reg_t* getVarPointer(SegManager *segMan) const;
@@ -229,6 +118,160 @@ enum {
GC_INTERVAL = 32768
};
+// Opcode formats
+enum opcode_format {
+ Script_Invalid = -1,
+ Script_None = 0,
+ Script_Byte,
+ Script_SByte,
+ Script_Word,
+ Script_SWord,
+ Script_Variable,
+ Script_SVariable,
+ Script_SRelative,
+ Script_Property,
+ Script_Global,
+ Script_Local,
+ Script_Temp,
+ Script_Param,
+ Script_Offset,
+ Script_End
+};
+
+enum sci_opcodes {
+ op_bnot = 0x00, // 000
+ op_add = 0x01, // 001
+ op_sub = 0x02, // 002
+ op_mul = 0x03, // 003
+ op_div = 0x04, // 004
+ op_mod = 0x05, // 005
+ op_shr = 0x06, // 006
+ op_shl = 0x07, // 007
+ op_xor = 0x08, // 008
+ op_and = 0x09, // 009
+ op_or = 0x0a, // 010
+ op_neg = 0x0b, // 011
+ op_not = 0x0c, // 012
+ op_eq_ = 0x0d, // 013
+ op_ne_ = 0x0e, // 014
+ op_gt_ = 0x0f, // 015
+ op_ge_ = 0x10, // 016
+ op_lt_ = 0x11, // 017
+ op_le_ = 0x12, // 018
+ op_ugt_ = 0x13, // 019
+ op_uge_ = 0x14, // 020
+ op_ult_ = 0x15, // 021
+ op_ule_ = 0x16, // 022
+ op_bt = 0x17, // 023
+ op_bnt = 0x18, // 024
+ op_jmp = 0x19, // 025
+ op_ldi = 0x1a, // 026
+ op_push = 0x1b, // 027
+ op_pushi = 0x1c, // 028
+ op_toss = 0x1d, // 029
+ op_dup = 0x1e, // 030
+ op_link = 0x1f, // 031
+ op_call = 0x20, // 032
+ op_callk = 0x21, // 033
+ op_callb = 0x22, // 034
+ op_calle = 0x23, // 035
+ op_ret = 0x24, // 036
+ op_send = 0x25, // 037
+ // dummy 0x26, // 038
+ // dummy 0x27, // 039
+ op_class = 0x28, // 040
+ // dummy 0x29, // 041
+ op_self = 0x2a, // 042
+ op_super = 0x2b, // 043
+ op_rest = 0x2c, // 044
+ op_lea = 0x2d, // 045
+ op_selfID = 0x2e, // 046
+ // dummy 0x2f // 047
+ op_pprev = 0x30, // 048
+ op_pToa = 0x31, // 049
+ op_aTop = 0x32, // 050
+ op_pTos = 0x33, // 051
+ op_sTop = 0x34, // 052
+ op_ipToa = 0x35, // 053
+ op_dpToa = 0x36, // 054
+ op_ipTos = 0x37, // 055
+ op_dpTos = 0x38, // 056
+ op_lofsa = 0x39, // 057
+ op_lofss = 0x3a, // 058
+ op_push0 = 0x3b, // 059
+ op_push1 = 0x3c, // 060
+ op_push2 = 0x3d, // 061
+ op_pushSelf = 0x3e, // 062
+ op_line = 0x3f, // 063
+ op_lag = 0x40, // 064
+ op_lal = 0x41, // 065
+ op_lat = 0x42, // 066
+ op_lap = 0x43, // 067
+ op_lsg = 0x44, // 068
+ op_lsl = 0x45, // 069
+ op_lst = 0x46, // 070
+ op_lsp = 0x47, // 071
+ op_lagi = 0x48, // 072
+ op_lali = 0x49, // 073
+ op_lati = 0x4a, // 074
+ op_lapi = 0x4b, // 075
+ op_lsgi = 0x4c, // 076
+ op_lsli = 0x4d, // 077
+ op_lsti = 0x4e, // 078
+ op_lspi = 0x4f, // 079
+ op_sag = 0x50, // 080
+ op_sal = 0x51, // 081
+ op_sat = 0x52, // 082
+ op_sap = 0x53, // 083
+ op_ssg = 0x54, // 084
+ op_ssl = 0x55, // 085
+ op_sst = 0x56, // 086
+ op_ssp = 0x57, // 087
+ op_sagi = 0x58, // 088
+ op_sali = 0x59, // 089
+ op_sati = 0x5a, // 090
+ op_sapi = 0x5b, // 091
+ op_ssgi = 0x5c, // 092
+ op_ssli = 0x5d, // 093
+ op_ssti = 0x5e, // 094
+ op_sspi = 0x5f, // 095
+ op_plusag = 0x60, // 096
+ op_plusal = 0x61, // 097
+ op_plusat = 0x62, // 098
+ op_plusap = 0x63, // 099
+ op_plussg = 0x64, // 100
+ op_plussl = 0x65, // 101
+ op_plusst = 0x66, // 102
+ op_plussp = 0x67, // 103
+ op_plusagi = 0x68, // 104
+ op_plusali = 0x69, // 105
+ op_plusati = 0x6a, // 106
+ op_plusapi = 0x6b, // 107
+ op_plussgi = 0x6c, // 108
+ op_plussli = 0x6d, // 109
+ op_plussti = 0x6e, // 110
+ op_plusspi = 0x6f, // 111
+ op_minusag = 0x70, // 112
+ op_minusal = 0x71, // 113
+ op_minusat = 0x72, // 114
+ op_minusap = 0x73, // 115
+ op_minussg = 0x74, // 116
+ op_minussl = 0x75, // 117
+ op_minusst = 0x76, // 118
+ op_minussp = 0x77, // 119
+ op_minusagi = 0x78, // 120
+ op_minusali = 0x79, // 121
+ op_minusati = 0x7a, // 122
+ op_minusapi = 0x7b, // 123
+ op_minussgi = 0x7c, // 124
+ op_minussli = 0x7d, // 125
+ op_minussti = 0x7e, // 126
+ op_minusspi = 0x7f // 127
+};
+
+extern opcode_format g_opcode_formats[128][4];
+
+void script_adjust_opcode_formats();
/**
* Executes function pubfunct of the specified script.
@@ -283,14 +326,6 @@ void run_vm(EngineState *s, bool restoring);
void script_debug(EngineState *s);
/**
- * Initializes a EngineState block
- * @param[in] s The state to initialize
- * @return 0 on success, 1 if vocab.996 (the class table) is missing
- * or corrupted
- */
-int script_init_engine(EngineState *);
-
-/**
* Looks up a selector and returns its type and value
* varindex is written to iff it is non-NULL and the selector indicates a property of the object.
* @param[in] segMan The Segment Manager
@@ -314,95 +349,6 @@ SelectorType lookupSelector(SegManager *segMan, reg_t obj, Selector selectorid,
ObjVarRef *varp, reg_t *fptr);
/**
- * Makes sure that a script and its superclasses get loaded to the heap.
- * If the script already has been loaded, only the number of lockers is
- * increased. All scripts containing superclasses of this script are loaded
- * recursively as well, unless 'recursive' is set to zero. The
- * complementary function is "script_uninstantiate()" below.
- * @param[in] resMan The resource manager
- * @param[in] segMan The segment manager
- * @param[in] script_nr The script number to load
- * @return The script's segment ID or 0 if out of heap
- */
-int script_instantiate(ResourceManager *resMan, SegManager *segMan, int script_nr);
-
-/**
- * Decreases the numer of lockers of a script and unloads it if that number
- * reaches zero.
- * This function will recursively unload scripts containing its
- * superclasses, if those aren't locked by other scripts as well.
- * @param[in] segMan The segment manager
- * @param[in] version The SCI version to use
- * @param[in] script_nr The script number that is requestet to be unloaded
- */
-void script_uninstantiate(SegManager *segMan, int script_nr);
-
-/**
- * Converts the builtin Sierra game IDs to the ones we use in ScummVM
- * @param[in] gameId The internal game ID
- * @param[in] gameFlags The game's flags, which are adjusted accordingly for demos
- * @return The equivalent ScummVM game id
- */
-Common::String convertSierraGameId(const char *gameId, uint32 *gameFlags, ResourceManager *resMan);
-
-/**
- * Initializes an SCI game
- * This function must be run before script_run() is executed. Graphics data
- * is initialized iff s->gfx_state != NULL.
- * @param[in] s The state to operate on
- * @return 0 on success, 1 if an error occured.
- */
-int game_init(EngineState *s);
-
-#ifdef USE_OLD_MUSIC_FUNCTIONS
-/**
- * Initializes the sound part of an SCI game
- * This function may only be called if game_init() did not initialize
- * the sound data.
- * @param[in] s The state to initialize the sound in
- * @param[in] sound_flags Flags to pass to the sound subsystem
- * @param[in] soundVersion sound-version that got detected during game init
- * @return 0 on success, 1 if an error occured
- */
-int game_init_sound(EngineState *s, int sound_flags, SciVersion soundVersion);
-#endif
-
-/**
- * Runs an SCI game
- * This is the main function for SCI games. It takes a valid state, loads
- * script 0 to it, finds the game object, allocates a stack, and runs the
- * init method of the game object. In layman's terms, this runs an SCI game.
- * Note that, EngineState *s may be changed during the game, e.g. if a game
- * state is restored.
- * @param[in] s Pointer to the pointer of the state to operate on
- * @return 0 on success, 1 if an error occured.
- */
-int game_run(EngineState **s);
-
-/**
- * Restores an SCI game state and runs the game
- * This restores a savegame; otherwise, it behaves just like game_run().
- * @param[in] s Pointer to the pointer of the state to
- * operate on
- * @param[in] savegame_name Name of the savegame to restore
- * @return 0 on success, 1 if an error occured.
- */
-int game_restore(EngineState **s, char *savegame_name);
-
-/**
- * Uninitializes an initialized SCI game
- * This function should be run after each script_run() call.
- * @param[in] s The state to operate on
- * @return 0 on success, 1 if an error occured.
- */
-int game_exit(EngineState *s);
-
-/**
- * Instructs the virtual machine to abort
- */
-void quit_vm(EngineState *s);
-
-/**
* Read a PMachine instruction from a memory buffer and return its length.
*
* @param[in] src address from which to start parsing
diff --git a/engines/sci/engine/vm_types.h b/engines/sci/engine/vm_types.h
index 29dc798c35..828fba3d7d 100644
--- a/engines/sci/engine/vm_types.h
+++ b/engines/sci/engine/vm_types.h
@@ -27,6 +27,7 @@
#define SCI_ENGINE_VM_TYPES_H
#include "common/scummsys.h"
+#include "common/serializer.h"
namespace Sci {
@@ -56,6 +57,11 @@ struct reg_t {
int16 toSint16() const {
return (int16) offset;
}
+
+ void saveLoadWithSerializer(Common::Serializer &s) {
+ s.syncAsUint16LE(segment);
+ s.syncAsUint16LE(offset);
+ }
};
static inline reg_t make_reg(SegmentId segment, uint16 offset) {
@@ -81,6 +87,7 @@ enum {
extern const reg_t NULL_REG;
extern const reg_t SIGNAL_REG;
+extern const reg_t TRUE_REG;
// Selector ID
typedef int Selector;
diff --git a/engines/sci/event.cpp b/engines/sci/event.cpp
index c3be22b143..a100dda27c 100644
--- a/engines/sci/event.cpp
+++ b/engines/sci/event.cpp
@@ -25,6 +25,7 @@
#include "common/system.h"
#include "common/events.h"
+#include "common/file.h"
#include "sci/sci.h"
#include "sci/event.h"
@@ -34,33 +35,51 @@
namespace Sci {
-#define SCANCODE_ROWS_NR 3
+EventManager::EventManager(bool fontIsExtended) : _fontIsExtended(fontIsExtended), _modifierStates(0) {
-SciEvent::SciEvent(ResourceManager *resMan) {
- // Check, if font of current game includes extended chars
- _fontIsExtended = resMan->detectFontExtended();
+ if (getSciVersion() >= SCI_VERSION_1_MIDDLE) {
+ _usesNewKeyboardDirectionType = true;
+ } else if (getSciVersion() <= SCI_VERSION_01) {
+ _usesNewKeyboardDirectionType = false;
+ } else {
+ // they changed this somewhere inbetween SCI1EGA/EARLY
+ _usesNewKeyboardDirectionType = false;
+
+ // We are looking if script 933 exists, that one has the PseudoMouse class in it that handles it
+ // The good thing is that PseudoMouse seems to only exists in games that use the new method
+ if (g_sci->getResMan()->testResource(ResourceId(kResourceTypeScript, 933)))
+ _usesNewKeyboardDirectionType = true;
+ // Checking the keyboard driver size in here would also be a valid method, but the driver is only available
+ // in PC versions of the game
+ }
+}
+
+EventManager::~EventManager() {
}
-SciEvent::~SciEvent() {
+bool EventManager::getUsesNewKeyboardDirectionType() {
+ return _usesNewKeyboardDirectionType;
}
-struct scancode_row {
+struct ScancodeRow {
int offset;
const char *keys;
-} scancode_rows[SCANCODE_ROWS_NR] = {
+};
+
+static const ScancodeRow s_scancodeRows[] = {
{0x10, "QWERTYUIOP[]"},
{0x1e, "ASDFGHJKL;'\\"},
{0x2c, "ZXCVBNM,./"}
};
-int SciEvent::altify (int ch) {
+static int altify(int ch) {
// Calculates a PC keyboard scancode from a character */
int row;
int c = toupper((char)ch);
- for (row = 0; row < SCANCODE_ROWS_NR; row++) {
- const char *keys = scancode_rows[row].keys;
- int offset = scancode_rows[row].offset;
+ for (row = 0; row < ARRAYSIZE(s_scancodeRows); row++) {
+ const char *keys = s_scancodeRows[row].keys;
+ int offset = s_scancodeRows[row].offset;
while (*keys) {
if (*keys == c)
@@ -74,7 +93,8 @@ int SciEvent::altify (int ch) {
return ch;
}
-int SciEvent::numlockify (int c) {
+/*
+static int numlockify(int c) {
switch (c) {
case SCI_KEY_DELETE:
return '.';
@@ -102,6 +122,7 @@ int SciEvent::numlockify (int c) {
return c; // Unchanged
}
}
+*/
static const byte codepagemap_88591toDOS[0x80] = {
'?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', // 0x8x
@@ -114,9 +135,8 @@ static const byte codepagemap_88591toDOS[0x80] = {
'?', 0xa4, 0x95, 0xa2, 0x93, '?', 0x94, '?', '?', 0x97, 0xa3, 0x96, 0x81, '?', '?', 0x98 // 0xFx
};
-sciEvent SciEvent::getFromScummVM() {
- static int _modifierStates = 0; // FIXME: Avoid non-const global vars
- sciEvent input = { SCI_EVENT_NONE, 0, 0, 0 };
+SciEvent EventManager::getScummVMEvent() {
+ SciEvent input = { SCI_EVENT_NONE, 0, 0, 0 };
Common::EventManager *em = g_system->getEventManager();
Common::Event ev;
@@ -315,22 +335,26 @@ sciEvent SciEvent::getFromScummVM() {
return input;
}
-sciEvent SciEvent::get(unsigned int mask) {
+SciEvent EventManager::getSciEvent(unsigned int mask) {
//sci_event_t error_event = { SCI_EVT_ERROR, 0, 0, 0 };
- sciEvent event = { 0, 0, 0, 0 };
+ SciEvent event = { 0, 0, 0, 0 };
- // Update the screen here, since it's called very often
- g_system->updateScreen();
+ // Update the screen here, since it's called very often.
+ // Throttle the screen update rate to 60fps.
+ if (g_system->getMillis() - g_sci->getEngineState()->_screenUpdateTime >= 1000 / 60) {
+ g_system->updateScreen();
+ g_sci->getEngineState()->_screenUpdateTime = g_system->getMillis();
+ }
// Get all queued events from graphics driver
do {
- event = getFromScummVM();
+ event = getScummVMEvent();
if (event.type != SCI_EVENT_NONE)
_events.push_back(event);
} while (event.type != SCI_EVENT_NONE);
// Search for matching event in queue
- Common::List<sciEvent>::iterator iter = _events.begin();
+ Common::List<SciEvent>::iterator iter = _events.begin();
while (iter != _events.end() && !((*iter).type & mask))
++iter;
@@ -380,13 +404,13 @@ sciEvent SciEvent::get(unsigned int mask) {
return event;
}
-void SciEvent::sleep(uint32 msecs) {
+void SciEngine::sleep(uint32 msecs) {
uint32 time;
const uint32 wakeup_time = g_system->getMillis() + msecs;
while (true) {
// let backend process events and update the screen
- get(SCI_EVENT_PEEK);
+ _eventMan->getSciEvent(SCI_EVENT_PEEK);
time = g_system->getMillis();
if (time + 10 < wakeup_time) {
g_system->delayMillis(10);
diff --git a/engines/sci/event.h b/engines/sci/event.h
index 9301b1ca09..30098b0f6e 100644
--- a/engines/sci/event.h
+++ b/engines/sci/event.h
@@ -23,31 +23,29 @@
*
*/
-#ifndef SCI_ENGINE_EVENT_H
-#define SCI_ENGINE_EVENT_H
+#ifndef SCI_EVENT_H
+#define SCI_EVENT_H
#include "common/list.h"
namespace Sci {
-#define SCI_INPUT_DEFAULT_CLOCKTIME 100000
-#define SCI_INPUT_DEFAULT_REDRAWTIME 30000
-
-
-struct sciEvent {
+struct SciEvent {
short type;
short data;
short modifiers;
- short character; /* for keyboard events: 'data' after applying
- ** the effects of 'modifiers', e.g. if
- ** type == SCI_EVT_KEYBOARD
- ** data == 'a'
- ** buckybits == SCI_EVM_LSHIFT
- ** then
- ** character == 'A'
- ** For 'Alt', characters are interpreted by their
- ** PC keyboard scancodes.
- */
+ /**
+ * For keyboard events: 'data' after applying
+ * the effects of 'modifiers', e.g. if
+ * type == SCI_EVT_KEYBOARD
+ * data == 'a'
+ * buckybits == SCI_EVM_LSHIFT
+ * then
+ * character == 'A'
+ * For 'Alt', characters are interpreted by their
+ * PC keyboard scancodes.
+ */
+ short character;
};
/*Values for type*/
@@ -55,7 +53,7 @@ struct sciEvent {
#define SCI_EVENT_MOUSE_PRESS (1<<0)
#define SCI_EVENT_MOUSE_RELEASE (1<<1)
#define SCI_EVENT_KEYBOARD (1<<2)
-#define SCI_EVENT_JOYSTICK (1<<6)
+#define SCI_EVENT_DIRECTION (1<<6)
#define SCI_EVENT_SAID (1<<7)
/*Fake values for other events*/
#define SCI_EVENT_ERROR (1<<10)
@@ -111,25 +109,22 @@ struct sciEvent {
#define SCI_KEYMOD_NO_FOOLOCK (~(SCI_KEYMOD_SCRLOCK | SCI_KEYMOD_NUMLOCK | SCI_KEYMOD_CAPSLOCK | SCI_KEYMOD_INSERT))
#define SCI_KEYMOD_ALL 0xFF
-class SciEvent {
+class EventManager {
public:
- SciEvent(ResourceManager *resMgr);
- ~SciEvent();
-
- sciEvent get(unsigned int mask);
+ EventManager(bool fontIsExtended);
+ ~EventManager();
- void sleep(uint32 msecs);
+ SciEvent getSciEvent(unsigned int mask);
+ bool getUsesNewKeyboardDirectionType();
private:
- int altify (int ch);
- int shiftify (int c);
- int numlockify (int c);
- sciEvent getFromScummVM();
+ SciEvent getScummVMEvent();
- ResourceManager *_resMan;
+ const bool _fontIsExtended;
+ int _modifierStates;
+ Common::List<SciEvent> _events;
- bool _fontIsExtended;
- Common::List<sciEvent> _events;
+ bool _usesNewKeyboardDirectionType;
};
} // End of namespace Sci
diff --git a/engines/sci/graphics/animate.cpp b/engines/sci/graphics/animate.cpp
index c201b2cfb7..6fb427500d 100644
--- a/engines/sci/graphics/animate.cpp
+++ b/engines/sci/graphics/animate.cpp
@@ -28,6 +28,7 @@
#include "graphics/primitives.h"
#include "sci/sci.h"
+#include "sci/engine/kernel.h"
#include "sci/engine/state.h"
#include "sci/engine/selector.h"
#include "sci/engine/vm.h"
@@ -35,6 +36,7 @@
#include "sci/graphics/cursor.h"
#include "sci/graphics/ports.h"
#include "sci/graphics/paint16.h"
+#include "sci/graphics/palette.h"
#include "sci/graphics/view.h"
#include "sci/graphics/screen.h"
#include "sci/graphics/transitions.h"
@@ -48,28 +50,25 @@ GfxAnimate::GfxAnimate(EngineState *state, GfxCache *cache, GfxPorts *ports, Gfx
}
GfxAnimate::~GfxAnimate() {
- free(_listData);
- free(_lastCastData);
}
void GfxAnimate::init() {
- _listData = NULL;
- _listCount = 0;
- _lastCastData = NULL;
- _lastCastCount = 0;
+ _lastCastData.clear();
_ignoreFastCast = false;
// fastCast object is not found in any SCI games prior SCI1
if (getSciVersion() <= SCI_VERSION_01)
_ignoreFastCast = true;
// Also if fastCast object exists at gamestartup, we can assume that the interpreter doesnt do kAnimate aborts
- // (found in larry 1)
- if (!_s->_segMan->findObjectByName("fastCast").isNull())
- _ignoreFastCast = true;
+ // (found in Larry 1)
+ if (getSciVersion() > SCI_VERSION_0_EARLY) {
+ if (!_s->_segMan->findObjectByName("fastCast").isNull())
+ _ignoreFastCast = true;
+ }
}
void GfxAnimate::disposeLastCast() {
- _lastCastCount = 0;
+ _lastCastData.clear();
}
bool GfxAnimate::invoke(List *list, int argc, reg_t *argv) {
@@ -84,10 +83,8 @@ bool GfxAnimate::invoke(List *list, int argc, reg_t *argv) {
if (!_ignoreFastCast) {
// Check if the game has a fastCast object set
// if we don't abort kAnimate processing, at least in kq5 there will be animation cels drawn into speech boxes.
- reg_t global84 = _s->script_000->_localsBlock->_locals[84];
-
- if (!global84.isNull()) {
- if (!strcmp(_s->_segMan->getObjectName(global84), "fastCast"))
+ if (!_s->variables[VAR_GLOBAL][84].isNull()) {
+ if (!strcmp(_s->_segMan->getObjectName(_s->variables[VAR_GLOBAL][84]), "fastCast"))
return false;
}
}
@@ -95,7 +92,12 @@ bool GfxAnimate::invoke(List *list, int argc, reg_t *argv) {
signal = readSelectorValue(_s->_segMan, curObject, SELECTOR(signal));
if (!(signal & kSignalFrozen)) {
// Call .doit method of that object
- invokeSelector(_s, curObject, g_sci->getKernel()->_selectorCache.doit, kContinueOnInvalidSelector, argc, argv, 0);
+ invokeSelector(_s, curObject, SELECTOR(doit), argc, argv, 0);
+
+ // If a game is being loaded, stop processing
+ if (_s->abortScriptProcessing != kAbortNone || g_engine->shouldQuit())
+ return true; // Stop processing
+
// Lookup node again, since the nodetable it was in may have been reallocated
curNode = _s->_segMan->lookupNode(curAddress);
}
@@ -108,99 +110,64 @@ bool GfxAnimate::invoke(List *list, int argc, reg_t *argv) {
return true;
}
-bool sortHelper(const AnimateEntry* entry1, const AnimateEntry* entry2) {
- if (entry1->y == entry2->y) {
+bool sortHelper(const AnimateEntry &entry1, const AnimateEntry &entry2) {
+ if (entry1.y == entry2.y) {
// if both y and z are the same, use the order we were given originally
// this is needed for special cases like iceman room 35
- if (entry1->z == entry2->z)
- return entry1->givenOrderNo < entry2->givenOrderNo;
+ if (entry1.z == entry2.z)
+ return entry1.givenOrderNo < entry2.givenOrderNo;
else
- return entry1->z < entry2->z;
+ return entry1.z < entry2.z;
}
- return entry1->y < entry2->y;
+ return entry1.y < entry2.y;
}
void GfxAnimate::makeSortedList(List *list) {
reg_t curAddress = list->first;
Node *curNode = _s->_segMan->lookupNode(curAddress);
- reg_t curObject;
- AnimateEntry *listEntry;
- int16 listNr, listCount = 0;
-
- // Count the list entries
- while (curNode) {
- listCount++;
- curAddress = curNode->succ;
- curNode = _s->_segMan->lookupNode(curAddress);
- }
+ int16 listNr;
+ // Clear lists
_list.clear();
-
- // No entries -> exit immediately
- if (listCount == 0)
- return;
-
- // Adjust list size, if needed
- if ((_listData == NULL) || (_listCount < listCount)) {
- free(_listData);
- _listData = (AnimateEntry *)malloc(listCount * sizeof(AnimateEntry));
- if (!_listData)
- error("Could not allocate memory for _listData");
- _listCount = listCount;
-
- free(_lastCastData);
- _lastCastData = (AnimateEntry *)malloc(listCount * sizeof(AnimateEntry));
- if (!_lastCastData)
- error("Could not allocate memory for _lastCastData");
- _lastCastCount = 0;
- }
+ _lastCastData.clear();
// Fill the list
- curAddress = list->first;
- curNode = _s->_segMan->lookupNode(curAddress);
- listEntry = _listData;
- for (listNr = 0; listNr < listCount; listNr++) {
- curObject = curNode->value;
- listEntry->object = curObject;
+ for (listNr = 0; curNode != 0; listNr++) {
+ AnimateEntry listEntry;
+ const reg_t curObject = curNode->value;
+ listEntry.object = curObject;
// Get data from current object
- listEntry->givenOrderNo = listNr;
- 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));
+ listEntry.givenOrderNo = listNr;
+ 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 = readSelectorValue(_s->_segMan, curObject, SELECTOR(scaleSignal));
- if (listEntry->scaleSignal & kScaleSignalDoScaling) {
- listEntry->scaleX = readSelectorValue(_s->_segMan, curObject, SELECTOR(scaleX));
- listEntry->scaleY = readSelectorValue(_s->_segMan, curObject, SELECTOR(scaleY));
+ listEntry.scaleSignal = readSelectorValue(_s->_segMan, curObject, SELECTOR(scaleSignal));
+ if (listEntry.scaleSignal & kScaleSignalDoScaling) {
+ listEntry.scaleX = readSelectorValue(_s->_segMan, curObject, SELECTOR(scaleX));
+ listEntry.scaleY = readSelectorValue(_s->_segMan, curObject, SELECTOR(scaleY));
} else {
- listEntry->scaleX = 128;
- listEntry->scaleY = 128;
+ listEntry.scaleX = 128;
+ listEntry.scaleY = 128;
}
- // TODO
- // On scaleSignal bit 1 sierra sci does some stuff with global var 2, current Port
- // and some other stuff and sets scaleX/Y accordingly. It seems this functionality is needed in at
- // least sq5 right when starting the game before wilco exists the room. Currently we dont get scaling
- // but sierra sci does scaling there. I dont fully understand the code yet, that's why i didnt implement
- // anything.
} else {
- listEntry->scaleSignal = 0;
- listEntry->scaleX = 128;
- listEntry->scaleY = 128;
+ listEntry.scaleSignal = 0;
+ listEntry.scaleX = 128;
+ listEntry.scaleY = 128;
}
- // listEntry->celRect is filled in AnimateFill()
- listEntry->showBitsFlag = false;
+ // listEntry.celRect is filled in AnimateFill()
+ listEntry.showBitsFlag = false;
_list.push_back(listEntry);
- listEntry++;
curAddress = curNode->succ;
curNode = _s->_segMan->lookupNode(curAddress);
}
@@ -211,47 +178,73 @@ void GfxAnimate::makeSortedList(List *list) {
void GfxAnimate::fill(byte &old_picNotValid) {
reg_t curObject;
- AnimateEntry *listEntry;
uint16 signal;
GfxView *view = NULL;
- AnimateList::iterator listIterator;
- AnimateList::iterator listEnd = _list.end();
+ AnimateList::iterator it;
+ const AnimateList::iterator end = _list.end();
- listIterator = _list.begin();
- while (listIterator != listEnd) {
- listEntry = *listIterator;
- curObject = listEntry->object;
+ for (it = _list.begin(); it != end; ++it) {
+ curObject = it->object;
// Get the corresponding view
- view = _cache->getView(listEntry->viewId);
+ view = _cache->getView(it->viewId);
// adjust loop and cel, if any of those is invalid
- if (listEntry->loopNo >= view->getLoopCount()) {
- listEntry->loopNo = 0;
- writeSelectorValue(_s->_segMan, curObject, SELECTOR(loop), listEntry->loopNo);
+ if (it->loopNo >= view->getLoopCount()) {
+ it->loopNo = 0;
+ writeSelectorValue(_s->_segMan, curObject, SELECTOR(loop), it->loopNo);
}
- if (listEntry->celNo >= view->getCelCount(listEntry->loopNo)) {
- listEntry->celNo = 0;
- writeSelectorValue(_s->_segMan, curObject, SELECTOR(cel), listEntry->celNo);
+ if (it->celNo >= view->getCelCount(it->loopNo)) {
+ it->celNo = 0;
+ writeSelectorValue(_s->_segMan, curObject, SELECTOR(cel), it->celNo);
+ }
+
+ // Process global scaling, if needed
+ if (it->scaleSignal & kScaleSignalDoScaling) {
+ if (it->scaleSignal & kScaleSignalGlobalScaling) {
+ // Global scaling uses global var 2 and some other stuff to calculate scaleX/scaleY
+ int16 maxScale = readSelectorValue(_s->_segMan, curObject, SELECTOR(maxScale));
+ int16 celHeight = view->getHeight(it->loopNo, it->celNo);
+ int16 maxCelHeight = (maxScale * celHeight) >> 7;
+ reg_t globalVar2 = _s->variables[VAR_GLOBAL][2]; // current room object
+ int16 vanishingY = readSelectorValue(_s->_segMan, globalVar2, SELECTOR(vanishingY));
+
+ int16 fixedPortY = _ports->getPort()->rect.bottom - vanishingY;
+ int16 fixedEntryY = it->y - vanishingY;
+ if (!fixedEntryY)
+ fixedEntryY = 1;
+
+ if ((celHeight == 0) || (fixedPortY == 0))
+ error("global scaling panic");
+
+ it->scaleY = ( maxCelHeight * fixedEntryY ) / fixedPortY;
+ it->scaleY = (it->scaleY * 128) / celHeight;
+
+ it->scaleX = it->scaleY;
+
+ // and set objects scale selectors
+ writeSelectorValue(_s->_segMan, curObject, SELECTOR(scaleX), it->scaleX);
+ writeSelectorValue(_s->_segMan, curObject, SELECTOR(scaleY), it->scaleY);
+ }
}
// Create rect according to coordinates and given cel
- if (listEntry->scaleSignal & kScaleSignalDoScaling) {
- view->getCelScaledRect(listEntry->loopNo, listEntry->celNo, listEntry->x, listEntry->y, listEntry->z, listEntry->scaleX, listEntry->scaleY, &listEntry->celRect);
+ if (it->scaleSignal & kScaleSignalDoScaling) {
+ view->getCelScaledRect(it->loopNo, it->celNo, it->x, it->y, it->z, it->scaleX, it->scaleY, it->celRect);
} else {
- view->getCelRect(listEntry->loopNo, listEntry->celNo, listEntry->x, listEntry->y, listEntry->z, &listEntry->celRect);
+ view->getCelRect(it->loopNo, it->celNo, it->x, it->y, it->z, it->celRect);
}
- 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);
+ writeSelectorValue(_s->_segMan, curObject, SELECTOR(nsLeft), it->celRect.left);
+ writeSelectorValue(_s->_segMan, curObject, SELECTOR(nsTop), it->celRect.top);
+ writeSelectorValue(_s->_segMan, curObject, SELECTOR(nsRight), it->celRect.right);
+ writeSelectorValue(_s->_segMan, curObject, SELECTOR(nsBottom), it->celRect.bottom);
- signal = listEntry->signal;
+ signal = it->signal;
// Calculate current priority according to y-coordinate
if (!(signal & kSignalFixedPriority)) {
- listEntry->priority = _ports->kernelCoordinateToPriority(listEntry->y);
- writeSelectorValue(_s->_segMan, curObject, SELECTOR(priority), listEntry->priority);
+ it->priority = _ports->kernelCoordinateToPriority(it->y);
+ writeSelectorValue(_s->_segMan, curObject, SELECTOR(priority), it->priority);
}
if (signal & kSignalNoUpdate) {
@@ -260,177 +253,154 @@ void GfxAnimate::fill(byte &old_picNotValid) {
|| (!(signal & kSignalHidden) && signal & kSignalRemoveView)
|| (signal & kSignalAlwaysUpdate))
old_picNotValid++;
- signal &= 0xFFFF ^ kSignalStopUpdate;
+ signal &= ~kSignalStopUpdate;
} else {
if (signal & kSignalStopUpdate || signal & kSignalAlwaysUpdate)
old_picNotValid++;
- signal &= 0xFFFF ^ kSignalForceUpdate;
+ signal &= ~kSignalForceUpdate;
}
- listEntry->signal = signal;
-
- listIterator++;
+ it->signal = signal;
}
}
void GfxAnimate::update() {
reg_t curObject;
- AnimateEntry *listEntry;
uint16 signal;
reg_t bitsHandle;
Common::Rect rect;
- AnimateList::iterator listIterator;
- AnimateList::iterator listBegin = _list.begin();
- AnimateList::iterator listEnd = _list.end();
+ AnimateList::iterator it;
+ const AnimateList::iterator end = _list.end();
// Remove all no-update cels, if requested
- listIterator = _list.reverse_begin();
- while (listIterator != listEnd) {
- listEntry = *listIterator;
- curObject = listEntry->object;
- signal = listEntry->signal;
+ for (it = _list.reverse_begin(); it != end; --it) {
+ curObject = it->object;
+ signal = it->signal;
if (signal & kSignalNoUpdate) {
if (!(signal & kSignalRemoveView)) {
bitsHandle = readSelector(_s->_segMan, curObject, SELECTOR(underBits));
if (_screen->_picNotValid != 1) {
_paint16->bitsRestore(bitsHandle);
- listEntry->showBitsFlag = true;
+ it->showBitsFlag = true;
} else {
_paint16->bitsFree(bitsHandle);
}
writeSelectorValue(_s->_segMan, curObject, SELECTOR(underBits), 0);
}
- signal &= 0xFFFF ^ kSignalForceUpdate;
- signal &= signal & kSignalViewUpdated ? 0xFFFF ^ (kSignalViewUpdated | kSignalNoUpdate) : 0xFFFF;
+ signal &= ~kSignalForceUpdate;
+ if (signal & kSignalViewUpdated)
+ signal &= ~(kSignalViewUpdated | kSignalNoUpdate);
} else if (signal & kSignalStopUpdate) {
- signal = (signal & (0xFFFF ^ kSignalStopUpdate)) | kSignalNoUpdate;
+ signal &= ~kSignalStopUpdate;
+ signal |= kSignalNoUpdate;
}
- listEntry->signal = signal;
- listIterator--;
+ it->signal = signal;
}
// Draw always-update cels
- listIterator = listBegin;
- while (listIterator != listEnd) {
- listEntry = *listIterator;
- curObject = listEntry->object;
- signal = listEntry->signal;
+ for (it = _list.begin(); it != end; ++it) {
+ curObject = it->object;
+ signal = it->signal;
if (signal & kSignalAlwaysUpdate) {
// draw corresponding cel
- _paint16->drawCel(listEntry->viewId, listEntry->loopNo, listEntry->celNo, listEntry->celRect, listEntry->priority, listEntry->paletteNo, listEntry->scaleX, listEntry->scaleY);
- listEntry->showBitsFlag = true;
+ _paint16->drawCel(it->viewId, it->loopNo, it->celNo, it->celRect, it->priority, it->paletteNo, it->scaleX, it->scaleY);
+ it->showBitsFlag = true;
- signal &= 0xFFFF ^ (kSignalStopUpdate | kSignalViewUpdated | kSignalNoUpdate | kSignalForceUpdate);
+ signal &= ~(kSignalStopUpdate | kSignalViewUpdated | kSignalNoUpdate | kSignalForceUpdate);
if ((signal & kSignalIgnoreActor) == 0) {
- rect = listEntry->celRect;
- rect.top = CLIP<int16>(_ports->kernelPriorityToCoordinate(listEntry->priority) - 1, rect.top, rect.bottom - 1);
+ rect = it->celRect;
+ rect.top = CLIP<int16>(_ports->kernelPriorityToCoordinate(it->priority) - 1, rect.top, rect.bottom - 1);
_paint16->fillRect(rect, GFX_SCREEN_MASK_CONTROL, 0, 0, 15);
}
- listEntry->signal = signal;
+ it->signal = signal;
}
- listIterator++;
}
// Saving background for all NoUpdate-cels
- listIterator = listBegin;
- while (listIterator != listEnd) {
- listEntry = *listIterator;
- curObject = listEntry->object;
- signal = listEntry->signal;
+ for (it = _list.begin(); it != end; ++it) {
+ curObject = it->object;
+ signal = it->signal;
if (signal & kSignalNoUpdate) {
if (signal & kSignalHidden) {
signal |= kSignalRemoveView;
} else {
- signal &= 0xFFFF ^ kSignalRemoveView;
+ signal &= ~kSignalRemoveView;
if (signal & kSignalIgnoreActor)
- bitsHandle = _paint16->bitsSave(listEntry->celRect, GFX_SCREEN_MASK_VISUAL|GFX_SCREEN_MASK_PRIORITY);
+ bitsHandle = _paint16->bitsSave(it->celRect, GFX_SCREEN_MASK_VISUAL|GFX_SCREEN_MASK_PRIORITY);
else
- bitsHandle = _paint16->bitsSave(listEntry->celRect, GFX_SCREEN_MASK_ALL);
+ bitsHandle = _paint16->bitsSave(it->celRect, GFX_SCREEN_MASK_ALL);
writeSelector(_s->_segMan, curObject, SELECTOR(underBits), bitsHandle);
}
- listEntry->signal = signal;
+ it->signal = signal;
}
- listIterator++;
}
// Draw NoUpdate cels
- listIterator = listBegin;
- while (listIterator != listEnd) {
- listEntry = *listIterator;
- curObject = listEntry->object;
- signal = listEntry->signal;
+ for (it = _list.begin(); it != end; ++it) {
+ curObject = it->object;
+ signal = it->signal;
if (signal & kSignalNoUpdate && !(signal & kSignalHidden)) {
// draw corresponding cel
- _paint16->drawCel(listEntry->viewId, listEntry->loopNo, listEntry->celNo, listEntry->celRect, listEntry->priority, listEntry->paletteNo, listEntry->scaleX, listEntry->scaleY);
- listEntry->showBitsFlag = true;
+ _paint16->drawCel(it->viewId, it->loopNo, it->celNo, it->celRect, it->priority, it->paletteNo, it->scaleX, it->scaleY);
+ it->showBitsFlag = true;
- if ((signal & kSignalIgnoreActor) == 0) {
- rect = listEntry->celRect;
- rect.top = CLIP<int16>(_ports->kernelPriorityToCoordinate(listEntry->priority) - 1, rect.top, rect.bottom - 1);
+ if (!(signal & kSignalIgnoreActor)) {
+ rect = it->celRect;
+ rect.top = CLIP<int16>(_ports->kernelPriorityToCoordinate(it->priority) - 1, rect.top, rect.bottom - 1);
_paint16->fillRect(rect, GFX_SCREEN_MASK_CONTROL, 0, 0, 15);
}
}
- listIterator++;
}
}
void GfxAnimate::drawCels() {
reg_t curObject;
- AnimateEntry *listEntry;
- AnimateEntry *lastCastEntry = _lastCastData;
uint16 signal;
reg_t bitsHandle;
- AnimateList::iterator listIterator;
- AnimateList::iterator listEnd = _list.end();
- _lastCastCount = 0;
+ AnimateList::iterator it;
+ const AnimateList::iterator end = _list.end();
+ _lastCastData.clear();
- listIterator = _list.begin();
- while (listIterator != listEnd) {
- listEntry = *listIterator;
- curObject = listEntry->object;
- signal = listEntry->signal;
+ for (it = _list.begin(); it != end; ++it) {
+ curObject = it->object;
+ signal = it->signal;
if (!(signal & (kSignalNoUpdate | kSignalHidden | kSignalAlwaysUpdate))) {
// Save background
- bitsHandle = _paint16->bitsSave(listEntry->celRect, GFX_SCREEN_MASK_ALL);
+ bitsHandle = _paint16->bitsSave(it->celRect, GFX_SCREEN_MASK_ALL);
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);
- listEntry->showBitsFlag = true;
+ _paint16->drawCel(it->viewId, it->loopNo, it->celNo, it->celRect, it->priority, it->paletteNo, it->scaleX, it->scaleY);
+ it->showBitsFlag = true;
if (signal & kSignalRemoveView) {
- signal &= 0xFFFF ^ kSignalRemoveView;
+ signal &= ~kSignalRemoveView;
}
- listEntry->signal = signal;
+ it->signal = signal;
// Remember that entry in lastCast
- memcpy(lastCastEntry, listEntry, sizeof(AnimateEntry));
- lastCastEntry++; _lastCastCount++;
+ _lastCastData.push_back(*it);
}
- listIterator++;
}
}
void GfxAnimate::updateScreen(byte oldPicNotValid) {
reg_t curObject;
- AnimateEntry *listEntry;
uint16 signal;
- AnimateList::iterator listIterator;
- AnimateList::iterator listEnd = _list.end();
+ AnimateList::iterator it;
+ const AnimateList::iterator end = _list.end();
Common::Rect lsRect;
Common::Rect workerRect;
- listIterator = _list.begin();
- while (listIterator != listEnd) {
- listEntry = *listIterator;
- curObject = listEntry->object;
- signal = listEntry->signal;
+ for (it = _list.begin(); it != end; ++it) {
+ curObject = it->object;
+ signal = it->signal;
- if (listEntry->showBitsFlag || !(signal & (kSignalRemoveView | kSignalNoUpdate) ||
+ if (it->showBitsFlag || !(signal & (kSignalRemoveView | kSignalNoUpdate) ||
(!(signal & kSignalRemoveView) && (signal & kSignalNoUpdate) && oldPicNotValid))) {
lsRect.left = readSelectorValue(_s->_segMan, curObject, SELECTOR(lsLeft));
lsRect.top = readSelectorValue(_s->_segMan, curObject, SELECTOR(lsTop));
@@ -438,27 +408,27 @@ void GfxAnimate::updateScreen(byte oldPicNotValid) {
lsRect.bottom = readSelectorValue(_s->_segMan, curObject, SELECTOR(lsBottom));
workerRect = lsRect;
- workerRect.clip(listEntry->celRect);
+ workerRect.clip(it->celRect);
if (!workerRect.isEmpty()) {
workerRect = lsRect;
- workerRect.extend(listEntry->celRect);
+ workerRect.extend(it->celRect);
} else {
_paint16->bitsShow(lsRect);
- workerRect = listEntry->celRect;
+ workerRect = it->celRect;
}
- 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);
+ writeSelectorValue(_s->_segMan, curObject, SELECTOR(lsLeft), it->celRect.left);
+ writeSelectorValue(_s->_segMan, curObject, SELECTOR(lsTop), it->celRect.top);
+ writeSelectorValue(_s->_segMan, curObject, SELECTOR(lsRight), it->celRect.right);
+ writeSelectorValue(_s->_segMan, curObject, SELECTOR(lsBottom), it->celRect.bottom);
+ // may get used for debugging
+ //_paint16->frameRect(workerRect);
_paint16->bitsShow(workerRect);
if (signal & kSignalHidden) {
- listEntry->signal |= kSignalRemoveView;
+ it->signal |= kSignalRemoveView;
}
}
-
- listIterator++;
}
// use this for debug purposes
// _screen->copyToScreen();
@@ -466,30 +436,26 @@ void GfxAnimate::updateScreen(byte oldPicNotValid) {
void GfxAnimate::restoreAndDelete(int argc, reg_t *argv) {
reg_t curObject;
- AnimateEntry *listEntry;
uint16 signal;
- AnimateList::iterator listIterator;
- AnimateList::iterator listEnd = _list.end();
+ AnimateList::iterator it;
+ const AnimateList::iterator end = _list.end();
- // This has to be done in a separate loop. At least in sq1 some .dispose modifies FIXEDLOOP flag in signal for
- // another object. In that case we would overwrite the new signal with our version of the old signal
- listIterator = _list.begin();
- while (listIterator != listEnd) {
- listEntry = *listIterator;
- curObject = listEntry->object;
- signal = listEntry->signal;
+ // This has to be done in a separate loop. At least in sq1 some .dispose
+ // modifies FIXEDLOOP flag in signal for another object. In that case we
+ // would overwrite the new signal with our version of the old signal.
+ for (it = _list.begin(); it != end; ++it) {
+ curObject = it->object;
+ signal = it->signal;
// Finally update signal
writeSelectorValue(_s->_segMan, curObject, SELECTOR(signal), signal);
- listIterator++;
}
- listIterator = _list.reverse_begin();
- while (listIterator != listEnd) {
- 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
+ for (it = _list.reverse_begin(); it != end; --it) {
+ curObject = it->object;
+ // We read out signal here again, this is not by accident but to ensure
+ // that we got an up-to-date signal
signal = readSelectorValue(_s->_segMan, curObject, SELECTOR(signal));
if ((signal & (kSignalNoUpdate | kSignalRemoveView)) == 0) {
@@ -499,31 +465,24 @@ void GfxAnimate::restoreAndDelete(int argc, reg_t *argv) {
if (signal & kSignalDisposeMe) {
// Call .delete_ method of that object
- invokeSelector(_s, curObject, g_sci->getKernel()->_selectorCache.delete_, kContinueOnInvalidSelector, argc, argv, 0);
+ invokeSelector(_s, curObject, SELECTOR(delete_), argc, argv, 0);
}
- listIterator--;
}
}
void GfxAnimate::reAnimate(Common::Rect rect) {
- AnimateEntry *lastCastEntry;
- uint16 lastCastCount;
-
- if (_lastCastCount > 0) {
- lastCastEntry = _lastCastData;
- lastCastCount = _lastCastCount;
- while (lastCastCount > 0) {
- lastCastEntry->castHandle = _paint16->bitsSave(lastCastEntry->celRect, GFX_SCREEN_MASK_VISUAL|GFX_SCREEN_MASK_PRIORITY);
- _paint16->drawCel(lastCastEntry->viewId, lastCastEntry->loopNo, lastCastEntry->celNo, lastCastEntry->celRect, lastCastEntry->priority, lastCastEntry->paletteNo, lastCastEntry->scaleX, lastCastEntry->scaleY);
- lastCastEntry++; lastCastCount--;
+ if (!_lastCastData.empty()) {
+ AnimateArray::iterator it;
+ AnimateArray::iterator end = _lastCastData.end();
+ for (it = _lastCastData.begin(); it != end; ++it) {
+ it->castHandle = _paint16->bitsSave(it->celRect, GFX_SCREEN_MASK_VISUAL|GFX_SCREEN_MASK_PRIORITY);
+ _paint16->drawCel(it->viewId, it->loopNo, it->celNo, it->celRect, it->priority, it->paletteNo, it->scaleX, it->scaleY);
}
_paint16->bitsShow(rect);
// restoring
- lastCastCount = _lastCastCount;
- while (lastCastCount > 0) {
- lastCastEntry--;
- _paint16->bitsRestore(lastCastEntry->castHandle);
- lastCastCount--;
+ while (it != _lastCastData.begin()) { // FIXME: HACK, this iterator use is not very safe
+ it--;
+ _paint16->bitsRestore(it->castHandle);
}
} else {
_paint16->bitsShow(rect);
@@ -532,33 +491,28 @@ void GfxAnimate::reAnimate(Common::Rect rect) {
void GfxAnimate::addToPicDrawCels() {
reg_t curObject;
- AnimateEntry *listEntry;
GfxView *view = NULL;
- AnimateList::iterator listIterator;
- AnimateList::iterator listEnd = _list.end();
+ AnimateList::iterator it;
+ const AnimateList::iterator end = _list.end();
- listIterator = _list.begin();
- while (listIterator != listEnd) {
- listEntry = *listIterator;
- curObject = listEntry->object;
+ for (it = _list.begin(); it != end; ++it) {
+ curObject = it->object;
- if (listEntry->priority == -1)
- listEntry->priority = _ports->kernelCoordinateToPriority(listEntry->y);
+ if (it->priority == -1)
+ it->priority = _ports->kernelCoordinateToPriority(it->y);
// Get the corresponding view
- view = _cache->getView(listEntry->viewId);
+ view = _cache->getView(it->viewId);
// Create rect according to coordinates and given cel
- view->getCelRect(listEntry->loopNo, listEntry->celNo, listEntry->x, listEntry->y, listEntry->z, &listEntry->celRect);
+ view->getCelRect(it->loopNo, it->celNo, it->x, it->y, it->z, it->celRect);
// draw corresponding cel
- _paint16->drawCel(listEntry->viewId, listEntry->loopNo, listEntry->celNo, listEntry->celRect, listEntry->priority, listEntry->paletteNo);
- if ((listEntry->signal & kSignalIgnoreActor) == 0) {
- listEntry->celRect.top = CLIP<int16>(_ports->kernelPriorityToCoordinate(listEntry->priority) - 1, listEntry->celRect.top, listEntry->celRect.bottom - 1);
- _paint16->fillRect(listEntry->celRect, GFX_SCREEN_MASK_CONTROL, 0, 0, 15);
+ _paint16->drawCel(it->viewId, it->loopNo, it->celNo, it->celRect, it->priority, it->paletteNo);
+ if ((it->signal & kSignalIgnoreActor) == 0) {
+ it->celRect.top = CLIP<int16>(_ports->kernelPriorityToCoordinate(it->priority) - 1, it->celRect.top, it->celRect.bottom - 1);
+ _paint16->fillRect(it->celRect, GFX_SCREEN_MASK_CONTROL, 0, 0, 15);
}
-
- listIterator++;
}
}
@@ -567,7 +521,7 @@ void GfxAnimate::addToPicDrawView(GuiResourceId viewId, int16 loopNo, int16 celN
Common::Rect celRect;
// Create rect according to coordinates and given cel
- view->getCelRect(loopNo, celNo, leftPos, topPos, priority, &celRect);
+ view->getCelRect(loopNo, celNo, leftPos, topPos, priority, celRect);
_paint16->drawCel(view, loopNo, celNo, celRect, priority, 0);
}
@@ -592,6 +546,9 @@ void GfxAnimate::animateShowPic() {
void GfxAnimate::kernelAnimate(reg_t listReference, bool cycle, int argc, reg_t *argv) {
byte old_picNotValid = _screen->_picNotValid;
+ if (getSciVersion() >= SCI_VERSION_1_1)
+ _palette->palVaryUpdate();
+
if (listReference.isNull()) {
disposeLastCast();
if (_screen->_picNotValid)
@@ -606,6 +563,9 @@ void GfxAnimate::kernelAnimate(reg_t listReference, bool cycle, int argc, reg_t
if (cycle) {
if (!invoke(list, argc, argv))
return;
+
+ // Look up the list again, as it may have been modified
+ list = _s->_segMan->lookupList(listReference);
}
Port *oldPort = _ports->setPort((Port *)_ports->_picWind);
@@ -615,9 +575,9 @@ void GfxAnimate::kernelAnimate(reg_t listReference, bool cycle, int argc, reg_t
fill(old_picNotValid);
if (old_picNotValid) {
- // beginUpdate()/endUpdate() were introduced SCI1
- // calling those for SCI0 will work most of the time but breaks minor stuff like percentage bar of qfg1ega
- // at the character skill screen
+ // beginUpdate()/endUpdate() were introduced SCI1.
+ // Calling those for SCI0 will work most of the time but breaks minor
+ // stuff like percentage bar of qfg1ega at the character skill screen.
if (getSciVersion() >= SCI_VERSION_1_EGA)
_ports->beginUpdate(_ports->_picWind);
update();
@@ -633,7 +593,7 @@ void GfxAnimate::kernelAnimate(reg_t listReference, bool cycle, int argc, reg_t
updateScreen(old_picNotValid);
restoreAndDelete(argc, argv);
- if (getLastCastCount() > 1)
+ if (_lastCastData.size() > 1)
_s->_throttleTrigger = true;
_ports->setPort(oldPort);
diff --git a/engines/sci/graphics/animate.h b/engines/sci/graphics/animate.h
index 706b7182cf..7e82187eed 100644
--- a/engines/sci/graphics/animate.h
+++ b/engines/sci/graphics/animate.h
@@ -51,9 +51,9 @@ enum ViewSignals {
};
enum ViewScaleSignals {
- kScaleSignalDoScaling = 0x0001, // enables scaling when drawing that cel (involves scaleX and scaleY)
- kScaleSignalUnknown1 = 0x0002, // seems to do something with globalvar 2, sets scaleX/scaleY
- kScaleSignalUnknown2 = 0x0004 // really unknown
+ kScaleSignalDoScaling = 0x0001, // enables scaling when drawing that cel (involves scaleX and scaleY)
+ kScaleSignalGlobalScaling = 0x0002, // means that global scaling shall get applied on that cel (sets scaleX/scaleY)
+ kScaleSignalUnknown2 = 0x0004 // really unknown
};
struct AnimateEntry {
@@ -73,7 +73,8 @@ struct AnimateEntry {
bool showBitsFlag;
reg_t castHandle;
};
-typedef Common::List<AnimateEntry *> AnimateList;
+typedef Common::List<AnimateEntry> AnimateList;
+typedef Common::Array<AnimateEntry> AnimateArray;
class GfxCache;
class GfxCursor;
@@ -90,9 +91,6 @@ public:
GfxAnimate(EngineState *state, GfxCache *cache, GfxPorts *ports, GfxPaint16 *paint16, GfxScreen *screen, GfxPalette *palette, GfxCursor *cursor, GfxTransitions *transitions);
virtual ~GfxAnimate();
- // FIXME: Don't store EngineState
- void resetEngineState(EngineState *newState) { _s = newState; }
-
void disposeLastCast();
bool invoke(List *list, int argc, reg_t *argv);
void makeSortedList(List *list);
@@ -105,8 +103,6 @@ public:
void addToPicDrawCels();
void addToPicDrawView(GuiResourceId viewId, int16 loopNo, int16 celNo, int16 leftPos, int16 topPos, int16 priority, int16 control);
- uint16 getLastCastCount() { return _lastCastCount; }
-
virtual void kernelAnimate(reg_t listReference, bool cycle, int argc, reg_t *argv);
virtual void kernelAddToPicList(reg_t listReference, int argc, reg_t *argv);
virtual void kernelAddToPicView(GuiResourceId viewId, int16 loopNo, int16 celNo, int16 leftPos, int16 topPos, int16 priority, int16 control);
@@ -126,12 +122,8 @@ private:
GfxCursor *_cursor;
GfxTransitions *_transitions;
- uint16 _listCount;
- AnimateEntry *_listData;
AnimateList _list;
-
- uint16 _lastCastCount;
- AnimateEntry *_lastCastData;
+ AnimateArray _lastCastData;
bool _ignoreFastCast;
};
diff --git a/engines/sci/graphics/cache.cpp b/engines/sci/graphics/cache.cpp
index 81bdab80ea..8caa28b3a1 100644
--- a/engines/sci/graphics/cache.cpp
+++ b/engines/sci/graphics/cache.cpp
@@ -102,7 +102,7 @@ int16 GfxCache::kernelViewGetLoopCount(GuiResourceId viewId) {
}
int16 GfxCache::kernelViewGetCelCount(GuiResourceId viewId, int16 loopNo) {
- return getView(viewId)->getLoopInfo(loopNo)->celCount;
+ return getView(viewId)->getCelCount(loopNo);
}
} // End of namespace Sci
diff --git a/engines/sci/graphics/cache.h b/engines/sci/graphics/cache.h
index 16ab1916d4..2e9a345230 100644
--- a/engines/sci/graphics/cache.h
+++ b/engines/sci/graphics/cache.h
@@ -26,8 +26,6 @@
#ifndef SCI_GRAPHICS_CACHE_H
#define SCI_GRAPHICS_CACHE_H
-#include "sci/graphics/gui.h"
-
#include "common/hashmap.h"
namespace Sci {
diff --git a/engines/sci/graphics/compare.cpp b/engines/sci/graphics/compare.cpp
index 3102edc2fa..31c2b210ce 100644
--- a/engines/sci/graphics/compare.cpp
+++ b/engines/sci/graphics/compare.cpp
@@ -28,6 +28,7 @@
#include "graphics/primitives.h"
#include "sci/sci.h"
+#include "sci/engine/kernel.h"
#include "sci/engine/state.h"
#include "sci/engine/selector.h"
#include "sci/graphics/compare.h"
@@ -126,14 +127,22 @@ void GfxCompare::kernelSetNowSeen(reg_t objectReference) {
int16 x = (int16)readSelectorValue(_segMan, objectReference, SELECTOR(x));
int16 y = (int16)readSelectorValue(_segMan, objectReference, SELECTOR(y));
int16 z = 0;
- if (_kernel->_selectorCache.z > -1)
+ if (SELECTOR(z) > -1)
z = (int16)readSelectorValue(_segMan, objectReference, SELECTOR(z));
- // now get cel rectangle
view = _cache->getView(viewId);
- view->getCelRect(loopNo, celNo, x, y, z, &celRect);
- if (lookupSelector(_segMan, objectReference, _kernel->_selectorCache.nsTop, NULL, NULL) == kSelectorVariable) {
+ if (view->isSci2Hires())
+ _screen->adjustToUpscaledCoordinates(y, x);
+
+ view->getCelRect(loopNo, celNo, x, y, z, celRect);
+
+ if (view->isSci2Hires()) {
+ _screen->adjustBackUpscaledCoordinates(celRect.top, celRect.left);
+ _screen->adjustBackUpscaledCoordinates(celRect.bottom, celRect.right);
+ }
+
+ if (lookupSelector(_segMan, objectReference, SELECTOR(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);
@@ -174,19 +183,19 @@ bool GfxCompare::kernelCanBeHere(reg_t curObject, reg_t listReference) {
bool GfxCompare::kernelIsItSkip(GuiResourceId viewId, int16 loopNo, int16 celNo, Common::Point position) {
GfxView *tmpView = _cache->getView(viewId);
- CelInfo *celInfo = tmpView->getCelInfo(loopNo, celNo);
+ const CelInfo *celInfo = tmpView->getCelInfo(loopNo, celNo);
position.x = CLIP<int>(position.x, 0, celInfo->width - 1);
position.y = CLIP<int>(position.y, 0, celInfo->height - 1);
- byte *celData = tmpView->getBitmap(loopNo, celNo);
+ const byte *celData = tmpView->getBitmap(loopNo, celNo);
bool result = (celData[position.y * celInfo->width + position.x] == celInfo->clearKey);
return result;
}
void GfxCompare::kernelBaseSetter(reg_t object) {
- if (lookupSelector(_segMan, object, _kernel->_selectorCache.brLeft, NULL, NULL) == kSelectorVariable) {
+ if (lookupSelector(_segMan, object, SELECTOR(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 z = (SELECTOR(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));
@@ -199,7 +208,16 @@ void GfxCompare::kernelBaseSetter(reg_t object) {
GfxView *tmpView = _cache->getView(viewId);
Common::Rect celRect;
- tmpView->getCelRect(loopNo, celNo, x, y, z, &celRect);
+ if (tmpView->isSci2Hires())
+ _screen->adjustToUpscaledCoordinates(y, x);
+
+ tmpView->getCelRect(loopNo, celNo, x, y, z, celRect);
+
+ if (tmpView->isSci2Hires()) {
+ _screen->adjustBackUpscaledCoordinates(celRect.top, celRect.left);
+ _screen->adjustBackUpscaledCoordinates(celRect.bottom, celRect.right);
+ }
+
celRect.bottom = y + 1;
celRect.top = celRect.bottom - yStep;
diff --git a/engines/sci/graphics/compare.h b/engines/sci/graphics/compare.h
index 348d5ef723..be461cdc5b 100644
--- a/engines/sci/graphics/compare.h
+++ b/engines/sci/graphics/compare.h
@@ -26,8 +26,6 @@
#ifndef SCI_GRAPHICS_GFX_H
#define SCI_GRAPHICS_GFX_H
-#include "sci/graphics/gui.h"
-
#include "common/hashmap.h"
namespace Sci {
diff --git a/engines/sci/graphics/controls.cpp b/engines/sci/graphics/controls.cpp
index 26af9741c2..ff5a91eed4 100644
--- a/engines/sci/graphics/controls.cpp
+++ b/engines/sci/graphics/controls.cpp
@@ -30,6 +30,7 @@
#include "sci/sci.h"
#include "sci/event.h"
+#include "sci/engine/kernel.h"
#include "sci/engine/state.h"
#include "sci/engine/selector.h"
#include "sci/graphics/ports.h"
diff --git a/engines/sci/graphics/coordadjuster.cpp b/engines/sci/graphics/coordadjuster.cpp
index 422df52f27..9481a68f13 100644
--- a/engines/sci/graphics/coordadjuster.cpp
+++ b/engines/sci/graphics/coordadjuster.cpp
@@ -26,6 +26,7 @@
#include "common/util.h"
#include "sci/sci.h"
+#include "sci/engine/kernel.h"
#include "sci/engine/state.h"
#include "sci/engine/selector.h"
#include "sci/graphics/coordadjuster.h"
diff --git a/engines/sci/graphics/cursor.cpp b/engines/sci/graphics/cursor.cpp
index 2f8393f9ac..e1c05c97da 100644
--- a/engines/sci/graphics/cursor.cpp
+++ b/engines/sci/graphics/cursor.cpp
@@ -43,18 +43,18 @@ GfxCursor::GfxCursor(ResourceManager *resMan, GfxPalette *palette, GfxScreen *sc
: _resMan(resMan), _palette(palette), _screen(screen) {
_upscaledHires = _screen->getUpscaledHires();
+ _isVisible = true;
+
// center mouse cursor
setPosition(Common::Point(_screen->getWidth() / 2, _screen->getHeight() / 2));
kernelSetMoveZone(Common::Rect(0, 0, _screen->getDisplayWidth(), _screen->getDisplayHeight()));
-
- _isVisible = true;
}
GfxCursor::~GfxCursor() {
purgeCache();
}
-void GfxCursor::init(GfxCoordAdjuster *coordAdjuster, SciEvent *event) {
+void GfxCursor::init(GfxCoordAdjuster *coordAdjuster, EventManager *event) {
_coordAdjuster = coordAdjuster;
_event = event;
}
@@ -120,7 +120,7 @@ void GfxCursor::kernelSetShape(GuiResourceId resourceId) {
colorMapping[0] = 0; // Black is hardcoded
colorMapping[1] = _screen->getColorWhite(); // White is also hardcoded
colorMapping[2] = SCI_CURSOR_SCI0_TRANSPARENCYCOLOR;
- colorMapping[3] = _palette->matchColor(&_palette->_sysPalette, 170, 170, 170); // Grey
+ colorMapping[3] = _palette->matchColor(170, 170, 170); // Grey
// Seek to actual data
resourceData += 4;
@@ -164,42 +164,39 @@ void GfxCursor::kernelSetView(GuiResourceId viewNum, int loopNum, int celNum, Co
GfxView *cursorView = _cachedCursors[viewNum];
- CelInfo *celInfo = cursorView->getCelInfo(loopNum, celNum);
+ const CelInfo *celInfo = cursorView->getCelInfo(loopNum, celNum);
int16 width = celInfo->width;
int16 height = celInfo->height;
byte clearKey = celInfo->clearKey;
Common::Point *cursorHotspot = hotspot;
- byte *cursorBitmap;
if (!cursorHotspot)
// Compute hotspot from xoffset/yoffset
cursorHotspot = new Common::Point((celInfo->width >> 1) - celInfo->displaceX, celInfo->height - celInfo->displaceY - 1);
- // Eco Quest 1 uses a 1x1 transparent cursor to hide the cursor from the user. Some scalers don't seem to support this
+ // Eco Quest 1 uses a 1x1 transparent cursor to hide the cursor from the
+ // user. Some scalers don't seem to support this
if (width < 2 || height < 2) {
kernelHide();
delete cursorHotspot;
return;
}
- celInfo->rawBitmap = cursorView->getBitmap(loopNum, celNum);
+ const byte *rawBitmap = cursorView->getBitmap(loopNum, celNum);
if (_upscaledHires) {
// Scale cursor by 2x - note: sierra didn't do this, but it looks much better
width *= 2;
height *= 2;
cursorHotspot->x *= 2;
cursorHotspot->y *= 2;
- cursorBitmap = new byte[width * height];
- _screen->scale2x(celInfo->rawBitmap, cursorBitmap, celInfo->width, celInfo->height);
+ byte *cursorBitmap = new byte[width * height];
+ _screen->scale2x(rawBitmap, cursorBitmap, celInfo->width, celInfo->height);
+ CursorMan.replaceCursor(cursorBitmap, width, height, cursorHotspot->x, cursorHotspot->y, clearKey);
+ delete[] cursorBitmap;
} else {
- cursorBitmap = celInfo->rawBitmap;
+ CursorMan.replaceCursor(rawBitmap, width, height, cursorHotspot->x, cursorHotspot->y, clearKey);
}
- CursorMan.replaceCursor(cursorBitmap, width, height, cursorHotspot->x, cursorHotspot->y, clearKey);
-
- if (_upscaledHires)
- delete[] cursorBitmap;
-
kernelShow();
delete cursorHotspot;
@@ -209,7 +206,7 @@ void GfxCursor::kernelSetMacCursor(GuiResourceId viewNum, int loopNum, int celNu
// See http://developer.apple.com/legacy/mac/library/documentation/mac/QuickDraw/QuickDraw-402.html
// for more information.
- // View 998 seems to be a fake resource used to call for for the Mac CURS resources
+ // View 998 seems to be a fake resource used to call for the Mac CURS resources.
// For other resources, they're still in the views, so use them.
if (viewNum != 998) {
kernelSetView(viewNum, loopNum, celNum, hotspot);
@@ -219,6 +216,7 @@ void GfxCursor::kernelSetMacCursor(GuiResourceId viewNum, int loopNum, int celNu
// TODO: What about the 2000 resources? Inventory items? How to handle?
// TODO: What games does this work for? At least it does for KQ6.
// TODO: Stop asking rhetorical questions.
+ // TODO: It was fred all along!
Resource *resource = _resMan->findResource(ResourceId(kResourceTypeCursor, 1000 + celNum), false);
@@ -258,6 +256,15 @@ void GfxCursor::kernelSetMacCursor(GuiResourceId viewNum, int loopNum, int celNu
}
void GfxCursor::setPosition(Common::Point pos) {
+ // Don't set position, when cursor is not visible.
+ // This fixes eco quest 1 (floppy) right at the start, which is setting
+ // mouse cursor to (0,0) all the time during the intro. It's escapeable
+ // (now) by moving to the left or top, but it's getting on your nerves. This
+ // could theoretically break some things, although sierra normally sets
+ // position only when showing the cursor.
+ if (!_isVisible)
+ return;
+
if (!_upscaledHires) {
g_system->warpMouse(pos.x, pos.y);
} else {
@@ -269,21 +276,8 @@ void GfxCursor::setPosition(Common::Point pos) {
Common::Point GfxCursor::getPosition() {
Common::Point mousePos = g_system->getEventManager()->getMousePos();
- switch (_upscaledHires) {
- case GFX_SCREEN_UPSCALED_640x400:
- mousePos.x /= 2;
- mousePos.y /= 2;
- break;
- case GFX_SCREEN_UPSCALED_640x440:
- mousePos.x /= 2;
- mousePos.y = (mousePos.y * 5) / 11;
- break;
- case GFX_SCREEN_UPSCALED_640x480:
- mousePos.x /= 2;
- mousePos.y = (mousePos.y * 5) / 12;
- default:
- break;
- }
+ if (_upscaledHires)
+ _screen->adjustBackUpscaledCoordinates(mousePos.y, mousePos.x);
return mousePos;
}
@@ -333,7 +327,7 @@ void GfxCursor::kernelMoveCursor(Common::Point pos) {
// Trigger event reading to make sure the mouse coordinates will
// actually have changed the next time we read them.
- _event->get(SCI_EVENT_PEEK);
+ _event->getSciEvent(SCI_EVENT_PEEK);
}
} // End of namespace Sci
diff --git a/engines/sci/graphics/cursor.h b/engines/sci/graphics/cursor.h
index 6d92b3cf5f..7acd14acd9 100644
--- a/engines/sci/graphics/cursor.h
+++ b/engines/sci/graphics/cursor.h
@@ -45,7 +45,7 @@ public:
GfxCursor(ResourceManager *resMan, GfxPalette *palette, GfxScreen *screen);
~GfxCursor();
- void init(GfxCoordAdjuster *coordAdjuster, SciEvent *event);
+ void init(GfxCoordAdjuster *coordAdjuster, EventManager *event);
void kernelShow();
void kernelHide();
@@ -74,7 +74,7 @@ private:
GfxScreen *_screen;
GfxPalette *_palette;
GfxCoordAdjuster *_coordAdjuster;
- SciEvent *_event;
+ EventManager *_event;
int _upscaledHires;
diff --git a/engines/sci/graphics/font.cpp b/engines/sci/graphics/font.cpp
index 91cf01c912..852771d081 100644
--- a/engines/sci/graphics/font.cpp
+++ b/engines/sci/graphics/font.cpp
@@ -82,17 +82,18 @@ void GfxFontFromResource::draw(uint16 chr, int16 top, int16 left, byte color, bo
int charWidth = MIN<int>(getCharWidth(chr), _screen->getWidth() - left);
int charHeight = MIN<int>(getCharHeight(chr), _screen->getHeight() - top);
byte b = 0, mask = 0xFF;
- int y = top;
+ int y = 0;
+ int16 greyedTop = top;
byte *pIn = getCharData(chr);
for (int i = 0; i < charHeight; i++, y++) {
if (greyedOutput)
- mask = top++ % 2 ? 0xAA : 0x55;
+ mask = greyedTop++ % 2 ? 0xAA : 0x55;
for (int done = 0; done < charWidth; done++) {
if ((done & 7) == 0) // fetching next data byte
b = *(pIn++) & mask;
if (b & 0x80) // if MSB is set - paint it
- _screen->putPixel(left + done, y, 1, color, 0, 0);
+ _screen->putFontPixel(top, left + done, y, color);
b = b << 1;
}
}
diff --git a/engines/sci/graphics/fontsjis.h b/engines/sci/graphics/fontsjis.h
index 24c8423ddb..684e6cac5e 100644
--- a/engines/sci/graphics/fontsjis.h
+++ b/engines/sci/graphics/fontsjis.h
@@ -26,10 +26,12 @@
#ifndef SCI_GRAPHICS_FONTSJIS_H
#define SCI_GRAPHICS_FONTSJIS_H
-#include "graphics/sjis.h"
-
#include "sci/graphics/helpers.h"
+namespace Graphics {
+ class FontSJIS;
+}
+
namespace Sci {
/**
diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp
index 3cc5ca5447..9ce4474ee3 100644
--- a/engines/sci/graphics/frameout.cpp
+++ b/engines/sci/graphics/frameout.cpp
@@ -28,6 +28,7 @@
#include "graphics/primitives.h"
#include "sci/sci.h"
+#include "sci/engine/kernel.h"
#include "sci/engine/state.h"
#include "sci/engine/selector.h"
#include "sci/engine/vm.h"
@@ -37,6 +38,7 @@
#include "sci/graphics/view.h"
#include "sci/graphics/screen.h"
#include "sci/graphics/paint32.h"
+#include "sci/graphics/palette.h"
#include "sci/graphics/picture.h"
#include "sci/graphics/frameout.h"
@@ -46,7 +48,6 @@ GfxFrameout::GfxFrameout(SegManager *segMan, ResourceManager *resMan, GfxCoordAd
: _segMan(segMan), _resMan(resMan), _cache(cache), _screen(screen), _palette(palette), _paint32(paint32) {
_coordAdjuster = (GfxCoordAdjuster32 *)coordAdjuster;
- _highPlanePri = 0;
}
GfxFrameout::~GfxFrameout() {
@@ -54,30 +55,20 @@ GfxFrameout::~GfxFrameout() {
void GfxFrameout::kernelAddPlane(reg_t object) {
_planes.push_back(object);
- int16 planePri = readSelectorValue(_segMan, object, SELECTOR(priority)) & 0xFFFF;
- if (planePri > _highPlanePri)
- _highPlanePri = planePri;
+ sortPlanes();
}
void GfxFrameout::kernelUpdatePlane(reg_t object) {
+ sortPlanes();
}
void GfxFrameout::kernelDeletePlane(reg_t object) {
- for (uint32 planeNr = 0; planeNr < _planes.size(); planeNr++) {
- if (_planes[planeNr] == object) {
- _planes.remove_at(planeNr);
- break;
+ for (Common::List<reg_t>::iterator it = _planes.begin(); it != _planes.end(); it++) {
+ if (object == *it) {
+ _planes.erase(it);
+ return;
}
}
-
- // Recalculate highPlanePri
- _highPlanePri = 0;
-
- for (uint32 planeNr = 0; planeNr < _planes.size(); planeNr++) {
- int16 planePri = readSelectorValue(_segMan, _planes[planeNr], SELECTOR(priority)) & 0xFFFF;
- if (planePri > _highPlanePri)
- _highPlanePri = planePri;
- }
}
void GfxFrameout::kernelAddScreenItem(reg_t object) {
@@ -95,73 +86,102 @@ void GfxFrameout::kernelDeleteScreenItem(reg_t object) {
}
int16 GfxFrameout::kernelGetHighPlanePri() {
- return _highPlanePri;
+ sortPlanes();
+ return readSelectorValue(g_sci->getEngineState()->_segMan, _planes.back(), SELECTOR(priority));
}
bool sortHelper(const FrameoutEntry* entry1, const FrameoutEntry* entry2) {
return (entry1->priority == entry2->priority) ? (entry1->y < entry2->y) : (entry1->priority < entry2->priority);
}
+bool planeSortHelper(const reg_t entry1, const reg_t entry2) {
+ SegManager *segMan = g_sci->getEngineState()->_segMan;
+
+ uint16 plane1Priority = readSelectorValue(segMan, entry1, SELECTOR(priority));
+ uint16 plane2Priority = readSelectorValue(segMan, entry2, SELECTOR(priority));
+
+ if (plane1Priority == 0xffff)
+ return true;
+
+ if (plane2Priority == 0xffff)
+ return false;
+
+ return plane1Priority < plane2Priority;
+}
+
+void GfxFrameout::sortPlanes() {
+ // First, remove any invalid planes
+ for (Common::List<reg_t>::iterator it = _planes.begin(); it != _planes.end();) {
+ if (!_segMan->isObject(*it))
+ it = _planes.erase(it);
+ else
+ it++;
+ }
+
+ // Sort the rest of them
+ Common::sort(_planes.begin(), _planes.end(), planeSortHelper);
+}
+
void GfxFrameout::kernelFrameout() {
- int16 itemCount = 0;
- reg_t planeObject;
- GuiResourceId planePictureNr;
- GfxPicture *planePicture = 0;
- int16 planePictureCels = 0;
- int16 planePictureCel;
- int16 planePriority;
- Common::Rect planeRect;
- int16 planeResY, planeResX;
- byte planeBack;
-
- reg_t itemObject;
- reg_t itemPlane;
-
- FrameoutEntry *itemData;
- FrameoutList itemList;
- FrameoutEntry *itemEntry;
+ _palette->palVaryUpdate();
// Allocate enough space for all screen items
- itemData = (FrameoutEntry *)malloc(_screenItems.size() * sizeof(FrameoutEntry));
+ FrameoutEntry *itemData = (FrameoutEntry *)malloc(_screenItems.size() * sizeof(FrameoutEntry));
+
+ const SciGameId gameId = g_sci->getGameId();
- for (uint32 planeNr = 0; planeNr < _planes.size(); planeNr++) {
- planeObject = _planes[planeNr];
- planePriority = readSelectorValue(_segMan, planeObject, SELECTOR(priority));
+ for (Common::List<reg_t>::iterator it = _planes.begin(); it != _planes.end(); it++) {
+ reg_t planeObject = *it;
+ uint16 planePriority = readSelectorValue(_segMan, planeObject, SELECTOR(priority));
- if (planePriority == -1) // Plane currently not meant to be shown
+ if (planePriority == 0xffff) // Plane currently not meant to be shown
continue;
+ Common::Rect planeRect;
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));
+ int16 planeResY = readSelectorValue(_segMan, planeObject, SELECTOR(resY));
+ int16 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 = readSelectorValue(_segMan, planeObject, SELECTOR(back));
- if (planeBack) {
+ byte planeBack = readSelectorValue(_segMan, planeObject, SELECTOR(back));
+ if (planeBack)
_paint32->fillRect(planeRect, planeBack);
- }
- planePictureNr = readSelectorValue(_segMan, planeObject, SELECTOR(picture));
+ GuiResourceId planePictureNr = readSelectorValue(_segMan, planeObject, SELECTOR(picture));
+ GfxPicture *planePicture = 0;
+ int16 planePictureCels = 0;
+
if ((planePictureNr != 0xFFFF) && (planePictureNr != 0xFFFE)) {
planePicture = new GfxPicture(_resMan, _coordAdjuster, 0, _screen, _palette, planePictureNr, false);
planePictureCels = planePicture->getSci32celCount();
_coordAdjuster->pictureSetDisplayArea(planeRect);
+ _palette->drewPicture(planePictureNr);
}
// Fill our itemlist for this plane
- itemCount = 0;
- itemEntry = itemData;
+ int16 itemCount = 0;
+ FrameoutEntry *itemEntry = itemData;
+ FrameoutList itemList;
+
for (uint32 itemNr = 0; itemNr < _screenItems.size(); itemNr++) {
- itemObject = _screenItems[itemNr];
- itemPlane = readSelector(_segMan, itemObject, SELECTOR(plane));
+ reg_t itemObject = _screenItems[itemNr];
+
+ // Remove any invalid items
+ if (!_segMan->isObject(itemObject)) {
+ _screenItems.remove_at(itemNr);
+ itemNr--;
+ continue;
+ }
+
+ reg_t itemPlane = readSelector(_segMan, itemObject, SELECTOR(plane));
if (planeObject == itemPlane) {
// Found an item on current plane
itemEntry->viewId = readSelectorValue(_segMan, itemObject, SELECTOR(view));
@@ -171,6 +191,14 @@ void GfxFrameout::kernelFrameout() {
itemEntry->y = readSelectorValue(_segMan, itemObject, SELECTOR(y));
itemEntry->z = readSelectorValue(_segMan, itemObject, SELECTOR(z));
itemEntry->priority = readSelectorValue(_segMan, itemObject, SELECTOR(priority));
+ if (gameId == GID_GK1) {
+ if ((itemEntry->viewId == 11000) && (itemEntry->loopNo == 0) && (itemEntry->celNo == 0) && (itemEntry->priority == 1)) {
+ itemEntry->priority = 0; // HACK for gk1 hires main menu
+ }
+ if ((itemEntry->viewId == 10100) && (itemEntry->priority == 0)) {
+ itemEntry->priority = 1; // HACK for gk1 hires main menu
+ }
+ }
itemEntry->signal = readSelectorValue(_segMan, itemObject, SELECTOR(signal));
itemEntry->scaleX = readSelectorValue(_segMan, itemObject, SELECTOR(scaleX));
itemEntry->scaleY = readSelectorValue(_segMan, itemObject, SELECTOR(scaleY));
@@ -181,8 +209,9 @@ void GfxFrameout::kernelFrameout() {
itemEntry->y += planeRect.top;
itemEntry->x += planeRect.left;
- if (itemEntry->priority == 0)
- itemEntry->priority = itemEntry->y;
+ if (!(itemEntry->signal & 0x0010)) { // kSignalFixedPriority
+ // TODO: Change priority of this item
+ }
itemList.push_back(itemEntry);
itemEntry++;
@@ -194,12 +223,10 @@ void GfxFrameout::kernelFrameout() {
Common::sort(itemList.begin(), itemList.end(), sortHelper);
// Now display itemlist
- planePictureCel = 0;
-
+ int16 planePictureCel = 0;
itemEntry = itemData;
- FrameoutList::iterator listIterator = itemList.begin();
- FrameoutList::iterator listEnd = itemList.end();
- while (listIterator != listEnd) {
+
+ for (FrameoutList::iterator listIterator = itemList.begin(); listIterator != itemList.end(); listIterator++) {
itemEntry = *listIterator;
if (planePicture) {
while ((planePictureCel <= itemEntry->priority) && (planePictureCel < planePictureCels)) {
@@ -207,63 +234,72 @@ void GfxFrameout::kernelFrameout() {
planePictureCel++;
}
}
+
if (itemEntry->viewId != 0xFFFF) {
GfxView *view = _cache->getView(itemEntry->viewId);
- if ((itemEntry->scaleX == 128) && (itemEntry->scaleY == 128)) {
- view->getCelRect(itemEntry->loopNo, itemEntry->celNo, itemEntry->x, itemEntry->y, itemEntry->z, &itemEntry->celRect);
- } else
- view->getCelScaledRect(itemEntry->loopNo, itemEntry->celNo, itemEntry->x, itemEntry->y, itemEntry->z, itemEntry->scaleX, itemEntry->scaleY, &itemEntry->celRect);
+ if (view->isSci2Hires())
+ _screen->adjustToUpscaledCoordinates(itemEntry->y, itemEntry->x);
- if (itemEntry->celRect.top < 0 || itemEntry->celRect.top >= _screen->getHeight()) {
- listIterator++;
- continue;
+ if ((itemEntry->scaleX == 128) && (itemEntry->scaleY == 128))
+ view->getCelRect(itemEntry->loopNo, itemEntry->celNo, itemEntry->x, itemEntry->y, itemEntry->z, itemEntry->celRect);
+ else
+ view->getCelScaledRect(itemEntry->loopNo, itemEntry->celNo, itemEntry->x, itemEntry->y, itemEntry->z, itemEntry->scaleX, itemEntry->scaleY, itemEntry->celRect);
+
+ int16 screenHeight = _screen->getHeight();
+ int16 screenWidth = _screen->getWidth();
+ if (view->isSci2Hires()) {
+ screenHeight = _screen->getDisplayHeight();
+ screenWidth = _screen->getDisplayWidth();
}
- if (itemEntry->celRect.left < 0 || itemEntry->celRect.left >= _screen->getWidth()) {
- listIterator++;
+ if (itemEntry->celRect.top < 0 || itemEntry->celRect.top >= screenHeight)
+ continue;
+
+ if (itemEntry->celRect.left < 0 || itemEntry->celRect.left >= screenWidth)
continue;
- }
Common::Rect clipRect;
clipRect = itemEntry->celRect;
- clipRect.clip(planeRect);
+ if (view->isSci2Hires()) {
+ Common::Rect upscaledPlaneRect = planeRect;
+ _screen->adjustToUpscaledCoordinates(upscaledPlaneRect.top, upscaledPlaneRect.left);
+ _screen->adjustToUpscaledCoordinates(upscaledPlaneRect.bottom, upscaledPlaneRect.right);
+ clipRect.clip(upscaledPlaneRect);
+ } else {
+ clipRect.clip(planeRect);
+ }
if ((itemEntry->scaleX == 128) && (itemEntry->scaleY == 128))
- view->draw(itemEntry->celRect, clipRect, clipRect, itemEntry->loopNo, itemEntry->celNo, 255, 0, false);
+ view->draw(itemEntry->celRect, clipRect, clipRect, itemEntry->loopNo, itemEntry->celNo, 255, 0, view->isSci2Hires());
else
view->drawScaled(itemEntry->celRect, clipRect, clipRect, itemEntry->loopNo, itemEntry->celNo, 255, itemEntry->scaleX, itemEntry->scaleY);
} else {
// Most likely a text entry
// This draws text the "SCI0-SCI11" way. In SCI2, text is prerendered in kCreateTextBitmap
// TODO: rewrite this the "SCI2" way (i.e. implement the text buffer to draw inside kCreateTextBitmap)
- // This doesn't work for SCI2.1 games...
- if (getSciVersion() == SCI_VERSION_2) {
- Kernel *kernel = g_sci->getKernel();
- 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 = 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++) {
- // TODO: proper text splitting... this is a hack
- if ((text[i] == ' ' && i > 0 && text[i - i] == ' ') || text[i] == '\n' ||
- (curX + font->getCharWidth(text[i]) > _screen->getWidth())) {
- curY += font->getHeight();
- curX = itemEntry->x;
- }
- font->draw(text[i], curY, curX, foreColor, dimmed);
- curX += font->getCharWidth(text[i]);
+ if (lookupSelector(_segMan, itemEntry->object, SELECTOR(text), NULL, NULL) == kSelectorVariable) {
+ Common::String text = _segMan->getString(readSelector(_segMan, itemEntry->object, SELECTOR(text)));
+ GfxFont *font = _cache->getFont(readSelectorValue(_segMan, itemEntry->object, SELECTOR(font)));
+ 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++) {
+ unsigned char curChar = text[i];
+ // TODO: proper text splitting... this is a hack
+ if ((curChar == ' ' && i > 0 && text[i - i] == ' ') || curChar == '\n' ||
+ (curX + font->getCharWidth(curChar) > _screen->getWidth())) {
+ curY += font->getHeight();
+ curX = itemEntry->x;
}
- delete font;
+ font->draw(curChar, curY, curX, foreColor, dimmed);
+ curX += font->getCharWidth(curChar);
}
}
}
- listIterator++;
}
+
if (planePicture) {
while (planePictureCel < planePictureCels) {
planePicture->drawSci32Vga(planePictureCel);
@@ -273,6 +309,7 @@ void GfxFrameout::kernelFrameout() {
planePicture = 0;
}
}
+
free(itemData);
_screen->copyToScreen();
}
diff --git a/engines/sci/graphics/frameout.h b/engines/sci/graphics/frameout.h
index 36c02af278..e4568ad602 100644
--- a/engines/sci/graphics/frameout.h
+++ b/engines/sci/graphics/frameout.h
@@ -41,10 +41,14 @@ struct FrameoutEntry {
int16 scaleY;
Common::Rect celRect;
};
+
typedef Common::List<FrameoutEntry *> FrameoutList;
class GfxCache;
+class GfxCoordAdjuster32;
class GfxPaint32;
+class GfxPalette;
+class GfxScreen;
/**
* Frameout class, kFrameout and relevant functions for SCI32 games
*/
@@ -71,8 +75,9 @@ private:
GfxPaint32 *_paint32;
Common::Array<reg_t> _screenItems;
- Common::Array<reg_t> _planes;
- int16 _highPlanePri;
+ Common::List<reg_t> _planes;
+
+ void sortPlanes();
};
} // End of namespace Sci
diff --git a/engines/sci/graphics/gui.cpp b/engines/sci/graphics/gui.cpp
deleted file mode 100644
index e427edd732..0000000000
--- a/engines/sci/graphics/gui.cpp
+++ /dev/null
@@ -1,139 +0,0 @@
-/* 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/timer.h"
-#include "common/util.h"
-
-#include "sci/sci.h"
-#include "sci/debug.h" // for g_debug_sleeptime_factor
-#include "sci/event.h"
-#include "sci/engine/state.h"
-#include "sci/engine/selector.h"
-#include "sci/graphics/gui.h"
-#include "sci/graphics/screen.h"
-#include "sci/graphics/palette.h"
-#include "sci/graphics/cursor.h"
-#include "sci/graphics/ports.h"
-#include "sci/graphics/paint16.h"
-#include "sci/graphics/cache.h"
-#include "sci/graphics/compare.h"
-#include "sci/graphics/coordadjuster.h"
-#include "sci/graphics/animate.h"
-#include "sci/graphics/controls.h"
-#include "sci/graphics/menu.h"
-#include "sci/graphics/portrait.h"
-#include "sci/graphics/text16.h"
-#include "sci/graphics/transitions.h"
-#include "sci/graphics/view.h"
-#include "sci/sound/audio.h"
-
-namespace Sci {
-
-SciGui::SciGui(EngineState *state, GfxScreen *screen, GfxPalette *palette, GfxCache *cache, GfxCursor *cursor, GfxPorts *ports, AudioPlayer *audio)
- : _s(state), _screen(screen), _palette(palette), _cache(cache), _cursor(cursor), _ports(ports), _audio(audio) {
-
- // FIXME/TODO: If SciGui inits all the stuff below, then it should *own* it,
- // not SciEngine. Conversely, if we want SciEngine to own this stuff,
- // then it should init it!
- _coordAdjuster = new GfxCoordAdjuster16(_ports);
- g_sci->_gfxCoordAdjuster = _coordAdjuster;
- _cursor->init(_coordAdjuster, _s->_event);
- _compare = new GfxCompare(_s->_segMan, g_sci->getKernel(), _cache, _screen, _coordAdjuster);
- g_sci->_gfxCompare = _compare;
- _transitions = new GfxTransitions(this, _screen, _palette, g_sci->getResMan()->isVGA());
- _paint16 = new GfxPaint16(g_sci->getResMan(), _s->_segMan, g_sci->getKernel(), this, _cache, _ports, _coordAdjuster, _screen, _palette, _transitions);
- g_sci->_gfxPaint = _paint16;
- g_sci->_gfxPaint16 = _paint16;
- _animate = new GfxAnimate(_s, _cache, _ports, _paint16, _screen, _palette, _cursor, _transitions);
- g_sci->_gfxAnimate = _animate;
- _text16 = new GfxText16(g_sci->getResMan(), _cache, _ports, _paint16, _screen);
- _controls = new GfxControls(_s->_segMan, _ports, _paint16, _text16, _screen);
- g_sci->_gfxControls = _controls;
- _menu = new GfxMenu(_s->_event, _s->_segMan, this, _ports, _paint16, _text16, _screen, _cursor);
- g_sci->_gfxMenu = _menu;
-}
-
-SciGui::~SciGui() {
- delete _menu;
- delete _controls;
- delete _text16;
- delete _animate;
- delete _paint16;
- delete _transitions;
- delete _compare;
- delete _coordAdjuster;
-}
-
-void SciGui::resetEngineState(EngineState *s) {
- _s = s;
- _animate->resetEngineState(s);
-}
-
-void SciGui::init(bool usesOldGfxFunctions) {
- _ports->init(usesOldGfxFunctions, this, _paint16, _text16);
- _paint16->init(_animate, _text16);
-}
-
-void SciGui::wait(int16 ticks) {
- _s->wait(ticks);
-}
-
-void SciGui::textSize(const char *text, int16 font, int16 maxWidth, int16 *textWidth, int16 *textHeight) {
- Common::Rect rect(0, 0, *textWidth, *textHeight);
- _text16->Size(rect, text, font, maxWidth);
- *textWidth = rect.width();
- *textHeight = rect.height();
-}
-
-// Used SCI1+ for text codes
-void SciGui::textFonts(int argc, reg_t *argv) {
- _text16->CodeSetFonts(argc, argv);
-}
-
-// Used SCI1+ for text codes
-void SciGui::textColors(int argc, reg_t *argv) {
- _text16->CodeSetColors(argc, argv);
-}
-
-reg_t SciGui::portraitLoad(Common::String resourceName) {
- //Portrait *myPortrait = new Portrait(g_sci->getResMan(), _screen, _palette, resourceName);
- return NULL_REG;
-}
-
-void SciGui::portraitShow(Common::String resourceName, Common::Point position, uint16 resourceId, uint16 noun, uint16 verb, uint16 cond, uint16 seq) {
- Portrait *myPortrait = new Portrait(g_sci->getResMan(), _s->_event, this, _screen, _palette, _audio, resourceName);
- // TODO: cache portraits
- // adjust given coordinates to curPort (but dont adjust coordinates on upscaledHires_Save_Box and give us hires coordinates
- // on kDrawCel, yeah this whole stuff makes sense)
- position.x += _ports->getPort()->left; position.y += _ports->getPort()->top;
- _screen->adjustToUpscaledCoordinates(position.y, position.x);
- myPortrait->doit(position, resourceId, noun, verb, cond, seq);
- delete myPortrait;
-}
-
-void SciGui::portraitUnload(uint16 portraitId) {
-}
-
-} // End of namespace Sci
diff --git a/engines/sci/graphics/gui.h b/engines/sci/graphics/gui.h
deleted file mode 100644
index 7663036117..0000000000
--- a/engines/sci/graphics/gui.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/* 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 SCI_GRAPHICS_GUI_H
-#define SCI_GRAPHICS_GUI_H
-
-#include "sci/graphics/helpers.h"
-
-namespace Sci {
-
-class GfxScreen;
-class GfxPalette;
-class GfxCursor;
-class GfxCache;
-class GfxCompare;
-class GfxCoordAdjuster16;
-class GfxPorts;
-class GfxPaint16;
-class GfxAnimate;
-class GfxControls;
-class GfxMenu;
-class GfxText16;
-class GfxTransitions;
-
-class SciGui {
-public:
- SciGui(EngineState *s, GfxScreen *screen, GfxPalette *palette, GfxCache *cache, GfxCursor *cursor, GfxPorts *ports, AudioPlayer *audio);
- virtual ~SciGui();
-
- virtual void init(bool usesOldGfxFunctions);
-
- virtual void wait(int16 ticks);
-
- virtual void textSize(const char *text, int16 font, int16 maxWidth, int16 *textWidth, int16 *textHeight);
- virtual void textFonts(int argc, reg_t *argv);
- virtual void textColors(int argc, reg_t *argv);
-
- virtual reg_t portraitLoad(Common::String resourceName);
- virtual void portraitShow(Common::String resourceName, Common::Point position, uint16 resourceNum, uint16 noun, uint16 verb, uint16 cond, uint16 seq);
- virtual void portraitUnload(uint16 portraitId);
-
- // FIXME: Don't store EngineState
- virtual void resetEngineState(EngineState *s);
-
-protected:
- GfxCursor *_cursor;
- EngineState *_s;
- GfxScreen *_screen;
- GfxPalette *_palette;
- GfxCache *_cache;
- GfxCoordAdjuster16 *_coordAdjuster;
- GfxCompare *_compare;
- GfxPorts *_ports;
- GfxPaint16 *_paint16;
-
-private:
- AudioPlayer *_audio;
- GfxAnimate *_animate;
- GfxControls *_controls;
- GfxMenu *_menu;
- GfxText16 *_text16;
- GfxTransitions *_transitions;
-
- bool _usesOldGfxFunctions;
-};
-
-} // End of namespace Sci
-
-#endif
diff --git a/engines/sci/graphics/gui32.cpp b/engines/sci/graphics/gui32.cpp
deleted file mode 100644
index 4b72050d0b..0000000000
--- a/engines/sci/graphics/gui32.cpp
+++ /dev/null
@@ -1,83 +0,0 @@
-/* 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/timer.h"
-#include "common/util.h"
-
-#include "sci/sci.h"
-#include "sci/event.h"
-#include "sci/engine/state.h"
-#include "sci/engine/selector.h"
-#include "sci/graphics/gui32.h"
-#include "sci/graphics/screen.h"
-#include "sci/graphics/palette.h"
-#include "sci/graphics/cursor.h"
-#include "sci/graphics/cache.h"
-#include "sci/graphics/compare.h"
-#include "sci/graphics/coordadjuster.h"
-#include "sci/graphics/frameout.h"
-#include "sci/graphics/paint32.h"
-#include "sci/graphics/picture.h"
-#include "sci/graphics/robot.h"
-#include "sci/graphics/view.h"
-
-namespace Sci {
-
-SciGui32::SciGui32(SegManager *segMan, SciEvent *event, GfxScreen *screen, GfxPalette *palette, GfxCache *cache, GfxCursor *cursor)
- : _screen(screen), _palette(palette), _cache(cache), _cursor(cursor) {
-
- _coordAdjuster = new GfxCoordAdjuster32(segMan);
- g_sci->_gfxCoordAdjuster = _coordAdjuster;
- _cursor->init(_coordAdjuster, event);
- _compare = new GfxCompare(segMan, g_sci->getKernel(), _cache, _screen, _coordAdjuster);
- g_sci->_gfxCompare = _compare;
- _paint32 = new GfxPaint32(g_sci->getResMan(), segMan, g_sci->getKernel(), _coordAdjuster, _cache, _screen, _palette);
- g_sci->_gfxPaint = _paint32;
- _frameout = new GfxFrameout(segMan, g_sci->getResMan(), _coordAdjuster, _cache, _screen, _palette, _paint32);
- g_sci->_gfxFrameout = _frameout;
-}
-
-SciGui32::~SciGui32() {
- delete _frameout;
- delete _paint32;
- delete _compare;
- delete _coordAdjuster;
-}
-
-void SciGui32::init() {
-}
-
-void SciGui32::textSize(const char *text, int16 font, int16 maxWidth, int16 *textWidth, int16 *textHeight) {
- *textWidth = 0;
- *textHeight = 0;
-}
-
-void SciGui32::drawRobot(GuiResourceId robotId) {
- Robot *test = new Robot(g_sci->getResMan(), _screen, robotId);
- test->draw();
- delete test;
-}
-
-} // End of namespace Sci
diff --git a/engines/sci/graphics/maciconbar.cpp b/engines/sci/graphics/maciconbar.cpp
index a06e98ccbf..6f2c9596db 100644
--- a/engines/sci/graphics/maciconbar.cpp
+++ b/engines/sci/graphics/maciconbar.cpp
@@ -24,6 +24,7 @@
*/
#include "sci/sci.h"
+#include "sci/engine/kernel.h"
#include "sci/engine/selector.h"
#include "sci/engine/state.h"
#include "sci/graphics/maciconbar.h"
diff --git a/engines/sci/graphics/menu.cpp b/engines/sci/graphics/menu.cpp
index 880e1aba12..bfe58e03d5 100644
--- a/engines/sci/graphics/menu.cpp
+++ b/engines/sci/graphics/menu.cpp
@@ -29,9 +29,9 @@
#include "sci/sci.h"
#include "sci/event.h"
+#include "sci/engine/kernel.h"
#include "sci/engine/state.h"
#include "sci/engine/selector.h"
-#include "sci/graphics/gui.h"
#include "sci/graphics/ports.h"
#include "sci/graphics/paint16.h"
#include "sci/graphics/animate.h"
@@ -43,8 +43,8 @@
namespace Sci {
-GfxMenu::GfxMenu(SciEvent *event, SegManager *segMan, SciGui *gui, GfxPorts *ports, GfxPaint16 *paint16, GfxText16 *text16, GfxScreen *screen, GfxCursor *cursor)
- : _event(event), _segMan(segMan), _gui(gui), _ports(ports), _paint16(paint16), _text16(text16), _screen(screen), _cursor(cursor) {
+GfxMenu::GfxMenu(EventManager *event, SegManager *segMan, GfxPorts *ports, GfxPaint16 *paint16, GfxText16 *text16, GfxScreen *screen, GfxCursor *cursor)
+ : _event(event), _segMan(segMan), _ports(ports), _paint16(paint16), _text16(text16), _screen(screen), _cursor(cursor) {
_menuSaveHandle = NULL_REG;
_barSaveHandle = NULL_REG;
@@ -63,8 +63,9 @@ void GfxMenu::reset() {
_itemList.clear();
_listCount = 0;
- // We actually set active item in here and remember last selection of the user
- // sierra sci always defaulted to first item every time menu was called via ESC, we dont follow that logic
+ // We actually set active item in here and remember last selection of the
+ // user. Sierra SCI always defaulted to first item every time menu was
+ // called via ESC, we don't follow that logic.
_curMenuId = 1;
_curItemId = 1;
}
@@ -92,15 +93,16 @@ void GfxMenu::kernelAddEntry(Common::String title, Common::String content, reg_t
beginPos = curPos;
- // Now go through the content till we find end-marker and collect data about it
- // ':' is an end-marker for each item
+ // Now go through the content till we find end-marker and collect data about it.
+ // ':' is an end-marker for each item.
tagPos = 0; rightAlignedPos = 0;
controlPos = 0; altPos = 0; functionPos = 0;
while ((curPos < contentSize) && (content[curPos] != ':')) {
switch (content[curPos]) {
case '=': // Set tag
- // Special case for normal animation speed - they use right aligned "=" for that one, so we ignore it
- // as being recognized as tag marker
+ // Special case for normal animation speed - they use right
+ // aligned "=" for that one, so we ignore it as being recognized
+ // as tag marker.
if (rightAlignedPos == curPos - 1)
break;
if (tagPos)
@@ -199,8 +201,9 @@ void GfxMenu::kernelAddEntry(Common::String title, Common::String content, reg_t
if (separatorCount == tempPos - beginPos) {
itemEntry->separatorLine = true;
} else {
- // we don't strSplit here, because multilingual SCI01 support language switching on the fly, so we have to do
- // this everytime the menu is called
+ // We don't strSplit here, because multilingual SCI01 support
+ // language switching on the fly, so we have to do this everytime
+ // the menu is called.
itemEntry->text = Common::String(content.c_str() + beginPos, tempPos - beginPos);
// LSL6 uses "Ctrl-" prefix string instead of ^ like all the other games do
@@ -222,10 +225,12 @@ void GfxMenu::kernelAddEntry(Common::String title, Common::String content, reg_t
if (tagPos && tagPos >= rightAlignedPos)
tempPos = tagPos;
itemEntry->textRightAligned = Common::String(content.c_str() + rightAlignedPos, tempPos - rightAlignedPos);
- // Remove ending space, if there is one. Strangely sometimes there are lone spaces at the end in some games
+ // Remove ending space, if there is one. Strangely sometimes there
+ // are lone spaces at the end in some games
if (itemEntry->textRightAligned.hasSuffix(" "))
itemEntry->textRightAligned.deleteLastChar();
- // - and + are used sometimes for volume control/animation speed, = sometimes for animation speed
+ // - and + are used sometimes for volume control/animation speed,
+ // = sometimes for animation speed
if (itemEntry->textRightAligned == "-") {
itemEntry->keyPress = '-';
} else if (itemEntry->textRightAligned == "+") {
@@ -377,7 +382,7 @@ void GfxMenu::calculateMenuAndItemWidth() {
}
}
-reg_t GfxMenu::kernelSelect(reg_t eventObject) {
+reg_t GfxMenu::kernelSelect(reg_t eventObject, bool pauseSound) {
int16 eventType = readSelectorValue(_segMan, eventObject, SELECTOR(type));
int16 keyPress, keyModifier;
Common::Point mousePosition;
@@ -392,7 +397,8 @@ reg_t GfxMenu::kernelSelect(reg_t eventObject) {
case SCI_EVENT_KEYBOARD:
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 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;
keyPress = 'i';
@@ -401,9 +407,9 @@ reg_t GfxMenu::kernelSelect(reg_t eventObject) {
case 0:
break;
case SCI_KEY_ESC:
- interactiveShowMouse();
+ interactiveStart(pauseSound);
itemEntry = interactiveWithKeyboard();
- interactiveRestoreMouse();
+ interactiveEnd(pauseSound);
forceClaimed = true;
break;
default:
@@ -439,9 +445,9 @@ reg_t GfxMenu::kernelSelect(reg_t eventObject) {
case SCI_EVENT_MOUSE_PRESS:
mousePosition = _cursor->getPosition();
if (mousePosition.y < 10) {
- interactiveShowMouse();
+ interactiveStart(pauseSound);
itemEntry = interactiveWithMouse();
- interactiveRestoreMouse();
+ interactiveEnd(pauseSound);
forceClaimed = true;
}
break;
@@ -562,7 +568,8 @@ void GfxMenu::drawMenu(uint16 oldMenuId, uint16 newMenuId) {
if (!maxTextRightAlignedWidth)
_menuRect.right -= 5;
- // if part of menu window is outside the screen, move it into the screen (this happens in multilingual sq3 and lsl3)
+ // If part of menu window is outside the screen, move it into the screen
+ // (this happens in multilingual sq3 and lsl3).
if (_menuRect.right > _screen->getWidth()) {
_menuRect.translate(-(_menuRect.right - _screen->getWidth()), 0);
}
@@ -625,12 +632,16 @@ void GfxMenu::invertMenuSelection(uint16 itemId) {
_paint16->bitsShow(itemRect);
}
-void GfxMenu::interactiveShowMouse() {
+void GfxMenu::interactiveStart(bool pauseSound) {
_mouseOldState = _cursor->isVisible();
_cursor->kernelShow();
+ if (pauseSound)
+ g_sci->_soundCmd->pauseAll(true);
}
-void GfxMenu::interactiveRestoreMouse() {
+void GfxMenu::interactiveEnd(bool pauseSound) {
+ if (pauseSound)
+ g_sci->_soundCmd->pauseAll(false);
if (!_mouseOldState)
_cursor->kernelHide();
}
@@ -682,18 +693,19 @@ uint16 GfxMenu::mouseFindMenuItemSelection(Common::Point mousePosition, uint16 m
}
GuiMenuItemEntry *GfxMenu::interactiveWithKeyboard() {
- sciEvent curEvent;
+ SciEvent curEvent;
uint16 newMenuId = _curMenuId;
uint16 newItemId = _curItemId;
GuiMenuItemEntry *curItemEntry = findItem(_curMenuId, _curItemId);
GuiMenuItemEntry *newItemEntry = curItemEntry;
Common::Point mousePosition;
- // We don't 100% follow sierra here: we select last item instead of selecting first item of first menu everytime
- // Also sierra sci didnt allow mouse interaction, when menu was activated via keyboard
+ // We don't 100% follow Sierra here: we select last item instead of
+ // selecting first item of first menu every time. Also sierra sci didn't
+ // allow mouse interaction, when menu was activated via keyboard.
- calculateMenuAndItemWidth();
_oldPort = _ports->setPort(_ports->_menuPort);
+ calculateMenuAndItemWidth();
_barSaveHandle = _paint16->bitsSave(_ports->_menuRect, GFX_SCREEN_MASK_VISUAL);
_ports->penColor(0);
@@ -706,12 +718,13 @@ GuiMenuItemEntry *GfxMenu::interactiveWithKeyboard() {
_paint16->bitsShow(_menuRect);
while (true) {
- curEvent = _event->get(SCI_EVENT_ANY);
+ curEvent = _event->getSciEvent(SCI_EVENT_ANY);
switch (curEvent.type) {
case SCI_EVENT_KEYBOARD:
- // We don't 100% follow sierra here: - sierra didn't wrap around when changing item id
- // - sierra allowed item id to be 0, which didnt make any sense
+ // We don't 100% follow sierra here:
+ // - sierra didn't wrap around when changing item id
+ // - sierra allowed item id to be 0, which didn't make any sense
do {
switch (curEvent.data) {
case SCI_KEY_ESC:
@@ -796,25 +809,26 @@ GuiMenuItemEntry *GfxMenu::interactiveWithKeyboard() {
}
break;
case SCI_EVENT_NONE:
- _event->sleep(2500 / 1000);
+ g_sci->sleep(2500 / 1000);
break;
}
}
}
-// Mouse button is currently pressed - we are now interpreting mouse coordinates till mouse button is released
-// The menu item that is selected at that time is chosen. If no menu item is selected we cancel
-// No keyboard interaction is allowed, cause that wouldnt make any sense at all
+// Mouse button is currently pressed - we are now interpreting mouse coordinates
+// till mouse button is released. The menu item that is selected at that time is
+// chosen. If no menu item is selected we cancel. No keyboard interaction is
+// allowed, cause that wouldnt make any sense at all.
GuiMenuItemEntry *GfxMenu::interactiveWithMouse() {
- sciEvent curEvent;
+ SciEvent curEvent;
uint16 newMenuId = 0, newItemId = 0;
uint16 curMenuId = 0, curItemId = 0;
Common::Point mousePosition = _cursor->getPosition();
bool firstMenuChange = true;
GuiMenuItemEntry *curItemEntry = NULL;
- calculateMenuAndItemWidth();
_oldPort = _ports->setPort(_ports->_menuPort);
+ calculateMenuAndItemWidth();
_barSaveHandle = _paint16->bitsSave(_ports->_menuRect, GFX_SCREEN_MASK_VISUAL);
_ports->penColor(0);
@@ -824,7 +838,7 @@ GuiMenuItemEntry *GfxMenu::interactiveWithMouse() {
_paint16->bitsShow(_ports->_menuRect);
while (true) {
- curEvent = _event->get(SCI_EVENT_ANY);
+ curEvent = _event->getSciEvent(SCI_EVENT_ANY);
switch (curEvent.type) {
case SCI_EVENT_MOUSE_RELEASE:
@@ -835,7 +849,7 @@ GuiMenuItemEntry *GfxMenu::interactiveWithMouse() {
return curItemEntry;
case SCI_EVENT_NONE:
- _event->sleep(2500 / 1000);
+ g_sci->sleep(2500 / 1000);
break;
}
diff --git a/engines/sci/graphics/menu.h b/engines/sci/graphics/menu.h
index 8f23b46ff8..9a14d4c64a 100644
--- a/engines/sci/graphics/menu.h
+++ b/engines/sci/graphics/menu.h
@@ -83,7 +83,7 @@ typedef Common::List<GuiMenuItemEntry *> GuiMenuItemList;
*/
class GfxMenu {
public:
- GfxMenu(SciEvent *event, SegManager *segMan, SciGui *gui, GfxPorts *ports, GfxPaint16 *paint16, GfxText16 *text16, GfxScreen *screen, GfxCursor *cursor);
+ GfxMenu(EventManager *event, SegManager *segMan, GfxPorts *ports, GfxPaint16 *paint16, GfxText16 *text16, GfxScreen *screen, GfxCursor *cursor);
~GfxMenu();
void reset();
@@ -92,7 +92,7 @@ public:
reg_t kernelGetAttribute(uint16 menuId, uint16 itemId, uint16 attributeId);
void drawBar();
- reg_t kernelSelect(reg_t eventObject);
+ reg_t kernelSelect(reg_t eventObject, bool pauseSound);
void kernelDrawStatus(const char *text, int16 colorPen, int16 colorBack);
void kernelDrawMenuBar(bool clear);
@@ -103,17 +103,16 @@ private:
void calculateMenuAndItemWidth();
void drawMenu(uint16 oldMenuId, uint16 newMenuId);
void invertMenuSelection(uint16 itemId);
- void interactiveShowMouse();
- void interactiveRestoreMouse();
+ void interactiveStart(bool pauseSound);
+ void interactiveEnd(bool pauseSound);
GuiMenuItemEntry *interactiveWithKeyboard();
GuiMenuItemEntry *interactiveWithMouse();
uint16 mouseFindMenuSelection(Common::Point mousePosition);
uint16 mouseFindMenuItemSelection(Common::Point mousePosition, uint16 menuId);
GuiMenuItemEntry *interactiveGetItem(uint16 menuId, uint16 itemId, bool menuChanged);
- SciEvent *_event;
+ EventManager *_event;
SegManager *_segMan;
- SciGui *_gui;
GfxPorts *_ports;
GfxPaint16 *_paint16;
GfxText16 *_text16;
diff --git a/engines/sci/graphics/paint.h b/engines/sci/graphics/paint.h
index f1342d55e5..75a17461d4 100644
--- a/engines/sci/graphics/paint.h
+++ b/engines/sci/graphics/paint.h
@@ -26,8 +26,6 @@
#ifndef SCI_GRAPHICS_PAINT_H
#define SCI_GRAPHICS_PAINT_H
-#include "sci/graphics/gui.h"
-
#include "common/hashmap.h"
namespace Sci {
diff --git a/engines/sci/graphics/paint16.cpp b/engines/sci/graphics/paint16.cpp
index ff4f3bec52..90b05c68a6 100644
--- a/engines/sci/graphics/paint16.cpp
+++ b/engines/sci/graphics/paint16.cpp
@@ -42,13 +42,14 @@
#include "sci/graphics/view.h"
#include "sci/graphics/screen.h"
#include "sci/graphics/palette.h"
+#include "sci/graphics/portrait.h"
#include "sci/graphics/text16.h"
#include "sci/graphics/transitions.h"
namespace Sci {
-GfxPaint16::GfxPaint16(ResourceManager *resMan, SegManager *segMan, Kernel *kernel, SciGui *gui, GfxCache *cache, GfxPorts *ports, GfxCoordAdjuster *coordAdjuster, GfxScreen *screen, GfxPalette *palette, GfxTransitions *transitions)
- : _resMan(resMan), _segMan(segMan), _kernel(kernel), _gui(gui), _cache(cache), _ports(ports), _coordAdjuster(coordAdjuster), _screen(screen), _palette(palette), _transitions(transitions) {
+GfxPaint16::GfxPaint16(ResourceManager *resMan, SegManager *segMan, Kernel *kernel, GfxCache *cache, GfxPorts *ports, GfxCoordAdjuster *coordAdjuster, GfxScreen *screen, GfxPalette *palette, GfxTransitions *transitions, AudioPlayer *audio)
+ : _resMan(resMan), _segMan(segMan), _kernel(kernel), _cache(cache), _ports(ports), _coordAdjuster(coordAdjuster), _screen(screen), _palette(palette), _transitions(transitions), _audio(audio) {
}
GfxPaint16::~GfxPaint16() {
@@ -74,6 +75,11 @@ void GfxPaint16::drawPicture(GuiResourceId pictureId, int16 animationNr, bool mi
picture->draw(animationNr, mirroredFlag, addToFlag, paletteId);
delete picture;
+
+ // We make a call to SciPalette here, for increasing sys timestamp and also loading targetpalette, if palvary active
+ // (SCI1.1 only)
+ if (getSciVersion() == SCI_VERSION_1_1)
+ _palette->drewPicture(pictureId);
}
// This one is the only one that updates screen!
@@ -101,12 +107,12 @@ void GfxPaint16::drawCelAndShow(GuiResourceId viewId, int16 loopNo, int16 celNo,
}
// This version of drawCel is not supposed to call BitsShow()!
-void GfxPaint16::drawCel(GuiResourceId viewId, int16 loopNo, int16 celNo, Common::Rect celRect, byte priority, uint16 paletteNo, uint16 scaleX, uint16 scaleY) {
+void GfxPaint16::drawCel(GuiResourceId viewId, int16 loopNo, int16 celNo, const Common::Rect &celRect, byte priority, uint16 paletteNo, uint16 scaleX, uint16 scaleY) {
drawCel(_cache->getView(viewId), loopNo, celNo, celRect, priority, paletteNo, scaleX, scaleY);
}
// This version of drawCel is not supposed to call BitsShow()!
-void GfxPaint16::drawCel(GfxView *view, int16 loopNo, int16 celNo, Common::Rect celRect, byte priority, uint16 paletteNo, uint16 scaleX, uint16 scaleY) {
+void GfxPaint16::drawCel(GfxView *view, int16 loopNo, int16 celNo, const Common::Rect &celRect, byte priority, uint16 paletteNo, uint16 scaleX, uint16 scaleY) {
Common::Rect clipRect = celRect;
clipRect.clip(_ports->_curPort->rect);
if (clipRect.isEmpty()) // nothing to draw
@@ -121,8 +127,8 @@ void GfxPaint16::drawCel(GfxView *view, int16 loopNo, int16 celNo, Common::Rect
}
}
-// This is used as replacement for drawCelAndShow() when hires-cels are drawn to screen
-// Hires-cels are available only SCI 1.1+
+// This is used as replacement for drawCelAndShow() when hires-cels are drawn to
+// screen. Hires-cels are available only SCI 1.1+.
void GfxPaint16::drawHiresCelAndShow(GuiResourceId viewId, int16 loopNo, int16 celNo, uint16 leftPos, uint16 topPos, byte priority, uint16 paletteNo, reg_t upscaledHiresHandle, uint16 scaleX, uint16 scaleY) {
GfxView *view = _cache->getView(viewId);
Common::Rect celRect, curPortRect, clipRect, clipRectTranslated;
@@ -131,9 +137,10 @@ void GfxPaint16::drawHiresCelAndShow(GuiResourceId viewId, int16 loopNo, int16 c
if (view) {
if ((leftPos == 0) && (topPos == 0)) {
- // HACK: in kq6, we get leftPos&topPos == 0 SOMETIMES, that's why we need to get coordinates from upscaledHiresHandle
- // I'm not sure if this is what we are supposed to do or if there is some other bug that actually makes
- // coordinates to be 0 in the first place
+ // HACK: in kq6, we get leftPos&topPos == 0 SOMETIMES, that's why we
+ // need to get coordinates from upscaledHiresHandle. I'm not sure if
+ // this is what we are supposed to do or if there is some other bug
+ // that actually makes coordinates to be 0 in the first place.
byte *memoryPtr = NULL;
memoryPtr = _segMan->getHunkPointer(upscaledHiresHandle);
if (memoryPtr) {
@@ -310,11 +317,12 @@ reg_t GfxPaint16::bitsSave(const Common::Rect &rect, byte screenMask) {
return NULL_REG;
if (screenMask == GFX_SCREEN_MASK_DISPLAY) {
+ // The coordinates we are given are actually up-to-including right/bottom - we extend accordingly
+ workerRect.bottom++;
+ workerRect.right++;
// Adjust rect to upscaled hires, but dont adjust according to port
_screen->adjustToUpscaledCoordinates(workerRect.top, workerRect.left);
_screen->adjustToUpscaledCoordinates(workerRect.bottom, workerRect.right);
- workerRect.bottom++;
- workerRect.right++;
} else {
_ports->offsetRect(workerRect);
}
@@ -324,7 +332,8 @@ reg_t GfxPaint16::bitsSave(const Common::Rect &rect, byte screenMask) {
memoryId = _segMan->allocateHunkEntry("SaveBits()", size);
memoryPtr = _segMan->getHunkPointer(memoryId);
- _screen->bitsSave(workerRect, screenMask, memoryPtr);
+ if (memoryPtr)
+ _screen->bitsSave(workerRect, screenMask, memoryPtr);
return memoryId;
}
@@ -374,7 +383,8 @@ void GfxPaint16::kernelDrawPicture(GuiResourceId pictureId, int16 animationNr, b
}
void GfxPaint16::kernelDrawCel(GuiResourceId viewId, int16 loopNo, int16 celNo, uint16 leftPos, uint16 topPos, int16 priority, uint16 paletteNo, bool hiresMode, reg_t upscaledHiresHandle) {
- // some calls are hiresMode even under kq6 DOS, that's why we check for upscaled hires here
+ // some calls are hiresMode even under kq6 DOS, that's why we check for
+ // upscaled hires here
if ((!hiresMode) || (!_screen->getUpscaledHires())) {
drawCelAndShow(viewId, loopNo, celNo, leftPos, topPos, priority, paletteNo);
} else {
@@ -382,19 +392,19 @@ void GfxPaint16::kernelDrawCel(GuiResourceId viewId, int16 loopNo, int16 celNo,
}
}
-void GfxPaint16::kernelGraphFillBoxForeground(Common::Rect rect) {
+void GfxPaint16::kernelGraphFillBoxForeground(const Common::Rect &rect) {
paintRect(rect);
}
-void GfxPaint16::kernelGraphFillBoxBackground(Common::Rect rect) {
+void GfxPaint16::kernelGraphFillBoxBackground(const Common::Rect &rect) {
eraseRect(rect);
}
-void GfxPaint16::kernelGraphFillBox(Common::Rect rect, uint16 colorMask, int16 color, int16 priority, int16 control) {
+void GfxPaint16::kernelGraphFillBox(const Common::Rect &rect, uint16 colorMask, int16 color, int16 priority, int16 control) {
fillRect(rect, colorMask, color, priority, control);
}
-void GfxPaint16::kernelGraphFrameBox(Common::Rect rect, int16 color) {
+void GfxPaint16::kernelGraphFrameBox(const Common::Rect &rect, int16 color) {
int16 oldColor = _ports->getPort()->penClr;
_ports->penColor(color);
frameRect(rect);
@@ -406,11 +416,11 @@ void GfxPaint16::kernelGraphDrawLine(Common::Point startPoint, Common::Point end
_screen->drawLine(startPoint.x, startPoint.y, endPoint.x, endPoint.y, color, priority, control);
}
-reg_t GfxPaint16::kernelGraphSaveBox(Common::Rect rect, uint16 screenMask) {
+reg_t GfxPaint16::kernelGraphSaveBox(const Common::Rect &rect, uint16 screenMask) {
return bitsSave(rect, screenMask);
}
-reg_t GfxPaint16::kernelGraphSaveUpscaledHiresBox(Common::Rect rect) {
+reg_t GfxPaint16::kernelGraphSaveUpscaledHiresBox(const Common::Rect &rect) {
return bitsSave(rect, GFX_SCREEN_MASK_DISPLAY);
}
@@ -418,8 +428,9 @@ void GfxPaint16::kernelGraphRestoreBox(reg_t handle) {
bitsRestore(handle);
}
-void GfxPaint16::kernelGraphUpdateBox(Common::Rect rect, bool hiresMode) {
- // some calls are hiresMode even under kq6 DOS, that's why we check for upscaled hires here
+void GfxPaint16::kernelGraphUpdateBox(const Common::Rect &rect, bool hiresMode) {
+ // some calls are hiresMode even under kq6 DOS, that's why we check for
+ // upscaled hires here
if ((!hiresMode) || (!_screen->getUpscaledHires()))
bitsShow(rect);
else
@@ -447,6 +458,8 @@ void GfxPaint16::kernelGraphRedrawBox(Common::Rect rect) {
#define SCI_DISPLAY_WIDTH 106
#define SCI_DISPLAY_SAVEUNDER 107
#define SCI_DISPLAY_RESTOREUNDER 108
+#define SCI_DISPLAY_DUMMY1 114 // used in longbow-demo, not supported in sierra sci - no parameters
+#define SCI_DISPLAY_DUMMY2 115 // used in longbow-demo, not supported in sierra sci - has 1 parameter
#define SCI_DISPLAY_DONTSHOWBITS 121
reg_t GfxPaint16::kernelDisplay(const char *text, int argc, reg_t *argv) {
@@ -457,7 +470,8 @@ reg_t GfxPaint16::kernelDisplay(const char *text, int argc, reg_t *argv) {
Common::Rect rect;
reg_t result = NULL_REG;
- // Make a "backup" of the port settings (required for some SCI0LATE and SCI01+ only)
+ // Make a "backup" of the port settings (required for some SCI0LATE and
+ // SCI01+ only)
Port oldPort = *_ports->getPort();
// setting defaults
@@ -512,8 +526,24 @@ reg_t GfxPaint16::kernelDisplay(const char *text, int argc, reg_t *argv) {
case SCI_DISPLAY_DONTSHOWBITS:
bRedraw = 0;
break;
+
+ // 2 Dummy functions, longbow-demo is using those several times but sierra sci doesn't support them at all
+ case SCI_DISPLAY_DUMMY1:
+ case SCI_DISPLAY_DUMMY2:
+ if (!((g_sci->getGameId() == GID_LONGBOW) && (g_sci->isDemo())))
+ error("Unknown kDisplay argument %X", displayArg);
+ if (displayArg == SCI_DISPLAY_DUMMY2) {
+ if (argc) {
+ argc--; argv++;
+ } else {
+ error("No parameter left for kDisplay(0x73)");
+ }
+ }
+ break;
default:
- warning("Unknown kDisplay argument %X", displayArg);
+ if ((g_sci->getGameId() == GID_ISLANDBRAIN) && (g_sci->getEngineState()->currentRoomNumber() == 300))
+ break; // WORKAROUND: we are called there with an forwarded 0 as additional parameter (script bug)
+ error("Unknown kDisplay argument %X", displayArg);
break;
}
}
@@ -540,14 +570,20 @@ reg_t GfxPaint16::kernelDisplay(const char *text, int argc, reg_t *argv) {
uint16 tTop = currport->curTop;
uint16 tLeft = currport->curLeft;
if (!g_sci->_features->usesOldGfxFunctions()) {
- // Restore port settings for some SCI0LATE and SCI01+ only
- // the change actually happened inbetween .530 (hoyle1) and .566 (heros quest). We don't have any detection for
- // that currently, so we are using oldGfxFunctions (.502). The only games that could get regressions because of
- // this are hoyle1, kq4 and funseeker. If there are regressions, we should use interpreter version (which would
- // require exe version detection)
- // If we restore the port for whole SCI0LATE, at least sq3old will get an issue - font 0 will get used when
- // scanning for planets instead of font 600 - a setfont parameter is missing in one of the kDisplay calls in
- // script 19. I assume this is a script bug, because it was added in sq3new.
+ // Restore port settings for some SCI0LATE and SCI01+ only.
+ //
+ // The change actually happened inbetween .530 (hoyle1) and .566 (heros
+ // quest). We don't have any detection for that currently, so we are
+ // using oldGfxFunctions (.502). The only games that could get
+ // regressions because of this are hoyle1, kq4 and funseeker. If there
+ // are regressions, we should use interpreter version (which would
+ // require exe version detection).
+ //
+ // If we restore the port for whole SCI0LATE, at least sq3old will get
+ // an issue - font 0 will get used when scanning for planets instead of
+ // font 600 - a setfont parameter is missing in one of the kDisplay
+ // calls in script 19. I assume this is a script bug, because it was
+ // added in sq3new.
*currport = oldPort;
}
currport->curTop = tTop;
@@ -562,12 +598,33 @@ void GfxPaint16::kernelShakeScreen(uint16 shakeCount, uint16 directions) {
_screen->setVerticalShakePos(10);
// TODO: horizontal shakes
g_system->updateScreen();
- _gui->wait(3);
+ g_sci->getEngineState()->wait(3);
+
if (directions & SCI_SHAKE_DIRECTION_VERTICAL)
_screen->setVerticalShakePos(0);
+
g_system->updateScreen();
- _gui->wait(3);
+ g_sci->getEngineState()->wait(3);
}
}
+reg_t GfxPaint16::kernelPortraitLoad(const Common::String &resourceName) {
+ //Portrait *myPortrait = new Portrait(g_sci->getResMan(), _screen, _palette, resourceName);
+ return NULL_REG;
+}
+
+void GfxPaint16::kernelPortraitShow(const Common::String &resourceName, Common::Point position, uint16 resourceId, uint16 noun, uint16 verb, uint16 cond, uint16 seq) {
+ Portrait *myPortrait = new Portrait(g_sci->getResMan(), g_sci->getEventManager(), _screen, _palette, _audio, resourceName);
+ // TODO: cache portraits
+ // adjust given coordinates to curPort (but dont adjust coordinates on upscaledHires_Save_Box and give us hires coordinates
+ // on kDrawCel, yeah this whole stuff makes sense)
+ position.x += _ports->getPort()->left; position.y += _ports->getPort()->top;
+ _screen->adjustToUpscaledCoordinates(position.y, position.x);
+ myPortrait->doit(position, resourceId, noun, verb, cond, seq);
+ delete myPortrait;
+}
+
+void GfxPaint16::kernelPortraitUnload(uint16 portraitId) {
+}
+
} // End of namespace Sci
diff --git a/engines/sci/graphics/paint16.h b/engines/sci/graphics/paint16.h
index 65f9dd0d9c..4c3ac255c4 100644
--- a/engines/sci/graphics/paint16.h
+++ b/engines/sci/graphics/paint16.h
@@ -26,7 +26,6 @@
#ifndef SCI_GRAPHICS_PAINT16_H
#define SCI_GRAPHICS_PAINT16_H
-#include "sci/graphics/gui.h"
#include "sci/graphics/paint.h"
#include "common/hashmap.h"
@@ -45,7 +44,7 @@ class GfxView;
*/
class GfxPaint16 : public GfxPaint {
public:
- GfxPaint16(ResourceManager *resMan, SegManager *segMan, Kernel *kernel, SciGui *gui, GfxCache *cache, GfxPorts *ports, GfxCoordAdjuster *coordAdjuster, GfxScreen *screen, GfxPalette *palette, GfxTransitions *transitions);
+ GfxPaint16(ResourceManager *resMan, SegManager *segMan, Kernel *kernel, GfxCache *cache, GfxPorts *ports, GfxCoordAdjuster *coordAdjuster, GfxScreen *screen, GfxPalette *palette, GfxTransitions *transitions, AudioPlayer *audio);
~GfxPaint16();
void init(GfxAnimate *animate, GfxText16 *text16);
@@ -54,8 +53,8 @@ public:
void drawPicture(GuiResourceId pictureId, int16 animationNr, bool mirroredFlag, bool addToFlag, GuiResourceId paletteId);
void drawCelAndShow(GuiResourceId viewId, int16 loopNo, int16 celNo, uint16 leftPos, uint16 topPos, byte priority, uint16 paletteNo, uint16 scaleX = 128, uint16 scaleY = 128);
- void drawCel(GuiResourceId viewId, int16 loopNo, int16 celNo, Common::Rect celRect, byte priority, uint16 paletteNo, uint16 scaleX = 128, uint16 scaleY = 128);
- void drawCel(GfxView *view, int16 loopNo, int16 celNo, Common::Rect celRect, byte priority, uint16 paletteNo, uint16 scaleX = 128, uint16 scaleY = 128);
+ void drawCel(GuiResourceId viewId, int16 loopNo, int16 celNo, const Common::Rect &celRect, byte priority, uint16 paletteNo, uint16 scaleX = 128, uint16 scaleY = 128);
+ void drawCel(GfxView *view, int16 loopNo, int16 celNo, const Common::Rect &celRect, byte priority, uint16 paletteNo, uint16 scaleX = 128, uint16 scaleY = 128);
void drawHiresCelAndShow(GuiResourceId viewId, int16 loopNo, int16 celNo, uint16 leftPos, uint16 topPos, byte priority, uint16 paletteNo, reg_t upscaledHiresHandle, uint16 scaleX = 128, uint16 scaleY = 128);
void clearScreen(byte color = 255);
@@ -76,26 +75,30 @@ public:
void kernelDrawPicture(GuiResourceId pictureId, int16 animationNr, bool animationBlackoutFlag, bool mirroredFlag, bool addToFlag, int16 EGApaletteNo);
void kernelDrawCel(GuiResourceId viewId, int16 loopNo, int16 celNo, uint16 leftPos, uint16 topPos, int16 priority, uint16 paletteNo, bool hiresMode, reg_t upscaledHiresHandle);
- void kernelGraphFillBoxForeground(Common::Rect rect);
- void kernelGraphFillBoxBackground(Common::Rect rect);
- void kernelGraphFillBox(Common::Rect rect, uint16 colorMask, int16 color, int16 priority, int16 control);
- void kernelGraphFrameBox(Common::Rect rect, int16 color);
+ void kernelGraphFillBoxForeground(const Common::Rect &rect);
+ void kernelGraphFillBoxBackground(const Common::Rect &rect);
+ void kernelGraphFillBox(const Common::Rect &rect, uint16 colorMask, int16 color, int16 priority, int16 control);
+ void kernelGraphFrameBox(const Common::Rect &rect, int16 color);
void kernelGraphDrawLine(Common::Point startPoint, Common::Point endPoint, int16 color, int16 priority, int16 control);
- reg_t kernelGraphSaveBox(Common::Rect rect, uint16 flags);
- reg_t kernelGraphSaveUpscaledHiresBox(Common::Rect rect);
+ reg_t kernelGraphSaveBox(const Common::Rect &rect, uint16 flags);
+ reg_t kernelGraphSaveUpscaledHiresBox(const Common::Rect &rect);
void kernelGraphRestoreBox(reg_t handle);
- void kernelGraphUpdateBox(Common::Rect rect, bool hiresMode);
+ void kernelGraphUpdateBox(const Common::Rect &rect, bool hiresMode);
void kernelGraphRedrawBox(Common::Rect rect);
reg_t kernelDisplay(const char *text, int argc, reg_t *argv);
void kernelShakeScreen(uint16 shakeCount, uint16 directions);
+ reg_t kernelPortraitLoad(const Common::String &resourceName);
+ void kernelPortraitShow(const Common::String &resourceName, Common::Point position, uint16 resourceNum, uint16 noun, uint16 verb, uint16 cond, uint16 seq);
+ void kernelPortraitUnload(uint16 portraitId);
+
private:
ResourceManager *_resMan;
SegManager *_segMan;
Kernel *_kernel;
- SciGui *_gui;
+ AudioPlayer *_audio;
GfxAnimate *_animate;
GfxCache *_cache;
GfxPorts *_ports;
diff --git a/engines/sci/graphics/paint32.cpp b/engines/sci/graphics/paint32.cpp
index 711efc9816..9b24da413b 100644
--- a/engines/sci/graphics/paint32.cpp
+++ b/engines/sci/graphics/paint32.cpp
@@ -38,6 +38,7 @@
#include "sci/graphics/view.h"
#include "sci/graphics/screen.h"
#include "sci/graphics/palette.h"
+#include "sci/graphics/robot.h"
namespace Sci {
@@ -79,4 +80,10 @@ void GfxPaint32::kernelGraphDrawLine(Common::Point startPoint, Common::Point end
_screen->drawLine(startPoint.x, startPoint.y, endPoint.x, endPoint.y, color, priority, control);
}
+void GfxPaint32::debugDrawRobot(GuiResourceId robotId) {
+ GfxRobot *test = new GfxRobot(g_sci->getResMan(), _screen, robotId);
+ test->draw();
+ delete test;
+}
+
} // End of namespace Sci
diff --git a/engines/sci/graphics/paint32.h b/engines/sci/graphics/paint32.h
index f4d6340361..a048d7f307 100644
--- a/engines/sci/graphics/paint32.h
+++ b/engines/sci/graphics/paint32.h
@@ -26,7 +26,6 @@
#ifndef SCI_GRAPHICS_PAINT32_H
#define SCI_GRAPHICS_PAINT32_H
-#include "sci/graphics/gui.h"
#include "sci/graphics/paint.h"
#include "common/hashmap.h"
@@ -49,6 +48,8 @@ public:
void kernelDrawCel(GuiResourceId viewId, int16 loopNo, int16 celNo, uint16 leftPos, uint16 topPos, int16 priority, uint16 paletteNo, bool hiresMode, reg_t upscaledHiresHandle);
void kernelGraphDrawLine(Common::Point startPoint, Common::Point endPoint, int16 color, int16 priority, int16 control);
+ void debugDrawRobot(GuiResourceId robotId);
+
private:
ResourceManager *_resMan;
SegManager *_segMan;
diff --git a/engines/sci/graphics/palette.cpp b/engines/sci/graphics/palette.cpp
index 3c4cf7e964..957199f8b9 100644
--- a/engines/sci/graphics/palette.cpp
+++ b/engines/sci/graphics/palette.cpp
@@ -30,13 +30,15 @@
#include "sci/sci.h"
#include "sci/engine/state.h"
+#include "sci/graphics/cache.h"
#include "sci/graphics/maciconbar.h"
#include "sci/graphics/palette.h"
#include "sci/graphics/screen.h"
+#include "sci/graphics/view.h"
namespace Sci {
-GfxPalette::GfxPalette(ResourceManager *resMan, GfxScreen *screen, bool autoSetPalette)
+GfxPalette::GfxPalette(ResourceManager *resMan, GfxScreen *screen, bool useMerging)
: _resMan(resMan), _screen(screen) {
int16 color;
@@ -57,23 +59,39 @@ GfxPalette::GfxPalette(ResourceManager *resMan, GfxScreen *screen, bool autoSetP
_sysPalette.colors[255].b = 255;
_sysPaletteChanged = false;
- if (autoSetPalette) {
- if (_resMan->getViewType() == kViewEga)
- setEGA();
- else if (_resMan->isAmiga32color())
- setAmiga();
- else
- kernelSetFromResource(999, true);
- }
+
+ // Pseudo-WORKAROUND
+ // Quest for Glory 3 demo, Eco Quest 1 demo, Laura Bow 2 demo, Police Quest 1 vga and all Nick's Picks
+ // all use an inbetween interpreter, some parts are SCI1.1, some parts are SCI1
+ // It's not using the SCI1.1 palette merging (copying over all the colors) but the real merging
+ // If we use the copying over, we will get issues because some views have marked all colors as being used
+ // and those will overwrite the current palette in that case
+ _useMerging = useMerging;
+
+ palVaryInit();
}
GfxPalette::~GfxPalette() {
}
+bool GfxPalette::isMerging() {
+ return _useMerging;
+}
+
+// meant to get called only once during init of engine
+void GfxPalette::setDefault() {
+ if (_resMan->getViewType() == kViewEga)
+ setEGA();
+ else if (_resMan->isAmiga32color())
+ setAmiga();
+ else
+ kernelSetFromResource(999, true);
+}
+
#define SCI_PAL_FORMAT_CONSTANT 1
#define SCI_PAL_FORMAT_VARIABLE 0
-void GfxPalette::createFromData(byte *data, Palette *paletteOut) {
+void GfxPalette::createFromData(byte *data, int bytesLeft, Palette *paletteOut) {
int palFormat = 0;
int palOffset = 0;
int palColorStart = 0;
@@ -81,10 +99,16 @@ void GfxPalette::createFromData(byte *data, Palette *paletteOut) {
int colorNo = 0;
memset(paletteOut, 0, sizeof(Palette));
- // Setup default mapping
+ // Setup 1:1 mapping
for (colorNo = 0; colorNo < 256; colorNo++) {
paletteOut->mapping[colorNo] = colorNo;
}
+ if (bytesLeft < 37) {
+ // This happens when loading palette of picture 0 in sq5 - the resource is broken and doesn't contain a full
+ // palette
+ warning("GfxPalette::createFromData() - not enough bytes in resource, expected palette header");
+ return;
+ }
if ((data[0] == 0 && data[1] == 1) || (data[0] == 0 && data[1] == 0 && READ_LE_UINT16(data + 29) == 0)) {
// SCI0/SCI1 palette
palFormat = SCI_PAL_FORMAT_VARIABLE; // CONSTANT;
@@ -99,6 +123,11 @@ void GfxPalette::createFromData(byte *data, Palette *paletteOut) {
}
switch (palFormat) {
case SCI_PAL_FORMAT_CONSTANT:
+ // Check, if enough bytes left
+ if (bytesLeft < palOffset + (3 * palColorCount)) {
+ warning("GfxPalette::createFromData() - not enough bytes in resource, expected palette colors");
+ return;
+ }
for (colorNo = palColorStart; colorNo < palColorStart + palColorCount; colorNo++) {
paletteOut->colors[colorNo].used = 1;
paletteOut->colors[colorNo].r = data[palOffset++];
@@ -107,6 +136,10 @@ void GfxPalette::createFromData(byte *data, Palette *paletteOut) {
}
break;
case SCI_PAL_FORMAT_VARIABLE:
+ if (bytesLeft < palOffset + (4 * palColorCount)) {
+ warning("GfxPalette::createFromData() - not enough bytes in resource, expected palette colors");
+ return;
+ }
for (colorNo = palColorStart; colorNo < palColorStart + palColorCount; colorNo++) {
paletteOut->colors[colorNo].used = data[palOffset++];
paletteOut->colors[colorNo].r = data[palOffset++];
@@ -180,112 +213,144 @@ void GfxPalette::setEGA() {
// Now setting colors 16-254 to the correct mix colors that occur when not doing a dithering run on
// finished pictures
for (curColor = 0x10; curColor <= 0xFE; curColor++) {
- _sysPalette.colors[curColor].used = curColor;
+ _sysPalette.colors[curColor].used = 1;
color1 = curColor & 0x0F; color2 = curColor >> 4;
_sysPalette.colors[curColor].r = (_sysPalette.colors[color1].r >> 1) + (_sysPalette.colors[color2].r >> 1);
_sysPalette.colors[curColor].g = (_sysPalette.colors[color1].g >> 1) + (_sysPalette.colors[color2].g >> 1);
_sysPalette.colors[curColor].b = (_sysPalette.colors[color1].b >> 1) + (_sysPalette.colors[color2].b >> 1);
}
+ _sysPalette.timestamp = 1;
setOnScreen();
}
-void GfxPalette::set(Palette *sciPal, bool force, bool forceRealMerge) {
+void GfxPalette::set(Palette *newPalette, bool force, bool forceRealMerge) {
uint32 systime = _sysPalette.timestamp;
- if (force || sciPal->timestamp != systime) {
- _sysPaletteChanged |= merge(sciPal, &_sysPalette, force, forceRealMerge);
- sciPal->timestamp = _sysPalette.timestamp;
- if (_sysPaletteChanged && _screen->_picNotValid == 0) { // && systime != _sysPalette.timestamp) {
- // Removed timestamp checking, because this shouldnt be needed anymore. I'm leaving it commented just in
- // case this causes regressions
+ if (force || newPalette->timestamp != systime) {
+ // SCI1.1+ doesnt do real merging anymore, but simply copying over the used colors from other palettes
+ // There are some games with inbetween SCI1.1 interpreters, use real merging for them (e.g. laura bow 2 demo)
+ if ((forceRealMerge) || (_useMerging))
+ _sysPaletteChanged |= merge(newPalette, force, forceRealMerge);
+ else
+ _sysPaletteChanged |= insert(newPalette, &_sysPalette);
+
+ // Adjust timestamp on newPalette, so it wont get merged/inserted w/o need
+ newPalette->timestamp = _sysPalette.timestamp;
+
+ bool updatePalette = _sysPaletteChanged && _screen->_picNotValid == 0;
+
+ if (_palVaryResourceId != -1) {
+ // Pal-vary currently active, we don't set at any time, but also insert into origin palette
+ insert(newPalette, &_palVaryOriginPalette);
+ palVaryProcess(0, updatePalette);
+ return;
+ }
+
+ if (updatePalette) {
setOnScreen();
_sysPaletteChanged = false;
}
}
}
-bool GfxPalette::merge(Palette *pFrom, Palette *pTo, bool force, bool forceRealMerge) {
+bool GfxPalette::insert(Palette *newPalette, Palette *destPalette) {
+ bool paletteChanged = false;
+
+ for (int i = 1; i < 255; i++) {
+ if (newPalette->colors[i].used) {
+ if ((newPalette->colors[i].r != destPalette->colors[i].r) || (newPalette->colors[i].g != destPalette->colors[i].g) || (newPalette->colors[i].b != destPalette->colors[i].b)) {
+ destPalette->colors[i].r = newPalette->colors[i].r;
+ destPalette->colors[i].g = newPalette->colors[i].g;
+ destPalette->colors[i].b = newPalette->colors[i].b;
+ paletteChanged = true;
+ }
+ destPalette->colors[i].used = newPalette->colors[i].used;
+ newPalette->mapping[i] = i;
+ }
+ }
+ // We don't update the timestamp for SCI1.1, it's only updated on kDrawPic calls
+ return paletteChanged;
+}
+
+bool GfxPalette::merge(Palette *newPalette, bool force, bool forceRealMerge) {
uint16 res;
int i,j;
bool paletteChanged = false;
- if ((!forceRealMerge) && (getSciVersion() >= SCI_VERSION_1_1)) {
- // SCI1.1+ doesnt do real merging anymore, but simply copying over the used colors from other palettes
- for (i = 1; i < 255; i++) {
- if (pFrom->colors[i].used) {
- if ((pFrom->colors[i].r != pTo->colors[i].r) || (pFrom->colors[i].g != pTo->colors[i].g) || (pFrom->colors[i].b != pTo->colors[i].b)) {
- pTo->colors[i].r = pFrom->colors[i].r;
- pTo->colors[i].g = pFrom->colors[i].g;
- pTo->colors[i].b = pFrom->colors[i].b;
- paletteChanged = true;
- }
- pTo->colors[i].used = pFrom->colors[i].used;
- pFrom->mapping[i] = i;
+ // colors 0 (black) and 255 (white) are not affected by merging
+ for (i = 1 ; i < 255; i++) {
+ if (!newPalette->colors[i].used)// color is not used - so skip it
+ continue;
+ // forced palette merging or dest color is not used yet
+ if (force || (!_sysPalette.colors[i].used)) {
+ _sysPalette.colors[i].used = newPalette->colors[i].used;
+ if ((newPalette->colors[i].r != _sysPalette.colors[i].r) || (newPalette->colors[i].g != _sysPalette.colors[i].g) || (newPalette->colors[i].b != _sysPalette.colors[i].b)) {
+ _sysPalette.colors[i].r = newPalette->colors[i].r;
+ _sysPalette.colors[i].g = newPalette->colors[i].g;
+ _sysPalette.colors[i].b = newPalette->colors[i].b;
+ paletteChanged = true;
}
+ newPalette->mapping[i] = i;
+ continue;
}
- } else {
- // colors 0 (black) and 255 (white) are not affected by merging
- for (i = 1 ; i < 255; i++) {
- if (!pFrom->colors[i].used)// color is not used - so skip it
- continue;
- // forced palette merging or dest color is not used yet
- if (force || (!pTo->colors[i].used)) {
- pTo->colors[i].used = pFrom->colors[i].used;
- if ((pFrom->colors[i].r != pTo->colors[i].r) || (pFrom->colors[i].g != pTo->colors[i].g) || (pFrom->colors[i].b != pTo->colors[i].b)) {
- pTo->colors[i].r = pFrom->colors[i].r;
- pTo->colors[i].g = pFrom->colors[i].g;
- pTo->colors[i].b = pFrom->colors[i].b;
- paletteChanged = true;
- }
- pFrom->mapping[i] = i;
- continue;
- }
- // is the same color already at the same position? -> match it directly w/o lookup
- // this fixes games like lsl1demo/sq5 where the same rgb color exists multiple times and where we would
- // otherwise match the wrong one (which would result into the pixels affected (or not) by palette changes)
- if ((pTo->colors[i].r == pFrom->colors[i].r) && (pTo->colors[i].g == pFrom->colors[i].g) && (pTo->colors[i].b == pFrom->colors[i].b)) {
- pFrom->mapping[i] = i;
- continue;
- }
- // check if exact color could be matched
- res = matchColor(pTo, pFrom->colors[i].r, pFrom->colors[i].g, pFrom->colors[i].b);
- if (res & 0x8000) { // exact match was found
- pFrom->mapping[i] = res & 0xFF;
- continue;
- }
- // no exact match - see if there is an unused color
- for (j = 1; j < 256; j++)
- if (!pTo->colors[j].used) {
- pTo->colors[j].used = pFrom->colors[i].used;
- pTo->colors[j].r = pFrom->colors[i].r;
- pTo->colors[j].g = pFrom->colors[i].g;
- pTo->colors[j].b = pFrom->colors[i].b;
- pFrom->mapping[i] = j;
- paletteChanged = true;
- break;
- }
- // if still no luck - set an approximate color
- if (j == 256) {
- pFrom->mapping[i] = res & 0xFF;
- pTo->colors[res & 0xFF].used |= 0x10;
+ // is the same color already at the same position? -> match it directly w/o lookup
+ // this fixes games like lsl1demo/sq5 where the same rgb color exists multiple times and where we would
+ // otherwise match the wrong one (which would result into the pixels affected (or not) by palette changes)
+ if ((_sysPalette.colors[i].r == newPalette->colors[i].r) && (_sysPalette.colors[i].g == newPalette->colors[i].g) && (_sysPalette.colors[i].b == newPalette->colors[i].b)) {
+ newPalette->mapping[i] = i;
+ continue;
+ }
+ // check if exact color could be matched
+ res = matchColor(newPalette->colors[i].r, newPalette->colors[i].g, newPalette->colors[i].b);
+ if (res & 0x8000) { // exact match was found
+ newPalette->mapping[i] = res & 0xFF;
+ continue;
+ }
+ // no exact match - see if there is an unused color
+ for (j = 1; j < 256; j++)
+ if (!_sysPalette.colors[j].used) {
+ _sysPalette.colors[j].used = newPalette->colors[i].used;
+ _sysPalette.colors[j].r = newPalette->colors[i].r;
+ _sysPalette.colors[j].g = newPalette->colors[i].g;
+ _sysPalette.colors[j].b = newPalette->colors[i].b;
+ newPalette->mapping[i] = j;
+ paletteChanged = true;
+ break;
}
+ // if still no luck - set an approximate color
+ if (j == 256) {
+ newPalette->mapping[i] = res & 0xFF;
+ _sysPalette.colors[res & 0xFF].used |= 0x10;
}
}
- pTo->timestamp = g_system->getMillis() * 60 / 1000;
+
+ if (!forceRealMerge)
+ _sysPalette.timestamp = g_system->getMillis() * 60 / 1000;
return paletteChanged;
}
-uint16 GfxPalette::matchColor(Palette *pPal, byte r, byte g, byte b) {
+// This is called for SCI1.1, when kDrawPic got done. We update sysPalette timestamp this way for SCI1.1 and also load
+// target-palette, if palvary is active
+void GfxPalette::drewPicture(GuiResourceId pictureId) {
+ if (!_useMerging) // Don't do this on inbetween SCI1.1 games
+ _sysPalette.timestamp++;
+
+ if (_palVaryResourceId != -1) {
+ palVaryLoadTargetPalette(pictureId);
+ }
+}
+
+uint16 GfxPalette::matchColor(byte r, byte g, byte b) {
byte found = 0xFF;
int diff = 0x2FFFF, cdiff;
int16 dr,dg,db;
for (int i = 1; i < 255; i++) {
- if ((!pPal->colors[i].used))
+ if ((!_sysPalette.colors[i].used))
continue;
- dr = pPal->colors[i].r - r;
- dg = pPal->colors[i].g - g;
- db = pPal->colors[i].b - b;
+ dr = _sysPalette.colors[i].r - r;
+ dg = _sysPalette.colors[i].g - g;
+ db = _sysPalette.colors[i].b - b;
// minimum squares match
cdiff = (dr*dr) + (dg*dg) + (db*db);
// minimum sum match (Sierra's)
@@ -306,8 +371,6 @@ void GfxPalette::getSys(Palette *pal) {
}
void GfxPalette::setOnScreen() {
-// if (pal != &_sysPalette)
-// memcpy(&_sysPalette,pal,sizeof(Palette));
// We dont change palette at all times for amiga
if (_resMan->isAmiga32color())
return;
@@ -319,11 +382,11 @@ void GfxPalette::setOnScreen() {
}
bool GfxPalette::kernelSetFromResource(GuiResourceId resourceId, bool force) {
- Resource *palResource = _resMan->findResource(ResourceId(kResourceTypePalette, resourceId), 0);
+ Resource *palResource = _resMan->findResource(ResourceId(kResourceTypePalette, resourceId), false);
Palette palette;
if (palResource) {
- createFromData(palResource->data, &palette);
+ createFromData(palResource->data, palResource->size, &palette);
set(&palette, force);
return true;
}
@@ -346,12 +409,18 @@ void GfxPalette::kernelUnsetFlag(uint16 fromColor, uint16 toColor, uint16 flag)
void GfxPalette::kernelSetIntensity(uint16 fromColor, uint16 toColor, uint16 intensity, bool setPalette) {
memset(&_sysPalette.intensity[0] + fromColor, intensity, toColor - fromColor);
- if (setPalette)
+ if (setPalette) {
setOnScreen();
+ EngineState *state = g_sci->getEngineState();
+ // Call speed throttler from here as well just in case we need it
+ // At least in kq6 intro the scripts call us in a tight loop for fadein/fadeout
+ state->speedThrottler(30);
+ state->_throttleTrigger = true;
+ }
}
int16 GfxPalette::kernelFindColor(uint16 r, uint16 g, uint16 b) {
- return matchColor(&_sysPalette, r, g, b) & 0xFF;
+ return matchColor(r, g, b) & 0xFF;
}
// Returns true, if palette got changed
@@ -378,6 +447,8 @@ bool GfxPalette::kernelAnimate(byte fromColor, byte toColor, int speed) {
scheduleCount++;
}
+ g_sci->getEngineState()->_throttleTrigger = true;
+
for (scheduleNr = 0; scheduleNr < scheduleCount; scheduleNr++) {
if (_schedules[scheduleNr].from == fromColor) {
if (_schedules[scheduleNr].schedule <= now) {
@@ -414,7 +485,21 @@ void GfxPalette::kernelAnimateSet() {
}
void GfxPalette::kernelAssertPalette(GuiResourceId resourceId) {
- warning("kAssertPalette %d", resourceId);
+ // Sometimes invalid viewIds are asked for, ignore those (e.g. qfg1vga)
+ //if (!_resMan->testResource(ResourceId(kResourceTypeView, resourceId)))
+ // return;
+ // maybe we took the wrong parameter before, if this causes invalid view again, enable to commented out code again
+
+ GfxView *view = g_sci->_gfxCache->getView(resourceId);
+ Palette *viewPalette = view->getPalette();
+ if (viewPalette) {
+ // merge/insert this palette
+ set(viewPalette, true);
+ }
+}
+
+void GfxPalette::kernelSyncScreenPalette() {
+ _screen->getPalette(&_sysPalette);
}
// palVary
@@ -443,40 +528,191 @@ void GfxPalette::kernelAssertPalette(GuiResourceId resourceId) {
// Saving/restoring
// need to save start and target-palette, when palVaryOn = true
-void GfxPalette::startPalVary(uint16 paletteId, uint16 ticks) {
- kernelSetFromResource(paletteId, true);
- return;
+void GfxPalette::palVaryInit() {
+ _palVaryResourceId = -1;
+ _palVaryPaused = 0;
+ _palVarySignal = 0;
+ _palVaryStep = 0;
+ _palVaryStepStop = 0;
+ _palVaryDirection = 0;
+ _palVaryTicks = 0;
+}
- if (_palVaryId >= 0) // another palvary is taking place, return
- return;
+bool GfxPalette::palVaryLoadTargetPalette(GuiResourceId resourceId) {
+ _palVaryResourceId = resourceId;
+ Resource *palResource = _resMan->findResource(ResourceId(kResourceTypePalette, resourceId), false);
+ if (palResource) {
+ // Load and initialize destination palette
+ createFromData(palResource->data, palResource->size, &_palVaryTargetPalette);
+ return true;
+ }
+ return false;
+}
+
+void GfxPalette::palVaryInstallTimer() {
+ int16 ticks = _palVaryTicks > 0 ? _palVaryTicks : 1;
+ // Call signal increase every [ticks]
+ g_sci->getTimerManager()->installTimerProc(&palVaryCallback, 1000000 / 60 * ticks, this);
+}
+
+void GfxPalette::palVaryRemoveTimer() {
+ g_sci->getTimerManager()->removeTimerProc(&palVaryCallback);
+}
+
+bool GfxPalette::kernelPalVaryInit(GuiResourceId resourceId, uint16 ticks, uint16 stepStop, uint16 direction) {
+ if (_palVaryResourceId != -1) // another palvary is taking place, return
+ return false;
+
+ if (palVaryLoadTargetPalette(resourceId)) {
+ // Save current palette
+ memcpy(&_palVaryOriginPalette, &_sysPalette, sizeof(Palette));
+
+ _palVarySignal = 0;
+ _palVaryTicks = ticks;
+ _palVaryStep = 1;
+ _palVaryStepStop = stepStop;
+ _palVaryDirection = direction;
+ // if no ticks are given, jump directly to destination
+ if (!_palVaryTicks)
+ _palVaryDirection = stepStop;
+ palVaryInstallTimer();
+ return true;
+ }
+ return false;
+}
+
+int16 GfxPalette::kernelPalVaryReverse(int16 ticks, uint16 stepStop, int16 direction) {
+ if (_palVaryResourceId == -1)
+ return 0;
+
+ if (_palVaryStep > 64)
+ _palVaryStep = 64;
+ if (ticks != -1)
+ _palVaryTicks = ticks;
+ _palVaryStepStop = stepStop;
+ _palVaryDirection = direction != -1 ? -direction : -_palVaryDirection;
+
+ if (!_palVaryTicks)
+ _palVaryDirection = _palVaryStepStop - _palVaryStep;
+ palVaryInstallTimer();
+ return kernelPalVaryGetCurrentStep();
+}
+
+int16 GfxPalette::kernelPalVaryGetCurrentStep() {
+ if (_palVaryDirection >= 0)
+ return _palVaryStep;
+ return -_palVaryStep;
+}
+
+int16 GfxPalette::kernelPalVaryChangeTarget(GuiResourceId resourceId) {
+ if (_palVaryResourceId != -1) {
+ Resource *palResource = _resMan->findResource(ResourceId(kResourceTypePalette, resourceId), false);
+ if (palResource) {
+ Palette insertPalette;
+ createFromData(palResource->data, palResource->size, &insertPalette);
+ // insert new palette into target
+ insert(&insertPalette, &_palVaryTargetPalette);
+ // update palette and set on screen
+ palVaryProcess(0, true);
+ }
+ }
+ return kernelPalVaryGetCurrentStep();
+}
- _palVaryId = paletteId;
- _palVaryStart = g_system->getMillis();
- _palVaryEnd = _palVaryStart + ticks * 1000 / 60;
- g_sci->getTimerManager()->installTimerProc(&palVaryCallback, 1000 / 60, this);
+void GfxPalette::kernelPalVaryChangeTicks(uint16 ticks) {
+ _palVaryTicks = ticks;
+ if (_palVaryStep - _palVaryStepStop) {
+ palVaryRemoveTimer();
+ palVaryInstallTimer();
+ }
}
-void GfxPalette::togglePalVary(bool pause) {
+void GfxPalette::kernelPalVaryPause(bool pause) {
+ if (_palVaryResourceId == -1)
+ return;
// this call is actually counting states, so calling this 3 times with true will require calling it later
// 3 times with false to actually remove pause
-
- // TODO
+ if (pause) {
+ _palVaryPaused++;
+ } else {
+ if (_palVaryPaused)
+ _palVaryPaused--;
+ }
}
-void GfxPalette::stopPalVary() {
- g_sci->getTimerManager()->removeTimerProc(&palVaryCallback);
- _palVaryId = -1; // invalidate the target palette
+void GfxPalette::kernelPalVaryDeinit() {
+ palVaryRemoveTimer();
- // HACK: just set the target palette
- kernelSetFromResource(_palVaryId, true);
+ _palVaryResourceId = -1; // invalidate the target palette
}
void GfxPalette::palVaryCallback(void *refCon) {
- ((GfxPalette *)refCon)->doPalVary();
+ ((GfxPalette *)refCon)->palVaryIncreaseSignal();
+}
+
+void GfxPalette::palVaryIncreaseSignal() {
+ if (!_palVaryPaused)
+ _palVarySignal++;
+}
+
+// Actually do the pal vary processing
+void GfxPalette::palVaryUpdate() {
+ if (_palVarySignal) {
+ palVaryProcess(_palVarySignal, true);
+ _palVarySignal = 0;
+ }
+}
+
+void GfxPalette::palVaryPrepareForTransition() {
+ if (_palVaryResourceId != -1) {
+ // Before doing transitions, we have to prepare palette
+ palVaryProcess(0, false);
+ }
}
-void GfxPalette::doPalVary() {
- // TODO: do palette transition here...
+// Processes pal vary updates
+void GfxPalette::palVaryProcess(int signal, bool setPalette) {
+ int16 stepChange = signal * _palVaryDirection;
+
+ _palVaryStep += stepChange;
+ if (stepChange > 0) {
+ if (_palVaryStep > _palVaryStepStop)
+ _palVaryStep = _palVaryStepStop;
+ } else {
+ if (_palVaryStep < _palVaryStepStop) {
+ if (signal)
+ _palVaryStep = _palVaryStepStop;
+ }
+ }
+
+ // We don't need updates anymore, if we reached end-position
+ if (_palVaryStep == _palVaryStepStop)
+ palVaryRemoveTimer();
+ if (_palVaryStep == 0)
+ _palVaryResourceId = -1;
+
+ // Calculate inbetween palette
+ Sci::Color inbetween;
+ int16 color;
+ for (int colorNr = 1; colorNr < 255; colorNr++) {
+ inbetween.used = _sysPalette.colors[colorNr].used;
+ color = _palVaryTargetPalette.colors[colorNr].r - _palVaryOriginPalette.colors[colorNr].r;
+ inbetween.r = ((color * _palVaryStep) / 64) + _palVaryOriginPalette.colors[colorNr].r;
+ color = _palVaryTargetPalette.colors[colorNr].g - _palVaryOriginPalette.colors[colorNr].g;
+ inbetween.g = ((color * _palVaryStep) / 64) + _palVaryOriginPalette.colors[colorNr].g;
+ color = _palVaryTargetPalette.colors[colorNr].b - _palVaryOriginPalette.colors[colorNr].b;
+ inbetween.b = ((color * _palVaryStep) / 64) + _palVaryOriginPalette.colors[colorNr].b;
+
+ if (memcmp(&inbetween, &_sysPalette.colors[colorNr], sizeof(Sci::Color))) {
+ _sysPalette.colors[colorNr] = inbetween;
+ _sysPaletteChanged = true;
+ }
+ }
+
+ if ((_sysPaletteChanged) && (setPalette) && (_screen->_picNotValid == 0)) {
+ setOnScreen();
+ _sysPaletteChanged = false;
+ }
}
} // End of namespace Sci
diff --git a/engines/sci/graphics/palette.h b/engines/sci/graphics/palette.h
index 46fec48739..6af1d5a490 100644
--- a/engines/sci/graphics/palette.h
+++ b/engines/sci/graphics/palette.h
@@ -36,20 +36,26 @@ class Screen;
*/
class GfxPalette {
public:
- GfxPalette(ResourceManager *resMan, GfxScreen *screen, bool autoSetPalette = true);
+ GfxPalette(ResourceManager *resMan, GfxScreen *screen, bool useMerging);
~GfxPalette();
- void createFromData(byte *data, Palette *paletteOut);
+ bool isMerging();
+
+ void setDefault();
+ void createFromData(byte *data, int bytesLeft, Palette *paletteOut);
bool setAmiga();
void modifyAmigaPalette(byte *data);
void setEGA();
void set(Palette *sciPal, bool force, bool forceRealMerge = false);
- bool merge(Palette *pFrom, Palette *pTo, bool force, bool forceRealMerge);
- uint16 matchColor(Palette *pPal, byte r, byte g, byte b);
+ bool insert(Palette *newPalette, Palette *destPalette);
+ bool merge(Palette *pFrom, bool force, bool forceRealMerge);
+ uint16 matchColor(byte r, byte g, byte b);
void getSys(Palette *pal);
void setOnScreen();
+ void drewPicture(GuiResourceId pictureId);
+
bool kernelSetFromResource(GuiResourceId resourceId, bool force);
void kernelSetFlag(uint16 fromColor, uint16 toColor, uint16 flag);
void kernelUnsetFlag(uint16 fromColor, uint16 toColor, uint16 flag);
@@ -59,25 +65,46 @@ public:
void kernelAnimateSet();
void kernelAssertPalette(GuiResourceId resourceId);
- void startPalVary(uint16 paletteId, uint16 ticks);
- void togglePalVary(bool pause);
- void stopPalVary();
+ void kernelSyncScreenPalette();
+
+ bool kernelPalVaryInit(GuiResourceId resourceId, uint16 ticks, uint16 stepStop, uint16 direction);
+ int16 kernelPalVaryReverse(int16 ticks, uint16 stepStop, int16 direction);
+ int16 kernelPalVaryGetCurrentStep();
+ int16 kernelPalVaryChangeTarget(GuiResourceId resourceId);
+ void kernelPalVaryChangeTicks(uint16 ticks);
+ void kernelPalVaryPause(bool pause);
+ void kernelPalVaryDeinit();
+ void palVaryUpdate();
+ void palVaryPrepareForTransition();
+ void palVaryProcess(int signal, bool setPalette);
Palette _sysPalette;
private:
+ void palVaryInit();
+ void palVaryInstallTimer();
+ void palVaryRemoveTimer();
+ bool palVaryLoadTargetPalette(GuiResourceId resourceId);
static void palVaryCallback(void *refCon);
- void doPalVary();
+ void palVaryIncreaseSignal();
GfxScreen *_screen;
ResourceManager *_resMan;
- int16 _palVaryId;
- uint32 _palVaryStart;
- uint32 _palVaryEnd;
bool _sysPaletteChanged;
+ bool _useMerging;
Common::Array<PalSchedule> _schedules;
+
+ GuiResourceId _palVaryResourceId;
+ Palette _palVaryOriginPalette;
+ Palette _palVaryTargetPalette;
+ int16 _palVaryStep;
+ int16 _palVaryStepStop;
+ int16 _palVaryDirection;
+ uint16 _palVaryTicks;
+ int _palVaryPaused;
+ int _palVarySignal;
};
} // End of namespace Sci
diff --git a/engines/sci/graphics/picture.cpp b/engines/sci/graphics/picture.cpp
index a59153f116..7cd37b6f46 100644
--- a/engines/sci/graphics/picture.cpp
+++ b/engines/sci/graphics/picture.cpp
@@ -109,7 +109,7 @@ void GfxPicture::drawSci11Vga() {
Palette palette;
// Create palette and set it
- _palette->createFromData(inbuffer + palette_data_ptr, &palette);
+ _palette->createFromData(inbuffer + palette_data_ptr, size - palette_data_ptr, &palette);
_palette->set(&palette, true);
// display Cel-data
@@ -147,7 +147,7 @@ void GfxPicture::drawSci32Vga(int16 celNo) {
if ((celNo == -1) || (celNo == 0)) {
// Create palette and set it
- _palette->createFromData(inbuffer + palette_data_ptr, &palette);
+ _palette->createFromData(inbuffer + palette_data_ptr, size - palette_data_ptr, &palette);
_palette->set(&palette, true);
}
if (celNo != -1) {
@@ -437,7 +437,7 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) {
memcpy(&EGApalettes[i], &vector_defaultEGApalette, sizeof(vector_defaultEGApalette));
memcpy(&EGApriority, &vector_defaultEGApriority, sizeof(vector_defaultEGApriority));
- if (strcmp(g_sci->getGameID(), "iceman") == 0) {
+ if (g_sci->getGameId() == GID_ICEMAN) {
// WORKAROUND: we remove certain visual&priority lines in underwater rooms of iceman, when not dithering the
// picture. Normally those lines aren't shown, because they share the same color as the dithered
// fill color combination. When not dithering, those lines would appear and get distracting.
@@ -532,7 +532,7 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) {
// inside picture data for such games
case PIC_OP_SET_PATTERN:
if (_resourceType >= SCI_PICTURE_TYPE_SCI11) {
- if (strcmp(g_sci->getGameID(), "sq4") == 0) {
+ if (g_sci->getGameId() == GID_SQ4) {
// 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
diff --git a/engines/sci/graphics/portrait.cpp b/engines/sci/graphics/portrait.cpp
index 1208f8ed65..8f4fe094a8 100644
--- a/engines/sci/graphics/portrait.cpp
+++ b/engines/sci/graphics/portrait.cpp
@@ -23,6 +23,7 @@
*
*/
+#include "common/archive.h"
#include "common/util.h"
#include "common/stack.h"
#include "graphics/primitives.h"
@@ -30,7 +31,6 @@
#include "sci/sci.h"
#include "sci/event.h"
#include "sci/engine/state.h"
-#include "sci/graphics/gui.h"
#include "sci/graphics/screen.h"
#include "sci/graphics/palette.h"
#include "sci/graphics/portrait.h"
@@ -38,8 +38,8 @@
namespace Sci {
-Portrait::Portrait(ResourceManager *resMan, SciEvent *event, SciGui *gui, GfxScreen *screen, GfxPalette *palette, AudioPlayer *audio, Common::String resourceName)
- : _resMan(resMan), _event(event), _gui(gui), _screen(screen), _palette(palette), _audio(audio), _resourceName(resourceName) {
+Portrait::Portrait(ResourceManager *resMan, EventManager *event, GfxScreen *screen, GfxPalette *palette, AudioPlayer *audio, Common::String resourceName)
+ : _resMan(resMan), _event(event), _screen(screen), _palette(palette), _audio(audio), _resourceName(resourceName) {
init();
}
@@ -166,7 +166,7 @@ void Portrait::doit(Common::Point position, uint16 resourceId, uint16 noun, uint
// Do animation depending on sync resource till audio is done playing
uint16 syncCue;
int timerPosition, curPosition;
- sciEvent curEvent;
+ SciEvent curEvent;
bool userAbort = false;
while ((syncOffset < syncResource->size - 2) && (!userAbort)) {
@@ -181,8 +181,8 @@ void Portrait::doit(Common::Point position, uint16 resourceId, uint16 noun, uint
// Wait till syncTime passed, then show specific animation bitmap
do {
- _gui->wait(1);
- curEvent = _event->get(SCI_EVENT_ANY);
+ g_sci->getEngineState()->wait(1);
+ curEvent = _event->getSciEvent(SCI_EVENT_ANY);
if (curEvent.type == SCI_EVENT_MOUSE_PRESS ||
(curEvent.type == SCI_EVENT_KEYBOARD && curEvent.data == SCI_KEY_ESC) ||
g_engine->shouldQuit())
diff --git a/engines/sci/graphics/portrait.h b/engines/sci/graphics/portrait.h
index 4b22e209a3..7da9425c9d 100644
--- a/engines/sci/graphics/portrait.h
+++ b/engines/sci/graphics/portrait.h
@@ -42,7 +42,7 @@ struct PortraitBitmap {
*/
class Portrait {
public:
- Portrait(ResourceManager *resMan, SciEvent *event, SciGui *gui, GfxScreen *screen, GfxPalette *palette, AudioPlayer *audio, Common::String resourceName);
+ Portrait(ResourceManager *resMan, EventManager *event, GfxScreen *screen, GfxPalette *palette, AudioPlayer *audio, Common::String resourceName);
~Portrait();
void setupAudio(uint16 resourceId, uint16 noun, uint16 verb, uint16 cond, uint16 seq);
@@ -56,8 +56,7 @@ private:
void bitsShow();
ResourceManager *_resMan;
- SciEvent *_event;
- SciGui *_gui;
+ EventManager *_event;
GfxPalette *_palette;
GfxScreen *_screen;
AudioPlayer *_audio;
diff --git a/engines/sci/graphics/ports.cpp b/engines/sci/graphics/ports.cpp
index cdb6fe4ae1..611db1061a 100644
--- a/engines/sci/graphics/ports.cpp
+++ b/engines/sci/graphics/ports.cpp
@@ -54,11 +54,10 @@ GfxPorts::~GfxPorts() {
delete _menuPort;
}
-void GfxPorts::init(bool usesOldGfxFunctions, SciGui *gui, GfxPaint16 *paint16, GfxText16 *text16) {
+void GfxPorts::init(bool usesOldGfxFunctions, GfxPaint16 *paint16, GfxText16 *text16) {
int16 offTop = 10;
_usesOldGfxFunctions = usesOldGfxFunctions;
- _gui = gui;
_paint16 = paint16;
_text16 = text16;
@@ -85,20 +84,44 @@ void GfxPorts::init(bool usesOldGfxFunctions, SciGui *gui, GfxPaint16 *paint16,
else
_styleUser = SCI_WINDOWMGR_STYLE_USER | SCI_WINDOWMGR_STYLE_TRANSPARENT;
- // Jones, Slater and Hoyle 3 were called with parameter -Nw 0 0 200 320.
- // Mother Goose (SCI1) uses -Nw 0 0 159 262. The game will later use SetPort so we don't need to set the other fields.
+ // Jones, Slater, Hoyle 3&4 and Crazy Nicks Laura Bow/Kings Quest were
+ // called with parameter -Nw 0 0 200 320.
+ // Mother Goose (SCI1) uses -Nw 0 0 159 262. The game will later use
+ // SetPort so we don't need to set the other fields.
// This actually meant not skipping the first 10 pixellines in windowMgrPort
- Common::String gameId = g_sci->getGameID();
- if (gameId == "jones" || gameId == "slater" || gameId == "hoyle3" || (gameId == "mothergoose" && getSciVersion() == SCI_VERSION_1_EARLY))
+ switch (g_sci->getGameId()) {
+ case GID_JONES:
+ case GID_SLATER:
+ case GID_HOYLE3:
+ case GID_HOYLE4:
+ case GID_CNICK_LAURABOW:
+ case GID_CNICK_KQ:
offTop = 0;
+ break;
+ case GID_MOTHERGOOSE:
+ if (getSciVersion() == SCI_VERSION_1_EARLY)
+ offTop = 0;
+ break;
+ case GID_FAIRYTALES:
+ // Mixed-Up Fairy Tales (& its demo) uses -w 26 0 200 320. If we don't
+ // also do this we will get not-fully-removed windows everywhere.
+ offTop = 26;
+ break;
+ default:
+ offTop = 10;
+ break;
+ }
openPort(_wmgrPort);
setPort(_wmgrPort);
// SCI0 games till kq4 (.502 - not including) did not adjust against _wmgrPort in kNewWindow
// We leave _wmgrPort top at 0, so the adjustment wont get done
- if (!g_sci->_features->usesOldGfxFunctions())
+ if (!g_sci->_features->usesOldGfxFunctions()) {
setOrigin(0, offTop);
- _wmgrPort->rect.bottom = _screen->getHeight() - offTop;
+ _wmgrPort->rect.bottom = _screen->getHeight() - offTop;
+ } else {
+ _wmgrPort->rect.bottom = _screen->getHeight();
+ }
_wmgrPort->rect.right = _screen->getWidth();
_wmgrPort->rect.moveTo(0, 0);
_wmgrPort->curTop = 0;
@@ -214,7 +237,7 @@ Window *GfxPorts::newWindow(const Common::Rect &dims, const Common::Rect *restor
Common::Rect r;
if (!pwnd) {
- warning("Can't open window!");
+ error("Can't open window!");
return 0;
}
@@ -228,6 +251,7 @@ Window *GfxPorts::newWindow(const Common::Rect &dims, const Common::Rect *restor
r = dims;
if (r.width() > _screen->getWidth()) {
// We get invalid dimensions at least at the end of sq3 (script bug!)
+ // same happens very often in lsl5, sierra sci didnt fix it but it looked awful
warning("fixing too large window, given left&right was %d, %d", dims.left, dims.right);
r.left = 0;
r.right = _screen->getWidth() - 1;
@@ -496,6 +520,11 @@ void GfxPorts::priorityBandsInit(int16 bandCount, int16 top, int16 bottom) {
// We fill space that is left over with the highest band (hardcoded 200 limit, because this algo isnt meant to be used on hires)
for (y = _priorityBottom; y < 200; y++)
_priorityBands[y] = _priorityBandCount;
+
+ // adjust, if bottom is 200 (one over the actual screen range) - we could otherwise go possible out of bounds
+ // sierra sci also adjust accordingly
+ if (_priorityBottom == 200)
+ _priorityBottom--;
}
void GfxPorts::priorityBandsInit(byte *data) {
diff --git a/engines/sci/graphics/ports.h b/engines/sci/graphics/ports.h
index c8ce6b3470..818f92f44f 100644
--- a/engines/sci/graphics/ports.h
+++ b/engines/sci/graphics/ports.h
@@ -45,7 +45,7 @@ public:
GfxPorts(SegManager *segMan, GfxScreen *screen);
~GfxPorts();
- void init(bool usesOldGfxFunctions, SciGui *gui, GfxPaint16 *paint16, GfxText16 *text16);
+ void init(bool usesOldGfxFunctions, GfxPaint16 *paint16, GfxText16 *text16);
void kernelSetActive(uint16 portId);
Common::Rect kernelGetPicWindow(int16 &picTop, int16 &picLeft);
@@ -102,7 +102,6 @@ private:
typedef Common::List<Port *> PortList;
SegManager *_segMan;
- SciGui *_gui;
GfxPaint16 *_paint16;
GfxScreen *_screen;
GfxText16 *_text16;
diff --git a/engines/sci/graphics/robot.cpp b/engines/sci/graphics/robot.cpp
index 2f711eb58a..d926e037f4 100644
--- a/engines/sci/graphics/robot.cpp
+++ b/engines/sci/graphics/robot.cpp
@@ -31,17 +31,17 @@
namespace Sci {
#ifdef ENABLE_SCI32
-Robot::Robot(ResourceManager *resMan, GfxScreen *screen, GuiResourceId resourceId)
+GfxRobot::GfxRobot(ResourceManager *resMan, GfxScreen *screen, GuiResourceId resourceId)
: _resMan(resMan), _screen(screen), _resourceId(resourceId) {
assert(resourceId != -1);
initData(resourceId);
}
-Robot::~Robot() {
+GfxRobot::~GfxRobot() {
_resMan->unlockResource(_resource);
}
-void Robot::initData(GuiResourceId resourceId) {
+void GfxRobot::initData(GuiResourceId resourceId) {
_resource = _resMan->findResource(ResourceId(kResourceTypeRobot, resourceId), true);
if (!_resource) {
error("robot resource %d not found", resourceId);
@@ -158,7 +158,7 @@ void Robot::initData(GuiResourceId resourceId) {
// TODO: just trying around in here...
-void Robot::draw() {
+void GfxRobot::draw() {
byte *bitmapData = _resourceData + ROBOT_FILE_STARTOFDATA;
int x, y;
//int frame;
diff --git a/engines/sci/graphics/robot.h b/engines/sci/graphics/robot.h
index 009b76a91c..80dae95f95 100644
--- a/engines/sci/graphics/robot.h
+++ b/engines/sci/graphics/robot.h
@@ -31,10 +31,10 @@ namespace Sci {
#define ROBOT_FILE_STARTOFDATA 58
#ifdef ENABLE_SCI32
-class Robot {
+class GfxRobot {
public:
- Robot(ResourceManager *resMan, GfxScreen *screen, GuiResourceId resourceId);
- ~Robot();
+ GfxRobot(ResourceManager *resMan, GfxScreen *screen, GuiResourceId resourceId);
+ ~GfxRobot();
void draw();
diff --git a/engines/sci/graphics/screen.cpp b/engines/sci/graphics/screen.cpp
index 0e054d5a76..488bb83ab3 100644
--- a/engines/sci/graphics/screen.cpp
+++ b/engines/sci/graphics/screen.cpp
@@ -35,8 +35,35 @@
namespace Sci {
-GfxScreen::GfxScreen(ResourceManager *resMan, int16 width, int16 height, int upscaledHires) :
- _resMan(resMan), _width(width), _height(height), _upscaledHires(upscaledHires) {
+GfxScreen::GfxScreen(ResourceManager *resMan) : _resMan(resMan) {
+
+ // Scale the screen, if needed
+ _upscaledHires = GFX_SCREEN_UPSCALED_DISABLED;
+
+ // King's Quest 6 and Gabriel Knight 1 have hires content, gk1/cd was able
+ // to provide that under DOS as well, but as gk1/floppy does support
+ // upscaled hires scriptswise, but doesn't actually have the hires content
+ // we need to limit it to platform windows.
+ if (g_sci->getPlatform() == Common::kPlatformWindows) {
+ if (g_sci->getGameId() == GID_KQ6)
+ _upscaledHires = GFX_SCREEN_UPSCALED_640x440;
+#ifdef ENABLE_SCI32
+ if (g_sci->getGameId() == GID_GK1)
+ _upscaledHires = GFX_SCREEN_UPSCALED_640x480;
+#endif
+ }
+
+ if (_resMan->detectHires()) {
+ _width = 640;
+ _height = 480;
+ } else {
+ _width = 320;
+ _height = 200;
+ }
+
+ // Japanese versions of games use hi-res font on upscaled version of the game.
+ if ((g_sci->getLanguage() == Common::JA_JPN) && (getSciVersion() <= SCI_VERSION_1_1))
+ _upscaledHires = GFX_SCREEN_UPSCALED_640x400;
_pixels = _width * _height;
@@ -80,8 +107,9 @@ GfxScreen::GfxScreen(ResourceManager *resMan, int16 width, int16 height, int ups
_unditherState = true;
if (_resMan->isVGA() || (_resMan->isAmiga32color())) {
- // It's not 100% accurate to set white to be 255 for amiga 32-color games
- // 255 is defined as white in our sci at all times, so it doesnt matter
+ // It is not 100% accurate to set white to be 255 for Amiga 32-color
+ // games. But 255 is defined as white in our SCI at all times, so it
+ // doesn't matter.
_colorWhite = 255;
if (getSciVersion() >= SCI_VERSION_1_1)
_colorDefaultVectorData = 255;
@@ -97,9 +125,9 @@ GfxScreen::GfxScreen(ResourceManager *resMan, int16 width, int16 height, int ups
if (_resMan->isSci11Mac() && getSciVersion() == SCI_VERSION_1_1) {
// For SCI1.1 Mac, we need to expand the screen to accommodate for
// the icon bar. Of course, both KQ6 and QFG1 VGA differ in size.
- if (!scumm_stricmp(g_sci->getGameID(), "kq6"))
+ if (g_sci->getGameId() == GID_KQ6)
initGraphics(_displayWidth, _displayHeight + 26, _displayWidth > 320);
- else if (!scumm_stricmp(g_sci->getGameID(), "qfg1"))
+ else if (g_sci->getGameId() == GID_QFG1)
initGraphics(_displayWidth, _displayHeight + 20, _displayWidth > 320);
else
error("Unknown SCI1.1 Mac game");
@@ -141,7 +169,10 @@ void GfxScreen::copyRectToScreen(const Common::Rect &rect) {
}
}
-// This copies a rect to screen w/o scaling adjustment and is only meant to be used on hires graphics used in upscaled hires mode
+/**
+ * This copies a rect to screen w/o scaling adjustment and is only meant to be
+ * used on hires graphics used in upscaled hires mode.
+ */
void GfxScreen::copyDisplayRectToScreen(const Common::Rect &rect) {
if (!_upscaledHires)
error("copyDisplayRectToScreen: not in upscaled hires mode");
@@ -192,15 +223,42 @@ void GfxScreen::putPixel(int x, int y, byte drawMask, byte color, byte priority,
_controlScreen[offset] = control;
}
-// This will just change a pixel directly on displayscreen. Its supposed to get only used on upscaled-Hires games where
-// hires content needs to get drawn ONTO the upscaled display screen (like japanese fonts, hires portraits, etc.)
+/**
+ * This is used to put font pixels onto the screen - we adjust differently, so that we won't
+ * do triple pixel lines in any case on upscaled hires. That way the font will not get distorted
+ * Sierra SCI didn't do this
+ */
+void GfxScreen::putFontPixel(int startingY, int x, int y, byte color) {
+ int offset = (startingY + y) * _width + x;
+
+ _visualScreen[offset] = color;
+ if (!_upscaledHires) {
+ _displayScreen[offset] = color;
+ } else {
+ int displayOffset = (_upscaledMapping[startingY] + y * 2) * _displayWidth + x * 2;
+ _displayScreen[displayOffset] = color;
+ _displayScreen[displayOffset + 1] = color;
+ displayOffset += _displayWidth;
+ _displayScreen[displayOffset] = color;
+ _displayScreen[displayOffset + 1] = color;
+ }
+}
+
+/**
+ * This will just change a pixel directly on displayscreen. It is supposed to be
+ * only used on upscaled-Hires games where hires content needs to get drawn ONTO
+ * the upscaled display screen (like japanese fonts, hires portraits, etc.).
+ */
void GfxScreen::putPixelOnDisplay(int x, int y, byte color) {
int offset = y * _displayWidth + x;
_displayScreen[offset] = color;
}
-// Sierra's Bresenham line drawing
-// WARNING: Do not just blindly replace this with Graphics::drawLine(), as it seems to create issues with flood fill
+/**
+ * Sierra's Bresenham line drawing.
+ * WARNING: Do not replace this with Graphics::drawLine(), as this causes issues
+ * with flood fill, due to small difference in the Bresenham logic.
+ */
void GfxScreen::drawLine(Common::Point startPoint, Common::Point endPoint, byte color, byte priority, byte control) {
int16 left = startPoint.x;
int16 top = startPoint.y;
@@ -263,7 +321,8 @@ void GfxScreen::drawLine(Common::Point startPoint, Common::Point endPoint, byte
}
}
-// We put hires kanji chars onto upscaled background, so we need to adjust coordinates. Caller gives use low-res ones
+// We put hires kanji chars onto upscaled background, so we need to adjust
+// coordinates. Caller gives use low-res ones.
void GfxScreen::putKanjiChar(Graphics::FontSJIS *commonFont, int16 x, int16 y, uint16 chr, byte color) {
byte *displayPtr = _displayScreen + y * _displayWidth * 2 + x * 2;
// we don't use outline, so color 0 is actually not used
@@ -400,6 +459,11 @@ void GfxScreen::bitsRestore(byte *memoryPtr) {
if (!_upscaledHires)
error("bitsRestore() called w/o being in upscaled hires mode");
bitsRestoreScreen(rect, memoryPtr, _displayScreen, _displayWidth);
+ // WORKAROUND - we are not sure what sierra is doing. If we don't do this here, portraits won't get fully removed
+ // from screen. Some lowres showBits() call is used for that and it's not covering the whole area
+ // We would need to find out inside the kq6 windows interpreter, but this here works already and seems not to have
+ // any side-effects. The whole hires is hacked into the interpreter, so maybe this is even right.
+ copyDisplayRectToScreen(rect);
}
}
@@ -435,7 +499,19 @@ void GfxScreen::bitsRestoreDisplayScreen(Common::Rect rect, byte *&memoryPtr) {
}
}
-void GfxScreen::setPalette(Palette*pal) {
+void GfxScreen::getPalette(Palette *pal) {
+ // just copy palette to system
+ byte bpal[4 * 256];
+ // Get current palette, update it and put back
+ g_system->grabPalette(bpal, 0, 256);
+ for (int16 i = 1; i < 255; i++) {
+ pal->colors[i].r = bpal[i * 4];
+ pal->colors[i].g = bpal[i * 4 + 1];
+ pal->colors[i].b = bpal[i * 4 + 2];
+ }
+}
+
+void GfxScreen::setPalette(Palette *pal) {
// just copy palette to system
byte bpal[4 * 256];
// Get current palette, update it and put back
@@ -557,19 +633,20 @@ void GfxScreen::debugShowMap(int mapNo) {
copyToScreen();
}
-void GfxScreen::scale2x(byte *src, byte *dst, int16 srcWidth, int16 srcHeight) {
- int newWidth = srcWidth * 2;
- byte *srcPtr = src;
+void GfxScreen::scale2x(const byte *src, byte *dst, int16 srcWidth, int16 srcHeight) {
+ const int newWidth = srcWidth * 2;
+ const byte *srcPtr = src;
for (int y = 0; y < srcHeight; y++) {
for (int x = 0; x < srcWidth; x++) {
- int destOffset = y * 2 * newWidth + x * 2;
- dst[destOffset] = *srcPtr;
- dst[destOffset + 1] = *srcPtr;
- dst[destOffset + newWidth] = *srcPtr;
- dst[destOffset + newWidth + 1] = *srcPtr;
- srcPtr++;
+ const byte color = *srcPtr++;
+ dst[0] = color;
+ dst[1] = color;
+ dst[newWidth] = color;
+ dst[newWidth + 1] = color;
+ dst += 2;
}
+ dst += newWidth;
}
}
@@ -578,6 +655,24 @@ void GfxScreen::adjustToUpscaledCoordinates(int16 &y, int16 &x) {
y = _upscaledMapping[y];
}
+void GfxScreen::adjustBackUpscaledCoordinates(int16 &y, int16 &x) {
+ switch (_upscaledHires) {
+ case GFX_SCREEN_UPSCALED_640x400:
+ x /= 2;
+ y /= 2;
+ break;
+ case GFX_SCREEN_UPSCALED_640x440:
+ x /= 2;
+ y = (y * 5) / 11;
+ break;
+ case GFX_SCREEN_UPSCALED_640x480:
+ x /= 2;
+ y = (y * 5) / 12;
+ default:
+ break;
+ }
+}
+
int16 GfxScreen::kernelPicNotValid(int16 newPicNotValid) {
int16 oldPicNotValid;
diff --git a/engines/sci/graphics/screen.h b/engines/sci/graphics/screen.h
index b2479e9735..f1e3d028a8 100644
--- a/engines/sci/graphics/screen.h
+++ b/engines/sci/graphics/screen.h
@@ -50,16 +50,21 @@ enum GfxScreenMasks {
GFX_SCREEN_MASK_ALL = GFX_SCREEN_MASK_VISUAL|GFX_SCREEN_MASK_PRIORITY|GFX_SCREEN_MASK_CONTROL
};
-#define SCI_SCREEN_UNDITHERMEMORIAL_SIZE 256
+enum {
+ SCI_SCREEN_UNDITHERMEMORIAL_SIZE = 256
+};
/**
- * Screen class, actually creates 3 (4) screens internally - which is visual/display (for the user),
- * priority (contains priority information) and control (contains control information). Handles all operations to it
- * and copies parts of visual/display screen to the actual screen, so the user can really see it.
+ * Screen class, actually creates 3 (4) screens internally:
+ * - visual/display (for the user),
+ * - priority (contains priority information) and
+ * - control (contains control information).
+ * Handles all operations to it and copies parts of visual/display screen to
+ * the actual screen, so the user can really see it.
*/
class GfxScreen {
public:
- GfxScreen(ResourceManager *resMan, int16 width = 320, int16 height = 200, int upscaledHires = GFX_SCREEN_UPSCALED_DISABLED);
+ GfxScreen(ResourceManager *resMan);
~GfxScreen();
uint16 getWidth() { return _width; }
@@ -78,15 +83,16 @@ public:
byte getDrawingMask(byte color, byte prio, byte control);
void putPixel(int x, int y, byte drawMask, byte color, byte prio, byte control);
+ void putFontPixel(int startingY, int x, int y, byte color);
void putPixelOnDisplay(int x, int y, byte color);
void drawLine(Common::Point startPoint, Common::Point endPoint, byte color, byte prio, byte control);
void drawLine(int16 left, int16 top, int16 right, int16 bottom, byte color, byte prio, byte control) {
drawLine(Common::Point(left, top), Common::Point(right, bottom), color, prio, control);
}
- int getUpscaledHires() {
+ int getUpscaledHires() const {
return _upscaledHires;
}
- bool getUnditherState() {
+ bool getUnditherState() const {
return _unditherState;
}
void putKanjiChar(Graphics::FontSJIS *commonFont, int16 x, int16 y, uint16 chr, byte color);
@@ -100,13 +106,15 @@ public:
void bitsGetRect(byte *memoryPtr, Common::Rect *destRect);
void bitsRestore(byte *memoryPtr);
- void setPalette(Palette*pal);
+ void getPalette(Palette *pal);
+ void setPalette(Palette *pal);
void setVerticalShakePos(uint16 shakePos);
- void scale2x(byte *src, byte *dst, int16 srcWidth, int16 srcHeight);
+ void scale2x(const byte *src, byte *dst, int16 srcWidth, int16 srcHeight);
void adjustToUpscaledCoordinates(int16 &y, int16 &x);
+ void adjustBackUpscaledCoordinates(int16 &y, int16 &x);
void dither(bool addToFlag);
void debugUnditherSetState(bool flag);
@@ -138,27 +146,34 @@ private:
bool _unditherState;
int16 _unditherMemorial[SCI_SCREEN_UNDITHERMEMORIAL_SIZE];
- // these screens have the real resolution of the game engine (320x200 for SCI0/SCI1/SCI11 games, 640x480 for SCI2 games)
- // SCI0 games will be dithered in here at any time
+ // These screens have the real resolution of the game engine (320x200 for
+ // SCI0/SCI1/SCI11 games, 640x480 for SCI2 games). SCI0 games will be
+ // dithered in here at any time.
byte *_visualScreen;
byte *_priorityScreen;
byte *_controlScreen;
- // this screen is the one that is actually displayed to the user. It may be 640x400 for japanese SCI1 games
- // SCI0 games may be undithered in here. Only read from this buffer for Save/ShowBits usage.
+ // This screen is the one that is actually displayed to the user. It may be
+ // 640x400 for japanese SCI1 games. SCI0 games may be undithered in here.
+ // Only read from this buffer for Save/ShowBits usage.
byte *_displayScreen;
Common::Rect getScaledRect(Common::Rect rect);
ResourceManager *_resMan;
- // this is a pointer to the currently active screen (changing it only required for debug purposes)
+ /**
+ * Pointer to the currently active screen (changing it only required for
+ * debug purposes).
+ */
byte *_activeScreen;
- // this variable defines, if upscaled hires is active and what upscaled mode is used
+ // This variable defines, if upscaled hires is active and what upscaled mode
+ // is used.
int _upscaledHires;
- // this here holds a translation for vertical coordinates between native (visual) and actual (display) screen
+ // This here holds a translation for vertical coordinates between native
+ // (visual) and actual (display) screen.
int _upscaledMapping[SCI_SCREEN_UPSCALEDMAXHEIGHT + 1];
};
diff --git a/engines/sci/graphics/text16.cpp b/engines/sci/graphics/text16.cpp
index 952d13fbbd..4aa2346aed 100644
--- a/engines/sci/graphics/text16.cpp
+++ b/engines/sci/graphics/text16.cpp
@@ -73,28 +73,6 @@ void GfxText16::SetFont(GuiResourceId fontId) {
_ports->_curPort->fontHeight = _font->getHeight();
}
-void GfxText16::CodeSetFonts(int argc, reg_t *argv) {
- int i;
-
- delete _codeFonts;
- _codeFontsCount = argc;
- _codeFonts = new GuiResourceId[argc];
- for (i = 0; i < argc; i++) {
- _codeFonts[i] = (GuiResourceId)argv[i].toUint16();
- }
-}
-
-void GfxText16::CodeSetColors(int argc, reg_t *argv) {
- int i;
-
- delete _codeColors;
- _codeColorsCount = argc;
- _codeColors = new uint16[argc];
- for (i = 0; i < argc; i++) {
- _codeColors[i] = argv[i].toUint16();
- }
-}
-
void GfxText16::ClearChar(int16 chr) {
if (_ports->_curPort->penMode != 1)
return;
@@ -106,10 +84,10 @@ void GfxText16::ClearChar(int16 chr) {
_paint16->eraseRect(rect);
}
-// This internal function gets called as soon as a '|' is found in a text
-// It will process the encountered code and set new font/set color
-// We only support one-digit codes currently, don't know if multi-digit codes are possible
-// Returns textcode character count
+// This internal function gets called as soon as a '|' is found in a text. It
+// will process the encountered code and set new font/set color. We only support
+// one-digit codes currently, don't know if multi-digit codes are possible.
+// Returns textcode character count.
int16 GfxText16::CodeProcessing(const char *&text, GuiResourceId orgFontId, int16 orgPenColor) {
const char *textCode = text;
int16 textCodeSize = 0;
@@ -155,9 +133,11 @@ int16 GfxText16::CodeProcessing(const char *&text, GuiResourceId orgFontId, int1
static const uint16 text16_punctuationSjis[] = {
0x9F82, 0xA182, 0xA382, 0xA582, 0xA782, 0xC182, 0xA782, 0xC182, 0xE182, 0xE382, 0xE582, 0xEC82,
0x4083, 0x4283, 0x4483, 0x4683, 0x4883, 0x6283, 0x8383, 0x8583, 0x8783, 0x8E83, 0x9583, 0x9683,
- 0x5B81, 0x4181, 0x4281, 0x7681, 0x7881, 0x4981, 0x4881, 0 };
+ 0x5B81, 0x4181, 0x4281, 0x7681, 0x7881, 0x4981, 0x4881, 0
+};
-// return max # of chars to fit maxwidth with full words, does not include breaking space
+// return max # of chars to fit maxwidth with full words, does not include
+// breaking space
int16 GfxText16::GetLongest(const char *text, int16 maxWidth, GuiResourceId orgFontId) {
uint16 curChar = 0;
int16 maxChars = 0, curCharCount = 0;
@@ -217,9 +197,10 @@ int16 GfxText16::GetLongest(const char *text, int16 maxWidth, GuiResourceId orgF
uint16 nextChar;
- // we remove the last char only, if maxWidth was actually equal width before adding the last char
- // otherwise we won't get the same cutting as in sierra pc98 sci
- // note: changing the while() instead will NOT WORK. it would break all sorts of regular sci games
+ // We remove the last char only, if maxWidth was actually equal width
+ // before adding the last char. Otherwise we won't get the same cutting
+ // as in sierra pc98 sci. Note: changing the while() instead will NOT
+ // WORK. it would break all sorts of regular sci games.
if (maxWidth == (width - _font->getCharWidth(curChar))) {
maxChars--;
if (curChar > 0xFF)
@@ -453,11 +434,14 @@ void GfxText16::Box(const char *text, int16 bshow, const Common::Rect &rect, Tex
_ports->penColor(orgPenColor);
if (doubleByteMode) {
- // kanji is written by pc98 rom to screen directly. Because of GetLongest() behaviour (not cutting off the last
- // char, that causes a new line), results in the script thinking that the text would need less space. The coordinate
- // adjustment in fontsjis.cpp handles the incorrect centering because of that and this code actually shows all of
- // the chars - if we don't do this, the scripts will only show most of the chars, but the last few pixels won't get
- // shown most of the time.
+ // Kanji is written by pc98 rom to screen directly. Because of
+ // GetLongest() behaviour (not cutting off the last char, that causes a
+ // new line), results in the script thinking that the text would need
+ // less space. The coordinate adjustment in fontsjis.cpp handles the
+ // incorrect centering because of that and this code actually shows all
+ // of the chars - if we don't do this, the scripts will only show most
+ // of the chars, but the last few pixels won't get shown most of the
+ // time.
Common::Rect kanjiRect = rect;
_ports->offsetRect(kanjiRect);
kanjiRect.left &= 0xFFC;
@@ -478,7 +462,8 @@ void GfxText16::Draw_String(const char *text) {
_ports->penColor(orgPenColor);
}
-// Sierra did this in their PC98 interpreter only, they identify a text as being sjis and then switch to font 900
+// Sierra did this in their PC98 interpreter only, they identify a text as being
+// sjis and then switch to font 900
bool GfxText16::SwitchToFont900OnSjis(const char *text) {
byte firstChar = (*(const byte *)text++);
if (((firstChar >= 0x81) && (firstChar <= 0x9F)) || ((firstChar >= 0xE0) && (firstChar <= 0xEF))) {
@@ -488,4 +473,35 @@ bool GfxText16::SwitchToFont900OnSjis(const char *text) {
return false;
}
+void GfxText16::kernelTextSize(const char *text, int16 font, int16 maxWidth, int16 *textWidth, int16 *textHeight) {
+ Common::Rect rect(0, 0, 0, 0);
+ Size(rect, text, font, maxWidth);
+ *textWidth = rect.width();
+ *textHeight = rect.height();
+}
+
+// Used SCI1+ for text codes
+void GfxText16::kernelTextFonts(int argc, reg_t *argv) {
+ int i;
+
+ delete _codeFonts;
+ _codeFontsCount = argc;
+ _codeFonts = new GuiResourceId[argc];
+ for (i = 0; i < argc; i++) {
+ _codeFonts[i] = (GuiResourceId)argv[i].toUint16();
+ }
+}
+
+// Used SCI1+ for text codes
+void GfxText16::kernelTextColors(int argc, reg_t *argv) {
+ int i;
+
+ delete _codeColors;
+ _codeColorsCount = argc;
+ _codeColors = new uint16[argc];
+ for (i = 0; i < argc; i++) {
+ _codeColors[i] = argv[i].toUint16();
+ }
+}
+
} // End of namespace Sci
diff --git a/engines/sci/graphics/text16.h b/engines/sci/graphics/text16.h
index 2885fc928b..71b602d116 100644
--- a/engines/sci/graphics/text16.h
+++ b/engines/sci/graphics/text16.h
@@ -48,8 +48,6 @@ public:
GfxFont *GetFont();
void SetFont(GuiResourceId fontId);
- void CodeSetFonts(int argc, reg_t *argv);
- void CodeSetColors(int argc, reg_t *argv);
int16 CodeProcessing(const char *&text, GuiResourceId orgFontId, int16 orgPenColor);
void ClearChar(int16 chr);
@@ -67,6 +65,10 @@ public:
GfxFont *_font;
+ void kernelTextSize(const char *text, int16 font, int16 maxWidth, int16 *textWidth, int16 *textHeight);
+ void kernelTextFonts(int argc, reg_t *argv);
+ void kernelTextColors(int argc, reg_t *argv);
+
private:
void init();
bool SwitchToFont900OnSjis(const char *text);
diff --git a/engines/sci/graphics/transitions.cpp b/engines/sci/graphics/transitions.cpp
index 1976326aa9..c1f626449f 100644
--- a/engines/sci/graphics/transitions.cpp
+++ b/engines/sci/graphics/transitions.cpp
@@ -31,15 +31,14 @@
#include "sci/sci.h"
#include "sci/engine/state.h"
-#include "sci/graphics/gui.h"
#include "sci/graphics/screen.h"
#include "sci/graphics/palette.h"
#include "sci/graphics/transitions.h"
namespace Sci {
-GfxTransitions::GfxTransitions(SciGui *gui, GfxScreen *screen, GfxPalette *palette, bool isVGA)
- : _gui(gui), _screen(screen), _palette(palette), _isVGA(isVGA) {
+GfxTransitions::GfxTransitions(GfxScreen *screen, GfxPalette *palette, bool isVGA)
+ : _screen(screen), _palette(palette), _isVGA(isVGA) {
init();
}
@@ -159,7 +158,8 @@ void GfxTransitions::doit(Common::Rect picRect) {
}
if (_blackoutFlag) {
- // We need to find out what transition we are supposed to use for blackout
+ // We need to find out what transition we are supposed to use for
+ // blackout
translationEntry = translateNumber(_number, blackoutTransitionIDs);
if (translationEntry) {
doTransition(translationEntry->newId, true);
@@ -168,12 +168,15 @@ void GfxTransitions::doit(Common::Rect picRect) {
}
}
+ _palette->palVaryPrepareForTransition();
+
// Now we do the actual transition to the new screen
doTransition(_number, false);
if (picRect.bottom != _screen->getHeight()) {
// TODO: this is a workaround for lsl6 not showing menubar when playing
- // There is some new code in the sierra sci in ShowPic that seems to do something similar to this
+ // There is some new code in the sierra sci in ShowPic that seems to do
+ // something similar to this
_screen->copyToScreen();
g_system->updateScreen();
}
@@ -181,8 +184,8 @@ void GfxTransitions::doit(Common::Rect picRect) {
_screen->_picNotValid = 0;
}
-// This may get called twice, if blackoutFlag is set. It will get once called with blackoutFlag set and another time
-// with no blackoutFlag.
+// This may get called twice, if blackoutFlag is set. It will get once called
+// with blackoutFlag set and another time with no blackoutFlag.
void GfxTransitions::doTransition(int16 number, bool blackoutFlag) {
if (number != SCI_TRANSITIONS_FADEPALETTE) {
setNewPalette(blackoutFlag);
@@ -193,7 +196,7 @@ void GfxTransitions::doTransition(int16 number, bool blackoutFlag) {
verticalRollFromCenter(blackoutFlag);
break;
case SCI_TRANSITIONS_VERTICALROLL_TOCENTER:
- verticalRollFromCenter(blackoutFlag);
+ verticalRollToCenter(blackoutFlag);
break;
case SCI_TRANSITIONS_HORIZONTALROLL_FROMCENTER:
horizontalRollFromCenter(blackoutFlag);
@@ -277,7 +280,8 @@ void GfxTransitions::copyRectToScreen(const Common::Rect rect, bool blackoutFlag
}
}
-// Note: dont do too many steps in here, otherwise cpu will crap out because of the load
+// Note: don't do too many steps in here, otherwise cpu will crap out because of
+// the load
void GfxTransitions::fadeOut() {
byte oldPalette[4 * 256], workPalette[4 * 256];
int16 stepNr, colorNr;
@@ -291,23 +295,24 @@ void GfxTransitions::fadeOut() {
workPalette[colorNr * 4 + 2] = oldPalette[colorNr * 4 + 2] * stepNr / 100;
}
g_system->setPalette(workPalette + 4, 1, 254);
- _gui->wait(2);
+ g_sci->getEngineState()->wait(2);
}
}
-// Note: dont do too many steps in here, otherwise cpu will crap out because of the load
+// Note: don't do too many steps in here, otherwise cpu will crap out because of
+// the load
void GfxTransitions::fadeIn() {
int16 stepNr;
for (stepNr = 0; stepNr <= 100; stepNr += 10) {
_palette->kernelSetIntensity(1, 255, stepNr, true);
- _gui->wait(2);
+ g_sci->getEngineState()->wait(2);
}
}
-// pixelates the new picture over the old one - works against the whole screen
+// Pixelates the new picture over the old one - works against the whole screen.
// TODO: it seems this needs to get applied on _picRect only if possible
-void GfxTransitions::pixelation (bool blackoutFlag) {
+void GfxTransitions::pixelation(bool blackoutFlag) {
uint16 mask = 0x40, stepNr = 0;
Common::Rect pixelRect;
@@ -327,7 +332,7 @@ void GfxTransitions::pixelation (bool blackoutFlag) {
} while (mask != 0x40);
}
-// like pixelation but uses 8x8 blocks - works against the whole screen
+// Like pixelation but uses 8x8 blocks - works against the whole screen.
// TODO: it seems this needs to get applied on _picRect only if possible
void GfxTransitions::blocks(bool blackoutFlag) {
uint16 mask = 0x40, stepNr = 0;
@@ -349,7 +354,8 @@ void GfxTransitions::blocks(bool blackoutFlag) {
} while (mask != 0x40);
}
-// directly shows new screen starting up/down/left/right and going to the opposite direction - works on _picRect area only
+// Directly shows new screen starting up/down/left/right and going to the
+// opposite direction - works on _picRect area only
void GfxTransitions::straight(int16 number, bool blackoutFlag) {
int16 stepNr = 0;
Common::Rect newScreenRect = _picRect;
@@ -401,7 +407,8 @@ void GfxTransitions::straight(int16 number, bool blackoutFlag) {
}
}
-// scroll old screen (up/down/left/right) and insert new screen that way - works on _picRect area only
+// Scroll old screen (up/down/left/right) and insert new screen that way - works
+// on _picRect area only.
void GfxTransitions::scroll(int16 number) {
int16 screenWidth, screenHeight;
byte *oldScreenPtr;
@@ -440,8 +447,12 @@ void GfxTransitions::scroll(int16 number) {
}
stepNr++;
}
- if ((stepNr & 1) == 0)
- g_system->updateScreen();
+ if ((stepNr & 1) == 0) {
+ if (g_system->getMillis() - g_sci->getEngineState()->_screenUpdateTime >= 1000 / 60) {
+ g_system->updateScreen();
+ g_sci->getEngineState()->_screenUpdateTime = g_system->getMillis();
+ }
+ }
break;
case SCI_TRANSITIONS_SCROLL_RIGHT:
@@ -461,8 +472,12 @@ void GfxTransitions::scroll(int16 number) {
}
stepNr++;
}
- if ((stepNr & 1) == 0)
- g_system->updateScreen();
+ if ((stepNr & 1) == 0) {
+ if (g_system->getMillis() - g_sci->getEngineState()->_screenUpdateTime >= 1000 / 60) {
+ g_system->updateScreen();
+ g_sci->getEngineState()->_screenUpdateTime = g_system->getMillis();
+ }
+ }
break;
case SCI_TRANSITIONS_SCROLL_UP:
@@ -503,7 +518,8 @@ void GfxTransitions::scroll(int16 number) {
}
}
-// vertically displays new screen starting from center - works on _picRect area only
+// Vertically displays new screen starting from center - works on _picRect area
+// only
void GfxTransitions::verticalRollFromCenter(bool blackoutFlag) {
Common::Rect leftRect = Common::Rect(_picRect.left + (_picRect.width() / 2) -1, _picRect.top, _picRect.left + (_picRect.width() / 2), _picRect.bottom);
Common::Rect rightRect = Common::Rect(leftRect.right, _picRect.top, leftRect.right + 1, _picRect.bottom);
@@ -519,7 +535,8 @@ void GfxTransitions::verticalRollFromCenter(bool blackoutFlag) {
}
}
-// vertically displays new screen starting from edges - works on _picRect area only
+// Vertically displays new screen starting from edges - works on _picRect area
+// only
void GfxTransitions::verticalRollToCenter(bool blackoutFlag) {
Common::Rect leftRect = Common::Rect(_picRect.left, _picRect.top, _picRect.left + 1, _picRect.bottom);
Common::Rect rightRect = Common::Rect(leftRect.right - 1, _picRect.top, leftRect.right, _picRect.bottom);
@@ -531,7 +548,8 @@ void GfxTransitions::verticalRollToCenter(bool blackoutFlag) {
}
}
-// horizontally displays new screen starting from center - works on _picRect area only
+// Horizontally displays new screen starting from center - works on _picRect
+// area only
void GfxTransitions::horizontalRollFromCenter(bool blackoutFlag) {
Common::Rect upperRect = Common::Rect(_picRect.left, _picRect.top + (_picRect.height() / 2) - 1, _picRect.right, _picRect.top + (_picRect.height() / 2));
Common::Rect lowerRect = Common::Rect(upperRect.left, upperRect.bottom, upperRect.right, upperRect.bottom + 1);
@@ -547,7 +565,8 @@ void GfxTransitions::horizontalRollFromCenter(bool blackoutFlag) {
}
}
-// horizontally displays new screen starting from upper and lower edge - works on _picRect area only
+// Horizontally displays new screen starting from upper and lower edge - works
+// on _picRect area only
void GfxTransitions::horizontalRollToCenter(bool blackoutFlag) {
Common::Rect upperRect = Common::Rect(_picRect.left, _picRect.top, _picRect.right, _picRect.top + 1);
Common::Rect lowerRect = Common::Rect(upperRect.left, _picRect.bottom - 1, upperRect.right, _picRect.bottom);
@@ -559,8 +578,8 @@ void GfxTransitions::horizontalRollToCenter(bool blackoutFlag) {
}
}
-// diagonally displays new screen starting from center - works on _picRect area only
-// assumes that height of rect is larger than width
+// Diagonally displays new screen starting from center - works on _picRect area
+// only. Assumes that height of rect is larger than width.
void GfxTransitions::diagonalRollFromCenter(bool blackoutFlag) {
int16 halfHeight = _picRect.height() / 2;
Common::Rect upperRect(_picRect.left + halfHeight - 2, _picRect.top + halfHeight, _picRect.right - halfHeight + 1, _picRect.top + halfHeight + 1);
@@ -589,8 +608,8 @@ void GfxTransitions::diagonalRollFromCenter(bool blackoutFlag) {
}
}
-// diagonally displays new screen starting from edges - works on _picRect area only
-// assumes that height of rect is larger than width
+// Diagonally displays new screen starting from edges - works on _picRect area
+// only. Assumes that height of rect is larger than width.
void GfxTransitions::diagonalRollToCenter(bool blackoutFlag) {
Common::Rect upperRect(_picRect.left, _picRect.top, _picRect.right, _picRect.top + 1);
Common::Rect lowerRect(_picRect.left, _picRect.bottom - 1, _picRect.right, _picRect.bottom);
diff --git a/engines/sci/graphics/transitions.h b/engines/sci/graphics/transitions.h
index 9a1a412d5b..788cefabca 100644
--- a/engines/sci/graphics/transitions.h
+++ b/engines/sci/graphics/transitions.h
@@ -65,7 +65,7 @@ class Screen;
*/
class GfxTransitions {
public:
- GfxTransitions(SciGui *gui, GfxScreen *screen, GfxPalette *palette, bool isVGA);
+ GfxTransitions(GfxScreen *screen, GfxPalette *palette, bool isVGA);
~GfxTransitions();
void setup(int16 number, bool blackoutFlag);
@@ -92,7 +92,6 @@ private:
void diagonalRollToCenter(bool blackoutFlag);
void updateScreenAndWait(int msec);
- SciGui *_gui;
GfxScreen *_screen;
GfxPalette *_palette;
diff --git a/engines/sci/graphics/view.cpp b/engines/sci/graphics/view.cpp
index 2ba14fbd8f..93df45820c 100644
--- a/engines/sci/graphics/view.cpp
+++ b/engines/sci/graphics/view.cpp
@@ -62,6 +62,7 @@ void GfxView::initData(GuiResourceId resourceId) {
error("view resource %d not found", resourceId);
}
_resourceData = _resource->data;
+ _resourceSize = _resource->size;
byte *celData, *loopData;
uint16 celOffset;
@@ -75,12 +76,28 @@ void GfxView::initData(GuiResourceId resourceId) {
byte seekEntry;
bool isEGA = false;
bool isCompressed = true;
+ ViewType curViewType = _resMan->getViewType();
_loopCount = 0;
_embeddedPal = false;
_EGAmapping = NULL;
+ _isSci2Hires = false;
+
+ // If we find an SCI1/SCI1.1 view (not amiga), we switch to that type for
+ // EGA. This could get used to make view patches for EGA games, where the
+ // new views include more colors. Users could manually adjust old views to
+ // make them look better (like removing dithered colors that aren't caught
+ // by our undithering or even improve the graphics overall).
+ if (curViewType == kViewEga) {
+ if (_resourceData[1] == 0x80) {
+ curViewType = kViewVga;
+ } else {
+ if (READ_LE_UINT16(_resourceData + 4) == 1)
+ curViewType = kViewVga11;
+ }
+ }
- switch (_resMan->getViewType()) {
+ switch (curViewType) {
case kViewEga: // View-format SCI0 (and Amiga 16 colors)
isEGA = true;
case kViewAmiga: // View-format Amiga (32 colors)
@@ -95,19 +112,21 @@ void GfxView::initData(GuiResourceId resourceId) {
palOffset = READ_LE_UINT16(_resourceData + 6);
if (palOffset && palOffset != 0x100) {
- // Some SCI0/SCI01 games also have an offset set. It seems that it points to a 16-byte mapping table
- // but on those games using that mapping will actually screw things up.
- // On the other side: vga sci1 games have this pointing to a VGA palette
- // and ega sci1 games have this pointing to a 8x16 byte mapping table that needs to get applied then
+ // Some SCI0/SCI01 games also have an offset set. It seems that it
+ // points to a 16-byte mapping table but on those games using that
+ // mapping will actually screw things up. On the other side: VGA
+ // SCI1 games have this pointing to a VGA palette and EGA SCI1 games
+ // have this pointing to a 8x16 byte mapping table that needs to get
+ // applied then.
if (!isEGA) {
- _palette->createFromData(&_resourceData[palOffset], &_viewPalette);
+ _palette->createFromData(&_resourceData[palOffset], _resourceSize - palOffset, &_viewPalette);
_embeddedPal = true;
} else {
// Only use the EGA-mapping, when being SCI1
if (getSciVersion() >= SCI_VERSION_1_EGA) {
_EGAmapping = &_resourceData[palOffset];
for (EGAmapNr = 0; EGAmapNr < SCI_VIEW_EGAMAPPING_COUNT; EGAmapNr++) {
- if (memcmp(_EGAmapping, EGAmappingStraight, SCI_VIEW_EGAMAPPING_SIZE)!=0)
+ if (memcmp(_EGAmapping, EGAmappingStraight, SCI_VIEW_EGAMAPPING_SIZE) != 0)
break;
_EGAmapping += SCI_VIEW_EGAMAPPING_SIZE;
}
@@ -169,12 +188,14 @@ void GfxView::initData(GuiResourceId resourceId) {
case kViewVga11: // View-format SCI1.1+
// HeaderSize:WORD LoopCount:BYTE Unknown:BYTE Version:WORD Unknown:WORD PaletteOffset:WORD
- headerSize = READ_SCI11ENDIAN_UINT16(_resourceData + 0) + 2; // headerSize is not part of the header, so its added
+ headerSize = READ_SCI11ENDIAN_UINT16(_resourceData + 0) + 2; // headerSize is not part of the header, so it's added
assert(headerSize >= 16);
_loopCount = _resourceData[2];
assert(_loopCount);
+ _isSci2Hires = _resourceData[5] == 1 ? true : false;
palOffset = READ_SCI11ENDIAN_UINT32(_resourceData + 8);
- // FIXME: After LoopCount there is another byte and its set for view 50 within Laura Bow 2 CD, check what it means
+ // FIXME: After LoopCount there is another byte and its set for view 50
+ // within Laura Bow 2 CD, check what it means.
loopData = _resourceData + headerSize;
loopSize = _resourceData[12];
@@ -183,7 +204,7 @@ void GfxView::initData(GuiResourceId resourceId) {
assert(celSize >= 32);
if (palOffset) {
- _palette->createFromData(&_resourceData[palOffset], &_viewPalette);
+ _palette->createFromData(&_resourceData[palOffset], _resourceSize - palOffset, &_viewPalette);
_embeddedPal = true;
}
@@ -221,6 +242,9 @@ void GfxView::initData(GuiResourceId resourceId) {
cel->offsetEGA = 0;
cel->offsetRLE = READ_SCI11ENDIAN_UINT32(celData + 24);
cel->offsetLiteral = READ_SCI11ENDIAN_UINT32(celData + 28);
+ // GK1-hires content is actually uncompressed, we need to swap both so that we process it as such
+ if ((cel->offsetRLE) && (!cel->offsetLiteral))
+ SWAP(cel->offsetRLE, cel->offsetLiteral);
cel->rawBitmap = 0;
if (_loop[loopNo].mirrorFlag)
@@ -236,65 +260,68 @@ void GfxView::initData(GuiResourceId resourceId) {
}
}
-GuiResourceId GfxView::getResourceId() {
+GuiResourceId GfxView::getResourceId() const {
return _resourceId;
}
-int16 GfxView::getWidth(int16 loopNo, int16 celNo) {
- loopNo = CLIP<int16>(loopNo, 0, _loopCount - 1);
- celNo = CLIP<int16>(celNo, 0, _loop[loopNo].celCount - 1);
- return _loopCount ? _loop[loopNo].cel[celNo].width : 0;
+int16 GfxView::getWidth(int16 loopNo, int16 celNo) const {
+ return _loopCount ? getCelInfo(loopNo, celNo)->width : 0;
}
-int16 GfxView::getHeight(int16 loopNo, int16 celNo) {
- loopNo = CLIP<int16>(loopNo, 0, _loopCount -1);
- celNo = CLIP<int16>(celNo, 0, _loop[loopNo].celCount - 1);
- return _loopCount ? _loop[loopNo].cel[celNo].height : 0;
+int16 GfxView::getHeight(int16 loopNo, int16 celNo) const {
+ return _loopCount ? getCelInfo(loopNo, celNo)->height : 0;
}
-CelInfo *GfxView::getCelInfo(int16 loopNo, int16 celNo) {
+const CelInfo *GfxView::getCelInfo(int16 loopNo, int16 celNo) const {
+ assert(_loopCount);
loopNo = CLIP<int16>(loopNo, 0, _loopCount - 1);
celNo = CLIP<int16>(celNo, 0, _loop[loopNo].celCount - 1);
- return _loopCount ? &_loop[loopNo].cel[celNo] : NULL;
+ return &_loop[loopNo].cel[celNo];
}
-LoopInfo *GfxView::getLoopInfo(int16 loopNo) {
+uint16 GfxView::getCelCount(int16 loopNo) const {
+ assert(_loopCount);
loopNo = CLIP<int16>(loopNo, 0, _loopCount - 1);
- return _loopCount ? &_loop[loopNo] : NULL;
+ return _loop[loopNo].celCount;
}
-void GfxView::getCelRect(int16 loopNo, int16 celNo, int16 x, int16 y, int16 z, Common::Rect *outRect) {
- CelInfo *celInfo = getCelInfo(loopNo, celNo);
- if (celInfo) {
- outRect->left = x + celInfo->displaceX - (celInfo->width >> 1);
- outRect->right = outRect->left + celInfo->width;
- outRect->bottom = y + celInfo->displaceY - z + 1;
- outRect->top = outRect->bottom - celInfo->height;
- }
+Palette *GfxView::getPalette() {
+ return _embeddedPal ? &_viewPalette : NULL;
+}
+
+bool GfxView::isSci2Hires() {
+ return _isSci2Hires;
+}
+
+void GfxView::getCelRect(int16 loopNo, int16 celNo, int16 x, int16 y, int16 z, Common::Rect &outRect) const {
+ const CelInfo *celInfo = getCelInfo(loopNo, celNo);
+ outRect.left = x + celInfo->displaceX - (celInfo->width >> 1);
+ outRect.right = outRect.left + celInfo->width;
+ outRect.bottom = y + celInfo->displaceY - z + 1;
+ outRect.top = outRect.bottom - celInfo->height;
}
-void GfxView::getCelScaledRect(int16 loopNo, int16 celNo, int16 x, int16 y, int16 z, int16 scaleX, int16 scaleY, Common::Rect *outRect) {
+void GfxView::getCelScaledRect(int16 loopNo, int16 celNo, int16 x, int16 y, int16 z, int16 scaleX, int16 scaleY, Common::Rect &outRect) const {
int16 scaledDisplaceX, scaledDisplaceY;
int16 scaledWidth, scaledHeight;
- CelInfo *celInfo = getCelInfo(loopNo, celNo);
- if (celInfo) {
- // Scaling displaceX/Y, Width/Height
- scaledDisplaceX = (celInfo->displaceX * scaleX) >> 7;
- scaledDisplaceY = (celInfo->displaceY * scaleY) >> 7;
- scaledWidth = (celInfo->width * scaleX) >> 7;
- scaledHeight = (celInfo->height * scaleY) >> 7;
- scaledWidth = CLIP<int16>(scaledWidth, 0, _screen->getWidth());
- scaledHeight = CLIP<int16>(scaledHeight, 0, _screen->getHeight());
-
- outRect->left = x + scaledDisplaceX - (scaledWidth >> 1);
- outRect->right = outRect->left + scaledWidth;
- outRect->bottom = y + scaledDisplaceY - z + 1;
- outRect->top = outRect->bottom - scaledHeight;
- }
+ const CelInfo *celInfo = getCelInfo(loopNo, celNo);
+
+ // Scaling displaceX/Y, Width/Height
+ scaledDisplaceX = (celInfo->displaceX * scaleX) >> 7;
+ scaledDisplaceY = (celInfo->displaceY * scaleY) >> 7;
+ scaledWidth = (celInfo->width * scaleX) >> 7;
+ scaledHeight = (celInfo->height * scaleY) >> 7;
+ scaledWidth = CLIP<int16>(scaledWidth, 0, _screen->getWidth());
+ scaledHeight = CLIP<int16>(scaledHeight, 0, _screen->getHeight());
+
+ outRect.left = x + scaledDisplaceX - (scaledWidth >> 1);
+ outRect.right = outRect.left + scaledWidth;
+ outRect.bottom = y + scaledDisplaceY - z + 1;
+ outRect.top = outRect.bottom - scaledHeight;
}
void GfxView::unpackCel(int16 loopNo, int16 celNo, byte *outPtr, uint32 pixelCount) {
- CelInfo *celInfo = getCelInfo(loopNo, celNo);
+ const CelInfo *celInfo = getCelInfo(loopNo, celNo);
byte *rlePtr;
byte *literalPtr;
uint32 pixelNo = 0, runLength;
@@ -309,78 +336,42 @@ void GfxView::unpackCel(int16 loopNo, int16 celNo, byte *outPtr, uint32 pixelCou
memset(outPtr + pixelNo, pixel & 0x0F, MIN<uint32>(runLength, pixelCount - pixelNo));
pixelNo += runLength;
}
- return;
- }
-
- rlePtr = _resourceData + celInfo->offsetRLE;
- if (!celInfo->offsetLiteral) { // no additional literal data
- if (_resMan->isAmiga32color()) {
- // decompression for amiga views
- while (pixelNo < pixelCount) {
- pixel = *rlePtr++;
- if (pixel & 0x07) { // fill with color
- runLength = pixel & 0x07;
- pixel = pixel >> 3;
- while (runLength-- && pixelNo < pixelCount) {
- outPtr[pixelNo++] = pixel;
- }
- } else { // fill with transparent
- runLength = pixel >> 3;
- pixelNo += runLength;
- }
- }
- return;
- } else {
- // decompression for data that has just one combined stream
- while (pixelNo < pixelCount) {
- pixel = *rlePtr++;
- runLength = pixel & 0x3F;
- switch (pixel & 0xC0) {
- case 0: // copy bytes as-is
- while (runLength-- && pixelNo < pixelCount)
- outPtr[pixelNo++] = *rlePtr++;
- break;
- case 0x80: // fill with color
- memset(outPtr + pixelNo, *rlePtr++, MIN<uint32>(runLength, pixelCount - pixelNo));
- pixelNo += runLength;
- break;
- case 0xC0: // fill with transparent
- pixelNo += runLength;
- break;
- }
- }
- return;
- }
} else {
- literalPtr = _resourceData + celInfo->offsetLiteral;
- if (celInfo->offsetRLE) {
- if (g_sci->getPlatform() == Common::kPlatformMacintosh && getSciVersion() >= SCI_VERSION_1_1) {
- // Crazy-Ass compression for SCI1.1+ Mac
+ // We fill the buffer with transparent pixels, so that we can later skip
+ // over pixels to automatically have them transparent
+ // Also some RLE compressed cels are possibly ending with the last
+ // non-transparent pixel (is this even possible with the current code?)
+ memset(outPtr, _loop[loopNo].cel[celNo].clearKey, pixelCount);
+
+ rlePtr = _resourceData + celInfo->offsetRLE;
+ if (!celInfo->offsetLiteral) { // no additional literal data
+ if (_resMan->isAmiga32color()) {
+ // decompression for amiga views
while (pixelNo < pixelCount) {
- uint32 pixelLine = pixelNo;
- runLength = *rlePtr++;
- pixelNo += runLength;
- runLength = *rlePtr++;
- while (runLength-- && pixelNo < pixelCount) {
- outPtr[pixelNo] = *literalPtr++;
- if (outPtr[pixelNo] == 255)
- outPtr[pixelNo] = 0;
- pixelNo++;
+ pixel = *rlePtr++;
+ if (pixel & 0x07) { // fill with color
+ runLength = pixel & 0x07;
+ pixel = pixel >> 3;
+ while (runLength-- && pixelNo < pixelCount) {
+ outPtr[pixelNo++] = pixel;
+ }
+ } else { // fill with transparent
+ runLength = pixel >> 3;
+ pixelNo += runLength;
}
- pixelNo = pixelLine + celInfo->width;
}
} else {
- // decompression for data that has separate rle and literal streams
+ // decompression for data that has just one combined stream
while (pixelNo < pixelCount) {
pixel = *rlePtr++;
runLength = pixel & 0x3F;
switch (pixel & 0xC0) {
case 0: // copy bytes as-is
while (runLength-- && pixelNo < pixelCount)
- outPtr[pixelNo++] = *literalPtr++;
+ outPtr[pixelNo++] = *rlePtr++;
break;
case 0x80: // fill with color
- memset(outPtr + pixelNo, *literalPtr++, MIN<uint32>(runLength, pixelCount - pixelNo));
+ memset(outPtr + pixelNo, *rlePtr++, MIN<uint32>(runLength, pixelCount - pixelNo));
pixelNo += runLength;
break;
case 0xC0: // fill with transparent
@@ -390,15 +381,53 @@ void GfxView::unpackCel(int16 loopNo, int16 celNo, byte *outPtr, uint32 pixelCou
}
}
} else {
- // literal stream only, so no compression
- memcpy(outPtr, literalPtr, pixelCount);
+ literalPtr = _resourceData + celInfo->offsetLiteral;
+ if (celInfo->offsetRLE) {
+ if (g_sci->getPlatform() == Common::kPlatformMacintosh && getSciVersion() >= SCI_VERSION_1_1) {
+ // compression for SCI1.1+ Mac
+ while (pixelNo < pixelCount) {
+ uint32 pixelLine = pixelNo;
+ runLength = *rlePtr++;
+ pixelNo += runLength;
+ runLength = *rlePtr++;
+ while (runLength-- && pixelNo < pixelCount) {
+ outPtr[pixelNo] = *literalPtr++;
+ if (outPtr[pixelNo] == 255)
+ outPtr[pixelNo] = 0;
+ pixelNo++;
+ }
+ pixelNo = pixelLine + celInfo->width;
+ }
+ } else {
+ // decompression for data that has separate rle and literal streams
+ while (pixelNo < pixelCount) {
+ pixel = *rlePtr++;
+ runLength = pixel & 0x3F;
+ switch (pixel & 0xC0) {
+ case 0: // copy bytes as-is
+ while (runLength-- && pixelNo < pixelCount)
+ outPtr[pixelNo++] = *literalPtr++;
+ break;
+ case 0x80: // fill with color
+ memset(outPtr + pixelNo, *literalPtr++, MIN<uint32>(runLength, pixelCount - pixelNo));
+ pixelNo += runLength;
+ break;
+ case 0xC0: // fill with transparent
+ pixelNo += runLength;
+ break;
+ }
+ }
+ }
+ } else {
+ // literal stream only, so no compression
+ memcpy(outPtr, literalPtr, pixelCount);
+ pixelNo = pixelCount;
+ }
}
- return;
}
- error("Unable to decompress view");
}
-byte *GfxView::getBitmap(int16 loopNo, int16 celNo) {
+const byte *GfxView::getBitmap(int16 loopNo, int16 celNo) {
loopNo = CLIP<int16>(loopNo, 0, _loopCount -1);
celNo = CLIP<int16>(celNo, 0, _loop[loopNo].celCount - 1);
if (_loop[loopNo].cel[celNo].rawBitmap)
@@ -411,9 +440,7 @@ byte *GfxView::getBitmap(int16 loopNo, int16 celNo) {
_loop[loopNo].cel[celNo].rawBitmap = new byte[pixelCount];
byte *pBitmap = _loop[loopNo].cel[celNo].rawBitmap;
- // Some RLE compressed cels end with the last non-transparent pixel, thats why we fill it up here
- // FIXME: change this to fill the remaining bytes within unpackCel()
- memset(pBitmap, _loop[loopNo].cel[celNo].clearKey, pixelCount);
+ // unpack the actual cel bitmap data
unpackCel(loopNo, celNo, pBitmap, pixelCount);
if (!_resMan->isVGA()) {
@@ -429,17 +456,21 @@ byte *GfxView::getBitmap(int16 loopNo, int16 celNo) {
return _loop[loopNo].cel[celNo].rawBitmap;
}
-// Called after unpacking an EGA cel, this will try to undither (parts) of the cel if the dithering in here
-// matches dithering used by the current picture
+/**
+ * Called after unpacking an EGA cel, this will try to undither (parts) of the
+ * cel if the dithering in here matches dithering used by the current picture.
+ */
void GfxView::unditherBitmap(byte *bitmapPtr, int16 width, int16 height, byte clearKey) {
int16 *unditherMemorial = _screen->unditherGetMemorial();
- // It makes no sense to go further, if no memorial data from current picture is available
+ // It makes no sense to go further, if no memorial data from current picture
+ // is available
if (!unditherMemorial)
return;
// Makes no sense to process bitmaps that are 3 pixels wide or less
- if (width <= 3) return;
+ if (width <= 3)
+ return;
// If EGA mapping is used for this view, dont do undithering as well
if (_EGAmapping)
@@ -453,7 +484,8 @@ void GfxView::unditherBitmap(byte *bitmapPtr, int16 width, int16 height, byte cl
memset(&bitmapMemorial, 0, sizeof(bitmapMemorial));
- // Count all seemingly dithered pixel-combinations as soon as at least 4 pixels are adjacent
+ // Count all seemingly dithered pixel-combinations as soon as at least 4
+ // pixels are adjacent
curPtr = bitmapPtr;
for (y = 0; y < height; y++) {
color1 = curPtr[0]; color2 = (curPtr[1] << 4) | curPtr[2];
@@ -466,17 +498,20 @@ void GfxView::unditherBitmap(byte *bitmapPtr, int16 width, int16 height, byte cl
}
}
- // Now compare both memorial tables to find out matching dithering-combinations
+ // Now compare both memorial tables to find out matching
+ // dithering-combinations
bool unditherTable[SCI_SCREEN_UNDITHERMEMORIAL_SIZE];
byte color, unditherCount = 0;
memset(&unditherTable, false, sizeof(unditherTable));
for (color = 0; color < 255; color++) {
if ((bitmapMemorial[color] > 5) && (unditherMemorial[color] > 200)) {
- // match found, check if colorKey is contained -> if so, we ignore of course
+ // match found, check if colorKey is contained -> if so, we ignore
+ // of course
color1 = color & 0x0F; color2 = color >> 4;
if ((color1 != clearKey) && (color2 != clearKey) && (color1 != color2)) {
// so set this and the reversed color-combination for undithering
- unditherTable[color] = true; unditherTable[(color1 << 4) | color2] = true;
+ unditherTable[color] = true;
+ unditherTable[(color1 << 4) | color2] = true;
unditherCount++;
}
}
@@ -493,8 +528,9 @@ void GfxView::unditherBitmap(byte *bitmapPtr, int16 width, int16 height, byte cl
for (x = 1; x < width; x++) {
color = (color << 4) | curPtr[1];
if (unditherTable[color]) {
- // some color with black? turn colors around otherwise it wont be the right color at all
- if ((color & 0xF0)==0)
+ // Some color with black? Turn colors around, otherwise it won't
+ // be the right color at all.
+ if ((color & 0xF0) == 0)
color = (color << 4) | (color >> 4);
curPtr[0] = color; curPtr[1] = color;
}
@@ -504,15 +540,15 @@ void GfxView::unditherBitmap(byte *bitmapPtr, int16 width, int16 height, byte cl
}
}
-void GfxView::draw(Common::Rect rect, Common::Rect clipRect, Common::Rect clipRectTranslated, int16 loopNo, int16 celNo, byte priority, uint16 EGAmappingNr, bool upscaledHires) {
- Palette *palette = _embeddedPal ? &_viewPalette : &_palette->_sysPalette;
- CelInfo *celInfo = getCelInfo(loopNo, celNo);
- byte *bitmap = getBitmap(loopNo, celNo);
- int16 celHeight = celInfo->height, celWidth = celInfo->width;
- int16 width, height;
- byte clearKey = celInfo->clearKey;
- byte color;
- byte drawMask = priority == 255 ? GFX_SCREEN_MASK_VISUAL : GFX_SCREEN_MASK_VISUAL|GFX_SCREEN_MASK_PRIORITY;
+void GfxView::draw(const Common::Rect &rect, const Common::Rect &clipRect, const Common::Rect &clipRectTranslated,
+ int16 loopNo, int16 celNo, byte priority, uint16 EGAmappingNr, bool upscaledHires) {
+ const Palette *palette = _embeddedPal ? &_viewPalette : &_palette->_sysPalette;
+ const CelInfo *celInfo = getCelInfo(loopNo, celNo);
+ const byte *bitmap = getBitmap(loopNo, celNo);
+ const int16 celHeight = celInfo->height;
+ const int16 celWidth = celInfo->width;
+ const byte clearKey = celInfo->clearKey;
+ const byte drawMask = (priority == 255) ? GFX_SCREEN_MASK_VISUAL : GFX_SCREEN_MASK_VISUAL|GFX_SCREEN_MASK_PRIORITY;
int x, y;
if (_embeddedPal) {
@@ -520,24 +556,27 @@ void GfxView::draw(Common::Rect rect, Common::Rect clipRect, Common::Rect clipRe
_palette->set(&_viewPalette, false);
}
- width = MIN(clipRect.width(), celWidth);
- height = MIN(clipRect.height(), celHeight);
+ const int16 width = MIN(clipRect.width(), celWidth);
+ const int16 height = MIN(clipRect.height(), celHeight);
bitmap += (clipRect.top - rect.top) * celWidth + (clipRect.left - rect.left);
if (!_EGAmapping) {
for (y = 0; y < height; y++, bitmap += celWidth) {
for (x = 0; x < width; x++) {
- color = bitmap[x];
+ const byte color = bitmap[x];
if (color != clearKey) {
+ const int x2 = clipRectTranslated.left + x;
+ const int y2 = clipRectTranslated.top + y;
if (!upscaledHires) {
- if (priority >= _screen->getPriority(clipRectTranslated.left + x, clipRectTranslated.top + y))
- _screen->putPixel(clipRectTranslated.left + x, clipRectTranslated.top + y, drawMask, palette->mapping[color], priority, 0);
+ if (priority >= _screen->getPriority(x2, y2))
+ _screen->putPixel(x2, y2, drawMask, palette->mapping[color], priority, 0);
} else {
- // UpscaledHires means view is hires and is supposed to get drawn onto lowres screen
- // FIXME(?): we can't read priority directly with the hires coordinates. may not be needed at all
- // in kq6
- _screen->putPixelOnDisplay(clipRectTranslated.left + x, clipRectTranslated.top + y, palette->mapping[color]);
+ // UpscaledHires means view is hires and is supposed to
+ // get drawn onto lowres screen.
+ // FIXME(?): we can't read priority directly with the
+ // hires coordinates. may not be needed at all in kq6
+ _screen->putPixelOnDisplay(x2, y2, palette->mapping[color]);
}
}
}
@@ -546,30 +585,34 @@ void GfxView::draw(Common::Rect rect, Common::Rect clipRect, Common::Rect clipRe
byte *EGAmapping = _EGAmapping + (EGAmappingNr * SCI_VIEW_EGAMAPPING_SIZE);
for (y = 0; y < height; y++, bitmap += celWidth) {
for (x = 0; x < width; x++) {
- color = EGAmapping[bitmap[x]];
- if (color != clearKey && priority >= _screen->getPriority(clipRectTranslated.left + x, clipRectTranslated.top + y))
- _screen->putPixel(clipRectTranslated.left + x, clipRectTranslated.top + y, drawMask, color, priority, 0);
+ const byte color = EGAmapping[bitmap[x]];
+ const int x2 = clipRectTranslated.left + x;
+ const int y2 = clipRectTranslated.top + y;
+ if (color != clearKey && priority >= _screen->getPriority(x2, y2))
+ _screen->putPixel(x2, y2, drawMask, color, priority, 0);
}
}
}
}
-// We don't fully follow sierra sci here, I did the scaling algo myself and it's definitely not pixel-perfect
-// with the one sierra is using. It shouldn't matter because the scaled cel rect is definitely the same as in sierra sci
-void GfxView::drawScaled(Common::Rect rect, Common::Rect clipRect, Common::Rect clipRectTranslated, int16 loopNo, int16 celNo, byte priority, int16 scaleX, int16 scaleY) {
- Palette *palette = _embeddedPal ? &_viewPalette : &_palette->_sysPalette;
- CelInfo *celInfo = getCelInfo(loopNo, celNo);
- byte *bitmap = getBitmap(loopNo, celNo);
- int16 celHeight = celInfo->height, celWidth = celInfo->width;
- byte clearKey = celInfo->clearKey;
- byte color;
- byte drawMask = priority == 255 ? GFX_SCREEN_MASK_VISUAL : GFX_SCREEN_MASK_VISUAL|GFX_SCREEN_MASK_PRIORITY;
- int x, y;
- uint16 scalingX[320];
- uint16 scalingY[200];
+/**
+ * We don't fully follow sierra sci here, I did the scaling algo myself and it
+ * is definitely not pixel-perfect with the one sierra is using. It shouldn't
+ * matter because the scaled cel rect is definitely the same as in sierra sci.
+ */
+void GfxView::drawScaled(const Common::Rect &rect, const Common::Rect &clipRect, const Common::Rect &clipRectTranslated,
+ int16 loopNo, int16 celNo, byte priority, int16 scaleX, int16 scaleY) {
+ const Palette *palette = _embeddedPal ? &_viewPalette : &_palette->_sysPalette;
+ const CelInfo *celInfo = getCelInfo(loopNo, celNo);
+ const byte *bitmap = getBitmap(loopNo, celNo);
+ const int16 celHeight = celInfo->height;
+ const int16 celWidth = celInfo->width;
+ const byte clearKey = celInfo->clearKey;
+ const byte drawMask = (priority == 255) ? GFX_SCREEN_MASK_VISUAL : GFX_SCREEN_MASK_VISUAL|GFX_SCREEN_MASK_PRIORITY;
+ uint16 scalingX[640];
+ uint16 scalingY[480];
int16 scaledWidth, scaledHeight;
int16 pixelNo, scaledPixel, scaledPixelNo, prevScaledPixelNo;
- uint16 offsetX, offsetY;
if (_embeddedPal) {
// Merge view palette in...
@@ -590,6 +633,7 @@ void GfxView::drawScaled(Common::Rect rect, Common::Rect clipRect, Common::Rect
scaledPixel = scaledPixelNo = prevScaledPixelNo = 0;
while (pixelNo < celHeight) {
scaledPixelNo = scaledPixel >> 7;
+ assert(scaledPixelNo < ARRAYSIZE(scalingY));
if (prevScaledPixelNo < scaledPixelNo)
memset(&scalingY[prevScaledPixelNo], pixelNo, scaledPixelNo - prevScaledPixelNo);
scalingY[scaledPixelNo] = pixelNo;
@@ -606,6 +650,7 @@ void GfxView::drawScaled(Common::Rect rect, Common::Rect clipRect, Common::Rect
scaledPixel = scaledPixelNo = prevScaledPixelNo = 0;
while (pixelNo < celWidth) {
scaledPixelNo = scaledPixel >> 7;
+ assert(scaledPixelNo < ARRAYSIZE(scalingX));
if (prevScaledPixelNo < scaledPixelNo)
memset(&scalingX[prevScaledPixelNo], pixelNo, scaledPixelNo - prevScaledPixelNo);
scalingX[scaledPixelNo] = pixelNo;
@@ -620,27 +665,25 @@ void GfxView::drawScaled(Common::Rect rect, Common::Rect clipRect, Common::Rect
scaledWidth = MIN(clipRect.width(), scaledWidth);
scaledHeight = MIN(clipRect.height(), scaledHeight);
- offsetY = clipRect.top - rect.top;
- offsetX = clipRect.left - rect.left;
+ const int16 offsetY = clipRect.top - rect.top;
+ const int16 offsetX = clipRect.left - rect.left;
+
+ // Happens in SQ6, first room
+ if (offsetX < 0 || offsetY < 0)
+ return;
- for (y = 0; y < scaledHeight; y++) {
- for (x = 0; x < scaledWidth; x++) {
- color = bitmap[scalingY[y + offsetY] * celWidth + scalingX[x + offsetX]];
- if (color != clearKey && priority >= _screen->getPriority(clipRectTranslated.left + x, clipRectTranslated.top + y)) {
- _screen->putPixel(clipRectTranslated.left + x, clipRectTranslated.top + y, drawMask, palette->mapping[color], priority, 0);
+ assert(scaledHeight + offsetY <= ARRAYSIZE(scalingY));
+ assert(scaledWidth + offsetX <= ARRAYSIZE(scalingX));
+ for (int y = 0; y < scaledHeight; y++) {
+ for (int x = 0; x < scaledWidth; x++) {
+ const byte color = bitmap[scalingY[y + offsetY] * celWidth + scalingX[x + offsetX]];
+ const int x2 = clipRectTranslated.left + x;
+ const int y2 = clipRectTranslated.top + y;
+ if (color != clearKey && priority >= _screen->getPriority(x2, y2)) {
+ _screen->putPixel(x2, y2, drawMask, palette->mapping[color], priority, 0);
}
}
}
}
-uint16 GfxView::getCelCount(int16 loopNo) {
- if ((loopNo < 0) || (loopNo >= _loopCount))
- return 0;
- return _loop[loopNo].celCount;
-}
-
-Palette *GfxView::getPalette() {
- return _embeddedPal ? &_viewPalette : &_palette->_sysPalette;
-}
-
} // End of namespace Sci
diff --git a/engines/sci/graphics/view.h b/engines/sci/graphics/view.h
index a2050dc9d5..6eb1830b99 100644
--- a/engines/sci/graphics/view.h
+++ b/engines/sci/graphics/view.h
@@ -60,20 +60,21 @@ public:
GfxView(ResourceManager *resMan, GfxScreen *screen, GfxPalette *palette, GuiResourceId resourceId);
~GfxView();
- GuiResourceId getResourceId();
- int16 getWidth(int16 loopNo, int16 celNo);
- int16 getHeight(int16 loopNo, int16 celNo);
- CelInfo *getCelInfo(int16 loopNo, int16 celNo);
- LoopInfo *getLoopInfo(int16 loopNo);
- void getCelRect(int16 loopNo, int16 celNo, int16 x, int16 y, int16 z, Common::Rect *outRect);
- void getCelScaledRect(int16 loopNo, int16 celNo, int16 x, int16 y, int16 z, int16 scaleX, int16 scaleY, Common::Rect *outRect);
- byte *getBitmap(int16 loopNo, int16 celNo);
- void draw(Common::Rect rect, Common::Rect clipRect, Common::Rect clipRectTranslated, int16 loopNo, int16 celNo, byte priority, uint16 EGAmappingNr, bool upscaledHires);
- void drawScaled(Common::Rect rect, Common::Rect clipRect, Common::Rect clipRectTranslated, int16 loopNo, int16 celNo, byte priority, int16 scaleX, int16 scaleY);
+ GuiResourceId getResourceId() const;
+ int16 getWidth(int16 loopNo, int16 celNo) const;
+ int16 getHeight(int16 loopNo, int16 celNo) const;
+ const CelInfo *getCelInfo(int16 loopNo, int16 celNo) const;
+ void getCelRect(int16 loopNo, int16 celNo, int16 x, int16 y, int16 z, Common::Rect &outRect) const;
+ void getCelScaledRect(int16 loopNo, int16 celNo, int16 x, int16 y, int16 z, int16 scaleX, int16 scaleY, Common::Rect &outRect) const;
+ const byte *getBitmap(int16 loopNo, int16 celNo);
+ void draw(const Common::Rect &rect, const Common::Rect &clipRect, const Common::Rect &clipRectTranslated, int16 loopNo, int16 celNo, byte priority, uint16 EGAmappingNr, bool upscaledHires);
+ void drawScaled(const Common::Rect &rect, const Common::Rect &clipRect, const Common::Rect &clipRectTranslated, int16 loopNo, int16 celNo, byte priority, int16 scaleX, int16 scaleY);
uint16 getLoopCount() const { return _loopCount; }
- uint16 getCelCount(int16 loopNo);
+ uint16 getCelCount(int16 loopNo) const;
Palette *getPalette();
+ bool isSci2Hires();
+
private:
void initData(GuiResourceId resourceId);
void unpackCel(int16 loopNo, int16 celNo, byte *outPtr, uint32 pixelCount);
@@ -86,12 +87,16 @@ private:
GuiResourceId _resourceId;
Resource *_resource;
byte *_resourceData;
+ int _resourceSize;
uint16 _loopCount;
LoopInfo *_loop;
bool _embeddedPal;
Palette _viewPalette;
+ // set for SCI2 views in gk1/windows, means that views are hires and should be handled accordingly
+ bool _isSci2Hires;
+
byte *_EGAmapping;
};
diff --git a/engines/sci/module.mk b/engines/sci/module.mk
index a2cfd38f95..85988b8f1b 100644
--- a/engines/sci/module.mk
+++ b/engines/sci/module.mk
@@ -10,7 +10,6 @@ MODULE_OBJS := \
sci.o \
util.o \
engine/features.o \
- engine/game.o \
engine/gc.o \
engine/kernel.o \
engine/kevent.o \
@@ -44,7 +43,6 @@ MODULE_OBJS := \
graphics/cursor.o \
graphics/font.o \
graphics/fontsjis.o \
- graphics/gui.o \
graphics/maciconbar.o \
graphics/menu.o \
graphics/paint.o \
@@ -69,9 +67,6 @@ MODULE_OBJS := \
sound/drivers/fb01.o \
sound/drivers/midi.o \
sound/drivers/pcjr.o \
- sound/iterator/core.o \
- sound/iterator/iterator.o \
- sound/iterator/songlib.o \
video/seq_decoder.o
@@ -79,7 +74,6 @@ ifdef ENABLE_SCI32
MODULE_OBJS += \
engine/kernel32.o \
graphics/frameout.o \
- graphics/gui32.o \
graphics/paint32.o \
graphics/robot.o \
video/vmd_decoder.o
diff --git a/engines/sci/parser/vocabulary.cpp b/engines/sci/parser/vocabulary.cpp
index 00448f5d51..e48a9cdfda 100644
--- a/engines/sci/parser/vocabulary.cpp
+++ b/engines/sci/parser/vocabulary.cpp
@@ -33,59 +33,6 @@
namespace Sci {
-#if 0
-
-#define VOCAB_RESOURCE_CLASSES 996
-/**
- * Vocabulary class names.
- * These strange names were taken from an SCI01 interpreter.
- */
-const char *class_names[] = {"",
- "",
- "conj", // conjunction
- "ass", // ?
- "pos", // preposition ?
- "art", // article
- "adj", // adjective
- "pron", // pronoun
- "noun", // noun
- "auxv", // auxillary verb
- "adv", // adverb
- "verb", // verb
- "",
- "",
- "",
- ""
- };
-
-int *vocab_get_classes(ResourceManager *resMan, int* count) {
- Resource* r;
- int *c;
- unsigned int i;
-
- if ((r = resMan->findResource(ResourceId(kResourceTypeVocab, VOCAB_RESOURCE_CLASSES), 0)) == NULL)
- return 0;
-
- c = (int *)malloc(sizeof(int) * r->size / 2);
- for (i = 2; i < r->size; i += 4) {
- c[i/4] = READ_LE_UINT16(r->data + i);
- }
- *count = r->size / 4;
-
- return c;
-}
-
-int vocab_get_class_count(ResourceManager *resMan) {
- Resource* r;
-
- if ((r = resMan->findResource(ResourceId(kResourceTypeVocab, VOCAB_RESOURCE_CLASSES), 0)) == 0)
- return 0;
-
- return r->size / 4;
-}
-
-#endif
-
Vocabulary::Vocabulary(ResourceManager *resMan) : _resMan(resMan) {
_parserRules = NULL;
_vocabVersion = kVocabularySCI0;
diff --git a/engines/sci/resource.cpp b/engines/sci/resource.cpp
index 400f0b1e67..2958ca1e3b 100644
--- a/engines/sci/resource.cpp
+++ b/engines/sci/resource.cpp
@@ -26,8 +26,11 @@
// Resource library
#include "common/file.h"
+#include "common/fs.h"
+#include "common/macresman.h"
#include "sci/resource.h"
+#include "sci/resource_intern.h"
#include "sci/util.h"
namespace Sci {
@@ -111,7 +114,7 @@ static const char *resourceTypeNames[] = {
"memory", "vocab", "font", "cursor",
"patch", "bitmap", "palette", "cdaudio",
"audio", "sync", "message", "map", "heap",
- "audio36", "sync36", "", "", "robot"
+ "audio36", "sync36", "", "", "robot", "vmd"
};
static const char *resourceTypeSuffixes[] = {
@@ -119,7 +122,7 @@ static const char *resourceTypeSuffixes[] = {
" ", "voc", "fon", "cur", "pat",
"bit", "pal", "cda", "aud", "syn",
"msg", "map", "hep", "aud", "syn",
- "trn", " ", "rbt"
+ "trn", " ", "rbt", "vmd"
};
const char *getResourceTypeName(ResourceType restype) {
@@ -130,7 +133,7 @@ const char *getResourceTypeName(ResourceType restype) {
}
//-- Resource main functions --
-Resource::Resource() {
+Resource::Resource(ResourceId id) : _id(id) {
data = NULL;
size = 0;
_fileOffset = 0;
@@ -143,7 +146,7 @@ Resource::Resource() {
Resource::~Resource() {
delete[] data;
- if (_source && _source->source_type == kSourcePatch)
+ if (_source && _source->getSourceType() == kSourcePatch)
delete _source;
}
@@ -154,102 +157,76 @@ void Resource::unalloc() {
}
void Resource::writeToStream(Common::WriteStream *stream) const {
- stream->writeByte(_id.type | 0x80); // 0x80 is required by old sierra sci, otherwise it wont accept the patch file
+ stream->writeByte(getType() | 0x80); // 0x80 is required by old sierra sci, otherwise it wont accept the patch file
stream->writeByte(_headerSize);
if (_headerSize > 0)
stream->write(_header, _headerSize);
stream->write(data, size);
}
-uint32 Resource::getAudioCompressionType() {
- return _source->audioCompressionType;
+uint32 Resource::getAudioCompressionType() const {
+ return _source->getAudioCompressionType();
}
-//-- resMan helper functions --
+uint32 AudioVolumeResourceSource::getAudioCompressionType() const {
+ return _audioCompressionType;
+}
-// Resource source list management
-ResourceSource *ResourceManager::addExternalMap(const char *file_name, int volume_nr) {
- ResourceSource *newsrc = new ResourceSource();
+ResourceSource::ResourceSource(ResSourceType type, const Common::String &name, int volNum, const Common::FSNode *resFile)
+ : _sourceType(type), _name(name), _volumeNumber(volNum), _resourceFile(resFile) {
+ _scanned = false;
+}
- newsrc->source_type = kSourceExtMap;
- newsrc->location_name = file_name;
- newsrc->resourceFile = 0;
- newsrc->scanned = false;
- newsrc->associated_map = NULL;
- newsrc->volume_number = volume_nr;
+ResourceSource::~ResourceSource() {
+}
- _sources.push_back(newsrc);
- return newsrc;
+MacResourceForkResourceSource::MacResourceForkResourceSource(const Common::String &name, int volNum)
+ : ResourceSource(kSourceMacResourceFork, name, volNum) {
+ _macResMan = new Common::MacResManager();
+ assert(_macResMan);
}
-ResourceSource *ResourceManager::addExternalMap(const Common::FSNode *mapFile, int volume_nr) {
- ResourceSource *newsrc = new ResourceSource();
+MacResourceForkResourceSource::~MacResourceForkResourceSource() {
+ delete _macResMan;
+}
- newsrc->source_type = kSourceExtMap;
- newsrc->location_name = mapFile->getName();
- newsrc->resourceFile = mapFile;
- newsrc->scanned = false;
- newsrc->associated_map = NULL;
- newsrc->volume_number = volume_nr;
+//-- resMan helper functions --
+
+// Resource source list management
+
+ResourceSource *ResourceManager::addExternalMap(const Common::String &filename, int volume_nr) {
+ ResourceSource *newsrc = new ExtMapResourceSource(filename, volume_nr);
_sources.push_back(newsrc);
return newsrc;
}
-ResourceSource *ResourceManager::addSource(ResourceSource *map, ResSourceType type, const char *filename, int number) {
- ResourceSource *newsrc = new ResourceSource();
-
- newsrc->source_type = type;
- newsrc->scanned = false;
- newsrc->location_name = filename;
- newsrc->resourceFile = 0;
- newsrc->volume_number = number;
- newsrc->associated_map = map;
- newsrc->audioCompressionType = 0;
- newsrc->audioCompressionOffsetMapping = NULL;
- if (type == kSourceAudioVolume)
- checkIfAudioVolumeIsCompressed(newsrc);
+ResourceSource *ResourceManager::addExternalMap(const Common::FSNode *mapFile, int volume_nr) {
+ ResourceSource *newsrc = new ExtMapResourceSource(mapFile->getName(), volume_nr, mapFile);
_sources.push_back(newsrc);
return newsrc;
}
-ResourceSource *ResourceManager::addSource(ResourceSource *map, ResSourceType type, const Common::FSNode *resFile, int number) {
- ResourceSource *newsrc = new ResourceSource();
-
- newsrc->source_type = type;
- newsrc->scanned = false;
- newsrc->location_name = resFile->getName();
- newsrc->resourceFile = resFile;
- newsrc->volume_number = number;
- newsrc->associated_map = map;
- newsrc->audioCompressionType = 0;
- newsrc->audioCompressionOffsetMapping = NULL;
- if (type == kSourceAudioVolume)
- checkIfAudioVolumeIsCompressed(newsrc);
+ResourceSource *ResourceManager::addSource(ResourceSource *newsrc) {
+ assert(newsrc);
_sources.push_back(newsrc);
return newsrc;
}
-ResourceSource *ResourceManager::addPatchDir(const char *dirname) {
- ResourceSource *newsrc = new ResourceSource();
-
- newsrc->source_type = kSourceDirectory;
- newsrc->resourceFile = 0;
- newsrc->scanned = false;
- newsrc->location_name = dirname;
+ResourceSource *ResourceManager::addPatchDir(const Common::String &dirname) {
+ ResourceSource *newsrc = new DirectoryResourceSource(dirname);
_sources.push_back(newsrc);
return 0;
}
-ResourceSource *ResourceManager::getVolume(ResourceSource *map, int volume_nr) {
+ResourceSource *ResourceManager::findVolume(ResourceSource *map, int volume_nr) {
for (Common::List<ResourceSource *>::iterator it = _sources.begin(); it != _sources.end(); ++it) {
- ResourceSource *src = *it;
- if ((src->source_type == kSourceVolume || src->source_type == kSourceAudioVolume)
- && src->associated_map == map && src->volume_number == volume_nr)
+ ResourceSource *src = (*it)->findVolume(map, volume_nr);
+ if (src)
return src;
}
@@ -258,7 +235,9 @@ ResourceSource *ResourceManager::getVolume(ResourceSource *map, int volume_nr) {
// Resource manager constructors and operations
-bool ResourceManager::loadPatch(Resource *res, Common::SeekableReadStream *file) {
+bool Resource::loadPatch(Common::SeekableReadStream *file) {
+ Resource *res = this;
+
// 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)
@@ -286,27 +265,27 @@ bool ResourceManager::loadPatch(Resource *res, Common::SeekableReadStream *file)
return true;
}
-bool ResourceManager::loadFromPatchFile(Resource *res) {
+bool Resource::loadFromPatchFile() {
Common::File file;
- const char *filename = res->_source->location_name.c_str();
+ const Common::String &filename = _source->getLocationName();
if (file.open(filename) == false) {
- warning("Failed to open patch file %s", filename);
- res->unalloc();
+ warning("Failed to open patch file %s", filename.c_str());
+ unalloc();
return false;
}
// Skip resourceid and header size byte
file.seek(2, SEEK_SET);
- return loadPatch(res, &file);
+ return loadPatch(&file);
}
Common::SeekableReadStream *ResourceManager::getVolumeFile(ResourceSource *source) {
Common::List<Common::File *>::iterator it = _volumeFiles.begin();
Common::File *file;
- if (source->resourceFile)
- return source->resourceFile->createReadStream();
+ if (source->_resourceFile)
+ return source->_resourceFile->createReadStream();
- const char *filename = source->location_name.c_str();
+ const char *filename = source->getLocationName().c_str();
// check if file is already opened
while (it != _volumeFiles.end()) {
@@ -340,107 +319,131 @@ Common::SeekableReadStream *ResourceManager::getVolumeFile(ResourceSource *sourc
static uint32 resTypeToMacTag(ResourceType type);
void ResourceManager::loadResource(Resource *res) {
- if (res->_source->source_type == kSourcePatch && loadFromPatchFile(res))
- return;
+ res->_source->loadResource(this, res);
+}
- if (res->_source->source_type == kSourceMacResourceFork) {
- Common::SeekableReadStream *stream = res->_source->macResMan.getResource(resTypeToMacTag(res->_id.type), res->_id.number);
- if (!stream)
- error("Could not get Mac resource fork resource: %d %d", res->_id.type, res->_id.number);
+void PatchResourceSource::loadResource(ResourceManager *resMan, Resource *res) {
+ bool result = res->loadFromPatchFile();
+ if (!result) {
+ // TODO: We used to fallback to the "default" code here if loadFromPatchFile
+ // failed, but I am not sure whether that is really appropriate.
+ // In fact it looks like a bug to me, so I commented this out for now.
+ //ResourceSource::loadResource(res);
+ }
+}
- int error = decompress(res, stream);
- if (error) {
- warning("Error %d occured while reading %s from Mac resource file: %s",
- error, res->_id.toString().c_str(), sci_error_types[error]);
- res->unalloc();
- }
- return;
+void MacResourceForkResourceSource::loadResource(ResourceManager *resMan, Resource *res) {
+ Common::SeekableReadStream *stream = _macResMan->getResource(resTypeToMacTag(res->getType()), res->getNumber());
+
+ if (!stream)
+ error("Could not get Mac resource fork resource: %d %d", res->getType(), res->getNumber());
+
+ int error = res->decompress(resMan->getVolVersion(), stream);
+ if (error) {
+ warning("Error %d occurred while reading %s from Mac resource file: %s",
+ error, res->_id.toString().c_str(), sci_error_types[error]);
+ res->unalloc();
}
+}
- Common::SeekableReadStream *fileStream = getVolumeFile(res->_source);
+Common::SeekableReadStream *ResourceSource::getVolumeFile(ResourceManager *resMan, Resource *res) {
+ Common::SeekableReadStream *fileStream = resMan->getVolumeFile(this);
if (!fileStream) {
- warning("Failed to open %s", res->_source->location_name.c_str());
- res->unalloc();
- return;
+ warning("Failed to open %s", getLocationName().c_str());
+ if (res)
+ res->unalloc();
}
- switch(res->_source->source_type) {
- case kSourceWave:
- fileStream->seek(res->_fileOffset, SEEK_SET);
- loadFromWaveFile(res, fileStream);
- if (res->_source->resourceFile)
- delete fileStream;
+ return fileStream;
+}
+
+void WaveResourceSource::loadResource(ResourceManager *resMan, Resource *res) {
+ Common::SeekableReadStream *fileStream = getVolumeFile(resMan, res);
+ if (!fileStream)
return;
- case kSourceAudioVolume:
- if (res->_source->audioCompressionType) {
- // this file is compressed, so lookup our offset in the offset-translation table and get the new offset
- // also calculate the compressed size by using the next offset
- int32 *mappingTable = res->_source->audioCompressionOffsetMapping;
- int32 compressedOffset = 0;
-
- do {
- if (*mappingTable == res->_fileOffset) {
- mappingTable++;
- compressedOffset = *mappingTable;
- // Go to next compressed offset and use that to calculate size of compressed sample
- switch (res->_id.type) {
- case kResourceTypeSync:
- case kResourceTypeSync36:
- // we should already have a (valid) size
- break;
- default:
- mappingTable += 2;
- res->size = *mappingTable - compressedOffset;
- }
+ fileStream->seek(res->_fileOffset, SEEK_SET);
+ res->loadFromWaveFile(fileStream);
+ if (_resourceFile)
+ delete fileStream;
+}
+
+void AudioVolumeResourceSource::loadResource(ResourceManager *resMan, Resource *res) {
+ Common::SeekableReadStream *fileStream = getVolumeFile(resMan, res);
+ if (!fileStream)
+ return;
+
+ if (_audioCompressionType) {
+ // this file is compressed, so lookup our offset in the offset-translation table and get the new offset
+ // also calculate the compressed size by using the next offset
+ int32 *mappingTable = _audioCompressionOffsetMapping;
+ int32 compressedOffset = 0;
+
+ do {
+ if (*mappingTable == res->_fileOffset) {
+ mappingTable++;
+ compressedOffset = *mappingTable;
+ // Go to next compressed offset and use that to calculate size of compressed sample
+ switch (res->getType()) {
+ case kResourceTypeSync:
+ case kResourceTypeSync36:
+ // we should already have a (valid) size
break;
+ default:
+ mappingTable += 2;
+ res->size = *mappingTable - compressedOffset;
}
- mappingTable += 2;
- } while (*mappingTable);
-
- if (!compressedOffset)
- error("could not translate offset to compressed offset in audio volume");
- 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, 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
- fileStream->seek(res->_fileOffset, SEEK_SET);
+ mappingTable += 2;
+ } while (*mappingTable);
+
+ if (!compressedOffset)
+ error("could not translate offset to compressed offset in audio volume");
+ fileStream->seek(compressedOffset, SEEK_SET);
+
+ switch (res->getType()) {
+ case kResourceTypeAudio:
+ case kResourceTypeAudio36:
+ // Directly read the stream, compressed audio wont have resource type id and header size for SCI1.1
+ res->loadFromAudioVolumeSCI1(fileStream);
+ if (_resourceFile)
+ delete fileStream;
+ return;
+ default:
+ break;
}
- if (getSciVersion() < SCI_VERSION_1_1)
- loadFromAudioVolumeSCI1(res, fileStream);
- else
- loadFromAudioVolumeSCI11(res, fileStream);
+ } else {
+ // original file, directly seek to given offset and get SCI1/SCI1.1 audio resource
+ fileStream->seek(res->_fileOffset, SEEK_SET);
+ }
+ if (getSciVersion() < SCI_VERSION_1_1)
+ res->loadFromAudioVolumeSCI1(fileStream);
+ else
+ res->loadFromAudioVolumeSCI11(fileStream);
- if (res->_source->resourceFile)
- delete fileStream;
- return;
+ if (_resourceFile)
+ delete fileStream;
+}
- default:
- fileStream->seek(res->_fileOffset, SEEK_SET);
- int error = decompress(res, fileStream);
+void ResourceSource::loadResource(ResourceManager *resMan, Resource *res) {
+ Common::SeekableReadStream *fileStream = getVolumeFile(resMan, res);
+ if (!fileStream)
+ return;
- if (res->_source->resourceFile)
- delete fileStream;
+ fileStream->seek(res->_fileOffset, SEEK_SET);
- if (error) {
- warning("Error %d occured while reading %s from resource file: %s",
- error, res->_id.toString().c_str(), sci_error_types[error]);
- res->unalloc();
- }
+ int error = res->decompress(resMan->getVolVersion(), fileStream);
+ if (error) {
+ warning("Error %d occurred while reading %s from resource file: %s",
+ error, res->_id.toString().c_str(), sci_error_types[error]);
+ res->unalloc();
}
+
+ if (_resourceFile)
+ delete fileStream;
}
Resource *ResourceManager::testResource(ResourceId id) {
@@ -461,12 +464,12 @@ int ResourceManager::addAppropriateSources() {
const char *dot = strrchr(name.c_str(), '.');
int number = atoi(dot + 1);
- addSource(map, kSourceVolume, name.c_str(), number);
+ addSource(new VolumeResourceSource(name, map, number));
}
#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);
+ addSource(new VolumeResourceSource("resource.alt", addExternalMap("alt.map", 10), 10));
#endif
} else if (Common::File::exists("Data1")) {
// Mac SCI1.1+ file naming scheme
@@ -474,18 +477,19 @@ int ResourceManager::addAppropriateSources() {
for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) {
Common::String filename = (*x)->getName();
- addSource(0, kSourceMacResourceFork, filename.c_str(), atoi(filename.c_str() + 4));
+ addSource(new MacResourceForkResourceSource(filename, atoi(filename.c_str() + 4)));
}
#ifdef ENABLE_SCI32
// Mac SCI32 games have extra folders for patches
addPatchDir("Robot Folder");
addPatchDir("Sound Folder");
addPatchDir("Voices Folder");
+ addPatchDir("Voices");
//addPatchDir("VMD Folder");
// There can also be a "Patches" resource fork with patches
if (Common::File::exists("Patches"))
- addSource(0, kSourceMacResourceFork, "Patches", 100);
+ addSource(new MacResourceForkResourceSource("Patches", 100));
} else {
// SCI2.1-SCI3 file naming scheme
Common::ArchiveMemberList mapFiles;
@@ -505,7 +509,7 @@ int ResourceManager::addAppropriateSources() {
int resNumber = atoi(strrchr(resName.c_str(), '.') + 1);
if (mapNumber == resNumber) {
- addSource(addExternalMap(mapName.c_str(), mapNumber), kSourceVolume, resName.c_str(), mapNumber);
+ addSource(new VolumeResourceSource(resName, addExternalMap(mapName, mapNumber), mapNumber));
break;
}
}
@@ -514,7 +518,7 @@ int ResourceManager::addAppropriateSources() {
// SCI2.1 resource patches
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(new VolumeResourceSource("ressci.pat", addExternalMap("resmap.pat", 100), 100));
}
}
#else
@@ -524,7 +528,7 @@ int ResourceManager::addAppropriateSources() {
addPatchDir(".");
if (Common::File::exists("message.map"))
- addSource(addExternalMap("message.map"), kSourceVolume, "resource.msg", 0);
+ addSource(new VolumeResourceSource("resource.msg", addExternalMap("message.map"), 0));
return 1;
}
@@ -568,7 +572,7 @@ int ResourceManager::addAppropriateSources(const Common::FSList &fslist) {
#ifdef ENABLE_SCI32
if (sci21PatchMap && sci21PatchRes)
- addSource(sci21PatchMap, kSourceVolume, sci21PatchRes, 100);
+ addSource(new VolumeResourceSource(sci21PatchRes->getName(), sci21PatchMap, 100, sci21PatchRes));
#endif
// Now find all the resource.0?? files
@@ -583,7 +587,7 @@ int ResourceManager::addAppropriateSources(const Common::FSList &fslist) {
const char *dot = strrchr(filename.c_str(), '.');
int number = atoi(dot + 1);
- addSource(map, kSourceVolume, file, number);
+ addSource(new VolumeResourceSource(file->getName(), map, number, file));
}
}
@@ -598,12 +602,12 @@ int ResourceManager::addInternalSources() {
Common::List<ResourceId>::iterator itr = resources->begin();
while (itr != resources->end()) {
- ResourceSource *src = addSource(NULL, kSourceIntMap, "MAP", itr->number);
+ ResourceSource *src = addSource(new IntMapResourceSource("MAP", itr->getNumber()));
- if ((itr->number == 65535) && Common::File::exists("RESOURCE.SFX"))
- addSource(src, kSourceAudioVolume, "RESOURCE.SFX", 0);
+ if ((itr->getNumber() == 65535) && Common::File::exists("RESOURCE.SFX"))
+ addSource(new AudioVolumeResourceSource(this, "RESOURCE.SFX", src, 0));
else if (Common::File::exists("RESOURCE.AUD"))
- addSource(src, kSourceAudioVolume, "RESOURCE.AUD", 0);
+ addSource(new AudioVolumeResourceSource(this, "RESOURCE.AUD", src, 0));
++itr;
}
@@ -615,38 +619,39 @@ void ResourceManager::scanNewSources() {
for (Common::List<ResourceSource *>::iterator it = _sources.begin(); it != _sources.end(); ++it) {
ResourceSource *source = *it;
- if (!source->scanned) {
- source->scanned = true;
- switch (source->source_type) {
- case kSourceDirectory:
- readResourcePatches(source);
-#ifdef ENABLE_SCI32
- readResourcePatchesBase36(source);
-#endif
- readWaveAudioPatches();
- break;
- case kSourceExtMap:
- if (_mapVersion < kResVersionSci1Late)
- readResourceMapSCI0(source);
- else
- readResourceMapSCI1(source);
- break;
- case kSourceExtAudioMap:
- readAudioMapSCI1(source);
- break;
- case kSourceIntMap:
- readAudioMapSCI11(source);
- break;
- case kSourceMacResourceFork:
- readMacResourceFork(source);
- break;
- default:
- break;
- }
+ if (!source->_scanned) {
+ source->_scanned = true;
+ source->scanSource(this);
}
}
}
+void DirectoryResourceSource::scanSource(ResourceManager *resMan) {
+ resMan->readResourcePatches();
+
+ // We can't use getSciVersion() at this point, thus using _volVersion
+ if (resMan->_volVersion >= kResVersionSci11) // SCI1.1+
+ resMan->readResourcePatchesBase36();
+
+ resMan->readWaveAudioPatches();
+}
+
+void ExtMapResourceSource::scanSource(ResourceManager *resMan) {
+ if (resMan->_mapVersion < kResVersionSci1Late)
+ resMan->readResourceMapSCI0(this);
+ else
+ resMan->readResourceMapSCI1(this);
+}
+
+void ExtAudioMapResourceSource::scanSource(ResourceManager *resMan) {
+ resMan->readAudioMapSCI1(this);
+}
+
+void IntMapResourceSource::scanSource(ResourceManager *resMan) {
+ resMan->readAudioMapSCI11(this);
+}
+
+
void ResourceManager::freeResourceSources() {
for (Common::List<ResourceSource *>::iterator it = _sources.begin(); it != _sources.end(); ++it)
delete *it;
@@ -655,13 +660,6 @@ void ResourceManager::freeResourceSources() {
}
ResourceManager::ResourceManager() {
- addAppropriateSources();
- init();
-}
-
-ResourceManager::ResourceManager(const Common::FSList &fslist) {
- addAppropriateSources(fslist);
- init();
}
void ResourceManager::init() {
@@ -716,7 +714,7 @@ void ResourceManager::init() {
debugC(1, kDebugLevelResMan, "resMan: Detected Amiga graphic resources");
break;
default:
- warning("resMan: Couldn't determine view type");
+ error("resMan: Couldn't determine view type");
}
}
@@ -795,7 +793,7 @@ Common::List<ResourceId> *ResourceManager::listResources(ResourceType type, int
ResourceMap::iterator itr = _resMap.begin();
while (itr != _resMap.end()) {
- if ((itr->_value->_id.type == type) && ((mapNumber == -1) || (itr->_value->_id.number == mapNumber)))
+ if ((itr->_value->getType() == type) && ((mapNumber == -1) || (itr->_value->getNumber() == mapNumber)))
resources->push_back(itr->_value->_id);
++itr;
}
@@ -813,7 +811,7 @@ Resource *ResourceManager::findResource(ResourceId id, bool lock) {
loadResource(retval);
else if (retval->_status == kResStatusEnqueued)
removeFromLRU(retval);
- // Unless an error occured, the resource is now either
+ // Unless an error occurred, the resource is now either
// locked or allocated, but never queued or freed.
freeOldResources();
@@ -876,7 +874,7 @@ const char *ResourceManager::versionDescription(ResVersion version) const {
return "Version not valid";
}
-ResourceManager::ResVersion ResourceManager::detectMapVersion() {
+ResVersion ResourceManager::detectMapVersion() {
Common::SeekableReadStream *fileStream = 0;
byte buff[6];
ResourceSource *rsrc= 0;
@@ -884,17 +882,17 @@ ResourceManager::ResVersion ResourceManager::detectMapVersion() {
for (Common::List<ResourceSource *>::iterator it = _sources.begin(); it != _sources.end(); ++it) {
rsrc = *it;
- if (rsrc->source_type == kSourceExtMap) {
- if (rsrc->resourceFile) {
- fileStream = rsrc->resourceFile->createReadStream();
+ if (rsrc->getSourceType() == kSourceExtMap) {
+ if (rsrc->_resourceFile) {
+ fileStream = rsrc->_resourceFile->createReadStream();
} else {
Common::File *file = new Common::File();
- file->open(rsrc->location_name);
+ file->open(rsrc->getLocationName());
if (file->isOpen())
fileStream = file;
}
break;
- } else if (rsrc->source_type == kSourceMacResourceFork)
+ } else if (rsrc->getSourceType() == kSourceMacResourceFork)
return kResVersionSci11Mac;
}
@@ -909,7 +907,7 @@ ResourceManager::ResVersion ResourceManager::detectMapVersion() {
// check if 0 or 01 - try to read resources in SCI0 format and see if exists
fileStream->seek(0, SEEK_SET);
while (fileStream->read(buff, 6) == 6 && !(buff[0] == 0xFF && buff[1] == 0xFF && buff[2] == 0xFF)) {
- if (getVolume(rsrc, (buff[5] & 0xFC) >> 2) == NULL)
+ if (findVolume(rsrc, (buff[5] & 0xFC) >> 2) == NULL)
return kResVersionSci1Middle;
}
return kResVersionSci0Sci1Early;
@@ -966,24 +964,24 @@ ResourceManager::ResVersion ResourceManager::detectMapVersion() {
return kResVersionUnknown;
}
-ResourceManager::ResVersion ResourceManager::detectVolVersion() {
+ResVersion ResourceManager::detectVolVersion() {
Common::SeekableReadStream *fileStream = 0;
ResourceSource *rsrc;
for (Common::List<ResourceSource *>::iterator it = _sources.begin(); it != _sources.end(); ++it) {
rsrc = *it;
- if (rsrc->source_type == kSourceVolume) {
- if (rsrc->resourceFile) {
- fileStream = rsrc->resourceFile->createReadStream();
+ if (rsrc->getSourceType() == kSourceVolume) {
+ if (rsrc->_resourceFile) {
+ fileStream = rsrc->_resourceFile->createReadStream();
} else {
Common::File *file = new Common::File();
- file->open(rsrc->location_name);
+ file->open(rsrc->getLocationName());
if (file->isOpen())
fileStream = file;
}
break;
- } else if (rsrc->source_type == kSourceMacResourceFork)
+ } else if (rsrc->getSourceType() == kSourceMacResourceFork)
return kResVersionSci11Mac;
}
@@ -1068,41 +1066,41 @@ ResourceManager::ResVersion ResourceManager::detectVolVersion() {
// version-agnostic patch application
void ResourceManager::processPatch(ResourceSource *source, ResourceType resourceType, uint16 resourceNr, uint32 tuple) {
Common::SeekableReadStream *fileStream = 0;
- Resource *newrsc;
+ Resource *newrsc = 0;
ResourceId resId = ResourceId(resourceType, resourceNr, tuple);
- byte patchType, patchDataOffset;
- int fsize;
+ ResourceType checkForType = resourceType;
// 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 (checkForType == kResourceTypeAudio36)
+ checkForType = kResourceTypeAudio;
+ else if (checkForType == kResourceTypeSync36)
+ checkForType = kResourceTypeSync;
- if (source->resourceFile) {
- fileStream = source->resourceFile->createReadStream();
+ 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());
+ if (!file->open(source->getLocationName())) {
+ warning("ResourceManager::processPatch(): failed to open %s", source->getLocationName().c_str());
return;
}
fileStream = file;
}
- fsize = fileStream->size();
+
+ int fsize = fileStream->size();
if (fsize < 3) {
- debug("Patching %s failed - file too small", source->location_name.c_str());
+ debug("Patching %s failed - file too small", source->getLocationName().c_str());
return;
}
- patchType = fileStream->readByte() & 0x7F;
- patchDataOffset = fileStream->readByte();
+ byte patchType = fileStream->readByte() & 0x7F;
+ byte patchDataOffset = fileStream->readByte();
delete fileStream;
- if (patchType != resourceType) {
- debug("Patching %s failed - resource type mismatch", source->location_name.c_str());
+ if (patchType != checkForType) {
+ debug("Patching %s failed - resource type mismatch", source->getLocationName().c_str());
+ return;
}
// Fixes SQ5/German, patch file special case logic taken from SCI View disassembly
@@ -1118,35 +1116,27 @@ void ResourceManager::processPatch(ResourceSource *source, ResourceType resource
patchDataOffset = 8;
break;
default:
- warning("Resource patch unsupported special case %X", patchDataOffset & 0x7F);
+ error("Resource patch unsupported special case %X", patchDataOffset & 0x7F);
return;
}
}
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(), patchDataOffset + 2, fsize);
+ source->getLocationName().c_str(), patchDataOffset + 2, fsize);
return;
}
- // Prepare destination, if neccessary
- if (_resMap.contains(resId) == false) {
- newrsc = new Resource;
- _resMap.setVal(resId, newrsc);
- } else
- newrsc = _resMap.getVal(resId);
+
// Overwrite everything, because we're patching
- newrsc->_id = resId;
- newrsc->_status = kResStatusNoMalloc;
- newrsc->_source = source;
- newrsc->size = fsize - patchDataOffset - 2;
+ newrsc = updateResource(resId, source, 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) {
+ debugC(1, kDebugLevelResMan, "Patching %s - OK", source->getLocationName().c_str());
+}
+
+void ResourceManager::readResourcePatchesBase36() {
// 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
@@ -1176,42 +1166,61 @@ void ResourceManager::readResourcePatchesBase36(ResourceSource *source) {
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);
+ uint16 resourceNr = strtol(Common::String(inputName.c_str(), 3).c_str(), 0, 36); // 3 characters
+ uint16 noun = strtol(Common::String(inputName.c_str() + 3, 2).c_str(), 0, 36); // 2 characters
+ uint16 verb = strtol(Common::String(inputName.c_str() + 5, 2).c_str(), 0, 36); // 2 characters
+ uint16 cond = strtol(Common::String(inputName.c_str() + 7, 2).c_str(), 0, 36); // 2 characters
+ uint16 seq = strtol(Common::String(inputName.c_str() + 9, 1).c_str(), 0, 36); // 1 character
+
+ // Check, if we got valid results
+ if ((noun <= 255) && (verb <= 255) && (cond <= 255) && (seq <= 255)) {
+ 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());
+ */
+
+ // Make sure that the audio patch is a valid resource
+ if (i == kResourceTypeAudio36) {
+ Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(name);
+ uint32 tag = stream->readUint32BE();
+
+ if (tag == MKID_BE('RIFF') || tag == MKID_BE('FORM')) {
+ delete stream;
+ processWavePatch(resource36, name);
+ continue;
+ }
+
+ // Check for SOL as well
+ tag = (tag << 16) | stream->readUint16BE();
+
+ if (tag != MKID_BE('SOL\0')) {
+ delete stream;
+ continue;
+ }
+
+ delete stream;
+ }
+
+ psrcPatch = new PatchResourceSource(name);
+ processPatch(psrcPatch, (ResourceType)i, resourceNr, resource36.getTuple());
+ }
}
}
}
-#endif
-
-void ResourceManager::readResourcePatches(ResourceSource *source) {
+void ResourceManager::readResourcePatches() {
// 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
@@ -1237,10 +1246,12 @@ void ResourceManager::readResourcePatches(ResourceSource *source) {
for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) {
bool bAdd = false;
name = (*x)->getName();
+
// SCI1 scheme
if (isdigit(name[0])) {
- resourceNr = atoi(name.c_str());
- bAdd = true;
+ char *end = 0;
+ resourceNr = strtol(name.c_str(), &end, 10);
+ bAdd = (*end == '.'); // Ensure the next character is the period
} else {
// SCI0 scheme
int resname_len = strlen(szResType);
@@ -1252,10 +1263,7 @@ void ResourceManager::readResourcePatches(ResourceSource *source) {
}
if (bAdd) {
- psrcPatch = new ResourceSource;
- psrcPatch->source_type = kSourcePatch;
- psrcPatch->location_name = name;
- psrcPatch->resourceFile = 0;
+ psrcPatch = new PatchResourceSource(name);
processPatch(psrcPatch, (ResourceType)i, resourceNr);
}
}
@@ -1264,18 +1272,17 @@ void ResourceManager::readResourcePatches(ResourceSource *source) {
int ResourceManager::readResourceMapSCI0(ResourceSource *map) {
Common::SeekableReadStream *fileStream = 0;
- Resource *res;
ResourceType type;
uint16 number, id;
uint32 offset;
- if (map->resourceFile) {
- fileStream = map->resourceFile->createReadStream();
+ 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))
+ if (!file->open(map->getLocationName()))
return SCI_ERROR_RESMAP_NOT_FOUND;
fileStream = file;
}
@@ -1291,7 +1298,7 @@ int ResourceManager::readResourceMapSCI0(ResourceSource *map) {
if (fileStream->eos() || fileStream->err()) {
delete fileStream;
- warning("Error while reading %s", map->location_name.c_str());
+ warning("Error while reading %s", map->getLocationName().c_str());
return SCI_ERROR_RESMAP_NOT_FOUND;
}
if (offset == 0xFFFFFFFF)
@@ -1302,9 +1309,8 @@ int ResourceManager::readResourceMapSCI0(ResourceSource *map) {
ResourceId resId = ResourceId(type, number);
// adding a new resource
if (_resMap.contains(resId) == false) {
- res = new Resource;
- res->_source = getVolume(map, offset >> bShift);
- if (!res->_source) {
+ ResourceSource *source = findVolume(map, offset >> bShift);
+ if (!source) {
warning("Could not get volume for resource %d, VolumeID %d", id, offset >> bShift);
if (_mapVersion != _volVersion) {
warning("Retrying with the detected volume version instead");
@@ -1312,12 +1318,11 @@ int ResourceManager::readResourceMapSCI0(ResourceSource *map) {
_mapVersion = _volVersion;
bMask = (_mapVersion == kResVersionSci1Middle) ? 0xF0 : 0xFC;
bShift = (_mapVersion == kResVersionSci1Middle) ? 28 : 26;
- res->_source = getVolume(map, offset >> bShift);
+ source = findVolume(map, offset >> bShift);
}
}
- res->_fileOffset = offset & (((~bMask) << 24) | 0xFFFFFF);
- res->_id = resId;
- _resMap.setVal(resId, res);
+
+ addResource(resId, source, offset & (((~bMask) << 24) | 0xFFFFFF));
}
} while (!fileStream->eos());
@@ -1327,15 +1332,14 @@ int ResourceManager::readResourceMapSCI0(ResourceSource *map) {
int ResourceManager::readResourceMapSCI1(ResourceSource *map) {
Common::SeekableReadStream *fileStream = 0;
- Resource *res;
- if (map->resourceFile) {
- fileStream = map->resourceFile->createReadStream();
+ 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))
+ if (!file->open(map->getLocationName()))
return SCI_ERROR_RESMAP_NOT_FOUND;
fileStream = file;
}
@@ -1382,22 +1386,17 @@ int ResourceManager::readResourceMapSCI1(ResourceSource *map) {
}
if (fileStream->eos() || fileStream->err()) {
delete fileStream;
- warning("Error while reading %s", map->location_name.c_str());
+ warning("Error while reading %s", map->getLocationName().c_str());
return SCI_ERROR_RESMAP_NOT_FOUND;
}
resId = ResourceId((ResourceType)type, number);
// adding new resource only if it does not exist
if (_resMap.contains(resId) == false) {
- res = new Resource;
- _resMap.setVal(resId, res);
- 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
// 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);
- res->_fileOffset = off;
+ addResource(resId, findVolume(map, volume_nr + map->_volumeNumber), off);
}
}
}
@@ -1438,11 +1437,11 @@ static uint32 resTypeToMacTag(ResourceType type) {
return 0;
}
-int ResourceManager::readMacResourceFork(ResourceSource *source) {
- if (!source->macResMan.open(source->location_name.c_str()))
- error("%s is not a valid Mac resource fork", source->location_name.c_str());
+void MacResourceForkResourceSource::scanSource(ResourceManager *resMan) {
+ if (!_macResMan->open(getLocationName().c_str()))
+ error("%s is not a valid Mac resource fork", getLocationName().c_str());
- Common::MacResTagArray tagArray = source->macResMan.getResTagArray();
+ Common::MacResTagArray tagArray = _macResMan->getResTagArray();
for (uint32 i = 0; i < tagArray.size(); i++) {
ResourceType type = kResourceTypeInvalid;
@@ -1457,11 +1456,11 @@ int ResourceManager::readMacResourceFork(ResourceSource *source) {
if (type == kResourceTypeInvalid)
continue;
- Common::MacResIDArray idArray = source->macResMan.getResIDArray(tagArray[i]);
+ Common::MacResIDArray idArray = _macResMan->getResIDArray(tagArray[i]);
for (uint32 j = 0; j < idArray.size(); j++) {
// Get the size of the file
- Common::SeekableReadStream *stream = source->macResMan.getResource(tagArray[i], idArray[j]);
+ Common::SeekableReadStream *stream = _macResMan->getResource(tagArray[i], idArray[j]);
// Some IBIS resources have a size of 0, so we skip them
if (!stream)
@@ -1472,41 +1471,44 @@ int ResourceManager::readMacResourceFork(ResourceSource *source) {
ResourceId resId = ResourceId(type, idArray[j]);
- Resource *newrsc = NULL;
-
- // Prepare destination, if neccessary. Resource forks may contain patches.
- if (!_resMap.contains(resId)) {
- newrsc = new Resource;
- _resMap.setVal(resId, newrsc);
- } else
- newrsc = _resMap.getVal(resId);
-
- // Overwrite everything
- newrsc->_id = resId;
- newrsc->_status = kResStatusNoMalloc;
- newrsc->_source = source;
- newrsc->size = fileSize;
- newrsc->_headerSize = 0;
+ // Overwrite Resource instance. Resource forks may contain patches.
+ resMan->updateResource(resId, this, fileSize);
}
}
-
- return 0;
}
void ResourceManager::addResource(ResourceId resId, ResourceSource *src, uint32 offset, uint32 size) {
// Adding new resource only if it does not exist
if (_resMap.contains(resId) == false) {
- Resource *res = new Resource;
+ Resource *res = new Resource(resId);
_resMap.setVal(resId, res);
- res->_id = resId;
res->_source = src;
res->_fileOffset = offset;
res->size = size;
}
}
-int ResourceManager::readResourceInfo(Resource *res, Common::SeekableReadStream *file,
- uint32&szPacked, ResourceCompression &compression) {
+Resource *ResourceManager::updateResource(ResourceId resId, ResourceSource *src, uint32 size) {
+ // Update a patched resource, whether it exists or not
+ Resource *res = 0;
+
+ if (_resMap.contains(resId)) {
+ res = _resMap.getVal(resId);
+ } else {
+ res = new Resource(resId);
+ _resMap.setVal(resId, res);
+ }
+
+ res->_status = kResStatusNoMalloc;
+ res->_source = src;
+ res->_headerSize = 0;
+ res->size = size;
+
+ return res;
+}
+
+int Resource::readResourceInfo(ResVersion volVersion, Common::SeekableReadStream *file,
+ uint32 &szPacked, ResourceCompression &compression) {
// SCI0 volume format: {wResId wPacked+4 wUnpacked wCompression} = 8 bytes
// SCI1 volume format: {bResType wResNumber wPacked+4 wUnpacked wCompression} = 9 bytes
// SCI1.1 volume format: {bResType wResNumber wPacked wUnpacked wCompression} = 9 bytes
@@ -1515,7 +1517,7 @@ int ResourceManager::readResourceInfo(Resource *res, Common::SeekableReadStream
uint32 wCompression, szUnpacked;
ResourceType type;
- switch (_volVersion) {
+ switch (volVersion) {
case kResVersionSci0Sci1Early:
case kResVersionSci1Middle:
w = file->readUint16LE();
@@ -1542,8 +1544,8 @@ int ResourceManager::readResourceInfo(Resource *res, Common::SeekableReadStream
case kResVersionSci11Mac:
// Doesn't store this data in the resource. Fortunately,
// we already have this data.
- type = res->_id.type;
- number = res->_id.number;
+ type = getType();
+ number = getNumber();
szPacked = file->size();
szUnpacked = file->size();
wCompression = 0;
@@ -1565,8 +1567,8 @@ int ResourceManager::readResourceInfo(Resource *res, Common::SeekableReadStream
if ((file->eos() || file->err()))
return SCI_ERROR_IO_ERROR;
- res->_id = ResourceId(type, number);
- res->size = szUnpacked;
+ _id = ResourceId(type, number);
+ size = szUnpacked;
// checking compression method
switch (wCompression) {
@@ -1602,15 +1604,15 @@ int ResourceManager::readResourceInfo(Resource *res, Common::SeekableReadStream
return compression == kCompUnknown ? SCI_ERROR_UNKNOWN_COMPRESSION : 0;
}
-int ResourceManager::decompress(Resource *res, Common::SeekableReadStream *file) {
- int error;
+int Resource::decompress(ResVersion volVersion, Common::SeekableReadStream *file) {
+ int errorNum;
uint32 szPacked = 0;
ResourceCompression compression = kCompUnknown;
// fill resource info
- error = readResourceInfo(res, file, szPacked, compression);
- if (error)
- return error;
+ errorNum = readResourceInfo(volVersion, file, szPacked, compression);
+ if (errorNum)
+ return errorNum;
// getting a decompressor
Decompressor *dec = NULL;
@@ -1636,18 +1638,18 @@ int ResourceManager::decompress(Resource *res, Common::SeekableReadStream *file)
break;
#endif
default:
- warning("Resource %s: Compression method %d not supported", res->_id.toString().c_str(), compression);
+ error("Resource %s: Compression method %d not supported", _id.toString().c_str(), compression);
return SCI_ERROR_UNKNOWN_COMPRESSION;
}
- res->data = new byte[res->size];
- res->_status = kResStatusAllocated;
- error = res->data ? dec->unpack(file, res->data, szPacked, res->size) : SCI_ERROR_RESOURCE_TOO_BIG;
- if (error)
- res->unalloc();
+ data = new byte[size];
+ _status = kResStatusAllocated;
+ errorNum = data ? dec->unpack(file, data, szPacked, size) : SCI_ERROR_RESOURCE_TOO_BIG;
+ if (errorNum)
+ unalloc();
delete dec;
- return error;
+ return errorNum;
}
ResourceCompression ResourceManager::getViewCompression() {
@@ -1661,7 +1663,7 @@ ResourceCompression ResourceManager::getViewCompression() {
if (!res)
continue;
- if (res->_source->source_type != kSourceVolume)
+ if (res->_source->getSourceType() != kSourceVolume)
continue;
fileStream = getVolumeFile(res->_source);
@@ -1673,13 +1675,13 @@ ResourceCompression ResourceManager::getViewCompression() {
uint32 szPacked;
ResourceCompression compression;
- if (readResourceInfo(res, fileStream, szPacked, compression)) {
- if (res->_source->resourceFile)
+ if (res->readResourceInfo(_volVersion, fileStream, szPacked, compression)) {
+ if (res->_source->_resourceFile)
delete fileStream;
continue;
}
- if (res->_source->resourceFile)
+ if (res->_source->_resourceFile)
delete fileStream;
if (compression != kCompNone)
@@ -1697,6 +1699,10 @@ ViewType ResourceManager::detectViewType() {
Resource *res = findResource(ResourceId(kResourceTypeView, i), 0);
if (res) {
+ // Skip views coming from patch files
+ if (res->_source->getSourceType() == kSourcePatch)
+ continue;
+
switch (res->data[1]) {
case 128:
// If the 2nd byte is 128, it's a VGA game
@@ -1752,6 +1758,7 @@ ViewType ResourceManager::detectViewType() {
}
}
+ // this may happen if there are serious system issues (or trying to add a broken game)
warning("resMan: Couldn't find any views");
return kViewUnknown;
}
@@ -1862,7 +1869,7 @@ void ResourceManager::detectSciVersion() {
}
}
- warning("Failed to accurately determine SCI version");
+ error("Failed to accurately determine SCI version");
// No parser, we assume SCI_VERSION_01.
s_sciVersion = SCI_VERSION_01;
return;
@@ -1911,15 +1918,18 @@ bool ResourceManager::detectHires() {
// SCI32 picture
uint16 width = READ_LE_UINT16(res->data + 10);
uint16 height = READ_LE_UINT16(res->data + 12);
+ // Surely lowres (e.g. QFG4CD)
if ((width == 320) && ((height == 190) || (height == 200)))
return false;
+ // Surely hires
if ((width >= 600) || (height >= 400))
return true;
}
}
}
- warning("resMan: Couldn't detect hires");
+ // We haven't been able to find hires content
+
return false;
#else
error("no sci32 support");
@@ -1939,12 +1949,27 @@ bool ResourceManager::detectFontExtended() {
return false;
}
+// detects, if SCI1.1 game uses palette merging or copying - this is supposed to only get used on SCI1.1 games
+bool ResourceManager::detectForPaletteMergingForSci11() {
+ // Load palette 999 (default palette)
+ Resource *res = findResource(ResourceId(kResourceTypePalette, 999), false);
+
+ if ((res) && (res->size > 30)) {
+ byte *data = res->data;
+ // Old palette format used in palette resource? -> it's merging
+ if ((data[0] == 0 && data[1] == 1) || (data[0] == 0 && data[1] == 0 && READ_LE_UINT16(data + 29) == 0))
+ return true;
+ return false;
+ }
+ return false;
+}
+
// Functions below are based on PD code by Brian Provinciano (SCI Studio)
bool ResourceManager::hasOldScriptHeader() {
Resource *res = findResource(ResourceId(kResourceTypeScript, 0), 0);
if (!res) {
- warning("resMan: Failed to find script.000");
+ error("resMan: Failed to find script.000");
return false;
}
diff --git a/engines/sci/resource.h b/engines/sci/resource.h
index 533c81bdf5..f66b5b3956 100644
--- a/engines/sci/resource.h
+++ b/engines/sci/resource.h
@@ -23,22 +23,23 @@
*
*/
-#ifndef SCI_SCICORE_RESOURCE_H
-#define SCI_SCICORE_RESOURCE_H
+#ifndef SCI_RESOURCE_H
+#define SCI_RESOURCE_H
-#include "common/fs.h"
-#include "common/macresman.h"
#include "common/str.h"
+#include "common/list.h"
+#include "common/hashmap.h"
#include "sci/graphics/helpers.h" // for ViewType
#include "sci/decompressor.h"
#include "sci/sci.h"
namespace Common {
-class ReadStream;
-class WriteStream;
class File;
+class FSList;
class FSNode;
+class WriteStream;
+class SeekableReadStream;
}
namespace Sci {
@@ -73,18 +74,6 @@ enum {
MAX_OPENED_VOLUMES = 5 ///< Max number of simultaneously opened volumes
};
-enum ResSourceType {
- kSourceDirectory = 0,
- kSourcePatch,
- kSourceVolume,
- kSourceExtMap,
- kSourceIntMap,
- kSourceAudioVolume,
- kSourceExtAudioMap,
- kSourceWave,
- kSourceMacResourceFork
-};
-
enum ResourceType {
kResourceTypeView = 0,
kResourceTypePic,
@@ -109,6 +98,7 @@ enum ResourceType {
kResourceTypeUnknown1, // Translation, currently unsupported
kResourceTypeUnknown2,
kResourceTypeRobot,
+ kResourceTypeVMD,
kResourceTypeInvalid,
// Mac-only resources, these resource types are self-defined
@@ -121,152 +111,199 @@ enum ResourceType {
const char *getResourceTypeName(ResourceType restype);
-class ResourceManager;
-
-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;
+enum ResVersion {
+ kResVersionUnknown,
+ kResVersionSci0Sci1Early,
+ kResVersionSci1Middle,
+ kResVersionSci1Late,
+ kResVersionSci11,
+ kResVersionSci11Mac,
+ kResVersionSci32
};
+class ResourceManager;
+class ResourceSource;
+
class ResourceId {
-public:
- ResourceType type;
- uint16 number;
- uint32 tuple; // Only used for audio36 and sync36
+ static inline ResourceType fixupType(ResourceType type) {
+ if (type < kResourceTypeMacPict || type > kResourceTypeInvalid)
+ return kResourceTypeInvalid;
+ return type;
+ }
- ResourceId() : type(kResourceTypeInvalid), number(0), tuple(0) { }
+ ResourceType _type;
+ uint16 _number;
+ uint32 _tuple; // Only used for audio36 and sync36
+
+public:
+ ResourceId() : _type(kResourceTypeInvalid), _number(0), _tuple(0) { }
ResourceId(ResourceType type_, uint16 number_, uint32 tuple_ = 0)
- : type(type_), number(number_), tuple(tuple_) {
- if (type < kResourceTypeMacPict || type > kResourceTypeInvalid)
- type = kResourceTypeInvalid;
+ : _type(fixupType(type_)), _number(number_), _tuple(tuple_) {
}
ResourceId(ResourceType type_, uint16 number_, byte noun, byte verb, byte cond, byte seq)
- : type(type_), number(number_) {
- tuple = (noun << 24) | (verb << 16) | (cond << 8) | seq;
-
- if ((type < kResourceTypeView) || (type > kResourceTypeInvalid))
- type = kResourceTypeInvalid;
+ : _type(fixupType(type_)), _number(number_) {
+ _tuple = (noun << 24) | (verb << 16) | (cond << 8) | seq;
}
- Common::String toString() {
+ Common::String toString() const {
char buf[32];
- snprintf(buf, 32, "%s.%i", getResourceTypeName(type), number);
+ snprintf(buf, 32, "%s.%i", getResourceTypeName(_type), _number);
Common::String retStr = buf;
- if (tuple != 0) {
- snprintf(buf, 32, "(%i, %i, %i, %i)", tuple >> 24, (tuple >> 16) & 0xff, (tuple >> 8) & 0xff, tuple & 0xff);
+ if (_tuple != 0) {
+ snprintf(buf, 32, "(%i, %i, %i, %i)", _tuple >> 24, (_tuple >> 16) & 0xff, (_tuple >> 8) & 0xff, _tuple & 0xff);
retStr += buf;
}
return retStr;
}
-};
-struct ResourceIdHash : public Common::UnaryFunction<ResourceId, uint> {
- uint operator()(ResourceId val) const { return ((uint)((val.type << 16) | val.number)) ^ val.tuple; }
-};
+ inline ResourceType getType() const { return _type; }
+ inline uint16 getNumber() const { return _number; }
+ inline uint32 getTuple() const { return _tuple; }
-struct ResourceIdEqualTo : public Common::BinaryFunction<ResourceId, ResourceId, bool> {
- bool operator()(const ResourceId &x, const ResourceId &y) const { return (x.type == y.type) && (x.number == y.number) && (x.tuple == y.tuple); }
-};
+ inline uint hash() const {
+ return ((uint)((_type << 16) | _number)) ^ _tuple;
+ }
-struct ResourceIdLess : public Common::BinaryFunction<ResourceId, ResourceId, bool> {
- bool operator()(const ResourceId &x, const ResourceId &y) const {
- return (x.type < y.type) || ((x.type == y.type) && (x.number < y.number))
- || ((x.type == y.type) && (x.number == y.number) && (x.tuple < y.tuple));
+ bool operator==(const ResourceId &other) const {
+ return (_type == other._type) && (_number == other._number) && (_tuple == other._tuple);
+ }
+
+ bool operator<(const ResourceId &other) const {
+ return (_type < other._type) || ((_type == other._type) && (_number < other._number))
+ || ((_type == other._type) && (_number == other._number) && (_tuple < other._tuple));
}
};
+struct ResourceIdHash : public Common::UnaryFunction<ResourceId, uint> {
+ uint operator()(ResourceId val) const { return val.hash(); }
+};
+
/** Class for storing resources in memory */
class Resource {
friend class ResourceManager;
-public:
- Resource();
- ~Resource();
- void unalloc();
-// NOTE : Currently all member data has the same name and public visibility
-// to let the rest of the engine compile without changes
+ // FIXME: These 'friend' declarations are meant to be a temporary hack to
+ // ease transition to the ResourceSource class system.
+ friend class ResourceSource;
+ friend class PatchResourceSource;
+ friend class WaveResourceSource;
+ friend class AudioVolumeResourceSource;
+ friend class MacResourceForkResourceSource;
+
+// NOTE : Currently most member variables lack the underscore prefix and have
+// public visibility to let the rest of the engine compile without changes.
public:
- ResourceId _id;
byte *data;
uint32 size;
byte *_header;
uint32 _headerSize;
+public:
+ Resource(ResourceId id);
+ ~Resource();
+ void unalloc();
+
+ inline ResourceType getType() const { return _id.getType(); }
+ inline uint16 getNumber() const { return _id.getNumber(); }
+ bool isLocked() const { return _status == kResStatusLocked; }
+ /**
+ * Write the resource to the specified stream.
+ * This method is used only by the "dump" debugger command.
+ */
void writeToStream(Common::WriteStream *stream) const;
- uint32 getAudioCompressionType();
+
+ // FIXME: This audio specific method is a hack. After all, why should a
+ // Resource have audio specific methods? But for now we keep this, as it
+ // eases transition.
+ uint32 getAudioCompressionType() const;
protected:
+ ResourceId _id; // TODO: _id could almost be made const, only readResourceInfo() modifies it...
int32 _fileOffset; /**< Offset in file */
ResourceStatus _status;
uint16 _lockers; /**< Number of places where this resource was locked */
ResourceSource *_source;
+
+ bool loadPatch(Common::SeekableReadStream *file);
+ bool loadFromPatchFile();
+ bool loadFromWaveFile(Common::SeekableReadStream *file);
+ bool loadFromAudioVolumeSCI1(Common::SeekableReadStream *file);
+ bool loadFromAudioVolumeSCI11(Common::SeekableReadStream *file);
+ int decompress(ResVersion volVersion, Common::SeekableReadStream *file);
+ int readResourceInfo(ResVersion volVersion, Common::SeekableReadStream *file, uint32 &szPacked, ResourceCompression &compression);
};
-typedef Common::HashMap<ResourceId, Resource *, ResourceIdHash, ResourceIdEqualTo> ResourceMap;
+typedef Common::HashMap<ResourceId, Resource *, ResourceIdHash> ResourceMap;
class ResourceManager {
-public:
- enum ResVersion {
- kResVersionUnknown,
- kResVersionSci0Sci1Early,
- kResVersionSci1Middle,
- kResVersionSci1Late,
- kResVersionSci11,
- kResVersionSci11Mac,
- kResVersionSci32
- };
+ // FIXME: These 'friend' declarations are meant to be a temporary hack to
+ // ease transition to the ResourceSource class system.
+ friend class ResourceSource;
+ friend class DirectoryResourceSource;
+ friend class PatchResourceSource;
+ friend class ExtMapResourceSource;
+ friend class IntMapResourceSource;
+ friend class AudioVolumeResourceSource;
+ friend class ExtAudioMapResourceSource;
+ friend class WaveResourceSource;
+ friend class MacResourceForkResourceSource;
+public:
/**
* Creates a new SCI resource manager.
*/
ResourceManager();
- ResourceManager(const Common::FSList &fslist);
~ResourceManager();
+
+ /**
+ * Initializes the resource manager.
+ */
+ void init();
+
+ int addAppropriateSources();
+ int addAppropriateSources(const Common::FSList &fslist); // TODO: Switch from FSList to Common::Archive?
+
/**
* Looks up a resource's data.
- * @param id: The resource type to look for
- * @param lock: non-zero iff the resource should be locked
- * @return (Resource *): The resource, or NULL if it doesn't exist
+ * @param id The resource type to look for
+ * @param lock non-zero iff the resource should be locked
+ * @return The resource, or NULL if it doesn't exist
* @note Locked resources are guaranteed not to have their contents freed until
* they are unlocked explicitly (by unlockResource).
*/
Resource *findResource(ResourceId id, bool lock);
- /* Unlocks a previously locked resource
- ** (Resource *) res: The resource to free
- ** Returns : ()
- */
+ /**
+ * Unlocks a previously locked resource.
+ * @param res The resource to free
+ */
void unlockResource(Resource *res);
- /* Tests whether a resource exists
- ** (ResourceId) id: Id of the resource to check
- ** Returns : (Resource *) non-NULL if the resource exists, NULL otherwise
- ** This function may often be much faster than finding the resource
- ** and should be preferred for simple tests.
- ** The resource object returned is, indeed, the resource in question, but
- ** it should be used with care, as it may be unallocated.
- ** Use scir_find_resource() if you want to use the data contained in the resource.
- */
+ /**
+ * Tests whether a resource exists.
+ *
+ * This function may often be much faster than finding the resource
+ * and should be preferred for simple tests.
+ * The resource object returned is, indeed, the resource in question, but
+ * it should be used with care, as it may be unallocated.
+ * Use scir_find_resource() if you want to use the data contained in the resource.
+ *
+ * @param id Id of the resource to check
+ * @return non-NULL if the resource exists, NULL otherwise
+ */
Resource *testResource(ResourceId id);
/**
* Returns a list of all resources of the specified type.
- * @param type: The resource type to look for
- * @param mapNumber: For audio36 and sync36, limit search to this map
- * @return: The resource list
+ * @param type The resource type to look for
+ * @param mapNumber For audio36 and sync36, limit search to this map
+ * @return The resource list
*/
Common::List<ResourceId> *listResources(ResourceType type, int mapNumber = -1);
@@ -278,27 +315,30 @@ public:
ViewType getViewType() const { return _viewType; }
const char *getMapVersionDesc() const { return versionDescription(_mapVersion); }
const char *getVolVersionDesc() const { return versionDescription(_volVersion); }
+ ResVersion getVolVersion() const { return _volVersion; }
/**
* Adds the appropriate GM patch from the Sierra MIDI utility as 4.pat, without
* requiring the user to rename the file to 4.pat. Thus, the original Sierra
* archive can be extracted in the extras directory, and the GM patches can be
- * applied per game, if applicable
+ * applied per game, if applicable.
*/
- void addNewGMPatch(const Common::String &gameId);
+ void addNewGMPatch(SciGameId gameId);
bool detectHires();
// Detects, if standard font of current game includes extended characters (>0x80)
bool detectFontExtended();
+ // Detects, if SCI1.1 game uses palette merging
+ bool detectForPaletteMergingForSci11();
/**
- * Finds the internal Sierra ID of the current game from script 0
+ * Finds the internal Sierra ID of the current game from script 0.
*/
Common::String findSierraGameId();
/**
- * Finds the location of the game object from script 0
- * @param addSci11ScriptOffset: Adjust the return value for SCI1.1 and newer
+ * 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().
*/
@@ -325,85 +365,54 @@ protected:
ResVersion _mapVersion; ///< resource.map version
/**
- * Initializes the resource manager
- */
- void init();
-
- /**
* Add a path to the resource manager's list of sources.
* @return a pointer to the added source structure, or NULL if an error occurred.
*/
- ResourceSource *addPatchDir(const char *path);
+ ResourceSource *addPatchDir(const Common::String &path);
- ResourceSource *getVolume(ResourceSource *map, int volume_nr);
+ ResourceSource *findVolume(ResourceSource *map, int volume_nr);
/**
* Adds a source to the resource manager's list of sources.
- * @param map The map associated with this source
- * @param type The source type
- * @param filename The name of the source to add
- * @return A pointer to the added source structure, or NULL if an error occurred.
+ * @param source The new source to add
+ * @return A pointer to the added source structure, or NULL if an error occurred.
*/
- ResourceSource *addSource(ResourceSource *map, ResSourceType type, const char *filename,
- int number);
-
- ResourceSource *addSource(ResourceSource *map, ResSourceType type,
- const Common::FSNode *resFile, int number);
+ ResourceSource *addSource(ResourceSource *source);
/**
- * Add an external (i.e., separate file) map resource to the resource manager's list of sources.
- * @param file_name The name of the volume to add
+ * Add an external (i.e., separate file) map resource to the resource
+ * manager's list of sources.
+ * @param filename The name of the volume to add
* @param volume_nr The volume number the map starts at, 0 for <SCI2.1
* @return A pointer to the added source structure, or NULL if an error occurred.
*/
- ResourceSource *addExternalMap(const char *file_name, int volume_nr = 0);
+ ResourceSource *addExternalMap(const Common::String &filename, int volume_nr = 0);
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.
- * @param name The name of the resource to add
- * @param resNr The map resource number
- * @return A pointer to the added source structure, or NULL if an error occurred.
- */
- ResourceSource *addInternalMap(const char *name, int resNr);
-
- /**
- * Checks, if an audio volume got compressed by our tool. If that's the case, it will set audioCompressionType
- * and read in the offset translation table for later usage.
- */
- void checkIfAudioVolumeIsCompressed(ResourceSource *source);
-
- /**
* Scans newly registered resource sources for resources, earliest addition first.
- * @param detected_version: Pointer to the detected version number,
+ * @param detected_version Pointer to the detected version number,
* used during startup. May be NULL.
* @return One of SCI_ERROR_*.
*/
void scanNewSources();
- int addAppropriateSources();
- int addAppropriateSources(const Common::FSList &fslist);
+
int addInternalSources();
void freeResourceSources();
/**
- * Returns a string describing a ResVersion
- * @param version: The resource version
- * @return: The description of version
+ * Returns a string describing a ResVersion.
+ * @param version The resource version
+ * @return The description of version
*/
const char *versionDescription(ResVersion version) const;
Common::SeekableReadStream *getVolumeFile(ResourceSource *source);
void loadResource(Resource *res);
- bool loadPatch(Resource *res, Common::SeekableReadStream *file);
- bool loadFromPatchFile(Resource *res);
- 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);
void addResource(ResourceId resId, ResourceSource *src, uint32 offset, uint32 size = 0);
+ Resource *updateResource(ResourceId resId, ResourceSource *src, uint32 size);
void removeAudioResource(ResourceId resId);
/**--- Resource map decoding functions ---*/
@@ -423,23 +432,16 @@ protected:
* @return 0 on success, an SCI_ERROR_* code otherwise
*/
int readResourceMapSCI1(ResourceSource *map);
-
- /**
- * Reads the SCI1.1+ resource file from a Mac resource fork.
- * @param source The source
- * @return 0 on success, an SCI_ERROR_* code otherwise
- */
- int readMacResourceFork(ResourceSource *source);
/**
- * Reads SCI1.1 audio map resources
+ * Reads SCI1.1 audio map resources.
* @param map The map
* @return 0 on success, an SCI_ERROR_* code otherwise
*/
int readAudioMapSCI11(ResourceSource *map);
/**
- * Reads SCI1 audio map files
+ * Reads SCI1 audio map files.
* @param map The map
* @param unload Unload the map instead of loading it
* @return 0 on success, an SCI_ERROR_* code otherwise
@@ -451,16 +453,15 @@ protected:
/**
* Reads patch files from a local directory.
*/
- void readResourcePatches(ResourceSource *source);
-#ifdef ENABLE_SCI32
- void readResourcePatchesBase36(ResourceSource *source);
-#endif
+ void readResourcePatches();
+ void readResourcePatchesBase36();
void processPatch(ResourceSource *source, ResourceType resourceType, uint16 resourceNr, uint32 tuple = 0);
/**
- * Process wave files as patches for Audio resources
+ * Process wave files as patches for Audio resources.
*/
void readWaveAudioPatches();
+ void processWavePatch(ResourceId resourceId, Common::String name);
/**
* Applies to all versions before 0.000.395 (i.e. KQ4 old, XMAS 1988 and LSL2).
@@ -491,6 +492,7 @@ public:
uint16 prio;
uint16 size;
byte *data;
+ uint16 curPos;
long time;
byte prev;
};
@@ -515,7 +517,6 @@ 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;
@@ -523,11 +524,8 @@ private:
Track *_tracks;
Resource *_innerResource;
ResourceManager *_resMan;
- uint16 _channelsUsed;
-
- void setChannelUsed(byte channel) { _channelsUsed |= (1 << channel); }
};
} // End of namespace Sci
-#endif // SCI_SCICORE_RESOURCE_H
+#endif // SCI_RESOURCE_H
diff --git a/engines/sci/resource_audio.cpp b/engines/sci/resource_audio.cpp
index 57efbdcb38..13aa81cb24 100644
--- a/engines/sci/resource_audio.cpp
+++ b/engines/sci/resource_audio.cpp
@@ -25,20 +25,30 @@
// Resource library
+#include "common/archive.h"
#include "common/file.h"
#include "sci/resource.h"
+#include "sci/resource_intern.h"
#include "sci/util.h"
namespace Sci {
-void ResourceManager::checkIfAudioVolumeIsCompressed(ResourceSource *source) {
- Common::SeekableReadStream *fileStream = getVolumeFile(source);
+AudioVolumeResourceSource::AudioVolumeResourceSource(ResourceManager *resMan, const Common::String &name, ResourceSource *map, int volNum)
+ : VolumeResourceSource(name, map, volNum, kSourceAudioVolume) {
- if (!fileStream) {
- warning("Failed to open %s", source->location_name.c_str());
+ _audioCompressionType = 0;
+ _audioCompressionOffsetMapping = NULL;
+
+ /*
+ * Check if this audio volume got compressed by our tool. If that is the
+ * case, set _audioCompressionType and read in the offset translation
+ * table for later usage.
+ */
+
+ Common::SeekableReadStream *fileStream = getVolumeFile(resMan, 0);
+ if (!fileStream)
return;
- }
fileStream->seek(0, SEEK_SET);
uint32 compressionType = fileStream->readUint32BE();
@@ -47,13 +57,13 @@ void ResourceManager::checkIfAudioVolumeIsCompressed(ResourceSource *source) {
case MKID_BE('OGG '):
case MKID_BE('FLAC'):
// Detected a compressed audio volume
- source->audioCompressionType = compressionType;
+ _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;
+ _audioCompressionOffsetMapping = offsetMapping;
for (int recordNo = 0; recordNo < recordCount; recordNo++) {
*offsetMapping++ = fileStream->readUint32LE();
*offsetMapping++ = fileStream->readUint32LE();
@@ -63,105 +73,124 @@ void ResourceManager::checkIfAudioVolumeIsCompressed(ResourceSource *source) {
*offsetMapping++ = fileStream->size();
}
- if (source->resourceFile)
+ if (_resourceFile)
delete fileStream;
}
-bool ResourceManager::loadFromWaveFile(Resource *res, Common::SeekableReadStream *file) {
- res->data = new byte[res->size];
+bool Resource::loadFromWaveFile(Common::SeekableReadStream *file) {
+ data = new byte[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);
+ uint32 really_read = file->read(data, size);
+ if (really_read != size)
+ error("Read %d bytes from %s but expected %d", really_read, _id.toString().c_str(), size);
- res->_status = kResStatusAllocated;
+ _status = kResStatusAllocated;
return true;
}
-bool ResourceManager::loadFromAudioVolumeSCI11(Resource *res, Common::SeekableReadStream *file) {
+bool Resource::loadFromAudioVolumeSCI11(Common::SeekableReadStream *file) {
// Check for WAVE files here
uint32 riffTag = file->readUint32BE();
if (riffTag == MKID_BE('RIFF')) {
- res->_headerSize = 0;
- res->size = file->readUint32LE();
+ _headerSize = 0;
+ size = file->readUint32LE();
file->seek(-8, SEEK_CUR);
- return loadFromWaveFile(res, file);
+ return loadFromWaveFile(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();
+ if (((getType() == kResourceTypeAudio || getType() == kResourceTypeAudio36) && (type != kResourceTypeAudio))
+ || ((getType() == kResourceTypeSync || getType() == kResourceTypeSync36) && (type != kResourceTypeSync))) {
+ warning("Resource type mismatch loading %s", _id.toString().c_str());
+ unalloc();
return false;
}
- res->_headerSize = file->readByte();
+ _headerSize = file->readByte();
if (type == kResourceTypeAudio) {
- if (res->_headerSize != 11 && res->_headerSize != 12) {
+ if (_headerSize != 7 && _headerSize != 11 && _headerSize != 12) {
warning("Unsupported audio header");
- res->unalloc();
+ 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);
+ if (_headerSize != 7) { // Size is defined already from the map
+ // Load sample size
+ file->seek(7, SEEK_CUR);
+ size = file->readUint32LE();
+ // Adjust offset to point at the header data again
+ file->seek(-11, SEEK_CUR);
+ }
}
- return loadPatch(res, file);
+ return loadPatch(file);
}
-bool ResourceManager::loadFromAudioVolumeSCI1(Resource *res, Common::SeekableReadStream *file) {
- res->data = new byte[res->size];
+bool Resource::loadFromAudioVolumeSCI1(Common::SeekableReadStream *file) {
+ data = new byte[size];
- if (res->data == NULL) {
- error("Can't allocate %d bytes needed for loading %s", res->size, res->_id.toString().c_str());
+ if (data == NULL) {
+ error("Can't allocate %d bytes needed for loading %s", size, _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);
+ unsigned int really_read = file->read(data, size);
+ if (really_read != size)
+ warning("Read %d bytes from %s but expected %d", really_read, _id.toString().c_str(), size);
- res->_status = kResStatusAllocated;
+ _status = kResStatusAllocated;
return true;
}
-void ResourceManager::addNewGMPatch(const Common::String &gameId) {
+void ResourceManager::addNewGMPatch(SciGameId gameId) {
Common::String gmPatchFile;
- if (gameId == "ecoquest")
+ switch (gameId) {
+ case GID_ECOQUEST:
gmPatchFile = "ECO1GM.PAT";
- else if (gameId == "hoyle3")
- gmPatchFile = "HOY3GM.PAT";
- else if (gameId == "hoyle3")
+ break;
+ case GID_HOYLE3:
gmPatchFile = "HOY3GM.PAT";
- else if (gameId == "lsl1sci")
+ break;
+ case GID_LSL1:
gmPatchFile = "LL1_GM.PAT";
- else if (gameId == "lsl5")
+ break;
+ case GID_LSL5:
gmPatchFile = "LL5_GM.PAT";
- else if (gameId == "longbow")
+ break;
+ case GID_LONGBOW:
gmPatchFile = "ROBNGM.PAT";
- else if (gameId == "sq1sci")
+ break;
+ case GID_SQ1:
gmPatchFile = "SQ1_GM.PAT";
- else if (gameId == "sq4")
+ break;
+ case GID_SQ4:
gmPatchFile = "SQ4_GM.PAT";
- else if (gameId == "fairytales")
+ break;
+ case GID_FAIRYTALES:
gmPatchFile = "TALEGM.PAT";
+ break;
+ default:
+ break;
+ }
if (!gmPatchFile.empty() && Common::File::exists(gmPatchFile)) {
- ResourceSource *psrcPatch = new ResourceSource;
- psrcPatch->source_type = kSourcePatch;
- psrcPatch->resourceFile = 0;
- psrcPatch->location_name = gmPatchFile;
+ ResourceSource *psrcPatch = new PatchResourceSource(gmPatchFile);
processPatch(psrcPatch, kResourceTypePatch, 4);
}
}
+void ResourceManager::processWavePatch(ResourceId resourceId, Common::String name) {
+ ResourceSource *resSrc = new WaveResourceSource(name);
+ Common::File file;
+ file.open(name);
+
+ updateResource(resourceId, resSrc, file.size());
+
+ debugC(1, kDebugLevelResMan, "Patching %s - OK", name.c_str());
+}
+
void ResourceManager::readWaveAudioPatches() {
// Here we do check for SCI1.1+ so we can patch wav files in as audio resources
Common::ArchiveMemberList files;
@@ -170,39 +199,8 @@ void ResourceManager::readWaveAudioPatches() {
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());
- }
+ if (isdigit(name[0]))
+ processWavePatch(ResourceId(kResourceTypeAudio, atoi(name.c_str())), name);
}
}
@@ -211,7 +209,7 @@ void ResourceManager::removeAudioResource(ResourceId resId) {
if (_resMap.contains(resId)) {
Resource *res = _resMap.getVal(resId);
- if (res->_source->source_type == kSourceAudioVolume) {
+ if (res->_source->getSourceType() == kSourceAudioVolume) {
if (res->_status == kResStatusLocked) {
warning("Failed to remove resource %s (still in use)", resId.toString().c_str());
} else {
@@ -261,27 +259,31 @@ void ResourceManager::removeAudioResource(ResourceId resId) {
// 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);
+ Resource *mapRes = findResource(ResourceId(kResourceTypeMap, map->_volumeNumber), false);
if (!mapRes) {
- warning("Failed to open %i.MAP", map->volume_number);
+ warning("Failed to open %i.MAP", map->_volumeNumber);
return SCI_ERROR_RESMAP_NOT_FOUND;
}
- ResourceSource *src = getVolume(map, 0);
+ ResourceSource *src = findVolume(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;
+ // Heuristic to detect entry size
+ uint32 entrySize = 0;
+ for (int i = mapRes->size - 1; i >= 0; --i) {
+ if (ptr[i] == 0xff)
+ entrySize++;
+ else
+ break;
+ }
+ if (map->_volumeNumber == 65535) {
while (ptr < mapRes->data + mapRes->size) {
uint16 n = READ_LE_UINT16(ptr);
ptr += 2;
@@ -289,7 +291,7 @@ int ResourceManager::readAudioMapSCI11(ResourceSource *map) {
if (n == 0xffff)
break;
- if (isEarly) {
+ if (entrySize == 6) {
offset = READ_LE_UINT32(ptr);
ptr += 4;
} else {
@@ -299,10 +301,25 @@ int ResourceManager::readAudioMapSCI11(ResourceSource *map) {
addResource(ResourceId(kResourceTypeAudio, n), src, offset);
}
+ } else if (map->_volumeNumber == 0 && entrySize == 10 && ptr[3] == 0) {
+ // QFG3 demo format
+ // ptr[3] would be 'seq' in the normal format and cannot possibly be 0
+ while (ptr < mapRes->data + mapRes->size) {
+ uint16 n = READ_BE_UINT16(ptr);
+ ptr += 2;
+
+ if (n == 0xffff)
+ break;
+
+ offset = READ_LE_UINT32(ptr);
+ ptr += 4;
+ uint32 size = READ_LE_UINT32(ptr);
+ ptr += 4;
+
+ addResource(ResourceId(kResourceTypeAudio, n), src, offset, size);
+ }
} else {
- // Heuristic to detect late SCI1.1 map format
- if ((mapRes->size >= 11) && (ptr[mapRes->size - 11] == 0xff))
- isEarly = false;
+ bool isEarly = (entrySize != 11);
if (!isEarly) {
offset = READ_LE_UINT32(ptr);
@@ -330,15 +347,17 @@ int ResourceManager::readAudioMapSCI11(ResourceSource *map) {
ptr += 2;
if (syncSize > 0)
- addResource(ResourceId(kResourceTypeSync36, map->volume_number, n & 0xffffff3f), src, offset, syncSize);
+ addResource(ResourceId(kResourceTypeSync36, map->_volumeNumber, n & 0xffffff3f), src, offset, syncSize);
}
if (n & 0x40) {
+ // This seems to define the size of raw lipsync data (at least
+ // in kq6), may also just be general appended data.
syncSize += READ_LE_UINT16(ptr);
ptr += 2;
}
- addResource(ResourceId(kResourceTypeAudio36, map->volume_number, n & 0xffffff3f), src, offset + syncSize);
+ addResource(ResourceId(kResourceTypeAudio36, map->_volumeNumber, n & 0xffffff3f), src, offset + syncSize);
}
}
@@ -358,7 +377,7 @@ int ResourceManager::readAudioMapSCI11(ResourceSource *map) {
int ResourceManager::readAudioMapSCI1(ResourceSource *map, bool unload) {
Common::File file;
- if (!file.open(map->location_name))
+ if (!file.open(map->getLocationName()))
return SCI_ERROR_RESMAP_NOT_FOUND;
bool oldFormat = (file.readUint16LE() >> 11) == kResourceTypeAudio;
@@ -370,7 +389,7 @@ int ResourceManager::readAudioMapSCI1(ResourceSource *map, bool unload) {
uint32 size = file.readUint32LE();
if (file.eos() || file.err()) {
- warning("Error while reading %s", map->location_name.c_str());
+ warning("Error while reading %s", map->getLocationName().c_str());
return SCI_ERROR_RESMAP_NOT_FOUND;
}
@@ -388,7 +407,7 @@ int ResourceManager::readAudioMapSCI1(ResourceSource *map, bool unload) {
offset &= 0x0fffffff; // least significant 28 bits
}
- ResourceSource *src = getVolume(map, volume_nr);
+ ResourceSource *src = findVolume(map, volume_nr);
if (src) {
if (unload)
@@ -405,7 +424,7 @@ int ResourceManager::readAudioMapSCI1(ResourceSource *map, bool unload) {
void ResourceManager::setAudioLanguage(int language) {
if (_audioMapSCI1) {
- if (_audioMapSCI1->volume_number == language) {
+ if (_audioMapSCI1->_volumeNumber == language) {
// This language is already loaded
return;
}
@@ -417,7 +436,7 @@ void ResourceManager::setAudioLanguage(int language) {
Common::List<ResourceSource *>::iterator it = _sources.begin();
while (it != _sources.end()) {
ResourceSource *src = *it;
- if (src->associated_map == _audioMapSCI1) {
+ if (src->findVolume(_audioMapSCI1, src->_volumeNumber)) {
it = _sources.erase(it);
delete src;
} else {
@@ -441,7 +460,7 @@ void ResourceManager::setAudioLanguage(int language) {
return;
}
- _audioMapSCI1 = addSource(NULL, kSourceExtAudioMap, fullname.c_str(), language);
+ _audioMapSCI1 = addSource(new ExtAudioMapResourceSource(fullname, language));
// Search for audio volumes for this language and add them to the source list
Common::ArchiveMemberList files;
@@ -451,18 +470,18 @@ void ResourceManager::setAudioLanguage(int language) {
const char *dot = strrchr(name.c_str(), '.');
int number = atoi(dot + 1);
- addSource(_audioMapSCI1, kSourceAudioVolume, name.c_str(), number);
+ addSource(new AudioVolumeResourceSource(this, name, _audioMapSCI1, number));
}
scanNewSources();
}
int ResourceManager::getAudioLanguage() const {
- return (_audioMapSCI1 ? _audioMapSCI1->volume_number : 0);
+ return (_audioMapSCI1 ? _audioMapSCI1->_volumeNumber : 0);
}
-SoundResource::SoundResource(uint32 resNumber, ResourceManager *resMan, SciVersion soundVersion) : _resMan(resMan), _soundVersion(soundVersion) {
- Resource *resource = _resMan->findResource(ResourceId(kResourceTypeSound, resNumber), true);
+SoundResource::SoundResource(uint32 resourceNr, ResourceManager *resMan, SciVersion soundVersion) : _resMan(resMan), _soundVersion(soundVersion) {
+ Resource *resource = _resMan->findResource(ResourceId(kResourceTypeSound, resourceNr), true);
int trackNr, channelNr;
if (!resource)
return;
@@ -473,8 +492,6 @@ SoundResource::SoundResource(uint32 resNumber, ResourceManager *resMan, SciVersi
byte *dataEnd;
Channel *channel, *sampleChannel;
- _channelsUsed = 0;
-
switch (_soundVersion) {
case SCI_VERSION_0_EARLY:
case SCI_VERSION_0_LATE:
@@ -537,6 +554,9 @@ SoundResource::SoundResource(uint32 resNumber, ResourceManager *resMan, SciVersi
}
_tracks = new Track[_trackCount];
data = resource->data;
+
+ byte channelCount;
+
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
@@ -546,35 +566,47 @@ SoundResource::SoundResource(uint32 resNumber, ResourceManager *resMan, SciVersi
_tracks[trackNr].type = *data++;
// Counting # of channels used
data2 = data;
- _tracks[trackNr].channelCount = 0;
+ channelCount = 0;
while (*data2 != 0xFF) {
data2 += 6;
+ channelCount++;
_tracks[trackNr].channelCount++;
}
- _tracks[trackNr].channels = new Channel[_tracks[trackNr].channelCount];
+ _tracks[trackNr].channels = new Channel[channelCount];
+ _tracks[trackNr].channelCount = 0;
_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++) {
+ channelNr = 0;
+ while (channelCount--) {
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;
+ uint dataOffset = READ_LE_UINT16(data + 2);
+ if (dataOffset < resource->size) {
+ channel->data = resource->data + dataOffset;
+ channel->size = READ_LE_UINT16(data + 4);
+ channel->curPos = 0;
+ channel->number = *channel->data;
+ channel->poly = *(channel->data + 1);
+ channel->time = channel->prev = 0;
+ channel->data += 2; // skip over header
+ channel->size -= 2; // remove header size
+ 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;
+ }
+ _tracks[trackNr].channelCount++;
+ channelNr++;
+ } else {
+ warning("Invalid offset inside sound resource %d: track %d, channel %d", resourceNr, trackNr, channelNr);
}
data += 6;
}
diff --git a/engines/sci/resource_intern.h b/engines/sci/resource_intern.h
new file mode 100644
index 0000000000..45421dd722
--- /dev/null
+++ b/engines/sci/resource_intern.h
@@ -0,0 +1,193 @@
+/* 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 SCI_RESOURCE_INTERN_H
+#define SCI_RESOURCE_INTERN_H
+
+#include "sci/resource.h"
+
+namespace Common {
+ class MacResManager;
+}
+
+namespace Sci {
+
+enum ResSourceType {
+ kSourceDirectory = 0,
+ kSourcePatch,
+ kSourceVolume,
+ kSourceExtMap,
+ kSourceIntMap,
+ kSourceAudioVolume,
+ kSourceExtAudioMap,
+ kSourceWave,
+ kSourceMacResourceFork
+};
+
+
+class ResourceSource {
+protected:
+ const ResSourceType _sourceType;
+ const Common::String _name;
+
+public:
+ bool _scanned;
+ const Common::FSNode * const _resourceFile;
+ const int _volumeNumber;
+
+protected:
+ ResourceSource(ResSourceType type, const Common::String &name, int volNum = 0, const Common::FSNode *resFile = 0);
+public:
+ virtual ~ResourceSource();
+
+ ResSourceType getSourceType() const { return _sourceType; }
+ const Common::String &getLocationName() const { return _name; }
+
+ // Auxiliary method, used by loadResource implementations.
+ Common::SeekableReadStream *getVolumeFile(ResourceManager *resMan, Resource *res);
+
+ /**
+ * TODO: Document this
+ */
+ virtual ResourceSource *findVolume(ResourceSource *map, int volNum) {
+ return NULL;
+ };
+
+ /**
+ * Scan this source for TODO.
+ */
+ virtual void scanSource(ResourceManager *resMan) {}
+
+ /**
+ * Load a resource.
+ */
+ virtual void loadResource(ResourceManager *resMan, Resource *res);
+
+ // FIXME: This audio specific method is a hack. After all, why should a
+ // ResourceSource or a Resource (which uses this method) have audio
+ // specific methods? But for now we keep this, as it eases transition.
+ virtual uint32 getAudioCompressionType() const { return 0; }
+};
+
+class DirectoryResourceSource : public ResourceSource {
+public:
+ DirectoryResourceSource(const Common::String &name) : ResourceSource(kSourceDirectory, name) {}
+
+ virtual void scanSource(ResourceManager *resMan);
+};
+
+class PatchResourceSource : public ResourceSource {
+public:
+ PatchResourceSource(const Common::String &name) : ResourceSource(kSourcePatch, name) {}
+
+ virtual void loadResource(ResourceManager *resMan, Resource *res);
+};
+
+class VolumeResourceSource : public ResourceSource {
+protected:
+ ResourceSource * const _associatedMap;
+
+public:
+ VolumeResourceSource(const Common::String &name, ResourceSource *map, int volNum, ResSourceType type = kSourceVolume)
+ : ResourceSource(type, name, volNum), _associatedMap(map) {
+ }
+
+ VolumeResourceSource(const Common::String &name, ResourceSource *map, int volNum, const Common::FSNode *resFile)
+ : ResourceSource(kSourceVolume, name, volNum, resFile), _associatedMap(map) {
+ }
+
+ virtual ResourceSource *findVolume(ResourceSource *map, int volNum) {
+ if (_associatedMap == map && _volumeNumber == volNum)
+ return this;
+ return NULL;
+ };
+};
+
+class ExtMapResourceSource : public ResourceSource {
+public:
+ ExtMapResourceSource(const Common::String &name, int volNum, const Common::FSNode *resFile = 0)
+ : ResourceSource(kSourceExtMap, name, volNum, resFile) {
+ }
+
+ virtual void scanSource(ResourceManager *resMan);
+};
+
+class IntMapResourceSource : public ResourceSource {
+public:
+ IntMapResourceSource(const Common::String &name, int volNum)
+ : ResourceSource(kSourceIntMap, name, volNum) {
+ }
+
+ virtual void scanSource(ResourceManager *resMan);
+};
+
+class AudioVolumeResourceSource : public VolumeResourceSource {
+protected:
+ uint32 _audioCompressionType;
+ int32 *_audioCompressionOffsetMapping;
+
+public:
+ AudioVolumeResourceSource(ResourceManager *resMan, const Common::String &name, ResourceSource *map, int volNum);
+
+ virtual void loadResource(ResourceManager *resMan, Resource *res);
+
+ virtual uint32 getAudioCompressionType() const;
+};
+
+class ExtAudioMapResourceSource : public ResourceSource {
+public:
+ ExtAudioMapResourceSource(const Common::String &name, int volNum)
+ : ResourceSource(kSourceExtAudioMap, name, volNum) {
+ }
+
+ virtual void scanSource(ResourceManager *resMan);
+};
+
+class WaveResourceSource : public ResourceSource {
+public:
+ WaveResourceSource(const Common::String &name) : ResourceSource(kSourceWave, name) {}
+
+ virtual void loadResource(ResourceManager *resMan, Resource *res);
+};
+
+/**
+ * Reads SCI1.1+ resources from a Mac resource fork.
+ */
+class MacResourceForkResourceSource : public ResourceSource {
+protected:
+ Common::MacResManager *_macResMan;
+
+public:
+ MacResourceForkResourceSource(const Common::String &name, int volNum);
+ ~MacResourceForkResourceSource();
+
+ virtual void scanSource(ResourceManager *resMan);
+
+ virtual void loadResource(ResourceManager *resMan, Resource *res);
+};
+
+} // End of namespace Sci
+
+#endif // SCI_RESOURCE_INTERN_H
diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp
index 929bdf3307..1ebc6a2ba3 100644
--- a/engines/sci/sci.cpp
+++ b/engines/sci/sci.cpp
@@ -36,22 +36,32 @@
#include "sci/event.h"
#include "sci/engine/features.h"
+#include "sci/engine/message.h"
#include "sci/engine/state.h"
#include "sci/engine/kernel.h"
#include "sci/engine/script.h" // for script_adjust_opcode_formats
+#include "sci/engine/selector.h" // for SELECTOR
#include "sci/sound/audio.h"
#include "sci/sound/soundcmd.h"
-#include "sci/graphics/gui.h"
+#include "sci/graphics/animate.h"
+#include "sci/graphics/cache.h"
+#include "sci/graphics/compare.h"
+#include "sci/graphics/controls.h"
+#include "sci/graphics/coordadjuster.h"
+#include "sci/graphics/cursor.h"
#include "sci/graphics/maciconbar.h"
+#include "sci/graphics/menu.h"
+#include "sci/graphics/paint16.h"
+#include "sci/graphics/paint32.h"
#include "sci/graphics/ports.h"
#include "sci/graphics/palette.h"
-#include "sci/graphics/cursor.h"
#include "sci/graphics/screen.h"
-#include "sci/graphics/cache.h"
+#include "sci/graphics/text16.h"
+#include "sci/graphics/transitions.h"
#ifdef ENABLE_SCI32
-#include "sci/graphics/gui32.h"
+#include "sci/graphics/frameout.h"
#endif
namespace Sci {
@@ -61,13 +71,22 @@ SciEngine *g_sci = 0;
class GfxDriver;
-SciEngine::SciEngine(OSystem *syst, const ADGameDescription *desc)
- : Engine(syst), _gameDescription(desc), _system(syst) {
- _console = NULL;
+SciEngine::SciEngine(OSystem *syst, const ADGameDescription *desc, SciGameId gameId)
+ : Engine(syst), _gameDescription(desc), _gameId(gameId) {
assert(g_sci == 0);
g_sci = this;
+
+ _gfxMacIconBar = 0;
+
+ _audio = 0;
_features = 0;
+ _resMan = 0;
+ _gamestate = 0;
+ _kernel = 0;
+ _vocabulary = 0;
+ _eventMan = 0;
+ _console = 0;
// Set up the engine specific debug levels
DebugMan.addDebugChannel(kDebugLevelError, "Error", "Script error debugging");
@@ -78,10 +97,8 @@ SciEngine::SciEngine(OSystem *syst, const ADGameDescription *desc)
DebugMan.addDebugChannel(kDebugLevelFuncCheck, "Func", "Function parameter debugging");
DebugMan.addDebugChannel(kDebugLevelBresen, "Bresenham", "Bresenham algorithms debugging");
DebugMan.addDebugChannel(kDebugLevelSound, "Sound", "Sound debugging");
- DebugMan.addDebugChannel(kDebugLevelGfxDriver, "Gfxdriver", "Gfx driver debugging");
DebugMan.addDebugChannel(kDebugLevelBaseSetter, "Base", "Base Setter debugging");
DebugMan.addDebugChannel(kDebugLevelParser, "Parser", "Parser debugging");
- DebugMan.addDebugChannel(kDebugLevelMenu, "Menu", "Menu handling debugging");
DebugMan.addDebugChannel(kDebugLevelSaid, "Said", "Said specs debugging");
DebugMan.addDebugChannel(kDebugLevelFile, "File", "File I/O debugging");
DebugMan.addDebugChannel(kDebugLevelTime, "Time", "Time debugging");
@@ -91,26 +108,24 @@ SciEngine::SciEngine(OSystem *syst, const ADGameDescription *desc)
DebugMan.addDebugChannel(kDebugLevelVM, "VM", "VM debugging");
DebugMan.addDebugChannel(kDebugLevelScripts, "Scripts", "Notifies when scripts are unloaded");
DebugMan.addDebugChannel(kDebugLevelGC, "GC", "Garbage Collector debugging");
- DebugMan.addDebugChannel(kDebugLevelSci0Pic, "Sci0Pic", "SCI0 pic drawing debugging");
DebugMan.addDebugChannel(kDebugLevelResMan, "ResMan", "Resource manager debugging");
DebugMan.addDebugChannel(kDebugLevelOnStartup, "OnStartup", "Enter debugger at start of game");
- _gamestate = 0;
- _gfxMacIconBar = 0;
-
const Common::FSNode gameDataDir(ConfMan.get("path"));
SearchMan.addSubDirectoryMatching(gameDataDir, "actors"); // KQ6 hi-res portraits
SearchMan.addSubDirectoryMatching(gameDataDir, "aud"); // resource.aud and audio files
- SearchMan.addSubDirectoryMatching(gameDataDir, "avi"); // AVI movie files for Windows versions
- SearchMan.addSubDirectoryMatching(gameDataDir, "seq"); // SEQ movie files for DOS versions
SearchMan.addSubDirectoryMatching(gameDataDir, "wav"); // speech files in WAV format
SearchMan.addSubDirectoryMatching(gameDataDir, "sfx"); // music/sound files in WAV format
- SearchMan.addSubDirectoryMatching(gameDataDir, "robot"); // robot files
+ SearchMan.addSubDirectoryMatching(gameDataDir, "avi"); // AVI movie files for Windows versions
+ SearchMan.addSubDirectoryMatching(gameDataDir, "seq"); // SEQ movie files for DOS versions
+ SearchMan.addSubDirectoryMatching(gameDataDir, "robot"); // robot movie files
+ SearchMan.addSubDirectoryMatching(gameDataDir, "movies"); // vmd movie files
+ SearchMan.addSubDirectoryMatching(gameDataDir, "vmd"); // vmd movie files
// Add the patches directory, except for KQ6CD; The patches folder in some versions of KQ6CD
// is for the demo of Phantasmagoria, included in the disk
- if (strcmp(getGameID(), "kq6"))
+ if (_gameId != GID_KQ6)
SearchMan.addSubDirectoryMatching(gameDataDir, "patches"); // resource patches
}
@@ -118,14 +133,35 @@ SciEngine::~SciEngine() {
// Remove all of our debug levels here
DebugMan.clearAllDebugChannels();
+#ifdef ENABLE_SCI32
+ delete _gfxFrameout;
+#endif
+ delete _gfxMenu;
+ delete _gfxControls;
+ delete _gfxText16;
+ delete _gfxAnimate;
+ delete _gfxPaint;
+ delete _gfxTransitions;
+ delete _gfxCompare;
+ delete _gfxCoordAdjuster;
+ delete _gfxPorts;
+ delete _gfxCache;
+ delete _gfxPalette;
+ delete _gfxCursor;
+ delete _gfxScreen;
+
delete _audio;
+ delete _soundCmd;
delete _kernel;
delete _vocabulary;
delete _console;
- delete _resMan;
delete _features;
delete _gfxMacIconBar;
+ delete _eventMan;
+ delete _gamestate->_segMan;
+ delete _gamestate;
+ delete _resMan; // should be deleted last
g_sci = 0;
}
@@ -135,166 +171,289 @@ Common::Error SciEngine::run() {
ConfMan.registerDefault("enable_fb01", "false");
_resMan = new ResourceManager();
+ assert(_resMan);
+ _resMan->addAppropriateSources();
+ _resMan->init();
+ // TODO: Add error handling. Check return values of addAppropriateSources
+ // and init. We first have to *add* sensible return values, though ;).
+/*
if (!_resMan) {
warning("No resources found, aborting");
return Common::kNoGameDataFoundError;
}
+*/
- SegManager *segMan = new SegManager(_resMan);
-
- // Scale the screen, if needed
- int upscaledHires = GFX_SCREEN_UPSCALED_DISABLED;
-
- // King's Quest 6 and Gabriel Knight 1 have hires content, gk1/cd was able to provide that under DOS as well, but as
- // gk1/floppy does support upscaled hires scriptswise, but doesn't actually have the hires content we need to limit
- // it to platform windows.
- if (getPlatform() == Common::kPlatformWindows) {
- if (!strcmp(getGameID(), "kq6"))
- upscaledHires = GFX_SCREEN_UPSCALED_640x440;
-#ifdef ENABLE_SCI32
- if (!strcmp(getGameID(), "gk1"))
- upscaledHires = GFX_SCREEN_UPSCALED_640x480;
-#endif
- }
-
- // Japanese versions of games use hi-res font on upscaled version of the game
- if ((getLanguage() == Common::JA_JPN) && (getSciVersion() <= SCI_VERSION_1_1))
- upscaledHires = GFX_SCREEN_UPSCALED_640x400;
-
- // Initialize graphics-related parts
- GfxScreen *screen = 0;
+ // Reset, so that error()s before SoundCommandParser is initialized wont cause a crash
+ _soundCmd = NULL;
- // invokes initGraphics()
- if (_resMan->detectHires())
- screen = new GfxScreen(_resMan, 640, 480);
- else
- screen = new GfxScreen(_resMan, 320, 200, upscaledHires);
+ // Add the after market GM patches for the specified game, if they exist
+ _resMan->addNewGMPatch(_gameId);
+ _gameObj = _resMan->findGameObject();
- if (_resMan->isSci11Mac() && getSciVersion() == SCI_VERSION_1_1)
- _gfxMacIconBar = new GfxMacIconBar();
+ SegManager *segMan = new SegManager(_resMan);
- GfxPalette *palette = new GfxPalette(_resMan, screen);
- GfxCache *cache = new GfxCache(_resMan, screen, palette);
- GfxCursor *cursor = new GfxCursor(_resMan, palette, screen);
+ // Initialize the game screen
+ _gfxScreen = new GfxScreen(_resMan);
+ _gfxScreen->debugUnditherSetState(ConfMan.getBool("undither"));
// Create debugger console. It requires GFX to be initialized
_console = new Console(this);
-
_kernel = new Kernel(_resMan, segMan);
+ _features = new GameFeatures(segMan, _kernel);
// Only SCI0 and SCI01 games used a parser
_vocabulary = (getSciVersion() <= SCI_VERSION_1_EGA) ? new Vocabulary(_resMan) : NULL;
_audio = new AudioPlayer(_resMan);
+ _gamestate = new EngineState(segMan);
+ _eventMan = new EventManager(_resMan->detectFontExtended());
- _features = new GameFeatures(segMan, _kernel);
+ // The game needs to be initialized before the graphics system is initialized, as
+ // the graphics code checks parts of the seg manager upon initialization (e.g. for
+ // the presence of the fastCast object)
+ if (!initGame()) { /* Initialize */
+ warning("Game initialization failed: Aborting...");
+ // TODO: Add an "init failed" error?
+ return Common::kUnknownError;
+ }
- _gamestate = new EngineState(segMan);
+ script_adjust_opcode_formats();
- _gamestate->_event = new SciEvent(_resMan);
+ // Must be called after game_init(), as they use _features
+ _kernel->loadKernelNames(_features);
+ _soundCmd = new SoundCommandParser(_resMan, segMan, _kernel, _audio, _features->detectDoSoundType());
- if (script_init_engine(_gamestate))
- return Common::kUnknownError;
+ syncSoundSettings();
-#ifdef ENABLE_SCI32
- if (getSciVersion() >= SCI_VERSION_2) {
- _gfxAnimate = 0;
- _gfxControls = 0;
- _gfxMenu = 0;
- _gfxPaint16 = 0;
- _gfxPorts = 0;
- _gui = 0;
- _gui32 = new SciGui32(_gamestate->_segMan, _gamestate->_event, screen, palette, cache, cursor);
- } else {
-#endif
- _gfxPorts = new GfxPorts(segMan, screen);
- _gui = new SciGui(_gamestate, screen, palette, cache, cursor, _gfxPorts, _audio);
-#ifdef ENABLE_SCI32
- _gui32 = 0;
- _gfxFrameout = 0;
+ // Initialize all graphics related subsystems
+ initGraphics();
+
+ debug("Emulating SCI version %s\n", getSciVersionDesc(getSciVersion()));
+
+ // Check whether loading a savestate was requested
+ int saveSlot = ConfMan.getInt("save_slot");
+ if (saveSlot >= 0) {
+ reg_t restoreArgv[2] = { NULL_REG, make_reg(0, saveSlot) }; // special call (argv[0] is NULL)
+ kRestoreGame(_gamestate, 2, restoreArgv);
+
+ // Initialize the game menu, if there is one.
+ // This is not done when loading, so we must do it manually.
+ reg_t menuBarObj = _gamestate->_segMan->findObjectByName("MenuBar");
+ if (menuBarObj.isNull())
+ menuBarObj = _gamestate->_segMan->findObjectByName("TheMenuBar"); // LSL2
+ if (menuBarObj.isNull())
+ menuBarObj = _gamestate->_segMan->findObjectByName("menuBar"); // LSL6
+ if (!menuBarObj.isNull()) {
+ // Reset abortScriptProcessing before initializing the game menu, so that the
+ // VM call performed by invokeSelector will actually run.
+ _gamestate->abortScriptProcessing = kAbortNone;
+ Object *menuBar = _gamestate->_segMan->getObject(menuBarObj);
+ // Invoke the first method (init) of the menuBar object
+ invokeSelector(_gamestate, menuBarObj, menuBar->getFuncSelector(0), 0, _gamestate->stack_base);
+ _gamestate->abortScriptProcessing = kAbortLoadGame;
+ }
}
-#endif
- _gfxPalette = palette;
- _gfxScreen = screen;
- _gfxCache = cache;
- _gfxCursor = cursor;
+ runGame();
- if (game_init(_gamestate)) { /* Initialize */
- warning("Game initialization failed: Aborting...");
- // TODO: Add an "init failed" error?
- return Common::kUnknownError;
+ ConfMan.flushToDisk();
+
+ return Common::kNoError;
+}
+
+bool SciEngine::initGame() {
+ // Script 0 needs to be allocated here before anything else!
+ int script0Segment = _gamestate->_segMan->getScriptSegment(0, SCRIPT_GET_LOCK);
+ DataStack *stack = _gamestate->_segMan->allocateStack(VM_STACK_SIZE, NULL);
+
+ _gamestate->_msgState = new MessageState(_gamestate->_segMan);
+ _gamestate->gcCountDown = GC_INTERVAL - 1;
+
+ // Script 0 should always be at segment 1
+ if (script0Segment != 1) {
+ debug(2, "Failed to instantiate script.000");
+ return false;
}
- // Add the after market GM patches for the specified game, if they exist
- _resMan->addNewGMPatch(getGameID());
+ _gamestate->initGlobals();
- script_adjust_opcode_formats(_gamestate);
- _kernel->loadKernelNames(getGameID());
+ if (_gamestate->abortScriptProcessing == kAbortRestartGame && _gfxMenu)
+ _gfxMenu->reset();
- SciVersion soundVersion = _features->detectDoSoundType();
+ _gamestate->_segMan->initSysStrings();
- _gamestate->_soundCmd = new SoundCommandParser(_resMan, segMan, _kernel, _audio, soundVersion);
+ _gamestate->r_acc = _gamestate->r_prev = NULL_REG;
- screen->debugUnditherSetState(ConfMan.getBool("undither"));
+ _gamestate->_executionStack.clear(); // Start without any execution stack
+ _gamestate->executionStackBase = -1; // No vm is running yet
+ _gamestate->_executionStackPosChanged = false;
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- if (game_init_sound(_gamestate, 0, soundVersion)) {
- warning("Game initialization failed: Error in sound subsystem. Aborting...");
- return Common::kUnknownError;
+ _gamestate->abortScriptProcessing = kAbortNone;
+ _gamestate->gameWasRestarted = false;
+
+ _gamestate->stack_base = stack->_entries;
+ _gamestate->stack_top = stack->_entries + stack->_capacity;
+
+ if (!_gamestate->_segMan->instantiateScript(0)) {
+ error("initGame(): Could not instantiate script 0");
+ return false;
}
-#endif
- syncSoundSettings();
+ // Reset parser
+ if (_vocabulary) {
+ _vocabulary->parserIsValid = false; // Invalidate parser
+ _vocabulary->parser_event = NULL_REG; // Invalidate parser event
+ _vocabulary->parser_base = make_reg(_gamestate->_segMan->getSysStringsSegment(), SYS_STRING_PARSER_BASE);
+ }
+ _gamestate->gameStartTime = _gamestate->lastWaitTime = _gamestate->_screenUpdateTime = g_system->getMillis();
+
+ srand(g_system->getMillis()); // Initialize random number generator
+
+ // Load game language into printLang property of game object
+ setSciLanguage();
+
+ return true;
+}
+
+void SciEngine::initGraphics() {
+
+ // Reset all graphics objects
+ _gfxAnimate = 0;
+ _gfxCache = 0;
+ _gfxCompare = 0;
+ _gfxControls = 0;
+ _gfxCoordAdjuster = 0;
+ _gfxCursor = 0;
+ _gfxMacIconBar = 0;
+ _gfxMenu = 0;
+ _gfxPaint = 0;
+ _gfxPaint16 = 0;
+ _gfxPalette = 0;
+ _gfxPorts = 0;
+ _gfxText16 = 0;
+ _gfxTransitions = 0;
#ifdef ENABLE_SCI32
- if (_gui32)
- _gui32->init();
- else
+ _gfxFrameout = 0;
+ _gfxPaint32 = 0;
#endif
- _gui->init(_features->usesOldGfxFunctions());
- debug("Emulating SCI version %s\n", getSciVersionDesc(getSciVersion()));
+ if (_resMan->isSci11Mac() && getSciVersion() == SCI_VERSION_1_1)
+ _gfxMacIconBar = new GfxMacIconBar();
- // Check whether loading a savestate was requested
- if (ConfMan.hasKey("save_slot")) {
- _gamestate->loadFromLauncher = ConfMan.getInt("save_slot");
+ bool paletteMerging = true;
+ if (getSciVersion() >= SCI_VERSION_1_1) {
+ // there are some games that use inbetween SCI1.1 interpreter, so we have to detect if it's merging or copying
+ if (getSciVersion() == SCI_VERSION_1_1)
+ paletteMerging = _resMan->detectForPaletteMergingForSci11();
+ else
+ paletteMerging = false;
+ }
+
+ _gfxPalette = new GfxPalette(_resMan, _gfxScreen, paletteMerging);
+ _gfxCache = new GfxCache(_resMan, _gfxScreen, _gfxPalette);
+ _gfxCursor = new GfxCursor(_resMan, _gfxPalette, _gfxScreen);
+
+#ifdef ENABLE_SCI32
+ if (getSciVersion() >= SCI_VERSION_2) {
+ // SCI32 graphic objects creation
+ _gfxCoordAdjuster = new GfxCoordAdjuster32(_gamestate->_segMan);
+ _gfxCursor->init(_gfxCoordAdjuster, _eventMan);
+ _gfxCompare = new GfxCompare(_gamestate->_segMan, g_sci->getKernel(), _gfxCache, _gfxScreen, _gfxCoordAdjuster);
+ _gfxPaint32 = new GfxPaint32(g_sci->getResMan(), _gamestate->_segMan, g_sci->getKernel(), _gfxCoordAdjuster, _gfxCache, _gfxScreen, _gfxPalette);
+ _gfxPaint = _gfxPaint32;
+ _gfxFrameout = new GfxFrameout(_gamestate->_segMan, g_sci->getResMan(), _gfxCoordAdjuster, _gfxCache, _gfxScreen, _gfxPalette, _gfxPaint32);
} else {
- _gamestate->loadFromLauncher = -1;
+#endif
+ // SCI0-SCI1.1 graphic objects creation
+ _gfxPorts = new GfxPorts(_gamestate->_segMan, _gfxScreen);
+ _gfxCoordAdjuster = new GfxCoordAdjuster16(_gfxPorts);
+ _gfxCursor->init(_gfxCoordAdjuster, g_sci->getEventManager());
+ _gfxCompare = new GfxCompare(_gamestate->_segMan, g_sci->getKernel(), _gfxCache, _gfxScreen, _gfxCoordAdjuster);
+ _gfxTransitions = new GfxTransitions(_gfxScreen, _gfxPalette, g_sci->getResMan()->isVGA());
+ _gfxPaint16 = new GfxPaint16(g_sci->getResMan(), _gamestate->_segMan, g_sci->getKernel(), _gfxCache, _gfxPorts, _gfxCoordAdjuster, _gfxScreen, _gfxPalette, _gfxTransitions, _audio);
+ _gfxPaint = _gfxPaint16;
+ _gfxAnimate = new GfxAnimate(_gamestate, _gfxCache, _gfxPorts, _gfxPaint16, _gfxScreen, _gfxPalette, _gfxCursor, _gfxTransitions);
+ _gfxText16 = new GfxText16(g_sci->getResMan(), _gfxCache, _gfxPorts, _gfxPaint16, _gfxScreen);
+ _gfxControls = new GfxControls(_gamestate->_segMan, _gfxPorts, _gfxPaint16, _gfxText16, _gfxScreen);
+ _gfxMenu = new GfxMenu(g_sci->getEventManager(), _gamestate->_segMan, _gfxPorts, _gfxPaint16, _gfxText16, _gfxScreen, _gfxCursor);
+
+ _gfxMenu->reset();
+#ifdef ENABLE_SCI32
}
+#endif
- game_run(&_gamestate); // Run the game
+ if (_gfxPorts) {
+ _gfxPorts->init(_features->usesOldGfxFunctions(), _gfxPaint16, _gfxText16);
+ _gfxPaint16->init(_gfxAnimate, _gfxText16);
+ }
+ // Set default (EGA, amiga or resource 999) palette
+ _gfxPalette->setDefault();
+}
- game_exit(_gamestate);
+void SciEngine::initStackBaseWithSelector(Selector selector) {
+ _gamestate->stack_base[0] = make_reg(0, (uint16)selector);
+ _gamestate->stack_base[1] = NULL_REG;
- ConfMan.flushToDisk();
+ // Register the first element on the execution stack
+ if (!send_selector(_gamestate, _gameObj, _gameObj, _gamestate->stack_base, 2, _gamestate->stack_base)) {
+ _console->printObject(_gameObj);
+ error("initStackBaseWithSelector: error while registering the first selector in the call stack");
+ }
- delete _gamestate->_soundCmd;
- delete _gui;
-#ifdef ENABLE_SCI32
- delete _gui32;
-#endif
- delete _gfxPorts;
- delete _gfxCache;
- delete _gfxPalette;
- delete cursor;
- delete _gfxScreen;
- delete _gamestate->_event;
- delete segMan;
- delete _gamestate;
+}
- return Common::kNoError;
+void SciEngine::runGame() {
+ initStackBaseWithSelector(SELECTOR(play)); // Call the play selector
+
+ // Attach the debug console on game startup, if requested
+ if (DebugMan.isDebugChannelEnabled(kDebugLevelOnStartup))
+ _console->attach();
+
+ do {
+ _gamestate->_executionStackPosChanged = false;
+ run_vm(_gamestate, (_gamestate->abortScriptProcessing == kAbortLoadGame));
+ exitGame();
+
+ if (_gamestate->abortScriptProcessing == kAbortRestartGame) {
+ _gamestate->_segMan->resetSegMan();
+ initGame();
+ initStackBaseWithSelector(SELECTOR(play));
+ _gamestate->gameWasRestarted = true;
+ } else if (_gamestate->abortScriptProcessing == kAbortLoadGame) {
+ _gamestate->abortScriptProcessing = kAbortNone;
+ initStackBaseWithSelector(SELECTOR(replay));
+ } else {
+ break; // exit loop
+ }
+ } while (true);
+}
+
+void SciEngine::exitGame() {
+ if (_gamestate->abortScriptProcessing != kAbortLoadGame) {
+ _gamestate->_executionStack.clear();
+ _audio->stopAllAudio();
+ g_sci->_soundCmd->clearPlayList();
+ }
+
+ // TODO Free parser segment here
+
+ // TODO Free scripts here
+
+ // Close all opened file handles
+ _gamestate->_fileHandles.clear();
+ _gamestate->_fileHandles.resize(5);
}
// Invoked by error() when a severe error occurs
GUI::Debugger *SciEngine::getDebugger() {
if (_gamestate) {
ExecStack *xs = &(_gamestate->_executionStack.back());
- xs->addr.pc.offset = g_debugState.old_pc_offset;
- xs->sp = g_debugState.old_sp;
+ xs->addr.pc.offset = _debugState.old_pc_offset;
+ xs->sp = _debugState.old_sp;
}
- g_debugState.runningStep = 0; // Stop multiple execution
- g_debugState.seeking = kDebugSeekNothing; // Stop special seeks
+ _debugState.runningStep = 0; // Stop multiple execution
+ _debugState.seeking = kDebugSeekNothing; // Stop special seeks
return _console;
}
@@ -304,7 +463,7 @@ Console *SciEngine::getSciDebugger() {
return _console;
}
-const char* SciEngine::getGameID() const {
+const char *SciEngine::getGameIdStr() const {
return _gameDescription->gameid;
}
@@ -316,12 +475,8 @@ Common::Platform SciEngine::getPlatform() const {
return _gameDescription->platform;
}
-uint32 SciEngine::getFlags() const {
- return _gameDescription->flags;
-}
-
bool SciEngine::isDemo() const {
- return getFlags() & ADGF_DEMO;
+ return _gameDescription->flags & ADGF_DEMO;
}
Common::String SciEngine::getSavegameName(int nr) const {
@@ -333,14 +488,12 @@ Common::String SciEngine::getSavegamePattern() const {
}
Common::String SciEngine::getFilePrefix() const {
- const char* gameID = getGameID();
- if (!strcmp(gameID, "qfg2")) {
+ if (_gameId == GID_QFG2) {
// Quest for Glory 2 wants to read files from Quest for Glory 1 (EGA/VGA) to import character data
if (_gamestate->currentRoomNumber() == 805)
return "qfg1";
// TODO: Include import-room for qfg1vga
- }
- if (!strcmp(gameID, "qfg3")) {
+ } else if (_gameId == GID_QFG3) {
// Quest for Glory 3 wants to read files from Quest for Glory 2 to import character data
if (_gamestate->currentRoomNumber() == 54)
return "qfg2";
@@ -361,27 +514,22 @@ Common::String SciEngine::unwrapFilename(const Common::String &name) const {
}
void SciEngine::pauseEngineIntern(bool pause) {
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- _gamestate->_sound.sfx_suspend(pause);
-#endif
_mixer->pauseAll(pause);
}
void SciEngine::syncSoundSettings() {
Engine::syncSoundSettings();
-#ifndef USE_OLD_MUSIC_FUNCTIONS
bool mute = false;
if (ConfMan.hasKey("mute"))
mute = ConfMan.getBool("mute");
int soundVolumeMusic = (mute ? 0 : ConfMan.getInt("music_volume"));
- if (_gamestate && _gamestate->_soundCmd) {
+ if (_gamestate && g_sci->_soundCmd) {
int vol = (soundVolumeMusic + 1) * SoundCommandParser::kMaxSciVolume / Audio::Mixer::kMaxMixerVolume;
- _gamestate->_soundCmd->setMasterVolume(vol);
+ g_sci->_soundCmd->setMasterVolume(vol);
}
-#endif
}
} // End of namespace Sci
diff --git a/engines/sci/sci.h b/engines/sci/sci.h
index 685f05e685..c15f87e4e2 100644
--- a/engines/sci/sci.h
+++ b/engines/sci/sci.h
@@ -28,6 +28,8 @@
#include "engines/engine.h"
#include "common/util.h"
+#include "sci/engine/vm_types.h" // for Selector
+#include "sci/debug.h" // for DebugState
struct ADGameDescription;
@@ -41,9 +43,6 @@ struct ADGameDescription;
*/
namespace Sci {
-// Uncomment this to use old music functions
-//#define USE_OLD_MUSIC_FUNCTIONS
-
struct EngineState;
class Vocabulary;
class ResourceManager;
@@ -51,6 +50,8 @@ class Kernel;
class GameFeatures;
class Console;
class AudioPlayer;
+class SoundCommandParser;
+class EventManager;
class GfxAnimate;
class GfxCache;
@@ -58,14 +59,16 @@ class GfxCompare;
class GfxControls;
class GfxCoordAdjuster;
class GfxCursor;
+class GfxMacIconBar;
class GfxMenu;
class GfxPaint;
class GfxPaint16;
+class GfxPaint32;
class GfxPalette;
class GfxPorts;
class GfxScreen;
-class SciGui;
-class GfxMacIconBar;
+class GfxText16;
+class GfxTransitions;
#ifdef ENABLE_SCI32
class SciGui32;
@@ -82,22 +85,89 @@ enum kDebugLevels {
kDebugLevelFuncCheck = 1 << 5,
kDebugLevelBresen = 1 << 6,
kDebugLevelSound = 1 << 7,
- kDebugLevelGfxDriver = 1 << 8,
- kDebugLevelBaseSetter = 1 << 9,
- kDebugLevelParser = 1 << 10,
- kDebugLevelMenu = 1 << 11,
- kDebugLevelSaid = 1 << 12,
- kDebugLevelFile = 1 << 13,
- kDebugLevelTime = 1 << 14,
- kDebugLevelRoom = 1 << 15,
- kDebugLevelAvoidPath = 1 << 16,
- kDebugLevelDclInflate = 1 << 17,
- kDebugLevelVM = 1 << 18,
- kDebugLevelScripts = 1 << 19,
- kDebugLevelGC = 1 << 20,
- kDebugLevelSci0Pic = 1 << 21,
- kDebugLevelResMan = 1 << 22,
- kDebugLevelOnStartup = 1 << 23
+ kDebugLevelBaseSetter = 1 << 8,
+ kDebugLevelParser = 1 << 9,
+ kDebugLevelSaid = 1 << 10,
+ kDebugLevelFile = 1 << 11,
+ kDebugLevelTime = 1 << 12,
+ kDebugLevelRoom = 1 << 13,
+ kDebugLevelAvoidPath = 1 << 14,
+ kDebugLevelDclInflate = 1 << 15,
+ kDebugLevelVM = 1 << 16,
+ kDebugLevelScripts = 1 << 17,
+ kDebugLevelGC = 1 << 18,
+ kDebugLevelResMan = 1 << 19,
+ kDebugLevelOnStartup = 1 << 20
+};
+
+enum SciGameId {
+ GID_ASTROCHICKEN,
+ GID_CAMELOT,
+ GID_CASTLEBRAIN,
+ GID_CHRISTMAS1988,
+ GID_CHRISTMAS1990,
+ GID_CHRISTMAS1992,
+ GID_CNICK_KQ,
+ GID_CNICK_LAURABOW,
+ GID_CNICK_LONGBOW,
+ GID_CNICK_LSL,
+ GID_CNICK_SQ,
+ GID_ECOQUEST,
+ GID_ECOQUEST2,
+ GID_FAIRYTALES,
+ GID_FREDDYPHARKAS,
+ GID_FUNSEEKER,
+ GID_GK1,
+ GID_GK2,
+ GID_HOYLE1,
+ GID_HOYLE2,
+ GID_HOYLE3,
+ GID_HOYLE4,
+ GID_ICEMAN,
+ GID_ISLANDBRAIN,
+ GID_JONES,
+ GID_KQ1,
+ GID_KQ4,
+ GID_KQ5,
+ GID_KQ6,
+ GID_KQ7,
+ GID_LAURABOW,
+ GID_LAURABOW2,
+ GID_LIGHTHOUSE,
+ GID_LONGBOW,
+ GID_LSL1,
+ GID_LSL2,
+ GID_LSL3,
+ GID_LSL5,
+ GID_LSL6,
+ GID_LSL6HIRES, // We have a separate ID for LSL6 SCI32, because it's actually a completely different game
+ GID_LSL7,
+ GID_MOTHERGOOSE,
+ GID_MSASTROCHICKEN,
+ GID_PEPPER,
+ GID_PHANTASMAGORIA,
+ GID_PHANTASMAGORIA2,
+ GID_PQ1,
+ GID_PQ2,
+ GID_PQ3,
+ GID_PQ4,
+ GID_PQSWAT,
+ GID_QFG1,
+ GID_QFG2,
+ GID_QFG3,
+ GID_QFG4,
+ GID_RAMA,
+ GID_SHIVERS,
+ GID_SHIVERS2,
+ GID_SLATER,
+ GID_SQ1,
+ GID_SQ3,
+ GID_SQ4,
+ GID_SQ5,
+ GID_SQ6,
+ GID_TORIN,
+
+ GID_FANMADE // FIXME: Do we really need/want this?
};
/** SCI versions */
@@ -116,12 +186,6 @@ enum SciVersion {
SCI_VERSION_3 // LSL7, RAMA, Lighthouse
};
-enum MoveCountType {
- kMoveCountUninitialized,
- kIgnoreMoveCount,
- kIncrementMoveCount
-};
-
/** Supported languages */
enum kLanguage {
K_LANG_NONE = 0,
@@ -138,7 +202,7 @@ enum kLanguage {
class SciEngine : public Engine {
friend class Console;
public:
- SciEngine(OSystem *syst, const ADGameDescription *desc);
+ SciEngine(OSystem *syst, const ADGameDescription *desc, SciGameId gameId);
~SciEngine();
// Engine APIs
@@ -153,17 +217,19 @@ public:
bool canSaveGameStateCurrently();
void syncSoundSettings();
- const char* getGameID() const;
+ const SciGameId &getGameId() const { return _gameId; }
+ const char *getGameIdStr() const;
int getResourceVersion() const;
Common::Language getLanguage() const;
Common::Platform getPlatform() const;
- uint32 getFlags() const;
bool isDemo() const;
inline ResourceManager *getResMan() const { return _resMan; }
inline Kernel *getKernel() const { return _kernel; }
inline EngineState *getEngineState() const { return _gamestate; }
inline Vocabulary *getVocabulary() const { return _vocabulary; }
+ inline EventManager *getEventManager() const { return _eventMan; }
+ inline reg_t getGameObject() const { return _gameObj; }
Common::String getSavegameName(int nr) const;
Common::String getSavegamePattern() const;
@@ -176,6 +242,12 @@ public:
/** Remove the 'TARGET-' prefix of the given filename, if present. */
Common::String unwrapFilename(const Common::String &name) const;
+ void sleep(uint32 msecs);
+
+ void scriptDebug();
+ bool checkExportBreakpoint(uint16 script, uint16 pubfunct);
+ bool checkSelectorBreakpoint(reg_t send_obj, int selector);
+
public:
/**
@@ -189,9 +261,14 @@ public:
Common::String strSplit(const char *str, const char *sep = "\r----------\r");
kLanguage getSciLanguage();
+ void setSciLanguage(kLanguage lang);
+ void setSciLanguage();
Common::String getSciLanguageString(const char *str, kLanguage lang, kLanguage *lang2 = NULL) const;
+ // Initializes ports and paint16 for non-sci32 games, also sets default palette
+ void initGraphics();
+
public:
GfxAnimate *_gfxAnimate; // Animate for 16-bit gfx
GfxCache *_gfxCache;
@@ -203,27 +280,60 @@ public:
GfxPalette *_gfxPalette;
GfxPaint *_gfxPaint;
GfxPaint16 *_gfxPaint16; // Painting in 16-bit gfx
+ GfxPaint32 *_gfxPaint32; // Painting in 32-bit gfx
GfxPorts *_gfxPorts; // Port managment for 16-bit gfx
GfxScreen *_gfxScreen;
- SciGui *_gui; /* Currently active Gui */
+ GfxText16 *_gfxText16;
+ GfxTransitions *_gfxTransitions; // transitions between screens for 16-bit gfx
GfxMacIconBar *_gfxMacIconBar; // Mac Icon Bar manager
#ifdef ENABLE_SCI32
- SciGui32 *_gui32; // GUI for SCI32 games
GfxFrameout *_gfxFrameout; // kFrameout and the like for 32-bit gfx
#endif
AudioPlayer *_audio;
+ SoundCommandParser *_soundCmd;
GameFeatures *_features;
+ DebugState _debugState;
+
private:
+ /**
+ * Initializes a SCI game
+ * This function must be run before script_run() is executed. Graphics data
+ * is initialized iff s->gfx_state != NULL.
+ * @param[in] s The state to operate on
+ * @return true on success, false if an error occurred.
+ */
+ bool initGame();
+
+ /**
+ * Runs a SCI game
+ * This is the main function for SCI games. It takes a valid state, loads
+ * script 0 to it, finds the game object, allocates a stack, and runs the
+ * init method of the game object. In layman's terms, this runs a SCI game.
+ * @param[in] s Pointer to the pointer of the state to operate on
+ */
+ void runGame();
+
+ /**
+ * Uninitializes an initialized SCI game
+ * This function should be run after each script_run() call.
+ * @param[in] s The state to operate on
+ */
+ void exitGame();
+
+ void initStackBaseWithSelector(Selector selector);
+
const ADGameDescription *_gameDescription;
+ const SciGameId _gameId;
ResourceManager *_resMan; /**< The resource manager */
EngineState *_gamestate;
Kernel *_kernel;
Vocabulary *_vocabulary;
+ EventManager *_eventMan;
+ reg_t _gameObj; /**< Pointer to the game object */
Console *_console;
- OSystem *_system;
};
diff --git a/engines/sci/sound/audio.cpp b/engines/sci/sound/audio.cpp
index 7ecce3b2a3..d9e9d2e8db 100644
--- a/engines/sci/sound/audio.cpp
+++ b/engines/sci/sound/audio.cpp
@@ -24,20 +24,21 @@
*/
#include "sci/resource.h"
-#include "sci/engine/selector.h"
#include "sci/engine/kernel.h"
+#include "sci/engine/selector.h"
#include "sci/engine/seg_manager.h"
#include "sci/sound/audio.h"
-#include "common/system.h"
#include "common/file.h"
+#include "common/system.h"
#include "sound/audiostream.h"
-#include "sound/decoders/raw.h"
-#include "sound/decoders/wave.h"
+#include "sound/decoders/aiff.h"
#include "sound/decoders/flac.h"
#include "sound/decoders/mp3.h"
+#include "sound/decoders/raw.h"
#include "sound/decoders/vorbis.h"
+#include "sound/decoders/wave.h"
namespace Sci {
@@ -45,6 +46,7 @@ AudioPlayer::AudioPlayer(ResourceManager *resMan) : _resMan(resMan), _audioRate(
_syncResource(NULL), _syncOffset(0), _audioCdStart(0) {
_mixer = g_system->getMixer();
+ _wPlayFlag = false;
}
AudioPlayer::~AudioPlayer() {
@@ -63,11 +65,30 @@ int AudioPlayer::startAudio(uint16 module, uint32 number) {
Audio::AudioStream *audioStream = getAudioStream(number, module, &sampleLen);
if (audioStream) {
+ _wPlayFlag = false;
_mixer->playStream(Audio::Mixer::kSpeechSoundType, &_audioHandle, audioStream);
return sampleLen;
+ } else {
+ // Don't throw a warning in this case. getAudioStream() already has. Some games
+ // do miss audio entries (perhaps because of a typo, or because they were simply
+ // forgotten).
+ return 0;
}
+}
- return 0;
+int AudioPlayer::wPlayAudio(uint16 module, uint32 tuple) {
+ // Get the audio sample length and set the wPlay flag so we return 0 on
+ // position. SSCI pre-loads the audio here, but it's much easier for us to
+ // just get the sample length and return that. wPlayAudio should *not*
+ // actually start the sample.
+
+ int sampleLen = 0;
+ Audio::AudioStream *audioStream = getAudioStream(tuple, module, &sampleLen);
+ if (!audioStream)
+ warning("wPlayAudio: unable to create stream for audio tuple %d, module %d", tuple, module);
+ delete audioStream;
+ _wPlayFlag = true;
+ return sampleLen;
}
void AudioPlayer::stopAudio() {
@@ -85,6 +106,8 @@ void AudioPlayer::resumeAudio() {
int AudioPlayer::getAudioPosition() {
if (_mixer->isSoundHandleActive(_audioHandle))
return _mixer->getSoundElapsedTime(_audioHandle) * 6 / 100; // return elapsed time in ticks
+ else if (_wPlayFlag)
+ return 0; // Sound has "loaded" so return that it hasn't started
else
return -1; // Sound finished
}
@@ -160,17 +183,28 @@ static void deDPCM8(byte *soundBuf, Common::SeekableReadStream &audioStream, uin
// Sierra SOL audio file reader
// Check here for more info: http://wiki.multimedia.cx/index.php?title=Sierra_Audio
-static bool readSOLHeader(Common::SeekableReadStream *audioStream, int headerSize, uint32 &size, uint16 &audioRate, byte &audioFlags) {
- if (headerSize != 11 && headerSize != 12) {
+static bool readSOLHeader(Common::SeekableReadStream *audioStream, int headerSize, uint32 &size, uint16 &audioRate, byte &audioFlags, uint32 resSize) {
+ if (headerSize != 7 && headerSize != 11 && headerSize != 12) {
warning("SOL audio header of size %i not supported", headerSize);
return false;
}
- audioStream->readUint32LE(); // skip "SOL" + 0 (4 bytes)
+ uint32 tag = audioStream->readUint32BE();
+
+ if (tag != MKID_BE('SOL\0')) {
+ warning("No 'SOL' FourCC found");
+ return false;
+ }
+
audioRate = audioStream->readUint16LE();
audioFlags = audioStream->readByte();
- size = audioStream->readUint32LE();
+ // For the QFG3 demo format, just use the resource size
+ // Otherwise, load it from the header
+ if (headerSize == 7)
+ size = resSize;
+ else
+ size = audioStream->readUint32LE();
return true;
}
@@ -238,9 +272,11 @@ Audio::RewindableAudioStream *AudioPlayer::getAudioStream(uint32 number, uint32
// Compressed audio made by our tool
byte *compressedData = (byte *)malloc(audioRes->size);
assert(compressedData);
- // We copy over the compressed data in our own buffer. If we don't do this resourcemanager may free the data
- // later. All other compression-types already decompress completely into an additional buffer here.
- // MP3/OGG/FLAC decompression works on-the-fly instead.
+ // We copy over the compressed data in our own buffer. We have to do
+ // this, because ResourceManager may free the original data late. All
+ // other compression types already decompress completely into an
+ // additional buffer here. MP3/OGG/FLAC decompression works on-the-fly
+ // instead.
memcpy(compressedData, audioRes->data, audioRes->size);
Common::MemoryReadStream *compressedStream = new Common::MemoryReadStream(compressedData, audioRes->size, DisposeAfterUse::YES);
@@ -270,7 +306,7 @@ Audio::RewindableAudioStream *AudioPlayer::getAudioStream(uint32 number, uint32
// SCI1.1
Common::MemoryReadStream headerStream(audioRes->_header, audioRes->_headerSize, DisposeAfterUse::NO);
- if (readSOLHeader(&headerStream, audioRes->_headerSize, size, _audioRate, audioFlags)) {
+ if (readSOLHeader(&headerStream, audioRes->_headerSize, size, _audioRate, audioFlags, audioRes->size)) {
Common::MemoryReadStream dataStream(audioRes->data, audioRes->size, DisposeAfterUse::NO);
data = readSOLAudio(&dataStream, size, audioFlags, flags);
}
@@ -286,6 +322,18 @@ Audio::RewindableAudioStream *AudioPlayer::getAudioStream(uint32 number, uint32
waveStream->seek(0, SEEK_SET);
audioStream = Audio::makeWAVStream(waveStream, DisposeAfterUse::YES);
+ } else if (audioRes->size > 4 && READ_BE_UINT32(audioRes->data) == MKID_BE('FORM')) {
+ // AIFF detected
+ Common::MemoryReadStream *waveStream = new Common::MemoryReadStream(audioRes->data, audioRes->size, DisposeAfterUse::NO);
+
+ // Calculate samplelen from AIFF header
+ int waveSize = 0, waveRate = 0;
+ byte waveFlags = 0;
+ Audio::loadAIFFFromStream(*waveStream, waveSize, waveRate, waveFlags);
+ *sampleLen = (waveFlags & Audio::FLAG_16BITS ? waveSize >> 1 : waveSize) * 60 / waveRate;
+
+ waveStream->seek(0, SEEK_SET);
+ audioStream = Audio::makeAIFFStream(waveStream, DisposeAfterUse::YES);
} else if (audioRes->size > 14 && READ_BE_UINT16(audioRes->data) == 1 && READ_BE_UINT16(audioRes->data + 2) == 1
&& READ_BE_UINT16(audioRes->data + 4) == 5 && READ_BE_UINT32(audioRes->data + 10) == 0x00018051) {
// Mac snd detected
@@ -317,13 +365,12 @@ Audio::RewindableAudioStream *AudioPlayer::getAudioStream(uint32 number, uint32
}
if (audioSeekStream) {
- *sampleLen = (audioSeekStream->getLength().msecs() * 10000) / 166666; // we translate msecs to ticks
- // Original code
- //*sampleLen = (flags & Audio::FLAG_16BITS ? size >> 1 : size) * 60 / _audioRate;
+ *sampleLen = (audioSeekStream->getLength().msecs() * 60) / 1000; // we translate msecs to ticks
audioStream = audioSeekStream;
}
- // We have to make sure that we don't depend on resource manager pointers after this point, because the actual
- // audio resource may get unloaded by resource manager at any time
+ // We have to make sure that we don't depend on resource manager pointers
+ // after this point, because the actual audio resource may get unloaded by
+ // resource manager at any time.
if (audioStream)
return audioStream;
diff --git a/engines/sci/sound/audio.h b/engines/sci/sound/audio.h
index 9fc3cbac51..7c1221fc4c 100644
--- a/engines/sci/sound/audio.h
+++ b/engines/sci/sound/audio.h
@@ -33,8 +33,7 @@
namespace Sci {
enum AudioCommands {
- // TODO: find the difference between kSci1AudioWPlay and kSci1AudioPlay
- kSciAudioWPlay = 1, /* Plays an audio stream */
+ kSciAudioWPlay = 1, /* Loads an audio stream */
kSciAudioPlay = 2, /* Plays an audio stream */
kSciAudioStop = 3, /* Stops an audio stream */
kSciAudioPause = 4, /* Pauses an audio stream */
@@ -69,6 +68,7 @@ public:
Audio::RewindableAudioStream *getAudioStream(uint32 number, uint32 volume, int *sampleLen);
int getAudioPosition();
int startAudio(uint16 module, uint32 tuple);
+ int wPlayAudio(uint16 module, uint32 tuple);
void stopAudio();
void pauseAudio();
void resumeAudio();
@@ -92,6 +92,7 @@ private:
Resource *_syncResource; /**< Used by kDoSync for speech syncing in CD talkie games */
uint _syncOffset;
uint32 _audioCdStart;
+ bool _wPlayFlag;
};
} // End of namespace Sci
diff --git a/engines/sci/sound/drivers/adlib.cpp b/engines/sci/sound/drivers/adlib.cpp
index a743e4b5d9..55c3640c9d 100644
--- a/engines/sci/sound/drivers/adlib.cpp
+++ b/engines/sci/sound/drivers/adlib.cpp
@@ -704,6 +704,7 @@ void MidiDriver_AdLib::setVelocityReg(int regOffset, int velocity, int kbScaleLe
void MidiDriver_AdLib::setPatch(int voice, int patch) {
if ((patch < 0) || ((uint)patch >= _patches.size())) {
warning("ADLIB: Invalid patch %i requested", patch);
+ // Substitute instrument 0
patch = 0;
}
@@ -749,17 +750,24 @@ void MidiDriver_AdLib::playSwitch(bool play) {
bool MidiDriver_AdLib::loadResource(const byte *data, uint size) {
if ((size != 1344) && (size != 2690) && (size != 5382)) {
- warning("ADLIB: Unsupported patch format (%i bytes)", size);
+ error("ADLIB: Unsupported patch format (%i bytes)", size);
return false;
}
for (int i = 0; i < 48; i++)
loadInstrument(data + (28 * i));
- if (size == 2690) {
+ if (size == 1344) {
+ byte dummy[28] = {0};
+
+ // Only 48 instruments, add dummies
+ for (int i = 0; i < 48; i++)
+ loadInstrument(dummy);
+ } else if (size == 2690) {
for (int i = 48; i < 96; i++)
loadInstrument(data + 2 + (28 * i));
- } else if (size == 5382) {
+ } else {
+ // SCI1.1 and later
for (int i = 48; i < 190; i++)
loadInstrument(data + (28 * i));
_rhythmKeyMap = new byte[kRhythmKeys];
diff --git a/engines/sci/sound/drivers/fb01.cpp b/engines/sci/sound/drivers/fb01.cpp
index 7e9fbd51a1..ab9b2e3df5 100644
--- a/engines/sci/sound/drivers/fb01.cpp
+++ b/engines/sci/sound/drivers/fb01.cpp
@@ -128,8 +128,8 @@ private:
};
MidiPlayer_Fb01::MidiPlayer_Fb01(SciVersion version) : MidiPlayer(version), _playSwitch(true), _masterVolume(15), _timerParam(NULL), _timerProc(NULL) {
- MidiDriverType midiType = MidiDriver::detectMusicDriver(MDT_MIDI);
- _driver = createMidi(midiType);
+ MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI);
+ _driver = createMidi(dev);
_sysExBuf[0] = 0x43;
_sysExBuf[1] = 0x75;
diff --git a/engines/sci/sound/drivers/midi.cpp b/engines/sci/sound/drivers/midi.cpp
index 2432a8fab0..1ef0781906 100644
--- a/engines/sci/sound/drivers/midi.cpp
+++ b/engines/sci/sound/drivers/midi.cpp
@@ -55,6 +55,7 @@ public:
bool hasRhythmChannel() const { return true; }
byte getPlayId();
int getPolyphony() const { return kVoices; }
+ int getFirstChannel();
void setVolume(byte volume);
int getVolume();
void setReverb(byte reverb);
@@ -119,10 +120,10 @@ private:
};
MidiPlayer_Midi::MidiPlayer_Midi(SciVersion version) : MidiPlayer(version), _playSwitch(true), _masterVolume(15), _isMt32(false), _hasReverb(false), _isOldPatchFormat(true) {
- MidiDriverType midiType = MidiDriver::detectMusicDriver(MDT_MIDI);
- _driver = createMidi(midiType);
+ MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI);
+ _driver = createMidi(dev);
- if (midiType == MD_MT32 || ConfMan.getBool("native_mt32"))
+ if (MidiDriver::getMusicType(dev) == MT_MT32 || ConfMan.getBool("native_mt32"))
_isMt32 = true;
_sysExBuf[0] = 0x41;
@@ -271,6 +272,17 @@ void MidiPlayer_Midi::setPatch(int channel, int patch) {
_driver->setPitchBendRange(channel, bendRange);
_driver->send(0xc0 | channel, _patchMap[patch], 0);
+
+ // Send a pointless command to work around a firmware bug in common
+ // USB-MIDI cables. If the first MIDI command in a USB packet is a
+ // Cx or Dx command, the second command in the packet is dropped
+ // somewhere.
+ // FIXME: consider putting a workaround in the MIDI backend drivers
+ // instead.
+ // Known to be affected: alsa, coremidi
+ // Known *not* to be affected: windows (only seems to send one MIDI
+ // command per USB packet even if the device allows larger packets).
+ _driver->send(0xb0 | channel, 0x0a, _channels[channel].pan);
}
void MidiPlayer_Midi::send(uint32 b) {
@@ -306,6 +318,13 @@ void MidiPlayer_Midi::send(uint32 b) {
}
}
+// We return 1 for mt32, because if we remap channels to 0 for mt32, those won't get played at all
+int MidiPlayer_Midi::getFirstChannel() {
+ if (_isMt32)
+ return 1;
+ return 0;
+}
+
void MidiPlayer_Midi::setVolume(byte volume) {
_masterVolume = volume;
diff --git a/engines/sci/sound/drivers/mididriver.h b/engines/sci/sound/drivers/mididriver.h
index 12d3e57f5d..58803db260 100644
--- a/engines/sci/sound/drivers/mididriver.h
+++ b/engines/sci/sound/drivers/mididriver.h
@@ -86,6 +86,7 @@ public:
virtual byte getPlayId() = 0;
virtual int getPolyphony() const = 0;
+ virtual int getFirstChannel() { return 0; };
virtual void setVolume(byte volume) {
if(_driver)
diff --git a/engines/sci/sound/iterator/core.cpp b/engines/sci/sound/iterator/core.cpp
deleted file mode 100644
index 7cd730b3e2..0000000000
--- a/engines/sci/sound/iterator/core.cpp
+++ /dev/null
@@ -1,1013 +0,0 @@
-/* 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$
- *
- */
-
-/* Sound subsystem core: Event handler, sound player dispatching */
-
-#include "sci/sci.h"
-#ifdef USE_OLD_MUSIC_FUNCTIONS
-
-#include "sci/sound/iterator/core.h"
-#include "sci/sound/iterator/iterator.h"
-#include "sci/sound/drivers/mididriver.h"
-
-#include "common/system.h"
-#include "common/timer.h"
-
-#include "sound/mixer.h"
-
-namespace Sci {
-
-/* Plays a song iterator that found a PCM through a PCM device, if possible
-** Parameters: (SongIterator *) it: The iterator to play
-** (SongHandle) handle: Debug handle
-** Returns : (int) 0 if the effect will not be played, nonzero if it will
-** This assumes that the last call to 'it->next()' returned SI_PCM.
-*/
-static int sfx_play_iterator_pcm(SongIterator *it, SongHandle handle);
-
-
-#pragma mark -
-
-
-class SfxPlayer {
-public:
- /** Number of voices that can play simultaneously */
- int _polyphony;
-
-protected:
- SciVersion _soundVersion;
- MidiPlayer *_mididrv;
-
- SongIterator *_iterator;
- Audio::Timestamp _wakeupTime;
- Audio::Timestamp _currentTime;
- uint32 _pauseTimeDiff;
-
- bool _paused;
- bool _iteratorIsDone;
- uint32 _tempo;
-
- Common::Mutex _mutex;
- int _volume;
-
- void play_song(SongIterator *it);
- static void player_timer_callback(void *refCon);
-
-public:
- SfxPlayer(SciVersion soundVersion);
- ~SfxPlayer();
-
- /**
- * Initializes the player.
- * @param resMan a resource manager for driver initialization
- * @param expected_latency expected delay in between calls to 'maintenance' (in microseconds)
- * @return Common::kNoError on success, Common::kUnknownError on failure
- */
- Common::Error init(ResourceManager *resMan, int expected_latency);
-
- /**
- * Adds an iterator to the song player
- * @param it The iterator to play
- * @param start_time The time to assume as the time the first MIDI command executes at
- * @return Common::kNoError on success, Common::kUnknownError on failure
- *
- * The iterator should not be cloned (to avoid memory leaks) and
- * may be modified according to the needs of the player.
- * Implementors may use the 'sfx_iterator_combine()' function
- * to add iterators onto their already existing iterators.
- */
- Common::Error add_iterator(SongIterator *it, uint32 start_time);
-
- /**
- * Stops the currently playing song and deletes the associated iterator.
- * @return Common::kNoError on success, Common::kUnknownError on failure
- */
- Common::Error stop();
-
- /**
- * Transmits a song iterator message to the active song.
- * @param msg the message to transmit
- * @return Common::kNoError on success, Common::kUnknownError on failure
- */
- Common::Error iterator_message(const SongIterator::Message &msg);
-
- /**
- * Pauses song playing.
- * @return Common::kNoError on success, Common::kUnknownError on failure
- */
- Common::Error pause();
-
- /**
- * Resumes song playing after a pause.
- * @return Common::kNoError on success, Common::kUnknownError on failure
- */
- Common::Error resume();
-
- /**
- * Pass a raw MIDI event to the synth.
- * @param argc length of buffer holding the midi event
- * @param argv the buffer itself
- */
- void tell_synth(int buf_nr, byte *buf);
-
- void setVolume(int vol);
-
- int getVolume();
-};
-
-SfxPlayer::SfxPlayer(SciVersion soundVersion)
- : _soundVersion(soundVersion), _wakeupTime(0, SFX_TICKS_PER_SEC), _currentTime(0, 1) {
- _polyphony = 0;
-
- _mididrv = 0;
-
- _iterator = NULL;
- _pauseTimeDiff = 0;
-
- _paused = false;
- _iteratorIsDone = false;
- _tempo = 0;
-
- _volume = 15;
-}
-
-SfxPlayer::~SfxPlayer() {
- if (_mididrv) {
- _mididrv->close();
- delete _mididrv;
- }
- delete _iterator;
- _iterator = NULL;
-}
-
-void SfxPlayer::play_song(SongIterator *it) {
- while (_iterator && _wakeupTime.msecsDiff(_currentTime) <= 0) {
- int delay;
- byte buf[8];
- int result;
-
- switch ((delay = songit_next(&(_iterator),
- buf, &result,
- IT_READER_MASK_ALL
- | IT_READER_MAY_FREE
- | IT_READER_MAY_CLEAN))) {
-
- case SI_FINISHED:
- delete _iterator;
- _iterator = NULL;
- _iteratorIsDone = true;
- return;
-
- case SI_IGNORE:
- case SI_LOOP:
- case SI_RELATIVE_CUE:
- case SI_ABSOLUTE_CUE:
- break;
-
- case SI_PCM:
- sfx_play_iterator_pcm(_iterator, 0);
- break;
-
- case 0:
- static_cast<MidiDriver *>(_mididrv)->send(buf[0], buf[1], buf[2]);
-
- break;
-
- default:
- _wakeupTime = _wakeupTime.addFrames(delay);
- }
- }
-}
-
-void SfxPlayer::tell_synth(int buf_nr, byte *buf) {
- byte op1 = (buf_nr < 2 ? 0 : buf[1]);
- byte op2 = (buf_nr < 3 ? 0 : buf[2]);
-
- static_cast<MidiDriver *>(_mididrv)->send(buf[0], op1, op2);
-}
-
-void SfxPlayer::player_timer_callback(void *refCon) {
- SfxPlayer *thePlayer = (SfxPlayer *)refCon;
- assert(refCon);
- Common::StackLock lock(thePlayer->_mutex);
-
- if (thePlayer->_iterator && !thePlayer->_iteratorIsDone && !thePlayer->_paused) {
- thePlayer->play_song(thePlayer->_iterator);
- }
-
- thePlayer->_currentTime = thePlayer->_currentTime.addFrames(1);
-}
-
-/* API implementation */
-
-Common::Error SfxPlayer::init(ResourceManager *resMan, int expected_latency) {
- MidiDriverType musicDriver = MidiDriver::detectMusicDriver(MDT_PCSPK | MDT_ADLIB);
-
- switch (musicDriver) {
- case MD_ADLIB:
- // FIXME: There's no Amiga sound option, so we hook it up to AdLib
- if (g_sci->getPlatform() == Common::kPlatformAmiga)
- _mididrv = MidiPlayer_Amiga_create(_soundVersion);
- else
- _mididrv = MidiPlayer_AdLib_create(_soundVersion);
- break;
- case MD_PCJR:
- _mididrv = MidiPlayer_PCJr_create(_soundVersion);
- break;
- case MD_PCSPK:
- _mididrv = MidiPlayer_PCSpeaker_create(_soundVersion);
- break;
- default:
- break;
- }
-
- assert(_mididrv);
-
- _polyphony = _mididrv->getPolyphony();
-
- _tempo = _mididrv->getBaseTempo();
- uint32 time = g_system->getMillis();
- _currentTime = Audio::Timestamp(time, 1000000 / _tempo);
- _wakeupTime = Audio::Timestamp(time, SFX_TICKS_PER_SEC);
-
- _mididrv->setTimerCallback(this, player_timer_callback);
- _mididrv->open(resMan);
- _mididrv->setVolume(_volume);
-
- return Common::kNoError;
-}
-
-Common::Error SfxPlayer::add_iterator(SongIterator *it, uint32 start_time) {
- Common::StackLock lock(_mutex);
- SIMSG_SEND(it, SIMSG_SET_PLAYMASK(_mididrv->getPlayId()));
- SIMSG_SEND(it, SIMSG_SET_RHYTHM(_mididrv->hasRhythmChannel()));
-
- if (_iterator == NULL) {
- // Resync with clock
- _currentTime = Audio::Timestamp(g_system->getMillis(), 1000000 / _tempo);
- _wakeupTime = Audio::Timestamp(start_time, SFX_TICKS_PER_SEC);
- }
-
- _iterator = sfx_iterator_combine(_iterator, it);
- _iteratorIsDone = false;
-
- return Common::kNoError;
-}
-
-Common::Error SfxPlayer::stop() {
- debug(3, "Player: Stopping song iterator %p", (void *)_iterator);
- Common::StackLock lock(_mutex);
- delete _iterator;
- _iterator = NULL;
- for (int i = 0; i < MIDI_CHANNELS; i++)
- static_cast<MidiDriver *>(_mididrv)->send(0xb0 + i, SCI_MIDI_CHANNEL_NOTES_OFF, 0);
-
- return Common::kNoError;
-}
-
-Common::Error SfxPlayer::iterator_message(const SongIterator::Message &msg) {
- Common::StackLock lock(_mutex);
- if (!_iterator) {
- return Common::kUnknownError;
- }
-
- songit_handle_message(&_iterator, msg);
-
- return Common::kNoError;
-}
-
-Common::Error SfxPlayer::pause() {
- Common::StackLock lock(_mutex);
-
- _paused = true;
- _pauseTimeDiff = _wakeupTime.msecsDiff(_currentTime);
-
- _mididrv->playSwitch(false);
-
- return Common::kNoError;
-}
-
-Common::Error SfxPlayer::resume() {
- Common::StackLock lock(_mutex);
-
- _wakeupTime = Audio::Timestamp(_currentTime.msecs() + _pauseTimeDiff, SFX_TICKS_PER_SEC);
- _mididrv->playSwitch(true);
- _paused = false;
-
- return Common::kNoError;
-}
-
-void SfxPlayer::setVolume(int vol) {
- _mididrv->setVolume(vol);
-}
-
-int SfxPlayer::getVolume() {
- return _mididrv->getVolume();
-}
-
-#pragma mark -
-
-void SfxState::sfx_reset_player() {
- if (_player)
- _player->stop();
-}
-
-void SfxState::sfx_player_tell_synth(int buf_nr, byte *buf) {
- if (_player)
- _player->tell_synth(buf_nr, buf);
-}
-
-int SfxState::sfx_get_player_polyphony() {
- if (_player)
- return _player->_polyphony;
- else
- return 0;
-}
-
-SfxState::SfxState() {
- _player = NULL;
- _it = NULL;
- _flags = 0;
- _song = NULL;
- _suspended = 0;
-}
-
-SfxState::~SfxState() {
-}
-
-
-void SfxState::freezeTime() {
- /* Freezes the top song delay time */
- const Audio::Timestamp ctime = Audio::Timestamp(g_system->getMillis(), SFX_TICKS_PER_SEC);
- Song *song = _song;
-
- while (song) {
- song->_delay = song->_wakeupTime.frameDiff(ctime);
- if (song->_delay < 0)
- song->_delay = 0;
-
- song = song->_nextPlaying;
- }
-}
-
-void SfxState::thawTime() {
- /* inverse of freezeTime() */
- const Audio::Timestamp ctime = Audio::Timestamp(g_system->getMillis(), SFX_TICKS_PER_SEC);
- Song *song = _song;
-
- while (song) {
- song->_wakeupTime = ctime.addFrames(song->_delay);
-
- song = song->_nextPlaying;
- }
-}
-
-#if 0
-// Unreferenced - removed
-static void _dump_playing_list(SfxState *self, char *msg) {
- Song *song = self->_song;
-
- fprintf(stderr, "[] Song list : [ ");
- song = *(self->_songlib.lib);
- while (song) {
- fprintf(stderr, "%08lx:%d ", song->handle, song->_status);
- song = song->_nextPlaying;
- }
- fprintf(stderr, "]\n");
-
- fprintf(stderr, "[] Play list (%s) : [ " , msg);
-
- while (song) {
- fprintf(stderr, "%08lx ", song->handle);
- song = song->_nextPlaying;
- }
-
- fprintf(stderr, "]\n");
-}
-#endif
-
-#if 0
-static void _dump_songs(SfxState *self) {
- Song *song = self->_song;
-
- fprintf(stderr, "Cue iterators:\n");
- song = *(self->_songlib.lib);
- while (song) {
- fprintf(stderr, " **\tHandle %08x (p%d): status %d\n",
- song->handle, song->_priority, song->_status);
- SIMSG_SEND(song->_it, SIMSG_PRINT(1));
- song = song->_next;
- }
-
- if (self->_player) {
- fprintf(stderr, "Audio iterator:\n");
- self->_player->iterator_message(SongIterator::Message(0, SIMSG_PRINT(1)));
- }
-}
-#endif
-
-bool SfxState::isPlaying(Song *song) {
- Song *playing_song = _song;
-
- /* _dump_playing_list(this, "is-playing");*/
-
- while (playing_song) {
- if (playing_song == song)
- return true;
- playing_song = playing_song->_nextPlaying;
- }
- return false;
-}
-
-void SfxState::setSongStatus(Song *song, int status) {
- const Audio::Timestamp ctime = Audio::Timestamp(g_system->getMillis(), SFX_TICKS_PER_SEC);
-
- switch (status) {
-
- case SOUND_STATUS_STOPPED:
- // Reset
- song->_it->init();
- break;
-
- case SOUND_STATUS_SUSPENDED:
- case SOUND_STATUS_WAITING:
- if (song->_status == SOUND_STATUS_PLAYING) {
- // Update delay, set wakeup_time
- song->_delay += song->_wakeupTime.frameDiff(ctime);
- song->_wakeupTime = ctime;
- }
- if (status == SOUND_STATUS_SUSPENDED)
- break;
-
- /* otherwise... */
-
- case SOUND_STATUS_PLAYING:
- if (song->_status == SOUND_STATUS_STOPPED) {
- // Starting anew
- song->_wakeupTime = ctime;
- }
-
- if (isPlaying(song))
- status = SOUND_STATUS_PLAYING;
- else
- status = SOUND_STATUS_WAITING;
- break;
-
- default:
- fprintf(stderr, "%s L%d: Attempt to set invalid song"
- " state %d!\n", __FILE__, __LINE__, status);
- return;
-
- }
- song->_status = status;
-}
-
-/* Update internal state iff only one song may be played */
-void SfxState::updateSingleSong() {
- Song *newsong = _songlib.findFirstActive();
-
- if (newsong != _song) {
- freezeTime(); /* Store song delay time */
-
- if (_player)
- _player->stop();
-
- if (newsong) {
- if (!newsong->_it)
- return; /* Restore in progress and not ready for this yet */
-
- /* Change song */
- if (newsong->_status == SOUND_STATUS_WAITING)
- setSongStatus(newsong, SOUND_STATUS_PLAYING);
-
- /* Change instrument mappings */
- } else {
- /* Turn off sound */
- }
- if (_song) {
- if (_song->_status == SOUND_STATUS_PLAYING)
- setSongStatus(newsong, SOUND_STATUS_WAITING);
- }
-
- Common::String debugMessage = "[SFX] Changing active song:";
- if (!_song) {
- debugMessage += " New song:";
- } else {
- char tmp[50];
- sprintf(tmp, " pausing %08lx, now playing ", _song->_handle);
- debugMessage += tmp;
- }
-
- if (newsong) {
- char tmp[20];
- sprintf(tmp, "%08lx\n", newsong->_handle);
- debugMessage += tmp;
- } else {
- debugMessage += " none\n";
- }
-
- debugC(2, kDebugLevelSound, "%s", debugMessage.c_str());
-
- _song = newsong;
- thawTime(); /* Recover song delay time */
-
- if (newsong && _player) {
- SongIterator *clonesong = newsong->_it->clone(newsong->_delay);
-
- _player->add_iterator(clonesong, newsong->_wakeupTime.msecs());
- }
- }
-}
-
-
-void SfxState::updateMultiSong() {
- Song *oldfirst = _song;
- Song *oldseeker;
- Song *newsong = _songlib.findFirstActive();
- Song *newseeker;
- Song not_playing_anymore; /* Dummy object, referenced by
- ** songs which are no longer
- ** active. */
-
- /* _dump_playing_list(this, "before");*/
- freezeTime(); /* Store song delay time */
-
- // WORKAROUND: sometimes, newsong can be NULL (e.g. in SQ4).
- // Handle this here, so that we avoid a crash
- if (!newsong) {
- // Iterators should get freed when there's only one song left playing
- if(oldfirst && oldfirst->_status == SOUND_STATUS_STOPPED) {
- debugC(2, kDebugLevelSound, "[SFX] Stopping song %lx", oldfirst->_handle);
- if (_player && oldfirst->_it)
- _player->iterator_message(SongIterator::Message(oldfirst->_it->ID, SIMSG_STOP));
- }
- return;
- }
-
- for (newseeker = newsong; newseeker;
- newseeker = newseeker->_nextPlaying) {
- if (!newseeker || !newseeker->_it)
- return; /* Restore in progress and not ready for this yet */
- }
-
- /* First, put all old songs into the 'stopping' list and
- ** mark their 'next-playing' as not_playing_anymore. */
- for (oldseeker = oldfirst; oldseeker;
- oldseeker = oldseeker->_nextStopping) {
- oldseeker->_nextStopping = oldseeker->_nextPlaying;
- oldseeker->_nextPlaying = &not_playing_anymore;
-
- if (oldseeker == oldseeker->_nextPlaying) {
- error("updateMultiSong() failed. Breakpoint in %s, line %d", __FILE__, __LINE__);
- }
- }
-
- /* Second, re-generate the new song queue. */
- for (newseeker = newsong; newseeker; newseeker = newseeker->_nextPlaying) {
- newseeker->_nextPlaying = _songlib.findNextActive(newseeker);
-
- if (newseeker == newseeker->_nextPlaying) {
- error("updateMultiSong() failed. Breakpoint in %s, line %d", __FILE__, __LINE__);
- }
- }
- /* We now need to update the currently playing song list, because we're
- ** going to use some functions that require this list to be in a sane
- ** state (particularly isPlaying(), indirectly */
- _song = newsong;
-
- /* Third, stop all old songs */
- for (oldseeker = oldfirst; oldseeker;
- oldseeker = oldseeker->_nextStopping)
- if (oldseeker->_nextPlaying == &not_playing_anymore) {
- setSongStatus(oldseeker, SOUND_STATUS_SUSPENDED);
- debugC(2, kDebugLevelSound, "[SFX] Stopping song %lx", oldseeker->_handle);
-
- if (_player && oldseeker->_it)
- _player->iterator_message(SongIterator::Message(oldseeker->_it->ID, SIMSG_STOP));
- oldseeker->_nextPlaying = NULL; /* Clear this pointer; we don't need the tag anymore */
- }
-
- for (newseeker = newsong; newseeker; newseeker = newseeker->_nextPlaying) {
- if (newseeker->_status != SOUND_STATUS_PLAYING && _player) {
- debugC(2, kDebugLevelSound, "[SFX] Adding song %lx", newseeker->_it->ID);
-
- SongIterator *clonesong = newseeker->_it->clone(newseeker->_delay);
- _player->add_iterator(clonesong, g_system->getMillis());
- }
- setSongStatus(newseeker, SOUND_STATUS_PLAYING);
- }
-
- _song = newsong;
- thawTime();
- /* _dump_playing_list(this, "after");*/
-}
-
-/* Update internal state */
-void SfxState::update() {
- if (_flags & SFX_STATE_FLAG_MULTIPLAY)
- updateMultiSong();
- else
- updateSingleSong();
-}
-
-static int sfx_play_iterator_pcm(SongIterator *it, SongHandle handle) {
-#ifdef DEBUG_SONG_API
- fprintf(stderr, "[sfx-core] Playing PCM: %08lx\n", handle);
-#endif
- if (g_system->getMixer()->isReady()) {
- Audio::AudioStream *newfeed = it->getAudioStream();
- if (newfeed) {
- g_system->getMixer()->playStream(Audio::Mixer::kSFXSoundType, 0, newfeed);
- return 1;
- }
- }
- return 0;
-}
-
-#define DELAY (1000000 / SFX_TICKS_PER_SEC)
-
-void SfxState::sfx_init(ResourceManager *resMan, int flags, SciVersion soundVersion) {
- _songlib._lib = 0;
- _song = NULL;
- _flags = flags;
-
- _player = NULL;
-
- if (flags & SFX_STATE_FLAG_NOSOUND) {
- warning("[SFX] Sound disabled");
- return;
- }
-
-#ifdef DEBUG_SONG_API
- fprintf(stderr, "[sfx-core] Initialising: flags=%x\n", flags);
-#endif
-
- /*-------------------*/
- /* Initialise player */
- /*-------------------*/
-
- if (!resMan) {
- warning("[SFX] Warning: No resource manager present, cannot initialise player");
- return;
- }
-
- _player = new SfxPlayer(soundVersion);
-
- if (!_player) {
- warning("[SFX] No song player found");
- return;
- }
-
- if (_player->init(resMan, DELAY / 1000)) {
- warning("[SFX] Song player reported error, disabled");
- delete _player;
- _player = NULL;
- }
-
- _resMan = resMan;
-}
-
-void SfxState::sfx_exit() {
-#ifdef DEBUG_SONG_API
- fprintf(stderr, "[sfx-core] Uninitialising\n");
-#endif
-
- delete _player;
- _player = 0;
-
- g_system->getMixer()->stopAll();
-
- _songlib.freeSounds();
-}
-
-void SfxState::sfx_suspend(bool suspend) {
-#ifdef DEBUG_SONG_API
- fprintf(stderr, "[sfx-core] Suspending? = %d\n", suspend);
-#endif
- if (suspend && (!_suspended)) {
- /* suspend */
-
- freezeTime();
- if (_player)
- _player->pause();
- /* Suspend song player */
-
- } else if (!suspend && (_suspended)) {
- /* unsuspend */
-
- thawTime();
- if (_player)
- _player->resume();
-
- /* Unsuspend song player */
- }
-
- _suspended = suspend;
-}
-
-int SfxState::sfx_poll(SongHandle *handle, int *cue) {
- if (!_song)
- return 0; /* No milk today */
-
- *handle = _song->_handle;
-
-#ifdef DEBUG_SONG_API
- fprintf(stderr, "[sfx-core] Polling any (%08lx)\n", *handle);
-#endif
- return sfx_poll_specific(*handle, cue);
-}
-
-int SfxState::sfx_poll_specific(SongHandle handle, int *cue) {
- const Audio::Timestamp ctime = Audio::Timestamp(g_system->getMillis(), SFX_TICKS_PER_SEC);
- Song *song = _song;
-
- while (song && song->_handle != handle)
- song = song->_nextPlaying;
-
- if (!song)
- return 0; /* Song not playing */
-
- debugC(2, kDebugLevelSound, "[SFX:CUE] Polled song %08lx ", handle);
-
- while (1) {
- if (song->_wakeupTime.frameDiff(ctime) > 0)
- return 0; /* Patience, young hacker! */
-
- byte buf[8];
- int result = songit_next(&(song->_it), buf, cue, IT_READER_MASK_ALL);
-
- switch (result) {
-
- case SI_FINISHED:
- setSongStatus(song, SOUND_STATUS_STOPPED);
- update();
- /* ...fall through... */
- case SI_LOOP:
- case SI_RELATIVE_CUE:
- case SI_ABSOLUTE_CUE:
- if (result == SI_FINISHED)
- debugC(2, kDebugLevelSound, " => finished");
- else {
- if (result == SI_LOOP)
- debugC(2, kDebugLevelSound, " => Loop: %d (0x%x)", *cue, *cue);
- else
- debugC(2, kDebugLevelSound, " => Cue: %d (0x%x)", *cue, *cue);
-
- }
- return result;
-
- default:
- if (result > 0)
- song->_wakeupTime = song->_wakeupTime.addFrames(result);
-
- /* Delay */
- break;
- }
- }
-
-}
-
-
-/*****************/
-/* Song basics */
-/*****************/
-
-void SfxState::sfx_add_song(SongIterator *it, int priority, SongHandle handle, int number) {
- Song *song = _songlib.findSong(handle);
-
-#ifdef DEBUG_SONG_API
- fprintf(stderr, "[sfx-core] Adding song: %08lx at %d, it=%p\n", handle, priority, it);
-#endif
- if (!it) {
- error("[SFX] Attempt to add empty song with handle %08lx", handle);
- return;
- }
-
- it->init();
-
- /* If we're already playing this, stop it */
- /* Tell player to shut up */
-// _dump_songs(this);
-
- if (_player)
- _player->iterator_message(SongIterator::Message(handle, SIMSG_STOP));
-
- if (song) {
- setSongStatus( song, SOUND_STATUS_STOPPED);
-
- fprintf(stderr, "Overwriting old song (%08lx) ...\n", handle);
- if (song->_status == SOUND_STATUS_PLAYING || song->_status == SOUND_STATUS_SUSPENDED) {
- delete it;
- error("Unexpected (error): Song %ld still playing/suspended (%d)",
- handle, song->_status);
- return;
- } else {
- _songlib.removeSong(handle); /* No duplicates */
- }
-
- }
-
- song = new Song(handle, it, priority);
- song->_resourceNum = number;
- song->_hold = 0;
- song->_loops = 0;
- song->_wakeupTime = Audio::Timestamp(g_system->getMillis(), SFX_TICKS_PER_SEC);
- _songlib.addSong(song);
- _song = NULL; /* As above */
- update();
-
- return;
-}
-
-void SfxState::sfx_remove_song(SongHandle handle) {
-#ifdef DEBUG_SONG_API
- fprintf(stderr, "[sfx-core] Removing song: %08lx\n", handle);
-#endif
- if (_song && _song->_handle == handle)
- _song = NULL;
-
- _songlib.removeSong(handle);
- update();
-}
-
-
-
-/**********************/
-/* Song modifications */
-/**********************/
-
-#define ASSERT_SONG(s) if (!(s)) { warning("Looking up song handle %08lx failed in %s, L%d", handle, __FILE__, __LINE__); return; }
-
-void SfxState::sfx_song_set_status(SongHandle handle, int status) {
- Song *song = _songlib.findSong(handle);
- ASSERT_SONG(song);
-#ifdef DEBUG_SONG_API
- fprintf(stderr, "[sfx-core] Setting song status to %d"
- " (0:stop, 1:play, 2:susp, 3:wait): %08lx\n", status, handle);
-#endif
-
- setSongStatus(song, status);
-
- update();
-}
-
-void SfxState::sfx_song_set_fade(SongHandle handle, fade_params_t *params) {
-#ifdef DEBUG_SONG_API
- static const char *stopmsg[] = {"??? Should not happen", "Do not stop afterwards", "Stop afterwards"};
-#endif
- Song *song = _songlib.findSong(handle);
-
- ASSERT_SONG(song);
-
-#ifdef DEBUG_SONG_API
- fprintf(stderr, "[sfx-core] Setting fade params of %08lx to "
- "final volume %d in steps of %d per %d ticks. %s.",
- handle, fade->final_volume, fade->step_size, fade->ticks_per_step,
- stopmsg[fade->action]);
-#endif
-
- SIMSG_SEND_FADE(song->_it, params);
-
- update();
-}
-
-void SfxState::sfx_song_renice(SongHandle handle, int priority) {
- Song *song = _songlib.findSong(handle);
- ASSERT_SONG(song);
-#ifdef DEBUG_SONG_API
- fprintf(stderr, "[sfx-core] Renicing song %08lx to %d\n",
- handle, priority);
-#endif
-
- song->_priority = priority;
-
- update();
-}
-
-void SfxState::sfx_song_set_loops(SongHandle handle, int loops) {
- Song *song = _songlib.findSong(handle);
- SongIterator::Message msg = SongIterator::Message(handle, SIMSG_SET_LOOPS(loops));
- ASSERT_SONG(song);
-
- song->_loops = loops;
-#ifdef DEBUG_SONG_API
- fprintf(stderr, "[sfx-core] Setting loops on %08lx to %d\n",
- handle, loops);
-#endif
- songit_handle_message(&(song->_it), msg);
-
- if (_player/* && _player->send_iterator_message*/)
- /* FIXME: The above should be optional! */
- _player->iterator_message(msg);
-}
-
-void SfxState::sfx_song_set_hold(SongHandle handle, int hold) {
- Song *song = _songlib.findSong(handle);
- SongIterator::Message msg = SongIterator::Message(handle, SIMSG_SET_HOLD(hold));
- ASSERT_SONG(song);
-
- song->_hold = hold;
-#ifdef DEBUG_SONG_API
- fprintf(stderr, "[sfx-core] Setting hold on %08lx to %d\n",
- handle, hold);
-#endif
- songit_handle_message(&(song->_it), msg);
-
- if (_player/* && _player->send_iterator_message*/)
- /* FIXME: The above should be optional! */
- _player->iterator_message(msg);
-}
-
-/* Different from the one in iterator.c */
-static const int MIDI_cmdlen[16] = {0, 0, 0, 0, 0, 0, 0, 0,
- 3, 3, 0, 3, 2, 0, 3, 0
- };
-
-static const SongHandle midi_send_base = 0xffff0000;
-
-Common::Error SfxState::sfx_send_midi(SongHandle handle, int channel,
- int command, int arg1, int arg2) {
- byte buffer[5];
-
- /* Yes, in that order. SCI channel mutes are actually done via
- a counting semaphore. 0 means to decrement the counter, 1
- to increment it. */
- static const char *channel_state[] = {"ON", "OFF"};
-
- if (command == 0xb0 &&
- arg1 == SCI_MIDI_CHANNEL_MUTE) {
- warning("TODO: channel mute (channel %d %s)", channel, channel_state[arg2]);
- /* We need to have a GET_PLAYMASK interface to use
- here. SET_PLAYMASK we've got.
- */
- return Common::kNoError;
- }
-
- buffer[0] = channel | command; /* No channel remapping yet */
-
- switch (command) {
- case 0x80 :
- case 0x90 :
- case 0xb0 :
- buffer[1] = arg1 & 0xff;
- buffer[2] = arg2 & 0xff;
- break;
- case 0xc0 :
- buffer[1] = arg1 & 0xff;
- break;
- case 0xe0 :
- buffer[1] = (arg1 & 0x7f) | 0x80;
- buffer[2] = (arg1 & 0xff00) >> 7;
- break;
- default:
- warning("Unexpected explicit MIDI command %02x", command);
- return Common::kUnknownError;
- }
-
- if (_player)
- _player->tell_synth(MIDI_cmdlen[command >> 4], buffer);
- return Common::kNoError;
-}
-
-int SfxState::sfx_getVolume() {
- return _player->getVolume();
-}
-
-void SfxState::sfx_setVolume(int volume) {
- _player->setVolume(volume);
-}
-
-void SfxState::sfx_all_stop() {
-#ifdef DEBUG_SONG_API
- fprintf(stderr, "[sfx-core] All stop\n");
-#endif
-
- _songlib.freeSounds();
- update();
-}
-
-} // End of namespace Sci
-
-#endif // USE_OLD_MUSIC_FUNCTIONS
diff --git a/engines/sci/sound/iterator/core.h b/engines/sci/sound/iterator/core.h
deleted file mode 100644
index a44fe2ecae..0000000000
--- a/engines/sci/sound/iterator/core.h
+++ /dev/null
@@ -1,209 +0,0 @@
-/* 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$
- *
- */
-
-/* Sound engine */
-#ifndef SCI_SFX_CORE_H
-#define SCI_SFX_CORE_H
-
-#include "common/error.h"
-
-#include "sci/sci.h" // for USE_OLD_MUSIC_FUNCTIONS
-
-#ifdef USE_OLD_MUSIC_FUNCTIONS
-#include "sci/sound/iterator/songlib.h"
-#include "sci/resource.h"
-
-namespace Sci {
-
-class SfxPlayer;
-class SongIterator;
-struct fade_params_t;
-
-#define SFX_TICKS_PER_SEC 60 /* MIDI ticks per second */
-
-
-#define SFX_STATE_FLAG_MULTIPLAY (1 << 0) /* More than one song playable
-** simultaneously ? */
-#define SFX_STATE_FLAG_NOSOUND (1 << 1) /* Completely disable sound playing */
-
-class SfxState {
-private:
- SfxPlayer *_player;
-
-public: // FIXME, make private
- SongIterator *_it; /**< The song iterator at the heart of things */
- uint _flags; /**< SFX_STATE_FLAG_* */
- SongLibrary _songlib; /**< Song library */
- Song *_song; /**< Active song, or start of active song chain */
- bool _suspended; /**< Whether we are suspended */
- ResourceManager *_resMan;
-
-public:
- SfxState();
- ~SfxState();
-
- /***********/
- /* General */
- /***********/
-
- /* Initializes the sound engine
- ** Parameters: (ResourceManager *) resMan: Resource manager for initialization
- ** (int) flags: SFX_STATE_FLAG_*
- */
- void sfx_init(ResourceManager *resMan, int flags, SciVersion soundVersion);
-
- /** Deinitializes the sound subsystem. */
- void sfx_exit();
-
- /* Suspends/unsuspends the sound sybsystem
- ** Parameters: (int) suspend: Whether to suspend (non-null) or to unsuspend
- */
- void sfx_suspend(bool suspend);
-
- /* Polls the sound server for cues etc.
- ** Returns : (int) 0 if the cue queue is empty, SI_LOOP, SI_CUE, or SI_FINISHED otherwise
- ** (SongHandle) *handle: The affected handle
- ** (int) *cue: The sound cue number (if SI_CUE), or the loop number (if SI_LOOP)
- */
- int sfx_poll(SongHandle *handle, int *cue);
-
- /* Polls the sound server for cues etc.
- ** Parameters: (SongHandle) handle: The handle to poll
- ** Returns : (int) 0 if the cue queue is empty, SI_LOOP, SI_CUE, or SI_FINISHED otherwise
- ** (int) *cue: The sound cue number (if SI_CUE), or the loop number (if SI_LOOP)
- */
- int sfx_poll_specific(SongHandle handle, int *cue);
-
- /* Determines the current global volume settings
- ** Returns : (int) The global volume, between 0 (silent) and 127 (max. volume)
- */
- int sfx_getVolume();
-
- /* Determines the current global volume settings
- ** Parameters: (int) volume: The new global volume, between 0 and 127 (see above)
- */
- void sfx_setVolume(int volume);
-
- /* Stops all songs currently playing, purges song library
- */
- void sfx_all_stop();
-
-
- /*****************/
- /* Song basics */
- /*****************/
-
- /* Adds a song to the internal sound library
- ** Parameters: (SongIterator *) it: The iterator describing the song
- ** (int) priority: Initial song priority (higher <-> more important)
- ** (SongHandle) handle: The handle to associate with the song
- */
- void sfx_add_song(SongIterator *it, int priority, SongHandle handle, int resnum);
-
-
- /* Deletes a song and its associated song iterator from the song queue
- ** Parameters: (SongHandle) handle: The song to remove
- */
- void sfx_remove_song(SongHandle handle);
-
-
- /**********************/
- /* Song modifications */
- /**********************/
-
-
- /* Sets the song status, i.e. whether it is playing, suspended, or stopped.
- ** Parameters: (SongHandle) handle: Handle of the song to modify
- ** (int) status: The song status the song should assume
- ** WAITING and PLAYING are set implicitly and essentially describe the same state
- ** as far as this function is concerned.
- */
- void sfx_song_set_status(SongHandle handle, int status);
-
- /* Sets the new song priority
- ** Parameters: (SongHandle) handle: The handle to modify
- ** (int) priority: The priority to set
- */
- void sfx_song_renice(SongHandle handle, int priority);
-
- /* Sets the number of loops for the specified song
- ** Parameters: (SongHandle) handle: The song handle to reference
- ** (int) loops: Number of loops to set
- */
- void sfx_song_set_loops(SongHandle handle, int loops);
-
- /* Sets the number of loops for the specified song
- ** Parameters: (SongHandle) handle: The song handle to reference
- ** (int) hold: Number of loops to setn
- */
- void sfx_song_set_hold(SongHandle handle, int hold);
-
- /* Instructs a song to be faded out
- ** Parameters: (SongHandle) handle: The song handle to reference
- ** (fade_params_t *) fade_setup: The precise fade-out configuration to use
- */
- void sfx_song_set_fade(SongHandle handle, fade_params_t *fade_setup);
-
-
- // Previously undocumented:
- Common::Error sfx_send_midi(SongHandle handle, int channel,
- int command, int arg1, int arg2);
-
- // misc
-
- /**
- * Determines the polyphony of the player in use.
- * @return Number of voices the active player can emit
- */
- int sfx_get_player_polyphony();
-
- /**
- * Tells the player to stop its internal iterator.
- */
- void sfx_reset_player();
-
- /**
- * Pass a raw MIDI event to the synth of the player.
- * @param argc Length of buffer holding the midi event
- * @param argv The buffer itself
- */
- void sfx_player_tell_synth(int buf_nr, byte *buf);
-
-protected:
- void freezeTime();
- void thawTime();
-
- bool isPlaying(Song *song);
- void setSongStatus(Song *song, int status);
- void updateSingleSong();
- void updateMultiSong();
- void update();
-};
-
-} // End of namespace Sci
-
-#endif // USE_OLD_MUSIC_FUNCTIONS
-
-#endif // SCI_SFX_CORE_H
diff --git a/engines/sci/sound/iterator/iterator.cpp b/engines/sci/sound/iterator/iterator.cpp
deleted file mode 100644
index 5d9d63e5af..0000000000
--- a/engines/sci/sound/iterator/iterator.cpp
+++ /dev/null
@@ -1,1686 +0,0 @@
-/* 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$
- *
- */
-
-/* Song iterators */
-
-#include "common/util.h"
-
-#include "sci/sci.h"
-#ifdef USE_OLD_MUSIC_FUNCTIONS
-
-#include "sci/sound/iterator/iterator_internal.h"
-#include "sci/engine/state.h" // for sfx_player_tell_synth :/
-#include "sci/sound/iterator/core.h" // for sfx_player_tell_synth
-
-#include "sound/audiostream.h"
-#include "sound/mixer.h"
-#include "sound/decoders/raw.h"
-
-namespace Sci {
-
-
-static const int MIDI_cmdlen[16] = {0, 0, 0, 0, 0, 0, 0, 0,
- 2, 2, 2, 2, 1, 1, 2, 0
- };
-
-/*#define DEBUG_DECODING*/
-/*#define DEBUG_VERBOSE*/
-
-/** Find first set bit in bits and return its index. Returns 0 if bits is 0. */
-static int sci_ffs(int bits) {
- if (!bits)
- return 0;
-
- int retval = 1;
-
- while (!(bits & 1)) {
- retval++;
- bits >>= 1;
- }
-
- return retval;
-}
-
-static void print_tabs_id(int nr, songit_id_t id) {
- while (nr-- > 0)
- fprintf(stderr, "\t");
-
- fprintf(stderr, "[%08lx] ", id);
-}
-
-BaseSongIterator::BaseSongIterator(byte *data, uint size, songit_id_t id)
- : _data(data, size) {
- ID = id;
-}
-
-/************************************/
-/*-- SCI0 iterator implementation --*/
-/************************************/
-
-#define SCI0_MIDI_OFFSET 33
-#define SCI0_END_OF_SONG 0xfc /* proprietary MIDI command */
-
-#define SCI0_PCM_SAMPLE_RATE_OFFSET 0x0e
-#define SCI0_PCM_SIZE_OFFSET 0x20
-#define SCI0_PCM_DATA_OFFSET 0x2c
-
-#define CHECK_FOR_END_ABSOLUTE(offset) \
- if (offset > _data.size()) { \
- warning("Reached end of song without terminator (%x/%x) at %d", offset, _data.size(), __LINE__); \
- return SI_FINISHED; \
- }
-
-#define CHECK_FOR_END(offset_augment) \
- if ((channel->offset + (offset_augment)) > channel->end) { \
- channel->state = SI_STATE_FINISHED; \
- warning("Reached end of track %d without terminator (%x+%x/%x) at %d", channel->id, channel->offset, offset_augment, channel->end, __LINE__); \
- return SI_FINISHED; \
- }
-
-
-static int _parse_ticks(byte *data, int *offset_p, int size) {
- int ticks = 0;
- int tempticks;
- int offset = 0;
-
- do {
- tempticks = data[offset++];
- ticks += (tempticks == SCI_MIDI_TIME_EXPANSION_PREFIX) ?
- SCI_MIDI_TIME_EXPANSION_LENGTH : tempticks;
- } while (tempticks == SCI_MIDI_TIME_EXPANSION_PREFIX
- && offset < size);
-
- if (offset_p)
- *offset_p = offset;
-
- return ticks;
-}
-
-
-static int _sci0_get_pcm_data(Sci0SongIterator *self, int *rate, int *xoffset, uint *xsize);
-
-
-#define PARSE_FLAG_LOOPS_UNLIMITED (1 << 0) /* Unlimited # of loops? */
-#define PARSE_FLAG_PARAMETRIC_CUE (1 << 1) /* Assume that cues take an additional "cue value" argument */
-/* This implements a difference between SCI0 and SCI1 cues. */
-
-void SongIteratorChannel::init(int id_, int offset_, int end_) {
- playmask = PLAYMASK_NONE; /* Disable all channels */
- id = id_;
- state = SI_STATE_DELTA_TIME;
- loop_timepos = 0;
- total_timepos = 0;
- timepos_increment = 0;
- delay = 0; /* Only used for more than one channel */
- last_cmd = 0xfe;
-
- offset = loop_offset = initial_offset = offset_;
- end = end_;
-}
-
-void SongIteratorChannel::resetSynthChannels() {
- byte buf[5];
-
- // FIXME: Evil hack
- SfxState &sound = g_sci->getEngineState()->_sound;
-
- for (int i = 0; i < MIDI_CHANNELS; i++) {
- if (playmask & (1 << i)) {
- buf[0] = 0xe0 | i; /* Pitch bend */
- buf[1] = 0x80; /* Wheel center */
- buf[2] = 0x40;
- sound.sfx_player_tell_synth(3, buf);
-
- buf[0] = 0xb0 | i; // Set control
- buf[1] = 0x40; // Hold pedal
- buf[2] = 0x00; // Off
- sound.sfx_player_tell_synth(3, buf);
- /* TODO: Reset other controls? */
- }
- }
-}
-
-int BaseSongIterator::parseMidiCommand(byte *buf, int *result, SongIteratorChannel *channel, int flags) {
- byte cmd;
- int paramsleft;
- int midi_op;
- int midi_channel;
-
- channel->state = SI_STATE_DELTA_TIME;
-
- cmd = _data[channel->offset++];
-
- if (!(cmd & 0x80)) {
- /* 'Running status' mode */
- channel->offset--;
- cmd = channel->last_cmd;
- }
-
- if (cmd == 0xfe) {
- warning("song iterator subsystem: Corrupted sound resource detected.");
- return SI_FINISHED;
- }
-
- midi_op = cmd >> 4;
- midi_channel = cmd & 0xf;
- paramsleft = MIDI_cmdlen[midi_op];
-
-#if 0
- if (1) {
- fprintf(stderr, "[IT]: off=%x, cmd=%02x, takes %d args ",
- channel->offset - 1, cmd, paramsleft);
- fprintf(stderr, "[%02x %02x <%02x> %02x %02x %02x]\n",
- _data[channel->offset-3],
- _data[channel->offset-2],
- _data[channel->offset-1],
- _data[channel->offset],
- _data[channel->offset+1],
- _data[channel->offset+2]);
- }
-#endif
-
- buf[0] = cmd;
-
-
- CHECK_FOR_END(paramsleft);
- memcpy(buf + 1, _data.begin() + channel->offset, paramsleft);
- *result = 1 + paramsleft;
-
- channel->offset += paramsleft;
-
- channel->last_cmd = cmd;
-
- /* Are we supposed to play this channel? */
- if (
- /* First, exclude "global" properties-- such as cues-- from consideration */
- (midi_op < 0xf
- && !(cmd == SCI_MIDI_SET_SIGNAL)
- && !(SCI_MIDI_CONTROLLER(cmd)
- && buf[1] == SCI_MIDI_CUMULATIVE_CUE))
-
- /* Next, check if the channel is allowed */
- && (!((1 << midi_channel) & channel->playmask)))
- return /* Execute next command */
- nextCommand(buf, result);
-
-
- if (cmd == SCI_MIDI_EOT) {
- /* End of track? */
- channel->resetSynthChannels();
- if (_loops > 1) {
- /* If allowed, decrement the number of loops */
- if (!(flags & PARSE_FLAG_LOOPS_UNLIMITED))
- *result = --_loops;
-
-#ifdef DEBUG_DECODING
- fprintf(stderr, "%s L%d: (%p):%d Looping ", __FILE__, __LINE__, this, channel->id);
- if (flags & PARSE_FLAG_LOOPS_UNLIMITED)
- fprintf(stderr, "(indef.)");
- else
- fprintf(stderr, "(%d)", _loops);
- fprintf(stderr, " %x -> %x\n",
- channel->offset, channel->loop_offset);
-#endif
- channel->offset = channel->loop_offset;
- channel->state = SI_STATE_DELTA_TIME;
- channel->total_timepos = channel->loop_timepos;
- channel->last_cmd = 0xfe;
- debugC(2, kDebugLevelSound, "Looping song iterator %08lx.", ID);
- return SI_LOOP;
- } else {
- channel->state = SI_STATE_FINISHED;
- return SI_FINISHED;
- }
-
- } else if (cmd == SCI_MIDI_SET_SIGNAL) {
- if (buf[1] == SCI_MIDI_SET_SIGNAL_LOOP) {
- channel->loop_offset = channel->offset;
- channel->loop_timepos = channel->total_timepos;
-
- return /* Execute next command */
- nextCommand(buf, result);
- } else {
- /* Used to be conditional <= 127 */
- *result = buf[1]; /* Absolute cue */
- return SI_ABSOLUTE_CUE;
- }
- } else if (SCI_MIDI_CONTROLLER(cmd)) {
- switch (buf[1]) {
-
- case SCI_MIDI_CUMULATIVE_CUE:
- if (flags & PARSE_FLAG_PARAMETRIC_CUE)
- _ccc += buf[2];
- else { /* No parameter to CC */
- _ccc++;
- /* channel->offset--; */
- }
- *result = _ccc;
- return SI_RELATIVE_CUE;
-
- case SCI_MIDI_RESET_ON_SUSPEND:
- _resetflag = buf[2];
- break;
-
- case SCI_MIDI_SET_POLYPHONY:
- _polyphony[midi_channel] = buf[2];
-
-#if 0
- {
- Sci1SongIterator *self1 = (Sci1SongIterator *)this;
- int i;
- int voices = 0;
- for (i = 0; i < self1->_numChannels; i++) {
- voices += _polyphony[i];
- }
-
- printf("SET_POLYPHONY(%d, %d) for a total of %d voices\n", midi_channel, buf[2], voices);
- printf("[iterator] DEBUG: Polyphony = [ ");
- for (i = 0; i < self1->_numChannels; i++)
- printf("%d ", _polyphony[i]);
- printf("]\n");
- printf("[iterator] DEBUG: Importance = [ ");
- printf("]\n");
- }
-#endif
- break;
-
- case SCI_MIDI_SET_REVERB:
- break;
-
- case SCI_MIDI_CHANNEL_MUTE:
- warning("CHANNEL_MUTE(%d, %d)", midi_channel, buf[2]);
- break;
-
- case SCI_MIDI_HOLD: {
- // Safe cast: This controller is only used in SCI1
- Sci1SongIterator *self1 = (Sci1SongIterator *)this;
-
- if (buf[2] == self1->_hold) {
- channel->offset = channel->initial_offset;
- channel->state = SI_STATE_COMMAND;
- channel->total_timepos = 0;
-
- self1->_numLoopedChannels = self1->_numActiveChannels - 1;
-
- // FIXME:
- // This implementation of hold breaks getting out of the
- // limo when visiting the airport near the start of LSL5.
- // It seems like all channels should be reset here somehow,
- // but not sure how.
- // Forcing all channel offsets to 0 seems to fix the hang,
- // but somehow slows the exit sequence down to take 20 seconds
- // instead of about 3.
-
- return SI_LOOP;
- }
-
- break;
- }
- case 0x04: /* UNKNOWN NYI (happens in LSL2 gameshow) */
- case 0x46: /* UNKNOWN NYI (happens in LSL3 binoculars) */
- case 0x61: /* UNKNOWN NYI (special for AdLib? Iceman) */
- case 0x73: /* UNKNOWN NYI (happens in Hoyle) */
- case 0xd1: /* UNKNOWN NYI (happens in KQ4 when riding the unicorn) */
- return /* Execute next command */
- nextCommand(buf, result);
-
- case 0x01: /* modulation */
- case 0x07: /* volume */
- case 0x0a: /* panpot */
- case 0x0b: /* expression */
- case 0x40: /* hold */
- case 0x79: /* reset all */
- /* No special treatment neccessary */
- break;
-
- }
- return 0;
-
- } else {
-#if 0
- /* Perform remapping, if neccessary */
- if (cmd != SCI_MIDI_SET_SIGNAL
- && cmd < 0xf0) { /* Not a generic command */
- int chan = cmd & 0xf;
- int op = cmd & 0xf0;
-
- chan = channel_remap[chan];
- buf[0] = chan | op;
- }
-#endif
-
- /* Process as normal MIDI operation */
- return 0;
- }
-}
-
-int BaseSongIterator::processMidi(byte *buf, int *result,
- SongIteratorChannel *channel, int flags) {
- CHECK_FOR_END(0);
-
- switch (channel->state) {
-
- case SI_STATE_PCM: {
- if (_data[channel->offset] == 0
- && _data[channel->offset + 1] == SCI_MIDI_EOT)
- /* Fake one extra tick to trick the interpreter into not killing the song iterator right away */
- channel->state = SI_STATE_PCM_MAGIC_DELTA;
- else
- channel->state = SI_STATE_DELTA_TIME;
- return SI_PCM;
- }
-
- case SI_STATE_PCM_MAGIC_DELTA: {
- int rate;
- int offset;
- uint size;
- int delay;
- if (_sci0_get_pcm_data((Sci0SongIterator *)this, &rate, &offset, &size))
- return SI_FINISHED; /* 'tis broken */
- channel->state = SI_STATE_FINISHED;
- delay = (size * 50 + rate - 1) / rate; /* number of ticks to completion*/
-
- debugC(2, kDebugLevelSound, "delaying %d ticks", delay);
- return delay;
- }
-
- case SI_STATE_UNINITIALISED:
- warning("Attempt to read command from uninitialized iterator");
- init();
- return nextCommand(buf, result);
-
- case SI_STATE_FINISHED:
- return SI_FINISHED;
-
- case SI_STATE_DELTA_TIME: {
- int offset;
- int ticks = _parse_ticks(_data.begin() + channel->offset,
- &offset,
- _data.size() - channel->offset);
-
- channel->offset += offset;
- channel->delay += ticks;
- channel->timepos_increment = ticks;
-
- CHECK_FOR_END(0);
-
- channel->state = SI_STATE_COMMAND;
-
- if (ticks)
- return ticks;
- }
-
- /* continute otherwise... */
-
- case SI_STATE_COMMAND: {
- int retval;
- channel->total_timepos += channel->timepos_increment;
- channel->timepos_increment = 0;
-
- retval = parseMidiCommand(buf, result, channel, flags);
-
- if (retval == SI_FINISHED) {
- if (_numActiveChannels)
- --(_numActiveChannels);
-#ifdef DEBUG_DECODING
- fprintf(stderr, "%s L%d: (%p):%d Finished channel, %d channels left\n",
- __FILE__, __LINE__, this, channel->id,
- _numActiveChannels);
-#endif
- /* If we still have channels left... */
- if (_numActiveChannels) {
- return nextCommand(buf, result);
- }
-
- /* Otherwise, we have reached the end */
- _loops = 0;
- }
-
- return retval;
- }
-
- default:
- error("Invalid iterator state %d", channel->state);
- return SI_FINISHED;
- }
-}
-
-int Sci0SongIterator::nextCommand(byte *buf, int *result) {
- return processMidi(buf, result, &_channel, PARSE_FLAG_PARAMETRIC_CUE);
-}
-
-static int _sci0_header_magic_p(byte *data, int offset, int size) {
- if (offset + 0x10 > size)
- return 0;
- return (data[offset] == 0x1a)
- && (data[offset + 1] == 0x00)
- && (data[offset + 2] == 0x01)
- && (data[offset + 3] == 0x00);
-}
-
-
-static int _sci0_get_pcm_data(Sci0SongIterator *self,
- int *rate, int *xoffset, uint *xsize) {
- int tries = 2;
- bool found_it = false;
- byte *pcm_data;
- int size;
- uint offset = SCI0_MIDI_OFFSET;
-
- if (self->_data[0] != 2)
- return 1;
- /* No such luck */
-
- while ((tries--) && (offset < self->_data.size()) && (!found_it)) {
- // Search through the garbage manually
- // FIXME: Replace offset by an iterator
- Common::Array<byte>::iterator iter = Common::find(self->_data.begin() + offset, self->_data.end(), SCI0_END_OF_SONG);
-
- if (iter == self->_data.end()) {
- warning("Playing unterminated song");
- return 1;
- }
-
- // add one to move it past the END_OF_SONG marker
- iter++;
- offset = iter - self->_data.begin(); // FIXME
-
-
- if (_sci0_header_magic_p(self->_data.begin(), offset, self->_data.size()))
- found_it = true;
- }
-
- if (!found_it) {
- warning("Song indicates presence of PCM, but"
- " none found (finally at offset %04x)", offset);
-
- return 1;
- }
-
- pcm_data = self->_data.begin() + offset;
-
- size = READ_LE_UINT16(pcm_data + SCI0_PCM_SIZE_OFFSET);
-
- /* Two of the format parameters are fixed by design: */
- *rate = READ_LE_UINT16(pcm_data + SCI0_PCM_SAMPLE_RATE_OFFSET);
-
- if (offset + SCI0_PCM_DATA_OFFSET + size != self->_data.size()) {
- int d = offset + SCI0_PCM_DATA_OFFSET + size - self->_data.size();
-
- warning("PCM advertizes %d bytes of data, but %d"
- " bytes are trailing in the resource",
- size, self->_data.size() - (offset + SCI0_PCM_DATA_OFFSET));
-
- if (d > 0)
- size -= d; /* Fix this */
- }
-
- *xoffset = offset;
- *xsize = size;
-
- return 0;
-}
-
-static Audio::AudioStream *makeStream(byte *data, int size, int rate) {
- debugC(2, kDebugLevelSound, "Playing PCM data of size %d, rate %d", size, rate);
-
- // Duplicate the data
- byte *sound = (byte *)malloc(size);
- memcpy(sound, data, size);
-
- // Convert stream format flags
- int flags = Audio::FLAG_UNSIGNED;
- return Audio::makeRawStream(sound, size, rate, flags);
-}
-
-Audio::AudioStream *Sci0SongIterator::getAudioStream() {
- int rate;
- int offset;
- uint size;
- if (_sci0_get_pcm_data(this, &rate, &offset, &size))
- return NULL;
-
- _channel.state = SI_STATE_FINISHED; /* Don't play both PCM and music */
-
- return makeStream(_data.begin() + offset + SCI0_PCM_DATA_OFFSET, size, rate);
-}
-
-SongIterator *Sci0SongIterator::handleMessage(Message msg) {
- if (msg._class == _SIMSG_BASE) {
- switch (msg._type) {
-
- case _SIMSG_BASEMSG_PRINT:
- print_tabs_id(msg._arg.i, ID);
- debugC(2, kDebugLevelSound, "SCI0: dev=%d, active-chan=%d, size=%d, loops=%d",
- _deviceId, _numActiveChannels, _data.size(), _loops);
- break;
-
- case _SIMSG_BASEMSG_SET_LOOPS:
- _loops = msg._arg.i;
- break;
-
- case _SIMSG_BASEMSG_STOP: {
- songit_id_t sought_id = msg.ID;
-
- if (sought_id == ID)
- _channel.state = SI_STATE_FINISHED;
- break;
- }
-
- case _SIMSG_BASEMSG_SET_PLAYMASK: {
- int i;
- _deviceId = msg._arg.i;
-
- /* Set all but the rhytm channel mask bits */
- _channel.playmask &= ~(1 << MIDI_RHYTHM_CHANNEL);
-
- for (i = 0; i < MIDI_CHANNELS; i++)
- if (_data[2 + (i << 1)] & _deviceId
- && i != MIDI_RHYTHM_CHANNEL)
- _channel.playmask |= (1 << i);
- }
- break;
-
- case _SIMSG_BASEMSG_SET_RHYTHM:
- _channel.playmask &= ~(1 << MIDI_RHYTHM_CHANNEL);
- if (msg._arg.i)
- _channel.playmask |= (1 << MIDI_RHYTHM_CHANNEL);
- break;
-
- case _SIMSG_BASEMSG_SET_FADE: {
- fade_params_t *fp = (fade_params_t *) msg._arg.p;
- fade.action = fp->action;
- fade.final_volume = fp->final_volume;
- fade.ticks_per_step = fp->ticks_per_step;
- fade.step_size = fp->step_size;
- break;
- }
-
- default:
- return NULL;
- }
-
- return this;
- }
- return NULL;
-}
-
-int Sci0SongIterator::getTimepos() {
- return _channel.total_timepos;
-}
-
-Sci0SongIterator::Sci0SongIterator(byte *data, uint size, songit_id_t id)
- : BaseSongIterator(data, size, id) {
- channel_mask = 0xffff; // Allocate all channels by default
- _channel.state = SI_STATE_UNINITIALISED;
-
- for (int i = 0; i < MIDI_CHANNELS; i++)
- _polyphony[i] = data[1 + (i << 1)];
-
- init();
-}
-
-void Sci0SongIterator::init() {
- fade.action = FADE_ACTION_NONE;
- _resetflag = 0;
- _loops = 0;
- priority = 0;
-
- _ccc = 0; /* Reset cumulative cue counter */
- _numActiveChannels = 1;
- _channel.init(0, SCI0_MIDI_OFFSET, _data.size());
- _channel.resetSynthChannels();
-
- if (_data[0] == 2) /* Do we have an embedded PCM? */
- _channel.state = SI_STATE_PCM;
-}
-
-SongIterator *Sci0SongIterator::clone(int delta) {
- Sci0SongIterator *newit = new Sci0SongIterator(*this);
- return newit;
-}
-
-
-/***************************/
-/*-- SCI1 song iterators --*/
-/***************************/
-
-int Sci1SongIterator::initSample(const int offset) {
- Sci1Sample sample;
- int rate;
- int length;
- int begin;
- int end;
-
- CHECK_FOR_END_ABSOLUTE((uint)offset + 10);
- if (_data[offset + 1] != 0)
- warning("[iterator-1] In sample at offset 0x04x: Byte #1 is %02x instead of zero",
- _data[offset + 1]);
-
- rate = (int16)READ_LE_UINT16(_data.begin() + offset + 2);
- length = READ_LE_UINT16(_data.begin() + offset + 4);
- begin = (int16)READ_LE_UINT16(_data.begin() + offset + 6);
- end = (int16)READ_LE_UINT16(_data.begin() + offset + 8);
-
- CHECK_FOR_END_ABSOLUTE((uint)(offset + 10 + length));
-
- sample.delta = begin;
- sample.size = length;
- sample._data = _data.begin() + offset + 10;
-
-#ifdef DEBUG_VERBOSE
- fprintf(stderr, "[SAMPLE] %x/%x/%x/%x l=%x\n",
- offset + 10, begin, end, _data.size(), length);
-#endif
-
- sample.rate = rate;
-
- sample.announced = false;
-
- /* Insert into the sample list at the right spot, keeping it sorted by delta */
- Common::List<Sci1Sample>::iterator seeker = _samples.begin();
- while (seeker != _samples.end() && seeker->delta < begin)
- ++seeker;
- _samples.insert(seeker, sample);
-
- return 0; /* Everything's fine */
-}
-
-int Sci1SongIterator::initSong() {
- int last_time;
- uint offset = 0;
- _numChannels = 0;
- _samples.clear();
-// _deviceId = 0x0c;
-
- if (_data[offset] == 0xf0) {
- priority = _data[offset + 1];
-
- offset += 8;
- }
-
- while (_data[offset] != 0xff
- && _data[offset] != _deviceId) {
- offset++;
- CHECK_FOR_END_ABSOLUTE(offset + 1);
- while (_data[offset] != 0xff) {
- CHECK_FOR_END_ABSOLUTE(offset + 7);
- offset += 6;
- }
- offset++;
- }
-
- if (_data[offset] == 0xff) {
- warning("[iterator] Song does not support hardware 0x%02x", _deviceId);
- return 1;
- }
-
- offset++;
-
- while (_data[offset] != 0xff) { /* End of list? */
- uint track_offset;
- int end;
- offset += 2;
-
- CHECK_FOR_END_ABSOLUTE(offset + 4);
-
- track_offset = READ_LE_UINT16(_data.begin() + offset);
- end = READ_LE_UINT16(_data.begin() + offset + 2);
-
- CHECK_FOR_END_ABSOLUTE(track_offset - 1);
-
- if (_data[track_offset] == 0xfe) {
- if (initSample(track_offset))
- return 1; /* Error */
- } else {
- /* Regular MIDI channel */
- if (_numChannels >= MIDI_CHANNELS) {
- warning("[iterator] Song has more than %d channels, cutting them off",
- MIDI_CHANNELS);
- break; /* Scan for remaining samples */
- } else {
- int channel_nr = _data[track_offset] & 0xf;
- SongIteratorChannel &channel = _channels[_numChannels++];
-
- /*
- if (_data[track_offset] & 0xf0)
- printf("Channel %d has mapping bits %02x\n",
- channel_nr, _data[track_offset] & 0xf0);
- */
-
- // Add 2 to skip over header bytes */
- channel.init(channel_nr, track_offset + 2, track_offset + end);
- channel.resetSynthChannels();
-
- _polyphony[_numChannels - 1] = _data[channel.offset - 1] & 15;
-
- channel.playmask = ~0; /* Enable all */
- channel_mask |= (1 << channel_nr);
-
- CHECK_FOR_END_ABSOLUTE(offset + end);
- }
- }
- offset += 4;
- CHECK_FOR_END_ABSOLUTE(offset);
- }
-
- /* Now ensure that sample deltas are relative to the previous sample */
- last_time = 0;
- _numActiveChannels = _numChannels;
- _numLoopedChannels = 0;
-
- for (Common::List<Sci1Sample>::iterator seeker = _samples.begin();
- seeker != _samples.end(); ++seeker) {
- int prev_last_time = last_time;
- //printf("[iterator] Detected sample: %d Hz, %d bytes at time %d\n",
- // seeker->format.rate, seeker->size, seeker->delta);
- last_time = seeker->delta;
- seeker->delta -= prev_last_time;
- }
-
- return 0; /* Success */
-}
-
-int Sci1SongIterator::getSmallestDelta() const {
- int d = -1;
- for (int i = 0; i < _numChannels; i++)
- if (_channels[i].state == SI_STATE_COMMAND
- && (d == -1 || _channels[i].delay < d))
- d = _channels[i].delay;
-
- if (!_samples.empty() && _samples.begin()->delta < d)
- return _samples.begin()->delta;
- else
- return d;
-}
-
-void Sci1SongIterator::updateDelta(int delta) {
- if (!_samples.empty())
- _samples.begin()->delta -= delta;
-
- for (int i = 0; i < _numChannels; i++)
- if (_channels[i].state == SI_STATE_COMMAND)
- _channels[i].delay -= delta;
-}
-
-bool Sci1SongIterator::noDeltaTime() const {
- for (int i = 0; i < _numChannels; i++)
- if (_channels[i].state == SI_STATE_DELTA_TIME)
- return false;
- return true;
-}
-
-#define COMMAND_INDEX_NONE -1
-#define COMMAND_INDEX_PCM -2
-
-int Sci1SongIterator::getCommandIndex() const {
- /* Determine the channel # of the next active event, or -1 */
- int i;
- int base_delay = 0x7ffffff;
- int best_chan = COMMAND_INDEX_NONE;
-
- for (i = 0; i < _numChannels; i++)
- if ((_channels[i].state != SI_STATE_PENDING)
- && (_channels[i].state != SI_STATE_FINISHED)) {
-
- if ((_channels[i].state == SI_STATE_DELTA_TIME)
- && (_channels[i].delay == 0))
- return i;
- /* First, read all unknown delta times */
-
- if (_channels[i].delay < base_delay) {
- best_chan = i;
- base_delay = _channels[i].delay;
- }
- }
-
- if (!_samples.empty() && base_delay >= _samples.begin()->delta)
- return COMMAND_INDEX_PCM;
-
- return best_chan;
-}
-
-
-Audio::AudioStream *Sci1SongIterator::getAudioStream() {
- Common::List<Sci1Sample>::iterator sample = _samples.begin();
- if (sample != _samples.end() && sample->delta <= 0) {
- Audio::AudioStream *feed = makeStream(sample->_data, sample->size, sample->rate);
- _samples.erase(sample);
-
- return feed;
- } else
- return NULL;
-}
-
-int Sci1SongIterator::nextCommand(byte *buf, int *result) {
-
- if (!_initialised) {
- //printf("[iterator] DEBUG: Initialising for %d\n", _deviceId);
- _initialised = true;
- if (initSong())
- return SI_FINISHED;
- }
-
-
- if (_delayRemaining) {
- int delay = _delayRemaining;
- _delayRemaining = 0;
- return delay;
- }
-
- int retval = 0;
- do { /* All delays must be processed separately */
- int chan = getCommandIndex();
-
- if (chan == COMMAND_INDEX_NONE) {
- return SI_FINISHED;
- }
-
- if (chan == COMMAND_INDEX_PCM) {
-
- if (_samples.begin()->announced) {
- /* Already announced; let's discard it */
- Audio::AudioStream *feed = getAudioStream();
- delete feed;
- } else {
- int delay = _samples.begin()->delta;
-
- if (delay) {
- updateDelta(delay);
- return delay;
- }
- /* otherwise we're touching a PCM */
- _samples.begin()->announced = true;
- return SI_PCM;
- }
- } else { /* Not a PCM */
-
- retval = processMidi(buf, result,
- &(_channels[chan]),
- PARSE_FLAG_LOOPS_UNLIMITED);
-
- if (retval == SI_LOOP) {
- _numLoopedChannels++;
- _channels[chan].state = SI_STATE_PENDING;
- _channels[chan].delay = 0;
-
- if (_numLoopedChannels == _numActiveChannels) {
- int i;
-
- /* Everyone's ready: Let's loop */
- for (i = 0; i < _numChannels; i++)
- if (_channels[i].state == SI_STATE_PENDING)
- _channels[i].state = SI_STATE_DELTA_TIME;
-
- _numLoopedChannels = 0;
- return SI_LOOP;
- }
- } else if (retval == SI_FINISHED) {
-#ifdef DEBUG
- fprintf(stderr, "FINISHED some channel\n");
-#endif
- } else if (retval > 0) {
- int sd ;
- sd = getSmallestDelta();
-
- if (noDeltaTime() && sd) {
- /* No other channel is ready */
- updateDelta(sd);
-
- /* Only from here do we return delta times */
- return sd;
- }
- }
-
- } /* Not a PCM */
-
- } while (retval > 0);
-
- return retval;
-}
-
-SongIterator *Sci1SongIterator::handleMessage(Message msg) {
- if (msg._class == _SIMSG_BASE) { /* May extend this in the future */
- switch (msg._type) {
-
- case _SIMSG_BASEMSG_PRINT: {
- int playmask = 0;
- int i;
-
- for (i = 0; i < _numChannels; i++)
- playmask |= _channels[i].playmask;
-
- print_tabs_id(msg._arg.i, ID);
- debugC(2, kDebugLevelSound, "SCI1: chan-nr=%d, playmask=%04x",
- _numChannels, playmask);
- }
- break;
-
- case _SIMSG_BASEMSG_STOP: {
- songit_id_t sought_id = msg.ID;
- int i;
-
- if (sought_id == ID) {
- ID = 0;
-
- for (i = 0; i < _numChannels; i++)
- _channels[i].state = SI_STATE_FINISHED;
- }
- break;
- }
-
- case _SIMSG_BASEMSG_SET_PLAYMASK:
- if (msg.ID == ID) {
- channel_mask = 0;
-
- _deviceId = msg._arg.i;
-
- if (_initialised) {
- int i;
- int toffset = -1;
-
- for (i = 0; i < _numChannels; i++)
- if (_channels[i].state != SI_STATE_FINISHED
- && _channels[i].total_timepos > toffset) {
- toffset = _channels[i].total_timepos
- + _channels[i].timepos_increment
- - _channels[i].delay;
- }
-
- /* Find an active channel so that we can
- ** get the correct time offset */
-
- initSong();
-
- toffset -= _delayRemaining;
- _delayRemaining = 0;
-
- if (toffset > 0)
- return new_fast_forward_iterator(this, toffset);
- } else {
- initSong();
- _initialised = true;
- }
-
- break;
-
- }
-
- case _SIMSG_BASEMSG_SET_LOOPS:
- if (msg.ID == ID)
- _loops = (msg._arg.i > 32767) ? 99 : 0;
- /* 99 is arbitrary, but we can't use '1' because of
- ** the way we're testing in the decoding section. */
- break;
-
- case _SIMSG_BASEMSG_SET_HOLD:
- _hold = msg._arg.i;
- break;
- case _SIMSG_BASEMSG_SET_RHYTHM:
- /* Ignore */
- break;
-
- case _SIMSG_BASEMSG_SET_FADE: {
- fade_params_t *fp = (fade_params_t *) msg._arg.p;
- fade.action = fp->action;
- fade.final_volume = fp->final_volume;
- fade.ticks_per_step = fp->ticks_per_step;
- fade.step_size = fp->step_size;
- break;
- }
-
- default:
- warning("Unsupported command %d to SCI1 iterator", msg._type);
- }
- return this;
- }
- return NULL;
-}
-
-Sci1SongIterator::Sci1SongIterator(byte *data, uint size, songit_id_t id)
- : BaseSongIterator(data, size, id) {
- channel_mask = 0; // Defer channel allocation
-
- for (int i = 0; i < MIDI_CHANNELS; i++)
- _polyphony[i] = 0; // Unknown
-
- init();
-}
-
-void Sci1SongIterator::init() {
- fade.action = FADE_ACTION_NONE;
- _resetflag = 0;
- _loops = 0;
- priority = 0;
-
- _ccc = 0;
- _deviceId = 0x00; // Default to Sound Blaster/AdLib for purposes of cue computation
- _numChannels = 0;
- _initialised = false;
- _delayRemaining = 0;
- _loops = 0;
- _hold = 0;
- memset(_polyphony, 0, sizeof(_polyphony));
-}
-
-Sci1SongIterator::~Sci1SongIterator() {
-}
-
-
-SongIterator *Sci1SongIterator::clone(int delta) {
- Sci1SongIterator *newit = new Sci1SongIterator(*this);
- newit->_delayRemaining = delta;
- return newit;
-}
-
-int Sci1SongIterator::getTimepos() {
- int max = 0;
- int i;
-
- for (i = 0; i < _numChannels; i++)
- if (_channels[i].total_timepos > max)
- max = _channels[i].total_timepos;
-
- return max;
-}
-
-/**
- * A song iterator with the purpose of sending notes-off channel commands.
- */
-class CleanupSongIterator : public SongIterator {
-public:
- CleanupSongIterator(uint channels) {
- channel_mask = channels;
- ID = 17;
- }
-
- int nextCommand(byte *buf, int *result);
- Audio::AudioStream *getAudioStream() { return NULL; }
- SongIterator *handleMessage(Message msg);
- int getTimepos() { return 0; }
- SongIterator *clone(int delta) { return new CleanupSongIterator(*this); }
-};
-
-SongIterator *CleanupSongIterator::handleMessage(Message msg) {
- if (msg._class == _SIMSG_BASEMSG_PRINT && msg._type == _SIMSG_BASEMSG_PRINT) {
- print_tabs_id(msg._arg.i, ID);
- debugC(2, kDebugLevelSound, "CLEANUP");
- }
-
- return NULL;
-}
-
-int CleanupSongIterator::nextCommand(byte *buf, int *result) {
- /* Task: Return channel-notes-off for each channel */
- if (channel_mask) {
- int bs = sci_ffs(channel_mask) - 1;
-
- channel_mask &= ~(1 << bs);
- buf[0] = 0xb0 | bs; /* Controller */
- buf[1] = SCI_MIDI_CHANNEL_NOTES_OFF;
- buf[2] = 0; /* Hmm... */
- *result = 3;
- return 0;
- } else
- return SI_FINISHED;
-}
-
-/**********************/
-/*-- Timer iterator --*/
-/**********************/
-int TimerSongIterator::nextCommand(byte *buf, int *result) {
- if (_delta) {
- int d = _delta;
- _delta = 0;
- return d;
- }
- return SI_FINISHED;
-}
-
-SongIterator *new_timer_iterator(int delta) {
- return new TimerSongIterator(delta);
-}
-
-/**********************************/
-/*-- Fast-forward song iterator --*/
-/**********************************/
-
-int FastForwardSongIterator::nextCommand(byte *buf, int *result) {
- if (_delta <= 0)
- return SI_MORPH; /* Did our duty */
-
- while (1) {
- int rv = _delegate->nextCommand(buf, result);
-
- if (rv > 0) {
- /* Subtract from the delta we want to wait */
- _delta -= rv;
-
- /* Done */
- if (_delta < 0)
- return -_delta;
- }
-
- if (rv <= 0)
- return rv;
- }
-}
-
-Audio::AudioStream *FastForwardSongIterator::getAudioStream() {
- return _delegate->getAudioStream();
-}
-
-SongIterator *FastForwardSongIterator::handleMessage(Message msg) {
- if (msg._class == _SIMSG_PLASTICWRAP) {
- assert(msg._type == _SIMSG_PLASTICWRAP_ACK_MORPH);
-
- if (_delta <= 0) {
- SongIterator *it = _delegate;
- delete this;
- return it;
- }
-
- warning("[ff-iterator] Morphing without need");
- return this;
- }
-
- if (msg._class == _SIMSG_BASE && msg._type == _SIMSG_BASEMSG_PRINT) {
- print_tabs_id(msg._arg.i, ID);
- debugC(2, kDebugLevelSound, "FASTFORWARD:");
- msg._arg.i++;
- }
-
- // And continue with the delegate
- songit_handle_message(&_delegate, msg);
-
- return NULL;
-}
-
-
-int FastForwardSongIterator::getTimepos() {
- return _delegate->getTimepos();
-}
-
-FastForwardSongIterator::FastForwardSongIterator(SongIterator *capsit, int delta)
- : _delegate(capsit), _delta(delta) {
-
- channel_mask = capsit->channel_mask;
-}
-
-SongIterator *FastForwardSongIterator::clone(int delta) {
- FastForwardSongIterator *newit = new FastForwardSongIterator(*this);
- newit->_delegate = _delegate->clone(delta);
- return newit;
-}
-
-SongIterator *new_fast_forward_iterator(SongIterator *capsit, int delta) {
- if (capsit == NULL)
- return NULL;
-
- FastForwardSongIterator *it = new FastForwardSongIterator(capsit, delta);
- return it;
-}
-
-
-/********************/
-/*-- Tee iterator --*/
-/********************/
-
-
-static void song_iterator_add_death_listener(SongIterator *it, TeeSongIterator *client) {
- for (int i = 0; i < SONGIT_MAX_LISTENERS; ++i) {
- if (it->_deathListeners[i] == 0) {
- it->_deathListeners[i] = client;
- return;
- }
- }
- error("FATAL: Too many death listeners for song iterator");
-}
-
-static void song_iterator_remove_death_listener(SongIterator *it, TeeSongIterator *client) {
- for (int i = 0; i < SONGIT_MAX_LISTENERS; ++i) {
- if (it->_deathListeners[i] == client) {
- it->_deathListeners[i] = 0;
- return;
- }
- }
-}
-
-static void song_iterator_transfer_death_listeners(SongIterator *it, SongIterator *it_from) {
- for (int i = 0; i < SONGIT_MAX_LISTENERS; ++i) {
- if (it_from->_deathListeners[i])
- song_iterator_add_death_listener(it, it_from->_deathListeners[i]);
- it_from->_deathListeners[i] = 0;
- }
-}
-
-static void songit_tee_death_notification(TeeSongIterator *self, SongIterator *corpse) {
- if (corpse == self->_children[TEE_LEFT].it) {
- self->_status &= ~TEE_LEFT_ACTIVE;
- self->_children[TEE_LEFT].it = NULL;
- } else if (corpse == self->_children[TEE_RIGHT].it) {
- self->_status &= ~TEE_RIGHT_ACTIVE;
- self->_children[TEE_RIGHT].it = NULL;
- } else {
- error("songit_tee_death_notification() failed: Breakpoint in %s, line %d", __FILE__, __LINE__);
- }
-}
-
-TeeSongIterator::TeeSongIterator(SongIterator *left, SongIterator *right) {
- int i;
- int firstfree = 1; /* First free channel */
- int incomplete_map = 0;
-
- _readyToMorph = false;
- _status = TEE_LEFT_ACTIVE | TEE_RIGHT_ACTIVE;
-
- _children[TEE_LEFT].it = left;
- _children[TEE_RIGHT].it = right;
-
- /* Default to lhs channels */
- channel_mask = left->channel_mask;
- for (i = 0; i < 16; i++)
- if (channel_mask & (1 << i) & right->channel_mask
- && (i != MIDI_RHYTHM_CHANNEL) /* Share rhythm */) { /*conflict*/
- while ((firstfree == MIDI_RHYTHM_CHANNEL)
- /* Either if it's the rhythm channel or if it's taken */
- || (firstfree < MIDI_CHANNELS
- && ((1 << firstfree) & channel_mask)))
- ++firstfree;
-
- if (firstfree == MIDI_CHANNELS) {
- incomplete_map = 1;
- //warning("[songit-tee <%08lx,%08lx>] Could not remap right channel #%d: Out of channels",
- // left->ID, right->ID, i);
- } else {
- _children[TEE_RIGHT].it->channel_remap[i] = firstfree;
-
- channel_mask |= (1 << firstfree);
- }
- }
-#ifdef DEBUG_TEE_ITERATOR
- if (incomplete_map) {
- int c;
- fprintf(stderr, "[songit-tee <%08lx,%08lx>] Channels:"
- " %04x <- %04x | %04x\n",
- left->ID, right->ID,
- channel_mask,
- left->channel_mask, right->channel_mask);
- for (c = 0 ; c < 2; c++)
- for (i = 0 ; i < 16; i++)
- fprintf(stderr, " map [%d][%d] -> %d\n",
- c, i, _children[c].it->channel_remap[i]);
- }
-#endif
-
-
- song_iterator_add_death_listener(left, this);
- song_iterator_add_death_listener(right, this);
-}
-
-TeeSongIterator::~TeeSongIterator() {
- // When we die, remove any listeners from our children
- if (_children[TEE_LEFT].it) {
- song_iterator_remove_death_listener(_children[TEE_LEFT].it, this);
- }
-
- if (_children[TEE_RIGHT].it) {
- song_iterator_remove_death_listener(_children[TEE_RIGHT].it, this);
- }
-}
-
-
-int TeeSongIterator::nextCommand(byte *buf, int *result) {
- static const int ready_masks[2] = {TEE_LEFT_READY, TEE_RIGHT_READY};
- static const int active_masks[2] = {TEE_LEFT_ACTIVE, TEE_RIGHT_ACTIVE};
- static const int pcm_masks[2] = {TEE_LEFT_PCM, TEE_RIGHT_PCM};
- int i;
- int retid;
-
-#ifdef DEBUG_TEE_ITERATOR
- fprintf(stderr, "[Tee] %02x\n", _status);
-#endif
-
- if (!(_status & (TEE_LEFT_ACTIVE | TEE_RIGHT_ACTIVE)))
- /* None is active? */
- return SI_FINISHED;
-
- if (_readyToMorph)
- return SI_MORPH;
-
- if ((_status & (TEE_LEFT_ACTIVE | TEE_RIGHT_ACTIVE))
- != (TEE_LEFT_ACTIVE | TEE_RIGHT_ACTIVE)) {
- /* Not all are is active? */
- int which = 0;
-#ifdef DEBUG_TEE_ITERATOR
- fprintf(stderr, "\tRequesting transformation...\n");
-#endif
- if (_status & TEE_LEFT_ACTIVE)
- which = TEE_LEFT;
- else if (_status & TEE_RIGHT_ACTIVE)
- which = TEE_RIGHT;
- memcpy(buf, _children[which].buf, sizeof(buf));
- *result = _children[which].result;
- _readyToMorph = true;
- return _children[which].retval;
- }
-
- /* First, check for unreported PCMs */
- for (i = TEE_LEFT; i <= TEE_RIGHT; i++)
- if ((_status & (ready_masks[i] | pcm_masks[i]))
- == (ready_masks[i] | pcm_masks[i])) {
- _status &= ~ready_masks[i];
- return SI_PCM;
- }
-
- for (i = TEE_LEFT; i <= TEE_RIGHT; i++)
- if (!(_status & ready_masks[i])) {
-
- /* Buffers aren't ready yet */
- _children[i].retval =
- songit_next(&(_children[i].it),
- _children[i].buf,
- &(_children[i].result),
- IT_READER_MASK_ALL
- | IT_READER_MAY_FREE
- | IT_READER_MAY_CLEAN);
-
- _status |= ready_masks[i];
-#ifdef DEBUG_TEE_ITERATOR
- fprintf(stderr, "\t Must check %d: %d\n", i, _children[i].retval);
-#endif
-
- if (_children[i].retval == SI_ABSOLUTE_CUE ||
- _children[i].retval == SI_RELATIVE_CUE)
- return _children[i].retval;
- if (_children[i].retval == SI_FINISHED) {
- _status &= ~active_masks[i];
- /* Recurse to complete */
-#ifdef DEBUG_TEE_ITERATOR
- fprintf(stderr, "\t Child %d signalled completion, recursing w/ status %02x\n", i, _status);
-#endif
- return nextCommand(buf, result);
- } else if (_children[i].retval == SI_PCM) {
- _status |= pcm_masks[i];
- _status &= ~ready_masks[i];
- return SI_PCM;
- }
- }
-
-
- /* We've already handled PCM, MORPH and FINISHED, CUEs & LOOP remain */
-
- retid = TEE_LEFT;
- if ((_children[TEE_LEFT].retval > 0)
- /* Asked to delay */
- && (_children[TEE_RIGHT].retval <= _children[TEE_LEFT].retval))
- /* Is not delaying or not delaying as much */
- retid = TEE_RIGHT;
-
-#ifdef DEBUG_TEE_ITERATOR
- fprintf(stderr, "\tl:%d / r:%d / chose %d\n",
- _children[TEE_LEFT].retval, _children[TEE_RIGHT].retval, retid);
-#endif
-
- /* Adjust delta times */
- if (_children[retid].retval > 0
- && _children[1-retid].retval > 0) {
- if (_children[1-retid].retval
- == _children[retid].retval)
- /* If both _children wait the same amount of time,
- ** we have to re-fetch commands from both */
- _status &= ~ready_masks[1-retid];
- else
- /* If they don't, we can/must re-use the other
- ** child's delay time */
- _children[1-retid].retval
- -= _children[retid].retval;
- }
-
- _status &= ~ready_masks[retid];
- memcpy(buf, _children[retid].buf, sizeof(buf));
- *result = _children[retid].result;
-
- return _children[retid].retval;
-}
-
-Audio::AudioStream *TeeSongIterator::getAudioStream() {
- static const int pcm_masks[2] = {TEE_LEFT_PCM, TEE_RIGHT_PCM};
- int i;
-
- for (i = TEE_LEFT; i <= TEE_RIGHT; i++)
- if (_status & pcm_masks[i]) {
- _status &= ~pcm_masks[i];
- return _children[i].it->getAudioStream();
- }
-
- return NULL; // No iterator
-}
-
-SongIterator *TeeSongIterator::handleMessage(Message msg) {
- if (msg._class == _SIMSG_PLASTICWRAP) {
- assert(msg._type == _SIMSG_PLASTICWRAP_ACK_MORPH);
-
- SongIterator *old_it;
- if (!(_status & (TEE_LEFT_ACTIVE | TEE_RIGHT_ACTIVE))) {
- delete this;
- return NULL;
- } else if (!(_status & TEE_LEFT_ACTIVE)) {
- delete _children[TEE_LEFT].it;
- _children[TEE_LEFT].it = 0;
- old_it = _children[TEE_RIGHT].it;
- song_iterator_remove_death_listener(old_it, this);
- song_iterator_transfer_death_listeners(old_it, this);
- delete this;
- return old_it;
- } else if (!(_status & TEE_RIGHT_ACTIVE)) {
- delete _children[TEE_RIGHT].it;
- _children[TEE_RIGHT].it = 0;
- old_it = _children[TEE_LEFT].it;
- song_iterator_remove_death_listener(old_it, this);
- song_iterator_transfer_death_listeners(old_it, this);
- delete this;
- return old_it;
- }
-
- warning("[tee-iterator] Morphing without need");
- return this;
- }
-
- if (msg._class == _SIMSG_BASE && msg._type == _SIMSG_BASEMSG_PRINT) {
- print_tabs_id(msg._arg.i, ID);
- debugC(2, kDebugLevelSound, "TEE:");
- msg._arg.i++;
- }
-
- // And continue with the children
- if (_children[TEE_LEFT].it)
- songit_handle_message(&(_children[TEE_LEFT].it), msg);
- if (_children[TEE_RIGHT].it)
- songit_handle_message(&(_children[TEE_RIGHT].it), msg);
-
- return NULL;
-}
-
-void TeeSongIterator::init() {
- _status = TEE_LEFT_ACTIVE | TEE_RIGHT_ACTIVE;
- _children[TEE_LEFT].it->init();
- _children[TEE_RIGHT].it->init();
-}
-
-SongIterator *TeeSongIterator::clone(int delta) {
- TeeSongIterator *newit = new TeeSongIterator(*this);
-
- if (_children[TEE_LEFT].it)
- newit->_children[TEE_LEFT].it = _children[TEE_LEFT].it->clone(delta);
- if (_children[TEE_RIGHT].it)
- newit->_children[TEE_RIGHT].it = _children[TEE_RIGHT].it->clone(delta);
-
- return newit;
-}
-
-
-/*************************************/
-/*-- General purpose functionality --*/
-/*************************************/
-
-int songit_next(SongIterator **it, byte *buf, int *result, int mask) {
- int retval;
-
- if (!*it)
- return SI_FINISHED;
-
- do {
- retval = (*it)->nextCommand(buf, result);
- if (retval == SI_MORPH) {
- debugC(2, kDebugLevelSound, " Morphing %p (stored at %p)", (void *)*it, (void *)it);
- if (!SIMSG_SEND((*it), SIMSG_ACK_MORPH)) {
- error("SI_MORPH failed. Breakpoint in %s, line %d", __FILE__, __LINE__);
- } else
- debugC(2, kDebugLevelSound, "SI_MORPH successful");
- }
-
- if (retval == SI_FINISHED)
- debugC(2, kDebugLevelSound, "[song-iterator] Song finished. mask = %04x, cm=%04x",
- mask, (*it)->channel_mask);
- if (retval == SI_FINISHED
- && (mask & IT_READER_MAY_CLEAN)
- && (*it)->channel_mask) { /* This last test will fail
- ** with a terminated
- ** cleanup iterator */
- int channel_mask = (*it)->channel_mask;
-
- SongIterator *old_it = *it;
- *it = new CleanupSongIterator(channel_mask);
- for(uint i = 0; i < MIDI_CHANNELS; i++)
- (*it)->channel_remap[i] = old_it->channel_remap[i];
- song_iterator_transfer_death_listeners(*it, old_it);
- if (mask & IT_READER_MAY_FREE)
- delete old_it;
- retval = -9999; /* Continue */
- }
- } while (!( /* Until one of the following holds */
- (retval > 0 && (mask & IT_READER_MASK_DELAY))
- || (retval == 0 && (mask & IT_READER_MASK_MIDI))
- || (retval == SI_LOOP && (mask & IT_READER_MASK_LOOP))
- || (retval == SI_ABSOLUTE_CUE &&
- (mask & IT_READER_MASK_CUE))
- || (retval == SI_RELATIVE_CUE &&
- (mask & IT_READER_MASK_CUE))
- || (retval == SI_PCM && (mask & IT_READER_MASK_PCM))
- || (retval == SI_FINISHED)
- ));
-
- if (retval == SI_FINISHED && (mask & IT_READER_MAY_FREE)) {
- delete *it;
- *it = NULL;
- }
-
- return retval;
-}
-
-SongIterator::SongIterator() {
- ID = 0;
- channel_mask = 0;
- fade.action = FADE_ACTION_NONE;
- priority = 0;
- memset(_deathListeners, 0, sizeof(_deathListeners));
-
- // By default, don't remap
- for (uint i = 0; i < 16; i++)
- channel_remap[i] = i;
-}
-
-SongIterator::SongIterator(const SongIterator &si) {
- ID = si.ID;
- channel_mask = si.channel_mask;
- fade = si.fade;
- priority = si.priority;
- memset(_deathListeners, 0, sizeof(_deathListeners));
-
- for (uint i = 0; i < 16; i++)
- channel_remap[i] = si.channel_remap[i];
-}
-
-
-SongIterator::~SongIterator() {
- for (int i = 0; i < SONGIT_MAX_LISTENERS; ++i)
- if (_deathListeners[i])
- songit_tee_death_notification(_deathListeners[i], this);
-}
-
-SongIterator *songit_new(byte *data, uint size, SongIteratorType type, songit_id_t id) {
- BaseSongIterator *it;
-
- if (!data || size < 22) {
- warning("Attempt to instantiate song iterator for null song data");
- return NULL;
- }
-
-
- switch (type) {
- case SCI_SONG_ITERATOR_TYPE_SCI0:
- it = new Sci0SongIterator(data, size, id);
- break;
-
- case SCI_SONG_ITERATOR_TYPE_SCI1:
- it = new Sci1SongIterator(data, size, id);
- break;
-
- default:
- /**-- Invalid/unsupported sound resources --**/
- warning("Attempt to instantiate invalid/unknown song iterator type %d", type);
- return NULL;
- }
-
- return it;
-}
-
-int songit_handle_message(SongIterator **it_reg_p, SongIterator::Message msg) {
- SongIterator *it = *it_reg_p;
- SongIterator *newit;
-
- newit = it->handleMessage(msg);
-
- if (!newit)
- return 0; /* Couldn't handle */
-
- *it_reg_p = newit; /* Might have self-morphed */
- return 1;
-}
-
-SongIterator *sfx_iterator_combine(SongIterator *it1, SongIterator *it2) {
- if (it1 == NULL)
- return it2;
- if (it2 == NULL)
- return it1;
-
- /* Both are non-NULL: */
- return new TeeSongIterator(it1, it2);
-}
-
-} // End of namespace Sci
-
-#endif // USE_OLD_MUSIC_FUNCTIONS
diff --git a/engines/sci/sound/iterator/iterator.h b/engines/sci/sound/iterator/iterator.h
deleted file mode 100644
index e5c8f50702..0000000000
--- a/engines/sci/sound/iterator/iterator.h
+++ /dev/null
@@ -1,326 +0,0 @@
-/* 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$
- *
- */
-
-/* Song iterator declarations */
-
-#ifndef SCI_SFX_SFX_ITERATOR_H
-#define SCI_SFX_SFX_ITERATOR_H
-
-#include "sci/sci.h" // for USE_OLD_MUSIC_FUNCTIONS
-
-#ifdef USE_OLD_MUSIC_FUNCTIONS
-#include "sci/sound/drivers/mididriver.h"
-
-namespace Audio {
- class AudioStream;
-}
-
-namespace Sci {
-
-enum SongIteratorStatus {
- SI_FINISHED = -1, /**< Song finished playing */
- SI_LOOP = -2, /**< Song just looped */
- SI_ABSOLUTE_CUE = -3, /**< Found a song cue (absolute) */
- SI_RELATIVE_CUE = -4, /**< Found a song cue (relative) */
- SI_PCM = -5, /**< Found a PCM */
- SI_IGNORE = -6, /**< This event got edited out by the remapper */
- SI_MORPH = -255 /**< Song iterator requested self-morph. */
-};
-
-#define FADE_ACTION_NONE 0
-#define FADE_ACTION_FADE_AND_STOP 1
-#define FADE_ACTION_FADE_AND_CONT 2
-
-struct fade_params_t {
- int ticks_per_step;
- int final_volume;
- int step_size;
- int action;
-};
-
-/* Helper defs for messages */
-enum {
- _SIMSG_BASE, /* Any base decoder */
- _SIMSG_PLASTICWRAP /* Any "Plastic" (discardable) wrapper decoder */
-};
-
-/* Base messages */
-enum {
- _SIMSG_BASEMSG_SET_LOOPS, /* Set loops */
- _SIMSG_BASEMSG_SET_PLAYMASK, /* Set the current playmask for filtering */
- _SIMSG_BASEMSG_SET_RHYTHM, /* Activate/deactivate rhythm channel */
- _SIMSG_BASEMSG_ACK_MORPH, /* Acknowledge self-morph */
- _SIMSG_BASEMSG_STOP, /* Stop iterator */
- _SIMSG_BASEMSG_PRINT, /* Print self to stderr, after printing param1 tabs */
- _SIMSG_BASEMSG_SET_HOLD, /* Set value of hold parameter to expect */
- _SIMSG_BASEMSG_SET_FADE /* Set fade parameters */
-};
-
-/* "Plastic" (discardable) wrapper messages */
-enum {
- _SIMSG_PLASTICWRAP_ACK_MORPH = _SIMSG_BASEMSG_ACK_MORPH /* Acknowledge self-morph */
-};
-
-/* Messages */
-#define SIMSG_SET_LOOPS(x) _SIMSG_BASE,_SIMSG_BASEMSG_SET_LOOPS,(x)
-#define SIMSG_SET_PLAYMASK(x) _SIMSG_BASE,_SIMSG_BASEMSG_SET_PLAYMASK,(x)
-#define SIMSG_SET_RHYTHM(x) _SIMSG_BASE,_SIMSG_BASEMSG_SET_RHYTHM,(x)
-#define SIMSG_ACK_MORPH _SIMSG_PLASTICWRAP,_SIMSG_PLASTICWRAP_ACK_MORPH,0
-#define SIMSG_STOP _SIMSG_BASE,_SIMSG_BASEMSG_STOP,0
-#define SIMSG_PRINT(indentation) _SIMSG_BASE,_SIMSG_BASEMSG_PRINT,(indentation)
-#define SIMSG_SET_HOLD(x) _SIMSG_BASE,_SIMSG_BASEMSG_SET_HOLD,(x)
-
-/* Message transmission macro: Takes song reference, message reference */
-#define SIMSG_SEND(o, m) songit_handle_message(&(o), SongIterator::Message((o)->ID, m))
-#define SIMSG_SEND_FADE(o, m) songit_handle_message(&(o), SongIterator::Message((o)->ID, _SIMSG_BASE, _SIMSG_BASEMSG_SET_FADE, m))
-
-typedef unsigned long songit_id_t;
-
-
-#define SONGIT_MAX_LISTENERS 2
-
-class TeeSongIterator;
-
-class SongIterator {
-public:
- struct Message {
- songit_id_t ID;
- uint _class; /* Type of iterator supposed to receive this */
- uint _type;
- union {
- uint i;
- void *p;
- } _arg;
-
- Message() : ID(0), _class(0xFFFF), _type(0xFFFF) {}
-
- /**
- * Create a song iterator message.
- *
- * @param id: song ID the message is targeted to
- * @param recipient_class: Message recipient class
- * @param type message type
- * @param a argument
- *
- * @note You should only use this with the SIMSG_* macros
- */
- Message(songit_id_t id, int recipient_class, int type, int a)
- : ID(id), _class(recipient_class), _type(type) {
- _arg.i = a;
- }
-
- /**
- * Create a song iterator message, wherein the first parameter is a pointer.
- *
- * @param id: song ID the message is targeted to
- * @param recipient_class: Message recipient class
- * @param type message type
- * @param a argument
- *
- * @note You should only use this with the SIMSG_* macros
- */
- Message(songit_id_t id, int recipient_class, int type, void *a)
- : ID(id), _class(recipient_class), _type(type) {
- _arg.p = a;
- }
- };
-
-public:
- songit_id_t ID;
- uint16 channel_mask; /* Bitmask of all channels this iterator will use */
- fade_params_t fade;
- int priority;
-
- /* Death listeners */
- /* These are not reset during initialisation */
- TeeSongIterator *_deathListeners[SONGIT_MAX_LISTENERS];
-
- /* See songit_* for the constructor and non-virtual member functions */
-
- byte channel_remap[MIDI_CHANNELS]; ///< Remapping for channels
-
-public:
- SongIterator();
- SongIterator(const SongIterator &);
- virtual ~SongIterator();
-
- /**
- * Resets/initializes the sound iterator.
- */
- virtual void init() {}
-
- /**
- * Reads the next MIDI operation _or_ delta time.
- * @param buf The buffer to write to (needs to be able to store at least 4 bytes)
- * @param result Number of bytes written to the buffer
- * (equals the number of bytes that need to be passed
- * to the lower layers) for 0, the cue value for SI_CUE,
- * or the number of loops remaining for SI_LOOP.
- * @return zero if a MIDI operation was written, SI_FINISHED
- * if the song has finished playing, SI_LOOP if looping
- * (after updating the loop variable), SI_CUE if we found
- * a cue, SI_PCM if a PCM was found, or the number of ticks
- * to wait before this function should be called next.
- *
- * @note If SI_PCM is returned, get_pcm() may be used to retrieve the associated
- * PCM, but this must be done before any subsequent calls to next().
- *
- * @todo The actual buffer size should either be specified or passed in, so that
- * we can detect buffer overruns.
- */
- virtual int nextCommand(byte *buf, int *result) = 0;
-
- /**
- Checks for the presence of a pcm sample.
- * @return NULL if no PCM data was found, an AudioStream otherwise.
- */
- virtual Audio::AudioStream *getAudioStream() = 0;
-
- /**
- * Handles a message to the song iterator.
- * @param msg the message to handle
- * @return NULL if the message was not understood,
- * this if the message could be handled, or a new song iterator
- * if the current iterator had to be morphed (but the message could
- * still be handled)
- *
- * @note This function is not supposed to be called directly; use
- * songit_handle_message() instead. It should not recurse, since songit_handle_message()
- * takes care of that and makes sure that its delegate received the message (and
- * was morphed) before self.
- */
- virtual SongIterator *handleMessage(Message msg) = 0;
-
- /**
- * Gets the song position to store in a savegame.
- */
- virtual int getTimepos() = 0;
-
- /**
- * Clone this song iterator.
- * @param delta number of ticks that still need to elapse until the
- * next item should be read from the song iterator
- */
- virtual SongIterator *clone(int delta) = 0;
-
-
-private:
- // Make the assignment operator unreachable, just in case...
- SongIterator& operator=(const SongIterator&);
-};
-
-
-/********************************/
-/*-- Song iterator operations --*/
-/********************************/
-
-enum SongIteratorType {
- SCI_SONG_ITERATOR_TYPE_SCI0 = 0,
- SCI_SONG_ITERATOR_TYPE_SCI1 = 1
-};
-
-#define IT_READER_MASK_MIDI (1 << 0)
-#define IT_READER_MASK_DELAY (1 << 1)
-#define IT_READER_MASK_LOOP (1 << 2)
-#define IT_READER_MASK_CUE (1 << 3)
-#define IT_READER_MASK_PCM (1 << 4)
-#define IT_READER_MAY_FREE (1 << 10) /* Free SI_FINISHED iterators */
-#define IT_READER_MAY_CLEAN (1 << 11)
-/* MAY_CLEAN: May instantiate cleanup iterators
-** (use for players; this closes open channels at the end of a song) */
-
-#define IT_READER_MASK_ALL ( IT_READER_MASK_MIDI \
- | IT_READER_MASK_DELAY \
- | IT_READER_MASK_LOOP \
- | IT_READER_MASK_CUE \
- | IT_READER_MASK_PCM )
-
-/* Convenience wrapper around it->next
-** Parameters: (SongIterator **it) Reference to the iterator to access
-** (byte *) buf: The buffer to write to (needs to be able to
-** store at least 4 bytes)
-** (int) mask: IT_READER_MASK options specifying the events to
-** listen for
-** Returns : (int) zero if a MIDI operation was written, SI_FINISHED
-** if the song has finished playing, SI_LOOP if looping
-** (after updating the loop variable), SI_CUE if we found
-** a cue, SI_PCM if a PCM was found, or the number of ticks
-** to wait before this function should be called next.
-** (int) *result: Number of bytes written to the buffer
-** (equals the number of bytes that need to be passed
-** to the lower layers) for 0, the cue value for SI_CUE,
-** or the number of loops remaining for SI_LOOP.
-*/
-int songit_next(SongIterator **it, byte *buf, int *result, int mask);
-
-/* Constructs a new song iterator object
-** Parameters: (byte *) data: The song data to iterate over
-** (uint) size: Number of bytes in the song
-** (int) type: One of the SCI_SONG_ITERATOR_TYPEs
-** (songit_id_t) id: An ID for addressing the song iterator
-** Returns : (SongIterator *) A newly allocated but uninitialized song
-** iterator, or NULL if 'type' was invalid or unsupported
-*/
-SongIterator *songit_new(byte *data, uint size, SongIteratorType type, songit_id_t id);
-
-/* Constructs a new song timer iterator object
-** Parameters: (int) delta: The delta after which to fire SI_FINISHED
-** Returns : (SongIterator *) A newly allocated but uninitialized song
-** iterator
-*/
-SongIterator *new_timer_iterator(int delta);
-
-/* Handles a message to the song iterator
-** Parameters: (SongIterator **): A reference to the variable storing the song iterator
-** Returns : (int) Non-zero if the message was understood
-** The song iterator may polymorph as result of msg, so a writeable reference is required.
-*/
-int songit_handle_message(SongIterator **it_reg, SongIterator::Message msg);
-
-
-/* Creates a new song iterator which fast-forwards
-** Parameters: (SongIterator *) it: The iterator to wrap
-** (int) delta: The number of ticks to skip
-** Returns : (SongIterator) A newly created song iterator
-** which skips all delta times
-** until 'delta' has been used up
-*/
-SongIterator *new_fast_forward_iterator(SongIterator *it, int delta);
-
-/* Combines two song iterators into one
-** Parameters: (sfx_iterator_t *) it1: One of the two iterators, or NULL
-** (sfx_iterator_t *) it2: The other iterator, or NULL
-** Returns : (sfx_iterator_t *) A combined iterator
-** If a combined iterator is returned, it will be flagged to be allowed to
-** dispose of 'it1' and 'it2', where applicable. This means that this
-** call should be used by song players, but not by the core sound system
-*/
-SongIterator *sfx_iterator_combine(SongIterator *it1, SongIterator *it2);
-
-} // End of namespace Sci
-
-#endif // USE_OLD_MUSIC_FUNCTIONS
-
-#endif // SCI_SFX_SFX_ITERATOR_H
diff --git a/engines/sci/sound/iterator/iterator_internal.h b/engines/sci/sound/iterator/iterator_internal.h
deleted file mode 100644
index 5a0f0d3ec9..0000000000
--- a/engines/sci/sound/iterator/iterator_internal.h
+++ /dev/null
@@ -1,276 +0,0 @@
-/* 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 SCI_SFX_SFX_ITERATOR_INTERNAL
-#define SCI_SFX_SFX_ITERATOR_INTERNAL
-
-#include "sci/sci.h" // for USE_OLD_MUSIC_FUNCTIONS
-
-#ifdef USE_OLD_MUSIC_FUNCTIONS
-#include "sci/sound/iterator/iterator.h"
-#include "sci/sound/drivers/mididriver.h"
-
-#include "common/array.h"
-#include "common/list.h"
-
-namespace Sci {
-
-/* Iterator types */
-
-enum {
- SI_STATE_UNINITIALISED = -1,
- SI_STATE_DELTA_TIME = 0, ///< Now at a delta time
- SI_STATE_COMMAND = 1, ///< Now at a MIDI operation
- SI_STATE_PENDING = 2, ///< Pending for loop
- SI_STATE_FINISHED = 3, ///< End of song
- SI_STATE_PCM = 4, ///< Should report a PCM next (-> DELTA_TIME)
- SI_STATE_PCM_MAGIC_DELTA = 5 ///< Should report a ``magic'' one tick delta time next (goes on to FINISHED)
-};
-
-struct SongIteratorChannel {
-
- int state; ///< State of this song iterator channel
- int offset; ///< Offset into the data chunk */
- int end; ///< Last allowed byte in track */
- int id; ///< Some channel ID */
-
- /**
- * Number of ticks before the specified channel is next used, or
- * CHANNEL_DELAY_MISSING to indicate that the delay has not yet
- * been read.
- */
- int delay;
-
- /* Two additional offsets for recovering: */
- int loop_offset;
- int initial_offset;
-
- int playmask; ///< Active playmask (MIDI channels to play in here) */
- int loop_timepos; ///< Total delay for this channel's loop marker */
- int total_timepos; ///< Number of ticks since the beginning, ignoring loops */
- int timepos_increment; ///< Number of ticks until the next command (to add) */
-
- byte last_cmd; ///< Last operation executed, for running status */
-
-public:
- void init(int id, int offset, int end);
- void resetSynthChannels();
-};
-
-class BaseSongIterator : public SongIterator {
-public:
- int _polyphony[MIDI_CHANNELS]; ///< # of simultaneous notes on each
-
- int _ccc; ///< Cumulative cue counter, for those who need it
- byte _resetflag; ///< for 0x4C -- on DoSound StopSound, do we return to start?
- int _deviceId; ///< ID of the device we generating events for
- int _numActiveChannels; ///< Number of active channels
- Common::Array<byte> _data; ///< Song data
-
- int _loops; ///< Number of loops remaining
-
-public:
- BaseSongIterator(byte *data, uint size, songit_id_t id);
-
-protected:
- int parseMidiCommand(byte *buf, int *result, SongIteratorChannel *channel, int flags);
- int processMidi(byte *buf, int *result, SongIteratorChannel *channel, int flags);
-};
-
-/********************************/
-/*--------- SCI 0 --------------*/
-/********************************/
-
-class Sci0SongIterator : public BaseSongIterator {
-public:
- SongIteratorChannel _channel;
-
-public:
- Sci0SongIterator(byte *data, uint size, songit_id_t id);
-
- int nextCommand(byte *buf, int *result);
- Audio::AudioStream *getAudioStream();
- SongIterator *handleMessage(Message msg);
- void init();
- int getTimepos();
- SongIterator *clone(int delta);
-};
-
-
-/********************************/
-/*--------- SCI 1 --------------*/
-/********************************/
-
-
-struct Sci1Sample {
- /**
- * Time left-- initially, this is 'Sample point 1'.
- * After initialisation, it is 'sample point 1 minus the sample
- * point of the previous sample'
- */
- int delta;
- int size;
- bool announced; /* Announced for download (SI_PCM) */
- int rate;
- byte *_data;
-};
-
-class Sci1SongIterator : public BaseSongIterator {
-public:
- SongIteratorChannel _channels[MIDI_CHANNELS];
-
- /* Invariant: Whenever channels[i].delay == CHANNEL_DELAY_MISSING,
- ** channel_offset[i] points to a delta time object. */
-
- bool _initialised; /**!< Whether the MIDI channel setup has been initialised */
- int _numChannels; /**!< Number of channels actually used */
- Common::List<Sci1Sample> _samples;
- int _numLoopedChannels; /**!< Number of channels that are ready to loop */
-
- int _delayRemaining; /**!< Number of ticks that haven't been polled yet */
- int _hold;
-
-public:
- Sci1SongIterator(byte *data, uint size, songit_id_t id);
- ~Sci1SongIterator();
-
- int nextCommand(byte *buf, int *result);
- Audio::AudioStream *getAudioStream();
- SongIterator *handleMessage(Message msg);
- void init();
- int getTimepos();
- SongIterator *clone(int delta);
-
-private:
- int initSample(const int offset);
- int initSong();
-
- int getSmallestDelta() const;
-
- void updateDelta(int delta);
-
- /** Checks that none of the channels is waiting for its delta to be read */
- bool noDeltaTime() const;
-
- /** Determine the channel # of the next active event, or -1 */
- int getCommandIndex() const;
-};
-
-#define PLAYMASK_NONE 0x0
-
-/***************************/
-/*--------- Timer ---------*/
-/***************************/
-
-/**
- * A song iterator which waits a specified time and then fires
- * SI_FINISHED. Used by DoSound, where audio resources are played (SCI1)
- */
-class TimerSongIterator : public SongIterator {
-protected:
- int _delta; /**!< Remaining time */
-
-public:
- TimerSongIterator(int delta) : _delta(delta) {}
-
- int nextCommand(byte *buf, int *result);
- Audio::AudioStream *getAudioStream() { return NULL; }
- SongIterator *handleMessage(Message msg) { return NULL; }
- int getTimepos() { return 0; }
- SongIterator *clone(int delta) { return new TimerSongIterator(*this); }
-};
-
-/**********************************/
-/*--------- Fast Forward ---------*/
-/**********************************/
-
-/**
- * A song iterator which fast-forwards another iterator.
- * Skips all delta times until a specified 'delta' has been used up.
- */
-class FastForwardSongIterator : public SongIterator {
-protected:
- SongIterator *_delegate;
- int _delta; /**!< Remaining time */
-
-public:
- FastForwardSongIterator(SongIterator *capsit, int delta);
-
- int nextCommand(byte *buf, int *result);
- Audio::AudioStream *getAudioStream();
- SongIterator *handleMessage(Message msg);
- int getTimepos();
- SongIterator *clone(int delta);
-};
-
-
-/**********************************/
-/*--------- Tee iterator ---------*/
-/**********************************/
-
-enum {
- TEE_LEFT = 0,
- TEE_RIGHT = 1,
- TEE_LEFT_ACTIVE = (1<<0),
- TEE_RIGHT_ACTIVE = (1<<1),
- TEE_LEFT_READY = (1<<2), /**!< left result is ready */
- TEE_RIGHT_READY = (1<<3), /**!< right result is ready */
- TEE_LEFT_PCM = (1<<4),
- TEE_RIGHT_PCM = (1<<5)
-};
-
-/**
- * This iterator combines two iterators, returns the next event available from either.
- */
-class TeeSongIterator : public SongIterator {
-public:
- int _status;
-
- bool _readyToMorph; /**!< One of TEE_MORPH_* above */
-
- struct {
- SongIterator *it;
- byte buf[4];
- int result;
- int retval;
- } _children[2];
-
-public:
- TeeSongIterator(SongIterator *left, SongIterator *right);
- ~TeeSongIterator();
-
- int nextCommand(byte *buf, int *result);
- Audio::AudioStream *getAudioStream();
- SongIterator *handleMessage(Message msg);
- void init();
- int getTimepos() { return 0; }
- SongIterator *clone(int delta);
-};
-
-} // End of namespace Sci
-
-#endif // USE_OLD_MUSIC_FUNCTIONS
-
-#endif // SCI_SFX_SFX_ITERATOR_INTERNAL
diff --git a/engines/sci/sound/iterator/songlib.cpp b/engines/sci/sound/iterator/songlib.cpp
deleted file mode 100644
index 8bc2e8f476..0000000000
--- a/engines/sci/sound/iterator/songlib.cpp
+++ /dev/null
@@ -1,189 +0,0 @@
-/* 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 "sci/sci.h" // for USE_OLD_MUSIC_FUNCTIONS
-
-#ifdef USE_OLD_MUSIC_FUNCTIONS
-#include "sci/sound/iterator/core.h"
-#include "sci/sound/iterator/iterator.h"
-
-namespace Sci {
-
-#define debug_stream stderr
-
-Song::Song() : _wakeupTime(0, SFX_TICKS_PER_SEC) {
- _handle = 0;
- _resourceNum = 0;
- _priority = 0;
- _status = SOUND_STATUS_STOPPED;
-
- _restoreBehavior = RESTORE_BEHAVIOR_CONTINUE;
- _restoreTime = 0;
-
- _loops = 0;
- _hold = 0;
-
- _it = 0;
- _delay = 0;
-
- _next = NULL;
- _nextPlaying = NULL;
- _nextStopping = NULL;
-}
-
-Song::Song(SongHandle handle, SongIterator *it, int priority) : _wakeupTime(0, SFX_TICKS_PER_SEC) {
- _handle = handle;
- _resourceNum = 0;
- _priority = priority;
- _status = SOUND_STATUS_STOPPED;
-
- _restoreBehavior = RESTORE_BEHAVIOR_CONTINUE;
- _restoreTime = 0;
-
- _loops = 0;
- _hold = 0;
-
- _it = it;
- _delay = 0;
-
- _next = NULL;
- _nextPlaying = NULL;
- _nextStopping = NULL;
-}
-
-void SongLibrary::addSong(Song *song) {
- Song **seeker = NULL;
- int pri = song->_priority;
-
- if (NULL == song) {
- warning("addSong(): NULL passed for song");
- return;
- }
-
- seeker = &_lib;
- while (*seeker && ((*seeker)->_priority > pri))
- seeker = &((*seeker)->_next);
-
- song->_next = *seeker;
- *seeker = song;
-}
-
-void SongLibrary::freeSounds() {
- Song *next = _lib;
- while (next) {
- Song *song = next;
- delete song->_it;
- song->_it = NULL;
- next = song->_next;
- delete song;
- }
- _lib = NULL;
-}
-
-
-Song *SongLibrary::findSong(SongHandle handle) {
- Song *seeker = _lib;
-
- while (seeker) {
- if (seeker->_handle == handle)
- break;
- seeker = seeker->_next;
- }
-
- return seeker;
-}
-
-Song *SongLibrary::findNextActive(Song *other) {
- Song *seeker = other ? other->_next : _lib;
-
- while (seeker) {
- if ((seeker->_status == SOUND_STATUS_WAITING) ||
- (seeker->_status == SOUND_STATUS_PLAYING))
- break;
- seeker = seeker->_next;
- }
-
- /* Only return songs that have equal priority */
- if (other && seeker && other->_priority > seeker->_priority)
- return NULL;
-
- return seeker;
-}
-
-Song *SongLibrary::findFirstActive() {
- return findNextActive(NULL);
-}
-
-int SongLibrary::removeSong(SongHandle handle) {
- int retval;
- Song *goner = _lib;
-
- if (!goner)
- return -1;
-
- if (goner->_handle == handle)
- _lib = goner->_next;
-
- else {
- while ((goner->_next) && (goner->_next->_handle != handle))
- goner = goner->_next;
-
- if (goner->_next) { /* Found him? */
- Song *oldnext = goner->_next;
-
- goner->_next = goner->_next->_next;
- goner = oldnext;
- } else return -1; /* No. */
- }
-
- retval = goner->_status;
-
- delete goner->_it;
- delete goner;
-
- return retval;
-}
-
-int SongLibrary::countSongs() {
- Song *seeker = _lib;
- int retval = 0;
-
- while (seeker) {
- retval++;
- seeker = seeker->_next;
- }
-
- return retval;
-}
-
-void SongLibrary::setSongRestoreBehavior(SongHandle handle, RESTORE_BEHAVIOR action) {
- Song *seeker = findSong(handle);
-
- seeker->_restoreBehavior = action;
-}
-
-} // End of namespace Sci
-
-#endif // USE_OLD_MUSIC_FUNCTIONS
diff --git a/engines/sci/sound/iterator/songlib.h b/engines/sci/sound/iterator/songlib.h
deleted file mode 100644
index acb704edaa..0000000000
--- a/engines/sci/sound/iterator/songlib.h
+++ /dev/null
@@ -1,171 +0,0 @@
-/* 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$
- *
- */
-
-/* Song library */
-
-#ifndef SCI_SFX_SFX_SONGLIB_H
-#define SCI_SFX_SFX_SONGLIB_H
-
-#include "common/scummsys.h"
-#include "sound/timestamp.h"
-
-#include "sci/sci.h" // for USE_OLD_MUSIC_FUNCTIONS
-#ifdef USE_OLD_MUSIC_FUNCTIONS
-
-namespace Sci {
-
-class SongIterator;
-
-#define SOUND_STATUS_STOPPED 0
-#define SOUND_STATUS_PLAYING 1
-#define SOUND_STATUS_SUSPENDED 2
-/* suspended: only if ordered from kernel space */
-#define SOUND_STATUS_WAITING 3
-/* "waiting" means "tagged for playing, but not active right now" */
-
-typedef unsigned long SongHandle;
-
-enum RESTORE_BEHAVIOR {
- RESTORE_BEHAVIOR_CONTINUE, /* restart a song when restored from
- a saved game */
- RESTORE_BEHAVIOR_RESTART /* continue it from where it was */
-};
-
-class Song {
-public:
- SongHandle _handle;
- int _resourceNum; /**<! Resource number */
- int _priority; /**!< Song priority (more important if priority is higher) */
- int _status; /* See above */
-
- int _restoreBehavior;
- int _restoreTime;
-
- /* Grabbed from the sound iterator, for save/restore purposes */
- int _loops;
- int _hold;
-
- SongIterator *_it;
- int _delay; /**!< Delay before accessing the iterator, in ticks */
-
- Audio::Timestamp _wakeupTime; /**!< Timestamp indicating the next MIDI event */
-
- Song *_next; /**!< Next song or NULL if this is the last one */
-
- /**
- * Next playing song. Used by the core song system.
- */
- Song *_nextPlaying;
-
- /**
- * Next song pending stopping. Used exclusively by the core song system's
- * _update_multi_song()
- */
- Song *_nextStopping;
-
-public:
-
- Song();
-
- /**
- * Initializes a new song.
- * @param handle the sound handle
- * @param it the song
- * @param priority the song's priority
- * @return a freshly allocated song
- */
- Song(SongHandle handle, SongIterator *it, int priority);
-};
-
-
-class SongLibrary {
-public:
- Song *_lib;
-
-public:
- SongLibrary() : _lib(0) {}
-
- /** Frees a song library. */
- void freeSounds();
-
- /**
- * Adds a song to a song library.
- * @param song song to add
- */
- void addSong(Song *song);
-
- /**
- * Looks up the song with the specified handle.
- * @param handle sound handle to look for
- * @return the song or NULL if it wasn't found
- */
- Song *findSong(SongHandle handle);
-
- /**
- * Finds the first song playing with the highest priority.
- * @return the song that should be played next, or NULL if there is none
- */
- Song *findFirstActive();
-
- /**
- * Finds the next song playing with the highest priority.
- *
- * The functions 'findFirstActive' and 'findNextActive'
- * allow to iterate over all songs that satisfy the requirement of
- * being 'playable'.
- *
- * @param song a song previously returned from the song library
- * @return the next song to play relative to 'song', or NULL if none are left
- */
- Song *findNextActive(Song *song);
-
- /**
- * Removes a song from the library.
- * @param handle handle of the song to remove
- * @return the status of the song that was removed
- */
- int removeSong(SongHandle handle);
-
- /**
- * Counts the number of songs in a song library.
- * @return the number of songs
- */
- int countSongs();
-
- /**
- * Determines what should be done with the song "handle" when restoring
- * it from a saved game.
- * @param handle sound handle being restored
- * @param action desired action
- */
- void setSongRestoreBehavior(SongHandle handle,
- RESTORE_BEHAVIOR action);
-};
-
-} // End of namespace Sci
-
-#endif // USE_OLD_MUSIC_FUNCTIONS
-
-#endif // SCI_SSFX_SFX_SONGLIB_H
diff --git a/engines/sci/sound/iterator/test-iterator.cpp b/engines/sci/sound/iterator/test-iterator.cpp
deleted file mode 100644
index 0d603a89fd..0000000000
--- a/engines/sci/sound/iterator/test-iterator.cpp
+++ /dev/null
@@ -1,423 +0,0 @@
-/* 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 "iterator.h"
-#include "iterator_internal.h"
-#include <stdarg.h>
-#include <stdio.h>
-
-using namespace Sci;
-
-#define ASSERT_S(x) if (!(x)) { error("Failed assertion in L%d: " #x, __LINE__); return; }
-#define ASSERT(x) ASSERT_S(x)
-
-/* Tests the song iterators */
-
-int errors = 0;
-
-void error(char *fmt, ...) {
- va_list ap;
-
- fprintf(stderr, "[ERROR] ");
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
-
- ++errors;
-}
-
-
-/* The simple iterator will finish after a fixed amount of time. Before that,
-** it emits (absolute) cues in ascending order. */
-struct simple_iterator : public SongIterator {
- int lifetime_remaining;
- char *cues;
- int cue_counter;
- int cue_progress;
- int cues_nr;
-};
-
-int simple_it_next(SongIterator *_self, unsigned char *buf, int *result) {
- simple_iterator *self = (simple_iterator *)_self;
-
- if (self->lifetime_remaining == -1) {
- error("Song iterator called post mortem");
- return SI_FINISHED;
- }
-
- if (self->lifetime_remaining) {
-
- if (self->cue_counter < self->cues_nr) {
- int time_to_cue = self->cues[self->cue_counter];
-
- if (self->cue_progress == time_to_cue) {
- ++self->cue_counter;
- self->cue_progress = 0;
- *result = self->cue_counter;
- return SI_ABSOLUTE_CUE;
- } else {
- int retval = time_to_cue - self->cue_progress;
- self->cue_progress = time_to_cue;
-
- if (retval > self->lifetime_remaining) {
- retval = self->lifetime_remaining;
- self->lifetime_remaining = 0;
- self->cue_progress = retval;
- return retval;
- }
-
- self->lifetime_remaining -= retval;
- return retval;
- }
- } else {
- int retval = self->lifetime_remaining;
- self->lifetime_remaining = 0;
- return retval;
- }
-
- } else {
- self->lifetime_remaining = -1;
- return SI_FINISHED;
- }
-}
-
-Audio::AudioStream *simple_it_pcm_feed(SongIterator *_self) {
- error("No PCM feed");
- return NULL;
-}
-
-void simple_it_init(SongIterator *_self) {
-}
-
-SongIterator *simple_it_handle_message(SongIterator *_self, SongIterator::Message msg) {
- return NULL;
-}
-
-void simple_it_cleanup(SongIterator *_self) {
-}
-
-/* Initialises the simple iterator.
-** Parameters: (int) delay: Number of ticks until the iterator finishes
-** (int *) cues: An array of cue delays (cue values are [1,2...])
-** (int) cues_nr: Number of cues in ``cues''
-** The first cue is emitted after cues[0] ticks, and it is 1. After cues[1] additional ticks
-** the next cue is emitted, and so on. */
-SongIterator *setup_simple_iterator(int delay, char *cues, int cues_nr) {
- simple_iterator.lifetime_remaining = delay;
- simple_iterator.cues = cues;
- simple_iterator.cue_counter = 0;
- simple_iterator.cues_nr = cues_nr;
- simple_iterator.cue_progress = 0;
-
- simple_iterator.ID = 42;
- simple_iterator.channel_mask = 0x004f;
- simple_iterator.flags = 0;
- simple_iterator.priority = 1;
-
- simple_iterator.death_listeners_nr = 0;
-
- simple_iterator.cleanup = simple_it_cleanup;
- simple_iterator.init = simple_it_init;
- simple_iterator.handle_message = simple_it_handle_message;
- simple_iterator.get_pcm_feed = simple_it_pcm_feed;
- simple_iterator.next = simple_it_next;
-
- return (SongIterator *) &simple_iterator;
-}
-
-#define ASSERT_SIT ASSERT(it == simple_it)
-#define ASSERT_FFIT ASSERT(it == ff_it)
-#define ASSERT_NEXT(n) ASSERT(songit_next(&it, data, &result, IT_READER_MASK_ALL) == n)
-#define ASSERT_RESULT(n) ASSERT(result == n)
-#define ASSERT_CUE(n) ASSERT_NEXT(SI_ABSOLUTE_CUE); ASSERT_RESULT(n)
-
-void test_simple_it() {
- SongIterator *it;
- SongIterator *simple_it = (SongIterator *) & simple_iterator;
- unsigned char data[4];
- int result;
- puts("[TEST] simple iterator (test artifact)");
-
- it = setup_simple_iterator(42, NULL, 0);
-
- ASSERT_SIT;
- ASSERT_NEXT(42);
- ASSERT_SIT;
- ASSERT_NEXT(SI_FINISHED);
- ASSERT_SIT;
-
- it = setup_simple_iterator(42, "\003\004", 2);
- ASSERT_SIT;
- ASSERT_NEXT(3);
- ASSERT_CUE(1);
- ASSERT_SIT;
- ASSERT_NEXT(4);
- ASSERT_CUE(2);
- ASSERT_SIT;
-// warning("XXX => %d", songit_next(&it, data, &result, IT_READER_MASK_ALL));
- ASSERT_NEXT(35);
- ASSERT_NEXT(SI_FINISHED);
- ASSERT_SIT;
-
- puts("[TEST] Test OK.");
-}
-
-void test_fastforward() {
- SongIterator *it;
- SongIterator *simple_it = (SongIterator *) & simple_iterator;
- SongIterator *ff_it;
- unsigned char data[4];
- int result;
- puts("[TEST] fast-forward iterator");
-
- it = setup_simple_iterator(42, NULL, 0);
- ff_it = it = new_fast_forward_iterator(it, 0);
- ASSERT_FFIT;
- ASSERT_NEXT(42);
- ASSERT_SIT; /* Must have morphed back */
- ASSERT_NEXT(SI_FINISHED);
- ASSERT_SIT;
-
- it = setup_simple_iterator(42, NULL, 0);
- ff_it = it = new_fast_forward_iterator(it, 1);
- ASSERT_FFIT;
- ASSERT_NEXT(41);
- /* May or may not have morphed back here */
- ASSERT_NEXT(SI_FINISHED);
- ASSERT_SIT;
-
- it = setup_simple_iterator(42, NULL, 0);
- ff_it = it = new_fast_forward_iterator(it, 41);
- ASSERT_FFIT;
- ASSERT_NEXT(1);
- /* May or may not have morphed back here */
- ASSERT_NEXT(SI_FINISHED);
- ASSERT_SIT;
-
- it = setup_simple_iterator(42, NULL, 0);
- ff_it = it = new_fast_forward_iterator(it, 42);
- ASSERT_NEXT(SI_FINISHED);
- /* May or may not have morphed back here */
-
- it = setup_simple_iterator(42, NULL, 0);
- ff_it = it = new_fast_forward_iterator(it, 10000);
- ASSERT_NEXT(SI_FINISHED);
- /* May or may not have morphed back here */
-
- it = setup_simple_iterator(42, "\003\004", 2);
- ff_it = it = new_fast_forward_iterator(it, 2);
- ASSERT_FFIT;
- ASSERT_NEXT(1);
- ASSERT_CUE(1);
- ASSERT_SIT;
- ASSERT_NEXT(4);
- ASSERT_CUE(2);
- ASSERT_SIT;
- ASSERT_NEXT(35);
- ASSERT_NEXT(SI_FINISHED);
- ASSERT_SIT;
-
- it = setup_simple_iterator(42, "\003\004", 2);
- ff_it = it = new_fast_forward_iterator(it, 5);
- ASSERT_FFIT;
- ASSERT_CUE(1);
- ASSERT_FFIT;
- ASSERT_NEXT(2);
- ASSERT_CUE(2);
- ASSERT_SIT;
- ASSERT_NEXT(35);
- ASSERT_NEXT(SI_FINISHED);
- ASSERT_SIT;
-
- it = setup_simple_iterator(42, "\003\004", 2);
- ff_it = it = new_fast_forward_iterator(it, 41);
- ASSERT_FFIT;
- ASSERT_CUE(1);
- ASSERT_FFIT;
- ASSERT_CUE(2);
- ASSERT_FFIT;
- ASSERT_NEXT(1);
- ASSERT_NEXT(SI_FINISHED);
- ASSERT_SIT;
-
- puts("[TEST] Test OK.");
-}
-
-#define SIMPLE_SONG_SIZE 50
-
-static unsigned char simple_song[SIMPLE_SONG_SIZE] = {
- 0x00, /* Regular song */
- /* Only use channel 0 for all devices */
- 0x02, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- /* Song begins here */
- 42, 0x90, 60, 0x7f, /* Play C after 42 ticks */
- 02, 64, 0x42, /* Play E after 2 more ticks, using running status mode */
- 0xf8, 10, 0x80, 60, 0x02, /* Stop C after 250 ticks */
- 0, 64, 0x00, /* Stop E immediately */
- 00, 0xfc /* Stop song */
-};
-
-#define ASSERT_MIDI3(cmd, arg0, arg1) \
- ASSERT(data[0] == cmd); \
- ASSERT(data[1] == arg0); \
- ASSERT(data[2] == arg1);
-
-void test_iterator_sci0() {
- SongIterator *it = songit_new(simple_song, SIMPLE_SONG_SIZE, SCI_SONG_ITERATOR_TYPE_SCI0, 0l);
- unsigned char data[4];
- int result;
- SIMSG_SEND(it, SIMSG_SET_PLAYMASK(0x0001)); /* Initialise song, enabling channel 0 */
-
- puts("[TEST] SCI0-style song");
- ASSERT_NEXT(42);
- ASSERT_NEXT(0);
- ASSERT_MIDI3(0x90, 60, 0x7f);
- ASSERT_NEXT(2);
- ASSERT_NEXT(0);
- ASSERT_MIDI3(0x90, 64, 0x42);
- ASSERT_NEXT(250);
- ASSERT_NEXT(0);
- ASSERT_MIDI3(0x80, 60, 0x02);
- ASSERT_NEXT(0);
- ASSERT_MIDI3(0x80, 64, 0x00);
- ASSERT_NEXT(SI_FINISHED);
- puts("[TEST] Test OK.");
-}
-
-
-
-void test_iterator_sci0_loop() {
- SongIterator *it = songit_new(simple_song, SIMPLE_SONG_SIZE, SCI_SONG_ITERATOR_TYPE_SCI0, 0l);
- unsigned char data[4];
- int result;
- SIMSG_SEND(it, SIMSG_SET_PLAYMASK(0x0001)); /* Initialise song, enabling channel 0 */
- SIMSG_SEND(it, SIMSG_SET_LOOPS(2)); /* Loop one additional time */
-
- puts("[TEST] SCI0-style song with looping");
- ASSERT_NEXT(42);
- ASSERT_NEXT(0);
- ASSERT_MIDI3(0x90, 60, 0x7f);
- ASSERT_NEXT(2);
- ASSERT_NEXT(0);
- ASSERT_MIDI3(0x90, 64, 0x42);
- ASSERT_NEXT(250);
- ASSERT_NEXT(0);
- ASSERT_MIDI3(0x80, 60, 0x02);
- ASSERT_NEXT(0);
- ASSERT_MIDI3(0x80, 64, 0x00);
- ASSERT_NEXT(SI_LOOP);
- ASSERT_NEXT(42);
- ASSERT_NEXT(0);
- ASSERT_MIDI3(0x90, 60, 0x7f);
- ASSERT_NEXT(2);
- ASSERT_NEXT(0);
- ASSERT_MIDI3(0x90, 64, 0x42);
- ASSERT_NEXT(250);
- ASSERT_NEXT(0);
- ASSERT_MIDI3(0x80, 60, 0x02);
- ASSERT_NEXT(0);
- ASSERT_MIDI3(0x80, 64, 0x00);
- ASSERT_NEXT(SI_FINISHED);
- puts("[TEST] Test OK.");
-}
-
-
-
-#define LOOP_SONG_SIZE 54
-
-unsigned char loop_song[LOOP_SONG_SIZE] = {
- 0x00, /* Regular song song */
- /* Only use channel 0 for all devices */
- 0x02, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- /* Song begins here */
- 42, 0x90, 60, 0x7f, /* Play C after 42 ticks */
- 13, 0x80, 60, 0x00, /* Stop C after 13 ticks */
- 00, 0xCF, 0x7f, /* Set loop point */
- 02, 0x90, 64, 0x42, /* Play E after 2 more ticks, using running status mode */
- 03, 0x80, 64, 0x00, /* Stop E after 3 ticks */
- 00, 0xfc /* Stop song/loop */
-};
-
-
-void test_iterator_sci0_mark_loop() {
- SongIterator *it = songit_new(loop_song, LOOP_SONG_SIZE, SCI_SONG_ITERATOR_TYPE_SCI0, 0l);
- unsigned char data[4];
- int result;
- SIMSG_SEND(it, SIMSG_SET_PLAYMASK(0x0001)); /* Initialise song, enabling channel 0 */
- SIMSG_SEND(it, SIMSG_SET_LOOPS(3)); /* Loop once more */
-
- puts("[TEST] SCI0-style song with loop mark, looping");
- ASSERT_NEXT(42);
- ASSERT_NEXT(0);
- ASSERT_MIDI3(0x90, 60, 0x7f);
- ASSERT_NEXT(13);
- ASSERT_NEXT(0);
- ASSERT_MIDI3(0x80, 60, 0x00);
- /* Loop point here: we don't observe that in the iterator interface yet, though */
- ASSERT_NEXT(2);
- ASSERT_NEXT(0);
- ASSERT_MIDI3(0x90, 64, 0x42);
- ASSERT_NEXT(3);
- ASSERT_NEXT(0);
- ASSERT_MIDI3(0x80, 64, 0x00);
- /* Now we loop back to the loop pont */
- ASSERT_NEXT(SI_LOOP);
- ASSERT_NEXT(2);
- ASSERT_NEXT(0);
- ASSERT_MIDI3(0x90, 64, 0x42);
- ASSERT_NEXT(3);
- ASSERT_NEXT(0);
- ASSERT_MIDI3(0x80, 64, 0x00);
- /* ...and one final time */
- ASSERT_NEXT(SI_LOOP);
- ASSERT_NEXT(2);
- ASSERT_NEXT(0);
- ASSERT_MIDI3(0x90, 64, 0x42);
- ASSERT_NEXT(3);
- ASSERT_NEXT(0);
- ASSERT_MIDI3(0x80, 64, 0x00);
-
- ASSERT_NEXT(SI_FINISHED);
- puts("[TEST] Test OK.");
-}
-
-
-
-int main(int argc, char **argv) {
- test_simple_it();
- test_fastforward();
- test_iterator_sci0();
- test_iterator_sci0_loop();
- test_iterator_sci0_mark_loop();
- if (errors != 0)
- warning("[ERROR] %d errors total", errors);
- return (errors != 0);
-}
diff --git a/engines/sci/sound/midiparser_sci.cpp b/engines/sci/sound/midiparser_sci.cpp
index 3ee8a3a83d..cc09ba79f0 100644
--- a/engines/sci/sound/midiparser_sci.cpp
+++ b/engines/sci/sound/midiparser_sci.cpp
@@ -43,30 +43,39 @@ enum SciMidiCommands {
// MidiParser_SCI
//
-MidiParser_SCI::MidiParser_SCI(SciVersion soundVersion) :
+MidiParser_SCI::MidiParser_SCI(SciVersion soundVersion, SciMusic *music) :
MidiParser() {
_soundVersion = soundVersion;
+ _music = music;
_mixedData = NULL;
// mididata contains delta in 1/60th second
// values of ppqn and tempo are found experimentally and may be wrong
_ppqn = 1;
setTempo(16667);
- _volume = 0;
+ _volume = 127;
_signalSet = false;
_signalToSet = 0;
_dataincAdd = false;
_dataincToAdd = 0;
_resetOnPause = false;
- _channelsUsed = 0;
-
- for (int i = 0; i < 16; i++)
- _channelRemap[i] = i;
+ _pSnd = 0;
}
MidiParser_SCI::~MidiParser_SCI() {
unloadMusic();
+ // we do this, so that MidiParser won't be able to call his own ::allNotesOff()
+ // this one would affect all channels and we can't let that happen
+ _driver = 0;
+}
+
+void MidiParser_SCI::mainThreadBegin() {
+ _mainThreadCalled = true;
+}
+
+void MidiParser_SCI::mainThreadEnd() {
+ _mainThreadCalled = false;
}
bool MidiParser_SCI::loadMusic(SoundResource::Track *track, MusicEntry *psnd, int channelFilterMask, SciVersion soundVersion) {
@@ -75,7 +84,14 @@ bool MidiParser_SCI::loadMusic(SoundResource::Track *track, MusicEntry *psnd, in
_pSnd = psnd;
_soundVersion = soundVersion;
- setVolume(psnd->volume);
+ for (int i = 0; i < 16; i++) {
+ _channelUsed[i] = false;
+ _channelRemap[i] = -1;
+ _channelMuted[i] = false;
+ _channelVolume[i] = 127;
+ }
+ _channelRemap[9] = 9; // never map channel 9, because that's used for percussion
+ _channelRemap[15] = 15; // never map channel 15, because thats used by sierra internally
if (channelFilterMask) {
// SCI0 only has 1 data stream, but we need to filter out channels depending on music hardware selection
@@ -86,30 +102,272 @@ bool MidiParser_SCI::loadMusic(SoundResource::Track *track, MusicEntry *psnd, in
_num_tracks = 1;
_tracks[0] = _mixedData;
- setTrack(0);
+ if (_pSnd)
+ setTrack(0);
_loopTick = 0;
- if (_soundVersion <= SCI_VERSION_0_LATE) {
- // Set initial voice count
- for (int i = 0; i < 16; ++i) {
- byte voiceCount = 0;
- if (channelFilterMask & (1 << i))
- voiceCount = psnd->soundRes->getInitialVoiceCount(i);
- _driver->send(0xB0 | i, 0x4B, voiceCount);
+ return true;
+}
+
+byte MidiParser_SCI::midiGetNextChannel(long ticker) {
+ byte curr = 0xFF;
+ long closest = ticker + 1000000, next = 0;
+
+ for (int i = 0; i < _track->channelCount; i++) {
+ if (_track->channels[i].time == -1) // channel ended
+ continue;
+ SoundResource::Channel *curChannel = &_track->channels[i];
+ if (curChannel->curPos >= curChannel->size)
+ continue;
+ next = curChannel->data[curChannel->curPos]; // when the next event should occur
+ if (next == 0xF8) // 0xF8 means 240 ticks delay
+ next = 240;
+ next += _track->channels[i].time;
+ if (next < closest) {
+ curr = i;
+ closest = next;
+ }
+ }
+
+ return curr;
+}
+
+byte *MidiParser_SCI::midiMixChannels() {
+ int totalSize = 0;
+
+ for (int i = 0; i < _track->channelCount; i++) {
+ _track->channels[i].time = 0;
+ _track->channels[i].prev = 0;
+ _track->channels[i].curPos = 0;
+ totalSize += _track->channels[i].size;
+ }
+
+ byte *outData = new byte[totalSize * 2]; // FIXME: creates overhead and still may be not enough to hold all data
+ _mixedData = outData;
+ long ticker = 0;
+ byte channelNr, curDelta;
+ byte midiCommand = 0, midiParam, global_prev = 0;
+ long newDelta;
+ SoundResource::Channel *channel;
+
+ while ((channelNr = midiGetNextChannel(ticker)) != 0xFF) { // there is still an active channel
+ channel = &_track->channels[channelNr];
+ curDelta = channel->data[channel->curPos++];
+ channel->time += (curDelta == 0xF8 ? 240 : curDelta); // when the command is supposed to occur
+ if (curDelta == 0xF8)
+ continue;
+ newDelta = channel->time - ticker;
+ ticker += newDelta;
+
+ midiCommand = channel->data[channel->curPos++];
+ if (midiCommand != kEndOfTrack) {
+ // Write delta
+ while (newDelta > 240) {
+ *outData++ = 0xF8;
+ newDelta -= 240;
+ }
+ *outData++ = (byte)newDelta;
+ }
+ // Write command
+ switch (midiCommand) {
+ case 0xF0: // sysEx
+ *outData++ = midiCommand;
+ do {
+ midiParam = channel->data[channel->curPos++];
+ *outData++ = midiParam;
+ } while (midiParam != 0xF7);
+ break;
+ case kEndOfTrack: // end of channel
+ channel->time = -1;
+ break;
+ default: // MIDI command
+ if (midiCommand & 0x80) {
+ midiParam = channel->data[channel->curPos++];
+ } else {// running status
+ midiParam = midiCommand;
+ midiCommand = channel->prev;
+ }
+
+ // remember which channel got used for channel remapping
+ byte midiChannel = midiCommand & 0xF;
+ _channelUsed[midiChannel] = true;
+
+ if (midiCommand != global_prev)
+ *outData++ = midiCommand;
+ *outData++ = midiParam;
+ if (nMidiParams[(midiCommand >> 4) - 8] == 2)
+ *outData++ = channel->data[channel->curPos++];
+ channel->prev = midiCommand;
+ global_prev = midiCommand;
+ }
+ }
+
+ // Insert stop event
+ *outData++ = 0; // Delta
+ *outData++ = 0xFF; // Meta event
+ *outData++ = 0x2F; // End of track (EOT)
+ *outData++ = 0x00;
+ *outData++ = 0x00;
+ return _mixedData;
+}
+
+// This is used for SCI0 sound-data. SCI0 only has one stream that may
+// contain several channels and according to output device we remove
+// certain channels from that data.
+byte *MidiParser_SCI::midiFilterChannels(int channelMask) {
+ SoundResource::Channel *channel = &_track->channels[0];
+ byte *channelData = channel->data;
+ byte *channelDataEnd = channel->data + channel->size;
+ byte *outData = new byte[channel->size + 5];
+ byte curChannel = 15, curByte, curDelta;
+ byte command = 0, lastCommand = 0;
+ int delta = 0;
+ int midiParamCount = 0;
+
+ _mixedData = outData;
+
+ while (channelData < channelDataEnd) {
+ curDelta = *channelData++;
+ if (curDelta == 0xF8) {
+ delta += 240;
+ continue;
+ }
+ delta += curDelta;
+ curByte = *channelData++;
+
+ switch (curByte) {
+ case 0xF0: // sysEx
+ case kEndOfTrack: // end of channel
+ command = curByte;
+ curChannel = 15;
+ break;
+ default:
+ if (curByte & 0x80) {
+ command = curByte;
+ curChannel = command & 0x0F;
+ midiParamCount = nMidiParams[(command >> 4) - 8];
+ }
+ }
+ if ((1 << curChannel) & channelMask) {
+ if (command != kEndOfTrack) {
+ // Write delta
+ while (delta > 240) {
+ *outData++ = 0xF8;
+ delta -= 240;
+ }
+ *outData++ = (byte)delta;
+ delta = 0;
+ }
+ // Write command
+ switch (command) {
+ case 0xF0: // sysEx
+ *outData++ = command;
+ do {
+ curByte = *channelData++;
+ *outData++ = curByte; // out
+ } while (curByte != 0xF7);
+ lastCommand = command;
+ break;
+
+ case kEndOfTrack: // end of channel
+ break;
+
+ default: // MIDI command
+ // remember which channel got used for channel remapping
+ byte midiChannel = command & 0xF;
+ _channelUsed[midiChannel] = true;
+
+ if (lastCommand != command) {
+ *outData++ = command;
+ lastCommand = command;
+ }
+ if (midiParamCount > 0) {
+ if (curByte & 0x80)
+ *outData++ = *channelData++;
+ else
+ *outData++ = curByte;
+ }
+ if (midiParamCount > 1) {
+ *outData++ = *channelData++;
+ }
+ }
+ } else {
+ if (curByte & 0x80)
+ channelData += midiParamCount;
+ else
+ channelData += midiParamCount - 1;
+ }
+ }
+
+ // Insert stop event
+ *outData++ = 0; // Delta
+ *outData++ = 0xFF; // Meta event
+ *outData++ = 0x2F; // End of track (EOT)
+ *outData++ = 0x00;
+ *outData++ = 0x00;
+
+ return _mixedData;
+}
+
+// This will get called right before actual playing and will try to own the used channels
+void MidiParser_SCI::tryToOwnChannels() {
+ // We don't have SciMusic in case debug command show_instruments is used
+ if (!_music)
+ return;
+ for (int curChannel = 0; curChannel < 15; curChannel++) {
+ if (_channelUsed[curChannel]) {
+ if (_channelRemap[curChannel] == -1) {
+ _channelRemap[curChannel] = _music->tryToOwnChannel(_pSnd, curChannel);
+ }
+ }
+ }
+}
+
+void MidiParser_SCI::lostChannels() {
+ for (int curChannel = 0; curChannel < 15; curChannel++)
+ if ((_channelUsed[curChannel]) && (curChannel != 9))
+ _channelRemap[curChannel] = -1;
+}
+
+void MidiParser_SCI::sendInitCommands() {
+ // reset our "global" volume and channel volumes
+ _volume = 127;
+ for (int i = 0; i < 16; i++)
+ _channelVolume[i] = 127;
+
+ // Set initial voice count
+ if (_pSnd) {
+ if (_soundVersion <= SCI_VERSION_0_LATE) {
+ for (int i = 0; i < 15; ++i) {
+ byte voiceCount = 0;
+ if (_channelUsed[i]) {
+ voiceCount = _pSnd->soundRes->getInitialVoiceCount(i);
+ sendToDriver(0xB0 | i, 0x4B, voiceCount);
+ }
+ }
}
}
// Send a velocity off signal to all channels
- for (int i = 0; i < 16; ++i) {
- _driver->send(0xB0 | i, 0x4E, 0); // Reset velocity
+ for (int i = 0; i < 15; ++i) {
+ if (_channelUsed[i])
+ sendToDriver(0xB0 | i, 0x4E, 0); // Reset velocity
}
- return true;
+ // Center the pitch wheels and hold pedal in preparation for the next piece of music
+ for (int i = 0; i < 16; ++i) {
+ if (_channelUsed[i]) {
+ sendToDriver(0xE0 | i, 0, 0x40); // Reset pitch wheel
+ sendToDriver(0xB0 | i, 0x40, 0); // Reset hold pedal
+ }
+ }
}
void MidiParser_SCI::unloadMusic() {
- resetTracking();
- allNotesOff();
+ if (_pSnd) {
+ resetTracking();
+ allNotesOff();
+ }
_num_tracks = 0;
_active_track = 255;
_resetOnPause = false;
@@ -118,36 +376,76 @@ void MidiParser_SCI::unloadMusic() {
delete[] _mixedData;
_mixedData = NULL;
}
+}
- // 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 (isChannelUsed(i)) {
- _driver->send(0xE0 | i, 0, 0x40); // Reset pitch wheel
- _driver->send(0xB0 | i, 0x40, 0); // Reset hold pedal
- }
+// this is used for scripts sending midi commands to us. we verify in that case that the channel is actually
+// used, so that channel remapping will work as well and then send them on
+void MidiParser_SCI::sendFromScriptToDriver(uint32 midi) {
+ byte midiChannel = midi & 0xf;
+
+ if (!_channelUsed[midiChannel]) {
+ // trying to send to an unused channel
+ // this happens for cmdSendMidi at least in sq1vga right at the start, it's a script issue
+ return;
+ }
+ if (_channelRemap[midiChannel] == -1) {
+ // trying to send to an unmapped channel
+ // this happens for cmdSendMidi at least in sq1vga right at the start, scripts are pausing the sound
+ // and then sending manually. it's a script issue
+ return;
+ }
+ sendToDriver(midi);
+}
+
+void MidiParser_SCI::sendToDriver(uint32 midi) {
+ byte midiChannel = midi & 0xf;
+
+ if ((midi & 0xFFF0) == 0x4EB0) {
+ // this is channel mute only for sci1
+ // it's velocity control for sci0
+ if (_soundVersion >= SCI_VERSION_1_EARLY) {
+ _channelMuted[midiChannel] = midi & 0xFF0000 ? true : false;
+ return; // don't send this to driver at all
}
}
- for (int i = 0; i < 16; i++)
- _channelRemap[i] = i;
+ // Is channel muted? if so, don't send command
+ if (_channelMuted[midiChannel])
+ return;
+
+ if ((midi & 0xFFF0) == 0x07B0) {
+ // someone trying to set channel volume?
+ int channelVolume = (midi >> 16) & 0xFF;
+ // Remember, if we need to set it ourselves
+ _channelVolume[midiChannel] = channelVolume;
+ // Adjust volume accordingly to current "global" volume
+ channelVolume = channelVolume * _volume / 127;
+ midi = (midi & 0xFFF0) | ((channelVolume & 0xFF) << 16);
+ }
+
+ // Channel remapping
+ int16 realChannel = _channelRemap[midiChannel];
+ assert(realChannel != -1);
+
+ midi = (midi & 0xFFFFFFF0) | realChannel;
+ if (_mainThreadCalled)
+ _music->putMidiCommandInQueue(midi);
+ else
+ _driver->send(midi);
}
void MidiParser_SCI::parseNextEvent(EventInfo &info) {
- // Monitor which channels are used by this song
- setChannelUsed(info.channel());
-
// Set signal AFTER waiting for delta, otherwise we would set signal too soon resulting in all sorts of bugs
if (_dataincAdd) {
_dataincAdd = false;
_pSnd->dataInc += _dataincToAdd;
_pSnd->signal = 0x7f + _pSnd->dataInc;
- debugC(2, kDebugLevelSound, "datainc %04x", _dataincToAdd);
+ debugC(4, kDebugLevelSound, "datainc %04x", _dataincToAdd);
}
if (_signalSet) {
_signalSet = false;
_pSnd->signal = _signalToSet;
- debugC(2, kDebugLevelSound, "signal %04x", _signalToSet);
+ debugC(4, kDebugLevelSound, "signal %04x", _signalToSet);
}
info.start = _position._play_pos;
@@ -173,10 +471,16 @@ void MidiParser_SCI::parseNextEvent(EventInfo &info) {
info.basic.param2 = 0;
if (info.channel() == 0xF) {// SCI special case
if (info.basic.param1 != kSetSignalLoop) {
- _signalSet = true;
- _signalToSet = info.basic.param1;
+ // at least in kq5/french&mac the first scene in the intro has a song that sets signal to 4 immediately
+ // on tick 0. Signal isn't set at that point by sierra sci and it would cause the castle daventry text to
+ // get immediately removed, so we currently filter it.
+ // Sierra SCI ignores them as well at that time
+ if (_position._play_tick) {
+ _signalSet = true;
+ _signalToSet = info.basic.param1;
+ }
} else {
- _loopTick = _position._play_tick;
+ _loopTick = _position._play_tick + info.delta;
}
}
break;
@@ -235,7 +539,6 @@ void MidiParser_SCI::parseNextEvent(EventInfo &info) {
case 0x0A: // pan
case 0x0B: // expression
case 0x40: // sustain
- case 0x4E: // velocity control
case 0x79: // reset all
case 0x7B: // notes off
// These are all handled by the music driver, so ignore them
@@ -249,8 +552,6 @@ void MidiParser_SCI::parseNextEvent(EventInfo &info) {
break;
}
}
- if (info.basic.param1 == 7) // channel volume change -scale it
- info.basic.param2 = info.basic.param2 * _volume / MUSIC_VOLUME_MAX;
info.length = 0;
break;
@@ -307,7 +608,7 @@ void MidiParser_SCI::parseNextEvent(EventInfo &info) {
_pSnd->status = kSoundStopped;
_pSnd->signal = SIGNAL_OFFSET;
- debugC(2, kDebugLevelSound, "signal EOT");
+ debugC(4, kDebugLevelSound, "signal EOT");
}
}
break;
@@ -319,254 +620,66 @@ void MidiParser_SCI::parseNextEvent(EventInfo &info) {
}// switch (info.command())
}
+void MidiParser_SCI::allNotesOff() {
+ if (!_driver)
+ return;
-byte MidiParser_SCI::midiGetNextChannel(long ticker) {
- byte curr = 0xFF;
- long closest = ticker + 1000000, next = 0;
-
- 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 should occur
- if (next == 0xF8) // 0xF8 means 240 ticks delay
- next = 240;
- next += _track->channels[i].time;
- if (next < closest) {
- curr = i;
- closest = next;
- }
- }
-
- return curr;
-}
-
-byte *MidiParser_SCI::midiMixChannels() {
- int totalSize = 0;
- byte **dataPtr = new byte *[_track->channelCount];
-
- for (int i = 0; i < _track->channelCount; i++) {
- dataPtr[i] = _track->channels[i].data;
- _track->channels[i].time = 0;
- _track->channels[i].prev = 0;
- totalSize += _track->channels[i].size;
- }
-
- byte *outData = new byte[totalSize * 2]; // FIXME: creates overhead and still may be not enough to hold all data
- _mixedData = outData;
- long ticker = 0;
- byte curr, curDelta;
- byte command = 0, par1, global_prev = 0;
- long new_delta;
- SoundResource::Channel *channel;
-
- while ((curr = midiGetNextChannel(ticker)) != 0xFF) { // there is still active channel
- channel = &_track->channels[curr];
- curDelta = *channel->data++;
- channel->time += (curDelta == 0xF8 ? 240 : curDelta); // when the comamnd is supposed to occur
- if (curDelta == 0xF8)
- continue;
- new_delta = channel->time - ticker;
- ticker += new_delta;
-
- command = *channel->data++;
- if (command != kEndOfTrack) {
- debugC(2, kDebugLevelSound, "\nDELTA ");
- // Write delta
- while (new_delta > 240) {
- *outData++ = 0xF8;
- debugC(2, kDebugLevelSound, "F8 ");
- new_delta -= 240;
- }
- *outData++ = (byte)new_delta;
- debugC(2, kDebugLevelSound, "%02X ", (uint32)new_delta);
- }
- // Write command
- switch (command) {
- case 0xF0: // sysEx
- *outData++ = command;
- debugC(2, kDebugLevelSound, "%02X ", command);
- do {
- par1 = *channel->data++;
- *outData++ = par1; // out
- } while (par1 != 0xF7);
- break;
- case kEndOfTrack: // end of channel
- channel->time = -1; // FIXME
- break;
- default: // MIDI command
- if (command & 0x80) {
- par1 = *channel->data++;
-
- // TODO: Fix remapping
+ int i, j;
-#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;
+ // Turn off all active notes
+ for (i = 0; i < 128; ++i) {
+ for (j = 0; j < 16; ++j) {
+ if ((_active_notes[i] & (1 << j)) && (_channelRemap[j] != -1)){
+ sendToDriver(0x80 | j, i, 0);
}
- if (command != global_prev)
- *outData++ = command; // out command
- *outData++ = par1;// pout par1
- if (nMidiParams[(command >> 4) - 8] == 2)
- *outData++ = *channel->data++; // out par2
- channel->prev = command;
- global_prev = command;
- }// switch(command)
- }// while (curr)
-
- // Insert stop event
- *outData++ = 0; // Delta
- *outData++ = 0xFF; // Meta event
- *outData++ = 0x2F; // End of track (EOT)
- *outData++ = 0x00;
- *outData++ = 0x00;
-
- for (int channelNr = 0; channelNr < _track->channelCount; channelNr++)
- _track->channels[channelNr].data = dataPtr[channelNr];
-
- delete[] dataPtr;
- return _mixedData;
-}
-
-// This is used for SCI0 sound-data. SCI0 only has one stream that may
-// contain several channels and according to output device we remove
-// certain channels from that data.
-byte *MidiParser_SCI::midiFilterChannels(int channelMask) {
- SoundResource::Channel *channel = &_track->channels[0];
- byte *channelData = channel->data;
- byte *channelDataEnd = channel->data + channel->size;
- byte *outData = new byte[channel->size + 5];
- byte curChannel = 15, curByte, curDelta;
- byte command = 0, lastCommand = 0;
- int delta = 0;
- int midiParamCount = 0;
-
- _mixedData = outData;
-
- while (channelData < channelDataEnd) {
- curDelta = *channelData++;
- if (curDelta == 0xF8) {
- delta += 240;
- continue;
}
- delta += curDelta;
- curByte = *channelData++;
+ }
- switch (curByte) {
- case 0xF0: // sysEx
- case kEndOfTrack: // end of channel
- command = curByte;
- curChannel = 15;
- break;
- default:
- if (curByte & 0x80) {
- command = curByte;
- curChannel = command & 0x0F;
- midiParamCount = nMidiParams[(command >> 4) - 8];
- }
+ // Turn off all hanging notes
+ for (i = 0; i < ARRAYSIZE(_hanging_notes); i++) {
+ byte midiChannel = _hanging_notes[i].channel;
+ if ((_hanging_notes[i].time_left) && (_channelRemap[midiChannel] != -1)) {
+ sendToDriver(0x80 | midiChannel, _hanging_notes[i].note, 0);
+ _hanging_notes[i].time_left = 0;
}
- if ((1 << curChannel) & channelMask) {
- if (command != kEndOfTrack) {
- debugC(2, kDebugLevelSound, "\nDELTA ");
- // Write delta
- while (delta > 240) {
- *outData++ = 0xF8;
- debugC(2, kDebugLevelSound, "F8 ");
- delta -= 240;
- }
- *outData++ = (byte)delta;
- debugC(2, kDebugLevelSound, "%02X ", delta);
- delta = 0;
- }
- // Write command
- switch (command) {
- case 0xF0: // sysEx
- *outData++ = command;
- debugC(2, kDebugLevelSound, "%02X ", command);
- do {
- curByte = *channelData++;
- *outData++ = curByte; // out
- } while (curByte != 0xF7);
- lastCommand = command;
- break;
+ }
+ _hanging_notes_count = 0;
- case kEndOfTrack: // end of channel
- break;
+ // To be sure, send an "All Note Off" event (but not all MIDI devices
+ // support this...).
- default: // MIDI command
- if (lastCommand != command) {
- *outData++ = command;
- debugC(2, kDebugLevelSound, "%02X ", command);
- lastCommand = command;
- }
- if (midiParamCount > 0) {
- if (curByte & 0x80) {
- debugC(2, kDebugLevelSound, "%02X ", *channelData);
- *outData++ = *channelData++;
- } else {
- debugC(2, kDebugLevelSound, "%02X ", curByte);
- *outData++ = curByte;
- }
- }
- if (midiParamCount > 1) {
- debugC(2, kDebugLevelSound, "%02X ", *channelData);
- *outData++ = *channelData++;
- }
- }
- } else {
- if (curByte & 0x80) {
- channelData += midiParamCount;
- } else {
- channelData += midiParamCount - 1;
- }
- }
+ for (i = 0; i < 16; ++i) {
+ if (_channelRemap[i] != -1)
+ sendToDriver(0xB0 | i, 0x7b, 0); // All notes off
}
- // Insert stop event
- *outData++ = 0; // Delta
- *outData++ = 0xFF; // Meta event
- *outData++ = 0x2F; // End of track (EOT)
- *outData++ = 0x00;
- *outData++ = 0x00;
-
- return _mixedData;
+ memset(_active_notes, 0, sizeof(_active_notes));
}
void MidiParser_SCI::setVolume(byte volume) {
- // FIXME: This receives values > 127... throw a warning for now and clip the variable
- if (volume > MUSIC_VOLUME_MAX) {
- warning("attempted to set an invalid volume(%d)", volume);
- volume = MUSIC_VOLUME_MAX; // reset
- }
-
assert(volume <= MUSIC_VOLUME_MAX);
- if (_volume != volume) {
- _volume = volume;
-
- switch (_soundVersion) {
- case SCI_VERSION_0_EARLY:
- case SCI_VERSION_0_LATE: {
- int16 globalVolume = _volume * 15 / 127;
- ((MidiPlayer *)_driver)->setVolume(globalVolume);
- break;
- }
+ _volume = volume;
+
+ switch (_soundVersion) {
+ case SCI_VERSION_0_EARLY:
+ case SCI_VERSION_0_LATE: {
+ // SCI0 adlib driver doesn't support channel volumes, so we need to go this way
+ // TODO: this should take the actual master volume into account
+ int16 globalVolume = _volume * 15 / 127;
+ ((MidiPlayer *)_driver)->setVolume(globalVolume);
+ break;
+ }
- case SCI_VERSION_1_EARLY:
- case SCI_VERSION_1_LATE:
- // sending volume change to all active channels
- for (int i = 0; i < _track->channelCount; i++)
- if (_track->channels[i].number <= 0xF)
- _driver->send(0xB0 + _track->channels[i].number, 7, _volume);
- break;
+ case SCI_VERSION_1_EARLY:
+ case SCI_VERSION_1_LATE:
+ // Send previous channel volumes again to actually update the volume
+ for (int i = 0; i < 15; i++)
+ if (_channelRemap[i] != -1)
+ sendToDriver(0xB0 + i, 7, _channelVolume[i]);
+ break;
- default:
- error("MidiParser_SCI::setVolume: Unsupported soundVersion");
- }
+ default:
+ error("MidiParser_SCI::setVolume: Unsupported soundVersion");
}
}
diff --git a/engines/sci/sound/midiparser_sci.h b/engines/sci/sound/midiparser_sci.h
index 9d4b5a39da..90db06e539 100644
--- a/engines/sci/sound/midiparser_sci.h
+++ b/engines/sci/sound/midiparser_sci.h
@@ -53,12 +53,17 @@ namespace Sci {
*/
class MidiParser_SCI : public MidiParser {
public:
- MidiParser_SCI(SciVersion soundVersion);
+ MidiParser_SCI(SciVersion soundVersion, SciMusic *music);
~MidiParser_SCI();
+
+ void mainThreadBegin();
+ void mainThreadEnd();
+
bool loadMusic(SoundResource::Track *track, MusicEntry *psnd, int channelFilterMask, SciVersion soundVersion);
bool loadMusic(byte *, uint32) {
return false;
}
+ void sendInitCommands();
void unloadMusic();
void setVolume(byte volume);
void stop() {
@@ -71,23 +76,29 @@ 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 allNotesOff();
- void clearUsedChannels() { _channelsUsed = 0; }
+ const byte *getMixedData() const { return _mixedData; }
-protected:
- bool isChannelUsed(byte channel) const { return _channelsUsed & (1 << channel); }
- void setChannelUsed(byte channel) { _channelsUsed |= (1 << channel); }
+ void tryToOwnChannels();
+ void lostChannels();
+ void sendFromScriptToDriver(uint32 midi);
+ void sendToDriver(uint32 midi);
+ void sendToDriver(byte status, byte firstOp, byte secondOp) {
+ sendToDriver(status | ((uint32)firstOp << 8) | ((uint32)secondOp << 16));
+ }
+protected:
void parseNextEvent(EventInfo &info);
byte *midiMixChannels();
byte *midiFilterChannels(int channelMask);
byte midiGetNextChannel(long ticker);
+ SciMusic *_music;
+
+ // this is set, when main thread calls us -> we send commands to queue instead to driver
+ bool _mainThreadCalled;
+
SciVersion _soundVersion;
byte *_mixedData;
SoundResource::Track *_track;
@@ -101,11 +112,10 @@ protected:
int16 _dataincToAdd;
bool _resetOnPause;
- // A 16-bit mask, containing the channels used
- // by the currently parsed song
- uint16 _channelsUsed;
-
- byte _channelRemap[16];
+ bool _channelUsed[16];
+ int16 _channelRemap[16];
+ bool _channelMuted[16];
+ byte _channelVolume[16];
};
} // End of namespace Sci
diff --git a/engines/sci/sound/music.cpp b/engines/sci/sound/music.cpp
index fa5716e7cc..55a7e1fdc4 100644
--- a/engines/sci/sound/music.cpp
+++ b/engines/sci/sound/music.cpp
@@ -43,6 +43,11 @@ SciMusic::SciMusic(SciVersion soundVersion)
// Reserve some space in the playlist, to avoid expensive insertion
// operations
_playList.reserve(10);
+
+ for (int i = 0; i < 16; i++)
+ _usedChannel[i] = 0;
+
+ _queuedCommands.reserve(1000);
}
SciMusic::~SciMusic() {
@@ -58,31 +63,30 @@ void SciMusic::init() {
// SCI sound init
_dwTempo = 0;
- MidiDriverType midiType;
-
// Default to MIDI in SCI32 games, as many don't have AdLib support.
- // WORKAROUND: Default to MIDI in Amiga SCI1_EGA+ games as we don't support those patches yet.
- // We also don't yet support the 7.pat file of SCI1+ Mac games or SCI0 Mac patches, so we
- // default to MIDI in those games to let them run.
+ // WORKAROUND: Default to MIDI in Amiga SCI1_EGA+ games as we don't support
+ // those patches yet. We also don't yet support the 7.pat file of SCI1+ Mac
+ // games or SCI0 Mac patches, so we default to MIDI in those games to let
+ // them run.
Common::Platform platform = g_sci->getPlatform();
-
- if (getSciVersion() >= SCI_VERSION_2 || platform == Common::kPlatformMacintosh || (platform == Common::kPlatformAmiga && getSciVersion() >= SCI_VERSION_1_EGA))
- midiType = MidiDriver::detectMusicDriver(MDT_PCSPK | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MIDI);
- else
- midiType = MidiDriver::detectMusicDriver(MDT_PCSPK | MDT_ADLIB | MDT_MIDI);
-
- switch (midiType) {
- case MD_ADLIB:
+ uint32 dev = MidiDriver::detectDevice(
+ (getSciVersion() >= SCI_VERSION_2 || platform == Common::kPlatformMacintosh ||
+ (platform == Common::kPlatformAmiga && getSciVersion() >= SCI_VERSION_1_EGA))
+ ? (MDT_PCSPK | MDT_ADLIB | MDT_MIDI | MDT_PREFER_GM)
+ : (MDT_PCSPK | MDT_ADLIB | MDT_MIDI));
+
+ switch (MidiDriver::getMusicType(dev)) {
+ case MT_ADLIB:
// FIXME: There's no Amiga sound option, so we hook it up to AdLib
if (g_sci->getPlatform() == Common::kPlatformAmiga)
_pMidiDrv = MidiPlayer_Amiga_create(_soundVersion);
else
_pMidiDrv = MidiPlayer_AdLib_create(_soundVersion);
break;
- case MD_PCJR:
+ case MT_PCJR:
_pMidiDrv = MidiPlayer_PCJr_create(_soundVersion);
break;
- case MD_PCSPK:
+ case MT_PCSPK:
_pMidiDrv = MidiPlayer_PCSpeaker_create(_soundVersion);
break;
default:
@@ -100,6 +104,49 @@ void SciMusic::init() {
}
_bMultiMidi = ConfMan.getBool("multi_midi");
+
+ // Find out what the first possible channel is (used, when doing channel
+ // remapping).
+ _driverFirstChannel = _pMidiDrv->getFirstChannel();
+}
+
+void SciMusic::miditimerCallback(void *p) {
+ SciMusic *sciMusic = (SciMusic *)p;
+
+ Common::StackLock lock(sciMusic->_mutex);
+ sciMusic->onTimer();
+}
+
+void SciMusic::onTimer() {
+ const MusicList::iterator end = _playList.end();
+ // sending out queued commands that were "sent" via main thread
+ sendMidiCommandsFromQueue();
+
+ for (MusicList::iterator i = _playList.begin(); i != end; ++i)
+ (*i)->onTimer();
+}
+
+void SciMusic::putMidiCommandInQueue(byte status, byte firstOp, byte secondOp) {
+ putMidiCommandInQueue(status | ((uint32)firstOp << 8) | ((uint32)secondOp << 16));
+}
+
+void SciMusic::putMidiCommandInQueue(uint32 midi) {
+ _queuedCommands.push_back(midi);
+}
+
+// This sends the stored commands from queue to driver (is supposed to get
+// called only during onTimer()). At least mt32 emulation doesn't like getting
+// note-on commands from main thread (if we directly send, we would get a crash
+// during piano scene in lsl5).
+void SciMusic::sendMidiCommandsFromQueue() {
+ uint curCommand = 0;
+ uint commandCount = _queuedCommands.size();
+
+ while (curCommand < commandCount) {
+ _pMidiDrv->send(_queuedCommands[curCommand]);
+ curCommand++;
+ }
+ _queuedCommands.clear();
}
void SciMusic::clearPlayList() {
@@ -119,22 +166,12 @@ void SciMusic::pauseAll(bool pause) {
}
void SciMusic::stopAll() {
- Common::StackLock lock(_mutex);
-
const MusicList::iterator end = _playList.end();
for (MusicList::iterator i = _playList.begin(); i != end; ++i) {
soundStop(*i);
}
}
-
-void SciMusic::miditimerCallback(void *p) {
- SciMusic *aud = (SciMusic *)p;
-
- Common::StackLock lock(aud->_mutex);
- aud->onTimer();
-}
-
void SciMusic::soundSetSoundOn(bool soundOnFlag) {
Common::StackLock lock(_mutex);
@@ -160,6 +197,24 @@ MusicEntry *SciMusic::getSlot(reg_t obj) {
return NULL;
}
+// We return the currently active music slot for SCI0
+MusicEntry *SciMusic::getActiveSci0MusicSlot() {
+ const MusicList::iterator end = _playList.end();
+ MusicEntry *highestPrioritySlot = NULL;
+ for (MusicList::iterator i = _playList.begin(); i != end; ++i) {
+ MusicEntry *playSlot = *i;
+ if (playSlot->pMidiParser) {
+ if (playSlot->status == kSoundPlaying)
+ return playSlot;
+ if (playSlot->status == kSoundPaused) {
+ if ((!highestPrioritySlot) || (highestPrioritySlot->priority < playSlot->priority))
+ highestPrioritySlot = playSlot;
+ }
+ }
+ }
+ return highestPrioritySlot;
+}
+
void SciMusic::setReverb(byte reverb) {
Common::StackLock lock(_mutex);
_pMidiDrv->setReverb(reverb);
@@ -174,28 +229,14 @@ void SciMusic::sortPlayList() {
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());
- // If MIDI device is selected but there is no digital track in sound resource
- // try to use adlib's digital sample if possible
- // Also, if the track couldn't be found, load the digital track, as some games
- // depend on this (e.g. the Longbow demo)
+ // If MIDI device is selected but there is no digital track in sound
+ // resource try to use adlib's digital sample if possible. Also, if the
+ // track couldn't be found, load the digital track, as some games depend on
+ // this (e.g. the Longbow demo).
if (!track || (_bMultiMidi && track->digitalChannelNr == -1)) {
SoundResource::Track *digital = pSnd->soundRes->getDigitalTrack();
if (digital)
@@ -224,49 +265,53 @@ void SciMusic::soundInitSnd(MusicEntry *pSnd) {
_mutex.lock();
pSnd->soundType = Audio::Mixer::kMusicSoundType;
if (pSnd->pMidiParser == NULL) {
- pSnd->pMidiParser = new MidiParser_SCI(_soundVersion);
+ pSnd->pMidiParser = new MidiParser_SCI(_soundVersion, this);
pSnd->pMidiParser->setMidiDriver(_pMidiDrv);
pSnd->pMidiParser->setTimerRate(_dwTempo);
}
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);
- // Fast forward to the last position and perform associated events when loading
- pSnd->pMidiParser->jumpToTick(pSnd->ticker, true);
+ pSnd->pMidiParser->mainThreadBegin();
+ pSnd->pMidiParser->loadMusic(track, pSnd, channelFilterMask, _soundVersion);
+ pSnd->pMidiParser->mainThreadEnd();
_mutex.unlock();
}
}
}
-void SciMusic::onTimer() {
- const MusicList::iterator end = _playList.end();
- for (MusicList::iterator i = _playList.begin(); i != end; ++i)
- (*i)->onTimer();
+// This one checks, if requested channel is available -> in that case give
+// caller that channel. Otherwise look for an unused one
+int16 SciMusic::tryToOwnChannel(MusicEntry *caller, int16 bestChannel) {
+ // Don't even try this for SCI0
+ if (_soundVersion <= SCI_VERSION_0_LATE)
+ return bestChannel;
+ if (!_usedChannel[bestChannel]) {
+ // currently unused, so give it to caller directly
+ _usedChannel[bestChannel] = caller;
+ return bestChannel;
+ }
+ // otherwise look for unused channel
+ for (int channelNr = _driverFirstChannel; channelNr < 15; channelNr++) {
+ if (!_usedChannel[channelNr]) {
+ _usedChannel[channelNr] = caller;
+ return channelNr;
+ }
+ }
+ error("no free channels");
+}
+
+void SciMusic::freeChannels(MusicEntry *caller) {
+ // Remove used channels
+ for (int i = 0; i < 15; i++) {
+ if (_usedChannel[i] == caller)
+ _usedChannel[i] = 0;
+ }
+ // Also tell midiparser, that he lost ownership
+ caller->pMidiParser->lostChannels();
}
void SciMusic::soundPlay(MusicEntry *pSnd) {
@@ -294,7 +339,7 @@ void SciMusic::soundPlay(MusicEntry *pSnd) {
if ((_soundVersion <= SCI_VERSION_0_LATE) && (alreadyPlaying)) {
// Music already playing in SCI0?
if (pSnd->priority > alreadyPlaying->priority) {
- // And new priority higher? pause previous music and play new one immediately
+ // And new priority higher? pause previous music and play new one immediately.
// Example of such case: lsl3, when getting points (jingle is played then)
soundPause(alreadyPlaying);
alreadyPlaying->isQueued = true;
@@ -317,34 +362,54 @@ void SciMusic::soundPlay(MusicEntry *pSnd) {
pSnd->pLoopStream, -1, pSnd->volume, 0,
DisposeAfterUse::NO);
} else {
+ // Rewind in case we play the same sample multiple times
+ // (non-looped) like in pharkas right at the start
+ pSnd->pStreamAud->rewind();
_pMixer->playStream(pSnd->soundType, &pSnd->hCurrentAud,
pSnd->pStreamAud, -1, pSnd->volume, 0,
DisposeAfterUse::NO);
}
} else {
- _mutex.lock();
if (pSnd->pMidiParser) {
- pSnd->pMidiParser->setVolume(pSnd->volume);
+ _mutex.lock();
+ pSnd->pMidiParser->mainThreadBegin();
+ pSnd->pMidiParser->tryToOwnChannels();
if (pSnd->status == kSoundStopped)
+ pSnd->pMidiParser->sendInitCommands();
+ pSnd->pMidiParser->setVolume(pSnd->volume);
+ if (pSnd->status == kSoundStopped) {
pSnd->pMidiParser->jumpToTick(0);
+ } else {
+ // Fast forward to the last position and perform associated events when loading
+ pSnd->pMidiParser->jumpToTick(pSnd->ticker, true);
+ }
+ pSnd->pMidiParser->mainThreadEnd();
+ _mutex.unlock();
}
- _mutex.unlock();
}
pSnd->status = kSoundPlaying;
}
void SciMusic::soundStop(MusicEntry *pSnd) {
+ SoundStatus previousStatus = pSnd->status;
pSnd->status = kSoundStopped;
if (_soundVersion <= SCI_VERSION_0_LATE)
pSnd->isQueued = false;
if (pSnd->pStreamAud)
_pMixer->stopHandle(pSnd->hCurrentAud);
- _mutex.lock();
- if (pSnd->pMidiParser)
- pSnd->pMidiParser->stop();
- _mutex.unlock();
+ if (pSnd->pMidiParser) {
+ _mutex.lock();
+ pSnd->pMidiParser->mainThreadBegin();
+ // We shouldn't call stop in case it's paused, otherwise we would send
+ // allNotesOff() again
+ if (previousStatus == kSoundPlaying)
+ pSnd->pMidiParser->stop();
+ freeChannels(pSnd);
+ pSnd->pMidiParser->mainThreadEnd();
+ _mutex.unlock();
+ }
}
void SciMusic::soundSetVolume(MusicEntry *pSnd, byte volume) {
@@ -353,7 +418,9 @@ void SciMusic::soundSetVolume(MusicEntry *pSnd, byte volume) {
_pMixer->setChannelVolume(pSnd->hCurrentAud, volume * 2); // Mixer is 0-255, SCI is 0-127
} else if (pSnd->pMidiParser) {
_mutex.lock();
+ pSnd->pMidiParser->mainThreadBegin();
pSnd->pMidiParser->setVolume(volume);
+ pSnd->pMidiParser->mainThreadEnd();
_mutex.unlock();
}
}
@@ -368,13 +435,15 @@ void SciMusic::soundSetPriority(MusicEntry *pSnd, byte prio) {
void SciMusic::soundKill(MusicEntry *pSnd) {
pSnd->status = kSoundStopped;
- _mutex.lock();
if (pSnd->pMidiParser) {
+ _mutex.lock();
+ pSnd->pMidiParser->mainThreadBegin();
pSnd->pMidiParser->unloadMusic();
+ pSnd->pMidiParser->mainThreadEnd();
delete pSnd->pMidiParser;
pSnd->pMidiParser = NULL;
+ _mutex.unlock();
}
- _mutex.unlock();
if (pSnd->pStreamAud) {
_pMixer->stopHandle(pSnd->hCurrentAud);
@@ -406,10 +475,14 @@ void SciMusic::soundPause(MusicEntry *pSnd) {
if (pSnd->pStreamAud) {
_pMixer->pauseHandle(pSnd->hCurrentAud, true);
} else {
- _mutex.lock();
- if (pSnd->pMidiParser)
+ if (pSnd->pMidiParser) {
+ _mutex.lock();
+ pSnd->pMidiParser->mainThreadBegin();
pSnd->pMidiParser->pause();
- _mutex.unlock();
+ freeChannels(pSnd);
+ pSnd->pMidiParser->mainThreadEnd();
+ _mutex.unlock();
+ }
}
}
@@ -448,6 +521,21 @@ void SciMusic::soundSetMasterVolume(uint16 vol) {
_pMidiDrv->setVolume(vol);
}
+void SciMusic::sendMidiCommand(uint32 cmd) {
+ Common::StackLock lock(_mutex);
+ _pMidiDrv->send(cmd);
+}
+
+void SciMusic::sendMidiCommand(MusicEntry *pSnd, uint32 cmd) {
+ Common::StackLock lock(_mutex);
+ if (!pSnd->pMidiParser)
+ error("tried to cmdSendMidi on non midi slot (%04x:%04x)", PRINT_REG(pSnd->soundObj));
+
+ pSnd->pMidiParser->mainThreadBegin();
+ pSnd->pMidiParser->sendFromScriptToDriver(cmd);
+ pSnd->pMidiParser->mainThreadEnd();
+}
+
void SciMusic::printPlayList(Console *con) {
Common::StackLock lock(_mutex);
@@ -567,14 +655,6 @@ void MusicEntry::doFade() {
fadeStep = 0;
fadeCompleted = true;
}
-#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) {
diff --git a/engines/sci/sound/music.h b/engines/sci/sound/music.h
index 83cd59e89b..943a5bd2a8 100644
--- a/engines/sci/sound/music.h
+++ b/engines/sci/sound/music.h
@@ -26,15 +26,11 @@
#ifndef SCI_MUSIC_H
#define SCI_MUSIC_H
-#ifndef USE_OLD_MUSIC_FUNCTIONS
#include "common/serializer.h"
-#endif
#include "common/mutex.h"
#include "sound/mixer.h"
#include "sound/audiostream.h"
-//#include "sound/mididrv.h"
-//#include "sound/midiparser.h"
#include "sci/sci.h"
#include "sci/resource.h"
@@ -55,11 +51,7 @@ enum SoundStatus {
class MidiParser_SCI;
class SegManager;
-class MusicEntry
-#ifndef USE_OLD_MUSIC_FUNCTIONS
- : public Common::Serializable
-#endif
-{
+class MusicEntry : public Common::Serializable {
public:
// Do not get these directly for the sound objects!
// It's a bad idea, as the sound code (i.e. the SciMusic
@@ -96,9 +88,6 @@ public:
Audio::Mixer::SoundType soundType;
-#ifndef USE_OLD_MUSIC_FUNCTIONS
-//protected:
-#endif
MidiParser_SCI *pMidiParser;
// TODO: We need to revise how we store the different
@@ -114,25 +103,28 @@ public:
void doFade();
void onTimer();
-#ifndef USE_OLD_MUSIC_FUNCTIONS
virtual void saveLoadWithSerializer(Common::Serializer &ser);
-#endif
};
typedef Common::Array<MusicEntry *> MusicList;
+typedef Common::Array<uint32> MidiCommandQueue;
-class SciMusic
-#ifndef USE_OLD_MUSIC_FUNCTIONS
- : public Common::Serializable
-#endif
-{
+class SciMusic : public Common::Serializable {
public:
SciMusic(SciVersion soundVersion);
~SciMusic();
void init();
+
void onTimer();
+ void putMidiCommandInQueue(byte status, byte firstOp, byte secondOp);
+ void putMidiCommandInQueue(uint32 midi);
+private:
+ static void miditimerCallback(void *p);
+ void sendMidiCommandsFromQueue();
+
+public:
void clearPlayList();
void pauseAll(bool pause);
void stopAll();
@@ -165,6 +157,7 @@ public:
}
MusicEntry *getSlot(reg_t obj);
+ MusicEntry *getActiveSci0MusicSlot();
void pushBackSlot(MusicEntry *slotEntry) {
Common::StackLock lock(_mutex);
@@ -179,16 +172,12 @@ public:
MusicList::iterator getPlayListStart() { return _playList.begin(); }
MusicList::iterator getPlayListEnd() { return _playList.end(); }
- void sendMidiCommand(uint32 cmd) {
- Common::StackLock lock(_mutex);
- _pMidiDrv->send(cmd);
- }
+ void sendMidiCommand(uint32 cmd);
+ void sendMidiCommand(MusicEntry *pSnd, uint32 cmd);
void setReverb(byte reverb);
-#ifndef USE_OLD_MUSIC_FUNCTIONS
virtual void saveLoadWithSerializer(Common::Serializer &ser);
-#endif
// Mutex for music code. Used to guard access to the song playlist, to the
// MIDI parser and to the MIDI driver/player. Note that guarded code must NOT
@@ -196,6 +185,9 @@ public:
// where a deadlock can occur
Common::Mutex _mutex;
+ int16 tryToOwnChannel(MusicEntry *caller, int16 bestChannel);
+ void freeChannels(MusicEntry *caller);
+
protected:
void sortPlayList();
@@ -208,22 +200,16 @@ protected:
// Mixed AdLib/MIDI mode: when enabled from the ScummVM sound options screen,
// and a sound has a digital track, the sound from the AdLib track is played
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;
- }
+private:
MusicList _playList;
bool _soundOn;
byte _masterVolume;
- bool _usedChannels[16];
+ MusicEntry *_usedChannel[16];
+
+ MidiCommandQueue _queuedCommands;
+
+ int _driverFirstChannel;
};
} // End of namespace Sci
diff --git a/engines/sci/sound/soundcmd.cpp b/engines/sci/sound/soundcmd.cpp
index ece4c1430c..51832af09f 100644
--- a/engines/sci/sound/soundcmd.cpp
+++ b/engines/sci/sound/soundcmd.cpp
@@ -23,17 +23,12 @@
*
*/
-#include "sci/sci.h" // for USE_OLD_MUSIC_FUNCTIONS
-
-#ifdef USE_OLD_MUSIC_FUNCTIONS
-#include "sci/sound/iterator/iterator.h" // for SongIteratorStatus
-#endif
-
#include "common/config-manager.h"
#include "sci/sound/audio.h"
#include "sci/sound/music.h"
#include "sci/sound/soundcmd.h"
+#include "sci/engine/kernel.h"
#include "sci/engine/selector.h"
namespace Sci {
@@ -41,258 +36,29 @@ namespace Sci {
#define SCI1_SOUND_FLAG_MAY_PAUSE 1 /* Only here for completeness; The interpreter doesn't touch this bit */
#define SCI1_SOUND_FLAG_SCRIPTED_PRI 2 /* but does touch this */
-#ifdef USE_OLD_MUSIC_FUNCTIONS
-#define FROBNICATE_HANDLE(reg) ((reg).segment << 16 | (reg).offset)
-#define DEFROBNICATE_HANDLE(handle) (make_reg((handle >> 16) & 0xffff, handle & 0xffff))
-#endif
-
-#define SOUNDCOMMAND(x) _soundCommands.push_back(new MusicEntryCommand(#x, &SoundCommandParser::x))
-
-#ifdef USE_OLD_MUSIC_FUNCTIONS
-static void script_set_priority(ResourceManager *resMan, SegManager *segMan, SfxState *state, reg_t obj, int priority) {
- int song_nr = readSelectorValue(segMan, obj, SELECTOR(number));
- Resource *song = resMan->findResource(ResourceId(kResourceTypeSound, song_nr), 0);
- int flags = readSelectorValue(segMan, obj, SELECTOR(flags));
-
- if (priority == -1) {
- if (song->data[0] == 0xf0)
- priority = song->data[1];
- else
- warning("Attempt to unset song priority when there is no built-in value");
-
- flags &= ~SCI1_SOUND_FLAG_SCRIPTED_PRI;
- } else flags |= SCI1_SOUND_FLAG_SCRIPTED_PRI;
-
- state->sfx_song_renice(FROBNICATE_HANDLE(obj), priority);
- writeSelectorValue(segMan, obj, SELECTOR(flags), flags);
-}
-
-SongIterator *build_iterator(ResourceManager *resMan, int song_nr, SongIteratorType type, songit_id_t id) {
- Resource *song = resMan->findResource(ResourceId(kResourceTypeSound, song_nr), 0);
-
- if (!song)
- return NULL;
-
- return songit_new(song->data, song->size, type, id);
-}
-
-void process_sound_events(EngineState *s) { /* Get all sound events, apply their changes to the heap */
- int result;
- SongHandle handle;
- int cue;
- SegManager *segMan = s->_segMan;
-
- if (getSciVersion() > SCI_VERSION_01)
- return;
- // SCI1 and later explicitly poll for everything
-
- while ((result = s->_sound.sfx_poll(&handle, &cue))) {
- reg_t obj = DEFROBNICATE_HANDLE(handle);
- if (!s->_segMan->isObject(obj)) {
- warning("Non-object %04x:%04x received sound signal (%d/%d)", PRINT_REG(obj), result, cue);
- return;
- }
-
- switch (result) {
-
- case SI_LOOP:
- debugC(2, kDebugLevelSound, "[process-sound] Song %04x:%04x looped (to %d)",
- PRINT_REG(obj), cue);
- /* 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);
- 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);
- writeSelectorValue(segMan, obj, SELECTOR(signal), cue);
- break;
-
- case SI_FINISHED:
- debugC(2, kDebugLevelSound, "[process-sound] Song %04x:%04x finished",
- PRINT_REG(obj));
- writeSelectorValue(segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
- writeSelectorValue(segMan, obj, SELECTOR(state), kSoundStopped);
- break;
-
- default:
- warning("Unexpected result from sfx_poll: %d", result);
- break;
- }
- }
-}
-
-#endif
SoundCommandParser::SoundCommandParser(ResourceManager *resMan, SegManager *segMan, Kernel *kernel, AudioPlayer *audio, SciVersion soundVersion) :
_resMan(resMan), _segMan(segMan), _kernel(kernel), _audio(audio), _soundVersion(soundVersion) {
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- // The following hack is needed to ease the change from old to new sound code (because the new sound code does not use SfxState)
- _state = &g_sci->getEngineState()->_sound; // HACK
-#endif
-
- #ifndef USE_OLD_MUSIC_FUNCTIONS
- _music = new SciMusic(_soundVersion);
- _music->init();
- #endif
-
- switch (_soundVersion) {
- case SCI_VERSION_0_EARLY:
- case SCI_VERSION_0_LATE:
- SOUNDCOMMAND(cmdInitSound);
- SOUNDCOMMAND(cmdPlaySound);
- SOUNDCOMMAND(cmdDummy);
- SOUNDCOMMAND(cmdDisposeSound);
- SOUNDCOMMAND(cmdMuteSound);
- SOUNDCOMMAND(cmdStopSound);
- SOUNDCOMMAND(cmdPauseSound);
- SOUNDCOMMAND(cmdResumeSound);
- SOUNDCOMMAND(cmdMasterVolume);
- SOUNDCOMMAND(cmdUpdateSound);
- SOUNDCOMMAND(cmdFadeSound);
- SOUNDCOMMAND(cmdGetPolyphony);
- SOUNDCOMMAND(cmdStopAllSounds);
- _cmdUpdateCuesIndex = -1;
- break;
- case SCI_VERSION_1_EARLY:
- SOUNDCOMMAND(cmdMasterVolume);
- SOUNDCOMMAND(cmdMuteSound);
- SOUNDCOMMAND(cmdDummy);
- SOUNDCOMMAND(cmdGetPolyphony);
- SOUNDCOMMAND(cmdUpdateSound);
- SOUNDCOMMAND(cmdInitSound);
- SOUNDCOMMAND(cmdDisposeSound);
- SOUNDCOMMAND(cmdPlaySound);
- SOUNDCOMMAND(cmdStopSound);
- SOUNDCOMMAND(cmdPauseSound);
- SOUNDCOMMAND(cmdFadeSound);
- SOUNDCOMMAND(cmdUpdateCues);
- SOUNDCOMMAND(cmdSendMidi);
- SOUNDCOMMAND(cmdReverb);
- SOUNDCOMMAND(cmdSetSoundHold);
- _cmdUpdateCuesIndex = 11;
- break;
- case SCI_VERSION_1_LATE:
- SOUNDCOMMAND(cmdMasterVolume);
- SOUNDCOMMAND(cmdMuteSound);
- SOUNDCOMMAND(cmdDummy);
- SOUNDCOMMAND(cmdGetPolyphony);
- SOUNDCOMMAND(cmdGetAudioCapability);
- SOUNDCOMMAND(cmdSuspendSound);
- SOUNDCOMMAND(cmdInitSound);
- SOUNDCOMMAND(cmdDisposeSound);
- SOUNDCOMMAND(cmdPlaySound);
- SOUNDCOMMAND(cmdStopSound);
- SOUNDCOMMAND(cmdPauseSound);
- SOUNDCOMMAND(cmdFadeSound);
- SOUNDCOMMAND(cmdSetSoundHold);
- SOUNDCOMMAND(cmdDummy);
- SOUNDCOMMAND(cmdSetSoundVolume);
- SOUNDCOMMAND(cmdSetSoundPriority);
- SOUNDCOMMAND(cmdSetSoundLoop);
- SOUNDCOMMAND(cmdUpdateCues);
- SOUNDCOMMAND(cmdSendMidi);
- SOUNDCOMMAND(cmdReverb);
- SOUNDCOMMAND(cmdUpdateSound);
- _cmdUpdateCuesIndex = 17;
- break;
- default:
- warning("Sound command parser: unknown sound version %d", _soundVersion);
- break;
- }
+ _music = new SciMusic(_soundVersion);
+ _music->init();
}
SoundCommandParser::~SoundCommandParser() {
- for (SoundCommandContainer::iterator i = _soundCommands.begin(); i != _soundCommands.end(); ++i)
- delete *i;
-
-#ifndef USE_OLD_MUSIC_FUNCTIONS
delete _music;
-#endif
}
-reg_t SoundCommandParser::parseCommand(int argc, reg_t *argv, reg_t acc) {
- uint16 command = argv[0].toUint16();
- reg_t obj = (argc > 1) ? argv[1] : NULL_REG;
- int16 value = (argc > 2) ? argv[2].toSint16() : 0;
- _acc = acc;
- _argc = argc;
- _argv = argv;
-
- if (argc == 6) { // cmdSendMidi
- byte channel = argv[2].toUint16() & 0xf;
- byte midiCmd = argv[3].toUint16() & 0xff;
-
- uint16 controller = argv[4].toUint16();
- uint16 param = argv[5].toUint16();
-
- _midiCommand = (channel | midiCmd) | ((uint32)controller << 8) | ((uint32)param << 16);
- }
-
- if (command < _soundCommands.size()) {
- if (command != _cmdUpdateCuesIndex) {
- //printf("%s, object %04x:%04x\n", _soundCommands[command]->desc, PRINT_REG(obj)); // debug
- debugC(2, kDebugLevelSound, "%s, object %04x:%04x", _soundCommands[command]->desc, PRINT_REG(obj));
- }
-
- (this->*(_soundCommands[command]->sndCmd))(obj, value);
- } else {
- warning("Invalid sound command requested (%d), valid range is 0-%d", command, _soundCommands.size() - 1);
- }
-
- return _acc;
+reg_t SoundCommandParser::kDoSoundInit(int argc, reg_t *argv, reg_t acc) {
+ processInitSound(argv[0]);
+ return acc;
}
-void SoundCommandParser::cmdInitSound(reg_t obj, int16 value) {
- if (!obj.segment)
- return;
-
+void SoundCommandParser::processInitSound(reg_t obj) {
int resourceId = readSelectorValue(_segMan, obj, SELECTOR(number));
-#ifdef USE_OLD_MUSIC_FUNCTIONS
-
- SongHandle handle = FROBNICATE_HANDLE(obj);
-
- if (_soundVersion != SCI_VERSION_1_LATE) {
- if (!obj.segment)
- return;
- }
-
- SongIteratorType type = (_soundVersion <= SCI_VERSION_0_LATE) ? SCI_SONG_ITERATOR_TYPE_SCI0 : SCI_SONG_ITERATOR_TYPE_SCI1;
-
- if (_soundVersion <= SCI_VERSION_0_LATE) {
- if (readSelectorValue(_segMan, obj, SELECTOR(nodePtr))) {
- _state->sfx_song_set_status(handle, SOUND_STATUS_STOPPED);
- _state->sfx_remove_song(handle);
- }
- }
-
- if (!obj.segment || !_resMan->testResource(ResourceId(kResourceTypeSound, resourceId)))
- return;
-
- _state->sfx_add_song(build_iterator(_resMan, resourceId, type, handle), 0, handle, resourceId);
-
-
- // Notify the engine
- if (_soundVersion <= SCI_VERSION_0_LATE)
- writeSelectorValue(_segMan, obj, SELECTOR(state), kSoundInitialized);
- else
- writeSelector(_segMan, obj, SELECTOR(nodePtr), obj);
-
- writeSelector(_segMan, obj, SELECTOR(handle), obj);
-
-#else
-
// Check if a track with the same sound object is already playing
MusicEntry *oldSound = _music->getSlot(obj);
if (oldSound)
- cmdDisposeSound(obj, value);
+ processDisposeSound(obj);
MusicEntry *newSound = new MusicEntry();
newSound->resourceId = resourceId;
@@ -307,6 +73,9 @@ void SoundCommandParser::cmdInitSound(reg_t obj, int16 value) {
if (_soundVersion >= SCI_VERSION_1_EARLY)
newSound->volume = CLIP<int>(readSelectorValue(_segMan, obj, SELECTOR(vol)), 0, MUSIC_VOLUME_MAX);
+ debugC(2, kDebugLevelSound, "kDoSound(init): number %d, loop %d, prio %d, vol %d", resourceId,
+ newSound->loop, newSound->priority, newSound->volume);
+
// 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
@@ -333,98 +102,28 @@ void SoundCommandParser::cmdInitSound(reg_t obj, int16 value) {
writeSelector(_segMan, obj, SELECTOR(handle), obj);
}
-#endif
-
}
-void SoundCommandParser::cmdPlaySound(reg_t obj, int16 value) {
- if (!obj.segment)
- return;
-
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- SongHandle handle = FROBNICATE_HANDLE(obj);
-
- if (_soundVersion <= SCI_VERSION_0_LATE) {
- _state->sfx_song_set_status(handle, SOUND_STATUS_PLAYING);
- _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, 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);
- writeSelectorValue(_segMan, obj, SELECTOR(signal), 0);
- } else if (_soundVersion == SCI_VERSION_1_LATE) {
- 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 = readSelectorValue(_segMan, obj, SELECTOR(number));
-
- if (readSelectorValue(_segMan, obj, SELECTOR(nodePtr)) && (song && songNumber != song->_resourceNum)) {
- _state->sfx_song_set_status(handle, SOUND_STATUS_STOPPED);
- _state->sfx_remove_song(handle);
- writeSelector(_segMan, obj, SELECTOR(nodePtr), NULL_REG);
- }
-
- 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
- // effects map)
- if (_resMan->testResource(ResourceId(kResourceTypeAudio, songNumber)) &&
- getSciVersion() >= SCI_VERSION_1_1) {
- // Found a relevant audio resource, play it
- _audio->stopAudio();
- warning("Initializing audio resource instead of requested sound resource %d", songNumber);
- sampleLen = _audio->startAudio(65535, songNumber);
- // Also create iterator, that will fire SI_FINISHED event, when the sound is done playing
- _state->sfx_add_song(new_timer_iterator(sampleLen), 0, handle, songNumber);
- } else {
- if (!_resMan->testResource(ResourceId(kResourceTypeSound, songNumber))) {
- 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);
- writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
- return;
- }
- debugC(2, kDebugLevelSound, "Initializing song number %d", songNumber);
- _state->sfx_add_song(build_iterator(_resMan, songNumber, SCI_SONG_ITERATOR_TYPE_SCI1,
- handle), 0, handle, songNumber);
- }
-
- 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);
- writeSelectorValue(_segMan, obj, SELECTOR(signal), 0);
- }
- }
-
-#else
+reg_t SoundCommandParser::kDoSoundPlay(int argc, reg_t *argv, reg_t acc) {
+ processPlaySound(argv[0]);
+ return acc;
+}
+void SoundCommandParser::processPlaySound(reg_t obj) {
MusicEntry *musicSlot = _music->getSlot(obj);
if (!musicSlot) {
- warning("cmdPlaySound: Slot not found (%04x:%04x)", PRINT_REG(obj));
+ warning("kDoSound(play): Slot not found (%04x:%04x)", PRINT_REG(obj));
return;
}
int resourceId = obj.segment ? readSelectorValue(_segMan, obj, SELECTOR(number)) : -1;
if (musicSlot->resourceId != resourceId) { // another sound loaded into struct
- cmdDisposeSound(obj, value);
- cmdInitSound(obj, value);
+ processDisposeSound(obj);
+ processInitSound(obj);
// Find slot again :)
musicSlot = _music->getSlot(obj);
}
- int16 loop = readSelectorValue(_segMan, obj, SELECTOR(loop));
- debugC(2, kDebugLevelSound, "cmdPlaySound: resource number %d, loop %d", resourceId, loop);
writeSelector(_segMan, obj, SELECTOR(handle), obj);
@@ -442,51 +141,31 @@ void SoundCommandParser::cmdPlaySound(reg_t obj, int16 value) {
musicSlot->priority = readSelectorValue(_segMan, obj, SELECTOR(priority));
if (_soundVersion >= SCI_VERSION_1_EARLY)
musicSlot->volume = readSelectorValue(_segMan, obj, SELECTOR(vol));
- _music->soundPlay(musicSlot);
-#endif
+ debugC(2, kDebugLevelSound, "kDoSound(play): number %d, loop %d, prio %d, vol %d", resourceId,
+ musicSlot->loop, musicSlot->priority, musicSlot->volume);
+ _music->soundPlay(musicSlot);
}
-void SoundCommandParser::cmdDummy(reg_t obj, int16 value) {
+reg_t SoundCommandParser::kDoSoundDummy(int argc, reg_t *argv, reg_t acc) {
warning("cmdDummy invoked"); // not supposed to occur
+ return acc;
}
-#ifdef USE_OLD_MUSIC_FUNCTIONS
-void SoundCommandParser::changeSoundStatus(reg_t obj, int newStatus) {
- SongHandle handle = FROBNICATE_HANDLE(obj);
- if (obj.segment) {
- _state->sfx_song_set_status(handle, newStatus);
- if (_soundVersion <= SCI_VERSION_0_LATE)
- writeSelectorValue(_segMan, obj, SELECTOR(state), newStatus);
- }
+reg_t SoundCommandParser::kDoSoundDispose(int argc, reg_t *argv, reg_t acc) {
+ processDisposeSound(argv[0]);
+ return acc;
}
-#endif
-
-void SoundCommandParser::cmdDisposeSound(reg_t obj, int16 value) {
- if (!obj.segment)
- return;
-
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- SongHandle handle = FROBNICATE_HANDLE(obj);
- changeSoundStatus(obj, SOUND_STATUS_STOPPED);
-
- if (obj.segment) {
- _state->sfx_remove_song(handle);
-
- if (_soundVersion <= SCI_VERSION_0_LATE)
- writeSelectorValue(_segMan, obj, SELECTOR(handle), 0x0000);
- }
-
-#else
+void SoundCommandParser::processDisposeSound(reg_t obj) {
MusicEntry *musicSlot = _music->getSlot(obj);
if (!musicSlot) {
- warning("cmdDisposeSound: Slot not found (%04x:%04x)", PRINT_REG(obj));
+ warning("kDoSound(dispose): Slot not found (%04x:%04x)", PRINT_REG(obj));
return;
}
- cmdStopSound(obj, value);
+ processStopSound(obj, false);
_music->soundKill(musicSlot);
writeSelectorValue(_segMan, obj, SELECTOR(handle), 0);
@@ -494,26 +173,17 @@ void SoundCommandParser::cmdDisposeSound(reg_t obj, int16 value) {
writeSelector(_segMan, obj, SELECTOR(nodePtr), NULL_REG);
else
writeSelectorValue(_segMan, obj, SELECTOR(state), kSoundStopped);
-#endif
}
-void SoundCommandParser::cmdStopSound(reg_t obj, int16 value) {
- processStopSound(obj, value, false);
+reg_t SoundCommandParser::kDoSoundStop(int argc, reg_t *argv, reg_t acc) {
+ processStopSound(argv[0], false);
+ return acc;
}
-void SoundCommandParser::processStopSound(reg_t obj, int16 value, bool sampleFinishedPlaying) {
- if (!obj.segment)
- return;
-
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- changeSoundStatus(obj, SOUND_STATUS_STOPPED);
-
- if (_soundVersion >= SCI_VERSION_1_EARLY)
- writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
-#else
+void SoundCommandParser::processStopSound(reg_t obj, bool sampleFinishedPlaying) {
MusicEntry *musicSlot = _music->getSlot(obj);
if (!musicSlot) {
- warning("cmdStopSound: Slot not found (%04x:%04x)", PRINT_REG(obj));
+ warning("kDoSound(stop): Slot not found (%04x:%04x)", PRINT_REG(obj));
return;
}
@@ -523,204 +193,156 @@ void SoundCommandParser::processStopSound(reg_t obj, int16 value, bool sampleFin
writeSelectorValue(_segMan, obj, SELECTOR(handle), 0);
}
- // Set signal selector in sound SCI0 games only, when the sample has finished playing
- // If we don't set it at all, we get a problem when using vaporizer on the 2 guys
- // If we set it all the time, we get no music in sq3new and kq1
- // FIXME: this *may* be wrong, it's impossible to find out in sierra DOS sci, because SCI0 under DOS didn't have
- // sfx drivers included
- // We need to set signal in sound SCI1+ games all the time
+ // Set signal selector in sound SCI0 games only, when the sample has
+ // finished playing. If we don't set it at all, we get a problem when using
+ // vaporizer on the 2 guys. If we set it all the time, we get no music in
+ // sq3new and kq1.
+ // FIXME: This *may* be wrong, it's impossible to find out in Sierra DOS
+ // SCI, because SCI0 under DOS didn't have sfx drivers included.
+ // We need to set signal in sound SCI1+ games all the time.
if ((_soundVersion > SCI_VERSION_0_LATE) || sampleFinishedPlaying)
writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
musicSlot->dataInc = 0;
musicSlot->signal = 0;
_music->soundStop(musicSlot);
-#endif
}
-void SoundCommandParser::cmdPauseSound(reg_t obj, int16 value) {
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- if (!obj.segment)
- return;
-
- if (_soundVersion <= SCI_VERSION_0_LATE)
- changeSoundStatus(obj, SOUND_STATUS_SUSPENDED);
- else
- changeSoundStatus(obj, value ? SOUND_STATUS_SUSPENDED : SOUND_STATUS_PLAYING);
-#else
+reg_t SoundCommandParser::kDoSoundPause(int argc, reg_t *argv, reg_t acc) {
+ if (_soundVersion <= SCI_VERSION_0_LATE) {
+ // SCI0 games give us 0/1 for either resuming or pausing the current music
+ // this one doesn't count, so pausing 2 times and resuming once means here that we are supposed to resume
+ uint16 value = argv[0].toUint16();
+ MusicEntry *musicSlot = _music->getActiveSci0MusicSlot();
+ switch (value) {
+ case 1:
+ if ((musicSlot) && (musicSlot->status == kSoundPlaying)) {
+ _music->soundPause(musicSlot);
+ writeSelectorValue(_segMan, musicSlot->soundObj, SELECTOR(state), kSoundPaused);
+ }
+ return make_reg(0, 0);
+ case 0:
+ if ((musicSlot) && (musicSlot->status == kSoundPaused)) {
+ _music->soundResume(musicSlot);
+ writeSelectorValue(_segMan, musicSlot->soundObj, SELECTOR(state), kSoundPlaying);
+ return make_reg(0, 1);
+ }
+ return make_reg(0, 0);
+ default:
+ error("kDoSound(pause): parameter 0 is invalid for sound-sci0");
+ }
+ }
+ reg_t obj = argv[0];
+ uint16 value = argc > 1 ? argv[1].toUint16() : 0;
if (!obj.segment) { // pause the whole playlist
- // Pausing/Resuming the whole playlist was introduced
- // in the SCI1 late sound scheme
- if (_soundVersion <= SCI_VERSION_1_EARLY)
- return;
-
_music->pauseAll(value);
} else { // pause a playlist slot
MusicEntry *musicSlot = _music->getSlot(obj);
if (!musicSlot) {
- warning("cmdPauseSound: Slot not found (%04x:%04x)", PRINT_REG(obj));
- return;
+ warning("kDoSound(pause): Slot not found (%04x:%04x)", PRINT_REG(obj));
+ return acc;
}
- if (_soundVersion <= SCI_VERSION_0_LATE) {
- // Always pause the sound in SCI0 games. It's resumed in cmdResumeSound()
- writeSelectorValue(_segMan, musicSlot->soundObj, SELECTOR(state), kSoundPaused);
- _music->soundPause(musicSlot);
- } else {
- _music->soundToggle(musicSlot, value);
- }
+ _music->soundToggle(musicSlot, value);
}
-
-#endif
+ return acc;
}
-void SoundCommandParser::cmdResumeSound(reg_t obj, int16 value) {
- // SCI0 only command
-
- if (!obj.segment)
- return;
+// SCI0 only command
+reg_t SoundCommandParser::kDoSoundResume(int argc, reg_t *argv, reg_t acc) {
+ // this doesn't seem to do what we think it's doing
+ // it's called with no arguments at all (just restore a game in qfg1)
+ return acc;
+ reg_t obj = argv[0];
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- changeSoundStatus(obj, SOUND_STATUS_PLAYING);
-#else
MusicEntry *musicSlot = _music->getSlot(obj);
if (!musicSlot) {
- warning("cmdResumeSound: Slot not found (%04x:%04x)", PRINT_REG(obj));
- return;
+ warning("kDoSound(resume):: Slot not found (%04x:%04x)", PRINT_REG(obj));
+ return acc;
}
writeSelectorValue(_segMan, musicSlot->soundObj, SELECTOR(state), kSoundPlaying);
_music->soundResume(musicSlot);
-#endif
+ return acc;
}
-void SoundCommandParser::cmdMuteSound(reg_t obj, int16 value) {
-#ifndef USE_OLD_MUSIC_FUNCTIONS
- if (_argc > 1) // the first parameter is the sound command
- _music->soundSetSoundOn(obj.toUint16());
- _acc = make_reg(0, _music->soundGetSoundOn());
-#endif
+reg_t SoundCommandParser::kDoSoundMute(int argc, reg_t *argv, reg_t acc) {
+ if (argc > 0)
+ _music->soundSetSoundOn(argv[0].toUint16());
+ return make_reg(0, _music->soundGetSoundOn());
}
-void SoundCommandParser::cmdMasterVolume(reg_t obj, int16 value) {
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- _acc = make_reg(0, _state->sfx_getVolume());
-
- if (obj != SIGNAL_REG)
- _state->sfx_setVolume(obj.toSint16());
-#else
- debugC(2, kDebugLevelSound, "cmdMasterVolume: %d", value);
- _acc = make_reg(0, _music->soundGetMasterVolume());
+reg_t SoundCommandParser::kDoSoundMasterVolume(int argc, reg_t *argv, reg_t acc) {
+ acc = make_reg(0, _music->soundGetMasterVolume());
- if (_argc > 1) { // the first parameter is the sound command
- int vol = CLIP<int16>(obj.toSint16(), 0, kMaxSciVolume);
+ if (argc > 0) {
+ debugC(2, kDebugLevelSound, "kDoSound(masterVolume): %d", argv[0].toSint16());
+ int vol = CLIP<int16>(argv[0].toSint16(), 0, kMaxSciVolume);
vol = vol * Audio::Mixer::kMaxMixerVolume / kMaxSciVolume;
ConfMan.setInt("music_volume", vol);
ConfMan.setInt("sfx_volume", vol);
g_engine->syncSoundSettings();
}
-#endif
+ return acc;
}
-void SoundCommandParser::cmdFadeSound(reg_t obj, int16 value) {
- if (!obj.segment)
- return;
+reg_t SoundCommandParser::kDoSoundFade(int argc, reg_t *argv, reg_t acc) {
+ reg_t obj = argv[0];
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- SongHandle handle = FROBNICATE_HANDLE(obj);
- if (_soundVersion != SCI_VERSION_1_LATE) {
- /* 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)
- 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();
- fade.ticks_per_step = _argv[3].toUint16();
- fade.step_size = _argv[4].toUint16();
- fade.action = _argv[5].toUint16() ?
- FADE_ACTION_FADE_AND_STOP :
- FADE_ACTION_FADE_AND_CONT;
-
- _state->sfx_song_set_fade(handle, &fade);
-
- /* FIXME: The next couple of lines actually STOP the handle, rather
- ** than fading it! */
- if (_argv[5].toUint16()) {
- 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.
- writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
- }
- }
-#else
MusicEntry *musicSlot = _music->getSlot(obj);
if (!musicSlot) {
- warning("cmdFadeSound: Slot not found (%04x:%04x)", PRINT_REG(obj));
- return;
+ warning("kDoSound(fade): Slot not found (%04x:%04x)", PRINT_REG(obj));
+ return acc;
}
int volume = musicSlot->volume;
- switch (_argc) {
- case 2: // SCI0
- // SCI0 fades out all the time and when fadeout is done it will also stop the music from playing
+ // If sound is not playing currently, set signal directly
+ if (musicSlot->status != kSoundPlaying) {
+ debugC(2, kDebugLevelSound, "kDoSound(fade): fading requested, but sound is currently not playing");
+ writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
+ return acc;
+ }
+
+ switch (argc) {
+ case 1: // SCI0
+ // SCI0 fades out all the time and when fadeout is done it will also
+ // stop the music from playing
musicSlot->fadeTo = 0;
musicSlot->fadeStep = -5;
musicSlot->fadeTickerStep = 10 * 16667 / _music->soundGetTempo();
musicSlot->fadeTicker = 0;
break;
- case 5: // SCI01+
- case 6: // SCI1+ (SCI1 late sound scheme), with fade and continue
- musicSlot->fadeTo = CLIP<uint16>(_argv[2].toUint16(), 0, MUSIC_VOLUME_MAX);
- musicSlot->fadeStep = volume > _argv[2].toUint16() ? -_argv[4].toUint16() : _argv[4].toUint16();
- musicSlot->fadeTickerStep = _argv[3].toUint16() * 16667 / _music->soundGetTempo();
+ case 4: // SCI01+
+ case 5: // SCI1+ (SCI1 late sound scheme), with fade and continue
+ musicSlot->fadeTo = CLIP<uint16>(argv[1].toUint16(), 0, MUSIC_VOLUME_MAX);
+ musicSlot->fadeStep = volume > argv[1].toUint16() ? -argv[3].toUint16() : argv[3].toUint16();
+ musicSlot->fadeTickerStep = argv[2].toUint16() * 16667 / _music->soundGetTempo();
musicSlot->fadeTicker = 0;
- musicSlot->stopAfterFading = (_argc == 6) ? (_argv[5].toUint16() != 0) : false;
+ musicSlot->stopAfterFading = (argc == 5) ? (argv[4].toUint16() != 0) : false;
break;
default:
- error("cmdFadeSound: unsupported argc %d", _argc);
- }
-
- // If sound is not playing currently, set signal directly
- if (musicSlot->status != kSoundPlaying) {
- warning("cmdFadeSound: fading requested, but sound is currently not playing");
- writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
+ error("kDoSound(fade): unsupported argc %d", argc);
}
- debugC(2, kDebugLevelSound, "cmdFadeSound: to %d, step %d, ticker %d", musicSlot->fadeTo, musicSlot->fadeStep, musicSlot->fadeTickerStep);
-#endif
+ debugC(2, kDebugLevelSound, "kDoSound(fade): to %d, step %d, ticker %d", musicSlot->fadeTo, musicSlot->fadeStep, musicSlot->fadeTickerStep);
+ return acc;
}
-void SoundCommandParser::cmdGetPolyphony(reg_t obj, int16 value) {
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- _acc = make_reg(0, _state->sfx_get_player_polyphony());
-#else
- _acc = make_reg(0, _music->soundGetVoices()); // Get the number of voices
-#endif
+reg_t SoundCommandParser::kDoSoundGetPolyphony(int argc, reg_t *argv, reg_t acc) {
+ return make_reg(0, _music->soundGetVoices()); // Get the number of voices
}
-void SoundCommandParser::cmdUpdateSound(reg_t obj, int16 value) {
- if (!obj.segment)
- return;
+reg_t SoundCommandParser::kDoSoundUpdate(int argc, reg_t *argv, reg_t acc) {
+ reg_t obj = argv[0];
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- SongHandle handle = FROBNICATE_HANDLE(obj);
- if (_soundVersion <= SCI_VERSION_0_LATE && obj.segment) {
- _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);
if (!musicSlot) {
- warning("cmdUpdateSound: Slot not found (%04x:%04x)", PRINT_REG(obj));
- return;
+ warning("kDoSound(update): Slot not found (%04x:%04x)", PRINT_REG(obj));
+ return acc;
}
musicSlot->loop = readSelectorValue(_segMan, obj, SELECTOR(loop));
@@ -730,88 +352,18 @@ void SoundCommandParser::cmdUpdateSound(reg_t obj, int16 value) {
uint32 objPrio = readSelectorValue(_segMan, obj, SELECTOR(pri));
if (objPrio != musicSlot->priority)
_music->soundSetPriority(musicSlot, objPrio);
-
-#endif
+ return acc;
}
-void SoundCommandParser::cmdUpdateCues(reg_t obj, int16 value) {
- if (!obj.segment)
- return;
-
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- int signal = 0;
- int min = 0;
- int sec = 0;
- int frame = 0;
- int result = SI_LOOP; // small hack
- SongHandle handle = FROBNICATE_HANDLE(obj);
-
- while (result == SI_LOOP)
- result = _state->sfx_poll_specific(handle, &signal);
-
- switch (result) {
- case SI_ABSOLUTE_CUE:
- debugC(2, kDebugLevelSound, "--- [CUE] %04x:%04x Absolute Cue: %d",
- PRINT_REG(obj), signal);
- debugC(2, kDebugLevelSound, "abs-signal %04X", signal);
- writeSelectorValue(_segMan, obj, SELECTOR(signal), signal);
- break;
-
- case SI_RELATIVE_CUE:
- debugC(2, kDebugLevelSound, "--- [CUE] %04x:%04x Relative Cue: %d",
- PRINT_REG(obj), signal);
-
- /* FIXME to match commented-out semantics
- * below, with proper storage of dataInc and
- * signal in the iterator code. */
- writeSelectorValue(_segMan, obj, SELECTOR(dataInc), signal);
- debugC(2, kDebugLevelSound, "rel-signal %04X", signal);
- if (_soundVersion == SCI_VERSION_1_EARLY)
- writeSelectorValue(_segMan, obj, SELECTOR(signal), signal);
- else
- writeSelectorValue(_segMan, obj, SELECTOR(signal), signal + 127);
- break;
-
- case SI_FINISHED:
- debugC(2, kDebugLevelSound, "--- [FINISHED] %04x:%04x", PRINT_REG(obj));
- writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
- break;
-
- case SI_LOOP:
- break; // Doesn't happen
- }
+reg_t SoundCommandParser::kDoSoundUpdateCues(int argc, reg_t *argv, reg_t acc) {
+ processUpdateCues(argv[0]);
+ return acc;
+}
- //switch (signal) {
- //case 0x00:
- // if (dataInc != readSelectorValue(segMan, obj, SELECTOR(dataInc))) {
- // writeSelectorValue(segMan, obj, SELECTOR(dataInc), dataInc);
- // writeSelectorValue(segMan, obj, SELECTOR(signal), dataInc+0x7f);
- // } else {
- // 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 != readSelectorValue(segMan, obj, SELECTOR(dataInc))) {
- // writeSelectorValue(segMan, obj, SELECTOR(dataInc), dataInc);
- // writeSelectorValue(segMan, obj, SELECTOR(signal), dataInc + 0x7f);
- // } else {
- // writeSelectorValue(segMan, obj, SELECTOR(signal), signal);
- // }
- // break;
- //}
-
- if (_soundVersion == SCI_VERSION_1_EARLY) {
- writeSelectorValue(_segMan, obj, SELECTOR(min), min);
- writeSelectorValue(_segMan, obj, SELECTOR(sec), sec);
- writeSelectorValue(_segMan, obj, SELECTOR(frame), frame);
- }
-#else
+void SoundCommandParser::processUpdateCues(reg_t obj) {
MusicEntry *musicSlot = _music->getSlot(obj);
if (!musicSlot) {
- warning("cmdUpdateCues: Slot not found (%04x:%04x)", PRINT_REG(obj));
+ warning("kDoSound(updateCues): Slot not found (%04x:%04x)", PRINT_REG(obj));
return;
}
@@ -828,7 +380,7 @@ void SoundCommandParser::cmdUpdateCues(reg_t obj, int16 value) {
musicSlot->sampleLoopCounter = currentLoopCounter;
}
if ((!_music->soundIsActive(musicSlot)) && (musicSlot->status != kSoundPaused)) {
- processStopSound(obj, 0, true);
+ processStopSound(obj, true);
} else {
_music->updateAudioStreamTicker(musicSlot);
}
@@ -841,7 +393,7 @@ void SoundCommandParser::cmdUpdateCues(reg_t obj, int16 value) {
// Update MIDI slots
if (musicSlot->signal == 0) {
if (musicSlot->dataInc != readSelectorValue(_segMan, obj, SELECTOR(dataInc))) {
- if (_kernel->_selectorCache.dataInc > -1)
+ if (SELECTOR(dataInc) > -1)
writeSelectorValue(_segMan, obj, SELECTOR(dataInc), musicSlot->dataInc);
writeSelectorValue(_segMan, obj, SELECTOR(signal), musicSlot->dataInc + 127);
}
@@ -850,13 +402,15 @@ void SoundCommandParser::cmdUpdateCues(reg_t obj, int16 value) {
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);
+ processStopSound(obj, false);
}
} else {
- // Slot actually has no data (which would mean that a sound-resource w/ unsupported data is used
+ // 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
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 we don't set signal here, at least the switch to the mud wrestling
+ // room in lsl5 will not work.
}
if (musicSlot->fadeCompleted) {
@@ -864,10 +418,10 @@ void SoundCommandParser::cmdUpdateCues(reg_t obj, int16 value) {
// We need signal for sci0 at least in iceman as well (room 14, fireworks)
writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
if (_soundVersion <= SCI_VERSION_0_LATE) {
- cmdStopSound(obj, 0);
+ processStopSound(obj, false);
} else {
if (musicSlot->stopAfterFading)
- cmdStopSound(obj, 0);
+ processStopSound(obj, false);
}
}
@@ -882,48 +436,58 @@ void SoundCommandParser::cmdUpdateCues(reg_t obj, int16 value) {
writeSelectorValue(_segMan, obj, SELECTOR(sec), musicSlot->ticker % 3600 / 60);
writeSelectorValue(_segMan, obj, SELECTOR(frame), musicSlot->ticker);
}
-
-#endif
}
-void SoundCommandParser::cmdSendMidi(reg_t obj, int16 value) {
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- //SongHandle handle = FROBNICATE_HANDLE(obj);
- //_state->sfx_send_midi(handle, value, _midiCmd, _controller, _param);
-#else
- _music->sendMidiCommand(_midiCommand);
-#endif
+reg_t SoundCommandParser::kDoSoundSendMidi(int argc, reg_t *argv, reg_t acc) {
+ reg_t obj = argv[0];
+ byte channel = argv[1].toUint16() & 0xf;
+ byte midiCmd = argv[2].toUint16() & 0xff;
+
+ uint16 controller = argv[3].toUint16();
+ uint16 param = argv[4].toUint16();
+
+ if (channel)
+ channel--; // channel is given 1-based, we are using 0-based
+
+ uint32 midiCommand = (channel | midiCmd) | ((uint32)controller << 8) | ((uint32)param << 16);
+
+ MusicEntry *musicSlot = _music->getSlot(obj);
+ if (!musicSlot) {
+ // TODO: maybe it's possible to call this with obj == 0:0 and send directly?!
+ // if so, allow it
+ //_music->sendMidiCommand(_midiCommand);
+ warning("kDoSound(sendMidi): Slot not found (%04x:%04x)", PRINT_REG(obj));
+ return acc;
+ }
+ _music->sendMidiCommand(musicSlot, midiCommand);
+ return acc;
}
-void SoundCommandParser::cmdReverb(reg_t obj, int16 value) {
-#ifndef USE_OLD_MUSIC_FUNCTIONS
- _music->setReverb(obj.toUint16() & 0xF);
-#endif
+reg_t SoundCommandParser::kDoSoundReverb(int argc, reg_t *argv, reg_t acc) {
+ _music->setReverb(argv[0].toUint16() & 0xF);
+ return acc;
}
-void SoundCommandParser::cmdSetSoundHold(reg_t obj, int16 value) {
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- SongHandle handle = FROBNICATE_HANDLE(obj);
- _state->sfx_song_set_hold(handle, value);
-#else
+reg_t SoundCommandParser::kDoSoundSetHold(int argc, reg_t *argv, reg_t acc) {
+ reg_t obj = argv[0];
+
MusicEntry *musicSlot = _music->getSlot(obj);
if (!musicSlot) {
- warning("cmdSetSoundHold: Slot not found (%04x:%04x)", PRINT_REG(obj));
- return;
+ warning("kDoSound(setHold): Slot not found (%04x:%04x)", PRINT_REG(obj));
+ return acc;
}
// Set the special hold marker ID where the song should be looped at.
- musicSlot->hold = value;
-#endif
+ musicSlot->hold = argv[1].toSint16();
+ return acc;
}
-void SoundCommandParser::cmdGetAudioCapability(reg_t obj, int16 value) {
+reg_t SoundCommandParser::kDoSoundGetAudioCapability(int argc, reg_t *argv, reg_t acc) {
// Tests for digital audio support
- _acc = make_reg(0, 1);
+ return make_reg(0, 1);
}
-void SoundCommandParser::cmdStopAllSounds(reg_t obj, int16 value) {
-#ifndef USE_OLD_MUSIC_FUNCTIONS
+reg_t SoundCommandParser::kDoSoundStopAll(int argc, reg_t *argv, reg_t acc) {
Common::StackLock(_music->_mutex);
const MusicList::iterator end = _music->getPlayListEnd();
@@ -931,32 +495,31 @@ void SoundCommandParser::cmdStopAllSounds(reg_t obj, int16 value) {
if (_soundVersion <= SCI_VERSION_0_LATE) {
writeSelectorValue(_segMan, (*i)->soundObj, SELECTOR(state), kSoundStopped);
} else {
- writeSelectorValue(_segMan, obj, SELECTOR(handle), 0);
+ writeSelectorValue(_segMan, (*i)->soundObj, SELECTOR(handle), 0);
writeSelectorValue(_segMan, (*i)->soundObj, SELECTOR(signal), SIGNAL_OFFSET);
}
(*i)->dataInc = 0;
_music->soundStop(*i);
}
-#endif
+ return acc;
}
-void SoundCommandParser::cmdSetSoundVolume(reg_t obj, int16 value) {
- if (!obj.segment)
- return;
+reg_t SoundCommandParser::kDoSoundSetVolume(int argc, reg_t *argv, reg_t acc) {
+ reg_t obj = argv[0];
+ int16 value = argv[1].toSint16();
-#ifndef USE_OLD_MUSIC_FUNCTIONS
MusicEntry *musicSlot = _music->getSlot(obj);
if (!musicSlot) {
// Do not throw a warning if the sound can't be found, as in some games
- // this is called before the actual sound is loaded (e.g. SQ4CD, with the
- // drum sounds of the energizer bunny at the beginning), so this is normal
- // behavior
+ // this is called before the actual sound is loaded (e.g. SQ4CD, with
+ // the drum sounds of the energizer bunny at the beginning), so this is
+ // normal behavior.
//warning("cmdSetSoundVolume: Slot not found (%04x:%04x)", PRINT_REG(obj));
- return;
+ return acc;
}
- debugC(2, kDebugLevelSound, "cmdSetSoundVolume: %d", value);
+ debugC(2, kDebugLevelSound, "kDoSound(setVolume): %d", value);
value = CLIP<int>(value, 0, MUSIC_VOLUME_MAX);
@@ -965,20 +528,17 @@ void SoundCommandParser::cmdSetSoundVolume(reg_t obj, int16 value) {
_music->soundSetVolume(musicSlot, value);
writeSelectorValue(_segMan, obj, SELECTOR(vol), value);
}
-#endif
+ return acc;
}
-void SoundCommandParser::cmdSetSoundPriority(reg_t obj, int16 value) {
- if (!obj.segment)
- return;
+reg_t SoundCommandParser::kDoSoundSetPriority(int argc, reg_t *argv, reg_t acc) {
+ reg_t obj = argv[0];
+ int16 value = argv[1].toSint16();
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- script_set_priority(_resMan, _segMan, _state, obj, value);
-#else
MusicEntry *musicSlot = _music->getSlot(obj);
if (!musicSlot) {
- warning("cmdSetSoundPriority: Slot not found (%04x:%04x)", PRINT_REG(obj));
- return;
+ warning("kDoSound(setPriority): Slot not found (%04x:%04x)", PRINT_REG(obj));
+ return acc;
}
if (value == -1) {
@@ -987,7 +547,7 @@ void SoundCommandParser::cmdSetSoundPriority(reg_t obj, int16 value) {
if (song->data[0] == 0xf0)
_music->soundSetPriority(musicSlot, song->data[1]);
else
- warning("cmdSetSoundPriority: Attempt to unset song priority when there is no built-in value");
+ warning("kDoSound(setPriority): Attempt to unset song priority when there is no built-in value");
//pSnd->prio=0;field_15B=0
writeSelectorValue(_segMan, obj, SELECTOR(flags), readSelectorValue(_segMan, obj, SELECTOR(flags)) & 0xFD);
@@ -998,32 +558,26 @@ void SoundCommandParser::cmdSetSoundPriority(reg_t obj, int16 value) {
writeSelectorValue(_segMan, obj, SELECTOR(flags), readSelectorValue(_segMan, obj, SELECTOR(flags)) | 2);
//DoSOund(0xF,hobj,w)
}
-#endif
+ return acc;
}
-void SoundCommandParser::cmdSetSoundLoop(reg_t obj, int16 value) {
- if (!obj.segment)
- return;
+reg_t SoundCommandParser::kDoSoundSetLoop(int argc, reg_t *argv, reg_t acc) {
+ reg_t obj = argv[0];
+ int16 value = argv[1].toSint16();
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- if (!readSelector(_segMan, obj, SELECTOR(nodePtr)).isNull()) {
- SongHandle handle = FROBNICATE_HANDLE(obj);
- _state->sfx_song_set_loops(handle, value);
- }
-#else
MusicEntry *musicSlot = _music->getSlot(obj);
if (!musicSlot) {
// Apparently, it's perfectly normal for a game to call cmdSetSoundLoop
// before actually initializing the sound and adding it to the playlist
// with cmdInitSound. Usually, it doesn't matter if the game doesn't
// request to loop the sound, so in this case, don't throw any warning,
- // otherwise do, because the sound won't be looped
+ // otherwise do, because the sound won't be looped.
if (value == -1) {
- warning("cmdSetSoundLoop: Slot not found (%04x:%04x) and the song was requested to be looped", PRINT_REG(obj));
+ warning("kDoSound(setLoop): Slot not found (%04x:%04x) and the song was requested to be looped", PRINT_REG(obj));
} else {
// Doesn't really matter
}
- return;
+ return acc;
}
if (value == -1) {
musicSlot->loop = 0xFFFF;
@@ -1032,40 +586,37 @@ void SoundCommandParser::cmdSetSoundLoop(reg_t obj, int16 value) {
}
writeSelectorValue(_segMan, obj, SELECTOR(loop), musicSlot->loop);
-#endif
+ return acc;
}
-void SoundCommandParser::cmdSuspendSound(reg_t obj, int16 value) {
+reg_t SoundCommandParser::kDoSoundSuspend(int argc, reg_t *argv, reg_t acc) {
// TODO
- warning("STUB: cmdSuspendSound");
+ warning("kDoSound(suspend): STUB");
+ return acc;
}
-#ifndef USE_OLD_MUSIC_FUNCTIONS
-
void SoundCommandParser::updateSci0Cues() {
bool noOnePlaying = true;
MusicEntry *pWaitingForPlay = NULL;
- _music->_mutex.lock();
-
const MusicList::iterator end = _music->getPlayListEnd();
for (MusicList::iterator i = _music->getPlayListStart(); i != end; ++i) {
// Is the sound stopped, and the sound object updated too? If yes, skip
- // this sound, as SCI0 only allows one active song
+ // this sound, as SCI0 only allows one active song.
if ((*i)->isQueued) {
pWaitingForPlay = (*i);
- // FIXME (?) - in iceman 2 songs are queued when playing the door sound - if we use the first song for resuming
- // then it's the wrong one. Both songs have same priority. Maybe the new sound function in sci0
- // is somehow responsible
+ // FIXME(?): In iceman 2 songs are queued when playing the door
+ // sound - if we use the first song for resuming then it's the wrong
+ // one. Both songs have same priority. Maybe the new sound function
+ // in sci0 is somehow responsible.
continue;
}
if ((*i)->signal == 0 && (*i)->status != kSoundPlaying)
continue;
- cmdUpdateCues((*i)->soundObj, 0);
+ processUpdateCues((*i)->soundObj);
noOnePlaying = false;
}
- _music->_mutex.unlock();
if (noOnePlaying && pWaitingForPlay) {
// If there is a queued entry, play it now ffs: SciMusic::soundPlay()
@@ -1074,90 +625,40 @@ void SoundCommandParser::updateSci0Cues() {
}
}
-#endif
-
void SoundCommandParser::clearPlayList() {
-#ifndef USE_OLD_MUSIC_FUNCTIONS
_music->clearPlayList();
-#endif
-}
-
-void SoundCommandParser::syncPlayList(Common::Serializer &s) {
-#ifndef USE_OLD_MUSIC_FUNCTIONS
- _music->saveLoadWithSerializer(s);
-#endif
-}
-
-void SoundCommandParser::reconstructPlayList(int savegame_version) {
-#ifndef USE_OLD_MUSIC_FUNCTIONS
- Common::StackLock lock(_music->_mutex);
-
- const MusicList::iterator end = _music->getPlayListEnd();
- for (MusicList::iterator i = _music->getPlayListStart(); i != end; ++i) {
- if ((*i)->resourceId && _resMan->testResource(ResourceId(kResourceTypeSound, (*i)->resourceId))) {
- (*i)->soundRes = new SoundResource((*i)->resourceId, _resMan, _soundVersion);
- _music->soundInitSnd(*i);
- } else {
- (*i)->soundRes = 0;
- }
- if ((*i)->status == kSoundPlaying) {
- if (savegame_version < 14) {
- (*i)->dataInc = readSelectorValue(_segMan, (*i)->soundObj, SELECTOR(dataInc));
- (*i)->signal = readSelectorValue(_segMan, (*i)->soundObj, SELECTOR(signal));
-
- if (_soundVersion >= SCI_VERSION_1_LATE)
- (*i)->volume = readSelectorValue(_segMan, (*i)->soundObj, SELECTOR(vol));
- }
-
- cmdPlaySound((*i)->soundObj, 0);
- }
- }
-
-#endif
}
void SoundCommandParser::printPlayList(Console *con) {
-#ifndef USE_OLD_MUSIC_FUNCTIONS
_music->printPlayList(con);
-#endif
}
void SoundCommandParser::printSongInfo(reg_t obj, Console *con) {
-#ifndef USE_OLD_MUSIC_FUNCTIONS
_music->printSongInfo(obj, con);
-#endif
}
void SoundCommandParser::stopAllSounds() {
-#ifndef USE_OLD_MUSIC_FUNCTIONS
_music->stopAll();
-#endif
}
void SoundCommandParser::startNewSound(int number) {
-#ifndef USE_OLD_MUSIC_FUNCTIONS
Common::StackLock lock(_music->_mutex);
// Overwrite the first sound in the playlist
MusicEntry *song = *_music->getPlayListStart();
reg_t soundObj = song->soundObj;
- cmdDisposeSound(soundObj, 0);
+ processDisposeSound(soundObj);
writeSelectorValue(_segMan, soundObj, SELECTOR(number), number);
- cmdInitSound(soundObj, 0);
- cmdPlaySound(soundObj, 0);
-#endif
+ processInitSound(soundObj);
+ processPlaySound(soundObj);
}
void SoundCommandParser::setMasterVolume(int vol) {
-#ifndef USE_OLD_MUSIC_FUNCTIONS
_music->soundSetMasterVolume(vol);
-#endif
}
void SoundCommandParser::pauseAll(bool pause) {
-#ifndef USE_OLD_MUSIC_FUNCTIONS
_music->pauseAll(pause);
-#endif
}
} // End of namespace Sci
diff --git a/engines/sci/sound/soundcmd.h b/engines/sci/sound/soundcmd.h
index 09cca23450..a8bc1eb280 100644
--- a/engines/sci/sound/soundcmd.h
+++ b/engines/sci/sound/soundcmd.h
@@ -26,8 +26,6 @@
#ifndef SCI_SOUNDCMD_H
#define SCI_SOUNDCMD_H
-#include "sci/sci.h" // for USE_OLD_MUSIC_FUNCTIONS
-
#include "common/list.h"
#include "sci/engine/state.h"
@@ -36,13 +34,13 @@ namespace Sci {
class Console;
class SciMusic;
class SoundCommandParser;
-typedef void (SoundCommandParser::*SoundCommand)(reg_t obj, int16 value);
+//typedef void (SoundCommandParser::*SoundCommand)(reg_t obj, int16 value);
-struct MusicEntryCommand {
- MusicEntryCommand(const char *d, SoundCommand c) : sndCmd(c), desc(d) {}
- SoundCommand sndCmd;
- const char *desc;
-};
+//struct MusicEntryCommand {
+// MusicEntryCommand(const char *d, SoundCommand c) : sndCmd(c), desc(d) {}
+// SoundCommand sndCmd;
+// const char *desc;
+//};
class SoundCommandParser {
public:
@@ -53,11 +51,7 @@ public:
kMaxSciVolume = 15
};
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- void updateSfxState(SfxState *newState) { _state = newState; }
-#endif
-
- reg_t parseCommand(int argc, reg_t *argv, reg_t acc);
+ //reg_t parseCommand(int argc, reg_t *argv, reg_t acc);
// Functions used for game state loading
void clearPlayList();
@@ -69,14 +63,14 @@ public:
void pauseAll(bool pause);
// Debug console functions
- void playSound(reg_t obj) { cmdPlaySound(obj, 0); }
- void stopSound(reg_t obj) { cmdStopSound(obj, 0); }
void startNewSound(int number);
void stopAllSounds();
void printPlayList(Console *con);
void printSongInfo(reg_t obj, Console *con);
-#ifndef USE_OLD_MUSIC_FUNCTIONS
+ void processPlaySound(reg_t obj);
+ void processStopSound(reg_t obj, bool sampleFinishedPlaying);
+
/**
* Synchronizes the current state of the music list to the rest of the engine, so that
* the changes that the sound thread makes to the music are registered with the engine
@@ -85,56 +79,43 @@ public:
* by the engine scripts themselves, so the engine itself polls for changes to the music
*/
void updateSci0Cues();
-#endif
+
+ reg_t kDoSoundInit(int argc, reg_t *argv, reg_t acc);
+ reg_t kDoSoundPlay(int argc, reg_t *argv, reg_t acc);
+ reg_t kDoSoundDummy(int argc, reg_t *argv, reg_t acc);
+ reg_t kDoSoundMute(int argc, reg_t *argv, reg_t acc);
+ reg_t kDoSoundPause(int argc, reg_t *argv, reg_t acc);
+ reg_t kDoSoundResume(int argc, reg_t *argv, reg_t acc);
+ reg_t kDoSoundStop(int argc, reg_t *argv, reg_t acc);
+ reg_t kDoSoundStopAll(int argc, reg_t *argv, reg_t acc);
+ reg_t kDoSoundDispose(int argc, reg_t *argv, reg_t acc);
+ reg_t kDoSoundMasterVolume(int argc, reg_t *argv, reg_t acc);
+ reg_t kDoSoundFade(int argc, reg_t *argv, reg_t acc);
+ reg_t kDoSoundGetPolyphony(int argc, reg_t *argv, reg_t acc);
+ reg_t kDoSoundUpdate(int argc, reg_t *argv, reg_t acc);
+ reg_t kDoSoundUpdateCues(int argc, reg_t *argv, reg_t acc);
+ reg_t kDoSoundSendMidi(int argc, reg_t *argv, reg_t acc);
+ reg_t kDoSoundReverb(int argc, reg_t *argv, reg_t acc);
+ reg_t kDoSoundSetHold(int argc, reg_t *argv, reg_t acc);
+ reg_t kDoSoundGetAudioCapability(int argc, reg_t *argv, reg_t acc);
+ reg_t kDoSoundSetVolume(int argc, reg_t *argv, reg_t acc);
+ reg_t kDoSoundSetPriority(int argc, reg_t *argv, reg_t acc);
+ reg_t kDoSoundSetLoop(int argc, reg_t *argv, reg_t acc);
+ reg_t kDoSoundSuspend(int argc, reg_t *argv, reg_t acc);
private:
- typedef Common::Array<MusicEntryCommand *> SoundCommandContainer;
- SoundCommandContainer _soundCommands;
+ //typedef Common::Array<MusicEntryCommand *> SoundCommandContainer;
+ //SoundCommandContainer _soundCommands;
ResourceManager *_resMan;
SegManager *_segMan;
Kernel *_kernel;
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- SfxState *_state;
- int _midiCmd, _controller, _param;
-#else
SciMusic *_music;
-#endif
AudioPlayer *_audio;
SciVersion _soundVersion;
- int _argc;
- reg_t *_argv; // for cmdFadeSound
- uint32 _midiCommand; // for cmdSendMidi
- reg_t _acc;
- int _cmdUpdateCuesIndex;
-
- void cmdInitSound(reg_t obj, int16 value);
- void cmdPlaySound(reg_t obj, int16 value);
- void cmdDummy(reg_t obj, int16 value);
- void cmdMuteSound(reg_t obj, int16 value);
- void cmdPauseSound(reg_t obj, int16 value);
- void cmdResumeSound(reg_t obj, int16 value);
- void cmdStopSound(reg_t obj, int16 value);
- void cmdDisposeSound(reg_t obj, int16 value);
- void cmdMasterVolume(reg_t obj, int16 value);
- void cmdFadeSound(reg_t obj, int16 value);
- void cmdGetPolyphony(reg_t obj, int16 value);
- void cmdStopAllSounds(reg_t obj, int16 value);
- void cmdUpdateSound(reg_t obj, int16 value);
- void cmdUpdateCues(reg_t obj, int16 value);
- void cmdSendMidi(reg_t obj, int16 value);
- void cmdReverb(reg_t obj, int16 value);
- void cmdSetSoundHold(reg_t obj, int16 value);
- void cmdGetAudioCapability(reg_t obj, int16 value);
- void cmdSetSoundVolume(reg_t obj, int16 value);
- void cmdSetSoundPriority(reg_t obj, int16 value);
- void cmdSetSoundLoop(reg_t obj, int16 value);
- void cmdSuspendSound(reg_t obj, int16 value);
-
- void processStopSound(reg_t obj, int16 value, bool sampleFinishedPlaying);
-
-#ifdef USE_OLD_MUSIC_FUNCTIONS
- void changeSoundStatus(reg_t obj, int newStatus);
-#endif
+
+ void processInitSound(reg_t obj);
+ void processDisposeSound(reg_t obj);
+ void processUpdateCues(reg_t obj);
};
} // End of namespace Sci
diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp
index 0e0c0e129e..fa4804ce7d 100644
--- a/engines/scumm/charset.cpp
+++ b/engines/scumm/charset.cpp
@@ -109,10 +109,9 @@ void ScummEngine::loadCJKFont() {
numChar = 8192;
break;
case Common::ZH_TWN:
- if (_game.id == GID_CMI) {
- fontFile = "chinese.fnt";
- numChar = 13630;
- }
+ // Both The DIG and COMI use same font
+ fontFile = "chinese.fnt";
+ numChar = 13630;
break;
case Common::ZH_CNA:
if (_game.id == GID_FT || _game.id == GID_LOOM || _game.id == GID_INDY3 ||
diff --git a/engines/scumm/detection.cpp b/engines/scumm/detection.cpp
index 7275caaa1e..9721c75677 100644
--- a/engines/scumm/detection.cpp
+++ b/engines/scumm/detection.cpp
@@ -314,7 +314,7 @@ static Common::Language detectLanguage(const Common::FSList &fslist, byte id) {
case 449787: // 64f3fe479d45b52902cf88145c41d172
return Common::ES_ESP;
}
- } else {
+ } else { // The DIG
switch (size) {
case 248627: // 1fd585ac849d57305878c77b2f6c74ff
return Common::DE_DEU;
@@ -328,6 +328,8 @@ static Common::Language detectLanguage(const Common::FSList &fslist, byte id) {
return Common::ES_ESP;
case 223107: // 64f3fe479d45b52902cf88145c41d172
return Common::JA_JPN;
+ case 180730: // 424fdd60822722cdc75356d921dad9bf
+ return Common::ZH_TWN;
}
}
}
@@ -381,10 +383,12 @@ static void computeGameSettingsFromMD5(const Common::FSList &fslist, const GameF
}
}
-static void detectGames(const Common::FSList &fslist, Common::List<DetectorResult> &results, const char *gameid) {
- DescMap fileMD5Map;
- DetectorResult dr;
- char md5str[32+1];
+static void composeFileHashMap(const Common::FSList &fslist, DescMap &fileMD5Map, int depth, const char **globs) {
+ if (depth <= 0)
+ return;
+
+ if (fslist.empty())
+ return;
for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
if (!file->isDirectory()) {
@@ -392,8 +396,36 @@ static void detectGames(const Common::FSList &fslist, Common::List<DetectorResul
d.node = *file;
d.md5Entry = 0;
fileMD5Map[file->getName()] = d;
+ } else {
+ if (!globs)
+ continue;
+
+ bool matched = false;
+ for (const char *glob = *globs; *glob; glob++)
+ if (file->getName().matchString(glob, true)) {
+ matched = true;
+ break;
+ }
+
+ if (!matched)
+ continue;
+
+ Common::FSList files;
+
+ if (file->getChildren(files, Common::FSNode::kListAll)) {
+ composeFileHashMap(files, fileMD5Map, depth - 1, globs);
+ }
}
}
+}
+
+static void detectGames(const Common::FSList &fslist, Common::List<DetectorResult> &results, const char *gameid) {
+ DescMap fileMD5Map;
+ DetectorResult dr;
+ char md5str[32+1];
+
+ // Dive one level down since mac indy3/loom has its files split into directories. See Bug #1438631
+ composeFileHashMap(fslist, fileMD5Map, 2, directoryGlobs);
// Iterate over all filename patterns.
for (const GameFilenamePattern *gfp = gameFilenamesTable; gfp->gameid; ++gfp) {
@@ -458,6 +490,12 @@ static void detectGames(const Common::FSList &fslist, Common::List<DetectorResul
// Exact match found. Compute the precise game settings.
computeGameSettingsFromMD5(fslist, gfp, d.md5Entry, dr);
+ // Print some debug info
+ int filesize = tmp->size();
+ if (d.md5Entry->filesize != filesize)
+ debug(1, "SCUMM detector found matching file '%s' with MD5 %s, size %d\n",
+ file.c_str(), md5str, filesize);
+
// Sanity check: We *should* have found a matching gameid / variant at this point.
// If not, then there's a bug in our data tables...
assert(dr.game.gameid != 0);
@@ -866,7 +904,8 @@ GameList ScummMetaEngine::detectGames(const Common::FSList &fslist) const {
}
}
- dg.setGUIOptions(x->game.guioptions);
+ dg.setGUIOptions(x->game.guioptions | MidiDriver::musicType2GUIO(x->game.midi));
+ dg.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(x->language));
detectedGames.push_back(dg);
}
@@ -966,6 +1005,10 @@ Common::Error ScummMetaEngine::createInstance(OSystem *syst, Engine **engine) co
debug(1, "Using MD5 '%s'", res.md5.c_str());
}
+ // If the GUI options were updated, we catch this here and update them in the users config
+ // file transparently.
+ Common::updateGameGUIOptions(res.game.guioptions, getGameGUIOptionsDescriptionLanguage(res.language));
+
// Check for a user override of the platform. We allow the user to override
// the platform, to make it possible to add games which are not yet in
// our MD5 database but require a specific platform setting.
@@ -982,11 +1025,6 @@ Common::Error ScummMetaEngine::createInstance(OSystem *syst, Engine **engine) co
// TODO: Maybe allow the null driver, too?
if (res.game.platform == Common::kPlatformFMTowns && res.game.version == 3)
res.game.midi = MDT_TOWNS;
-
- // If the GUI options were updated, we catch this here and update them in the users config
- // file transparently.
- Common::updateGameGUIOptions(res.game.guioptions);
-
// Finally, we have massaged the GameDescriptor to our satisfaction, and can
// instantiate the appropriate game engine. Hooray!
switch (res.game.version) {
diff --git a/engines/scumm/detection_tables.h b/engines/scumm/detection_tables.h
index 0b90af4ec4..d8987c816f 100644
--- a/engines/scumm/detection_tables.h
+++ b/engines/scumm/detection_tables.h
@@ -48,6 +48,15 @@ namespace Scumm {
#pragma mark --- Tables ---
#pragma mark -
+/**
+ * This table contains list of directories which could contain game data
+ * and which should be looked into during detection.
+ */
+static const char *directoryGlobs[] = {
+ "rooms *", // Mac version of indy3/loom
+ 0
+};
+
/**
* This table contains all game IDs supported by the SCUMM engine, and maps
@@ -215,7 +224,7 @@ static const GameSettings gameVariantsTable[] = {
{"indy3", "VGA", "vga", GID_INDY3, 3, 0, MDT_PCSPK | MDT_ADLIB, GF_OLD256 | GF_FEW_LOCALS, Common::kPlatformPC, GUIO_NOSPEECH | GUIO_NOMIDI},
{"indy3", "FM-TOWNS", 0, GID_INDY3, 3, 0, MDT_TOWNS, GF_OLD256 | GF_FEW_LOCALS | GF_AUDIOTRACKS, Common::kPlatformFMTowns, GUIO_NOSPEECH | GUIO_NOMIDI},
- {"loom", "EGA", "ega", GID_LOOM, 3, 0, MDT_PCSPK | MDT_CMS | MDT_ADLIB | MDT_MIDI, 0, UNK, GUIO_NOSPEECH},
+ {"loom", "EGA", "ega", GID_LOOM, 3, 0, MDT_PCSPK | MDT_CMS | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO_NOSPEECH},
{"loom", "No AdLib", "ega", GID_LOOM, 3, 0, MDT_PCSPK | MDT_CMS, 0, UNK, GUIO_NOSPEECH | GUIO_NOMIDI},
#ifdef USE_RGB_COLOR
{"loom", "PC-Engine", 0, GID_LOOM, 3, 0, MDT_NONE, GF_AUDIOTRACKS | GF_OLD256 | GF_16BIT_COLOR, Common::kPlatformPCEngine, GUIO_NOSPEECH | GUIO_NOMIDI},
@@ -225,24 +234,24 @@ static const GameSettings gameVariantsTable[] = {
{"pass", 0, 0, GID_PASS, 4, 0, MDT_PCSPK | MDT_ADLIB, GF_16COLOR, Common::kPlatformPC, GUIO_NOSPEECH | GUIO_NOMIDI},
- {"monkey", "VGA", "vga", GID_MONKEY_VGA, 4, 0, MDT_PCSPK | MDT_ADLIB | MDT_MIDI, 0, UNK, GUIO_NOSPEECH},
- {"monkey", "EGA", "ega", GID_MONKEY_EGA, 4, 0, MDT_PCSPK | MDT_CMS | MDT_ADLIB | MDT_MIDI, GF_16COLOR, Common::kPlatformPC, GUIO_NOSPEECH},
+ {"monkey", "VGA", "vga", GID_MONKEY_VGA, 4, 0, MDT_PCSPK | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO_NOSPEECH},
+ {"monkey", "EGA", "ega", GID_MONKEY_EGA, 4, 0, MDT_PCSPK | MDT_CMS | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, GF_16COLOR, Common::kPlatformPC, GUIO_NOSPEECH},
{"monkey", "No AdLib", "ega", GID_MONKEY_EGA, 4, 0, MDT_PCSPK, GF_16COLOR, Common::kPlatformAtariST, GUIO_NOSPEECH | GUIO_NOMIDI},
{"monkey", "Demo", "ega", GID_MONKEY_EGA, 4, 0, MDT_PCSPK | MDT_ADLIB, GF_16COLOR, Common::kPlatformPC, GUIO_NOSPEECH | GUIO_NOMIDI},
{"monkey", "CD", 0, GID_MONKEY, 5, 0, MDT_ADLIB, GF_AUDIOTRACKS, UNK, GUIO_NOSPEECH | GUIO_NOMIDI},
{"monkey", "FM-TOWNS", 0, GID_MONKEY, 5, 0, MDT_ADLIB, GF_AUDIOTRACKS, Common::kPlatformFMTowns, GUIO_NOSPEECH | GUIO_NOMIDI},
{"monkey", "SEGA", 0, GID_MONKEY, 5, 0, MDT_NONE, GF_AUDIOTRACKS, Common::kPlatformSegaCD, GUIO_NOSPEECH | GUIO_NOMIDI},
- {"monkey2", 0, 0, GID_MONKEY2, 5, 0, MDT_ADLIB | MDT_MIDI, 0, UNK, GUIO_NOSPEECH},
+ {"monkey2", 0, 0, GID_MONKEY2, 5, 0, MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO_NOSPEECH},
- {"atlantis", "" , 0, GID_INDY4, 5, 0, MDT_ADLIB | MDT_MIDI, 0, UNK, GUIO_NONE},
- {"atlantis", "Floppy", 0, GID_INDY4, 5, 0, MDT_ADLIB | MDT_MIDI, 0, UNK, GUIO_NOSPEECH},
+ {"atlantis", "" , 0, GID_INDY4, 5, 0, MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO_NONE},
+ {"atlantis", "Floppy", 0, GID_INDY4, 5, 0, MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO_NOSPEECH},
- {"tentacle", "", 0, GID_TENTACLE, 6, 0, MDT_ADLIB | MDT_MIDI, GF_USE_KEY, UNK, GUIO_NONE},
- {"tentacle", "Floppy", 0, GID_TENTACLE, 6, 0, MDT_ADLIB | MDT_MIDI, GF_USE_KEY, UNK, GUIO_NOSPEECH},
+ {"tentacle", "", 0, GID_TENTACLE, 6, 0, MDT_ADLIB | MDT_MIDI | MDT_PREFER_GM, GF_USE_KEY, UNK, GUIO_NONE},
+ {"tentacle", "Floppy", 0, GID_TENTACLE, 6, 0, MDT_ADLIB | MDT_MIDI | MDT_PREFER_GM, GF_USE_KEY, UNK, GUIO_NOSPEECH},
- {"samnmax", "", 0, GID_SAMNMAX, 6, 0, MDT_ADLIB | MDT_MIDI, GF_USE_KEY, UNK, GUIO_NONE},
- {"samnmax", "Floppy", 0, GID_SAMNMAX, 6, 0, MDT_ADLIB | MDT_MIDI, GF_USE_KEY, UNK, GUIO_NOSPEECH},
+ {"samnmax", "", 0, GID_SAMNMAX, 6, 0, MDT_ADLIB | MDT_MIDI | MDT_PREFER_GM, GF_USE_KEY, UNK, GUIO_NONE},
+ {"samnmax", "Floppy", 0, GID_SAMNMAX, 6, 0, MDT_ADLIB | MDT_MIDI | MDT_PREFER_GM, GF_USE_KEY, UNK, GUIO_NOSPEECH},
#ifdef ENABLE_SCUMM_7_8
{"ft", 0, 0, GID_FT, 7, 0, MDT_NONE, 0, UNK, GUIO_NOMIDI},
diff --git a/engines/scumm/dialogs.cpp b/engines/scumm/dialogs.cpp
index d9c24ddca2..1e0bf6d4be 100644
--- a/engines/scumm/dialogs.cpp
+++ b/engines/scumm/dialogs.cpp
@@ -26,6 +26,7 @@
#include "common/savefile.h"
#include "common/system.h"
#include "common/events.h"
+#include "common/translation.h"
#include "graphics/scaler.h"
@@ -283,9 +284,9 @@ HelpDialog::HelpDialog(const GameSettings &game)
_numPages = ScummHelp::numPages(_game.id);
- _prevButton = new GUI::ButtonWidget(this, "ScummHelp.Prev", "Previous", kPrevCmd, 'P');
- _nextButton = new GUI::ButtonWidget(this, "ScummHelp.Next", "Next", kNextCmd, 'N');
- new GUI::ButtonWidget(this, "ScummHelp.Close", "Close", GUI::kCloseCmd, 'C');
+ _prevButton = new GUI::ButtonWidget(this, "ScummHelp.Prev", _("~P~revious"), 0, kPrevCmd);
+ _nextButton = new GUI::ButtonWidget(this, "ScummHelp.Next", _("~N~ext"), 0, kNextCmd);
+ new GUI::ButtonWidget(this, "ScummHelp.Close", _("~C~lose"), 0, GUI::kCloseCmd);
_prevButton->clearFlags(WIDGET_ENABLED);
_numLines = HELP_NUM_LINES;
diff --git a/engines/scumm/saveload.cpp b/engines/scumm/saveload.cpp
index 1375281aaf..1d064cdf6a 100644
--- a/engines/scumm/saveload.cpp
+++ b/engines/scumm/saveload.cpp
@@ -576,6 +576,10 @@ bool ScummEngine::loadState(int slot, bool compat) {
// Fixes bug #1766072: MANIACNES: Music Doesn't Start On Load Game
if (_game.platform == Common::kPlatformNES) {
runScript(5, 0, 0, 0);
+
+ if (VAR(224)) {
+ _sound->addSoundToQueue(VAR(224));
+ }
}
return true;
diff --git a/engines/scumm/script_v5.cpp b/engines/scumm/script_v5.cpp
index b1545db0f3..5c20e0dfd3 100644
--- a/engines/scumm/script_v5.cpp
+++ b/engines/scumm/script_v5.cpp
@@ -2594,6 +2594,17 @@ void ScummEngine_v5::decodeParseString() {
else
strcpy((char *)tmpBuf+16, "^19^");
printString(textSlot, tmpBuf);
+ } else if (_game.id == GID_MONKEY_EGA && _roomResource == 30 && vm.slot[_currentScript].number == 411 &&
+ strstr((const char *)_scriptPointer, "NCREDIT-NOTE-AMOUNT")) {
+ // WORKAROUND for bug #3003643 (MI1EGA German: Credit text incorrect)
+ // The script contains buggy text.
+ const char *tmp = strstr((const char *)_scriptPointer, "NCREDIT-NOTE-AMOUNT");
+ char tmpBuf[256];
+ const int diff = tmp - (const char *)_scriptPointer;
+ memcpy(tmpBuf, _scriptPointer, diff);
+ strcpy(tmpBuf + diff, "5000");
+ strcpy(tmpBuf + diff + 4, tmp + sizeof("NCREDIT-NOTE-AMOUNT") - 1);
+ printString(textSlot, (byte *)tmpBuf);
} else {
printString(textSlot, _scriptPointer);
}
diff --git a/engines/scumm/scumm-md5.h b/engines/scumm/scumm-md5.h
index cc382d9621..a25fac1a88 100644
--- a/engines/scumm/scumm-md5.h
+++ b/engines/scumm/scumm-md5.h
@@ -1,5 +1,5 @@
/*
- This file was generated by the md5table tool on Mon May 24 13:24:24 2010
+ This file was generated by the md5table tool on Sun Jun 27 05:23:26 2010
DO NOT EDIT MANUALLY!
*/
@@ -40,14 +40,14 @@ static const MD5Table md5table[] = {
{ "0a295b80f9a9edf818e8e161a0e83830", "freddi2", "HE 80", "", -1, Common::FR_FRA, Common::kPlatformUnknown },
{ "0a41311d462b6639fc45297b9044bf16", "monkey", "No AdLib", "EGA", -1, Common::ES_ESP, Common::kPlatformAtariST },
{ "0a6d7b81b850ed4a77811c60c9b5c555", "PuttTime", "HE 99", "Mini Game", -1, Common::EN_USA, Common::kPlatformWindows },
- { "0aa050f4ad79402fbe9c4f78fb8ac494", "loom", "PC-Engine", "", -1, Common::EN_ANY, Common::kPlatformPCEngine },
+ { "0aa050f4ad79402fbe9c4f78fb8ac494", "loom", "PC-Engine", "", 6532, Common::EN_ANY, Common::kPlatformPCEngine },
{ "0ab19be9e2a3f6938226638b2a3744fe", "PuttTime", "HE 100", "Demo", -1, Common::EN_USA, Common::kPlatformUnknown },
{ "0ac41e2e3d2174e5a042a6b565328dba", "puttrace", "HE 98", "Demo", 13110, Common::EN_USA, Common::kPlatformUnknown },
{ "0b3222aaa7efcf283eb621e0cefd26cc", "puttputt", "HE 60", "", -1, Common::RU_RUS, Common::kPlatformPC },
{ "0be88565f734b1e9e77ccaaf3bb14b29", "loom", "EGA", "EGA", -1, Common::ES_ESP, Common::kPlatformPC },
{ "0bf1a3eb198ca1bd2ebe104825cec770", "puttrace", "HE 99", "Demo", -1, Common::FR_FRA, Common::kPlatformWindows },
{ "0c331637580950aea2346e012ef2a868", "maniac", "V2", "V2", 1988, Common::EN_ANY, Common::kPlatformAtariST },
- { "0c45eb4baff0c12c3d9dfa889c8070ab", "pajama3", "", "Demo", -1, Common::DE_DEU, Common::kPlatformUnknown },
+ { "0c45eb4baff0c12c3d9dfa889c8070ab", "pajama3", "", "Demo", 13884, Common::DE_DEU, Common::kPlatformUnknown },
{ "0cccfa5223099a60e76cfcca57a1a141", "freddi3", "", "", -1, Common::NL_NLD, Common::kPlatformUnknown },
{ "0d1b69471605201ef2fa9cec1f5f02d2", "maniac", "V2", "V2", -1, Common::ES_ESP, Common::kPlatformPC },
{ "0e4c5d54a0ad4b26132e78b5ea76642a", "samnmax", "Floppy", "Demo", 6485, Common::EN_ANY, Common::kPlatformPC },
@@ -75,11 +75,11 @@ static const MD5Table md5table[] = {
{ "15e03ffbfeddb9c2aebc13dcb2a4a8f4", "monkey", "VGA", "VGA", 8357, Common::EN_ANY, Common::kPlatformPC },
{ "15f588e887e857e8c56fe6ade4956168", "atlantis", "Floppy", "Floppy", -1, Common::ES_ESP, Common::kPlatformAmiga },
{ "16542a7342a918bfe4ba512007d36c47", "FreddisFunShop", "HE 99L", "", -1, Common::EN_USA, Common::kPlatformUnknown },
- { "166553538ff320c69edafeee29525419", "samnmax", "", "CD", -1, Common::EN_ANY, Common::kPlatformMacintosh },
+ { "166553538ff320c69edafeee29525419", "samnmax", "", "CD", 199195304, Common::EN_ANY, Common::kPlatformMacintosh },
{ "16effd200aa6b8abe9c569c3e578814d", "freddi4", "HE 99", "Demo", -1, Common::NL_NLD, Common::kPlatformWindows },
{ "179879b6e35c1ead0d93aab26db0951b", "fbear", "HE 70", "", 13381, Common::EN_ANY, Common::kPlatformWindows },
{ "17b5d5e6af4ae89d62631641d66d5a05", "indy3", "VGA", "VGA", -1, Common::IT_ITA, Common::kPlatformPC },
- { "17f7296f63c78642724f057fd8e736a7", "maniac", "NES", "", -1, Common::EN_GRB, Common::kPlatformNES },
+ { "17f7296f63c78642724f057fd8e736a7", "maniac", "NES", "", 2082, Common::EN_GRB, Common::kPlatformNES },
{ "17fa250eb72dae2dad511ba79c0b6b0a", "tentacle", "", "Demo", -1, Common::FR_FRA, Common::kPlatformPC },
{ "182344899c2e2998fca0bebcd82aa81a", "atlantis", "", "CD", 12035, Common::EN_ANY, Common::kPlatformPC },
{ "183d7464902d40d00800e8ee1f04117c", "maniac", "V2", "V2", 1988, Common::DE_DEU, Common::kPlatformPC },
@@ -130,13 +130,14 @@ static const MD5Table md5table[] = {
{ "2d388339d6050d8ccaa757b64633954e", "zak", "FM-TOWNS", "Demo", 7520, Common::EN_ANY, Common::kPlatformFMTowns },
{ "2d4536a56e01da4b02eb021e7770afa2", "zak", "FM-TOWNS", "", 7520, Common::EN_ANY, Common::kPlatformFMTowns },
{ "2d4acbdcfd8e374c9da8c2e7303a5cd0", "BluesBirthday", "", "Demo", -1, Common::EN_ANY, Common::kPlatformUnknown },
- { "2d624d1b214f7faf0094daea65c6d1a6", "maniac", "Apple II", "", -1, Common::EN_ANY, Common::kPlatformUnknown },
+ { "2d624d1b214f7faf0094daea65c6d1a6", "maniac", "Apple II", "", -1, Common::EN_ANY, Common::kPlatformApple2GS },
{ "2d9d46f23cb07bbc90b8ad464d3e4ff8", "atlantis", "", "CD", -1, Common::EN_ANY, Common::kPlatformMacintosh },
{ "2e85f7aa054930c692a5b1bed1dfc295", "football2002", "", "Demo", -1, Common::EN_ANY, Common::kPlatformUnknown },
{ "2e8a1f76ea33bc5e04347646feee173d", "pajama3", "", "", -1, Common::DE_DEU, Common::kPlatformUnknown },
{ "2fe369ad70f52a8cf7ad6077ee64f81a", "loom", "EGA", "EGA", -1, Common::DE_DEU, Common::kPlatformAmiga },
{ "305d3dd57c96c65b017bc70c8c7cfb5e", "monkey", "CD", "CD", 8955, Common::DE_DEU, Common::kPlatformPC },
{ "30ba1e825d4ad2b448143ae8df18482a", "pajama2", "HE 98.5", "Demo", -1, Common::NL_NLD, Common::kPlatformUnknown },
+ { "30d1903b0715759af064be2127381cd0", "freddi", "HE 100", "", 34837, Common::DE_DEU, Common::kPlatformWii },
{ "319a4dde52c7960b5aae8a1ec348d918", "monkey", "VGA", "VGA", -1, Common::DE_DEU, Common::kPlatformAmiga },
{ "31aa57f460a3d12429f0552a46a90b39", "puttputt", "Demo", "Demo", 6150, Common::EN_ANY, Common::kPlatformPC },
{ "31b8fda4c8c7413fa6b39997e776eba4", "loom", "FM-TOWNS", "", -1, Common::JA_JPN, Common::kPlatformFMTowns },
@@ -145,7 +146,7 @@ static const MD5Table md5table[] = {
{ "330f631502e381a4e199a3f7cb483c20", "indy3", "EGA", "EGA", -1, Common::DE_DEU, Common::kPlatformAmiga },
{ "33e989f85da700e2014d00f345cab3d7", "puttrace", "HE 98.5", "", -1, Common::FR_FRA, Common::kPlatformWindows },
{ "3433be9866ca4261b2d5d25374e3f243", "monkey", "VGA", "VGA", -1, Common::FR_FRA, Common::kPlatformAmiga },
- { "3486ede0f904789267d4bcc5537a46d4", "puttzoo", "", "Demo", -1, Common::EN_ANY, Common::kPlatformMacintosh },
+ { "3486ede0f904789267d4bcc5537a46d4", "puttzoo", "", "Demo", 14337, Common::EN_ANY, Common::kPlatformMacintosh },
{ "356fb5f680b68251333016175935d126", "BluesABCTime", "HE CUP", "Preview", 4133436, Common::UNK_LANG, Common::kPlatformUnknown },
{ "35a2d3040fa512f8232d9e443319d84d", "dig", "", "", 659335495, Common::EN_ANY, Common::kPlatformMacintosh },
{ "362c1d281fb9899254cda66ad246c66a", "dig", "Demo", "Demo", 3472, Common::EN_ANY, Common::kPlatformUnknown },
@@ -167,6 +168,7 @@ static const MD5Table md5table[] = {
{ "3a3e592b074f595489f7f11e150c398d", "puttzoo", "HE 99", "Updated", -1, Common::EN_USA, Common::kPlatformWindows },
{ "3a5d13675e9a23aedac0bac7730f0ac1", "samnmax", "", "CD", -1, Common::FR_FRA, Common::kPlatformMacintosh },
{ "3a5ec90d556d4920976c5578bfbfaf79", "maniac", "NES", "", -1, Common::DE_DEU, Common::kPlatformNES },
+ { "3ae7f002d9256b8bdf76aaf8a3a069f8", "freddi", "HE 100", "", 34837, Common::EN_GRB, Common::kPlatformWii },
{ "3af61c5edf8e15b43dbafd285b2e9777", "puttcircus", "", "Demo", -1, Common::HE_ISR, Common::kPlatformWindows },
{ "3b301b7892f883ce42ab4be6a274fea6", "samnmax", "Floppy", "Floppy", -1, Common::EN_ANY, Common::kPlatformPC },
{ "3b832f4a90740bf22e9b8ed42ca0128c", "freddi4", "HE 99", "", -1, Common::EN_GRB, Common::kPlatformWindows },
@@ -183,7 +185,7 @@ static const MD5Table md5table[] = {
{ "425205754fa749f4f0b0dd9d09fa45fd", "football", "", "Demo", -1, Common::EN_ANY, Common::kPlatformUnknown },
{ "430bc518017b6fac046f58bab6baad5d", "monkey2", "", "", -1, Common::JA_JPN, Common::kPlatformFMTowns },
{ "439a7f4adf510489981ac52308e7d7a2", "maniac", "C64", "", -1, Common::DE_DEU, Common::kPlatformC64 },
- { "45082a5c9f42ba14dacfe1fdeeba819d", "freddicove", "HE 100", "Demo", -1, Common::EN_ANY, Common::kPlatformUnknown },
+ { "45082a5c9f42ba14dacfe1fdeeba819d", "freddicove", "HE 100", "Demo", 18422, Common::EN_ANY, Common::kPlatformUnknown },
{ "45152f7cf2ba8f43cf8a8ea2e740ae09", "monkey", "VGA", "VGA", 8357, Common::ES_ESP, Common::kPlatformPC },
{ "4521138d15d1fd7649c31fb981746231", "pajama2", "HE 98.5", "Demo", -1, Common::DE_DEU, Common::kPlatformUnknown },
{ "46b53fd430adcfbed791b48a0d4b079f", "funpack", "", "", -1, Common::EN_ANY, Common::kPlatformPC },
@@ -196,6 +198,7 @@ static const MD5Table md5table[] = {
{ "49a1739981a89066b1121fac04b710f4", "spyfox2", "HE CUP", "Preview", 5756234, Common::UNK_LANG, Common::kPlatformUnknown },
{ "4aa93cb30e485b728504ba3a693f12bf", "pajama", "HE 100", "", -1, Common::RU_RUS, Common::kPlatformWindows },
{ "4af4a6b248103c1fe9edef619677f540", "puttmoon", "", "Demo", -1, Common::EN_ANY, Common::kPlatformMacintosh },
+ { "4afb734df8315ee412669c812d4cf0a1", "freddi", "HE 100", "", 34837, Common::FR_FRA, Common::kPlatformWii },
{ "4ba37f835be11a59d969f90f272f575b", "water", "HE 80", "", -1, Common::EN_USA, Common::kPlatformUnknown },
{ "4ba7fb331296c283e73d8f5b2096e551", "samnmax", "", "CD", -1, Common::ES_ESP, Common::kPlatformUnknown },
{ "4bedb49943df95a9c900a5a82ccbe9de", "ft", "", "", -1, Common::FR_FRA, Common::kPlatformUnknown },
@@ -225,11 +228,12 @@ static const MD5Table md5table[] = {
{ "5262a27afcaee04e5c4900220bd463e7", "PuttsFunShop", "", "", -1, Common::EN_USA, Common::kPlatformUnknown },
{ "52a4bae0746a11d7b1e8554e91a6645c", "zak", "V2", "V2", -1, Common::FR_FRA, Common::kPlatformPC },
{ "53e94115b55dd51d4b8ff0871aa1df1e", "spyfox", "", "Demo", 20103, Common::EN_ANY, Common::kPlatformUnknown },
- { "54a936ad06161ff7bfefcb96200f7bff", "monkey", "VGA", "VGA Demo", -1, Common::EN_ANY, Common::kPlatformAmiga },
+ { "54a936ad06161ff7bfefcb96200f7bff", "monkey", "VGA", "VGA Demo", 7617, Common::EN_ANY, Common::kPlatformAmiga },
{ "55518cd73cf9c6d23ea29c51ee06bdfe", "ft", "", "", -1, Common::IT_ITA, Common::kPlatformUnknown },
{ "55e4cc866ff9046824e1c638ba2b8c7f", "ft", "", "", -1, Common::RU_RUS, Common::kPlatformUnknown },
{ "55f4e9402bec2bded383843123f37c5c", "pajama2", "HE 98.5", "", -1, Common::DE_DEU, Common::kPlatformWindows },
{ "566165a7338fa11029e7c14d94fa70d0", "freddi", "HE 73", "Demo", 9800, Common::EN_ANY, Common::kPlatformWindows },
+ { "56b5922751be7ffd771b38dda56b028b", "freddi", "HE 100", "", 34837, Common::NL_NLD, Common::kPlatformWii },
{ "5719fc8a13b4638b78d9d8d12f091f94", "puttrace", "HE 99", "", -1, Common::FR_FRA, Common::kPlatformWindows },
{ "5798972220cd458be2626d54c80f71d7", "atlantis", "Floppy", "Floppy", -1, Common::IT_ITA, Common::kPlatformAmiga },
{ "57a17febe2183f521250e55d55b83e60", "PuttTime", "HE 99", "", -1, Common::FR_FRA, Common::kPlatformWindows },
@@ -242,7 +246,7 @@ static const MD5Table md5table[] = {
{ "59d5cfcc5e672a6e07baae01328b918b", "PuttTime", "HE 90", "Demo", -1, Common::FR_FRA, Common::kPlatformUnknown },
{ "5a35e36fd777e9c37a49c5b2faca52f9", "loom", "EGA", "EGA Demo", 6108, Common::EN_ANY, Common::kPlatformPC },
{ "5b08000a9c47b2887df6506ac767ca68", "fbear", "HE 62", "", -1, Common::EN_ANY, Common::kPlatform3DO },
- { "5bd335265a61caa3d78956ad9f88ba23", "football", "", "Demo", -1, Common::EN_ANY, Common::kPlatformUnknown },
+ { "5bd335265a61caa3d78956ad9f88ba23", "football", "", "Demo", 23135, Common::EN_ANY, Common::kPlatformUnknown },
{ "5c21fc49aee8f46e58fef21579e614a1", "thinker1", "", "", -1, Common::EN_USA, Common::kPlatformUnknown },
{ "5d88b9d6a88e6f8e90cded9d01b7f082", "loom", "VGA", "VGA", 8307, Common::EN_ANY, Common::kPlatformPC },
{ "5dda73606533d66a4c3f4f9ea6e842af", "farm", "", "", 87061, Common::RU_RUS, Common::kPlatformWindows },
@@ -357,6 +361,7 @@ static const MD5Table md5table[] = {
{ "87df3e0074624040407764b7c5e710b9", "pajama", "", "Demo", 18354, Common::NL_NLD, Common::kPlatformWindows },
{ "87f6e8037b7cc996e13474b491a7a98e", "maniac", "V2", "V2", -1, Common::IT_ITA, Common::kPlatformPC },
{ "8801fb4a1200b347f7a38523339526dd", "jungle", "", "", -1, Common::EN_ANY, Common::kPlatformWindows },
+ { "880c5ca5b944648b3f8b03feb41705a8", "freddi", "HE 100", "", 34837, Common::SE_SWE, Common::kPlatformWii },
{ "883af4b0af4f77a92f1dcf1d0a283140", "tentacle", "", "CD", -1, Common::ES_ESP, Common::kPlatformUnknown },
{ "898ce8eb1234a955ef75e87141902bb3", "freddi3", "", "", -1, Common::RU_RUS, Common::kPlatformWindows },
{ "898eaa21f79cf8d4f08db856244689ff", "pajama", "HE 99", "Updated", 66505, Common::EN_ANY, Common::kPlatformWindows },
@@ -366,13 +371,13 @@ static const MD5Table md5table[] = {
{ "8aed489aba45d2b9fb8a04079c9c6e6a", "baseball", "HE CUP", "Preview", 12876596, Common::UNK_LANG, Common::kPlatformUnknown },
{ "8afb3cf9f95abf208358e984f0c9e738", "funpack", "", "", -1, Common::EN_ANY, Common::kPlatform3DO },
{ "8bdb0bf87b5e303dd35693afb9351215", "ft", "", "", -1, Common::DE_DEU, Common::kPlatformUnknown },
- { "8d479e36f35e80257dfc102cf4b8a912", "farm", "HE 72", "Demo", -1, Common::EN_ANY, Common::kPlatformWindows },
+ { "8d479e36f35e80257dfc102cf4b8a912", "farm", "HE 72", "Demo", 34333, Common::EN_ANY, Common::kPlatformWindows },
{ "8de13897f0121c79d29a2377159f9ad0", "socks", "HE 99", "Updated", -1, Common::EN_ANY, Common::kPlatformWindows },
{ "8e3241ddd6c8dadf64305e8740d45e13", "balloon", "HE 100", "Updated", -1, Common::EN_ANY, Common::kPlatformUnknown },
{ "8e4ee4db46954bfe2912e259a16fad82", "monkey2", "", "", -1, Common::FR_FRA, Common::kPlatformPC },
{ "8e9417564f33790815445b2136efa667", "atlantis", "", "CD", 11915, Common::JA_JPN, Common::kPlatformMacintosh },
{ "8e9830a6f2702be5b22c8fa0a6aaf977", "freddi2", "HE 80", "", -1, Common::NL_NLD, Common::kPlatformMacintosh },
- { "8eb84cee9b429314c7f0bdcf560723eb", "monkey", "FM-TOWNS", "", -1, Common::EN_ANY, Common::kPlatformFMTowns },
+ { "8eb84cee9b429314c7f0bdcf560723eb", "monkey", "FM-TOWNS", "", 9925, Common::EN_ANY, Common::kPlatformFMTowns },
{ "8ee63cafb1fe9d62aa0d5a23117e70e7", "freddi2", "HE 100", "Updated", -1, Common::EN_USA, Common::kPlatformUnknown },
{ "8f3758ff98c9c5d78e5d635222cad026", "atlantis", "Floppy", "Floppy", -1, Common::IT_ITA, Common::kPlatformPC },
{ "8fec68383202d38c0d25e9e3b757c5df", "comi", "Demo", "Demo", 18041, Common::UNK_LANG, Common::kPlatformWindows },
@@ -413,7 +418,7 @@ static const MD5Table md5table[] = {
{ "9d7b67be003fea60be4dcbd193611936", "ft", "Demo", "Demo", 11164, Common::EN_ANY, Common::kPlatformMacintosh },
{ "9dc02577bf50d4cfaf3de3fbac06fbe2", "puttmoon", "", "", -1, Common::EN_ANY, Common::kPlatformMacintosh },
{ "9e5e0fb43bd22f4628719b7501adb717", "monkey", "No AdLib", "EGA", -1, Common::FR_FRA, Common::kPlatformAtariST },
- { "9fd66fb3b04703bd50da4356e4202558", "spyfox2", "", "", -1, Common::EN_ANY, Common::kPlatformMacintosh },
+ { "9fd66fb3b04703bd50da4356e4202558", "spyfox2", "", "", 51295, Common::EN_ANY, Common::kPlatformMacintosh },
{ "a00554c31d623fdb9fcb0f924b89b42b", "loom", "EGA", "EGA Demo", -1, Common::EN_ANY, Common::kPlatformPC },
{ "a01fab4a64d47b96e2e58e6b0f825cc7", "monkey", "VGA", "VGA", 8347, Common::FR_FRA, Common::kPlatformPC },
{ "a095616d2d23ccf43b8e257711202cba", "football2002", "", "", -1, Common::EN_ANY, Common::kPlatformUnknown },
@@ -430,6 +435,7 @@ static const MD5Table md5table[] = {
{ "a561d2e2413cc1c71d5a1bf87bf493ea", "lost", "HE 100", "Updated", -1, Common::EN_USA, Common::kPlatformUnknown },
{ "a56e8d9d4281c53c3f63c9bd22a59e21", "catalog", "HE CUP", "Preview", 10978342, Common::EN_ANY, Common::kPlatformUnknown },
{ "a570381b028972d891052ee1e51dc011", "maniac", "V2", "V2", 1988, Common::EN_ANY, Common::kPlatformAtariST },
+ { "a59a438cb182124c30c4447d8ed469e9", "freddi", "HE 100", "", 34837, Common::NB_NOR, Common::kPlatformWii },
{ "a5c5388da9bf0e6662fdca8813a79d13", "farm", "", "", 86962, Common::EN_ANY, Common::kPlatformWindows },
{ "a654fb60c3b67d6317a7894ffd9f25c5", "pajama3", "", "Demo", -1, Common::EN_USA, Common::kPlatformUnknown },
{ "a7cacad9c40c4dc9e1812abf6c8af9d5", "puttcircus", "", "Demo", -1, Common::EN_ANY, Common::kPlatformUnknown },
@@ -489,13 +495,13 @@ static const MD5Table md5table[] = {
{ "c4787c3e8b5e2dfda90850ee800af00f", "zak", "V2", "V2", -1, Common::FR_FRA, Common::kPlatformPC },
{ "c4ffae9fac495475d6bc3343ccc8faf9", "Soccer2004", "", "", -1, Common::EN_ANY, Common::kPlatformUnknown },
{ "c5cc7cba02a2fbd539c4439e775b0536", "puttzoo", "HE 99", "Updated", 43470, Common::DE_DEU, Common::kPlatformWindows },
- { "c5d10e190d4b4d59114b824f2fdbd00e", "loom", "FM-TOWNS", "", -1, Common::EN_ANY, Common::kPlatformFMTowns },
+ { "c5d10e190d4b4d59114b824f2fdbd00e", "loom", "FM-TOWNS", "", 7540, Common::EN_ANY, Common::kPlatformFMTowns },
{ "c63ee46143ba65f9ce14cf539ca51bd7", "atlantis", "Floppy", "Floppy", -1, Common::EN_ANY, Common::kPlatformPC },
{ "c666a998af90d81db447eccba9f72c8d", "monkey", "No AdLib", "EGA", -1, Common::EN_ANY, Common::kPlatformAtariST },
{ "c6907d44f1166941d982864cd42cdc89", "pajama2", "HE 99", "", -1, Common::DE_DEU, Common::kPlatformUnknown },
{ "c782fbbe74a987c3df8ac73cd3e289ed", "freddi", "HE 73", "", -1, Common::SE_SWE, Common::kPlatformMacintosh },
{ "c7890e038806df2bb5c0c8c6f1986ea2", "monkey", "VGA", "VGA", -1, Common::EN_ANY, Common::kPlatformPC },
- { "c7be10f775404fd9785a8b92a06d240c", "atlantis", "", "", -1, Common::EN_ANY, Common::kPlatformFMTowns },
+ { "c7be10f775404fd9785a8b92a06d240c", "atlantis", "", "", 12030, Common::EN_ANY, Common::kPlatformFMTowns },
{ "c7c492a107ec520d7a7943037d0ca54a", "freddi", "HE 71", "Demo", -1, Common::NL_NLD, Common::kPlatformWindows },
{ "c83079157ec765a28de445aec9768d60", "tentacle", "", "Demo", 7477, Common::EN_ANY, Common::kPlatformUnknown },
{ "c8575e0b973ff1723aba6cd92c642db2", "puttrace", "HE 99", "Demo", -1, Common::FR_FRA, Common::kPlatformWindows },
@@ -519,7 +525,7 @@ static const MD5Table md5table[] = {
{ "cf4ef315214c7d8cdab6302cdb7e50db", "freddi", "HE 73", "Demo", -1, Common::DE_DEU, Common::kPlatformWindows },
{ "cf8d13446ec6cb6222287a925fd47c1d", "baseball", "", "", -1, Common::EN_ANY, Common::kPlatformUnknown },
{ "cf8ef3a1fb483c5c4b1c584d1167b2c4", "freddi", "HE 73", "", -1, Common::DE_DEU, Common::kPlatformWindows },
- { "cf90b4db5486ef798db78fe6fbf897e5", "pajama3", "", "Demo", -1, Common::EN_USA, Common::kPlatformWindows },
+ { "cf90b4db5486ef798db78fe6fbf897e5", "pajama3", "", "Demo", 13902, Common::EN_USA, Common::kPlatformWindows },
{ "d00ffc8c32d17e575fd985d435d2eb88", "arttime", "", "Demo", -1, Common::EN_ANY, Common::kPlatformUnknown },
{ "d0549508a06bbb9f99ed19c9e97891f3", "football2002", "", "", -1, Common::EN_ANY, Common::kPlatformUnknown },
{ "d06fbe28818fef7bfc45c2cdf0c0849d", "zak", "V2", "V2", -1, Common::DE_DEU, Common::kPlatformPC },
@@ -554,14 +560,14 @@ static const MD5Table md5table[] = {
{ "dbf4d59d70b826733f379f998354d350", "BluesBirthday", "", "Demo", -1, Common::EN_ANY, Common::kPlatformUnknown },
{ "dcf0119a90451a7d6e0f1920931ba130", "freddi4", "HE 99", "Demo", -1, Common::FR_FRA, Common::kPlatformWindows },
{ "dd30a53035393baa5a5e222e716559af", "maniac", "V2", "V2", -1, Common::FR_FRA, Common::kPlatformAtariST },
- { "de4efb910210736813c9a1185384bace", "puttzoo", "HE 72", "Demo", -1, Common::EN_ANY, Common::kPlatformWindows },
+ { "de4efb910210736813c9a1185384bace", "puttzoo", "HE 72", "Demo", 14337, Common::EN_ANY, Common::kPlatformWindows },
{ "debe337f73d660e951ece7c1f1c81add", "zak", "V2", "V2", -1, Common::EN_ANY, Common::kPlatformPC },
{ "defb8cb9ec4b0f91acfb6b61c6129ad9", "PuttTime", "HE 99", "", -1, Common::RU_RUS, Common::kPlatformWindows },
{ "df03ee021aa9b81d90cab9c26da07614", "indy3", "EGA", "EGA", -1, Common::IT_ITA, Common::kPlatformAmiga },
{ "df047cc4792150f601290357566d36a6", "freddi", "HE 90", "Updated", -1, Common::EN_USA, Common::kPlatformUnknown },
{ "e01acc8c12ef44e8f778fe87e5f90f4e", "fbpack", "", "", -1, Common::EN_ANY, Common::kPlatform3DO },
{ "e03ed1474ec14de78359970e0457a820", "freddi4", "HE 99", "Demo", -1, Common::EN_GRB, Common::kPlatformWindows },
- { "e144f5f49d9241d2a9dee2576b3d09cb", "airport", "", "Demo", -1, Common::EN_ANY, Common::kPlatformWindows },
+ { "e144f5f49d9241d2a9dee2576b3d09cb", "airport", "", "Demo", 51152, Common::EN_ANY, Common::kPlatformWindows },
{ "e17db1ddf91b39ca6bbc8ad3ed19e883", "monkey", "FM-TOWNS", "", -1, Common::JA_JPN, Common::kPlatformFMTowns },
{ "e246e02db9630533a40d99c9f54a8e01", "monkey2", "", "", -1, Common::EN_ANY, Common::kPlatformMacintosh },
{ "e361a7058ed8e8ebb462663c0a3ae8d6", "puttputt", "HE 62", "", -1, Common::HE_ISR, Common::kPlatformPC },
@@ -576,12 +582,13 @@ static const MD5Table md5table[] = {
{ "e6cd81b25ab1453a8a6d3482118c391e", "pass", "", "", 7857, Common::EN_ANY, Common::kPlatformPC },
{ "e72bb4c2b613db2cf50f89ff6350e70a", "ft", "", "", -1, Common::ES_ESP, Common::kPlatformUnknown },
{ "e781230da44a44e2f0770edb2b3b3633", "maniac", "V2", "V2", -1, Common::EN_ANY, Common::kPlatformAmiga },
+ { "e8d0697906e53fee8b7e9f5652696da8", "atlantis", "", "CD", 11915, Common::JA_JPN, Common::kPlatformPC },
{ "e94c7cc3686fce406d3c91b5eae5a72d", "zak", "V2", "V2", -1, Common::EN_ANY, Common::kPlatformAmiga },
{ "e95cf980719c0be078fb68a67af97b4a", "funpack", "", "", -1, Common::JA_JPN, Common::kPlatform3DO },
{ "e98b982ceaf9d253d730bde8903233d6", "monkey", "EGA", "EGA", -1, Common::DE_DEU, Common::kPlatformPC },
{ "eae95b2b3546d8ba86ae1d397c383253", "dog", "", "", -1, Common::EN_ANY, Common::kPlatformUnknown },
{ "eb700bb73ca1cc44a1ad5e4b1a4bdeaf", "indy3", "EGA", "EGA", 5361, Common::DE_DEU, Common::kPlatformPC },
- { "ebd0b2c8a387f18887282afe6cad894a", "spyozon", "", "Demo", -1, Common::EN_ANY, Common::kPlatformUnknown },
+ { "ebd0b2c8a387f18887282afe6cad894a", "spyozon", "", "Demo", 15317, Common::EN_ANY, Common::kPlatformUnknown },
{ "ebd324dcf06a4c49e1ba5c231eee1060", "freddi4", "HE 99", "Demo", -1, Common::EN_USA, Common::kPlatformUnknown },
{ "ecc4340c2b801f5af8da4e00c0e432d9", "puttcircus", "", "", -1, Common::NL_NLD, Common::kPlatformUnknown },
{ "ed2b074bc3166087a747acb2a3c6abb0", "freddi3", "HE 98.5", "Demo", -1, Common::DE_DEU, Common::kPlatformUnknown },
@@ -617,7 +624,7 @@ static const MD5Table md5table[] = {
{ "fbbbb38a81fc9d6a61d509278390a290", "farm", "", "", -1, Common::EN_ANY, Common::kPlatformMacintosh },
{ "fbdd947d21e8f5bac6d6f7a316af1c5a", "spyfox", "", "Demo", 15693, Common::EN_ANY, Common::kPlatformUnknown },
{ "fc53ce0e5f6562b1c1e1b4b8203acafb", "samnmax", "Floppy", "Floppy", -1, Common::ES_ESP, Common::kPlatformPC },
- { "fc6b6148e80d67939d9a18697c0f626a", "monkey", "EGA", "EGA", -1, Common::DE_DEU, Common::kPlatformPC },
+ { "fc6b6148e80d67939d9a18697c0f626a", "monkey", "EGA", "EGA", 8367, Common::DE_DEU, Common::kPlatformPC },
{ "fc8d197a22146e74766e9cb0cfcaf1da", "freddi2", "HE 80", "Demo", 27298, Common::EN_ANY, Common::kPlatformUnknown },
{ "fcb78ebecab2757264c590890c319cc5", "PuttTime", "HE 85", "", -1, Common::NL_NLD, Common::kPlatformUnknown },
{ "fce4b8010704b103acfeea9413788f32", "freddi2", "HE 80", "", -1, Common::DE_DEU, Common::kPlatformUnknown },
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index 2d83622f05..86ec8c4748 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -1634,28 +1634,28 @@ void ScummEngine_v100he::resetScumm() {
#endif
void ScummEngine::setupMusic(int midi) {
- MidiDriverType midiDriver = MidiDriver::detectMusicDriver(midi);
- _native_mt32 = ((midiDriver == MD_MT32) || ConfMan.getBool("native_mt32"));
+ MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(midi);
+ _native_mt32 = ((MidiDriver::getMusicType(dev) == MT_MT32) || ConfMan.getBool("native_mt32"));
- switch (midiDriver) {
- case MD_NULL:
+ switch (MidiDriver::getMusicType(dev)) {
+ case MT_NULL:
_musicType = MDT_NONE;
break;
- case MD_PCSPK:
- case MD_PCJR:
+ case MT_PCSPK:
+ case MT_PCJR:
_musicType = MDT_PCSPK;
break;
- case MD_CMS:
+ //case MT_CMS:
#if 1
_musicType = MDT_ADLIB;
#else
_musicType = MDT_CMS; // Still has number of bugs, disable by default
#endif
break;
- case MD_TOWNS:
+ case MT_TOWNS:
_musicType = MDT_TOWNS;
break;
- case MD_ADLIB:
+ case MT_ADLIB:
_musicType = MDT_ADLIB;
break;
default:
@@ -1707,7 +1707,7 @@ void ScummEngine::setupMusic(int midi) {
if (!_mixer->isReady()) {
warning("Sound mixer initialization failed");
if (_musicType == MDT_ADLIB || _musicType == MDT_PCSPK || _musicType == MDT_CMS) {
- midiDriver = MD_NULL;
+ dev = 0;
_musicType = MDT_NONE;
warning("MIDI driver depends on sound mixer, switching to null MIDI driver");
}
@@ -1735,11 +1735,11 @@ void ScummEngine::setupMusic(int midi) {
} else if (_game.platform == Common::kPlatformAmiga && _game.version <= 4) {
_musicEngine = new Player_V4A(this, _mixer);
} else if (_game.id == GID_MANIAC && _game.version == 1) {
- _musicEngine = new Player_V1(this, _mixer, midiDriver != MD_PCSPK);
+ _musicEngine = new Player_V1(this, _mixer, MidiDriver::getMusicType(dev) != MT_PCSPK);
} else if (_game.version <= 2) {
- _musicEngine = new Player_V2(this, _mixer, midiDriver != MD_PCSPK);
+ _musicEngine = new Player_V2(this, _mixer, MidiDriver::getMusicType(dev) != MT_PCSPK);
} else if ((_musicType == MDT_PCSPK) && (_game.version > 2 && _game.version <= 4)) {
- _musicEngine = new Player_V2(this, _mixer, midiDriver != MD_PCSPK);
+ _musicEngine = new Player_V2(this, _mixer, MidiDriver::getMusicType(dev) != MT_PCSPK);
} else if (_musicType == MDT_CMS) {
_musicEngine = new Player_V2CMS(this, _mixer);
} else if (_game.platform == Common::kPlatform3DO && _game.heversion <= 62) {
@@ -1749,12 +1749,12 @@ void ScummEngine::setupMusic(int midi) {
MidiDriver *adlibMidiDriver = 0;
if (_musicType != MDT_ADLIB)
- nativeMidiDriver = MidiDriver::createMidi(midiDriver);
+ nativeMidiDriver = MidiDriver::createMidi(dev);
if (nativeMidiDriver != NULL && _native_mt32)
nativeMidiDriver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
bool multi_midi = ConfMan.getBool("multi_midi") && _musicType != MDT_NONE && (midi & MDT_ADLIB);
if (_musicType == MDT_ADLIB || multi_midi) {
- adlibMidiDriver = MidiDriver_ADLIB_create();
+ adlibMidiDriver = MidiDriver::createMidi(MidiDriver::detectDevice(MDT_ADLIB));
adlibMidiDriver->property(MidiDriver::PROP_OLD_ADLIB, (_game.features & GF_SMALL_HEADER) ? 1 : 0);
}
@@ -1769,7 +1769,7 @@ void ScummEngine::setupMusic(int midi) {
// YM2162 driver can't handle midi->getPercussionChannel(), NULL shouldn't init MT-32/GM/GS
if ((midi != MDT_TOWNS) && (midi != MDT_NONE)) {
_imuse->property(IMuse::PROP_NATIVE_MT32, _native_mt32);
- if (midiDriver != MD_MT32) // MT-32 Emulation shouldn't be GM/GS initialized
+ if (MidiDriver::getMusicType(dev) != MT_MT32) // MT-32 Emulation shouldn't be GM/GS initialized
_imuse->property(IMuse::PROP_GS, _enable_gs);
}
if (_game.heversion >= 60 || midi == MDT_TOWNS) {
@@ -2081,6 +2081,12 @@ void ScummEngine::scummLoop_updateScummVars() {
if (_game.version >= 7) {
VAR(VAR_CAMERA_POS_X) = camera._cur.x;
VAR(VAR_CAMERA_POS_Y) = camera._cur.y;
+ } else if (_game.platform == Common::kPlatformNES) {
+ // WORKAROUND:
+ // Since there are 2 2-stripes wide borders in MM NES screen,
+ // we have to compensate for it here. This fixes paning effects.
+ // Fixes bug #1328120: "MANIACNES: Screen width incorrect, camera halts sometimes"
+ VAR(VAR_CAMERA_POS_X) = (camera._cur.x >> V12_X_SHIFT) + 2;
} else if (_game.version <= 2) {
VAR(VAR_CAMERA_POS_X) = camera._cur.x >> V12_X_SHIFT;
} else {
diff --git a/engines/scumm/sound.cpp b/engines/scumm/sound.cpp
index 98fa3d0e4a..840f8fc779 100644
--- a/engines/scumm/sound.cpp
+++ b/engines/scumm/sound.cpp
@@ -1788,7 +1788,7 @@ static void convertADResource(ResourceManager *res, const GameSettings& game, in
// There is a constant delay of ppqn/3 before the music starts.
if (ppqn / 3 >= 128)
- *ptr++ = (ppqn / 3 >> 7) | 0x80;
+ *ptr++ = ((ppqn / 3) >> 7) | 0x80;
*ptr++ = ppqn / 3 & 0x7f;
// Now copy the actual music data
diff --git a/engines/scumm/string.cpp b/engines/scumm/string.cpp
index 480e18e514..62fdf48440 100644
--- a/engines/scumm/string.cpp
+++ b/engines/scumm/string.cpp
@@ -1351,6 +1351,8 @@ void ScummEngine_v7::loadLanguageBundle() {
// File contains Korean text (Hangul). just ignore it
} else if (*ptr == 'j') {
// File contains Japanese text. just ignore it
+ } else if (*ptr == 'c') {
+ // File contains Chinese text. just ignore it
} else if (*ptr == 'e') {
// File is encoded!
enc = 0x13;
diff --git a/engines/scumm/verbs.h b/engines/scumm/verbs.h
index 96a49a7ced..83e924edac 100644
--- a/engines/scumm/verbs.h
+++ b/engines/scumm/verbs.h
@@ -31,7 +31,7 @@
namespace Scumm {
/**
- * The area in which some click (or key press) occured and which is passed
+ * The area in which some click (or key press) occurred and which is passed
* to the input script.
*/
enum ClickArea {
diff --git a/engines/sky/sky.cpp b/engines/sky/sky.cpp
index 9ea20aafc6..45b3cab947 100644
--- a/engines/sky/sky.cpp
+++ b/engines/sky/sky.cpp
@@ -259,16 +259,16 @@ Common::Error SkyEngine::init() {
_systemVars.gameVersion = _skyDisk->determineGameVersion();
- MidiDriverType midiDriver = MidiDriver::detectMusicDriver(MDT_ADLIB | MDT_MIDI | MDT_PREFER_MIDI);
- if (midiDriver == MD_ADLIB) {
+ MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32);
+ if (MidiDriver::getMusicType(dev) == MT_ADLIB) {
_systemVars.systemFlags |= SF_SBLASTER;
_skyMusic = new AdLibMusic(_mixer, _skyDisk);
} else {
_systemVars.systemFlags |= SF_ROLAND;
- if ((midiDriver == MD_MT32) || ConfMan.getBool("native_mt32"))
- _skyMusic = new MT32Music(MidiDriver::createMidi(midiDriver), _skyDisk);
+ if ((MidiDriver::getMusicType(dev) == MT_MT32) || ConfMan.getBool("native_mt32"))
+ _skyMusic = new MT32Music(MidiDriver::createMidi(dev), _skyDisk);
else
- _skyMusic = new GmMusic(MidiDriver::createMidi(midiDriver), _skyDisk);
+ _skyMusic = new GmMusic(MidiDriver::createMidi(dev), _skyDisk);
}
if (isCDVersion()) {
diff --git a/engines/sword1/music.cpp b/engines/sword1/music.cpp
index 91c943472c..23cc30e4b1 100644
--- a/engines/sword1/music.cpp
+++ b/engines/sword1/music.cpp
@@ -107,7 +107,7 @@ bool MusicHandle::play(const char *fileBase, bool loop) {
if (!_audioSource) {
sprintf(fileName, "%s.aif", fileBase);
if (_file.open(fileName))
- _audioSource = Audio::makeLoopingAudioStream(Audio::makeAIFFStream(_file), loop ? 0 : 1);
+ _audioSource = Audio::makeLoopingAudioStream(Audio::makeAIFFStream(&_file, DisposeAfterUse::NO), loop ? 0 : 1);
}
if (!_audioSource)
diff --git a/engines/teenagent/detection.cpp b/engines/teenagent/detection.cpp
index 258bd982ed..a2dab9658d 100644
--- a/engines/teenagent/detection.cpp
+++ b/engines/teenagent/detection.cpp
@@ -91,7 +91,9 @@ static const ADParams detectionParams = {
"teenagent",
0,
0,
- Common::GUIO_NONE
+ Common::GUIO_NONE,
+ 1,
+ 0
};
#define MAX_SAVES 20
diff --git a/engines/tinsel/cliprect.cpp b/engines/tinsel/cliprect.cpp
index 69a71b874f..5f287d3eab 100644
--- a/engines/tinsel/cliprect.cpp
+++ b/engines/tinsel/cliprect.cpp
@@ -28,17 +28,15 @@
#include "tinsel/graphics.h" // normal object drawing
#include "tinsel/object.h"
#include "tinsel/palette.h"
+#include "tinsel/tinsel.h" // for _vm
namespace Tinsel {
-/** list of all clip rectangles */
-static RectList s_rectList;
-
/**
* Resets the clipping rectangle allocator.
*/
void ResetClipRect() {
- s_rectList.clear();
+ _vm->_clipRects.clear();
}
/**
@@ -46,11 +44,11 @@ void ResetClipRect() {
* @param pClip clip rectangle dimensions to allocate
*/
void AddClipRect(const Common::Rect &pClip) {
- s_rectList.push_back(pClip);
+ _vm->_clipRects.push_back(pClip);
}
const RectList &GetClipRects() {
- return s_rectList;
+ return _vm->_clipRects;
}
/**
@@ -175,6 +173,8 @@ void FindMovingObjects(OBJECT *pObjList, Common::Point *pWin, Common::Rect *pCli
* the total number of clip rectangles.
*/
void MergeClipRect() {
+ RectList &s_rectList = _vm->_clipRects;
+
if (s_rectList.size() <= 1)
return;
diff --git a/engines/tinsel/detection.cpp b/engines/tinsel/detection.cpp
index 70a2f475ee..d6bdad6032 100644
--- a/engines/tinsel/detection.cpp
+++ b/engines/tinsel/detection.cpp
@@ -75,549 +75,7 @@ static const PlainGameDescriptor tinselGames[] = {
{0, 0}
};
-
-namespace Tinsel {
-
-using Common::GUIO_NONE;
-using Common::GUIO_NOSPEECH;
-using Common::GUIO_NOSFX;
-using Common::GUIO_NOMUSIC;
-
-static const TinselGameDescription gameDescriptions[] = {
-
- // Note: The following is the (hopefully) definitive list of version details:
- // TINSEL_V0: Used only by the Discworld 1 demo - this used a more primitive version
- // of the Tinsel engine and graphics compression
- // TINSEL_V1: There were two versions of the Discworld 1 game - the first used .GRA
- // files, and the second used .SCN files. The second also provided some fixes to
- // various script bugs and coding errors, but is still considered TINSEL_V1,
- // as both game versions work equally well with the newer code.
- // TINSEL_V2: The Discworld 2 game used this updated version of the Tinsel 1 engine,
- // and as far as we know there aren't any variations of this engine.
-
- { // Floppy Demo V0 from http://www.adventure-treff.de/specials/dl_demos.php
- {
- "dw",
- "Floppy Demo",
- AD_ENTRY1s("dw.gra", "ce1b57761ba705221bcf70955b827b97", 441192),
- //AD_ENTRY1s("dw.scn", "ccd72f02183d0e96b6e7d8df9492cda8", 23308),
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_DEMO,
- GUIO_NOSPEECH | GUIO_NOSFX | GUIO_NOMUSIC
- },
- GID_DW1,
- 0,
- GF_DEMO,
- TINSEL_V0,
- },
-
- { // CD Demo V1 version, with *.gra files
- {
- "dw",
- "CD Demo",
- {
- {"dw.gra", 0, "ef5a2518c9e205f786f5a4526396e661", 781676},
- {"english.smp", 0, NULL, -1},
- },
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_DEMO,
- GUIO_NONE
- },
- GID_DW1,
- 0,
- GF_CD,
- TINSEL_V1,
- },
-
- { // Multilingual Floppy V1 with *.gra files.
- // Note: It contains no english subtitles.
- {
- "dw",
- "Floppy",
- {
- {"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
- {"french.txt", 0, NULL, -1},
- {"german.txt", 0, NULL, -1},
- {"italian.txt", 0, NULL, -1},
- {"spanish.txt", 0, NULL, -1},
- {NULL, 0, NULL, 0}
- },
- Common::FR_FRA,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE,
- GUIO_NOSPEECH
- },
- GID_DW1,
- 0,
- GF_FLOPPY | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
- TINSEL_V1,
- },
-
- { // Floppy V1 version, with *.gra files
- {
- "dw",
- "Floppy",
- AD_ENTRY1s("dw.gra", "c8808ccd988d603dd35dff42013ae7fd", 781656),
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NOSPEECH
- },
- GID_DW1,
- 0,
- GF_FLOPPY | GF_ENHANCED_AUDIO_SUPPORT,
- TINSEL_V1,
- },
-
- { // CD V1 version, with *.gra files (same as the floppy one, with english.smp)
- {
- "dw",
- "CD",
- {
- {"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
- {"english.smp", 0, NULL, -1},
- },
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GID_DW1,
- 0,
- GF_CD | GF_ENHANCED_AUDIO_SUPPORT,
- TINSEL_V1,
- },
-
- { // Italian CD with english speech and *.gra files.
- // Note: It contains only italian subtitles, but inside english.txt
- {
- "dw",
- "CD",
- {
- {"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
- {"english.txt", 0, "15f0703f85477d7fab4280bf938b61c1", 237774},
- {"english.smp", 0, NULL, -1},
- {NULL, 0, NULL, 0}
- },
- Common::IT_ITA,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE,
- GUIO_NONE
- },
- GID_DW1,
- 0,
- GF_CD | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
- TINSEL_V1,
- },
-
- { // Multilingual CD with english speech and *.gra files.
- // Note: It contains no english subtitles.
- {
- "dw",
- "CD",
- {
- {"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
- {"english.smp", 0, NULL, -1},
- {"french.txt", 0, NULL, -1},
- {"german.txt", 0, NULL, -1},
- {"italian.txt", 0, NULL, -1},
- {"spanish.txt", 0, NULL, -1},
- {NULL, 0, NULL, 0}
- },
- Common::FR_FRA,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE,
- GUIO_NONE
- },
- GID_DW1,
- 0,
- GF_CD | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
- TINSEL_V1,
- },
-
- {
- {
- "dw",
- "CD",
- {
- {"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
- {"english.smp", 0, NULL, -1},
- {"french.txt", 0, NULL, -1},
- {"german.txt", 0, NULL, -1},
- {"italian.txt", 0, NULL, -1},
- {"spanish.txt", 0, NULL, -1},
- {NULL, 0, NULL, 0}
- },
- Common::DE_DEU,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE,
- GUIO_NONE
- },
- GID_DW1,
- 0,
- GF_CD | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
- TINSEL_V1,
- },
- {
- {
- "dw",
- "CD",
- {
- {"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
- {"english.smp", 0, NULL, -1},
- {"french.txt", 0, NULL, -1},
- {"german.txt", 0, NULL, -1},
- {"italian.txt", 0, NULL, -1},
- {"spanish.txt", 0, NULL, -1},
- {NULL, 0, NULL, 0}
- },
- Common::IT_ITA,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE,
- GUIO_NONE
- },
- GID_DW1,
- 0,
- GF_CD | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
- TINSEL_V1,
- },
- {
- {
- "dw",
- "CD",
- {
- {"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
- {"english.smp", 0, NULL, -1},
- {"french.txt", 0, NULL, -1},
- {"german.txt", 0, NULL, -1},
- {"italian.txt", 0, NULL, -1},
- {"spanish.txt", 0, NULL, -1},
- {NULL, 0, NULL, 0}
- },
- Common::ES_ESP,
- Common::kPlatformPC,
- ADGF_DROPLANGUAGE,
- GUIO_NONE
- },
- GID_DW1,
- 0,
- GF_CD | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
- TINSEL_V1,
- },
-
- { // English CD v2
- {
- "dw",
- "CD",
- {
- {"dw.scn", 0, "70955425870c7720d6eebed903b2ef41", 776188},
- {"english.smp", 0, NULL, -1},
- {NULL, 0, NULL, 0}
- },
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GID_DW1,
- 0,
- GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
- TINSEL_V1,
- },
-
- { // Hebrew CD v2
- {
- "dw",
- "CD",
- {
- {"dw.scn", 0, "759d1374b4f02af6d52fc07c96679936", 770780},
- {"english.smp", 0, NULL, -1},
- {NULL, 0, NULL, 0}
- },
- Common::HE_ISR,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GID_DW1,
- 0,
- GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
- TINSEL_V1,
- },
-
- { // Discworld PSX CD
- {
- "dw",
- "CD",
- {
- {"english.txt", 0, "7526cfc3a64e00f223795de476b4e2c9", 230326},
- {NULL, 0, NULL, 0}
- },
- Common::EN_ANY,
- Common::kPlatformPSX,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GID_DW1,
- 0,
- GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
- TINSEL_V1,
- },
-
- { // multilanguage PSX demo
- {
- "dw",
- "CD demo",
- {
- {"french.txt", 0, "e7020d35f58d0d187052ac406d86cc87", 273914},
- {"german.txt", 0, "52f0a01e0ff0d340b02a36fd5109d705", 263942},
- {"italian.txt", 0, "15f0703f85477d7fab4280bf938b61c1", 239834},
- {"spanish.txt", 0, "c324170c3f1922c605c5cc09ba265aa5", 236702},
- {"english.txt", 0, "7526cfc3a64e00f223795de476b4e2c9", 230326},
- {NULL, 0, NULL, 0}
- },
- Common::EN_ANY,
- Common::kPlatformPSX,
- ADGF_DEMO,
- GUIO_NONE
- },
- GID_DW1,
- 0,
- GF_CD | GF_SCNFILES,
- TINSEL_V1,
- },
-
-#if 0
- { // English Saturn CD
- {
- "dw",
- "CD",
- {
- {"dw.scn", 0, "6803f293c88758057cc685b9437f7637", 382248},
- {"english.smp", 0, NULL, -1},
- {NULL, 0, NULL, 0}
- },
- Common::EN_ANY,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GID_DW1,
- 0,
- GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
- TINSEL_V1,
- },
-#endif
-
-#if 0
- { // Mac multilanguage CD
- {
- "dw",
- "CD",
- {
- {"dw.scn", 0, "cfc40a8d5d476a1c9d3abf826fa46f8c", 1265532},
- {"english.smp", 0, NULL, -1},
- {NULL, 0, NULL, 0}
- },
- Common::EN_ANY,
- Common::kPlatformMacintosh,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GID_DW1,
- 0,
- GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
- TINSEL_V1,
- },
-
-#endif
-
- { // German CD re-release "Neon Edition"
- // Note: This release has ENGLISH.TXT (with german content) instead of GERMAN.TXT
- {
- "dw",
- "CD",
- AD_ENTRY1s("dw.scn", "6182c7986eaec893c62fb6ea13a9f225", 774556),
- Common::DE_DEU,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GID_DW1,
- 0,
- GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
- TINSEL_V1,
- },
-
- { // Russian Discworld 1
- {
- "dw",
- "CD",
- {
- {"dw.scn", 0, "133041bde59d05c1bf084fd6f1bdce4b", 776524},
- {"english.txt", 0, "f73dcbd7b136b37c2adf7c9448ea336d", 231821},
- {"english.smp", 0, NULL, -1},
- {NULL, 0, NULL, 0}
- },
- Common::RU_RUS,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GID_DW1,
- 0,
- GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
- TINSEL_V1,
- },
-
- { // European/Australian Discworld 2 release
- {
- "dw2",
- "CD",
- {
- {"dw2.scn", 0, "c6d15ce9720a9d8fef06e6582dcf3f34", 103593},
- {"english1.smp", 0, NULL, -1},
- {NULL, 0, NULL, 0}
- },
- Common::EN_GRB,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GID_DW2,
- 0,
- GF_CD | GF_SCNFILES,
- TINSEL_V2,
- },
-
- { // US Discworld 2 release
- {
- "dw2",
- "CD",
- {
- {"dw2.scn", 0, "c6d15ce9720a9d8fef06e6582dcf3f34", 103593},
- {"us1.smp", 0, NULL, -1},
- {NULL, 0, NULL, 0}
- },
- Common::EN_USA,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GID_DW2,
- 0,
- GF_CD | GF_SCNFILES,
- TINSEL_V2,
- },
-
- { // French version of Discworld 2
- {
- "dw2",
- "CD",
- {
- {"dw2.scn", 0, "c6d15ce9720a9d8fef06e6582dcf3f34", 103593},
- {"french1.smp", 0, NULL, -1},
- {NULL, 0, NULL, 0}
- },
- Common::FR_FRA,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GID_DW2,
- 0,
- GF_CD | GF_SCNFILES,
- TINSEL_V2,
- },
-
- { // German Discworld 2 re-release "Neon Edition"
- {
- "dw2",
- "CD",
- {
- {"dw2.scn", 0, "c6d15ce9720a9d8fef06e6582dcf3f34", 103593},
- {"german1.smp", 0, NULL, -1},
- {NULL, 0, NULL, 0}
- },
- Common::DE_DEU,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GID_DW2,
- 0,
- GF_CD | GF_SCNFILES,
- TINSEL_V2,
- },
-
- { // Italian/Spanish Discworld 2
- {
- "dw2",
- "CD",
- {
- {"dw2.scn", 0, "c6d15ce9720a9d8fef06e6582dcf3f34", 103593},
- {"english1.smp", 0, NULL, -1},
- {"italian1.txt", 0, "d443249f8b55489b5888c227b9096f4e", 246495},
- {NULL, 0, NULL, 0}
- },
- Common::IT_ITA,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GID_DW2,
- 0,
- GF_CD | GF_SCNFILES,
- TINSEL_V2,
- },
- {
- {
- "dw2",
- "CD",
- {
- {"dw2.scn", 0, "c6d15ce9720a9d8fef06e6582dcf3f34", 103593},
- {"english1.smp", 0, NULL, -1},
- {"spanish1.txt", 0, "bc6e147c5f542db228ac577357e4d897", 230323},
- {NULL, 0, NULL, 0}
- },
- Common::ES_ESP,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GID_DW2,
- 0,
- GF_CD | GF_SCNFILES,
- TINSEL_V2,
- },
-
- { // Russian Discworld 2 release by Fargus
- {
- "dw2",
- "CD",
- {
- {"dw2.scn", 0, "c6d15ce9720a9d8fef06e6582dcf3f34", 103593},
- {"english1.smp", 0, NULL, -1},
- {"english1.txt", 0, "b522e19d7b2cd7b85e50e36fe48e36a9", 274444},
- {NULL, 0, NULL, 0}
- },
- Common::RU_RUS,
- Common::kPlatformPC,
- ADGF_NO_FLAGS,
- GUIO_NONE
- },
- GID_DW2,
- 0,
- GF_CD | GF_SCNFILES,
- TINSEL_V2,
- },
-
- { AD_TABLE_END_MARKER, 0, 0, 0, 0 }
-};
-
-} // End of namespace Tinsel
+#include "tinsel/detection_tables.h"
static const ADParams detectionParams = {
// Pointer to ADGameDescription or its superset structure
@@ -637,7 +95,11 @@ static const ADParams detectionParams = {
// Flags
0,
// Additional GUI options (for every game}
- Common::GUIO_NONE
+ Common::GUIO_NONE,
+ // Maximum directory depth
+ 1,
+ // List of directory globs
+ 0
};
class TinselMetaEngine : public AdvancedMetaEngine {
diff --git a/engines/tinsel/detection_tables.h b/engines/tinsel/detection_tables.h
new file mode 100644
index 0000000000..b467cc613e
--- /dev/null
+++ b/engines/tinsel/detection_tables.h
@@ -0,0 +1,567 @@
+/* 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$
+ *
+ */
+
+namespace Tinsel {
+
+using Common::GUIO_NONE;
+using Common::GUIO_NOSPEECH;
+using Common::GUIO_NOSFX;
+using Common::GUIO_NOMUSIC;
+
+static const TinselGameDescription gameDescriptions[] = {
+
+ // Note: The following is the (hopefully) definitive list of version details:
+ // TINSEL_V0: Used only by the Discworld 1 demo - this used a more primitive version
+ // of the Tinsel engine and graphics compression
+ // TINSEL_V1: There were two versions of the Discworld 1 game - the first used .GRA
+ // files, and the second used .SCN files. The second also provided some fixes to
+ // various script bugs and coding errors, but is still considered TINSEL_V1,
+ // as both game versions work equally well with the newer code.
+ // TINSEL_V2: The Discworld 2 game used this updated version of the Tinsel 1 engine,
+ // and as far as we know there aren't any variations of this engine.
+
+ { // Floppy Demo V0 from http://www.adventure-treff.de/specials/dl_demos.php
+ {
+ "dw",
+ "Floppy Demo",
+ AD_ENTRY1s("dw.gra", "ce1b57761ba705221bcf70955b827b97", 441192),
+ //AD_ENTRY1s("dw.scn", "ccd72f02183d0e96b6e7d8df9492cda8", 23308),
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_DEMO,
+ GUIO_NOSPEECH | GUIO_NOSFX | GUIO_NOMUSIC
+ },
+ GID_DW1,
+ 0,
+ GF_DEMO,
+ TINSEL_V0,
+ },
+
+ { // CD Demo V1 version, with *.gra files
+ {
+ "dw",
+ "CD Demo",
+ {
+ {"dw.gra", 0, "ef5a2518c9e205f786f5a4526396e661", 781676},
+ {"english.smp", 0, NULL, -1},
+ },
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_DEMO,
+ GUIO_NONE
+ },
+ GID_DW1,
+ 0,
+ GF_CD,
+ TINSEL_V1,
+ },
+
+ { // Multilingual Floppy V1 with *.gra files.
+ // Note: It contains no english subtitles.
+ {
+ "dw",
+ "Floppy",
+ {
+ {"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
+ {"french.txt", 0, NULL, -1},
+ {"german.txt", 0, NULL, -1},
+ {"italian.txt", 0, NULL, -1},
+ {"spanish.txt", 0, NULL, -1},
+ {NULL, 0, NULL, 0}
+ },
+ Common::FR_FRA,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE,
+ GUIO_NOSPEECH
+ },
+ GID_DW1,
+ 0,
+ GF_FLOPPY | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
+ TINSEL_V1,
+ },
+
+ { // Floppy V1 version, with *.gra files
+ {
+ "dw",
+ "Floppy",
+ AD_ENTRY1s("dw.gra", "c8808ccd988d603dd35dff42013ae7fd", 781656),
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NOSPEECH
+ },
+ GID_DW1,
+ 0,
+ GF_FLOPPY | GF_ENHANCED_AUDIO_SUPPORT,
+ TINSEL_V1,
+ },
+
+ { // CD V1 version, with *.gra files (same as the floppy one, with english.smp)
+ {
+ "dw",
+ "CD",
+ {
+ {"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
+ {"english.smp", 0, NULL, -1},
+ },
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GID_DW1,
+ 0,
+ GF_CD | GF_ENHANCED_AUDIO_SUPPORT,
+ TINSEL_V1,
+ },
+
+ { // Italian CD with english speech and *.gra files.
+ // Note: It contains only italian subtitles, but inside english.txt
+ {
+ "dw",
+ "CD",
+ {
+ {"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
+ {"english.txt", 0, "15f0703f85477d7fab4280bf938b61c1", 237774},
+ {"english.smp", 0, NULL, -1},
+ {NULL, 0, NULL, 0}
+ },
+ Common::IT_ITA,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE,
+ GUIO_NONE
+ },
+ GID_DW1,
+ 0,
+ GF_CD | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
+ TINSEL_V1,
+ },
+
+ { // Multilingual CD with english speech and *.gra files.
+ // Note: It contains no english subtitles.
+ {
+ "dw",
+ "CD",
+ {
+ {"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
+ {"english.smp", 0, NULL, -1},
+ {"french.txt", 0, NULL, -1},
+ {"german.txt", 0, NULL, -1},
+ {"italian.txt", 0, NULL, -1},
+ {"spanish.txt", 0, NULL, -1},
+ {NULL, 0, NULL, 0}
+ },
+ Common::FR_FRA,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE,
+ GUIO_NONE
+ },
+ GID_DW1,
+ 0,
+ GF_CD | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
+ TINSEL_V1,
+ },
+
+ {
+ {
+ "dw",
+ "CD",
+ {
+ {"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
+ {"english.smp", 0, NULL, -1},
+ {"french.txt", 0, NULL, -1},
+ {"german.txt", 0, NULL, -1},
+ {"italian.txt", 0, NULL, -1},
+ {"spanish.txt", 0, NULL, -1},
+ {NULL, 0, NULL, 0}
+ },
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE,
+ GUIO_NONE
+ },
+ GID_DW1,
+ 0,
+ GF_CD | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
+ TINSEL_V1,
+ },
+ {
+ {
+ "dw",
+ "CD",
+ {
+ {"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
+ {"english.smp", 0, NULL, -1},
+ {"french.txt", 0, NULL, -1},
+ {"german.txt", 0, NULL, -1},
+ {"italian.txt", 0, NULL, -1},
+ {"spanish.txt", 0, NULL, -1},
+ {NULL, 0, NULL, 0}
+ },
+ Common::IT_ITA,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE,
+ GUIO_NONE
+ },
+ GID_DW1,
+ 0,
+ GF_CD | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
+ TINSEL_V1,
+ },
+ {
+ {
+ "dw",
+ "CD",
+ {
+ {"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
+ {"english.smp", 0, NULL, -1},
+ {"french.txt", 0, NULL, -1},
+ {"german.txt", 0, NULL, -1},
+ {"italian.txt", 0, NULL, -1},
+ {"spanish.txt", 0, NULL, -1},
+ {NULL, 0, NULL, 0}
+ },
+ Common::ES_ESP,
+ Common::kPlatformPC,
+ ADGF_DROPLANGUAGE,
+ GUIO_NONE
+ },
+ GID_DW1,
+ 0,
+ GF_CD | GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
+ TINSEL_V1,
+ },
+
+ { // English CD v2
+ {
+ "dw",
+ "CD",
+ {
+ {"dw.scn", 0, "70955425870c7720d6eebed903b2ef41", 776188},
+ {"english.smp", 0, NULL, -1},
+ {NULL, 0, NULL, 0}
+ },
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GID_DW1,
+ 0,
+ GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
+ TINSEL_V1,
+ },
+
+ { // Hebrew CD v2
+ {
+ "dw",
+ "CD",
+ {
+ {"dw.scn", 0, "759d1374b4f02af6d52fc07c96679936", 770780},
+ {"english.smp", 0, NULL, -1},
+ {NULL, 0, NULL, 0}
+ },
+ Common::HE_ISR,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GID_DW1,
+ 0,
+ GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
+ TINSEL_V1,
+ },
+
+ { // Discworld PSX CD
+ {
+ "dw",
+ "CD",
+ {
+ {"english.txt", 0, "7526cfc3a64e00f223795de476b4e2c9", 230326},
+ {NULL, 0, NULL, 0}
+ },
+ Common::EN_ANY,
+ Common::kPlatformPSX,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GID_DW1,
+ 0,
+ GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
+ TINSEL_V1,
+ },
+
+ { // multilanguage PSX demo
+ {
+ "dw",
+ "CD demo",
+ {
+ {"french.txt", 0, "e7020d35f58d0d187052ac406d86cc87", 273914},
+ {"german.txt", 0, "52f0a01e0ff0d340b02a36fd5109d705", 263942},
+ {"italian.txt", 0, "15f0703f85477d7fab4280bf938b61c1", 239834},
+ {"spanish.txt", 0, "c324170c3f1922c605c5cc09ba265aa5", 236702},
+ {"english.txt", 0, "7526cfc3a64e00f223795de476b4e2c9", 230326},
+ {NULL, 0, NULL, 0}
+ },
+ Common::EN_ANY,
+ Common::kPlatformPSX,
+ ADGF_DEMO,
+ GUIO_NONE
+ },
+ GID_DW1,
+ 0,
+ GF_CD | GF_SCNFILES,
+ TINSEL_V1,
+ },
+
+#if 0
+ { // English Saturn CD
+ {
+ "dw",
+ "CD",
+ {
+ {"dw.scn", 0, "6803f293c88758057cc685b9437f7637", 382248},
+ {"english.smp", 0, NULL, -1},
+ {NULL, 0, NULL, 0}
+ },
+ Common::EN_ANY,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GID_DW1,
+ 0,
+ GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
+ TINSEL_V1,
+ },
+#endif
+
+#if 0
+ { // Mac multilanguage CD
+ {
+ "dw",
+ "CD",
+ {
+ {"dw.scn", 0, "cfc40a8d5d476a1c9d3abf826fa46f8c", 1265532},
+ {"english.smp", 0, NULL, -1},
+ {NULL, 0, NULL, 0}
+ },
+ Common::EN_ANY,
+ Common::kPlatformMacintosh,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GID_DW1,
+ 0,
+ GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
+ TINSEL_V1,
+ },
+
+#endif
+
+ { // German CD re-release "Neon Edition"
+ // Note: This release has ENGLISH.TXT (with german content) instead of GERMAN.TXT
+ {
+ "dw",
+ "CD",
+ AD_ENTRY1s("dw.scn", "6182c7986eaec893c62fb6ea13a9f225", 774556),
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GID_DW1,
+ 0,
+ GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
+ TINSEL_V1,
+ },
+
+ { // Russian Discworld 1
+ {
+ "dw",
+ "CD",
+ {
+ {"dw.scn", 0, "133041bde59d05c1bf084fd6f1bdce4b", 776524},
+ {"english.txt", 0, "f73dcbd7b136b37c2adf7c9448ea336d", 231821},
+ {"english.smp", 0, NULL, -1},
+ {NULL, 0, NULL, 0}
+ },
+ Common::RU_RUS,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GID_DW1,
+ 0,
+ GF_CD | GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
+ TINSEL_V1,
+ },
+
+ { // European/Australian Discworld 2 release
+ {
+ "dw2",
+ "CD",
+ {
+ {"dw2.scn", 0, "c6d15ce9720a9d8fef06e6582dcf3f34", 103593},
+ {"english1.smp", 0, NULL, -1},
+ {NULL, 0, NULL, 0}
+ },
+ Common::EN_GRB,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GID_DW2,
+ 0,
+ GF_CD | GF_SCNFILES,
+ TINSEL_V2,
+ },
+
+ { // US Discworld 2 release
+ {
+ "dw2",
+ "CD",
+ {
+ {"dw2.scn", 0, "c6d15ce9720a9d8fef06e6582dcf3f34", 103593},
+ {"us1.smp", 0, NULL, -1},
+ {NULL, 0, NULL, 0}
+ },
+ Common::EN_USA,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GID_DW2,
+ 0,
+ GF_CD | GF_SCNFILES,
+ TINSEL_V2,
+ },
+
+ { // French version of Discworld 2
+ {
+ "dw2",
+ "CD",
+ {
+ {"dw2.scn", 0, "c6d15ce9720a9d8fef06e6582dcf3f34", 103593},
+ {"french1.smp", 0, NULL, -1},
+ {NULL, 0, NULL, 0}
+ },
+ Common::FR_FRA,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GID_DW2,
+ 0,
+ GF_CD | GF_SCNFILES,
+ TINSEL_V2,
+ },
+
+ { // German Discworld 2 re-release "Neon Edition"
+ {
+ "dw2",
+ "CD",
+ {
+ {"dw2.scn", 0, "c6d15ce9720a9d8fef06e6582dcf3f34", 103593},
+ {"german1.smp", 0, NULL, -1},
+ {NULL, 0, NULL, 0}
+ },
+ Common::DE_DEU,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GID_DW2,
+ 0,
+ GF_CD | GF_SCNFILES,
+ TINSEL_V2,
+ },
+
+ { // Italian/Spanish Discworld 2
+ {
+ "dw2",
+ "CD",
+ {
+ {"dw2.scn", 0, "c6d15ce9720a9d8fef06e6582dcf3f34", 103593},
+ {"english1.smp", 0, NULL, -1},
+ {"italian1.txt", 0, "d443249f8b55489b5888c227b9096f4e", 246495},
+ {NULL, 0, NULL, 0}
+ },
+ Common::IT_ITA,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GID_DW2,
+ 0,
+ GF_CD | GF_SCNFILES,
+ TINSEL_V2,
+ },
+ {
+ {
+ "dw2",
+ "CD",
+ {
+ {"dw2.scn", 0, "c6d15ce9720a9d8fef06e6582dcf3f34", 103593},
+ {"english1.smp", 0, NULL, -1},
+ {"spanish1.txt", 0, "bc6e147c5f542db228ac577357e4d897", 230323},
+ {NULL, 0, NULL, 0}
+ },
+ Common::ES_ESP,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GID_DW2,
+ 0,
+ GF_CD | GF_SCNFILES,
+ TINSEL_V2,
+ },
+
+ { // Russian Discworld 2 release by Fargus
+ {
+ "dw2",
+ "CD",
+ {
+ {"dw2.scn", 0, "c6d15ce9720a9d8fef06e6582dcf3f34", 103593},
+ {"english1.smp", 0, NULL, -1},
+ {"english1.txt", 0, "b522e19d7b2cd7b85e50e36fe48e36a9", 274444},
+ {NULL, 0, NULL, 0}
+ },
+ Common::RU_RUS,
+ Common::kPlatformPC,
+ ADGF_NO_FLAGS,
+ GUIO_NONE
+ },
+ GID_DW2,
+ 0,
+ GF_CD | GF_SCNFILES,
+ TINSEL_V2,
+ },
+
+ { AD_TABLE_END_MARKER, 0, 0, 0, 0 }
+};
+
+} // End of namespace Tinsel
diff --git a/engines/tinsel/graphics.cpp b/engines/tinsel/graphics.cpp
index 9700e8947f..48270d94e3 100644
--- a/engines/tinsel/graphics.cpp
+++ b/engines/tinsel/graphics.cpp
@@ -443,6 +443,12 @@ static void t2WrtNonZero(DRAWOBJECT *pObj, uint8 *srcP, uint8 *destP, bool apply
int numBytes;
int clipAmount;
+ // WORKAROUND: One of the mortician frames has several corrupt bytes in the Russian version
+ if ((pObj->hBits == 2517583660UL) && (_vm->getLanguage() == Common::RU_RUS)) {
+ uint8 correctBytes[5] = {0xA3, 0x00, 0x89, 0xC0, 0xA6};
+ Common::copy(&correctBytes[0], &correctBytes[5], srcP);
+ }
+
for (int y = 0; y < pObj->height; ++y) {
// Get the position to start writing out from
uint8 *tempP = !horizFlipped ? destP :
@@ -596,6 +602,23 @@ static void PackedWrtNonZero(DRAWOBJECT *pObj, uint8 *srcP, uint8 *destP,
int numBytes, colour;
int v;
+ if (_vm->getLanguage() == Common::RU_RUS) {
+ // WORKAROUND: One of the mortician frames has several corrupt bytes in the Russian version
+ if (pObj->hBits == 2517583393UL) {
+ uint8 correctBytes[5] = {0x00, 0x00, 0x17, 0x01, 0x00};
+ Common::copy(&correctBytes[0], &correctBytes[5], srcP + 267);
+ }
+ // WORKAROUND: One of Dibbler's frames in the end sequence has corrupt bytes in the Russian version
+ if (pObj->hBits == 33651742) {
+ uint8 correctBytes[40] = {
+ 0x06, 0xc0, 0xd6, 0xc1, 0x09, 0xce, 0x0d, 0x24, 0x02, 0x12, 0x01, 0x00, 0x00, 0x23, 0x21, 0x32,
+ 0x12, 0x00, 0x00, 0x20, 0x01, 0x11, 0x32, 0x12, 0x01, 0x00, 0x00, 0x1b, 0x02, 0x11, 0x34, 0x11,
+ 0x00, 0x00, 0x18, 0x01, 0x11, 0x35, 0x21, 0x01
+ };
+ Common::copy(&correctBytes[0], &correctBytes[40], srcP);
+ }
+ }
+
if (applyClipping) {
pObj->height -= pObj->botClip;
topClip = pObj->topClip;
diff --git a/engines/tinsel/pcode.cpp b/engines/tinsel/pcode.cpp
index c472a770d2..98fb078459 100644
--- a/engines/tinsel/pcode.cpp
+++ b/engines/tinsel/pcode.cpp
@@ -148,6 +148,7 @@ static const byte fragment12[] = {OP_JMPTRUE | OPSIZE16, FRAGMENT_WORD(1491),
OP_ONE, OP_LIBCALL | OPSIZE8, 14, // Re-show the cursor
OP_IMM | OPSIZE16, FRAGMENT_WORD(322), OP_LIBCALL | OPSIZE8, 46, // Give back the whistle
OP_JUMP | OPSIZE16, FRAGMENT_WORD(1568)};
+static const byte fragment13[] = {OP_ZERO, OP_GSTORE | OPSIZE16, FRAGMENT_WORD(306)};
#undef FRAGMENT_WORD
@@ -207,6 +208,12 @@ const WorkaroundEntry workaroundList[] = {
// See bug report #2934211.
{TINSEL_V1, true, 352601285, 1569, sizeof(fragment11), fragment11},
{TINSEL_V1, false, 352602304, 1488, sizeof(fragment12), fragment12},
+
+ // DW2: Corrects a bug with global 306 not being cleared if you leave
+ // the marketplace scene whilst D'Blah is talking (even if it's not
+ // actually audible); returning to the scene and clicking on him multiple
+ // times would cause the game to crash
+ {TINSEL_V2, true, 1109294728, 0, sizeof(fragment13), fragment13},
{TINSEL_V0, false, 0, 0, 0, NULL}
};
diff --git a/engines/tinsel/tinlib.cpp b/engines/tinsel/tinlib.cpp
index 677392a35d..766d4ed54a 100644
--- a/engines/tinsel/tinlib.cpp
+++ b/engines/tinsel/tinlib.cpp
@@ -5557,7 +5557,7 @@ int CallLibraryRoutine(CORO_PARAM, int operand, int32 *pp, const INT_CONTEXT *pi
case WALKED: {
// Common to both DW1 & DW2
pp -= 3; // 4 parameters
- bool tmp;
+ bool tmp = false;
Walked(coroParam, pp[0], pp[1], pp[2], pp[3], pic->escOn, pic->myEscape, tmp);
if (!coroParam) {
// Only write the result to the stack if walked actually completed running.
diff --git a/engines/tinsel/tinsel.cpp b/engines/tinsel/tinsel.cpp
index 4335b51818..3a4191227a 100644
--- a/engines/tinsel/tinsel.cpp
+++ b/engines/tinsel/tinsel.cpp
@@ -855,11 +855,11 @@ TinselEngine::TinselEngine(OSystem *syst, const TinselGameDescription *gameDesc)
if (cd_num >= 0)
_system->getAudioCDManager()->openCD(cd_num);
- MidiDriverType midiDriver = MidiDriver::detectMusicDriver(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MIDI);
- bool native_mt32 = ((midiDriver == MD_MT32) || ConfMan.getBool("native_mt32"));
- //bool adlib = (midiDriver == MD_ADLIB);
+ MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
+ bool native_mt32 = ((MidiDriver::getMusicType(dev) == MT_MT32) || ConfMan.getBool("native_mt32"));
+ //bool adlib = (MidiDriver::getMusicType(dev) == MT_ADLIB);
- _driver = MidiDriver::createMidi(midiDriver);
+ _driver = MidiDriver::createMidi(dev);
if (native_mt32)
_driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
diff --git a/engines/tinsel/tinsel.h b/engines/tinsel/tinsel.h
index 0e1d705815..df27a1e0e1 100644
--- a/engines/tinsel/tinsel.h
+++ b/engines/tinsel/tinsel.h
@@ -61,6 +61,8 @@ class PCMMusicPlayer;
class Scheduler;
class SoundManager;
+typedef Common::List<Common::Rect> RectList;
+
enum TinselGameID {
GID_DW1 = 0,
GID_DW2 = 1
@@ -203,6 +205,10 @@ public:
/** Stack of pending keypresses. */
Common::List<Common::Event> _keypresses;
+
+ /** List of all clip rectangles. */
+ RectList _clipRects;
+
private:
//MidiMusicPlayer *_midiMusic;
int _musicVolume;
diff --git a/engines/touche/detection.cpp b/engines/touche/detection.cpp
index 65a6a29bcc..35f03fa657 100644
--- a/engines/touche/detection.cpp
+++ b/engines/touche/detection.cpp
@@ -124,6 +124,11 @@ static const ADFileBasedFallback fileBasedFallback[] = {
} // End of namespace Touche
+static const char *directoryGlobs[] = {
+ "database",
+ 0
+};
+
static const ADParams detectionParams = {
(const byte *)Touche::gameDescriptions,
sizeof(ADGameDescription),
@@ -134,7 +139,11 @@ static const ADParams detectionParams = {
Touche::fileBasedFallback, // file-based detection data to enable not yet known versions to start
kADFlagPrintWarningOnFileBasedFallback,
// Additional GUI options (for every game}
- Common::GUIO_NONE
+ Common::GUIO_NONE,
+ // Maximum directory depth
+ 2,
+ // List of directory globs
+ directoryGlobs
};
class ToucheMetaEngine : public AdvancedMetaEngine {
diff --git a/engines/touche/midi.cpp b/engines/touche/midi.cpp
index 9dbef4d76f..439d3b9ac2 100644
--- a/engines/touche/midi.cpp
+++ b/engines/touche/midi.cpp
@@ -92,9 +92,9 @@ void MidiPlayer::setVolume(int volume) {
}
int MidiPlayer::open() {
- MidiDriverType midiDriver = MidiDriver::detectMusicDriver(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MIDI);
- _nativeMT32 = ((midiDriver == MD_MT32) || ConfMan.getBool("native_mt32"));
- _driver = MidiDriver::createMidi(midiDriver);
+ MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
+ _nativeMT32 = ((MidiDriver::getMusicType(dev) == MT_MT32) || ConfMan.getBool("native_mt32"));
+ _driver = MidiDriver::createMidi(dev);
int ret = _driver->open();
if (ret == 0) {
_parser = MidiParser::createParser_SMF();
diff --git a/engines/touche/touche.cpp b/engines/touche/touche.cpp
index 187e685d06..2dc8b76b4f 100644
--- a/engines/touche/touche.cpp
+++ b/engines/touche/touche.cpp
@@ -28,6 +28,8 @@
#include "common/debug-channels.h"
#include "common/events.h"
#include "common/EventRecorder.h"
+#include "common/file.h"
+#include "common/fs.h"
#include "common/system.h"
#include "engines/util.h"
@@ -70,6 +72,10 @@ ToucheEngine::ToucheEngine(OSystem *system, Common::Language language)
_menuRedrawCounter = 0;
memset(_paletteBuffer, 0, sizeof(_paletteBuffer));
+ const Common::FSNode gameDataDir(ConfMan.get("path"));
+
+ SearchMan.addSubDirectoryMatching(gameDataDir, "database");
+
DebugMan.addDebugChannel(kDebugEngine, "Engine", "Engine debug level");
DebugMan.addDebugChannel(kDebugGraphics, "Graphics", "Graphics debug level");
DebugMan.addDebugChannel(kDebugResource, "Resource", "Resource debug level");
diff --git a/engines/tucker/detection.cpp b/engines/tucker/detection.cpp
index b4f30cb7fd..0a9dec9b46 100644
--- a/engines/tucker/detection.cpp
+++ b/engines/tucker/detection.cpp
@@ -114,7 +114,9 @@ static const ADParams detectionParams = {
"tucker",
0,
0,
- Common::GUIO_NONE
+ Common::GUIO_NONE,
+ 1,
+ 0
};
static const ADGameDescription tuckerDemoGameDescription = {