aboutsummaryrefslogtreecommitdiff
path: root/engines/sci
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/sci
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/sci')
-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/gui32.h68
-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
118 files changed, 9758 insertions, 12452 deletions
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/gui32.h b/engines/sci/graphics/gui32.h
deleted file mode 100644
index 99eb03b321..0000000000
--- a/engines/sci/graphics/gui32.h
+++ /dev/null
@@ -1,68 +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_GUI32_H
-#define SCI_GRAPHICS_GUI32_H
-
-#include "sci/graphics/helpers.h"
-
-namespace Sci {
-
-class GfxCursor;
-class GfxScreen;
-class GfxPalette;
-class GfxCache;
-class GfxCoordAdjuster32;
-class GfxCompare;
-class GfxFrameout;
-class GfxPaint32;
-
-class SciGui32 {
-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);
-
-protected:
- GfxCursor *_cursor;
- GfxScreen *_screen;
- GfxPalette *_palette;
- GfxCache *_cache;
- GfxCoordAdjuster32 *_coordAdjuster;
- GfxCompare *_compare;
- GfxFrameout *_frameout;
- GfxPaint32 *_paint32;
-
-private:
-};
-
-} // End of namespace Sci
-
-#endif
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