aboutsummaryrefslogtreecommitdiff
path: root/engines
diff options
context:
space:
mode:
Diffstat (limited to 'engines')
-rw-r--r--engines/director/lingo/lingo-builtins.cpp2
-rw-r--r--engines/director/lingo/lingo-code.cpp41
-rw-r--r--engines/director/lingo/lingo-codegen.cpp17
-rw-r--r--engines/director/lingo/lingo-gr.cpp985
-rw-r--r--engines/director/lingo/lingo-gr.y17
-rw-r--r--engines/director/lingo/lingo.cpp47
-rw-r--r--engines/director/lingo/lingo.h3
-rw-r--r--engines/fullpipe/gfx.cpp20
-rw-r--r--engines/sci/console.cpp3
-rw-r--r--engines/sci/detection.cpp4
-rw-r--r--engines/sci/engine/kernel.h26
-rw-r--r--engines/sci/engine/kernel_tables.h68
-rw-r--r--engines/sci/engine/kevent.cpp71
-rw-r--r--engines/sci/engine/kfile.cpp38
-rw-r--r--engines/sci/engine/kgraphics.cpp13
-rw-r--r--engines/sci/engine/kgraphics32.cpp112
-rw-r--r--engines/sci/engine/kmisc.cpp80
-rw-r--r--engines/sci/engine/kvideo.cpp89
-rw-r--r--engines/sci/engine/savegame.cpp24
-rw-r--r--engines/sci/engine/savegame.h2
-rw-r--r--engines/sci/engine/seg_manager.h3
-rw-r--r--engines/sci/engine/state.cpp3
-rw-r--r--engines/sci/engine/state.h1
-rw-r--r--engines/sci/engine/workarounds.cpp3
-rw-r--r--engines/sci/event.cpp9
-rw-r--r--engines/sci/graphics/cache.cpp10
-rw-r--r--engines/sci/graphics/celobj32.cpp149
-rw-r--r--engines/sci/graphics/celobj32.h9
-rw-r--r--engines/sci/graphics/compare.cpp2
-rw-r--r--engines/sci/graphics/compare.h4
-rw-r--r--engines/sci/graphics/coordadjuster.cpp52
-rw-r--r--engines/sci/graphics/coordadjuster.h48
-rw-r--r--engines/sci/graphics/cursor.cpp2
-rw-r--r--engines/sci/graphics/cursor.h4
-rw-r--r--engines/sci/graphics/cursor32.cpp396
-rw-r--r--engines/sci/graphics/cursor32.h250
-rw-r--r--engines/sci/graphics/frameout.cpp114
-rw-r--r--engines/sci/graphics/frameout.h32
-rw-r--r--engines/sci/graphics/helpers.h6
-rw-r--r--engines/sci/graphics/paint16.cpp2
-rw-r--r--engines/sci/graphics/paint16.h4
-rw-r--r--engines/sci/graphics/picture.cpp2
-rw-r--r--engines/sci/graphics/picture.h6
-rw-r--r--engines/sci/graphics/screen.cpp27
-rw-r--r--engines/sci/graphics/screen_item32.h1
-rw-r--r--engines/sci/graphics/text16.cpp2
-rw-r--r--engines/sci/graphics/transitions32.cpp4
-rw-r--r--engines/sci/graphics/video32.cpp29
-rw-r--r--engines/sci/graphics/video32.h20
-rw-r--r--engines/sci/graphics/view.cpp12
-rw-r--r--engines/sci/graphics/view.h2
-rw-r--r--engines/sci/module.mk1
-rw-r--r--engines/sci/resource.cpp55
-rw-r--r--engines/sci/resource.h27
-rw-r--r--engines/sci/resource_audio.cpp38
-rw-r--r--engines/sci/resource_intern.h5
-rw-r--r--engines/sci/sci.cpp33
-rw-r--r--engines/sci/sci.h12
-rw-r--r--engines/sci/sound/audio32.cpp164
-rw-r--r--engines/sci/sound/audio32.h24
-rw-r--r--engines/sci/sound/decoders/sol.cpp19
-rw-r--r--engines/sci/video/robot_decoder.cpp1807
-rw-r--r--engines/sci/video/robot_decoder.h1454
-rw-r--r--engines/titanic/carry/chicken.h2
-rw-r--r--engines/titanic/carry/crushed_tv.cpp1
-rw-r--r--engines/titanic/core/click_responder.cpp21
-rw-r--r--engines/titanic/core/click_responder.h4
-rw-r--r--engines/titanic/game/bridge_view.cpp80
-rw-r--r--engines/titanic/game/bridge_view.h7
-rw-r--r--engines/titanic/game/chicken_cooler.cpp33
-rw-r--r--engines/titanic/game/chicken_cooler.h2
-rw-r--r--engines/titanic/game/chicken_dispensor.cpp141
-rw-r--r--engines/titanic/game/chicken_dispensor.h8
-rw-r--r--engines/titanic/game/close_broken_pel.cpp14
-rw-r--r--engines/titanic/game/close_broken_pel.h4
-rw-r--r--engines/titanic/game/cookie.cpp17
-rw-r--r--engines/titanic/game/cookie.h3
-rw-r--r--engines/titanic/game/credits.cpp35
-rw-r--r--engines/titanic/game/credits.h3
-rw-r--r--engines/titanic/game/credits_button.cpp20
-rw-r--r--engines/titanic/game/credits_button.h3
-rw-r--r--engines/titanic/game/desk_click_responder.cpp29
-rw-r--r--engines/titanic/game/desk_click_responder.h5
-rw-r--r--engines/titanic/game/null_port_hole.cpp12
-rw-r--r--engines/titanic/game/sgt/desk.cpp46
-rw-r--r--engines/titanic/game/sgt/desk.h4
-rw-r--r--engines/titanic/game/sgt/deskchair.cpp52
-rw-r--r--engines/titanic/game/sgt/deskchair.h5
-rw-r--r--engines/titanic/game/sgt/sgt_state_room.cpp79
-rw-r--r--engines/titanic/game/sgt/sgt_state_room.h9
-rw-r--r--engines/titanic/game/sgt/sgt_upper_doors_sound.cpp6
-rw-r--r--engines/titanic/gfx/chev_switch.cpp40
-rw-r--r--engines/titanic/gfx/chev_switch.h6
-rw-r--r--engines/titanic/gfx/toggle_switch.h2
-rw-r--r--engines/titanic/messages/door_auto_sound_event.cpp18
-rw-r--r--engines/titanic/messages/door_auto_sound_event.h4
96 files changed, 5835 insertions, 1389 deletions
diff --git a/engines/director/lingo/lingo-builtins.cpp b/engines/director/lingo/lingo-builtins.cpp
index 798ab0824b..6738d4b707 100644
--- a/engines/director/lingo/lingo-builtins.cpp
+++ b/engines/director/lingo/lingo-builtins.cpp
@@ -545,7 +545,7 @@ void Lingo::b_alert(int nargs) {
d.toString();
- warning("STUB: b_alert");
+ warning("STUB: b_alert(%s)", d.u.s->c_str());
delete d.u.s;
}
diff --git a/engines/director/lingo/lingo-code.cpp b/engines/director/lingo/lingo-code.cpp
index 150e242e88..8712f0990c 100644
--- a/engines/director/lingo/lingo-code.cpp
+++ b/engines/director/lingo/lingo-code.cpp
@@ -65,7 +65,7 @@ static struct FuncDescr {
{ Lingo::c_stringpush, "c_stringpush", "s" },
{ Lingo::c_varpush, "c_varpush", "s" },
{ Lingo::c_assign, "c_assign", "" },
- { Lingo::c_eval, "c_eval", "" },
+ { Lingo::c_eval, "c_eval", "s" },
{ Lingo::c_theentitypush,"c_theentitypush","ii" }, // entity, field
{ Lingo::c_theentityassign,"c_theentityassign","ii" },
{ Lingo::c_swap, "c_swap", "" },
@@ -92,6 +92,7 @@ static struct FuncDescr {
{ Lingo::c_repeatwhilecode,"c_repeatwhilecode","oo" },
{ Lingo::c_repeatwithcode,"c_repeatwithcode","ooooos" },
{ Lingo::c_ifcode, "c_ifcode", "oooi" },
+ { Lingo::c_whencode, "c_whencode", "os" },
{ Lingo::c_goto, "c_goto", "" },
{ Lingo::c_gotoloop, "c_gotoloop", "" },
{ Lingo::c_gotonext, "c_gotonext", "" },
@@ -170,6 +171,9 @@ void Lingo::c_printtop(void) {
case SYMBOL:
warning("%s", d.type2str(true));
break;
+ case OBJECT:
+ warning("#%s", d.u.s->c_str());
+ break;
default:
warning("--unknown--");
}
@@ -263,8 +267,11 @@ void Lingo::c_assign() {
delete d2.u.arr;
} else if (d2.type == SYMBOL) {
d1.u.sym->u.i = d2.u.i;
+ } else if (d2.type == OBJECT) {
+ d1.u.sym->u.s = d2.u.s;
} else {
warning("c_assign: unhandled type: %s", d2.type2str());
+ d1.u.sym->u.s = d2.u.s;
}
d1.u.sym->type = d2.type;
@@ -695,24 +702,38 @@ void Lingo::c_ifcode() {
int end = READ_UINT32(&(*g_lingo->_currentScript)[savepc + 2]);
int skipEnd = READ_UINT32(&(*g_lingo->_currentScript)[savepc + 3]);
- debug(8, "executing cond (have to %s end)", skipEnd ? "skip" : "execute");
+ debugC(8, kDebugLingoExec, "executing cond (have to %s end)", skipEnd ? "skip" : "execute");
g_lingo->execute(savepc + 4); /* condition */
d = g_lingo->pop();
if (d.toInt()) {
- debug(8, "executing then");
+ debugC(8, kDebugLingoExec, "executing then");
g_lingo->execute(then);
} else if (elsep) { /* else part? */
- debug(8, "executing else");
+ debugC(8, kDebugLingoExec, "executing else");
g_lingo->execute(elsep);
}
if (!g_lingo->_returning && !skipEnd) {
g_lingo->_pc = end; /* next stmt */
- debug(8, "executing end");
- } else
- debug(8, "Skipped end");
+ debugC(8, kDebugLingoExec, "executing end");
+ } else {
+ debugC(8, kDebugLingoExec, "Skipped end");
+ }
+}
+
+void Lingo::c_whencode() {
+ Datum d;
+ int start = g_lingo->_pc;
+ int end = READ_UINT32(&(*g_lingo->_currentScript)[start]);
+ Common::String eventname((char *)&(*g_lingo->_currentScript)[start]);
+
+ start += g_lingo->calcStringAlignment(eventname.c_str());
+
+ warning("STUB: c_whencode([%5d][%5d], %s)", start, end, eventname.c_str());
+
+ g_lingo->_pc = end;
}
//************************
@@ -789,7 +810,7 @@ void Lingo::call(Common::String &name, int nargs) {
if (!g_lingo->_handlers.contains(name)) {
Symbol *s = g_lingo->lookupVar(name.c_str(), false);
if (s && s->type == OBJECT) {
- debug(3, "Dereferencing object reference: %s to %s", name.c_str(), s->u.s->c_str());
+ debugC(3, kDebugLingoExec, "Dereferencing object reference: %s to %s", name.c_str(), s->u.s->c_str());
name = *s->u.s;
}
}
@@ -843,6 +864,7 @@ void Lingo::call(Common::String &name, int nargs) {
g_lingo->push(d);
}
+ debugC(5, kDebugLingoExec, "Pushing frame %d", g_lingo->_callstack.size() + 1);
CFrame *fp = new CFrame;
fp->sp = sym;
@@ -868,7 +890,10 @@ void Lingo::c_procret() {
return;
}
+ debugC(5, kDebugLingoExec, "Popping frame %d", g_lingo->_callstack.size() + 1);
+
CFrame *fp = g_lingo->_callstack.back();
+ g_lingo->_callstack.pop_back();
g_lingo->_currentScript = fp->retscript;
g_lingo->_pc = fp->retpc;
diff --git a/engines/director/lingo/lingo-codegen.cpp b/engines/director/lingo/lingo-codegen.cpp
index 9390a8b7f9..32ddea47ac 100644
--- a/engines/director/lingo/lingo-codegen.cpp
+++ b/engines/director/lingo/lingo-codegen.cpp
@@ -57,17 +57,21 @@ void Lingo::execute(int pc) {
debugC(1, kDebugLingoExec, "[%3d]: %s", _pc, instr.c_str());
+ Common::String stack("Stack: ");
+
for (uint i = 0; i < _stack.size(); i++) {
- debugCN(5, kDebugLingoExec, "%d ", _stack[i].u.i);
+ Datum d = _stack[i];
+ d.toString();
+ stack += Common::String::format("<%s> ", d.u.s->c_str());
}
- debugCN(5, kDebugLingoExec, "%s", "");
+ debugC(5, kDebugLingoExec, "%s", stack.c_str());
_pc++;
(*((*_currentScript)[_pc - 1]))();
}
}
-Common::String Lingo::decodeInstruction(int pc) {
+Common::String Lingo::decodeInstruction(int pc, int *newPc) {
Symbol sym;
Common::String res;
@@ -114,6 +118,9 @@ Common::String Lingo::decodeInstruction(int pc) {
res = "<unknown>";
}
+ if (newPc)
+ *newPc = pc;
+
return res;
}
@@ -182,7 +189,7 @@ void Lingo::define(Common::String &name, int start, int nargs, Common::String *p
if (prefix)
name = *prefix + "-" + name;
- debug(3, "define(\"%s\", %d, %d, %d)", name.c_str(), start, _currentScript->size() - 1, nargs);
+ debugC(3, kDebugLingoCompile, "define(\"%s\", %d, %d, %d)", name.c_str(), start, _currentScript->size() - 1, nargs);
if (!_handlers.contains(name)) { // Create variable if it was not defined
sym = new Symbol;
@@ -274,7 +281,7 @@ int Lingo::codeFunc(Common::String *s, int numpar) {
if (s->equalsIgnoreCase("me")) {
if (!g_lingo->_currentFactory.empty()) {
g_lingo->codeString(g_lingo->_currentFactory.c_str());
- debug(2, "Repaced 'me' with %s", g_lingo->_currentFactory.c_str());
+ debugC(2, kDebugLingoCompile, "Replaced 'me' with %s", g_lingo->_currentFactory.c_str());
} else {
warning("'me' out of factory method");
g_lingo->codeString(s->c_str());
diff --git a/engines/director/lingo/lingo-gr.cpp b/engines/director/lingo/lingo-gr.cpp
index 63128058ed..be5b3eb571 100644
--- a/engines/director/lingo/lingo-gr.cpp
+++ b/engines/director/lingo/lingo-gr.cpp
@@ -486,18 +486,18 @@ union yyalloc
#endif
/* YYFINAL -- State number of the termination state. */
-#define YYFINAL 87
+#define YYFINAL 88
/* YYLAST -- Last index in YYTABLE. */
-#define YYLAST 921
+#define YYLAST 931
/* YYNTOKENS -- Number of terminals. */
#define YYNTOKENS 83
/* YYNNTS -- Number of nonterminals. */
-#define YYNNTS 35
+#define YYNNTS 36
/* YYNRULES -- Number of rules. */
-#define YYNRULES 124
+#define YYNRULES 125
/* YYNRULES -- Number of states. */
-#define YYNSTATES 259
+#define YYNSTATES 261
/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
#define YYUNDEFTOK 2
@@ -551,69 +551,69 @@ static const yytype_uint16 yyprhs[] =
{
0, 0, 3, 7, 9, 12, 14, 15, 17, 19,
21, 23, 25, 30, 35, 40, 46, 51, 56, 62,
- 64, 66, 68, 70, 79, 91, 104, 109, 118, 130,
- 142, 149, 160, 171, 172, 176, 179, 181, 184, 186,
- 193, 195, 201, 203, 207, 211, 214, 218, 220, 222,
- 223, 224, 225, 228, 231, 233, 235, 237, 239, 244,
- 246, 248, 251, 253, 257, 261, 265, 269, 273, 277,
- 281, 285, 289, 293, 297, 300, 304, 308, 312, 316,
- 319, 322, 326, 331, 336, 339, 341, 343, 345, 348,
- 351, 354, 356, 359, 364, 367, 369, 373, 376, 379,
- 382, 385, 389, 392, 395, 397, 401, 404, 407, 410,
- 414, 417, 418, 427, 430, 431, 440, 441, 443, 447,
- 452, 453, 457, 458, 460
+ 64, 66, 68, 70, 79, 91, 104, 108, 117, 129,
+ 141, 148, 159, 170, 171, 175, 178, 180, 183, 185,
+ 192, 194, 200, 202, 206, 210, 213, 217, 219, 221,
+ 222, 223, 224, 227, 230, 234, 236, 238, 240, 242,
+ 247, 249, 251, 254, 256, 260, 264, 268, 272, 276,
+ 280, 284, 288, 292, 296, 300, 303, 307, 311, 315,
+ 319, 322, 325, 329, 334, 339, 342, 344, 346, 348,
+ 351, 354, 357, 359, 362, 367, 370, 372, 376, 379,
+ 382, 385, 388, 392, 395, 398, 400, 404, 407, 410,
+ 413, 417, 420, 421, 430, 433, 434, 443, 444, 446,
+ 450, 455, 456, 460, 461, 463
};
/* YYRHS -- A `-1'-separated list of the rules' RHS. */
static const yytype_int8 yyrhs[] =
{
84, 0, -1, 84, 85, 86, -1, 86, -1, 1,
- 85, -1, 76, -1, -1, 111, -1, 105, -1, 116,
- -1, 87, -1, 89, -1, 40, 104, 33, 21, -1,
- 42, 21, 70, 104, -1, 42, 13, 70, 104, -1,
- 42, 14, 104, 70, 104, -1, 42, 21, 44, 104,
- -1, 42, 13, 44, 104, -1, 42, 14, 104, 44,
- 104, -1, 104, -1, 105, -1, 88, -1, 90, -1,
+ 85, -1, 76, -1, -1, 112, -1, 106, -1, 117,
+ -1, 87, -1, 89, -1, 40, 105, 33, 21, -1,
+ 42, 21, 70, 105, -1, 42, 13, 70, 105, -1,
+ 42, 14, 105, 70, 105, -1, 42, 21, 44, 105,
+ -1, 42, 13, 44, 105, -1, 42, 14, 105, 44,
+ 105, -1, 105, -1, 106, -1, 88, -1, 90, -1,
97, 77, 96, 78, 103, 102, 27, 41, -1, 98,
- 70, 104, 102, 44, 104, 102, 103, 102, 27, 41,
- -1, 98, 70, 104, 102, 24, 44, 104, 102, 103,
- 102, 27, 41, -1, 45, 21, 43, 104, -1, 99,
- 96, 43, 85, 103, 102, 27, 32, -1, 99, 96,
- 43, 85, 103, 102, 48, 103, 102, 27, 32, -1,
- 99, 96, 43, 85, 103, 102, 101, 92, 102, 27,
- 32, -1, 99, 96, 43, 101, 88, 102, -1, 99,
- 96, 43, 101, 88, 102, 48, 101, 88, 102, -1,
- 99, 96, 43, 101, 88, 102, 93, 102, 91, 102,
- -1, -1, 48, 101, 88, -1, 92, 95, -1, 95,
- -1, 93, 94, -1, 94, -1, 100, 96, 43, 101,
- 89, 102, -1, 93, -1, 100, 96, 43, 103, 102,
- -1, 104, -1, 104, 70, 104, -1, 77, 96, 78,
- -1, 41, 47, -1, 41, 46, 21, -1, 32, -1,
- 26, -1, -1, -1, -1, 103, 85, -1, 103, 89,
- -1, 12, -1, 15, -1, 22, -1, 17, -1, 21,
- 77, 117, 78, -1, 21, -1, 13, -1, 14, 104,
- -1, 87, -1, 104, 71, 104, -1, 104, 72, 104,
- -1, 104, 73, 104, -1, 104, 74, 104, -1, 104,
- 79, 104, -1, 104, 80, 104, -1, 104, 60, 104,
- -1, 104, 55, 104, -1, 104, 56, 104, -1, 104,
- 61, 104, -1, 104, 62, 104, -1, 63, 104, -1,
- 104, 81, 104, -1, 104, 64, 104, -1, 104, 65,
- 104, -1, 104, 66, 104, -1, 71, 104, -1, 72,
- 104, -1, 77, 104, 78, -1, 67, 104, 68, 104,
- -1, 67, 104, 69, 104, -1, 40, 104, -1, 107,
- -1, 110, -1, 28, -1, 30, 106, -1, 19, 104,
- -1, 18, 104, -1, 18, -1, 20, 117, -1, 51,
- 104, 46, 104, -1, 51, 104, -1, 21, -1, 106,
- 82, 21, -1, 31, 34, -1, 31, 37, -1, 31,
- 39, -1, 31, 108, -1, 31, 108, 109, -1, 31,
- 109, -1, 29, 104, -1, 104, -1, 38, 36, 104,
- -1, 36, 104, -1, 52, 53, -1, 52, 108, -1,
- 52, 108, 109, -1, 52, 109, -1, -1, 35, 21,
- 112, 101, 114, 85, 115, 103, -1, 49, 21, -1,
- -1, 50, 21, 113, 101, 114, 85, 115, 103, -1,
- -1, 21, -1, 114, 82, 21, -1, 114, 85, 82,
- 21, -1, -1, 21, 101, 117, -1, -1, 104, -1,
- 117, 82, 104, -1
+ 70, 105, 102, 44, 105, 102, 103, 102, 27, 41,
+ -1, 98, 70, 105, 102, 24, 44, 105, 102, 103,
+ 102, 27, 41, -1, 104, 105, 102, -1, 99, 96,
+ 43, 85, 103, 102, 27, 32, -1, 99, 96, 43,
+ 85, 103, 102, 48, 103, 102, 27, 32, -1, 99,
+ 96, 43, 85, 103, 102, 101, 92, 102, 27, 32,
+ -1, 99, 96, 43, 101, 88, 102, -1, 99, 96,
+ 43, 101, 88, 102, 48, 101, 88, 102, -1, 99,
+ 96, 43, 101, 88, 102, 93, 102, 91, 102, -1,
+ -1, 48, 101, 88, -1, 92, 95, -1, 95, -1,
+ 93, 94, -1, 94, -1, 100, 96, 43, 101, 89,
+ 102, -1, 93, -1, 100, 96, 43, 103, 102, -1,
+ 105, -1, 105, 70, 105, -1, 77, 96, 78, -1,
+ 41, 47, -1, 41, 46, 21, -1, 32, -1, 26,
+ -1, -1, -1, -1, 103, 85, -1, 103, 89, -1,
+ 45, 21, 43, -1, 12, -1, 15, -1, 22, -1,
+ 17, -1, 21, 77, 118, 78, -1, 21, -1, 13,
+ -1, 14, 105, -1, 87, -1, 105, 71, 105, -1,
+ 105, 72, 105, -1, 105, 73, 105, -1, 105, 74,
+ 105, -1, 105, 79, 105, -1, 105, 80, 105, -1,
+ 105, 60, 105, -1, 105, 55, 105, -1, 105, 56,
+ 105, -1, 105, 61, 105, -1, 105, 62, 105, -1,
+ 63, 105, -1, 105, 81, 105, -1, 105, 64, 105,
+ -1, 105, 65, 105, -1, 105, 66, 105, -1, 71,
+ 105, -1, 72, 105, -1, 77, 105, 78, -1, 67,
+ 105, 68, 105, -1, 67, 105, 69, 105, -1, 40,
+ 105, -1, 108, -1, 111, -1, 28, -1, 30, 107,
+ -1, 19, 105, -1, 18, 105, -1, 18, -1, 20,
+ 118, -1, 51, 105, 46, 105, -1, 51, 105, -1,
+ 21, -1, 107, 82, 21, -1, 31, 34, -1, 31,
+ 37, -1, 31, 39, -1, 31, 109, -1, 31, 109,
+ 110, -1, 31, 110, -1, 29, 105, -1, 105, -1,
+ 38, 36, 105, -1, 36, 105, -1, 52, 53, -1,
+ 52, 109, -1, 52, 109, 110, -1, 52, 110, -1,
+ -1, 35, 21, 113, 101, 115, 85, 116, 103, -1,
+ 49, 21, -1, -1, 50, 21, 114, 101, 115, 85,
+ 116, 103, -1, -1, 21, -1, 115, 82, 21, -1,
+ 115, 85, 82, 21, -1, -1, 21, 101, 118, -1,
+ -1, 105, -1, 118, 82, 105, -1
};
/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
@@ -621,17 +621,17 @@ static const yytype_uint16 yyrline[] =
{
0, 103, 103, 104, 105, 108, 113, 114, 115, 116,
117, 118, 121, 127, 133, 141, 149, 155, 163, 172,
- 173, 175, 176, 181, 192, 208, 220, 225, 232, 241,
- 250, 260, 270, 281, 282, 285, 286, 289, 290, 293,
- 301, 302, 310, 311, 312, 314, 316, 322, 328, 335,
- 337, 339, 340, 341, 344, 345, 348, 351, 355, 358,
- 362, 369, 375, 376, 377, 378, 379, 380, 381, 382,
- 383, 384, 385, 386, 387, 388, 389, 390, 391, 392,
- 393, 394, 395, 396, 399, 400, 401, 402, 404, 405,
- 408, 411, 414, 415, 416, 419, 420, 431, 432, 433,
- 434, 437, 440, 445, 446, 449, 450, 453, 454, 457,
- 460, 490, 490, 496, 499, 499, 504, 505, 506, 507,
- 509, 513, 521, 522, 523
+ 173, 175, 176, 181, 192, 208, 220, 228, 235, 244,
+ 253, 263, 273, 284, 285, 288, 289, 292, 293, 296,
+ 304, 305, 313, 314, 315, 317, 319, 325, 331, 338,
+ 340, 342, 343, 344, 347, 353, 354, 357, 360, 364,
+ 367, 371, 378, 384, 385, 386, 387, 388, 389, 390,
+ 391, 392, 393, 394, 395, 396, 397, 398, 399, 400,
+ 401, 402, 403, 404, 405, 408, 409, 410, 411, 413,
+ 414, 417, 420, 423, 424, 425, 428, 429, 440, 441,
+ 442, 443, 446, 449, 454, 455, 458, 459, 462, 463,
+ 466, 469, 499, 499, 505, 508, 508, 513, 514, 515,
+ 516, 518, 522, 530, 531, 532
};
#endif
@@ -655,9 +655,9 @@ static const char *const yytname[] =
"programline", "asgn", "stmtoneliner", "stmt", "ifstmt",
"elsestmtoneliner", "elseifstmt", "elseifstmtoneliner",
"elseifstmtoneliner1", "elseifstmt1", "cond", "repeatwhile",
- "repeatwith", "if", "elseif", "begin", "end", "stmtlist", "expr", "func",
- "globallist", "gotofunc", "gotoframe", "gotomovie", "playfunc", "defn",
- "@1", "@2", "argdef", "argstore", "macro", "arglist", 0
+ "repeatwith", "if", "elseif", "begin", "end", "stmtlist", "when", "expr",
+ "func", "globallist", "gotofunc", "gotoframe", "gotomovie", "playfunc",
+ "defn", "@1", "@2", "argdef", "argstore", "macro", "arglist", 0
};
#endif
@@ -686,14 +686,14 @@ static const yytype_uint8 yyr1[] =
88, 89, 89, 89, 89, 89, 89, 90, 90, 90,
90, 90, 90, 91, 91, 92, 92, 93, 93, 94,
95, 95, 96, 96, 96, 97, 98, 99, 100, 101,
- 102, 103, 103, 103, 104, 104, 104, 104, 104, 104,
- 104, 104, 104, 104, 104, 104, 104, 104, 104, 104,
- 104, 104, 104, 104, 104, 104, 104, 104, 104, 104,
- 104, 104, 104, 104, 105, 105, 105, 105, 105, 105,
- 105, 105, 105, 105, 105, 106, 106, 107, 107, 107,
- 107, 107, 107, 108, 108, 109, 109, 110, 110, 110,
- 110, 112, 111, 111, 113, 111, 114, 114, 114, 114,
- 115, 116, 117, 117, 117
+ 102, 103, 103, 103, 104, 105, 105, 105, 105, 105,
+ 105, 105, 105, 105, 105, 105, 105, 105, 105, 105,
+ 105, 105, 105, 105, 105, 105, 105, 105, 105, 105,
+ 105, 105, 105, 105, 105, 106, 106, 106, 106, 106,
+ 106, 106, 106, 106, 106, 106, 107, 107, 108, 108,
+ 108, 108, 108, 108, 109, 109, 110, 110, 111, 111,
+ 111, 111, 113, 112, 112, 114, 112, 115, 115, 115,
+ 115, 116, 117, 118, 118, 118
};
/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
@@ -701,17 +701,17 @@ static const yytype_uint8 yyr2[] =
{
0, 2, 3, 1, 2, 1, 0, 1, 1, 1,
1, 1, 4, 4, 4, 5, 4, 4, 5, 1,
- 1, 1, 1, 8, 11, 12, 4, 8, 11, 11,
+ 1, 1, 1, 8, 11, 12, 3, 8, 11, 11,
6, 10, 10, 0, 3, 2, 1, 2, 1, 6,
1, 5, 1, 3, 3, 2, 3, 1, 1, 0,
- 0, 0, 2, 2, 1, 1, 1, 1, 4, 1,
- 1, 2, 1, 3, 3, 3, 3, 3, 3, 3,
- 3, 3, 3, 3, 2, 3, 3, 3, 3, 2,
- 2, 3, 4, 4, 2, 1, 1, 1, 2, 2,
- 2, 1, 2, 4, 2, 1, 3, 2, 2, 2,
- 2, 3, 2, 2, 1, 3, 2, 2, 2, 3,
- 2, 0, 8, 2, 0, 8, 0, 1, 3, 4,
- 0, 3, 0, 1, 3
+ 0, 0, 2, 2, 3, 1, 1, 1, 1, 4,
+ 1, 1, 2, 1, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 2, 3, 3, 3, 3,
+ 2, 2, 3, 4, 4, 2, 1, 1, 1, 2,
+ 2, 2, 1, 2, 4, 2, 1, 3, 2, 2,
+ 2, 2, 3, 2, 2, 1, 3, 2, 2, 2,
+ 3, 2, 0, 8, 2, 0, 8, 0, 1, 3,
+ 4, 0, 3, 0, 1, 3
};
/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
@@ -719,259 +719,263 @@ static const yytype_uint8 yyr2[] =
means the default is an error. */
static const yytype_uint8 yydefact[] =
{
- 0, 0, 54, 60, 0, 55, 57, 91, 0, 122,
- 49, 56, 87, 0, 0, 47, 0, 0, 0, 0,
+ 0, 0, 55, 61, 0, 56, 58, 92, 0, 123,
+ 49, 57, 88, 0, 0, 47, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 3, 62, 21, 11, 22, 0, 0, 0, 19,
- 8, 85, 86, 7, 9, 5, 4, 59, 0, 62,
- 61, 90, 89, 123, 92, 122, 122, 95, 88, 0,
- 97, 0, 98, 0, 99, 104, 100, 102, 111, 84,
- 0, 45, 0, 0, 0, 0, 113, 114, 94, 107,
- 108, 110, 74, 0, 79, 80, 0, 1, 6, 0,
- 0, 0, 0, 42, 0, 0, 0, 0, 0, 0,
+ 0, 3, 63, 21, 11, 22, 0, 0, 0, 0,
+ 19, 8, 86, 87, 7, 9, 5, 4, 60, 0,
+ 63, 62, 91, 90, 124, 93, 123, 123, 96, 89,
+ 0, 98, 0, 99, 0, 100, 105, 101, 103, 112,
+ 85, 0, 45, 0, 0, 0, 0, 114, 115, 95,
+ 108, 109, 111, 75, 0, 80, 81, 0, 1, 6,
+ 0, 0, 0, 0, 42, 50, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 121, 0, 103, 106, 0, 101, 49, 0,
- 46, 0, 0, 0, 0, 0, 0, 49, 0, 109,
- 0, 0, 81, 2, 0, 50, 0, 0, 49, 0,
- 70, 71, 69, 72, 73, 76, 77, 78, 63, 64,
- 65, 66, 67, 68, 75, 124, 58, 96, 105, 116,
- 12, 17, 14, 0, 0, 16, 13, 26, 116, 93,
- 82, 83, 51, 0, 44, 51, 0, 43, 117, 0,
- 18, 15, 0, 50, 0, 0, 50, 50, 20, 0,
- 120, 120, 52, 53, 0, 0, 50, 49, 30, 118,
- 0, 51, 51, 0, 50, 51, 0, 51, 0, 48,
- 49, 50, 38, 0, 119, 112, 115, 23, 51, 50,
- 27, 50, 50, 40, 36, 0, 0, 37, 33, 0,
- 50, 0, 0, 35, 0, 0, 50, 49, 50, 49,
- 0, 0, 0, 0, 49, 31, 0, 32, 0, 0,
- 24, 28, 29, 50, 34, 50, 25, 41, 39
+ 0, 0, 0, 0, 122, 0, 104, 107, 0, 102,
+ 49, 0, 46, 0, 0, 0, 0, 0, 54, 49,
+ 0, 110, 0, 0, 82, 2, 0, 50, 0, 0,
+ 49, 0, 26, 71, 72, 70, 73, 74, 77, 78,
+ 79, 64, 65, 66, 67, 68, 69, 76, 125, 59,
+ 97, 106, 117, 12, 17, 14, 0, 0, 16, 13,
+ 117, 94, 83, 84, 51, 0, 44, 51, 0, 43,
+ 118, 0, 18, 15, 0, 50, 0, 0, 50, 50,
+ 20, 0, 121, 121, 52, 53, 0, 0, 50, 49,
+ 30, 119, 0, 51, 51, 0, 50, 51, 0, 51,
+ 0, 48, 49, 50, 38, 0, 120, 113, 116, 23,
+ 51, 50, 27, 50, 50, 40, 36, 0, 0, 37,
+ 33, 0, 50, 0, 0, 35, 0, 0, 50, 49,
+ 50, 49, 0, 0, 0, 0, 49, 31, 0, 32,
+ 0, 0, 24, 28, 29, 50, 34, 50, 25, 41,
+ 39
};
/* YYDEFGOTO[NTERM-NUM]. */
static const yytype_int16 yydefgoto[] =
{
- -1, 30, 192, 31, 49, 33, 193, 35, 238, 222,
- 223, 212, 224, 92, 36, 37, 38, 213, 248, 173,
- 183, 39, 188, 58, 41, 66, 67, 42, 43, 118,
- 127, 179, 201, 44, 54
+ -1, 30, 194, 31, 50, 33, 195, 35, 240, 224,
+ 225, 214, 226, 93, 36, 37, 38, 215, 250, 142,
+ 185, 39, 40, 190, 59, 42, 67, 68, 43, 44,
+ 120, 129, 181, 203, 45, 55
};
/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
STATE-NUM. */
-#define YYPACT_NINF -198
+#define YYPACT_NINF -195
static const yytype_int16 yypact[] =
{
- 243, -53, -198, -198, 385, -198, -198, 385, 385, 385,
- 791, -198, -198, 18, 573, -198, 22, 385, 7, 6,
- 26, 31, 36, 385, 613, 385, 385, 385, 385, 385,
- 4, -198, 5, -198, -198, -198, -48, -8, 512, 769,
- -198, -198, -198, -198, -198, -198, -198, -14, 385, -198,
- 769, 769, 769, 769, -21, 385, 385, -198, -4, 385,
- -198, 385, -198, 38, -198, 769, 8, -198, -198, 151,
- 54, -198, -37, 385, -29, 39, -198, -198, 649, -198,
- 8, -198, 840, 671, 840, 840, 720, -198, 341, 512,
- 385, 512, 40, 747, 385, 385, 385, 385, 385, 385,
- 385, 385, 385, 385, 385, 385, 385, 385, 385, 151,
- 385, -57, -21, 63, 769, 769, 385, -198, -198, 64,
- -198, 385, 385, 627, 385, 385, 385, -198, 385, -198,
- 385, 385, -198, -198, 9, 769, 14, 693, -53, 385,
- 769, 769, 769, 769, 769, 769, 769, 769, 818, 818,
- 840, 840, 769, 769, 769, 769, -198, -198, 769, 65,
- -198, 769, 769, 385, 385, 769, 769, 769, 65, 769,
- 769, 769, -198, -12, -198, -198, 529, 769, -198, -58,
- 769, 769, -58, 402, 49, 385, 402, -198, -198, 73,
- 13, 13, -198, -198, 71, 385, 769, -11, -17, -198,
- 78, -198, -198, 60, 769, -198, 72, -198, 77, -198,
- -198, 77, -198, 512, -198, 402, 402, -198, -198, 402,
- -198, 402, 77, 77, -198, 512, 529, -198, 57, 67,
- 402, 79, 80, -198, 81, 68, -198, -198, -198, -198,
- 85, 74, 84, 87, -16, -198, 529, -198, 468, 76,
- -198, -198, -198, 402, -198, -198, -198, -198, -198
+ 254, -59, -195, -195, 364, -195, -195, 364, 364, 364,
+ 801, -195, -195, 14, 552, -195, 23, 364, 10, 6,
+ 30, 32, 39, 364, 592, 364, 364, 364, 364, 364,
+ 4, -195, 5, -195, -195, -195, -51, 1, 491, 364,
+ 779, -195, -195, -195, -195, -195, -195, -195, -5, 364,
+ -195, 779, 779, 779, 779, -8, 364, 364, -195, -3,
+ 364, -195, 364, -195, 37, -195, 779, 12, -195, -195,
+ 606, 61, -195, -31, 364, -30, 40, -195, -195, 659,
+ -195, 12, -195, 850, 681, 850, 850, 730, -195, 320,
+ 491, 364, 491, 41, 757, 779, 364, 364, 364, 364,
+ 364, 364, 364, 364, 364, 364, 364, 364, 364, 364,
+ 364, 606, 364, -57, -8, 64, 779, 779, 364, -195,
+ -195, 65, -195, 364, 364, 637, 364, 364, -195, -195,
+ 364, -195, 364, 364, -195, -195, 15, 779, 18, 703,
+ -59, 364, -195, 779, 779, 779, 779, 779, 779, 779,
+ 779, 828, 828, 850, 850, 779, 779, 779, 779, -195,
+ -195, 779, 67, -195, 779, 779, 364, 364, 779, 779,
+ 67, 779, 779, 779, -195, -1, -195, -195, 508, 779,
+ -195, -58, 779, 779, -58, 381, 46, 364, 381, -195,
+ -195, 76, 17, 17, -195, -195, 74, 364, 779, -20,
+ -11, -195, 81, -195, -195, 62, 779, -195, 72, -195,
+ 79, -195, -195, 79, -195, 491, -195, 381, 381, -195,
+ -195, 381, -195, 381, 79, 79, -195, 491, 508, -195,
+ 58, 66, 381, 80, 83, -195, 86, 71, -195, -195,
+ -195, -195, 88, 75, 85, 87, -17, -195, 508, -195,
+ 447, 77, -195, -195, -195, 381, -195, -195, -195, -195,
+ -195
};
/* YYPGOTO[NTERM-NUM]. */
static const yytype_int16 yypgoto[] =
{
- -198, -198, 12, 25, 2, -170, 0, -198, -198, -198,
- -78, -197, -101, -61, -198, -198, -198, -186, -9, 113,
- -167, 41, 3, -198, -198, 98, -7, -198, -198, -198,
- -198, -45, -67, -198, 16
+ -195, -195, 11, 19, 2, -170, 0, -195, -195, -195,
+ -79, -191, -102, -61, -195, -195, -195, -194, -9, -12,
+ -171, -195, 38, 3, -195, -195, 99, -13, -195, -195,
+ -195, -195, -46, -67, -195, 13
};
/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If
positive, shift that token. If negative, reduce the rule which
number is the opposite. If zero, do what YYDEFACT says.
If YYTABLE_NINF, syntax error. */
-#define YYTABLE_NINF -60
+#define YYTABLE_NINF -61
static const yytype_int16 yytable[] =
{
- 34, 56, 32, 40, 87, -10, 187, 121, 186, 209,
- -51, -51, 184, 46, 227, 124, 206, 81, 45, 72,
- 73, 156, 225, 45, 189, 110, 227, 74, 134, 89,
- 136, 210, 185, 122, 215, 216, 225, 207, 219, 57,
- 221, 125, 88, 68, 61, 50, 63, 75, 51, 52,
- 53, 230, 76, 70, 71, 65, 236, 77, 69, 117,
- -51, 110, 90, 55, 78, 65, 82, 83, 84, 85,
- 86, 111, 112, 129, 116, 120, 254, 253, 113, 93,
- 45, -10, 126, 138, 157, 160, 178, 172, 34, 109,
- 32, 40, 174, 195, 199, 200, 53, 53, 203, 214,
- 114, 217, 115, 209, 220, 237, 241, 242, 243, 159,
- 239, 244, 249, 133, 123, 250, 251, 256, 168, 252,
- 211, 233, 80, 182, 202, 0, 0, 0, 0, 176,
- 93, 135, 137, 0, 0, 140, 141, 142, 143, 144,
- 145, 146, 147, 148, 149, 150, 151, 152, 153, 154,
- 175, 155, 229, 0, 0, 0, 0, 158, 0, 0,
- 0, 0, 161, 162, 235, 165, 166, 167, 0, 169,
- 0, 170, 171, 0, 0, 0, 0, 0, 0, 0,
- 177, 0, 0, 0, 119, 0, 0, 0, 208, 0,
- 0, 190, 0, 0, 191, 0, 0, 0, 0, 0,
- 0, 226, 0, 0, 180, 181, 94, 95, 0, 0,
- 0, 96, 97, 98, 0, 99, 100, 101, 0, 0,
- 0, 0, 102, 103, 104, 105, 196, 0, 246, 0,
- 106, 107, 108, 0, 0, 0, 204, 0, 0, 0,
- 0, 0, 0, -6, 1, 0, 0, 0, 255, 0,
- 0, 0, 0, 0, 93, 2, 3, 4, 5, 0,
- 6, 7, 8, 9, 10, 11, 93, 0, 0, 0,
- 0, 12, 0, 13, 14, 15, 0, 0, 16, 0,
- 0, 0, 0, 17, 18, 19, 0, 0, 20, 0,
- 0, 0, 21, 22, 23, 24, 194, 0, 0, 197,
- 198, 0, 0, 0, 0, 0, 25, 0, 0, 205,
- 26, 0, 0, 0, 27, 28, 0, 218, 0, -6,
- 29, 0, 0, 0, 228, 0, 0, 0, 0, 0,
- 0, 0, 231, 0, 232, 234, 0, 0, 0, 0,
- 0, 0, 0, 240, 0, 0, 0, 0, 0, 245,
- 0, 247, 0, 2, 3, 4, 5, 0, 6, 7,
- 8, 9, 10, 11, 0, 0, 257, 0, 258, 12,
- 0, 13, 14, 15, 0, 0, 16, 0, 0, 0,
- 0, 17, 18, 19, 0, 0, 20, 0, 0, 0,
- 21, 22, 23, 24, 0, 0, 0, 2, 3, 4,
- 5, 0, 6, 0, 25, 0, 47, 11, 26, 0,
- 0, 0, 27, 28, 2, 3, 4, 5, 29, 6,
- 7, 8, 9, 47, 11, 48, 0, 19, 0, 0,
- 12, 0, 13, 14, 15, 0, 0, 0, 0, 0,
- 0, 0, 17, 18, 19, 0, 0, 20, 25, 0,
- 0, 0, 26, 23, 24, 0, 27, 28, 0, 0,
- 0, 0, 29, 0, 0, 25, 0, 0, 0, 26,
- 0, 0, 0, 27, 28, 0, 0, 0, 45, 29,
- 2, 3, 4, 5, 0, 6, 7, 8, 9, 47,
- 11, 0, 0, 0, 0, 0, 12, 0, 13, 14,
- 15, 0, 0, 0, 0, 0, 0, 0, 17, 18,
- 19, 0, 0, 20, 0, 0, 0, 0, 0, 23,
- 24, 0, 0, 0, 2, 3, 4, 5, 0, 6,
- 0, 25, 0, 47, 11, 26, 0, 0, 0, 27,
- 28, 2, 3, 4, 5, 29, 6, 7, 8, 9,
- 47, 11, 48, 0, 19, 0, 0, 12, 0, 13,
- 14, 0, 0, 0, 0, 0, 0, 0, 0, 17,
- 0, 19, 0, 0, 0, 25, 0, 0, 0, 26,
- 23, 24, 0, 27, 28, 2, 3, 4, 5, 91,
- 6, 0, 25, 0, 47, 11, 26, 0, 0, 0,
- 27, 28, 59, 0, 0, 0, 29, 60, 0, 61,
- 62, 63, 64, 48, 0, 19, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 2, 3, 4, 5, 0,
- 6, 0, 0, 0, 47, 11, 25, 0, 0, 0,
- 26, 0, 59, 0, 27, 28, 0, 0, 0, 61,
- 29, 63, 0, 48, 0, 19, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 79, 0, 0, 0,
- 0, 163, 0, 0, 0, 0, 25, 0, 0, 0,
- 26, 0, 94, 95, 27, 28, 0, 96, 97, 98,
- 29, 99, 100, 101, 0, 128, 0, 164, 102, 103,
- 104, 105, 0, 0, 94, 95, 106, 107, 108, 96,
- 97, 98, 0, 99, 100, 101, 0, 0, 0, 0,
- 102, 103, 104, 105, 0, 0, 94, 95, 106, 107,
- 108, 96, 97, 98, 0, 99, 100, 101, 0, 130,
- 131, 0, 102, 103, 104, 105, 0, 0, 94, 95,
- 106, 107, 108, 96, 97, 98, 0, 99, 100, 101,
- 0, 0, 0, 139, 102, 103, 104, 105, 0, 0,
- 0, 132, 106, 107, 108, 94, 95, 0, 0, 0,
- 96, 97, 98, 0, 99, 100, 101, 0, 0, 0,
- 0, 102, 103, 104, 105, 0, 0, 0, 132, 106,
- 107, 108, 94, 95, 0, 0, 0, 96, 97, 98,
- 0, 99, 100, 101, 0, 0, 0, 139, 102, 103,
- 104, 105, 0, 0, 94, 95, 106, 107, 108, 96,
- 97, 98, 0, 99, 100, 101, 0, 0, 0, 0,
- 102, 103, 104, 105, 0, 0, -59, -59, 106, 107,
- 108, -59, -59, -59, 0, -59, -59, -59, 0, 0,
- 0, 0, 0, 0, -59, -59, 0, 0, 55, 0,
- -59, -59, -59, 94, 95, 0, 0, 0, 96, 97,
- 98, 0, 99, 100, 101, 0, 0, 0, 0, 0,
- 0, 104, 105, 0, 0, 94, 95, 106, 107, 108,
- 96, 97, 98, 0, 99, 100, 101, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 106,
- 107, 108
+ 34, 57, 32, 41, 88, -10, 188, 208, 189, -51,
+ -51, 82, 47, 123, 126, 211, 227, 46, 46, 73,
+ 74, 159, 229, 186, 191, 112, 90, 75, 209, 136,
+ 227, 138, 217, 218, 229, 58, 221, 212, 223, 124,
+ 127, 89, 51, 187, 69, 52, 53, 54, 62, 232,
+ 64, 76, 66, 77, 119, 70, 71, 72, 238, -51,
+ 78, 79, 66, 83, 84, 85, 86, 87, 131, 113,
+ 114, 91, 56, 118, 112, 255, 94, 95, 256, 115,
+ 46, -10, 122, 128, 140, 160, 163, 111, 180, 34,
+ 197, 32, 41, 174, 54, 54, 176, 201, 116, 202,
+ 117, 205, 216, 219, 222, 211, 239, 243, 135, 241,
+ 244, 162, 125, 245, 246, 251, 252, 253, 258, 254,
+ 170, 213, 235, 81, 184, 175, 204, 0, 94, 137,
+ 139, 178, 0, 0, 143, 144, 145, 146, 147, 148,
+ 149, 150, 151, 152, 153, 154, 155, 156, 157, 0,
+ 158, 177, 0, 0, 231, 0, 161, 0, 0, 0,
+ 0, 164, 165, 0, 168, 169, 237, 0, 171, 0,
+ 172, 173, 0, 196, 0, 0, 199, 200, 0, 179,
+ 0, 0, 0, 0, 0, 0, 207, 0, 0, 0,
+ 210, 0, 192, 0, 220, 193, 0, 0, 0, 0,
+ 0, 230, 0, 228, 182, 183, 0, 0, 0, 233,
+ 0, 234, 236, 0, 0, 0, 0, 0, 0, 0,
+ 242, 0, 0, 0, 0, 198, 247, 0, 249, 0,
+ 248, 0, 0, 0, 0, 206, 0, 0, 0, 0,
+ 0, 0, 0, 259, 0, 260, 0, 0, 0, 0,
+ 257, 0, 0, 94, -6, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 94, 2, 3, 4, 5,
+ 0, 6, 7, 8, 9, 10, 11, 0, 0, 0,
+ 0, 0, 12, 0, 13, 14, 15, 0, 0, 16,
+ 0, 0, 0, 0, 17, 18, 19, 0, 0, 20,
+ 0, 0, 0, 21, 22, 23, 24, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 25, 0, 0,
+ 0, 26, 0, 0, 0, 27, 28, 0, 0, 0,
+ -6, 29, 2, 3, 4, 5, 0, 6, 7, 8,
+ 9, 10, 11, 0, 0, 0, 0, 0, 12, 0,
+ 13, 14, 15, 0, 0, 16, 0, 0, 0, 0,
+ 17, 18, 19, 0, 0, 20, 0, 0, 0, 21,
+ 22, 23, 24, 0, 0, 0, 2, 3, 4, 5,
+ 0, 6, 0, 25, 0, 48, 11, 26, 0, 0,
+ 0, 27, 28, 2, 3, 4, 5, 29, 6, 7,
+ 8, 9, 48, 11, 49, 0, 19, 0, 0, 12,
+ 0, 13, 14, 15, 0, 0, 0, 0, 0, 0,
+ 0, 17, 18, 19, 0, 0, 20, 25, 0, 0,
+ 0, 26, 23, 24, 0, 27, 28, 0, 0, 0,
+ 0, 29, 0, 0, 25, 0, 0, 0, 26, 0,
+ 0, 0, 27, 28, 0, 0, 0, 46, 29, 2,
+ 3, 4, 5, 0, 6, 7, 8, 9, 48, 11,
+ 0, 0, 0, 0, 0, 12, 0, 13, 14, 15,
+ 0, 0, 0, 0, 0, 0, 0, 17, 18, 19,
+ 0, 0, 20, 0, 0, 0, 0, 0, 23, 24,
+ 0, 0, 0, 2, 3, 4, 5, 0, 6, 0,
+ 25, 0, 48, 11, 26, 0, 0, 0, 27, 28,
+ 2, 3, 4, 5, 29, 6, 7, 8, 9, 48,
+ 11, 49, 0, 19, 0, 0, 12, 0, 13, 14,
+ 0, 0, 0, 0, 0, 0, 0, 0, 17, 0,
+ 19, 0, 0, 0, 25, 0, 0, 0, 26, 23,
+ 24, 0, 27, 28, 2, 3, 4, 5, 92, 6,
+ 0, 25, 0, 48, 11, 26, 0, 0, 0, 27,
+ 28, 60, 0, 0, 0, 29, 61, 0, 62, 63,
+ 64, 65, 49, 0, 19, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 3, 4, 5, 0, 6,
+ 0, 0, 0, 48, 11, 25, 0, 0, 0, 26,
+ 0, 60, 0, 27, 28, 0, 0, 0, 62, 29,
+ 64, 0, 49, 0, 19, 0, 0, 0, 0, 121,
+ 0, 0, 0, 0, 0, 80, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 25, 0, 0, 0, 26,
+ 0, 96, 97, 27, 28, 0, 98, 99, 100, 29,
+ 101, 102, 103, 0, 0, 0, 0, 104, 105, 106,
+ 107, 166, 0, 0, 0, 108, 109, 110, 0, 0,
+ 0, 0, 96, 97, 0, 0, 0, 98, 99, 100,
+ 0, 101, 102, 103, 0, 130, 0, 167, 104, 105,
+ 106, 107, 0, 0, 96, 97, 108, 109, 110, 98,
+ 99, 100, 0, 101, 102, 103, 0, 0, 0, 0,
+ 104, 105, 106, 107, 0, 0, 96, 97, 108, 109,
+ 110, 98, 99, 100, 0, 101, 102, 103, 0, 132,
+ 133, 0, 104, 105, 106, 107, 0, 0, 96, 97,
+ 108, 109, 110, 98, 99, 100, 0, 101, 102, 103,
+ 0, 0, 0, 141, 104, 105, 106, 107, 0, 0,
+ 0, 134, 108, 109, 110, 96, 97, 0, 0, 0,
+ 98, 99, 100, 0, 101, 102, 103, 0, 0, 0,
+ 0, 104, 105, 106, 107, 0, 0, 0, 134, 108,
+ 109, 110, 96, 97, 0, 0, 0, 98, 99, 100,
+ 0, 101, 102, 103, 0, 0, 0, 141, 104, 105,
+ 106, 107, 0, 0, 96, 97, 108, 109, 110, 98,
+ 99, 100, 0, 101, 102, 103, 0, 0, 0, 0,
+ 104, 105, 106, 107, 0, 0, -60, -60, 108, 109,
+ 110, -60, -60, -60, 0, -60, -60, -60, 0, 0,
+ 0, 0, 0, 0, -60, -60, 0, 0, 56, 0,
+ -60, -60, -60, 96, 97, 0, 0, 0, 98, 99,
+ 100, 0, 101, 102, 103, 0, 0, 0, 0, 0,
+ 0, 106, 107, 0, 0, 96, 97, 108, 109, 110,
+ 98, 99, 100, 0, 101, 102, 103, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 108,
+ 109, 110
};
static const yytype_int16 yycheck[] =
{
- 0, 10, 0, 0, 0, 0, 176, 44, 175, 26,
- 26, 27, 24, 1, 211, 44, 27, 24, 76, 13,
- 14, 78, 208, 76, 82, 82, 223, 21, 89, 77,
- 91, 48, 44, 70, 201, 202, 222, 48, 205, 21,
- 207, 70, 30, 21, 36, 4, 38, 21, 7, 8,
- 9, 218, 21, 46, 47, 14, 226, 21, 17, 66,
- 76, 82, 70, 77, 23, 24, 25, 26, 27, 28,
- 29, 55, 56, 80, 36, 21, 246, 244, 82, 38,
- 76, 76, 43, 43, 21, 21, 21, 78, 88, 48,
- 88, 88, 78, 44, 21, 82, 55, 56, 27, 21,
- 59, 41, 61, 26, 32, 48, 27, 27, 27, 118,
- 43, 43, 27, 88, 73, 41, 32, 41, 127, 32,
- 198, 222, 24, 168, 191, -1, -1, -1, -1, 138,
- 89, 90, 91, -1, -1, 94, 95, 96, 97, 98,
- 99, 100, 101, 102, 103, 104, 105, 106, 107, 108,
- 138, 110, 213, -1, -1, -1, -1, 116, -1, -1,
- -1, -1, 121, 122, 225, 124, 125, 126, -1, 128,
- -1, 130, 131, -1, -1, -1, -1, -1, -1, -1,
- 139, -1, -1, -1, 33, -1, -1, -1, 197, -1,
- -1, 179, -1, -1, 182, -1, -1, -1, -1, -1,
- -1, 210, -1, -1, 163, 164, 55, 56, -1, -1,
- -1, 60, 61, 62, -1, 64, 65, 66, -1, -1,
- -1, -1, 71, 72, 73, 74, 185, -1, 237, -1,
- 79, 80, 81, -1, -1, -1, 195, -1, -1, -1,
- -1, -1, -1, 0, 1, -1, -1, -1, 248, -1,
- -1, -1, -1, -1, 213, 12, 13, 14, 15, -1,
- 17, 18, 19, 20, 21, 22, 225, -1, -1, -1,
- -1, 28, -1, 30, 31, 32, -1, -1, 35, -1,
- -1, -1, -1, 40, 41, 42, -1, -1, 45, -1,
- -1, -1, 49, 50, 51, 52, 183, -1, -1, 186,
- 187, -1, -1, -1, -1, -1, 63, -1, -1, 196,
- 67, -1, -1, -1, 71, 72, -1, 204, -1, 76,
- 77, -1, -1, -1, 211, -1, -1, -1, -1, -1,
- -1, -1, 219, -1, 221, 222, -1, -1, -1, -1,
- -1, -1, -1, 230, -1, -1, -1, -1, -1, 236,
- -1, 238, -1, 12, 13, 14, 15, -1, 17, 18,
- 19, 20, 21, 22, -1, -1, 253, -1, 255, 28,
- -1, 30, 31, 32, -1, -1, 35, -1, -1, -1,
- -1, 40, 41, 42, -1, -1, 45, -1, -1, -1,
- 49, 50, 51, 52, -1, -1, -1, 12, 13, 14,
- 15, -1, 17, -1, 63, -1, 21, 22, 67, -1,
- -1, -1, 71, 72, 12, 13, 14, 15, 77, 17,
- 18, 19, 20, 21, 22, 40, -1, 42, -1, -1,
- 28, -1, 30, 31, 32, -1, -1, -1, -1, -1,
- -1, -1, 40, 41, 42, -1, -1, 45, 63, -1,
- -1, -1, 67, 51, 52, -1, 71, 72, -1, -1,
- -1, -1, 77, -1, -1, 63, -1, -1, -1, 67,
- -1, -1, -1, 71, 72, -1, -1, -1, 76, 77,
- 12, 13, 14, 15, -1, 17, 18, 19, 20, 21,
- 22, -1, -1, -1, -1, -1, 28, -1, 30, 31,
- 32, -1, -1, -1, -1, -1, -1, -1, 40, 41,
- 42, -1, -1, 45, -1, -1, -1, -1, -1, 51,
- 52, -1, -1, -1, 12, 13, 14, 15, -1, 17,
+ 0, 10, 0, 0, 0, 0, 177, 27, 178, 26,
+ 27, 24, 1, 44, 44, 26, 210, 76, 76, 13,
+ 14, 78, 213, 24, 82, 82, 77, 21, 48, 90,
+ 224, 92, 203, 204, 225, 21, 207, 48, 209, 70,
+ 70, 30, 4, 44, 21, 7, 8, 9, 36, 220,
+ 38, 21, 14, 21, 67, 17, 46, 47, 228, 76,
+ 21, 23, 24, 25, 26, 27, 28, 29, 81, 56,
+ 57, 70, 77, 36, 82, 246, 38, 39, 248, 82,
+ 76, 76, 21, 43, 43, 21, 21, 49, 21, 89,
+ 44, 89, 89, 78, 56, 57, 78, 21, 60, 82,
+ 62, 27, 21, 41, 32, 26, 48, 27, 89, 43,
+ 27, 120, 74, 27, 43, 27, 41, 32, 41, 32,
+ 129, 200, 224, 24, 170, 137, 193, -1, 90, 91,
+ 92, 140, -1, -1, 96, 97, 98, 99, 100, 101,
+ 102, 103, 104, 105, 106, 107, 108, 109, 110, -1,
+ 112, 140, -1, -1, 215, -1, 118, -1, -1, -1,
+ -1, 123, 124, -1, 126, 127, 227, -1, 130, -1,
+ 132, 133, -1, 185, -1, -1, 188, 189, -1, 141,
+ -1, -1, -1, -1, -1, -1, 198, -1, -1, -1,
+ 199, -1, 181, -1, 206, 184, -1, -1, -1, -1,
+ -1, 213, -1, 212, 166, 167, -1, -1, -1, 221,
+ -1, 223, 224, -1, -1, -1, -1, -1, -1, -1,
+ 232, -1, -1, -1, -1, 187, 238, -1, 240, -1,
+ 239, -1, -1, -1, -1, 197, -1, -1, -1, -1,
+ -1, -1, -1, 255, -1, 257, -1, -1, -1, -1,
+ 250, -1, -1, 215, 0, 1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 227, 12, 13, 14, 15,
+ -1, 17, 18, 19, 20, 21, 22, -1, -1, -1,
+ -1, -1, 28, -1, 30, 31, 32, -1, -1, 35,
+ -1, -1, -1, -1, 40, 41, 42, -1, -1, 45,
+ -1, -1, -1, 49, 50, 51, 52, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 63, -1, -1,
+ -1, 67, -1, -1, -1, 71, 72, -1, -1, -1,
+ 76, 77, 12, 13, 14, 15, -1, 17, 18, 19,
+ 20, 21, 22, -1, -1, -1, -1, -1, 28, -1,
+ 30, 31, 32, -1, -1, 35, -1, -1, -1, -1,
+ 40, 41, 42, -1, -1, 45, -1, -1, -1, 49,
+ 50, 51, 52, -1, -1, -1, 12, 13, 14, 15,
+ -1, 17, -1, 63, -1, 21, 22, 67, -1, -1,
+ -1, 71, 72, 12, 13, 14, 15, 77, 17, 18,
+ 19, 20, 21, 22, 40, -1, 42, -1, -1, 28,
+ -1, 30, 31, 32, -1, -1, -1, -1, -1, -1,
+ -1, 40, 41, 42, -1, -1, 45, 63, -1, -1,
+ -1, 67, 51, 52, -1, 71, 72, -1, -1, -1,
+ -1, 77, -1, -1, 63, -1, -1, -1, 67, -1,
+ -1, -1, 71, 72, -1, -1, -1, 76, 77, 12,
+ 13, 14, 15, -1, 17, 18, 19, 20, 21, 22,
+ -1, -1, -1, -1, -1, 28, -1, 30, 31, 32,
+ -1, -1, -1, -1, -1, -1, -1, 40, 41, 42,
+ -1, -1, 45, -1, -1, -1, -1, -1, 51, 52,
+ -1, -1, -1, 12, 13, 14, 15, -1, 17, -1,
+ 63, -1, 21, 22, 67, -1, -1, -1, 71, 72,
+ 12, 13, 14, 15, 77, 17, 18, 19, 20, 21,
+ 22, 40, -1, 42, -1, -1, 28, -1, 30, 31,
+ -1, -1, -1, -1, -1, -1, -1, -1, 40, -1,
+ 42, -1, -1, -1, 63, -1, -1, -1, 67, 51,
+ 52, -1, 71, 72, 12, 13, 14, 15, 77, 17,
-1, 63, -1, 21, 22, 67, -1, -1, -1, 71,
- 72, 12, 13, 14, 15, 77, 17, 18, 19, 20,
- 21, 22, 40, -1, 42, -1, -1, 28, -1, 30,
- 31, -1, -1, -1, -1, -1, -1, -1, -1, 40,
- -1, 42, -1, -1, -1, 63, -1, -1, -1, 67,
- 51, 52, -1, 71, 72, 12, 13, 14, 15, 77,
- 17, -1, 63, -1, 21, 22, 67, -1, -1, -1,
- 71, 72, 29, -1, -1, -1, 77, 34, -1, 36,
- 37, 38, 39, 40, -1, 42, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 12, 13, 14, 15, -1,
- 17, -1, -1, -1, 21, 22, 63, -1, -1, -1,
- 67, -1, 29, -1, 71, 72, -1, -1, -1, 36,
- 77, 38, -1, 40, -1, 42, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 53, -1, -1, -1,
- -1, 44, -1, -1, -1, -1, 63, -1, -1, -1,
- 67, -1, 55, 56, 71, 72, -1, 60, 61, 62,
- 77, 64, 65, 66, -1, 46, -1, 70, 71, 72,
+ 72, 29, -1, -1, -1, 77, 34, -1, 36, 37,
+ 38, 39, 40, -1, 42, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 12, 13, 14, 15, -1, 17,
+ -1, -1, -1, 21, 22, 63, -1, -1, -1, 67,
+ -1, 29, -1, 71, 72, -1, -1, -1, 36, 77,
+ 38, -1, 40, -1, 42, -1, -1, -1, -1, 33,
+ -1, -1, -1, -1, -1, 53, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 63, -1, -1, -1, 67,
+ -1, 55, 56, 71, 72, -1, 60, 61, 62, 77,
+ 64, 65, 66, -1, -1, -1, -1, 71, 72, 73,
+ 74, 44, -1, -1, -1, 79, 80, 81, -1, -1,
+ -1, -1, 55, 56, -1, -1, -1, 60, 61, 62,
+ -1, 64, 65, 66, -1, 46, -1, 70, 71, 72,
73, 74, -1, -1, 55, 56, 79, 80, 81, 60,
61, 62, -1, 64, 65, 66, -1, -1, -1, -1,
71, 72, 73, 74, -1, -1, 55, 56, 79, 80,
@@ -1005,28 +1009,29 @@ static const yytype_uint8 yystos[] =
21, 22, 28, 30, 31, 32, 35, 40, 41, 42,
45, 49, 50, 51, 52, 63, 67, 71, 72, 77,
84, 86, 87, 88, 89, 90, 97, 98, 99, 104,
- 105, 107, 110, 111, 116, 76, 85, 21, 40, 87,
- 104, 104, 104, 104, 117, 77, 101, 21, 106, 29,
- 34, 36, 37, 38, 39, 104, 108, 109, 21, 104,
- 46, 47, 13, 14, 21, 21, 21, 21, 104, 53,
- 108, 109, 104, 104, 104, 104, 104, 0, 85, 77,
- 70, 77, 96, 104, 55, 56, 60, 61, 62, 64,
- 65, 66, 71, 72, 73, 74, 79, 80, 81, 104,
- 82, 117, 117, 82, 104, 104, 36, 109, 112, 33,
- 21, 44, 70, 104, 44, 70, 43, 113, 46, 109,
- 68, 69, 78, 86, 96, 104, 96, 104, 43, 70,
- 104, 104, 104, 104, 104, 104, 104, 104, 104, 104,
- 104, 104, 104, 104, 104, 104, 78, 21, 104, 101,
- 21, 104, 104, 44, 70, 104, 104, 104, 101, 104,
- 104, 104, 78, 102, 78, 85, 101, 104, 21, 114,
- 104, 104, 114, 103, 24, 44, 103, 88, 105, 82,
- 85, 85, 85, 89, 102, 44, 104, 102, 102, 21,
- 82, 115, 115, 27, 104, 102, 27, 48, 101, 26,
- 48, 93, 94, 100, 21, 103, 103, 41, 102, 103,
- 32, 103, 92, 93, 95, 100, 101, 94, 102, 96,
- 103, 102, 102, 95, 102, 96, 88, 48, 91, 43,
- 102, 27, 27, 27, 43, 102, 101, 102, 101, 27,
- 41, 32, 32, 103, 88, 89, 41, 102, 102
+ 105, 106, 108, 111, 112, 117, 76, 85, 21, 40,
+ 87, 105, 105, 105, 105, 118, 77, 101, 21, 107,
+ 29, 34, 36, 37, 38, 39, 105, 109, 110, 21,
+ 105, 46, 47, 13, 14, 21, 21, 21, 21, 105,
+ 53, 109, 110, 105, 105, 105, 105, 105, 0, 85,
+ 77, 70, 77, 96, 105, 105, 55, 56, 60, 61,
+ 62, 64, 65, 66, 71, 72, 73, 74, 79, 80,
+ 81, 105, 82, 118, 118, 82, 105, 105, 36, 110,
+ 113, 33, 21, 44, 70, 105, 44, 70, 43, 114,
+ 46, 110, 68, 69, 78, 86, 96, 105, 96, 105,
+ 43, 70, 102, 105, 105, 105, 105, 105, 105, 105,
+ 105, 105, 105, 105, 105, 105, 105, 105, 105, 78,
+ 21, 105, 101, 21, 105, 105, 44, 70, 105, 105,
+ 101, 105, 105, 105, 78, 102, 78, 85, 101, 105,
+ 21, 115, 105, 105, 115, 103, 24, 44, 103, 88,
+ 106, 82, 85, 85, 85, 89, 102, 44, 105, 102,
+ 102, 21, 82, 116, 116, 27, 105, 102, 27, 48,
+ 101, 26, 48, 93, 94, 100, 21, 103, 103, 41,
+ 102, 103, 32, 103, 92, 93, 95, 100, 101, 94,
+ 102, 96, 103, 102, 102, 95, 102, 96, 88, 48,
+ 91, 43, 102, 27, 27, 27, 43, 102, 101, 102,
+ 101, 27, 41, 32, 32, 103, 88, 89, 41, 102,
+ 102
};
#define yyerrok (yyerrstatus = 0)
@@ -1986,12 +1991,15 @@ yyreduce:
case 26:
#line 220 "engines/director/lingo/lingo-gr.y"
{
- g_lingo->code1(g_lingo->c_ifcode);
+ inst end = 0;
+ WRITE_UINT32(&end, (yyvsp[(3) - (3)].code));
+ g_lingo->code1(STOP);
+ (*g_lingo->_currentScript)[(yyvsp[(1) - (3)].code) + 1] = end;
;}
break;
case 27:
-#line 225 "engines/director/lingo/lingo-gr.y"
+#line 228 "engines/director/lingo/lingo-gr.y"
{
inst then = 0, end = 0;
WRITE_UINT32(&then, (yyvsp[(5) - (8)].code));
@@ -2002,7 +2010,7 @@ yyreduce:
break;
case 28:
-#line 232 "engines/director/lingo/lingo-gr.y"
+#line 235 "engines/director/lingo/lingo-gr.y"
{
inst then = 0, else1 = 0, end = 0;
WRITE_UINT32(&then, (yyvsp[(5) - (11)].code));
@@ -2015,7 +2023,7 @@ yyreduce:
break;
case 29:
-#line 241 "engines/director/lingo/lingo-gr.y"
+#line 244 "engines/director/lingo/lingo-gr.y"
{
inst then = 0, else1 = 0, end = 0;
WRITE_UINT32(&then, (yyvsp[(5) - (11)].code));
@@ -2028,7 +2036,7 @@ yyreduce:
break;
case 30:
-#line 250 "engines/director/lingo/lingo-gr.y"
+#line 253 "engines/director/lingo/lingo-gr.y"
{
inst then = 0, else1 = 0, end = 0;
WRITE_UINT32(&then, (yyvsp[(4) - (6)].code));
@@ -2042,7 +2050,7 @@ yyreduce:
break;
case 31:
-#line 260 "engines/director/lingo/lingo-gr.y"
+#line 263 "engines/director/lingo/lingo-gr.y"
{
inst then = 0, else1 = 0, end = 0;
WRITE_UINT32(&then, (yyvsp[(4) - (10)].code));
@@ -2056,7 +2064,7 @@ yyreduce:
break;
case 32:
-#line 270 "engines/director/lingo/lingo-gr.y"
+#line 273 "engines/director/lingo/lingo-gr.y"
{
inst then = 0, else1 = 0, end = 0;
WRITE_UINT32(&then, (yyvsp[(4) - (10)].code));
@@ -2070,17 +2078,17 @@ yyreduce:
break;
case 33:
-#line 281 "engines/director/lingo/lingo-gr.y"
+#line 284 "engines/director/lingo/lingo-gr.y"
{ (yyval.code) = 0; ;}
break;
case 34:
-#line 282 "engines/director/lingo/lingo-gr.y"
+#line 285 "engines/director/lingo/lingo-gr.y"
{ (yyval.code) = (yyvsp[(2) - (3)].code); ;}
break;
case 39:
-#line 293 "engines/director/lingo/lingo-gr.y"
+#line 296 "engines/director/lingo/lingo-gr.y"
{
inst then = 0;
WRITE_UINT32(&then, (yyvsp[(4) - (6)].code));
@@ -2090,7 +2098,7 @@ yyreduce:
break;
case 41:
-#line 302 "engines/director/lingo/lingo-gr.y"
+#line 305 "engines/director/lingo/lingo-gr.y"
{
inst then = 0;
WRITE_UINT32(&then, (yyvsp[(4) - (5)].code));
@@ -2100,22 +2108,22 @@ yyreduce:
break;
case 42:
-#line 310 "engines/director/lingo/lingo-gr.y"
+#line 313 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(STOP); ;}
break;
case 43:
-#line 311 "engines/director/lingo/lingo-gr.y"
+#line 314 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code2(g_lingo->c_eq, STOP); ;}
break;
case 45:
-#line 314 "engines/director/lingo/lingo-gr.y"
+#line 317 "engines/director/lingo/lingo-gr.y"
{ (yyval.code) = g_lingo->code3(g_lingo->c_repeatwhilecode, STOP, STOP); ;}
break;
case 46:
-#line 316 "engines/director/lingo/lingo-gr.y"
+#line 319 "engines/director/lingo/lingo-gr.y"
{
(yyval.code) = g_lingo->code3(g_lingo->c_repeatwithcode, STOP, STOP);
g_lingo->code3(STOP, STOP, STOP);
@@ -2124,7 +2132,7 @@ yyreduce:
break;
case 47:
-#line 322 "engines/director/lingo/lingo-gr.y"
+#line 325 "engines/director/lingo/lingo-gr.y"
{
(yyval.code) = g_lingo->code1(g_lingo->c_ifcode);
g_lingo->code3(STOP, STOP, STOP);
@@ -2133,7 +2141,7 @@ yyreduce:
break;
case 48:
-#line 328 "engines/director/lingo/lingo-gr.y"
+#line 331 "engines/director/lingo/lingo-gr.y"
{
inst skipEnd;
WRITE_UINT32(&skipEnd, 1); // We have to skip end to avoid multiple executions
@@ -2143,64 +2151,73 @@ yyreduce:
break;
case 49:
-#line 335 "engines/director/lingo/lingo-gr.y"
+#line 338 "engines/director/lingo/lingo-gr.y"
{ (yyval.code) = g_lingo->_currentScript->size(); ;}
break;
case 50:
-#line 337 "engines/director/lingo/lingo-gr.y"
+#line 340 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(STOP); (yyval.code) = g_lingo->_currentScript->size(); ;}
break;
case 51:
-#line 339 "engines/director/lingo/lingo-gr.y"
+#line 342 "engines/director/lingo/lingo-gr.y"
{ (yyval.code) = g_lingo->_currentScript->size(); ;}
break;
case 54:
-#line 344 "engines/director/lingo/lingo-gr.y"
- { (yyval.code) = g_lingo->codeConst((yyvsp[(1) - (1)].i)); ;}
+#line 347 "engines/director/lingo/lingo-gr.y"
+ {
+ (yyval.code) = g_lingo->code1(g_lingo->c_whencode);
+ g_lingo->code1(STOP);
+ g_lingo->codeString((yyvsp[(2) - (3)].s)->c_str());
+ delete (yyvsp[(2) - (3)].s); ;}
break;
case 55:
-#line 345 "engines/director/lingo/lingo-gr.y"
+#line 353 "engines/director/lingo/lingo-gr.y"
+ { (yyval.code) = g_lingo->codeConst((yyvsp[(1) - (1)].i)); ;}
+ break;
+
+ case 56:
+#line 354 "engines/director/lingo/lingo-gr.y"
{
(yyval.code) = g_lingo->code1(g_lingo->c_fconstpush);
g_lingo->codeFloat((yyvsp[(1) - (1)].f)); ;}
break;
- case 56:
-#line 348 "engines/director/lingo/lingo-gr.y"
+ case 57:
+#line 357 "engines/director/lingo/lingo-gr.y"
{
(yyval.code) = g_lingo->code1(g_lingo->c_stringpush);
g_lingo->codeString((yyvsp[(1) - (1)].s)->c_str()); ;}
break;
- case 57:
-#line 351 "engines/director/lingo/lingo-gr.y"
+ case 58:
+#line 360 "engines/director/lingo/lingo-gr.y"
{
(yyval.code) = g_lingo->code1(g_lingo->_handlers[*(yyvsp[(1) - (1)].s)]->u.func);
g_lingo->codeConst(0); // Put dummy value
delete (yyvsp[(1) - (1)].s); ;}
break;
- case 58:
-#line 355 "engines/director/lingo/lingo-gr.y"
+ case 59:
+#line 364 "engines/director/lingo/lingo-gr.y"
{
(yyval.code) = g_lingo->codeFunc((yyvsp[(1) - (4)].s), (yyvsp[(3) - (4)].narg));
delete (yyvsp[(1) - (4)].s); ;}
break;
- case 59:
-#line 358 "engines/director/lingo/lingo-gr.y"
+ case 60:
+#line 367 "engines/director/lingo/lingo-gr.y"
{
(yyval.code) = g_lingo->code1(g_lingo->c_eval);
g_lingo->codeString((yyvsp[(1) - (1)].s)->c_str());
delete (yyvsp[(1) - (1)].s); ;}
break;
- case 60:
-#line 362 "engines/director/lingo/lingo-gr.y"
+ case 61:
+#line 371 "engines/director/lingo/lingo-gr.y"
{
(yyval.code) = g_lingo->codeConst(0); // Put dummy id
g_lingo->code1(g_lingo->c_theentitypush);
@@ -2210,8 +2227,8 @@ yyreduce:
g_lingo->code2(e, f); ;}
break;
- case 61:
-#line 369 "engines/director/lingo/lingo-gr.y"
+ case 62:
+#line 378 "engines/director/lingo/lingo-gr.y"
{
(yyval.code) = g_lingo->code1(g_lingo->c_theentitypush);
inst e = 0, f = 0;
@@ -2220,237 +2237,237 @@ yyreduce:
g_lingo->code2(e, f); ;}
break;
- case 63:
-#line 376 "engines/director/lingo/lingo-gr.y"
+ case 64:
+#line 385 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(g_lingo->c_add); ;}
break;
- case 64:
-#line 377 "engines/director/lingo/lingo-gr.y"
+ case 65:
+#line 386 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(g_lingo->c_sub); ;}
break;
- case 65:
-#line 378 "engines/director/lingo/lingo-gr.y"
+ case 66:
+#line 387 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(g_lingo->c_mul); ;}
break;
- case 66:
-#line 379 "engines/director/lingo/lingo-gr.y"
+ case 67:
+#line 388 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(g_lingo->c_div); ;}
break;
- case 67:
-#line 380 "engines/director/lingo/lingo-gr.y"
+ case 68:
+#line 389 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(g_lingo->c_gt); ;}
break;
- case 68:
-#line 381 "engines/director/lingo/lingo-gr.y"
+ case 69:
+#line 390 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(g_lingo->c_lt); ;}
break;
- case 69:
-#line 382 "engines/director/lingo/lingo-gr.y"
+ case 70:
+#line 391 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(g_lingo->c_neq); ;}
break;
- case 70:
-#line 383 "engines/director/lingo/lingo-gr.y"
+ case 71:
+#line 392 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(g_lingo->c_ge); ;}
break;
- case 71:
-#line 384 "engines/director/lingo/lingo-gr.y"
+ case 72:
+#line 393 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(g_lingo->c_le); ;}
break;
- case 72:
-#line 385 "engines/director/lingo/lingo-gr.y"
+ case 73:
+#line 394 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(g_lingo->c_and); ;}
break;
- case 73:
-#line 386 "engines/director/lingo/lingo-gr.y"
+ case 74:
+#line 395 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(g_lingo->c_or); ;}
break;
- case 74:
-#line 387 "engines/director/lingo/lingo-gr.y"
+ case 75:
+#line 396 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(g_lingo->c_not); ;}
break;
- case 75:
-#line 388 "engines/director/lingo/lingo-gr.y"
+ case 76:
+#line 397 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(g_lingo->c_ampersand); ;}
break;
- case 76:
-#line 389 "engines/director/lingo/lingo-gr.y"
+ case 77:
+#line 398 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(g_lingo->c_concat); ;}
break;
- case 77:
-#line 390 "engines/director/lingo/lingo-gr.y"
+ case 78:
+#line 399 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(g_lingo->c_contains); ;}
break;
- case 78:
-#line 391 "engines/director/lingo/lingo-gr.y"
+ case 79:
+#line 400 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(g_lingo->c_starts); ;}
break;
- case 79:
-#line 392 "engines/director/lingo/lingo-gr.y"
+ case 80:
+#line 401 "engines/director/lingo/lingo-gr.y"
{ (yyval.code) = (yyvsp[(2) - (2)].code); ;}
break;
- case 80:
-#line 393 "engines/director/lingo/lingo-gr.y"
+ case 81:
+#line 402 "engines/director/lingo/lingo-gr.y"
{ (yyval.code) = (yyvsp[(2) - (2)].code); g_lingo->code1(g_lingo->c_negate); ;}
break;
- case 81:
-#line 394 "engines/director/lingo/lingo-gr.y"
+ case 82:
+#line 403 "engines/director/lingo/lingo-gr.y"
{ (yyval.code) = (yyvsp[(2) - (3)].code); ;}
break;
- case 82:
-#line 395 "engines/director/lingo/lingo-gr.y"
+ case 83:
+#line 404 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(g_lingo->c_intersects); ;}
break;
- case 83:
-#line 396 "engines/director/lingo/lingo-gr.y"
+ case 84:
+#line 405 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(g_lingo->c_within); ;}
break;
- case 84:
-#line 399 "engines/director/lingo/lingo-gr.y"
+ case 85:
+#line 408 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(g_lingo->c_printtop); ;}
break;
- case 87:
-#line 402 "engines/director/lingo/lingo-gr.y"
+ case 88:
+#line 411 "engines/director/lingo/lingo-gr.y"
{ g_lingo->codeConst(0); // Push fake value on stack
g_lingo->code1(g_lingo->c_procret); ;}
break;
- case 89:
-#line 405 "engines/director/lingo/lingo-gr.y"
+ case 90:
+#line 414 "engines/director/lingo/lingo-gr.y"
{
g_lingo->code1(g_lingo->_handlers[*(yyvsp[(1) - (2)].s)]->u.func);
delete (yyvsp[(1) - (2)].s); ;}
break;
- case 90:
-#line 408 "engines/director/lingo/lingo-gr.y"
+ case 91:
+#line 417 "engines/director/lingo/lingo-gr.y"
{
g_lingo->code1(g_lingo->_handlers[*(yyvsp[(1) - (2)].s)]->u.func);
delete (yyvsp[(1) - (2)].s); ;}
break;
- case 91:
-#line 411 "engines/director/lingo/lingo-gr.y"
+ case 92:
+#line 420 "engines/director/lingo/lingo-gr.y"
{
g_lingo->code2(g_lingo->c_voidpush, g_lingo->_handlers[*(yyvsp[(1) - (1)].s)]->u.func);
delete (yyvsp[(1) - (1)].s); ;}
break;
- case 92:
-#line 414 "engines/director/lingo/lingo-gr.y"
+ case 93:
+#line 423 "engines/director/lingo/lingo-gr.y"
{ g_lingo->codeFunc((yyvsp[(1) - (2)].s), (yyvsp[(2) - (2)].narg)); ;}
break;
- case 93:
-#line 415 "engines/director/lingo/lingo-gr.y"
+ case 94:
+#line 424 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(g_lingo->c_open); ;}
break;
- case 94:
-#line 416 "engines/director/lingo/lingo-gr.y"
+ case 95:
+#line 425 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code2(g_lingo->c_voidpush, g_lingo->c_open); ;}
break;
- case 95:
-#line 419 "engines/director/lingo/lingo-gr.y"
+ case 96:
+#line 428 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(g_lingo->c_global); g_lingo->codeString((yyvsp[(1) - (1)].s)->c_str()); delete (yyvsp[(1) - (1)].s); ;}
break;
- case 96:
-#line 420 "engines/director/lingo/lingo-gr.y"
+ case 97:
+#line 429 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(g_lingo->c_global); g_lingo->codeString((yyvsp[(3) - (3)].s)->c_str()); delete (yyvsp[(3) - (3)].s); ;}
break;
- case 97:
-#line 431 "engines/director/lingo/lingo-gr.y"
+ case 98:
+#line 440 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(g_lingo->c_gotoloop); ;}
break;
- case 98:
-#line 432 "engines/director/lingo/lingo-gr.y"
+ case 99:
+#line 441 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(g_lingo->c_gotonext); ;}
break;
- case 99:
-#line 433 "engines/director/lingo/lingo-gr.y"
+ case 100:
+#line 442 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(g_lingo->c_gotoprevious); ;}
break;
- case 100:
-#line 434 "engines/director/lingo/lingo-gr.y"
+ case 101:
+#line 443 "engines/director/lingo/lingo-gr.y"
{
g_lingo->codeConst(1);
g_lingo->code1(g_lingo->c_goto); ;}
break;
- case 101:
-#line 437 "engines/director/lingo/lingo-gr.y"
+ case 102:
+#line 446 "engines/director/lingo/lingo-gr.y"
{
g_lingo->codeConst(3);
g_lingo->code1(g_lingo->c_goto); ;}
break;
- case 102:
-#line 440 "engines/director/lingo/lingo-gr.y"
+ case 103:
+#line 449 "engines/director/lingo/lingo-gr.y"
{
g_lingo->codeConst(2);
g_lingo->code1(g_lingo->c_goto); ;}
break;
- case 107:
-#line 453 "engines/director/lingo/lingo-gr.y"
+ case 108:
+#line 462 "engines/director/lingo/lingo-gr.y"
{ g_lingo->code1(g_lingo->c_playdone); ;}
break;
- case 108:
-#line 454 "engines/director/lingo/lingo-gr.y"
+ case 109:
+#line 463 "engines/director/lingo/lingo-gr.y"
{
g_lingo->codeConst(1);
g_lingo->code1(g_lingo->c_play); ;}
break;
- case 109:
-#line 457 "engines/director/lingo/lingo-gr.y"
+ case 110:
+#line 466 "engines/director/lingo/lingo-gr.y"
{
g_lingo->codeConst(3);
g_lingo->code1(g_lingo->c_play); ;}
break;
- case 110:
-#line 460 "engines/director/lingo/lingo-gr.y"
+ case 111:
+#line 469 "engines/director/lingo/lingo-gr.y"
{
g_lingo->codeConst(2);
g_lingo->code1(g_lingo->c_play); ;}
break;
- case 111:
-#line 490 "engines/director/lingo/lingo-gr.y"
+ case 112:
+#line 499 "engines/director/lingo/lingo-gr.y"
{ g_lingo->_indef = true; g_lingo->_currentFactory.clear(); ;}
break;
- case 112:
-#line 491 "engines/director/lingo/lingo-gr.y"
+ case 113:
+#line 500 "engines/director/lingo/lingo-gr.y"
{
g_lingo->codeConst(0); // Push fake value on stack
g_lingo->code1(g_lingo->c_procret);
@@ -2458,53 +2475,53 @@ yyreduce:
g_lingo->_indef = false; ;}
break;
- case 113:
-#line 496 "engines/director/lingo/lingo-gr.y"
+ case 114:
+#line 505 "engines/director/lingo/lingo-gr.y"
{
g_lingo->codeFactory(*(yyvsp[(2) - (2)].s));
;}
break;
- case 114:
-#line 499 "engines/director/lingo/lingo-gr.y"
+ case 115:
+#line 508 "engines/director/lingo/lingo-gr.y"
{ g_lingo->_indef = true; ;}
break;
- case 115:
-#line 500 "engines/director/lingo/lingo-gr.y"
+ case 116:
+#line 509 "engines/director/lingo/lingo-gr.y"
{
- g_lingo->code1(STOP);
+ g_lingo->code1(g_lingo->c_procret);
g_lingo->define(*(yyvsp[(2) - (8)].s), (yyvsp[(4) - (8)].code), (yyvsp[(5) - (8)].narg) + 1, &g_lingo->_currentFactory);
g_lingo->_indef = false; ;}
break;
- case 116:
-#line 504 "engines/director/lingo/lingo-gr.y"
+ case 117:
+#line 513 "engines/director/lingo/lingo-gr.y"
{ (yyval.narg) = 0; ;}
break;
- case 117:
-#line 505 "engines/director/lingo/lingo-gr.y"
+ case 118:
+#line 514 "engines/director/lingo/lingo-gr.y"
{ g_lingo->codeArg((yyvsp[(1) - (1)].s)); (yyval.narg) = 1; ;}
break;
- case 118:
-#line 506 "engines/director/lingo/lingo-gr.y"
+ case 119:
+#line 515 "engines/director/lingo/lingo-gr.y"
{ g_lingo->codeArg((yyvsp[(3) - (3)].s)); (yyval.narg) = (yyvsp[(1) - (3)].narg) + 1; ;}
break;
- case 119:
-#line 507 "engines/director/lingo/lingo-gr.y"
+ case 120:
+#line 516 "engines/director/lingo/lingo-gr.y"
{ g_lingo->codeArg((yyvsp[(4) - (4)].s)); (yyval.narg) = (yyvsp[(1) - (4)].narg) + 1; ;}
break;
- case 120:
-#line 509 "engines/director/lingo/lingo-gr.y"
+ case 121:
+#line 518 "engines/director/lingo/lingo-gr.y"
{ g_lingo->codeArgStore(); ;}
break;
- case 121:
-#line 513 "engines/director/lingo/lingo-gr.y"
+ case 122:
+#line 522 "engines/director/lingo/lingo-gr.y"
{
g_lingo->code1(g_lingo->c_call);
g_lingo->codeString((yyvsp[(1) - (3)].s)->c_str());
@@ -2513,24 +2530,24 @@ yyreduce:
g_lingo->code1(numpar); ;}
break;
- case 122:
-#line 521 "engines/director/lingo/lingo-gr.y"
+ case 123:
+#line 530 "engines/director/lingo/lingo-gr.y"
{ (yyval.narg) = 0; ;}
break;
- case 123:
-#line 522 "engines/director/lingo/lingo-gr.y"
+ case 124:
+#line 531 "engines/director/lingo/lingo-gr.y"
{ (yyval.narg) = 1; ;}
break;
- case 124:
-#line 523 "engines/director/lingo/lingo-gr.y"
+ case 125:
+#line 532 "engines/director/lingo/lingo-gr.y"
{ (yyval.narg) = (yyvsp[(1) - (3)].narg) + 1; ;}
break;
/* Line 1267 of yacc.c. */
-#line 2534 "engines/director/lingo/lingo-gr.cpp"
+#line 2551 "engines/director/lingo/lingo-gr.cpp"
default: break;
}
YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
@@ -2744,6 +2761,6 @@ yyreturn:
}
-#line 526 "engines/director/lingo/lingo-gr.y"
+#line 535 "engines/director/lingo/lingo-gr.y"
diff --git a/engines/director/lingo/lingo-gr.y b/engines/director/lingo/lingo-gr.y
index ea66bc6fd9..f74244e5fd 100644
--- a/engines/director/lingo/lingo-gr.y
+++ b/engines/director/lingo/lingo-gr.y
@@ -90,7 +90,7 @@ void yyerror(char *s) {
%token tCONCAT tCONTAINS tSTARTS
%token tSPRITE tINTERSECTS tWITHIN
-%type<code> asgn begin elseif elsestmtoneliner end expr if repeatwhile repeatwith stmtlist
+%type<code> asgn begin elseif elsestmtoneliner end expr if when repeatwhile repeatwith stmtlist
%type<narg> argdef arglist
%right '='
@@ -217,8 +217,11 @@ stmt: stmtoneliner
(*g_lingo->_currentScript)[$1 + 3] = body; /* body of loop */
(*g_lingo->_currentScript)[$1 + 4] = inc; /* increment */
(*g_lingo->_currentScript)[$1 + 5] = end; } /* end, if cond fails */
- | tWHEN ID tTHEN expr {
- g_lingo->code1(g_lingo->c_ifcode);
+ | when expr end {
+ inst end = 0;
+ WRITE_UINT32(&end, $3);
+ g_lingo->code1(STOP);
+ (*g_lingo->_currentScript)[$1 + 1] = end;
}
;
@@ -341,6 +344,12 @@ stmtlist: /* nothing */ { $$ = g_lingo->_currentScript->size(); }
| stmtlist stmt
;
+when: tWHEN ID tTHEN {
+ $$ = g_lingo->code1(g_lingo->c_whencode);
+ g_lingo->code1(STOP);
+ g_lingo->codeString($2->c_str());
+ delete $2; }
+
expr: INT { $$ = g_lingo->codeConst($1); }
| FLOAT {
$$ = g_lingo->code1(g_lingo->c_fconstpush);
@@ -498,7 +507,7 @@ defn: tMACRO ID { g_lingo->_indef = true; g_lingo->_currentFactory.clear(); }
}
| tMETHOD ID { g_lingo->_indef = true; }
begin argdef nl argstore stmtlist {
- g_lingo->code1(STOP);
+ g_lingo->code1(g_lingo->c_procret);
g_lingo->define(*$2, $4, $5 + 1, &g_lingo->_currentFactory);
g_lingo->_indef = false; } ;
argdef: /* nothing */ { $$ = 0; }
diff --git a/engines/director/lingo/lingo.cpp b/engines/director/lingo/lingo.cpp
index 9aea9593e9..d37da42670 100644
--- a/engines/director/lingo/lingo.cpp
+++ b/engines/director/lingo/lingo.cpp
@@ -121,17 +121,17 @@ const char *Lingo::findNextDefinition(const char *s) {
return NULL;
if (!strncmp(res, "macro ", 6)) {
- warning("See macro");
+ debugC(3, kDebugLingoCompile, "See macro");
return res;
}
if (!strncmp(res, "factory ", 8)) {
- warning("See factory");
+ debugC(3, kDebugLingoCompile, "See factory");
return res;
}
if (!strncmp(res, "method ", 7)) {
- warning("See method");
+ debugC(3, kDebugLingoCompile, "See method");
return res;
}
@@ -181,6 +181,14 @@ void Lingo::addCode(const char *code, ScriptType type, uint16 id) {
parse(chunk.c_str());
+ if (debugChannelSet(3, kDebugLingoCompile)) {
+ int pc = 0;
+ while (pc < _currentScript->size()) {
+ Common::String instr = decodeInstruction(pc, &pc);
+ debugC(3, kDebugLingoCompile, "[%5d] %s", pc, instr.c_str());
+ }
+ }
+
_currentScript->clear();
begin = end;
@@ -198,8 +206,16 @@ void Lingo::addCode(const char *code, ScriptType type, uint16 id) {
_inFactory = false;
- if (_currentScript->size() && !_hadError)
- Common::hexdump((byte *)&_currentScript->front(), _currentScript->size() * sizeof(inst));
+ if (debugChannelSet(3, kDebugLingoCompile)) {
+ if (_currentScript->size() && !_hadError)
+ Common::hexdump((byte *)&_currentScript->front(), _currentScript->size() * sizeof(inst));
+
+ int pc = 0;
+ while (pc < _currentScript->size()) {
+ Common::String instr = decodeInstruction(pc, &pc);
+ debugC(3, kDebugLingoCompile, "[%5d] %s", pc, instr.c_str());
+ }
+ }
}
void Lingo::executeScript(ScriptType type, uint16 id) {
@@ -225,7 +241,7 @@ void Lingo::processEvent(LEvent event, int entityId) {
if (!_eventHandlerTypes.contains(event))
error("processEvent: Unknown event %d for entity %d", event, entityId);
- debug(2, "processEvent(%s) for %d", _eventHandlerTypes[event], entityId);
+ debug(2, "STUB: processEvent(%s) for %d", _eventHandlerTypes[event], entityId);
}
int Lingo::alignTypes(Datum &d1, Datum &d2) {
@@ -285,6 +301,15 @@ Common::String *Datum::toString() {
delete s;
s = u.s;
break;
+ case OBJECT:
+ *s = Common::String::format("#%s", u.s->c_str());
+ break;
+ case VOID:
+ *s = "#void";
+ break;
+ case VAR:
+ *s = Common::String::format("var: #%s", u.sym->name);
+ break;
default:
warning("Incorrect operation toString() for type: %s", type2str());
}
@@ -313,6 +338,10 @@ const char *Datum::type2str(bool isk) {
return isk ? "#point" : "POINT";
case SYMBOL:
return isk ? "#symbol" : "SYMBOL";
+ case OBJECT:
+ return isk ? "#object" : "OBJECT";
+ case VAR:
+ return isk ? "#var" : "VAR";
default:
snprintf(res, 20, "-- (%d) --", type);
return res;
@@ -374,7 +403,7 @@ void Lingo::runTests() {
Common::sort(fileList.begin(), fileList.end());
- for (int i = 0; i < fileList.size(); i++) {
+ for (uint i = 0; i < fileList.size(); i++) {
Common::SeekableReadStream *const stream = SearchMan.createReadStreamForMember(fileList[i]);
if (stream) {
uint size = stream->size();
@@ -383,7 +412,7 @@ void Lingo::runTests() {
stream->read(script, size);
- warning("Compiling file %s of size %d, id: %d", fileList[i].c_str(), size, counter);
+ debugC(2, kDebugLingoCompile, "Compiling file %s of size %d, id: %d", fileList[i].c_str(), size, counter);
_hadError = false;
addCode(script, kMovieScript, counter);
@@ -391,7 +420,7 @@ void Lingo::runTests() {
if (!_hadError)
executeScript(kMovieScript, counter);
else
- warning("Skipping execution");
+ debugC(2, kDebugLingoCompile, "Skipping execution");
free(script);
diff --git a/engines/director/lingo/lingo.h b/engines/director/lingo/lingo.h
index 254030e9b7..71708cf910 100644
--- a/engines/director/lingo/lingo.h
+++ b/engines/director/lingo/lingo.h
@@ -180,7 +180,7 @@ public:
void addCode(const char *code, ScriptType type, uint16 id);
void executeScript(ScriptType type, uint16 id);
- Common::String decodeInstruction(int pc);
+ Common::String decodeInstruction(int pc, int *newPC = NULL);
void processEvent(LEvent event, int entityId);
@@ -267,6 +267,7 @@ public:
static void c_repeatwhilecode();
static void c_repeatwithcode();
static void c_ifcode();
+ static void c_whencode();
static void c_eq();
static void c_neq();
static void c_gt();
diff --git a/engines/fullpipe/gfx.cpp b/engines/fullpipe/gfx.cpp
index dd8d8b246d..b357bbd587 100644
--- a/engines/fullpipe/gfx.cpp
+++ b/engines/fullpipe/gfx.cpp
@@ -1122,18 +1122,24 @@ void Bitmap::copier(uint32 *dest, byte *src, int len, int32 *palette, bool cb05_
}
Bitmap *Bitmap::reverseImage(bool flip) {
- if (flip)
- _flipping = Graphics::FLIP_H;
- else
- _flipping = Graphics::FLIP_NONE;
+ Bitmap *b = new Bitmap(this);
+
+ if (flip) {
+ if (b->_flipping == Graphics::FLIP_NONE)
+ b->_flipping = Graphics::FLIP_H;
+ else
+ b->_flipping = Graphics::FLIP_NONE;
+ }
- return this;
+ return b;
}
Bitmap *Bitmap::flipVertical() {
- _flipping = Graphics::FLIP_V;
+ Bitmap *b = new Bitmap(this);
+
+ b->_flipping = Graphics::FLIP_V;
- return this;
+ return b;
}
void Bitmap::drawShaded(int type, int x, int y, byte *palette, int alpha) {
diff --git a/engines/sci/console.cpp b/engines/sci/console.cpp
index 3aaf13efdb..b20ed3f8be 100644
--- a/engines/sci/console.cpp
+++ b/engines/sci/console.cpp
@@ -54,7 +54,6 @@
#include "sci/graphics/frameout.h"
#include "sci/graphics/paint32.h"
#include "video/coktel_decoder.h"
-#include "sci/video/robot_decoder.h"
#endif
#include "common/file.h"
@@ -266,8 +265,6 @@ void Console::postEnter() {
#ifdef ENABLE_SCI32
} else if (_videoFile.hasSuffix(".vmd")) {
videoDecoder = new Video::AdvancedVMDDecoder();
- } else if (_videoFile.hasSuffix(".rbt")) {
- videoDecoder = new RobotDecoder(_engine->getPlatform() == Common::kPlatformMacintosh);
} else if (_videoFile.hasSuffix(".duk")) {
duckMode = true;
videoDecoder = new Video::AVIDecoder();
diff --git a/engines/sci/detection.cpp b/engines/sci/detection.cpp
index f5797dc106..ad2b0f31a5 100644
--- a/engines/sci/detection.cpp
+++ b/engines/sci/detection.cpp
@@ -565,8 +565,8 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const FileMap &allFiles,
// the file should be over 10MB, as it contains all the game speech and is usually
// around 450MB+. The size check is for some floppy game versions like KQ6 floppy, which
// also have a small resource.aud file
- if (allFiles.contains("resource.aud") || allFiles.contains("audio001.002")) {
- Common::FSNode file = allFiles.contains("resource.aud") ? allFiles["resource.aud"] : allFiles["audio001.002"];
+ if (allFiles.contains("resource.aud") || allFiles.contains("resaud.001") || allFiles.contains("audio001.002")) {
+ Common::FSNode file = allFiles.contains("resource.aud") ? allFiles["resource.aud"] : (allFiles.contains("resaud.001") ? allFiles["resaud.001"] : allFiles["audio001.002"]);
Common::SeekableReadStream *tmpStream = file.createReadStream();
if (tmpStream->size() > 10 * 1024 * 1024) {
// We got a CD version, so set the CD flag accordingly
diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h
index b02a7e545a..90f582c291 100644
--- a/engines/sci/engine/kernel.h
+++ b/engines/sci/engine/kernel.h
@@ -421,6 +421,14 @@ reg_t kStubNull(EngineState *s, int argc, reg_t *argv);
#ifdef ENABLE_SCI32
// SCI2 Kernel Functions
+reg_t kSetCursor32(EngineState *s, int argc, reg_t *argv);
+reg_t kSetNowSeen32(EngineState *s, int argc, reg_t *argv);
+reg_t kBaseSetter32(EngineState *s, int argc, reg_t *argv);
+reg_t kShakeScreen32(EngineState *s, int argc, reg_t *argv);
+reg_t kPlatform32(EngineState *s, int argc, reg_t *argv);
+reg_t kGlobalToLocal32(EngineState *s, int argc, reg_t *argv);
+reg_t kLocalToGlobal32(EngineState *s, int argc, reg_t *argv);
+
reg_t kDoAudio32(EngineState *s, int argc, reg_t *argv);
reg_t kDoAudioInit(EngineState *s, int argc, reg_t *argv);
reg_t kDoAudioWaitForPlay(EngineState *s, int argc, reg_t *argv);
@@ -441,10 +449,24 @@ reg_t kDoAudioFade(EngineState *s, int argc, reg_t *argv);
reg_t kDoAudioHasSignal(EngineState *s, int argc, reg_t *argv);
reg_t kDoAudioSetLoop(EngineState *s, int argc, reg_t *argv);
+reg_t kRobot(EngineState *s, int argc, reg_t *argv);
+reg_t kRobotOpen(EngineState *s, int argc, reg_t *argv);
+reg_t kRobotShowFrame(EngineState *s, int argc, reg_t *argv);
+reg_t kRobotGetFrameSize(EngineState *s, int argc, reg_t *argv);
+reg_t kRobotPlay(EngineState *s, int argc, reg_t *argv);
+reg_t kRobotGetIsFinished(EngineState *s, int argc, reg_t *argv);
+reg_t kRobotGetIsPlaying(EngineState *s, int argc, reg_t *argv);
+reg_t kRobotClose(EngineState *s, int argc, reg_t *argv);
+reg_t kRobotGetCue(EngineState *s, int argc, reg_t *argv);
+reg_t kRobotPause(EngineState *s, int argc, reg_t *argv);
+reg_t kRobotGetFrameNo(EngineState *s, int argc, reg_t *argv);
+reg_t kRobotSetPriority(EngineState *s, int argc, reg_t *argv);
+
reg_t kPlayVMD(EngineState *s, int argc, reg_t *argv);
reg_t kPlayVMDOpen(EngineState *s, int argc, reg_t *argv);
reg_t kPlayVMDInit(EngineState *s, int argc, reg_t *argv);
reg_t kPlayVMDClose(EngineState *s, int argc, reg_t *argv);
+reg_t kPlayVMDGetStatus(EngineState *s, int argc, reg_t *argv);
reg_t kPlayVMDPlayUntilEvent(EngineState *s, int argc, reg_t *argv);
reg_t kPlayVMDShowCursor(EngineState *s, int argc, reg_t *argv);
reg_t kPlayVMDSetBlackoutArea(EngineState *s, int argc, reg_t *argv);
@@ -587,9 +609,9 @@ reg_t kTextWidth(EngineState *s, int argc, reg_t *argv);
reg_t kSave(EngineState *s, int argc, reg_t *argv);
reg_t kAutoSave(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 kCD(EngineState *s, int argc, reg_t *argv);
+reg_t kCheckCD(EngineState *s, int argc, reg_t *argv);
+reg_t kGetSavedCD(EngineState *s, int argc, reg_t *argv);
reg_t kAddPicAt(EngineState *s, int argc, reg_t *argv);
reg_t kAddBefore(EngineState *s, int argc, reg_t *argv);
reg_t kMoveToFront(EngineState *s, int argc, reg_t *argv);
diff --git a/engines/sci/engine/kernel_tables.h b/engines/sci/engine/kernel_tables.h
index 76c24b09e3..68611b0dee 100644
--- a/engines/sci/engine/kernel_tables.h
+++ b/engines/sci/engine/kernel_tables.h
@@ -396,6 +396,13 @@ static const SciKernelMapSubEntry kBitmap_subops[] = {
};
// version, subId, function-mapping, signature, workarounds
+static const SciKernelMapSubEntry kCD_subops[] = {
+ { SIG_SINCE_SCI21MID, 0, MAP_CALL(CheckCD), "(i)", NULL },
+ { SIG_SINCE_SCI21MID, 1, MAP_CALL(GetSavedCD), "", NULL },
+ SCI_SUBOPENTRY_TERMINATOR
+};
+
+// version, subId, function-mapping, signature, workarounds
static const SciKernelMapSubEntry kList_subops[] = {
{ SIG_SINCE_SCI21, 0, MAP_CALL(NewList), "", NULL },
{ SIG_SINCE_SCI21, 1, MAP_CALL(DisposeList), "l", NULL },
@@ -406,8 +413,8 @@ static const SciKernelMapSubEntry kList_subops[] = {
{ SIG_SINCE_SCI21, 6, MAP_CALL(NextNode), "n", NULL },
{ SIG_SINCE_SCI21, 7, MAP_CALL(PrevNode), "n", NULL },
{ SIG_SINCE_SCI21, 8, MAP_CALL(NodeValue), "[n0]", NULL },
- { SIG_SINCE_SCI21, 9, MAP_CALL(AddAfter), "lnn.", NULL },
- { SIG_SINCE_SCI21, 10, MAP_CALL(AddToFront), "ln.", NULL },
+ { SIG_SINCE_SCI21, 9, MAP_CALL(AddAfter), "lnn(.)", NULL },
+ { SIG_SINCE_SCI21, 10, MAP_CALL(AddToFront), "ln(.)", NULL },
{ SIG_SINCE_SCI21, 11, MAP_CALL(AddToEnd), "ln(.)", NULL },
{ SIG_SINCE_SCI21, 12, MAP_CALL(AddBefore), "ln.", NULL },
{ SIG_SINCE_SCI21, 13, MAP_CALL(MoveToFront), "ln", NULL },
@@ -451,6 +458,7 @@ static const SciKernelMapSubEntry kPlayVMD_subops[] = {
{ SIG_SINCE_SCI21, 0, MAP_CALL(PlayVMDOpen), "r(i)(i)", NULL },
{ SIG_SINCE_SCI21, 1, MAP_CALL(PlayVMDInit), "ii(i)(i)(ii)", NULL },
{ SIG_SINCE_SCI21, 6, MAP_CALL(PlayVMDClose), "", NULL },
+ { SIG_SINCE_SCI21, 10, MAP_CALL(PlayVMDGetStatus), "", NULL },
{ SIG_SINCE_SCI21, 14, MAP_CALL(PlayVMDPlayUntilEvent), "i(i)(i)", NULL },
{ SIG_SINCE_SCI21, 16, MAP_CALL(PlayVMDShowCursor), "i", NULL },
{ SIG_SINCE_SCI21, 17, MAP_DUMMY(PlayVMDStartBlob), "", NULL },
@@ -461,6 +469,22 @@ static const SciKernelMapSubEntry kPlayVMD_subops[] = {
};
// version, subId, function-mapping, signature, workarounds
+static const SciKernelMapSubEntry kRobot_subops[] = {
+ { SIG_SINCE_SCI21, 0, MAP_CALL(RobotOpen), "ioiii(i)", NULL },
+ { SIG_SINCE_SCI21, 1, MAP_CALL(RobotShowFrame), "i(ii)", NULL },
+ { SIG_SINCE_SCI21, 2, MAP_CALL(RobotGetFrameSize), "r", NULL },
+ { SIG_SINCE_SCI21, 4, MAP_CALL(RobotPlay), "", NULL },
+ { SIG_SINCE_SCI21, 5, MAP_CALL(RobotGetIsFinished), "", NULL },
+ { SIG_SINCE_SCI21, 6, MAP_CALL(RobotGetIsPlaying), "", NULL },
+ { SIG_SINCE_SCI21, 7, MAP_CALL(RobotClose), "", NULL },
+ { SIG_SINCE_SCI21, 8, MAP_CALL(RobotGetCue), "o", NULL },
+ { SIG_SINCE_SCI21, 10, MAP_CALL(RobotPause), "", NULL },
+ { SIG_SINCE_SCI21, 11, MAP_CALL(RobotGetFrameNo), "", NULL },
+ { SIG_SINCE_SCI21, 12, MAP_CALL(RobotSetPriority), "i", NULL },
+ SCI_SUBOPENTRY_TERMINATOR
+};
+
+// version, subId, function-mapping, signature, workarounds
static const SciKernelMapSubEntry kRemapColors_subops[] = {
{ SIG_SCI32, 0, MAP_CALL(RemapColorsOff), "(i)", NULL },
{ SIG_SCI32, 1, MAP_CALL(RemapColorsByRange), "iiii(i)", NULL },
@@ -569,7 +593,10 @@ static SciKernelMapEntry s_kernelMap[] = {
{ 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(BaseSetter), SIG_SCI16, SIGFOR_ALL, "o", NULL, NULL },
+#ifdef ENABLE_SCI32
+ { "BaseSetter", kBaseSetter32, SIG_SCI32, SIGFOR_ALL, "o", NULL, NULL },
+#endif
{ MAP_CALL(CanBeHere), SIG_EVERYWHERE, "o(l)", NULL, NULL },
{ MAP_CALL(CantBeHere), SIG_SCI16, SIGFOR_ALL, "o(l)", NULL, NULL },
#ifdef ENABLE_SCI32
@@ -638,8 +665,10 @@ static SciKernelMapEntry s_kernelMap[] = {
{ 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(GlobalToLocal), SIG_SCI16, SIGFOR_ALL, "o", NULL, NULL },
+#ifdef ENABLE_SCI32
+ { "GlobalToLocal", kGlobalToLocal32, SIG_SCI32, SIGFOR_ALL, "oo", NULL, NULL },
+#endif
{ MAP_CALL(Graph), SIG_EVERYWHERE, NULL, kGraph_subops, NULL },
{ MAP_CALL(HaveMouse), SIG_EVERYWHERE, "", NULL, NULL },
{ MAP_CALL(HiliteControl), SIG_EVERYWHERE, "o", NULL, NULL },
@@ -650,8 +679,10 @@ static SciKernelMapEntry s_kernelMap[] = {
{ 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(LocalToGlobal), SIG_SCI16, SIGFOR_ALL, "o", NULL, NULL },
+#ifdef ENABLE_SCI32
+ { "LocalToGlobal", kLocalToGlobal32, SIG_SCI32, SIGFOR_ALL, "oo", NULL, NULL },
+#endif
{ MAP_CALL(Lock), SIG_EVERYWHERE, "ii(i)", NULL, NULL },
{ MAP_CALL(MapKeyToDir), SIG_EVERYWHERE, "o", NULL, NULL },
{ MAP_CALL(Memory), SIG_EVERYWHERE, "i(.*)", NULL, kMemory_workarounds }, // subop
@@ -676,7 +707,10 @@ static SciKernelMapEntry s_kernelMap[] = {
{ 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(Platform), SIG_SCI16, SIGFOR_ALL, "(.*)", NULL, NULL },
+#ifdef ENABLE_SCI32
+ { "Platform", kPlatform32, SIG_SCI32, SIGFOR_ALL, "(i)", NULL, NULL },
+#endif
{ 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 },
@@ -693,22 +727,26 @@ static SciKernelMapEntry s_kernelMap[] = {
{ MAP_CALL(Said), SIG_EVERYWHERE, "[r0]", NULL, NULL },
{ MAP_CALL(SaveGame), SIG_EVERYWHERE, "[r0]i[r0](r0)", NULL, NULL },
{ MAP_CALL(ScriptID), SIG_EVERYWHERE, "[io](i)", NULL, NULL },
- { MAP_CALL(SetCursor), SIG_SINCE_SCI21, SIGFOR_ALL, "i(i)([io])(i*)", NULL, NULL },
- // TODO: SCI2.1 may supply an object optionally (mother goose sci21 right on startup) - find out why
{ MAP_CALL(SetCursor), SIG_SCI11, SIGFOR_ALL, "i(i)(i)(i)(iiiiii)", NULL, NULL },
- { MAP_CALL(SetCursor), SIG_EVERYWHERE, "i(i)(i)(i)(i)", NULL, kSetCursor_workarounds },
+ { MAP_CALL(SetCursor), SIG_SCI16, SIGFOR_ALL, "i(i)(i)(i)(i)", NULL, kSetCursor_workarounds },
+#ifdef ENABLE_SCI32
+ { "SetCursor", kSetCursor32, SIG_SCI32, SIGFOR_ALL, "i(i)(i)(i)", NULL, kSetCursor_workarounds },
+#endif
{ 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_SCI16, SIGFOR_ALL, "o(i)", NULL, NULL },
#ifdef ENABLE_SCI32
- { MAP_CALL(SetNowSeen), SIG_SCI32, SIGFOR_ALL, "o", NULL, NULL },
+ { "SetNowSeen", kSetNowSeen32, SIG_SCI32, SIGFOR_ALL, "o", NULL, NULL },
#endif
{ MAP_CALL(SetPort), SIG_EVERYWHERE, "i(iiiii)(i)", NULL, kSetPort_workarounds },
{ 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(ShakeScreen), SIG_SCI16, SIGFOR_ALL, "(i)(i)", NULL, NULL },
+#ifdef ENABLE_SCI32
+ { "ShakeScreen", kShakeScreen32, SIG_SCI32, SIGFOR_ALL, "i(i)", NULL, NULL },
+#endif
{ MAP_CALL(ShowMovie), SIG_SCI16, SIGFOR_ALL, "(.*)", NULL, NULL },
#ifdef ENABLE_SCI32
{ "ShowMovie", kShowMovie32, SIG_SCI32, SIGFOR_DOS, "ri(i)(i)", NULL, NULL },
@@ -840,12 +878,12 @@ static SciKernelMapEntry s_kernelMap[] = {
{ MAP_DUMMY(PointSize), SIG_EVERYWHERE, "(.*)", NULL, NULL },
// SCI2.1 Kernel Functions
- { MAP_CALL(CD), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(CD), SIG_SINCE_SCI21MID, SIGFOR_ALL, "(.*)", kCD_subops, NULL },
{ MAP_CALL(IsOnMe), SIG_EVERYWHERE, "iioi", NULL, NULL },
{ MAP_CALL(List), SIG_SINCE_SCI21, SIGFOR_ALL, "(.*)", kList_subops, NULL },
{ MAP_CALL(MulDiv), SIG_EVERYWHERE, "iii", NULL, NULL },
{ MAP_CALL(PlayVMD), SIG_EVERYWHERE, "(.*)", kPlayVMD_subops, NULL },
- { MAP_EMPTY(Robot), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(Robot), SIG_EVERYWHERE, "(.*)", kRobot_subops, NULL },
{ MAP_CALL(Save), SIG_EVERYWHERE, "i(.*)", kSave_subops, NULL },
{ MAP_CALL(Text), SIG_SINCE_SCI21MID, SIGFOR_ALL, "i(.*)", kText_subops, NULL },
{ MAP_CALL(AddPicAt), SIG_EVERYWHERE, "oiii(i)(i)", NULL, NULL },
diff --git a/engines/sci/engine/kevent.cpp b/engines/sci/engine/kevent.cpp
index d7a716a504..9250e0fc13 100644
--- a/engines/sci/engine/kevent.cpp
+++ b/engines/sci/engine/kevent.cpp
@@ -34,6 +34,9 @@
#include "sci/graphics/coordadjuster.h"
#include "sci/graphics/cursor.h"
#include "sci/graphics/maciconbar.h"
+#ifdef ENABLE_SCI32
+#include "sci/graphics/frameout.h"
+#endif
namespace Sci {
@@ -58,10 +61,7 @@ reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) {
if (g_debug_simulated_key && (mask & SCI_EVENT_KEYBOARD)) {
// In case we use a simulated event we query the current mouse position
mousePos = g_sci->_gfxCursor->getPosition();
-#ifdef ENABLE_SCI32
- if (getSciVersion() >= SCI_VERSION_2_1_EARLY)
- g_sci->_gfxCoordAdjuster->fromDisplayToScript(mousePos.y, mousePos.x);
-#endif
+
// Limit the mouse cursor position, if necessary
g_sci->_gfxCursor->refreshPosition();
@@ -86,11 +86,14 @@ reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) {
#ifdef ENABLE_SCI32
if (getSciVersion() >= SCI_VERSION_2)
mousePos = curEvent.mousePosSci;
- else
+ else {
#endif
mousePos = curEvent.mousePos;
- // Limit the mouse cursor position, if necessary
- g_sci->_gfxCursor->refreshPosition();
+ // Limit the mouse cursor position, if necessary
+ g_sci->_gfxCursor->refreshPosition();
+#ifdef ENABLE_SCI32
+ }
+#endif
if (g_sci->getVocabulary())
g_sci->getVocabulary()->parser_event = NULL_REG; // Invalidate parser event
@@ -281,14 +284,13 @@ reg_t kMapKeyToDir(EngineState *s, int argc, reg_t *argv) {
reg_t kGlobalToLocal(EngineState *s, int argc, reg_t *argv) {
reg_t obj = argv[0];
- reg_t planeObject = argc > 1 ? argv[1] : NULL_REG; // SCI32
SegManager *segMan = s->_segMan;
if (obj.getSegment()) {
int16 x = readSelectorValue(segMan, obj, SELECTOR(x));
int16 y = readSelectorValue(segMan, obj, SELECTOR(y));
- g_sci->_gfxCoordAdjuster->kernelGlobalToLocal(x, y, planeObject);
+ g_sci->_gfxCoordAdjuster->kernelGlobalToLocal(x, y);
writeSelectorValue(segMan, obj, SELECTOR(x), x);
writeSelectorValue(segMan, obj, SELECTOR(y), y);
@@ -300,14 +302,13 @@ reg_t kGlobalToLocal(EngineState *s, int argc, reg_t *argv) {
reg_t kLocalToGlobal(EngineState *s, int argc, reg_t *argv) {
reg_t obj = argv[0];
- reg_t planeObject = argc > 1 ? argv[1] : NULL_REG; // SCI32
SegManager *segMan = s->_segMan;
if (obj.getSegment()) {
int16 x = readSelectorValue(segMan, obj, SELECTOR(x));
int16 y = readSelectorValue(segMan, obj, SELECTOR(y));
- g_sci->_gfxCoordAdjuster->kernelLocalToGlobal(x, y, planeObject);
+ g_sci->_gfxCoordAdjuster->kernelLocalToGlobal(x, y);
writeSelectorValue(segMan, obj, SELECTOR(x), x);
writeSelectorValue(segMan, obj, SELECTOR(y), y);
@@ -322,4 +323,52 @@ reg_t kJoystick(EngineState *s, int argc, reg_t *argv) {
return NULL_REG;
}
+#ifdef ENABLE_SCI32
+reg_t kGlobalToLocal32(EngineState *s, int argc, reg_t *argv) {
+ const reg_t result = argv[0];
+ const reg_t planeObj = argv[1];
+
+ bool visible = true;
+ Plane *plane = g_sci->_gfxFrameout->getVisiblePlanes().findByObject(planeObj);
+ if (plane == nullptr) {
+ plane = g_sci->_gfxFrameout->getPlanes().findByObject(planeObj);
+ visible = false;
+ }
+ if (plane == nullptr) {
+ error("kGlobalToLocal: Plane %04x:%04x not found", PRINT_REG(planeObj));
+ }
+
+ const int16 x = readSelectorValue(s->_segMan, result, SELECTOR(x)) - plane->_gameRect.left;
+ const int16 y = readSelectorValue(s->_segMan, result, SELECTOR(y)) - plane->_gameRect.top;
+
+ writeSelectorValue(s->_segMan, result, SELECTOR(x), x);
+ writeSelectorValue(s->_segMan, result, SELECTOR(y), y);
+
+ return make_reg(0, visible);
+}
+
+reg_t kLocalToGlobal32(EngineState *s, int argc, reg_t *argv) {
+ const reg_t result = argv[0];
+ const reg_t planeObj = argv[1];
+
+ bool visible = true;
+ Plane *plane = g_sci->_gfxFrameout->getVisiblePlanes().findByObject(planeObj);
+ if (plane == nullptr) {
+ plane = g_sci->_gfxFrameout->getPlanes().findByObject(planeObj);
+ visible = false;
+ }
+ if (plane == nullptr) {
+ error("kLocalToGlobal: Plane %04x:%04x not found", PRINT_REG(planeObj));
+ }
+
+ const int16 x = readSelectorValue(s->_segMan, result, SELECTOR(x)) + plane->_gameRect.left;
+ const int16 y = readSelectorValue(s->_segMan, result, SELECTOR(y)) + plane->_gameRect.top;
+
+ writeSelectorValue(s->_segMan, result, SELECTOR(x), x);
+ writeSelectorValue(s->_segMan, result, SELECTOR(y), y);
+
+ return make_reg(0, visible);
+}
+#endif
+
} // End of namespace Sci
diff --git a/engines/sci/engine/kfile.cpp b/engines/sci/engine/kfile.cpp
index 3bcadd143c..a3a97eb7ee 100644
--- a/engines/sci/engine/kfile.cpp
+++ b/engines/sci/engine/kfile.cpp
@@ -40,6 +40,9 @@
#include "sci/engine/savegame.h"
#include "sci/sound/audio.h"
#include "sci/console.h"
+#ifdef ENABLE_SCI32
+#include "sci/resource.h"
+#endif
namespace Sci {
@@ -196,26 +199,25 @@ reg_t kValidPath(EngineState *s, int argc, reg_t *argv) {
#ifdef ENABLE_SCI32
reg_t kCD(EngineState *s, int argc, reg_t *argv) {
- // TODO: Stub
- switch (argv[0].toUint16()) {
- case 0:
- if (argc == 1) {
- // Check if a disc is in the drive
- return TRUE_REG;
- } else {
- // Check if the specified disc is in the drive
- // and return the current disc number. We just
- // return the requested disc number.
- return argv[1];
- }
- case 1:
- // Return the current CD number
- return make_reg(0, 1);
- default:
- warning("CD(%d)", argv[0].toUint16());
+ if (!s)
+ return make_reg(0, getSciVersion());
+ error("not supposed to call this");
+}
+
+reg_t kCheckCD(EngineState *s, int argc, reg_t *argv) {
+ const int16 cdNo = argc > 0 ? argv[0].toSint16() : 0;
+
+ if (cdNo) {
+ g_sci->getResMan()->findDisc(cdNo);
}
- return NULL_REG;
+ return make_reg(0, g_sci->getResMan()->getCurrentDiscNo());
+}
+
+reg_t kGetSavedCD(EngineState *s, int argc, reg_t *argv) {
+ // TODO: This is wrong, CD number needs to be available prior to
+ // the save game being loaded
+ return make_reg(0, g_sci->getResMan()->getCurrentDiscNo());
}
#endif
diff --git a/engines/sci/engine/kgraphics.cpp b/engines/sci/engine/kgraphics.cpp
index cae5a09789..d375a27954 100644
--- a/engines/sci/engine/kgraphics.cpp
+++ b/engines/sci/engine/kgraphics.cpp
@@ -579,17 +579,8 @@ reg_t kBaseSetter(EngineState *s, int argc, reg_t *argv) {
}
reg_t kSetNowSeen(EngineState *s, int argc, reg_t *argv) {
-#ifdef ENABLE_SCI32
- if (getSciVersion() >= SCI_VERSION_2) {
- g_sci->_gfxFrameout->kernelSetNowSeen(argv[0]);
- return NULL_REG;
- } else {
-#endif
- g_sci->_gfxCompare->kernelSetNowSeen(argv[0]);
- return s->r_acc;
-#ifdef ENABLE_SCI32
- }
-#endif
+ g_sci->_gfxCompare->kernelSetNowSeen(argv[0]);
+ return s->r_acc;
}
reg_t kPalette(EngineState *s, int argc, reg_t *argv) {
diff --git a/engines/sci/engine/kgraphics32.cpp b/engines/sci/engine/kgraphics32.cpp
index e458109cc2..13a209ea55 100644
--- a/engines/sci/engine/kgraphics32.cpp
+++ b/engines/sci/engine/kgraphics32.cpp
@@ -37,8 +37,6 @@
#include "sci/graphics/cache.h"
#include "sci/graphics/compare.h"
#include "sci/graphics/controls16.h"
-#include "sci/graphics/coordadjuster.h"
-#include "sci/graphics/cursor.h"
#include "sci/graphics/palette.h"
#include "sci/graphics/paint16.h"
#include "sci/graphics/picture.h"
@@ -48,6 +46,7 @@
#include "sci/graphics/text16.h"
#include "sci/graphics/view.h"
#ifdef ENABLE_SCI32
+#include "sci/graphics/cursor32.h"
#include "sci/graphics/celobj32.h"
#include "sci/graphics/controls32.h"
#include "sci/graphics/font.h" // TODO: remove once kBitmap is moved in a separate class
@@ -64,6 +63,107 @@ namespace Sci {
extern void showScummVMDialog(const Common::String &message);
+reg_t kBaseSetter32(EngineState *s, int argc, reg_t *argv) {
+ reg_t object = argv[0];
+
+ const GuiResourceId viewId = readSelectorValue(s->_segMan, object, SELECTOR(view));
+ const int16 loopNo = readSelectorValue(s->_segMan, object, SELECTOR(loop));
+ const int16 celNo = readSelectorValue(s->_segMan, object, SELECTOR(cel));
+ const int16 x = readSelectorValue(s->_segMan, object, SELECTOR(x));
+ const int16 y = readSelectorValue(s->_segMan, object, SELECTOR(y));
+
+ CelObjView celObj(viewId, loopNo, celNo);
+
+ const int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth;
+ const int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight;
+
+ const Ratio scaleX(scriptWidth, celObj._scaledWidth);
+ const Ratio scaleY(scriptHeight, celObj._scaledHeight);
+
+ int16 brLeft;
+
+ if (celObj._mirrorX) {
+ brLeft = x - ((celObj._width - celObj._displace.x) * scaleX).toInt();
+ } else {
+ brLeft = x - (celObj._displace.x * scaleX).toInt();
+ }
+
+ const int16 brRight = brLeft + (celObj._width * scaleX).toInt() - 1;
+
+ writeSelectorValue(s->_segMan, object, SELECTOR(brLeft), brLeft);
+ writeSelectorValue(s->_segMan, object, SELECTOR(brRight), brRight);
+ writeSelectorValue(s->_segMan, object, SELECTOR(brBottom), y + 1);
+ writeSelectorValue(s->_segMan, object, SELECTOR(brTop), y + 1 - readSelectorValue(s->_segMan, object, SELECTOR(yStep)));
+
+ return s->r_acc;
+}
+
+reg_t kSetNowSeen32(EngineState *s, int argc, reg_t *argv) {
+ const bool found = g_sci->_gfxFrameout->kernelSetNowSeen(argv[0]);
+
+ // NOTE: MGDX is assumed to use the older kSetNowSeen since it was
+ // released before SQ6, but this has not been verified since it cannot be
+ // disassembled at the moment (Phar Lap Windows-only release)
+ if (getSciVersion() <= SCI_VERSION_2_1_EARLY ||
+ g_sci->getGameId() == GID_SQ6 ||
+ g_sci->getGameId() == GID_MOTHERGOOSEHIRES) {
+
+ if (!found) {
+ error("kSetNowSeen: Unable to find screen item %04x:%04x", PRINT_REG(argv[0]));
+ }
+ return s->r_acc;
+ }
+
+ if (!found) {
+ warning("kSetNowSeen: Unable to find screen item %04x:%04x", PRINT_REG(argv[0]));
+ }
+
+ return make_reg(0, found);
+}
+
+reg_t kSetCursor32(EngineState *s, int argc, reg_t *argv) {
+ switch (argc) {
+ case 1: {
+ if (argv[0].toSint16() == -2) {
+ g_sci->_gfxCursor32->clearRestrictedArea();
+ } else {
+ if (argv[0].isNull()) {
+ g_sci->_gfxCursor32->hide();
+ } else {
+ g_sci->_gfxCursor32->show();
+ }
+ }
+ break;
+ }
+ case 2: {
+ const Common::Point position(argv[0].toSint16(), argv[1].toSint16());
+ g_sci->_gfxCursor32->setPosition(position);
+ break;
+ }
+ case 3: {
+ g_sci->_gfxCursor32->setView(argv[0].toUint16(), argv[1].toSint16(), argv[2].toSint16());
+ break;
+ }
+ case 4: {
+ const Common::Rect restrictRect(argv[0].toSint16(),
+ argv[1].toSint16(),
+ argv[2].toSint16() + 1,
+ argv[3].toSint16() + 1);
+ g_sci->_gfxCursor32->setRestrictedArea(restrictRect);
+ break;
+ }
+ default:
+ error("kSetCursor: Invalid number of arguments (%d)", argc);
+ }
+
+ return s->r_acc;
+}
+
+reg_t kShakeScreen32(EngineState *s, int argc, reg_t *argv) {
+ g_sci->_gfxFrameout->shakeScreen(argv[0].toSint16(), (ShakeDirection)argv[1].toSint16());
+ return s->r_acc;
+}
+
reg_t kIsHiRes(EngineState *s, int argc, reg_t *argv) {
const Buffer &buffer = g_sci->_gfxFrameout->getCurrentBuffer();
if (buffer.screenWidth < 640 || buffer.screenHeight < 400)
@@ -266,7 +366,7 @@ reg_t kMessageBox(EngineState *s, int argc, reg_t *argv) {
* effect
*/
reg_t kSetShowStyle(EngineState *s, int argc, reg_t *argv) {
- ShowStyleType type = (ShowStyleType)argv[0].toUint16();
+ const uint16 type = argv[0].toUint16();
reg_t planeObj = argv[1];
int16 seconds = argv[2].toSint16();
// NOTE: This value seems to indicate whether the transition is an
@@ -301,6 +401,10 @@ reg_t kSetShowStyle(EngineState *s, int argc, reg_t *argv) {
divisions = argc > 9 ? argv[9].toSint16() : -1;
}
+ if ((getSciVersion() < SCI_VERSION_2_1_MIDDLE && g_sci->getGameId() != GID_KQ7 && type == 15) || type > 15) {
+ error("Illegal show style %d for plane %04x:%04x", type, PRINT_REG(planeObj));
+ }
+
// TODO: Reuse later for SCI2 and SCI3 implementation and then discard
// warning("kSetShowStyle: effect %d, plane: %04x:%04x (%s), sec: %d, "
// "dir: %d, prio: %d, animate: %d, ref frame: %d, black screen: %d, "
@@ -312,7 +416,7 @@ reg_t kSetShowStyle(EngineState *s, int argc, reg_t *argv) {
// NOTE: The order of planeObj and showStyle are reversed
// because this is how SCI3 called the corresponding method
// on the KernelMgr
- g_sci->_gfxTransitions32->kernelSetShowStyle(argc, planeObj, type, seconds, back, priority, animate, refFrame, pFadeArray, divisions, blackScreen);
+ g_sci->_gfxTransitions32->kernelSetShowStyle(argc, planeObj, (ShowStyleType)type, seconds, back, priority, animate, refFrame, pFadeArray, divisions, blackScreen);
return s->r_acc;
}
diff --git a/engines/sci/engine/kmisc.cpp b/engines/sci/engine/kmisc.cpp
index c99540967c..448d7bb8a3 100644
--- a/engines/sci/engine/kmisc.cpp
+++ b/engines/sci/engine/kmisc.cpp
@@ -540,57 +540,46 @@ enum kSciPlatforms {
kSciPlatformWindows = 2
};
-enum kPlatformOps {
- kPlatformUnk0 = 0,
- kPlatformCDSpeed = 1,
- kPlatformColorDepth = 2,
- kPlatformCDCheck = 3,
- kPlatformGetPlatform = 4,
- kPlatformUnk5 = 5,
- kPlatformIsHiRes = 6,
- kPlatformIsItWindows = 7
-};
-
reg_t kPlatform(EngineState *s, int argc, reg_t *argv) {
+ enum Operation {
+ kPlatformUnknown = 0,
+ kPlatformGetPlatform = 4,
+ kPlatformUnknown5 = 5,
+ kPlatformIsHiRes = 6,
+ kPlatformWin311OrHigher = 7
+ };
+
bool isWindows = g_sci->getPlatform() == Common::kPlatformWindows;
- if (argc == 0 && getSciVersion() < SCI_VERSION_2) {
+ if (argc == 0) {
// 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 0.
return NULL_REG;
}
+ if (g_sci->forceHiresGraphics()) {
+ // force Windows platform, so that hires-graphics are enabled
+ isWindows = true;
+ }
+
uint16 operation = (argc == 0) ? 0 : argv[0].toUint16();
switch (operation) {
- case kPlatformCDSpeed:
- // TODO: Returns CD Speed?
- warning("STUB: kPlatform(CDSpeed)");
- break;
- case kPlatformColorDepth:
- // Always returns 2
- return make_reg(0, /* 256-color */ 2);
- case kPlatformCDCheck:
- // TODO: Some sort of CD check?
- warning("STUB: kPlatform(CDCheck)");
- break;
- case kPlatformUnk0:
+ case kPlatformUnknown:
// For Mac versions, kPlatform(0) with other args has more functionality
if (g_sci->getPlatform() == Common::kPlatformMacintosh && argc > 1)
return kMacPlatform(s, argc - 1, argv + 1);
// Otherwise, fall through
case kPlatformGetPlatform:
return make_reg(0, (isWindows) ? kSciPlatformWindows : kSciPlatformDOS);
- case kPlatformUnk5:
+ case kPlatformUnknown5:
// This case needs to return the opposite of case 6 to get hires graphics
- return make_reg(0, !ConfMan.getBool("enable_high_resolution_graphics"));
+ return make_reg(0, !isWindows);
case kPlatformIsHiRes:
- return make_reg(0, ConfMan.getBool("enable_high_resolution_graphics"));
- case kPlatformIsItWindows:
+ case kPlatformWin311OrHigher:
return make_reg(0, isWindows);
default:
error("Unsupported kPlatform operation %d", operation);
@@ -599,6 +588,37 @@ reg_t kPlatform(EngineState *s, int argc, reg_t *argv) {
return NULL_REG;
}
+#ifdef ENABLE_SCI32
+reg_t kPlatform32(EngineState *s, int argc, reg_t *argv) {
+ enum Operation {
+ kGetPlatform = 0,
+ kGetCDSpeed = 1,
+ kGetColorDepth = 2,
+ kGetCDDrive = 3
+ };
+
+ const Operation operation = argc > 0 ? (Operation)argv[0].toSint16() : kGetPlatform;
+
+ switch (operation) {
+ case kGetPlatform:
+ switch (g_sci->getPlatform()) {
+ case Common::kPlatformDOS:
+ return make_reg(0, kSciPlatformDOS);
+ case Common::kPlatformWindows:
+ return make_reg(0, kSciPlatformWindows);
+ default:
+ error("Unknown platform %d", g_sci->getPlatform());
+ }
+ case kGetColorDepth:
+ return make_reg(0, /* 256 color */ 2);
+ case kGetCDSpeed:
+ case kGetCDDrive:
+ default:
+ return make_reg(0, 0);
+ }
+}
+#endif
+
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
diff --git a/engines/sci/engine/kvideo.cpp b/engines/sci/engine/kvideo.cpp
index 86d8a4b817..b539c84f5d 100644
--- a/engines/sci/engine/kvideo.cpp
+++ b/engines/sci/engine/kvideo.cpp
@@ -72,11 +72,10 @@ void playVideo(Video::VideoDecoder *videoDecoder, VideoState videoState) {
uint16 y = (screenHeight - height) / 2;
bool skipVideo = false;
- EngineState *s = g_sci->getEngineState();
if (videoDecoder->hasDirtyPalette()) {
- const byte *palette = videoDecoder->getPalette() + s->_vmdPalStart * 3;
- g_system->getPaletteManager()->setPalette(palette, s->_vmdPalStart, s->_vmdPalEnd - s->_vmdPalStart);
+ const byte *palette = videoDecoder->getPalette();
+ g_system->getPaletteManager()->setPalette(palette, 0, 255);
}
while (!g_engine->shouldQuit() && !videoDecoder->endOfVideo() && !skipVideo) {
@@ -85,7 +84,7 @@ void playVideo(Video::VideoDecoder *videoDecoder, VideoState videoState) {
if (frame) {
if (scaleBuffer) {
- // TODO: Probably should do aspect ratio correction in e.g. GK1 Windows
+ // TODO: Probably should do aspect ratio correction in KQ6
g_sci->_gfxScreen->scale2x((const byte *)frame->getPixels(), scaleBuffer, videoDecoder->getWidth(), videoDecoder->getHeight(), bytesPerPixel);
g_system->copyRectToScreen(scaleBuffer, pitch, x, y, width, height);
} else {
@@ -93,8 +92,8 @@ void playVideo(Video::VideoDecoder *videoDecoder, VideoState videoState) {
}
if (videoDecoder->hasDirtyPalette()) {
- const byte *palette = videoDecoder->getPalette() + s->_vmdPalStart * 3;
- g_system->getPaletteManager()->setPalette(palette, s->_vmdPalStart, s->_vmdPalEnd - s->_vmdPalStart);
+ const byte *palette = videoDecoder->getPalette();
+ g_system->getPaletteManager()->setPalette(palette, 0, 255);
}
g_system->updateScreen();
@@ -226,6 +225,80 @@ reg_t kShowMovie32(EngineState *s, int argc, reg_t *argv) {
return s->r_acc;
}
+reg_t kRobot(EngineState *s, int argc, reg_t *argv) {
+ if (!s)
+ return make_reg(0, getSciVersion());
+ error("not supposed to call this");
+}
+
+reg_t kRobotOpen(EngineState *s, int argc, reg_t *argv) {
+ const GuiResourceId robotId = argv[0].toUint16();
+ const reg_t plane = argv[1];
+ const int16 priority = argv[2].toSint16();
+ const int16 x = argv[3].toSint16();
+ const int16 y = argv[4].toSint16();
+ const int16 scale = argc > 5 ? argv[5].toSint16() : 128;
+ g_sci->_video32->getRobotPlayer().open(robotId, plane, priority, x, y, scale);
+ return make_reg(0, 0);
+}
+reg_t kRobotShowFrame(EngineState *s, int argc, reg_t *argv) {
+ const uint16 frameNo = argv[0].toUint16();
+ const uint16 newX = argc > 1 ? argv[1].toUint16() : (uint16)RobotDecoder::kUnspecified;
+ const uint16 newY = argc > 1 ? argv[2].toUint16() : (uint16)RobotDecoder::kUnspecified;
+ g_sci->_video32->getRobotPlayer().showFrame(frameNo, newX, newY, RobotDecoder::kUnspecified);
+ return s->r_acc;
+}
+
+reg_t kRobotGetFrameSize(EngineState *s, int argc, reg_t *argv) {
+ Common::Rect frameRect;
+ const uint16 numFramesTotal = g_sci->_video32->getRobotPlayer().getFrameSize(frameRect);
+
+ reg_t *outRect = s->_segMan->derefRegPtr(argv[0], 4);
+ outRect[0] = make_reg(0, frameRect.left);
+ outRect[1] = make_reg(0, frameRect.top);
+ outRect[2] = make_reg(0, frameRect.right - 1);
+ outRect[3] = make_reg(0, frameRect.bottom - 1);
+
+ return make_reg(0, numFramesTotal);
+}
+
+reg_t kRobotPlay(EngineState *s, int argc, reg_t *argv) {
+ g_sci->_video32->getRobotPlayer().resume();
+ return s->r_acc;
+}
+
+reg_t kRobotGetIsFinished(EngineState *s, int argc, reg_t *argv) {
+ return make_reg(0, g_sci->_video32->getRobotPlayer().getStatus() == RobotDecoder::kRobotStatusEnd);
+}
+
+reg_t kRobotGetIsPlaying(EngineState *s, int argc, reg_t *argv) {
+ return make_reg(0, g_sci->_video32->getRobotPlayer().getStatus() == RobotDecoder::kRobotStatusPlaying);
+}
+
+reg_t kRobotClose(EngineState *s, int argc, reg_t *argv) {
+ g_sci->_video32->getRobotPlayer().close();
+ return s->r_acc;
+}
+
+reg_t kRobotGetCue(EngineState *s, int argc, reg_t *argv) {
+ writeSelectorValue(s->_segMan, argv[0], SELECTOR(signal), g_sci->_video32->getRobotPlayer().getCue());
+ return s->r_acc;
+}
+
+reg_t kRobotPause(EngineState *s, int argc, reg_t *argv) {
+ g_sci->_video32->getRobotPlayer().pause();
+ return s->r_acc;
+}
+
+reg_t kRobotGetFrameNo(EngineState *s, int argc, reg_t *argv) {
+ return make_reg(0, g_sci->_video32->getRobotPlayer().getFrameNo());
+}
+
+reg_t kRobotSetPriority(EngineState *s, int argc, reg_t *argv) {
+ g_sci->_video32->getRobotPlayer().setPriority(argv[0].toSint16());
+ return s->r_acc;
+}
+
reg_t kShowMovieWin(EngineState *s, int argc, reg_t *argv) {
if (!s)
return make_reg(0, getSciVersion());
@@ -352,6 +425,10 @@ reg_t kPlayVMDClose(EngineState *s, int argc, reg_t *argv) {
return make_reg(0, g_sci->_video32->getVMDPlayer().close());
}
+reg_t kPlayVMDGetStatus(EngineState *s, int argc, reg_t *argv) {
+ return make_reg(0, g_sci->_video32->getVMDPlayer().getStatus());
+}
+
reg_t kPlayVMDPlayUntilEvent(EngineState *s, int argc, reg_t *argv) {
const VMDPlayer::EventFlags flags = (VMDPlayer::EventFlags)argv[0].toUint16();
const int16 lastFrameNo = argc > 1 ? argv[1].toSint16() : -1;
diff --git a/engines/sci/engine/savegame.cpp b/engines/sci/engine/savegame.cpp
index 7804b7892d..eeddda8390 100644
--- a/engines/sci/engine/savegame.cpp
+++ b/engines/sci/engine/savegame.cpp
@@ -48,6 +48,7 @@
#include "sci/sound/music.h"
#ifdef ENABLE_SCI32
+#include "sci/graphics/cursor32.h"
#include "sci/graphics/frameout.h"
#include "sci/graphics/palette32.h"
#include "sci/graphics/remap32.h"
@@ -889,6 +890,29 @@ void GfxRemap32::saveLoadWithSerializer(Common::Serializer &s) {
_needsUpdate = true;
}
}
+
+void GfxCursor32::saveLoadWithSerializer(Common::Serializer &s) {
+ if (s.getVersion() < 37) {
+ return;
+ }
+
+ s.syncAsSint32LE(_hideCount);
+ s.syncAsSint16LE(_restrictedArea.left);
+ s.syncAsSint16LE(_restrictedArea.top);
+ s.syncAsSint16LE(_restrictedArea.right);
+ s.syncAsSint16LE(_restrictedArea.bottom);
+ s.syncAsUint16LE(_cursorInfo.resourceId);
+ s.syncAsUint16LE(_cursorInfo.loopNo);
+ s.syncAsUint16LE(_cursorInfo.celNo);
+
+ if (s.isLoading()) {
+ hide();
+ setView(_cursorInfo.resourceId, _cursorInfo.loopNo, _cursorInfo.celNo);
+ if (!_hideCount) {
+ show();
+ }
+ }
+}
#endif
void GfxPorts::saveLoadWithSerializer(Common::Serializer &s) {
diff --git a/engines/sci/engine/savegame.h b/engines/sci/engine/savegame.h
index 1bf66864e8..51dbbedd87 100644
--- a/engines/sci/engine/savegame.h
+++ b/engines/sci/engine/savegame.h
@@ -37,7 +37,7 @@ struct EngineState;
*
* Version - new/changed feature
* =============================
- * 37 - Segment entry data changed to pointers
+ * 37 - Segment entry data changed to pointers, SCI32 cursor
* 36 - SCI32 bitmap segment
* 35 - SCI32 remap
* 34 - SCI32 palettes, and store play time in ticks
diff --git a/engines/sci/engine/seg_manager.h b/engines/sci/engine/seg_manager.h
index acebecea97..8ed1c3a143 100644
--- a/engines/sci/engine/seg_manager.h
+++ b/engines/sci/engine/seg_manager.h
@@ -30,8 +30,7 @@
#include "sci/engine/vm_types.h"
#include "sci/engine/segment.h"
#ifdef ENABLE_SCI32
-// TODO: Baaaad?
-#include "sci/graphics/celobj32.h"
+#include "sci/graphics/celobj32.h" // kLowResX, kLowResY
#endif
namespace Sci {
diff --git a/engines/sci/engine/state.cpp b/engines/sci/engine/state.cpp
index 2c85907628..a338beffc9 100644
--- a/engines/sci/engine/state.cpp
+++ b/engines/sci/engine/state.cpp
@@ -121,9 +121,6 @@ void EngineState::reset(bool isRestoring) {
_videoState.reset();
_syncedAudioOptions = false;
-
- _vmdPalStart = 0;
- _vmdPalEnd = 256;
}
void EngineState::speedThrottler(uint32 neededSleep) {
diff --git a/engines/sci/engine/state.h b/engines/sci/engine/state.h
index dd8d76f002..baa912b60e 100644
--- a/engines/sci/engine/state.h
+++ b/engines/sci/engine/state.h
@@ -203,7 +203,6 @@ public:
// TODO: Excise video code from the state manager
VideoState _videoState;
- uint16 _vmdPalStart, _vmdPalEnd;
bool _syncedAudioOptions;
/**
diff --git a/engines/sci/engine/workarounds.cpp b/engines/sci/engine/workarounds.cpp
index 9b3b329418..bc1390864e 100644
--- a/engines/sci/engine/workarounds.cpp
+++ b/engines/sci/engine/workarounds.cpp
@@ -329,7 +329,7 @@ const SciWorkaroundEntry uninitializedReadWorkarounds[] = {
{ GID_PEPPER, -1, 894, 0, "Package", "doVerb", NULL, 3, { WORKAROUND_FAKE, 0 } }, // using the hand on the book in the inventory - bug #5154
{ GID_PEPPER, 150, 928, 0, "Narrator", "startText", NULL, 0, { WORKAROUND_FAKE, 0 } }, // happens during the non-interactive demo of Pepper
{ GID_PQ4, -1, 25, 0, "iconToggle", "select", NULL, 1, { WORKAROUND_FAKE, 0 } }, // when toggling the icon bar to auto-hide or not
- { GID_PQSWAT, -1, 64950, 0, "View", "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // Using the menu in the beginning
+ { GID_PQSWAT, -1, 64950, 0, NULL, "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // Using any menus in-game
{ GID_QFG1, -1, 210, 0, "Encounter", "init", sig_uninitread_qfg1_1, 0, { WORKAROUND_FAKE, 0 } }, // qfg1/hq1: going to the brigands hideout
{ GID_QFG1VGA, 16, 16, 0, "lassoFailed", "changeState", NULL, -1, { WORKAROUND_FAKE, 0 } }, // qfg1vga: casting the "fetch" spell in the screen with the flowers, temps 0 and 1 - bug #5309
{ GID_QFG1VGA, -1, 210, 0, "Encounter", "init", sig_uninitread_qfg1vga_1, 0, { WORKAROUND_FAKE, 0 } }, // qfg1vga: going to the brigands hideout - bug #5515
@@ -688,6 +688,7 @@ const SciWorkaroundEntry kPaletteUnsetFlag_workarounds[] = {
// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround
const SciWorkaroundEntry kSetCursor_workarounds[] = {
{ GID_KQ5, -1, 768, 0, "KQCursor", "init", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // CD: gets called with 4 additional "900d" parameters
+ { GID_MOTHERGOOSEHIRES,0, 0, -1, "MG", "setCursor", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // At the start of the game, an object is passed as the cel number
SCI_WORKAROUNDENTRY_TERMINATOR
};
diff --git a/engines/sci/event.cpp b/engines/sci/event.cpp
index 4ad2a0cfa3..b267d2ebc2 100644
--- a/engines/sci/event.cpp
+++ b/engines/sci/event.cpp
@@ -30,6 +30,7 @@
#include "sci/engine/state.h"
#include "sci/engine/kernel.h"
#ifdef ENABLE_SCI32
+#include "sci/graphics/cursor32.h"
#include "sci/graphics/frameout.h"
#endif
#include "sci/graphics/screen.h"
@@ -168,9 +169,17 @@ SciEvent EventManager::getScummVMEvent() {
if (getSciVersion() >= SCI_VERSION_2) {
const Buffer &screen = g_sci->_gfxFrameout->getCurrentBuffer();
+ if (ev.type == Common::EVENT_MOUSEMOVE) {
+ // This will clamp `mousePos` according to the restricted zone,
+ // so any cursor or screen item associated with the mouse position
+ // does not bounce when it hits the edge (or ignore the edge)
+ g_sci->_gfxCursor32->deviceMoved(mousePos);
+ }
+
Common::Point mousePosSci = mousePos;
mulru(mousePosSci, Ratio(screen.scriptWidth, screen.screenWidth), Ratio(screen.scriptHeight, screen.screenHeight));
noEvent.mousePosSci = input.mousePosSci = mousePosSci;
+
} else {
#endif
g_sci->_gfxScreen->adjustBackUpscaledCoordinates(mousePos.y, mousePos.x);
diff --git a/engines/sci/graphics/cache.cpp b/engines/sci/graphics/cache.cpp
index fb1f557ad6..9c77f31a14 100644
--- a/engines/sci/graphics/cache.cpp
+++ b/engines/sci/graphics/cache.cpp
@@ -95,10 +95,20 @@ int16 GfxCache::kernelViewGetCelHeight(GuiResourceId viewId, int16 loopNo, int16
}
int16 GfxCache::kernelViewGetLoopCount(GuiResourceId viewId) {
+#ifdef ENABLE_SCI32
+ if (getSciVersion() >= SCI_VERSION_2) {
+ return CelObjView::getNumLoops(viewId);
+ }
+#endif
return getView(viewId)->getLoopCount();
}
int16 GfxCache::kernelViewGetCelCount(GuiResourceId viewId, int16 loopNo) {
+#ifdef ENABLE_SCI32
+ if (getSciVersion() >= SCI_VERSION_2) {
+ return CelObjView::getNumCels(viewId, loopNo);
+ }
+#endif
return getView(viewId)->getCelCount(loopNo);
}
diff --git a/engines/sci/graphics/celobj32.cpp b/engines/sci/graphics/celobj32.cpp
index 311684d595..d053fa2eef 100644
--- a/engines/sci/graphics/celobj32.cpp
+++ b/engines/sci/graphics/celobj32.cpp
@@ -45,7 +45,7 @@ void CelScaler::activateScaleTables(const Ratio &scaleX, const Ratio &scaleY) {
}
}
- int i = 1 - _activeIndex;
+ const int i = 1 - _activeIndex;
_activeIndex = i;
CelScalerTable &table = _scaleTables[i];
@@ -65,7 +65,7 @@ void CelScaler::activateScaleTables(const Ratio &scaleX, const Ratio &scaleY) {
void CelScaler::buildLookupTable(int *table, const Ratio &ratio, const int size) {
int value = 0;
int remainder = 0;
- int num = ratio.getNumerator();
+ const int num = ratio.getNumerator();
for (int i = 0; i < size; ++i) {
*table++ = value;
remainder += ratio.getDenominator();
@@ -164,8 +164,8 @@ struct SCALER_Scale {
const byte *_row;
READER _reader;
int16 _x;
- static int16 _valuesX[1024];
- static int16 _valuesY[1024];
+ static int16 _valuesX[4096];
+ static int16 _valuesY[4096];
SCALER_Scale(const CelObj &celObj, const Common::Rect &targetRect, const Common::Point &scaledPosition, const Ratio scaleX, const Ratio scaleY) :
_row(nullptr),
@@ -204,7 +204,7 @@ struct SCALER_Scale {
if (g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth == kLowResX) {
const int16 unscaledX = (scaledPosition.x / scaleX).toInt();
if (FLIP) {
- int lastIndex = celObj._width - 1;
+ const int lastIndex = celObj._width - 1;
for (int16 x = targetRect.left; x < targetRect.right; ++x) {
_valuesX[x] = lastIndex - (table->valuesX[x] - unscaledX);
}
@@ -220,7 +220,7 @@ struct SCALER_Scale {
}
} else {
if (FLIP) {
- int lastIndex = celObj._width - 1;
+ const int lastIndex = celObj._width - 1;
for (int16 x = 0; x < targetRect.width(); ++x) {
_valuesX[targetRect.left + x] = lastIndex - table->valuesX[x];
}
@@ -249,9 +249,9 @@ struct SCALER_Scale {
};
template<bool FLIP, typename READER>
-int16 SCALER_Scale<FLIP, READER>::_valuesX[1024];
+int16 SCALER_Scale<FLIP, READER>::_valuesX[4096];
template<bool FLIP, typename READER>
-int16 SCALER_Scale<FLIP, READER>::_valuesY[1024];
+int16 SCALER_Scale<FLIP, READER>::_valuesY[4096];
#pragma mark -
#pragma mark CelObj - Resource readers
@@ -261,7 +261,7 @@ private:
#ifndef NDEBUG
const int16 _sourceHeight;
#endif
- byte *_pixels;
+ const byte *_pixels;
const int16 _sourceWidth;
public:
@@ -270,7 +270,7 @@ public:
_sourceHeight(celObj._height),
#endif
_sourceWidth(celObj._width) {
- byte *resource = celObj.getResPointer();
+ const byte *resource = celObj.getResPointer();
_pixels = resource + READ_SCI11ENDIAN_UINT32(resource + celObj._celHeaderOffset + 24);
}
@@ -282,8 +282,8 @@ public:
struct READER_Compressed {
private:
- byte *_resource;
- byte _buffer[1024];
+ const byte *const _resource;
+ byte _buffer[4096];
uint32 _controlOffset;
uint32 _dataOffset;
uint32 _uncompressedDataOffset;
@@ -301,7 +301,7 @@ public:
_maxWidth(maxWidth) {
assert(maxWidth <= celObj._width);
- byte *celHeader = _resource + celObj._celHeaderOffset;
+ const byte *const celHeader = _resource + celObj._celHeaderOffset;
_dataOffset = READ_SCI11ENDIAN_UINT32(celHeader + 24);
_uncompressedDataOffset = READ_SCI11ENDIAN_UINT32(celHeader + 28);
_controlOffset = READ_SCI11ENDIAN_UINT32(celHeader + 32);
@@ -311,14 +311,14 @@ public:
assert(y >= 0 && y < _sourceHeight);
if (y != _y) {
// compressed data segment for row
- byte *row = _resource + _dataOffset + READ_SCI11ENDIAN_UINT32(_resource + _controlOffset + y * 4);
+ const byte *row = _resource + _dataOffset + READ_SCI11ENDIAN_UINT32(_resource + _controlOffset + y * 4);
// uncompressed data segment for row
- byte *literal = _resource + _uncompressedDataOffset + READ_SCI11ENDIAN_UINT32(_resource + _controlOffset + _sourceHeight * 4 + y * 4);
+ const byte *literal = _resource + _uncompressedDataOffset + READ_SCI11ENDIAN_UINT32(_resource + _controlOffset + _sourceHeight * 4 + y * 4);
uint8 length;
for (int16 i = 0; i < _maxWidth; i += length) {
- byte controlByte = *row++;
+ const byte controlByte = *row++;
length = controlByte;
// Run-length encoded
@@ -581,7 +581,7 @@ void CelObj::submitPalette() const {
int CelObj::_nextCacheId = 1;
CelCache *CelObj::_cache = nullptr;
-int CelObj::searchCache(const CelInfo32 &celInfo, int *nextInsertIndex) const {
+int CelObj::searchCache(const CelInfo32 &celInfo, int *const nextInsertIndex) const {
*nextInsertIndex = -1;
int oldestId = _nextCacheId + 1;
int oldestIndex = 0;
@@ -791,6 +791,49 @@ void CelObj::scaleDrawUncompNoMD(Buffer &target, const Ratio &scaleX, const Rati
#pragma mark -
#pragma mark CelObjView
+int16 CelObjView::getNumLoops(const GuiResourceId viewId) {
+ const Resource *const resource = g_sci->getResMan()->findResource(ResourceId(kResourceTypeView, viewId), false);
+
+ if (!resource) {
+ return 0;
+ }
+
+ assert(resource->size >= 3);
+ return resource->data[2];
+}
+
+int16 CelObjView::getNumCels(const GuiResourceId viewId, const int16 loopNo) {
+ const Resource *const resource = g_sci->getResMan()->findResource(ResourceId(kResourceTypeView, viewId), false);
+
+ if (!resource) {
+ return 0;
+ }
+
+ const byte *const data = resource->data;
+
+ const uint16 loopCount = data[2];
+ if (loopNo >= loopCount || loopNo < 0) {
+ return 0;
+ }
+
+ const uint16 viewHeaderSize = READ_SCI11ENDIAN_UINT16(data);
+ const uint8 loopHeaderSize = data[12];
+ const uint8 viewHeaderFieldSize = 2;
+
+#ifndef NDEBUG
+ const byte *const dataMax = data + resource->size;
+#endif
+ const byte *loopHeader = data + viewHeaderFieldSize + viewHeaderSize + (loopHeaderSize * loopNo);
+ assert(loopHeader + 3 <= dataMax);
+
+ if ((int8)loopHeader[0] != -1) {
+ loopHeader = data + viewHeaderFieldSize + viewHeaderSize + (loopHeaderSize * (int8)loopHeader[0]);
+ assert(loopHeader >= data && loopHeader + 3 <= dataMax);
+ }
+
+ return loopHeader[2];
+}
+
CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int16 celNo) {
_info.type = kCelTypeView;
_info.resourceId = viewId;
@@ -801,7 +844,7 @@ CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int
_transparent = true;
int cacheInsertIndex;
- int cacheIndex = searchCache(_info, &cacheInsertIndex);
+ const int cacheIndex = searchCache(_info, &cacheInsertIndex);
if (cacheIndex != -1) {
CelCacheEntry &entry = (*_cache)[cacheIndex];
const CelObjView *const cachedCelObj = dynamic_cast<CelObjView *>(entry.celObj);
@@ -817,15 +860,14 @@ CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int
// generates view resource metadata for both SCI16 and SCI32
// implementations
- Resource *resource = g_sci->getResMan()->findResource(ResourceId(kResourceTypeView, viewId), false);
+ const Resource *const resource = g_sci->getResMan()->findResource(ResourceId(kResourceTypeView, viewId), false);
// NOTE: SCI2.1/SQ6 just silently returns here.
if (!resource) {
- warning("View resource %d not loaded", viewId);
- return;
+ error("View resource %d not found", viewId);
}
- byte *data = resource->data;
+ const byte *const data = resource->data;
_scaledWidth = READ_SCI11ENDIAN_UINT16(data + 14);
_scaledHeight = READ_SCI11ENDIAN_UINT16(data + 16);
@@ -844,7 +886,7 @@ CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int
}
}
- uint16 loopCount = data[2];
+ const uint16 loopCount = data[2];
if (_info.loopNo >= loopCount) {
_info.loopNo = loopCount - 1;
}
@@ -859,7 +901,7 @@ CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int
const uint8 loopHeaderSize = data[12];
const uint8 viewHeaderFieldSize = 2;
- byte *loopHeader = data + viewHeaderFieldSize + viewHeaderSize + (loopHeaderSize * _info.loopNo);
+ const byte *loopHeader = data + viewHeaderFieldSize + viewHeaderSize + (loopHeaderSize * _info.loopNo);
if ((int8)loopHeader[0] != -1) {
if (loopHeader[1] == 1) {
@@ -874,10 +916,14 @@ CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int
_info.celNo = celCount - 1;
}
+ if (_info.celNo < 0) {
+ error("Cel is less than 0!");
+ }
+
_hunkPaletteOffset = READ_SCI11ENDIAN_UINT32(data + 8);
_celHeaderOffset = READ_SCI11ENDIAN_UINT32(loopHeader + 12) + (data[13] * _info.celNo);
- byte *celHeader = data + _celHeaderOffset;
+ const byte *const celHeader = data + _celHeaderOffset;
_width = READ_SCI11ENDIAN_UINT16(celHeader);
_height = READ_SCI11ENDIAN_UINT16(celHeader + 2);
@@ -906,7 +952,7 @@ CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int
}
bool CelObjView::analyzeUncompressedForRemap() const {
- byte *pixels = getResPointer() + READ_SCI11ENDIAN_UINT32(getResPointer() + _celHeaderOffset + 24);
+ const byte *pixels = getResPointer() + READ_SCI11ENDIAN_UINT32(getResPointer() + _celHeaderOffset + 24);
for (int i = 0; i < _width * _height; ++i) {
const byte pixel = pixels[i];
if (
@@ -923,7 +969,7 @@ bool CelObjView::analyzeUncompressedForRemap() const {
bool CelObjView::analyzeForRemap() const {
READER_Compressed reader(*this, _width);
for (int y = 0; y < _height; y++) {
- const byte *curRow = reader.getRow(y);
+ const byte *const curRow = reader.getRow(y);
for (int x = 0; x < _width; x++) {
const byte pixel = curRow[x];
if (
@@ -948,7 +994,7 @@ CelObjView *CelObjView::duplicate() const {
}
byte *CelObjView::getResPointer() const {
- const Resource *const resource = g_sci->getResMan()->findResource(ResourceId(kResourceTypeView, _info.resourceId), false);
+ Resource *const resource = g_sci->getResMan()->findResource(ResourceId(kResourceTypeView, _info.resourceId), false);
if (resource == nullptr) {
error("Failed to load view %d from resource manager", _info.resourceId);
}
@@ -969,7 +1015,7 @@ CelObjPic::CelObjPic(const GuiResourceId picId, const int16 celNo) {
_remap = false;
int cacheInsertIndex;
- int cacheIndex = searchCache(_info, &cacheInsertIndex);
+ const int cacheIndex = searchCache(_info, &cacheInsertIndex);
if (cacheIndex != -1) {
CelCacheEntry &entry = (*_cache)[cacheIndex];
const CelObjPic *const cachedCelObj = dynamic_cast<CelObjPic *>(entry.celObj);
@@ -981,15 +1027,14 @@ CelObjPic::CelObjPic(const GuiResourceId picId, const int16 celNo) {
return;
}
- Resource *resource = g_sci->getResMan()->findResource(ResourceId(kResourceTypePic, picId), false);
+ const Resource *const resource = g_sci->getResMan()->findResource(ResourceId(kResourceTypePic, picId), false);
// NOTE: SCI2.1/SQ6 just silently returns here.
if (!resource) {
- warning("Pic resource %d not loaded", picId);
- return;
+ error("Pic resource %d not found", picId);
}
- byte *data = resource->data;
+ const byte *const data = resource->data;
_celCount = data[2];
@@ -1000,7 +1045,7 @@ CelObjPic::CelObjPic(const GuiResourceId picId, const int16 celNo) {
_celHeaderOffset = READ_SCI11ENDIAN_UINT16(data) + (READ_SCI11ENDIAN_UINT16(data + 4) * _info.celNo);
_hunkPaletteOffset = READ_SCI11ENDIAN_UINT32(data + 6);
- byte *celHeader = data + _celHeaderOffset;
+ const byte *const celHeader = data + _celHeaderOffset;
_width = READ_SCI11ENDIAN_UINT16(celHeader);
_height = READ_SCI11ENDIAN_UINT16(celHeader + 2);
@@ -1012,8 +1057,8 @@ CelObjPic::CelObjPic(const GuiResourceId picId, const int16 celNo) {
_relativePosition.x = (int16)READ_SCI11ENDIAN_UINT16(celHeader + 38);
_relativePosition.y = (int16)READ_SCI11ENDIAN_UINT16(celHeader + 40);
- uint16 sizeFlag1 = READ_SCI11ENDIAN_UINT16(data + 10);
- uint16 sizeFlag2 = READ_SCI11ENDIAN_UINT16(data + 12);
+ const uint16 sizeFlag1 = READ_SCI11ENDIAN_UINT16(data + 10);
+ const uint16 sizeFlag2 = READ_SCI11ENDIAN_UINT16(data + 12);
if (sizeFlag2) {
_scaledWidth = sizeFlag1;
@@ -1032,7 +1077,7 @@ CelObjPic::CelObjPic(const GuiResourceId picId, const int16 celNo) {
if (celHeader[10] & 128) {
// NOTE: This is correct according to SCI2.1/SQ6/DOS;
// the engine re-reads the byte value as a word value
- uint16 flags = READ_SCI11ENDIAN_UINT16(celHeader + 10);
+ const uint16 flags = READ_SCI11ENDIAN_UINT16(celHeader + 10);
_transparent = flags & 1 ? true : false;
_remap = flags & 2 ? true : false;
} else {
@@ -1047,8 +1092,8 @@ CelObjPic::CelObjPic(const GuiResourceId picId, const int16 celNo) {
}
bool CelObjPic::analyzeUncompressedForSkip() const {
- byte *resource = getResPointer();
- byte *pixels = resource + READ_SCI11ENDIAN_UINT32(resource + _celHeaderOffset + 24);
+ const byte *const resource = getResPointer();
+ const byte *const pixels = resource + READ_SCI11ENDIAN_UINT32(resource + _celHeaderOffset + 24);
for (int i = 0; i < _width * _height; ++i) {
uint8 pixel = pixels[i];
if (pixel == _transparentColor) {
@@ -1060,7 +1105,7 @@ bool CelObjPic::analyzeUncompressedForSkip() const {
}
void CelObjPic::draw(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition, const bool mirrorX) {
- Ratio square;
+ const Ratio square;
_drawMirrored = mirrorX;
drawTo(target, targetRect, scaledPosition, square, square);
}
@@ -1088,15 +1133,21 @@ CelObjMem::CelObjMem(const reg_t bitmapObject) {
_celHeaderOffset = 0;
_transparent = true;
- SciBitmap &bitmap = *g_sci->getEngineState()->_segMan->lookupBitmap(bitmapObject);
- _width = bitmap.getWidth();
- _height = bitmap.getHeight();
- _displace = bitmap.getDisplace();
- _transparentColor = bitmap.getSkipColor();
- _scaledWidth = bitmap.getScaledWidth();
- _scaledHeight = bitmap.getScaledHeight();
- _hunkPaletteOffset = bitmap.getHunkPaletteOffset();
- _remap = bitmap.getRemap();
+ SciBitmap *bitmap = g_sci->getEngineState()->_segMan->lookupBitmap(bitmapObject);
+
+ // NOTE: SSCI did no error checking here at all.
+ if (!bitmap) {
+ error("Bitmap %04x:%04x not found", PRINT_REG(bitmapObject));
+ }
+
+ _width = bitmap->getWidth();
+ _height = bitmap->getHeight();
+ _displace = bitmap->getDisplace();
+ _transparentColor = bitmap->getSkipColor();
+ _scaledWidth = bitmap->getScaledWidth();
+ _scaledHeight = bitmap->getScaledHeight();
+ _hunkPaletteOffset = bitmap->getHunkPaletteOffset();
+ _remap = bitmap->getRemap();
}
CelObjMem *CelObjMem::duplicate() const {
diff --git a/engines/sci/graphics/celobj32.h b/engines/sci/graphics/celobj32.h
index eb6ce3a3c9..21e86d03e0 100644
--- a/engines/sci/graphics/celobj32.h
+++ b/engines/sci/graphics/celobj32.h
@@ -147,7 +147,7 @@ struct CelScalerTable {
* the correct column to read from the source bitmap
* when drawing a scaled version of the source bitmap.
*/
- int valuesX[1024];
+ int valuesX[4096];
/**
* The ratio used to generate the x-values.
@@ -159,7 +159,7 @@ struct CelScalerTable {
* the correct row to read from a source bitmap when
* drawing a scaled version of the source bitmap.
*/
- int valuesY[1024];
+ int valuesY[4096];
/**
* The ratio used to generate the y-values.
@@ -400,7 +400,7 @@ public:
* Reads the pixel at the given coordinates. This method
* is valid only for CelObjView and CelObjPic.
*/
- virtual uint8 readPixel(uint16 x, uint16 y, bool mirrorX) const;
+ virtual uint8 readPixel(const uint16 x, const uint16 y, const bool mirrorX) const;
/**
* Submits the palette from this cel to the palette
@@ -505,6 +505,9 @@ public:
using CelObj::draw;
+ static int16 getNumLoops(const GuiResourceId viewId);
+ static int16 getNumCels(const GuiResourceId viewId, const int16 loopNo);
+
/**
* Draws the cel to the target buffer using the
* positioning, mirroring, and scaling information from
diff --git a/engines/sci/graphics/compare.cpp b/engines/sci/graphics/compare.cpp
index 130416ff60..36026a8134 100644
--- a/engines/sci/graphics/compare.cpp
+++ b/engines/sci/graphics/compare.cpp
@@ -37,7 +37,7 @@
namespace Sci {
-GfxCompare::GfxCompare(SegManager *segMan, GfxCache *cache, GfxScreen *screen, GfxCoordAdjuster *coordAdjuster)
+GfxCompare::GfxCompare(SegManager *segMan, GfxCache *cache, GfxScreen *screen, GfxCoordAdjuster16 *coordAdjuster)
: _segMan(segMan), _cache(cache), _screen(screen), _coordAdjuster(coordAdjuster) {
}
diff --git a/engines/sci/graphics/compare.h b/engines/sci/graphics/compare.h
index c7005980d0..dd65b90bea 100644
--- a/engines/sci/graphics/compare.h
+++ b/engines/sci/graphics/compare.h
@@ -34,7 +34,7 @@ class Screen;
*/
class GfxCompare {
public:
- GfxCompare(SegManager *segMan, GfxCache *cache, GfxScreen *screen, GfxCoordAdjuster *coordAdjuster);
+ GfxCompare(SegManager *segMan, GfxCache *cache, GfxScreen *screen, GfxCoordAdjuster16 *coordAdjuster);
~GfxCompare();
uint16 kernelOnControl(byte screenMask, const Common::Rect &rect);
@@ -50,7 +50,7 @@ private:
SegManager *_segMan;
GfxCache *_cache;
GfxScreen *_screen;
- GfxCoordAdjuster *_coordAdjuster;
+ GfxCoordAdjuster16 *_coordAdjuster;
uint16 isOnControl(uint16 screenMask, const Common::Rect &rect);
diff --git a/engines/sci/graphics/coordadjuster.cpp b/engines/sci/graphics/coordadjuster.cpp
index 93dff10382..2f22d191d0 100644
--- a/engines/sci/graphics/coordadjuster.cpp
+++ b/engines/sci/graphics/coordadjuster.cpp
@@ -32,9 +32,6 @@
namespace Sci {
-GfxCoordAdjuster::GfxCoordAdjuster() {
-}
-
GfxCoordAdjuster16::GfxCoordAdjuster16(GfxPorts *ports)
: _ports(ports) {
}
@@ -83,53 +80,4 @@ Common::Rect GfxCoordAdjuster16::pictureGetDisplayArea() {
return displayArea;
}
-#ifdef ENABLE_SCI32
-GfxCoordAdjuster32::GfxCoordAdjuster32(SegManager *segMan)
- : _segMan(segMan) {
- _scriptsRunningWidth = 0;
- _scriptsRunningHeight = 0;
-}
-
-GfxCoordAdjuster32::~GfxCoordAdjuster32() {
-}
-
-void GfxCoordAdjuster32::kernelGlobalToLocal(int16 &x, int16 &y, reg_t planeObject) {
- uint16 planeTop = readSelectorValue(_segMan, planeObject, SELECTOR(top));
- uint16 planeLeft = readSelectorValue(_segMan, planeObject, SELECTOR(left));
-
- y -= planeTop;
- x -= planeLeft;
-}
-void GfxCoordAdjuster32::kernelLocalToGlobal(int16 &x, int16 &y, reg_t planeObject) {
- uint16 planeTop = readSelectorValue(_segMan, planeObject, SELECTOR(top));
- uint16 planeLeft = readSelectorValue(_segMan, planeObject, SELECTOR(left));
-
- x += planeLeft;
- y += planeTop;
-}
-
-void GfxCoordAdjuster32::setScriptsResolution(uint16 width, uint16 height) {
- _scriptsRunningWidth = width;
- _scriptsRunningHeight = height;
-}
-
-void GfxCoordAdjuster32::fromDisplayToScript(int16 &y, int16 &x) {
- y = ((y * _scriptsRunningHeight) / g_sci->_gfxScreen->getHeight());
- x = ((x * _scriptsRunningWidth) / g_sci->_gfxScreen->getWidth());
-}
-
-void GfxCoordAdjuster32::fromScriptToDisplay(int16 &y, int16 &x) {
- y = ((y * g_sci->_gfxScreen->getHeight()) / _scriptsRunningHeight);
- x = ((x * g_sci->_gfxScreen->getWidth()) / _scriptsRunningWidth);
-}
-
-void GfxCoordAdjuster32::pictureSetDisplayArea(Common::Rect displayArea) {
- _pictureDisplayArea = displayArea;
-}
-
-Common::Rect GfxCoordAdjuster32::pictureGetDisplayArea() {
- return _pictureDisplayArea;
-}
-#endif
-
} // End of namespace Sci
diff --git a/engines/sci/graphics/coordadjuster.h b/engines/sci/graphics/coordadjuster.h
index cb0227fbe4..f7ebd3ec75 100644
--- a/engines/sci/graphics/coordadjuster.h
+++ b/engines/sci/graphics/coordadjuster.h
@@ -35,27 +35,7 @@ class GfxPorts;
* most of the time sci32 doesn't do any coordinate adjustment at all
* sci16 does a lot of port adjustment on given coordinates
*/
-class GfxCoordAdjuster {
-public:
- GfxCoordAdjuster();
- virtual ~GfxCoordAdjuster() { }
-
- virtual void kernelGlobalToLocal(int16 &x, int16 &y, reg_t planeObject = NULL_REG) { }
- virtual void kernelLocalToGlobal(int16 &x, int16 &y, reg_t planeObject = NULL_REG) { }
-
- virtual Common::Rect onControl(Common::Rect rect) { return rect; }
- virtual void setCursorPos(Common::Point &pos) { }
- virtual void moveCursor(Common::Point &pos) { }
-
- virtual void setScriptsResolution(uint16 width, uint16 height) { }
- virtual void fromScriptToDisplay(int16 &y, int16 &x) { }
- virtual void fromDisplayToScript(int16 &y, int16 &x) { }
-
- virtual Common::Rect pictureGetDisplayArea() { return Common::Rect(0, 0); }
-private:
-};
-
-class GfxCoordAdjuster16 : public GfxCoordAdjuster {
+class GfxCoordAdjuster16 {
public:
GfxCoordAdjuster16(GfxPorts *ports);
~GfxCoordAdjuster16();
@@ -73,32 +53,6 @@ private:
GfxPorts *_ports;
};
-#ifdef ENABLE_SCI32
-class GfxCoordAdjuster32 : public GfxCoordAdjuster {
-public:
- GfxCoordAdjuster32(SegManager *segMan);
- ~GfxCoordAdjuster32();
-
- void kernelGlobalToLocal(int16 &x, int16 &y, reg_t planeObject = NULL_REG);
- void kernelLocalToGlobal(int16 &x, int16 &y, reg_t planeObject = NULL_REG);
-
- void setScriptsResolution(uint16 width, uint16 height);
- void fromScriptToDisplay(int16 &y, int16 &x);
- void fromDisplayToScript(int16 &y, int16 &x);
-
- void pictureSetDisplayArea(Common::Rect displayArea);
- Common::Rect pictureGetDisplayArea();
-
-private:
- SegManager *_segMan;
-
- Common::Rect _pictureDisplayArea;
-
- uint16 _scriptsRunningWidth;
- uint16 _scriptsRunningHeight;
-};
-#endif
-
} // End of namespace Sci
#endif
diff --git a/engines/sci/graphics/cursor.cpp b/engines/sci/graphics/cursor.cpp
index f5dd473959..c3229121c8 100644
--- a/engines/sci/graphics/cursor.cpp
+++ b/engines/sci/graphics/cursor.cpp
@@ -80,7 +80,7 @@ GfxCursor::~GfxCursor() {
kernelClearZoomZone();
}
-void GfxCursor::init(GfxCoordAdjuster *coordAdjuster, EventManager *event) {
+void GfxCursor::init(GfxCoordAdjuster16 *coordAdjuster, EventManager *event) {
_coordAdjuster = coordAdjuster;
_event = event;
}
diff --git a/engines/sci/graphics/cursor.h b/engines/sci/graphics/cursor.h
index 5125469cfe..c57d9dab52 100644
--- a/engines/sci/graphics/cursor.h
+++ b/engines/sci/graphics/cursor.h
@@ -55,7 +55,7 @@ public:
GfxCursor(ResourceManager *resMan, GfxPalette *palette, GfxScreen *screen);
~GfxCursor();
- void init(GfxCoordAdjuster *coordAdjuster, EventManager *event);
+ void init(GfxCoordAdjuster16 *coordAdjuster, EventManager *event);
void kernelShow();
void kernelHide();
@@ -103,7 +103,7 @@ private:
ResourceManager *_resMan;
GfxScreen *_screen;
GfxPalette *_palette;
- GfxCoordAdjuster *_coordAdjuster;
+ GfxCoordAdjuster16 *_coordAdjuster;
EventManager *_event;
int _upscaledHires;
diff --git a/engines/sci/graphics/cursor32.cpp b/engines/sci/graphics/cursor32.cpp
new file mode 100644
index 0000000000..014b617c74
--- /dev/null
+++ b/engines/sci/graphics/cursor32.cpp
@@ -0,0 +1,396 @@
+/* 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.
+ *
+ */
+
+#include "common/rational.h" // for Rational, operator*
+#include "common/system.h" // for OSystem, g_system
+#include "graphics/cursorman.h" // for CursorMan
+#include "sci/graphics/celobj32.h" // for CelObjView, CelInfo32, Ratio
+#include "sci/graphics/cursor32.h"
+#include "sci/graphics/frameout.h" // for GfxFrameout
+
+namespace Sci {
+
+GfxCursor32::GfxCursor32() :
+ _hideCount(0),
+ _position(0, 0),
+ _writeToVMAP(false) {
+ CursorMan.showMouse(false);
+}
+
+void GfxCursor32::init(const Buffer &vmap) {
+ _vmap = vmap;
+ _vmapRegion.rect = Common::Rect(_vmap.screenWidth, _vmap.screenHeight);
+ _vmapRegion.data = (byte *)_vmap.getPixels();
+ _restrictedArea = _vmapRegion.rect;
+}
+
+GfxCursor32::~GfxCursor32() {
+ CursorMan.showMouse(true);
+ free(_cursor.data);
+ free(_cursorBack.data);
+ free(_drawBuff1.data);
+ free(_drawBuff2.data);
+ free(_savedVmapRegion.data);
+}
+
+void GfxCursor32::hide() {
+ if (_hideCount++) {
+ return;
+ }
+
+ if (!_cursorBack.rect.isEmpty()) {
+ drawToHardware(_cursorBack);
+ }
+}
+
+void GfxCursor32::revealCursor() {
+ _cursorBack.rect = _cursor.rect;
+ _cursorBack.rect.clip(_vmapRegion.rect);
+ if (_cursorBack.rect.isEmpty()) {
+ return;
+ }
+
+ readVideo(_cursorBack);
+ _drawBuff1.rect = _cursor.rect;
+ copy(_drawBuff1, _cursorBack);
+ paint(_drawBuff1, _cursor);
+ drawToHardware(_drawBuff1);
+}
+
+void GfxCursor32::paint(DrawRegion &target, const DrawRegion &source) {
+ if (source.rect.isEmpty()) {
+ return;
+ }
+
+ Common::Rect drawRect(source.rect);
+ drawRect.clip(target.rect);
+ if (drawRect.isEmpty()) {
+ return;
+ }
+
+ const int16 sourceXOffset = drawRect.left - source.rect.left;
+ const int16 sourceYOffset = drawRect.top - source.rect.top;
+ const int16 drawRectWidth = drawRect.width();
+ const int16 drawRectHeight = drawRect.height();
+
+ byte *targetPixel = target.data + ((drawRect.top - target.rect.top) * target.rect.width()) + (drawRect.left - target.rect.left);
+ const byte *sourcePixel = source.data + (sourceYOffset * source.rect.width()) + sourceXOffset;
+ const uint8 skipColor = source.skipColor;
+
+ const int16 sourceStride = source.rect.width() - drawRectWidth;
+ const int16 targetStride = target.rect.width() - drawRectWidth;
+
+ for (int16 y = 0; y < drawRectHeight; ++y) {
+ for (int16 x = 0; x < drawRectWidth; ++x) {
+ if (*sourcePixel != skipColor) {
+ *targetPixel = *sourcePixel;
+ }
+ ++targetPixel;
+ ++sourcePixel;
+ }
+ sourcePixel += sourceStride;
+ targetPixel += targetStride;
+ }
+}
+
+void GfxCursor32::drawToHardware(const DrawRegion &source) {
+ Common::Rect drawRect(source.rect);
+ drawRect.clip(_vmapRegion.rect);
+ const int16 sourceXOffset = drawRect.left - source.rect.left;
+ const int16 sourceYOffset = drawRect.top - source.rect.top;
+ byte *sourcePixel = source.data + (sourceYOffset * source.rect.width()) + sourceXOffset;
+
+ g_system->copyRectToScreen(sourcePixel, source.rect.width(), drawRect.left, drawRect.top, drawRect.width(), drawRect.height());
+}
+
+void GfxCursor32::unhide() {
+ if (_hideCount == 0 || --_hideCount) {
+ return;
+ }
+
+ _cursor.rect.moveTo(_position.x - _hotSpot.x, _position.y - _hotSpot.y);
+ revealCursor();
+}
+
+void GfxCursor32::show() {
+ if (_hideCount) {
+ _hideCount = 0;
+ _cursor.rect.moveTo(_position.x - _hotSpot.x, _position.y - _hotSpot.y);
+ revealCursor();
+ }
+}
+
+void GfxCursor32::setRestrictedArea(const Common::Rect &rect) {
+ _restrictedArea = rect;
+
+ const int16 screenWidth = g_sci->_gfxFrameout->getCurrentBuffer().screenWidth;
+ const int16 screenHeight = g_sci->_gfxFrameout->getCurrentBuffer().screenHeight;
+ const int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth;
+ const int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight;
+
+ mulru(_restrictedArea, Ratio(screenWidth, scriptWidth), Ratio(screenHeight, scriptHeight), 0);
+
+ if (_position.x < rect.left) {
+ _position.x = rect.left;
+ }
+ if (_position.x >= rect.right) {
+ _position.x = rect.right - 1;
+ }
+ if (_position.y < rect.top) {
+ _position.y = rect.top;
+ }
+ if (_position.y >= rect.bottom) {
+ _position.y = rect.bottom - 1;
+ }
+
+ g_system->warpMouse(_position.x, _position.y);
+}
+
+void GfxCursor32::clearRestrictedArea() {
+ _restrictedArea = _vmapRegion.rect;
+}
+
+void GfxCursor32::setView(const GuiResourceId viewId, const int16 loopNo, const int16 celNo) {
+ hide();
+
+ _cursorInfo.resourceId = viewId;
+ _cursorInfo.loopNo = loopNo;
+ _cursorInfo.celNo = celNo;
+
+ if (viewId != -1) {
+ CelObjView view(viewId, loopNo, celNo);
+
+ _hotSpot = view._displace;
+ _width = view._width;
+ _height = view._height;
+
+ // SSCI never increased the size of cursors, but some of the cursors
+ // in early SCI32 games were designed for low-resolution display mode
+ // and so are kind of hard to pick out when running in high-resolution
+ // mode.
+ // To address this, we make some slight adjustments to cursor display
+ // in these early games:
+ // GK1: All the cursors are increased in size since they all appear to
+ // be designed for low-res display.
+ // PQ4: We only make the cursors bigger if they are above a set
+ // threshold size because inventory items usually have a
+ // high-resolution cursor representation.
+ bool pixelDouble = false;
+ if (g_sci->_gfxFrameout->_isHiRes &&
+ (g_sci->getGameId() == GID_GK1 ||
+ (g_sci->getGameId() == GID_PQ4 && _width <= 22 && _height <= 22))) {
+
+ _width *= 2;
+ _height *= 2;
+ _hotSpot.x *= 2;
+ _hotSpot.y *= 2;
+ pixelDouble = true;
+ }
+
+ _cursor.data = (byte *)realloc(_cursor.data, _width * _height);
+ _cursor.rect = Common::Rect(_width, _height);
+ memset(_cursor.data, 255, _width * _height);
+ _cursor.skipColor = 255;
+
+ Buffer target(_width, _height, _cursor.data);
+ if (pixelDouble) {
+ view.draw(target, _cursor.rect, Common::Point(0, 0), false, 2, 2);
+ } else {
+ view.draw(target, _cursor.rect, Common::Point(0, 0), false);
+ }
+ } else {
+ _hotSpot = Common::Point(0, 0);
+ _width = _height = 1;
+ _cursor.data = (byte *)realloc(_cursor.data, _width * _height);
+ _cursor.rect = Common::Rect(_width, _height);
+ *_cursor.data = _cursor.skipColor;
+ _cursorBack.rect = _cursor.rect;
+ _cursorBack.rect.clip(_vmapRegion.rect);
+ if (!_cursorBack.rect.isEmpty()) {
+ readVideo(_cursorBack);
+ }
+ }
+
+ _cursorBack.data = (byte *)realloc(_cursorBack.data, _width * _height);
+ _drawBuff1.data = (byte *)realloc(_drawBuff1.data, _width * _height);
+ _drawBuff2.data = (byte *)realloc(_drawBuff2.data, _width * _height * 4);
+ _savedVmapRegion.data = (byte *)realloc(_savedVmapRegion.data, _width * _height);
+
+ unhide();
+}
+
+void GfxCursor32::readVideo(DrawRegion &target) {
+ if (g_sci->_gfxFrameout->_frameNowVisible) {
+ copy(target, _vmapRegion);
+ } else {
+ // NOTE: SSCI would read the background for the cursor directly out of
+ // video memory here, but as far as can be determined, this does not
+ // seem to actually be necessary for proper cursor rendering
+ }
+}
+
+void GfxCursor32::copy(DrawRegion &target, const DrawRegion &source) {
+ if (source.rect.isEmpty()) {
+ return;
+ }
+
+ Common::Rect drawRect(source.rect);
+ drawRect.clip(target.rect);
+ if (drawRect.isEmpty()) {
+ return;
+ }
+
+ const int16 sourceXOffset = drawRect.left - source.rect.left;
+ const int16 sourceYOffset = drawRect.top - source.rect.top;
+ const int16 drawWidth = drawRect.width();
+ const int16 drawHeight = drawRect.height();
+
+ byte *targetPixel = target.data + ((drawRect.top - target.rect.top) * target.rect.width()) + (drawRect.left - target.rect.left);
+ const byte *sourcePixel = source.data + (sourceYOffset * source.rect.width()) + sourceXOffset;
+
+ const int16 sourceStride = source.rect.width();
+ const int16 targetStride = target.rect.width();
+
+ for (int y = 0; y < drawHeight; ++y) {
+ memcpy(targetPixel, sourcePixel, drawWidth);
+ targetPixel += targetStride;
+ sourcePixel += sourceStride;
+ }
+}
+
+void GfxCursor32::setPosition(const Common::Point &position) {
+ const int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth;
+ const int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight;
+ const int16 screenWidth = g_sci->_gfxFrameout->getCurrentBuffer().screenWidth;
+ const int16 screenHeight = g_sci->_gfxFrameout->getCurrentBuffer().screenHeight;
+
+ _position.x = (position.x * Ratio(screenWidth, scriptWidth)).toInt();
+ _position.y = (position.y * Ratio(screenHeight, scriptHeight)).toInt();
+
+ g_system->warpMouse(_position.x, _position.y);
+}
+
+void GfxCursor32::gonnaPaint(Common::Rect paintRect) {
+ if (!_hideCount && !_writeToVMAP && !_cursorBack.rect.isEmpty()) {
+ paintRect.left &= ~3;
+ paintRect.right |= 3;
+ if (_cursorBack.rect.intersects(paintRect)) {
+ _writeToVMAP = true;
+ }
+ }
+}
+
+void GfxCursor32::paintStarting() {
+ if (_writeToVMAP) {
+ _savedVmapRegion.rect = _cursor.rect;
+ copy(_savedVmapRegion, _vmapRegion);
+ paint(_vmapRegion, _cursor);
+ }
+}
+
+void GfxCursor32::donePainting() {
+ if (_writeToVMAP) {
+ copy(_vmapRegion, _savedVmapRegion);
+ _savedVmapRegion.rect = Common::Rect();
+ _writeToVMAP = false;
+ }
+
+ if (!_hideCount && !_cursorBack.rect.isEmpty()) {
+ copy(_cursorBack, _vmapRegion);
+ }
+}
+
+void GfxCursor32::deviceMoved(Common::Point &position) {
+ if (position.x < _restrictedArea.left) {
+ position.x = _restrictedArea.left;
+ }
+ if (position.x >= _restrictedArea.right) {
+ position.x = _restrictedArea.right - 1;
+ }
+ if (position.y < _restrictedArea.top) {
+ position.y = _restrictedArea.top;
+ }
+ if (position.y >= _restrictedArea.bottom) {
+ position.y = _restrictedArea.bottom - 1;
+ }
+
+ _position = position;
+
+ g_system->warpMouse(position.x, position.y);
+ move();
+}
+
+void GfxCursor32::move() {
+ if (_hideCount) {
+ return;
+ }
+
+ // Cursor moved onto the screen after being offscreen
+ _cursor.rect.moveTo(_position.x - _hotSpot.x, _position.y - _hotSpot.y);
+ if (_cursorBack.rect.isEmpty()) {
+ revealCursor();
+ return;
+ }
+
+ // Cursor moved offscreen
+ if (!_cursor.rect.intersects(_vmapRegion.rect)) {
+ drawToHardware(_cursorBack);
+ return;
+ }
+
+ if (!_cursor.rect.intersects(_cursorBack.rect)) {
+ // Cursor moved to a completely different part of the screen
+ _drawBuff1.rect = _cursor.rect;
+ _drawBuff1.rect.clip(_vmapRegion.rect);
+ readVideo(_drawBuff1);
+
+ _drawBuff2.rect = _drawBuff1.rect;
+ copy(_drawBuff2, _drawBuff1);
+
+ paint(_drawBuff1, _cursor);
+ drawToHardware(_drawBuff1);
+
+ drawToHardware(_cursorBack);
+
+ _cursorBack.rect = _cursor.rect;
+ _cursorBack.rect.clip(_vmapRegion.rect);
+ copy(_cursorBack, _drawBuff2);
+ } else {
+ // Cursor moved, but still overlaps the previous cursor location
+ Common::Rect mergedRect(_cursorBack.rect);
+ mergedRect.extend(_cursor.rect);
+ mergedRect.clip(_vmapRegion.rect);
+
+ _drawBuff2.rect = mergedRect;
+ readVideo(_drawBuff2);
+
+ copy(_drawBuff2, _cursorBack);
+
+ _cursorBack.rect = _cursor.rect;
+ _cursorBack.rect.clip(_vmapRegion.rect);
+ copy(_cursorBack, _drawBuff2);
+
+ paint(_drawBuff2, _cursor);
+ drawToHardware(_drawBuff2);
+ }
+}
+} // End of namespace Sci
diff --git a/engines/sci/graphics/cursor32.h b/engines/sci/graphics/cursor32.h
new file mode 100644
index 0000000000..d4745536b1
--- /dev/null
+++ b/engines/sci/graphics/cursor32.h
@@ -0,0 +1,250 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef SCI_GRAPHICS_CURSOR32_H
+#define SCI_GRAPHICS_CURSOR32_H
+
+#include "common/rect.h" // for Point, Rect
+#include "common/scummsys.h" // for int16, byte, uint8
+#include "common/serializer.h" // for Serializable, Serializer (ptr only)
+#include "sci/graphics/celobj32.h" // for CelInfo32
+#include "sci/graphics/helpers.h" // for GuiResourceId
+
+namespace Sci {
+
+class GfxCursor32 : Common::Serializable {
+public:
+ GfxCursor32();
+ ~GfxCursor32();
+
+ /**
+ * Initialises the cursor system with the given
+ * buffer to use as the output buffer for
+ * rendering the cursor.
+ */
+ void init(const Buffer &vmap);
+
+ /**
+ * Called when the hardware mouse moves.
+ */
+ void deviceMoved(Common::Point &position);
+
+ /**
+ * Called by GfxFrameout once for each show
+ * rectangle that is going to be drawn to
+ * hardware.
+ */
+ void gonnaPaint(Common::Rect paintRect);
+
+ /**
+ * Called by GfxFrameout when the rendering to
+ * hardware begins.
+ */
+ void paintStarting();
+
+ /**
+ * Called by GfxFrameout when the output buffer
+ * has finished rendering to hardware.
+ */
+ void donePainting();
+
+ /**
+ * Hides the cursor. Each call to `hide` will
+ * increment a hide counter, which must be
+ * returned to 0 before the cursor will be
+ * shown again.
+ */
+ void hide();
+
+ /**
+ * Shows the cursor, if the hide counter is
+ * returned to 0.
+ */
+ void unhide();
+
+ /**
+ * Shows the cursor regardless of the state of
+ * the hide counter.
+ */
+ void show();
+
+ /**
+ * Sets the view used to render the cursor.
+ */
+ void setView(const GuiResourceId viewId, const int16 loopNo, const int16 celNo);
+
+ /**
+ * Explicitly sets the position of the cursor,
+ * in game script coordinates.
+ */
+ void setPosition(const Common::Point &position);
+
+ /**
+ * Sets the region that the mouse is allowed
+ * to move within.
+ */
+ void setRestrictedArea(const Common::Rect &rect);
+
+ /**
+ * Removes restrictions on mouse movement.
+ */
+ void clearRestrictedArea();
+
+ virtual void saveLoadWithSerializer(Common::Serializer &ser);
+
+private:
+ struct DrawRegion {
+ Common::Rect rect;
+ byte *data;
+ uint8 skipColor;
+
+ DrawRegion() : rect(), data(nullptr) {}
+ };
+
+ /**
+ * Information about the current cursor.
+ * Used to restore cursor when loading a
+ * savegame.
+ */
+ CelInfo32 _cursorInfo;
+
+ /**
+ * Content behind the cursor? TODO
+ */
+ DrawRegion _cursorBack;
+
+ /**
+ * Scratch buffer.
+ */
+ DrawRegion _drawBuff1;
+
+ /**
+ * Scratch buffer 2.
+ */
+ DrawRegion _drawBuff2;
+
+ /**
+ * A draw region representing the current
+ * output buffer.
+ */
+ DrawRegion _vmapRegion;
+
+ /**
+ * The content behind the cursor in the
+ * output buffer.
+ */
+ DrawRegion _savedVmapRegion;
+
+ /**
+ * The cursor bitmap.
+ */
+ DrawRegion _cursor;
+
+ /**
+ * The width and height of the cursor,
+ * in screen coordinates.
+ */
+ int16 _width, _height;
+
+ /**
+ * The output buffer where the cursor is
+ * rendered.
+ */
+ Buffer _vmap;
+
+ /**
+ * The number of times the cursor has been
+ * hidden.
+ */
+ int _hideCount;
+
+ /**
+ * The rendered position of the cursor, in
+ * screen coordinates.
+ */
+ Common::Point _position;
+
+ /**
+ * The position of the cursor hot spot, relative
+ * to the cursor origin, in screen pixels.
+ */
+ Common::Point _hotSpot;
+
+ /**
+ * The area within which the cursor is allowed
+ * to move, in screen pixels.
+ */
+ Common::Rect _restrictedArea;
+
+ /**
+ * Indicates whether or not the cursor needs to
+ * be repainted on the output buffer due to a
+ * change of graphics in the area underneath the
+ * cursor.
+ */
+ bool _writeToVMAP;
+
+ /**
+ * Reads data from the output buffer or hardware
+ * to the given draw region.
+ */
+ void readVideo(DrawRegion &target);
+
+ /**
+ * Reads data from the output buffer to the
+ * given draw region.
+ */
+ void readVideoFromVmap(DrawRegion &target);
+
+ /**
+ * Copies pixel data from the given source to
+ * the given target.
+ */
+ void copy(DrawRegion &target, const DrawRegion &source);
+
+ /**
+ * Draws from the given source onto the given
+ * target, skipping pixels in the source that
+ * match the `skipColor` property.
+ */
+ void paint(DrawRegion &target, const DrawRegion &source);
+
+ /**
+ * Draws the cursor to the position it was
+ * drawn to prior to moving offscreen or being
+ * hidden by a call to `hide`.
+ */
+ void revealCursor();
+
+ /**
+ * Draws the given source to the output buffer.
+ */
+ void drawToHardware(const DrawRegion &source);
+
+ /**
+ * Renders the cursor at its new location.
+ */
+ void move();
+};
+
+} // End of namespace Sci
+#endif
diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp
index 21ffb5f937..4e0aa22669 100644
--- a/engines/sci/graphics/frameout.cpp
+++ b/engines/sci/graphics/frameout.cpp
@@ -29,6 +29,7 @@
#include "common/system.h"
#include "common/textconsole.h"
#include "engines/engine.h"
+#include "engines/util.h"
#include "graphics/palette.h"
#include "graphics/surface.h"
@@ -39,47 +40,53 @@
#include "sci/engine/selector.h"
#include "sci/engine/vm.h"
#include "sci/graphics/cache.h"
-#include "sci/graphics/coordadjuster.h"
#include "sci/graphics/compare.h"
+#include "sci/graphics/cursor32.h"
#include "sci/graphics/font.h"
-#include "sci/graphics/screen.h"
+#include "sci/graphics/frameout.h"
#include "sci/graphics/paint32.h"
#include "sci/graphics/palette32.h"
#include "sci/graphics/plane32.h"
#include "sci/graphics/remap32.h"
+#include "sci/graphics/screen.h"
#include "sci/graphics/screen_item32.h"
#include "sci/graphics/text32.h"
#include "sci/graphics/frameout.h"
-#include "sci/video/robot_decoder.h"
#include "sci/graphics/transitions32.h"
+#include "sci/graphics/video32.h"
namespace Sci {
-GfxFrameout::GfxFrameout(SegManager *segMan, ResourceManager *resMan, GfxCoordAdjuster *coordAdjuster, GfxScreen *screen, GfxPalette32 *palette, GfxTransitions32 *transitions) :
+GfxFrameout::GfxFrameout(SegManager *segMan, GfxPalette32 *palette, GfxTransitions32 *transitions, GfxCursor32 *cursor) :
_isHiRes(ConfMan.getBool("enable_high_resolution_graphics")),
_palette(palette),
- _resMan(resMan),
- _screen(screen),
+ _cursor(cursor),
_segMan(segMan),
_transitions(transitions),
_benchmarkingFinished(false),
_throttleFrameOut(true),
_throttleState(0),
- // TODO: Stop using _gfxScreen
- _currentBuffer(screen->getDisplayWidth(), screen->getDisplayHeight(), nullptr),
_remapOccurred(false),
_frameNowVisible(false),
- _screenRect(screen->getDisplayWidth(), screen->getDisplayHeight()),
_overdrawThreshold(0),
_palMorphIsOn(false) {
- _currentBuffer.setPixels(calloc(1, screen->getDisplayWidth() * screen->getDisplayHeight()));
-
- // QFG4 is the only SCI32 game that doesn't have a high-resolution toggle
+ // QFG4 is the only SCI32 game that doesn't have a high-resolution version
if (g_sci->getGameId() == GID_QFG4) {
_isHiRes = false;
}
+ if (g_sci->getGameId() == GID_PHANTASMAGORIA) {
+ _currentBuffer = Buffer(630, 450, nullptr);
+ } else if (_isHiRes) {
+ _currentBuffer = Buffer(640, 480, nullptr);
+ } else {
+ _currentBuffer = Buffer(320, 200, nullptr);
+ }
+ _currentBuffer.setPixels(calloc(1, _currentBuffer.screenWidth * _currentBuffer.screenHeight));
+ _screenRect = Common::Rect(_currentBuffer.screenWidth, _currentBuffer.screenHeight);
+ initGraphics(_currentBuffer.screenWidth, _currentBuffer.screenHeight, _isHiRes);
+
switch (g_sci->getGameId()) {
case GID_HOYLE5:
case GID_GK2:
@@ -96,20 +103,6 @@ GfxFrameout::GfxFrameout(SegManager *segMan, ResourceManager *resMan, GfxCoordAd
// default script width for other games is 320x200
break;
}
-
- // TODO: Nothing in the renderer really uses this. Currently,
- // the cursor renderer does, and kLocalToGlobal/kGlobalToLocal
- // do, but in the real engine (1) the cursor is handled in
- // frameOut, and (2) functions do a very simple lookup of the
- // plane and arithmetic with the plane's gameRect. In
- // principle, CoordAdjuster could be reused for
- // convertGameRectToPlaneRect, but it is not super clear yet
- // what the benefit would be to do that.
- _coordAdjuster = (GfxCoordAdjuster32 *)coordAdjuster;
-
- // TODO: Script resolution is hard-coded per game;
- // also this must be set or else the engine will crash
- _coordAdjuster->setScriptsResolution(_currentBuffer.scriptWidth, _currentBuffer.scriptHeight);
}
GfxFrameout::~GfxFrameout() {
@@ -493,10 +486,12 @@ void GfxFrameout::kernelAddPicAt(const reg_t planeObject, const GuiResourceId pi
#pragma mark Rendering
void GfxFrameout::frameOut(const bool shouldShowBits, const Common::Rect &eraseRect) {
-// TODO: Robot
-// if (_robot != nullptr) {
-// _robot.doRobot();
-// }
+ RobotDecoder &robotPlayer = g_sci->_video32->getRobotPlayer();
+ const bool robotIsActive = robotPlayer.getStatus() != RobotDecoder::kRobotStatusUninitialized;
+
+ if (robotIsActive) {
+ robotPlayer.doRobot();
+ }
// NOTE: The original engine allocated these as static arrays of 100
// pointers to ScreenItemList / RectList
@@ -534,10 +529,9 @@ void GfxFrameout::frameOut(const bool shouldShowBits, const Common::Rect &eraseR
drawScreenItemList(screenItemLists[i]);
}
-// TODO: Robot
-// if (_robot != nullptr) {
-// _robot->frameAlmostVisible();
-// }
+ if (robotIsActive) {
+ robotPlayer.frameAlmostVisible();
+ }
_palette->updateHardware(!shouldShowBits);
@@ -547,10 +541,9 @@ void GfxFrameout::frameOut(const bool shouldShowBits, const Common::Rect &eraseR
_frameNowVisible = true;
-// TODO: Robot
-// if (_robot != nullptr) {
-// robot->frameNowVisible();
-// }
+ if (robotIsActive) {
+ robotPlayer.frameNowVisible();
+ }
}
void GfxFrameout::palMorphFrameOut(const int8 *styleRanges, PlaneShowStyle *showStyle) {
@@ -559,7 +552,7 @@ void GfxFrameout::palMorphFrameOut(const int8 *styleRanges, PlaneShowStyle *show
int16 prevRoom = g_sci->getEngineState()->variables[VAR_GLOBAL][12].toSint16();
- Common::Rect rect(_screen->getDisplayWidth(), _screen->getDisplayHeight());
+ Common::Rect rect(_currentBuffer.screenWidth, _currentBuffer.screenHeight);
_showList.add(rect);
showBits();
@@ -1114,6 +1107,10 @@ void GfxFrameout::mergeToShowList(const Common::Rect &drawRect, RectList &showLi
}
void GfxFrameout::showBits() {
+ if (!_showList.size()) {
+ return;
+ }
+
for (RectList::const_iterator rect = _showList.begin(); rect != _showList.end(); ++rect) {
Common::Rect rounded(**rect);
// NOTE: SCI engine used BR-inclusive rects so used slightly
@@ -1121,13 +1118,10 @@ void GfxFrameout::showBits() {
// was always even.
rounded.left &= ~1;
rounded.right = (rounded.right + 1) & ~1;
-
- // TODO:
- // _cursor->GonnaPaint(rounded);
+ _cursor->gonnaPaint(rounded);
}
- // TODO:
- // _cursor->PaintStarting();
+ _cursor->paintStarting();
for (RectList::const_iterator rect = _showList.begin(); rect != _showList.end(); ++rect) {
Common::Rect rounded(**rect);
@@ -1149,8 +1143,7 @@ void GfxFrameout::showBits() {
g_system->copyRectToScreen(sourceBuffer, _currentBuffer.screenWidth, rounded.left, rounded.top, rounded.width(), rounded.height());
}
- // TODO:
- // _cursor->DonePainting();
+ _cursor->donePainting();
_showList.clear();
}
@@ -1266,6 +1259,30 @@ void GfxFrameout::showRect(const Common::Rect &rect) {
}
}
+void GfxFrameout::shakeScreen(int16 numShakes, const ShakeDirection direction) {
+ if (direction & kShakeHorizontal) {
+ // Used by QFG4 room 750
+ warning("TODO: Horizontal shake not implemented");
+ return;
+ }
+
+ while (numShakes--) {
+ if (direction & kShakeVertical) {
+ g_system->setShakePos(_isHiRes ? 8 : 4);
+ }
+
+ g_system->updateScreen();
+ g_sci->getEngineState()->wait(3);
+
+ if (direction & kShakeVertical) {
+ g_system->setShakePos(0);
+ }
+
+ g_system->updateScreen();
+ g_sci->getEngineState()->wait(3);
+ }
+}
+
#pragma mark -
#pragma mark Mouse cursor
@@ -1324,7 +1341,7 @@ bool GfxFrameout::isOnMe(const ScreenItem &screenItem, const Plane &plane, const
return true;
}
-void GfxFrameout::kernelSetNowSeen(const reg_t screenItemObject) const {
+bool GfxFrameout::kernelSetNowSeen(const reg_t screenItemObject) const {
const reg_t planeObject = readSelector(_segMan, screenItemObject, SELECTOR(plane));
Plane *plane = _planes.findByObject(planeObject);
@@ -1334,7 +1351,7 @@ void GfxFrameout::kernelSetNowSeen(const reg_t screenItemObject) const {
ScreenItem *screenItem = plane->_screenItemList.findByObject(screenItemObject);
if (screenItem == nullptr) {
- error("kSetNowSeen: Screen item %04x:%04x not found in plane %04x:%04x", PRINT_REG(screenItemObject), PRINT_REG(planeObject));
+ return false;
}
Common::Rect result = screenItem->getNowSeenRect(*plane);
@@ -1342,6 +1359,7 @@ void GfxFrameout::kernelSetNowSeen(const reg_t screenItemObject) const {
writeSelectorValue(_segMan, screenItemObject, SELECTOR(nsTop), result.top);
writeSelectorValue(_segMan, screenItemObject, SELECTOR(nsRight), result.right - 1);
writeSelectorValue(_segMan, screenItemObject, SELECTOR(nsBottom), result.bottom - 1);
+ return true;
}
void GfxFrameout::remapMarkRedraw() {
diff --git a/engines/sci/graphics/frameout.h b/engines/sci/graphics/frameout.h
index 012ecf9e64..e4caffd9e5 100644
--- a/engines/sci/graphics/frameout.h
+++ b/engines/sci/graphics/frameout.h
@@ -30,8 +30,7 @@ namespace Sci {
typedef Common::Array<DrawList> ScreenItemListList;
typedef Common::Array<RectList> EraseListList;
-class GfxCoordAdjuster32;
-class GfxScreen;
+class GfxCursor32;
class GfxTransitions32;
struct PlaneShowStyle;
@@ -41,16 +40,16 @@ struct PlaneShowStyle;
*/
class GfxFrameout {
private:
- GfxCoordAdjuster32 *_coordAdjuster;
+ GfxCursor32 *_cursor;
GfxPalette32 *_palette;
- ResourceManager *_resMan;
- GfxScreen *_screen;
SegManager *_segMan;
public:
- GfxFrameout(SegManager *segMan, ResourceManager *resMan, GfxCoordAdjuster *coordAdjuster, GfxScreen *screen, GfxPalette32 *palette, GfxTransitions32 *transitions);
+ GfxFrameout(SegManager *segMan, GfxPalette32 *palette, GfxTransitions32 *transitions, GfxCursor32 *cursor);
~GfxFrameout();
+ bool _isHiRes;
+
void clear();
void syncWithScripts(bool addElements); // this is what Game::restore does, only needed when our ScummVM dialogs are patched in
void run();
@@ -111,7 +110,7 @@ public:
void kernelAddScreenItem(const reg_t object);
void kernelUpdateScreenItem(const reg_t object);
void kernelDeleteScreenItem(const reg_t object);
- void kernelSetNowSeen(const reg_t screenItemObject) const;
+ bool kernelSetNowSeen(const reg_t screenItemObject) const;
#pragma mark -
#pragma mark Planes
@@ -196,13 +195,6 @@ private:
bool _remapOccurred;
/**
- * Whether or not the data in the current buffer is what
- * is visible to the user. During rendering updates,
- * this flag is set to false.
- */
- bool _frameNowVisible;
-
- /**
* TODO: Document
* TODO: Depending upon if the engine ever modifies this
* rect, it may be stupid to store it separately instead
@@ -308,7 +300,12 @@ private:
}
public:
- bool _isHiRes;
+ /**
+ * Whether or not the data in the current buffer is what
+ * is visible to the user. During rendering updates,
+ * this flag is set to false.
+ */
+ bool _frameNowVisible;
/**
* Whether palMorphFrameOut should be used instead of
@@ -366,6 +363,11 @@ public:
*/
void showRect(const Common::Rect &rect);
+ /**
+ * Shakes the screen.
+ */
+ void shakeScreen(const int16 numShakes, const ShakeDirection direction);
+
#pragma mark -
#pragma mark Mouse cursor
private:
diff --git a/engines/sci/graphics/helpers.h b/engines/sci/graphics/helpers.h
index 3fcc83c5e2..1da3749c90 100644
--- a/engines/sci/graphics/helpers.h
+++ b/engines/sci/graphics/helpers.h
@@ -40,8 +40,10 @@ namespace Sci {
#define MAX_CACHED_FONTS 20
#define MAX_CACHED_VIEWS 50
-#define SCI_SHAKE_DIRECTION_VERTICAL 1
-#define SCI_SHAKE_DIRECTION_HORIZONTAL 2
+enum ShakeDirection {
+ kShakeVertical = 1,
+ kShakeHorizontal = 2
+};
typedef int GuiResourceId; // is a resource-number and -1 means no parameter given
diff --git a/engines/sci/graphics/paint16.cpp b/engines/sci/graphics/paint16.cpp
index 6004e9ce7a..91817d4060 100644
--- a/engines/sci/graphics/paint16.cpp
+++ b/engines/sci/graphics/paint16.cpp
@@ -41,7 +41,7 @@
namespace Sci {
-GfxPaint16::GfxPaint16(ResourceManager *resMan, SegManager *segMan, GfxCache *cache, GfxPorts *ports, GfxCoordAdjuster *coordAdjuster, GfxScreen *screen, GfxPalette *palette, GfxTransitions *transitions, AudioPlayer *audio)
+GfxPaint16::GfxPaint16(ResourceManager *resMan, SegManager *segMan, GfxCache *cache, GfxPorts *ports, GfxCoordAdjuster16 *coordAdjuster, GfxScreen *screen, GfxPalette *palette, GfxTransitions *transitions, AudioPlayer *audio)
: _resMan(resMan), _segMan(segMan), _cache(cache), _ports(ports),
_coordAdjuster(coordAdjuster), _screen(screen), _palette(palette),
_transitions(transitions), _audio(audio), _EGAdrawingVisualize(false) {
diff --git a/engines/sci/graphics/paint16.h b/engines/sci/graphics/paint16.h
index 317388b2df..6fc9cbbdfc 100644
--- a/engines/sci/graphics/paint16.h
+++ b/engines/sci/graphics/paint16.h
@@ -36,7 +36,7 @@ class GfxView;
*/
class GfxPaint16 {
public:
- GfxPaint16(ResourceManager *resMan, SegManager *segMan, GfxCache *cache, GfxPorts *ports, GfxCoordAdjuster *coordAdjuster, GfxScreen *screen, GfxPalette *palette, GfxTransitions *transitions, AudioPlayer *audio);
+ GfxPaint16(ResourceManager *resMan, SegManager *segMan, GfxCache *cache, GfxPorts *ports, GfxCoordAdjuster16 *coordAdjuster, GfxScreen *screen, GfxPalette *palette, GfxTransitions *transitions, AudioPlayer *audio);
~GfxPaint16();
void init(GfxAnimate *animate, GfxText16 *text16);
@@ -91,7 +91,7 @@ private:
GfxAnimate *_animate;
GfxCache *_cache;
GfxPorts *_ports;
- GfxCoordAdjuster *_coordAdjuster;
+ GfxCoordAdjuster16 *_coordAdjuster;
GfxScreen *_screen;
GfxPalette *_palette;
GfxText16 *_text16;
diff --git a/engines/sci/graphics/picture.cpp b/engines/sci/graphics/picture.cpp
index 2eab391afd..0025b24476 100644
--- a/engines/sci/graphics/picture.cpp
+++ b/engines/sci/graphics/picture.cpp
@@ -35,7 +35,7 @@ namespace Sci {
//#define DEBUG_PICTURE_DRAW
-GfxPicture::GfxPicture(ResourceManager *resMan, GfxCoordAdjuster *coordAdjuster, GfxPorts *ports, GfxScreen *screen, GfxPalette *palette, GuiResourceId resourceId, bool EGAdrawingVisualize)
+GfxPicture::GfxPicture(ResourceManager *resMan, GfxCoordAdjuster16 *coordAdjuster, GfxPorts *ports, GfxScreen *screen, GfxPalette *palette, GuiResourceId resourceId, bool EGAdrawingVisualize)
: _resMan(resMan), _coordAdjuster(coordAdjuster), _ports(ports), _screen(screen), _palette(palette), _resourceId(resourceId), _EGAdrawingVisualize(EGAdrawingVisualize) {
assert(resourceId != -1);
initData(resourceId);
diff --git a/engines/sci/graphics/picture.h b/engines/sci/graphics/picture.h
index 942fa0f107..1be1ae3004 100644
--- a/engines/sci/graphics/picture.h
+++ b/engines/sci/graphics/picture.h
@@ -38,7 +38,7 @@ enum {
class GfxPorts;
class GfxScreen;
class GfxPalette;
-class GfxCoordAdjuster;
+class GfxCoordAdjuster16;
class ResourceManager;
class Resource;
@@ -48,7 +48,7 @@ class Resource;
*/
class GfxPicture {
public:
- GfxPicture(ResourceManager *resMan, GfxCoordAdjuster *coordAdjuster, GfxPorts *ports, GfxScreen *screen, GfxPalette *palette, GuiResourceId resourceId, bool EGAdrawingVisualize = false);
+ GfxPicture(ResourceManager *resMan, GfxCoordAdjuster16 *coordAdjuster, GfxPorts *ports, GfxScreen *screen, GfxPalette *palette, GuiResourceId resourceId, bool EGAdrawingVisualize = false);
~GfxPicture();
GuiResourceId getResourceId();
@@ -84,7 +84,7 @@ private:
void vectorPatternTexturedCircle(Common::Rect box, byte size, byte color, byte prio, byte control, byte texture);
ResourceManager *_resMan;
- GfxCoordAdjuster *_coordAdjuster;
+ GfxCoordAdjuster16 *_coordAdjuster;
GfxPorts *_ports;
GfxScreen *_screen;
GfxPalette *_palette;
diff --git a/engines/sci/graphics/screen.cpp b/engines/sci/graphics/screen.cpp
index c977a93817..601ab9f09f 100644
--- a/engines/sci/graphics/screen.cpp
+++ b/engines/sci/graphics/screen.cpp
@@ -53,12 +53,6 @@ GfxScreen::GfxScreen(ResourceManager *resMan) : _resMan(resMan) {
if ((g_sci->getPlatform() == Common::kPlatformWindows) || (g_sci->forceHiresGraphics())) {
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;
- if (g_sci->getGameId() == GID_PQ4)
- _upscaledHires = GFX_SCREEN_UPSCALED_640x480;
-#endif
}
// Japanese versions of games use hi-res font on upscaled version of the game.
@@ -90,28 +84,11 @@ GfxScreen::GfxScreen(ResourceManager *resMan) : _resMan(resMan) {
}
}
-#ifdef ENABLE_SCI32
- // GK1 Mac uses a 640x480 resolution too
- if (g_sci->getPlatform() == Common::kPlatformMacintosh) {
- if (g_sci->getGameId() == GID_GK1)
- _upscaledHires = GFX_SCREEN_UPSCALED_640x480;
- }
-#endif
-
if (_resMan->detectHires()) {
_scriptWidth = 640;
_scriptHeight = 480;
}
-#ifdef ENABLE_SCI32
- // Phantasmagoria 1 effectively outputs 630x450
- // Coordinate translation has to use this resolution as well
- if (g_sci->getGameId() == GID_PHANTASMAGORIA) {
- _width = 630;
- _height = 450;
- }
-#endif
-
// if not yet set, set those to script-width/height
if (!_width)
_width = _scriptWidth;
@@ -632,13 +609,13 @@ void GfxScreen::setVerticalShakePos(uint16 shakePos) {
void GfxScreen::kernelShakeScreen(uint16 shakeCount, uint16 directions) {
while (shakeCount--) {
- if (directions & SCI_SHAKE_DIRECTION_VERTICAL)
+ if (directions & kShakeVertical)
setVerticalShakePos(10);
// TODO: horizontal shakes
g_system->updateScreen();
g_sci->getEngineState()->wait(3);
- if (directions & SCI_SHAKE_DIRECTION_VERTICAL)
+ if (directions & kShakeVertical)
setVerticalShakePos(0);
g_system->updateScreen();
diff --git a/engines/sci/graphics/screen_item32.h b/engines/sci/graphics/screen_item32.h
index 3d9d5ef3d7..4221c0ea52 100644
--- a/engines/sci/graphics/screen_item32.h
+++ b/engines/sci/graphics/screen_item32.h
@@ -31,6 +31,7 @@ namespace Sci {
enum ScaleSignals32 {
kScaleSignalNone = 0,
+ // TODO: rename to 'manual'
kScaleSignalDoScaling32 = 1, // enables scaling when drawing that cel (involves scaleX and scaleY)
kScaleSignalUseVanishingPoint = 2,
// TODO: Is this actually a thing? I have not seen it and
diff --git a/engines/sci/graphics/text16.cpp b/engines/sci/graphics/text16.cpp
index b0f2c52791..cb6e614657 100644
--- a/engines/sci/graphics/text16.cpp
+++ b/engines/sci/graphics/text16.cpp
@@ -633,7 +633,7 @@ reg_t GfxText16::allocAndFillReferenceRectArray() {
if (rectCount) {
reg_t rectArray;
byte *rectArrayPtr = g_sci->getEngineState()->_segMan->allocDynmem(4 * 2 * (rectCount + 1), "text code reference rects", &rectArray);
- GfxCoordAdjuster *coordAdjuster = g_sci->_gfxCoordAdjuster;
+ GfxCoordAdjuster16 *coordAdjuster = g_sci->_gfxCoordAdjuster;
for (uint curRect = 0; curRect < rectCount; curRect++) {
coordAdjuster->kernelLocalToGlobal(_codeRefRects[curRect].left, _codeRefRects[curRect].top);
coordAdjuster->kernelLocalToGlobal(_codeRefRects[curRect].right, _codeRefRects[curRect].bottom);
diff --git a/engines/sci/graphics/transitions32.cpp b/engines/sci/graphics/transitions32.cpp
index bceb0fa84d..37f608da85 100644
--- a/engines/sci/graphics/transitions32.cpp
+++ b/engines/sci/graphics/transitions32.cpp
@@ -203,10 +203,6 @@ void GfxTransitions32::kernelSetShowStyle(const uint16 argc, const reg_t planeOb
color = 0;
}
- if ((getSciVersion() < SCI_VERSION_2_1_MIDDLE && g_sci->getGameId() != GID_KQ7 && type == 15) || type > 15) {
- error("Illegal show style %d for plane %04x:%04x", type, PRINT_REG(planeObj));
- }
-
Plane *plane = g_sci->_gfxFrameout->getPlanes().findByObject(planeObj);
if (plane == nullptr) {
error("Plane %04x:%04x is not present in active planes list", PRINT_REG(planeObj));
diff --git a/engines/sci/graphics/video32.cpp b/engines/sci/graphics/video32.cpp
index 51be08dac9..8b1d4ef32b 100644
--- a/engines/sci/graphics/video32.cpp
+++ b/engines/sci/graphics/video32.cpp
@@ -20,6 +20,7 @@
*
*/
+#include "audio/mixer.h" // for Audio::Mixer::kSFXSoundType
#include "common/config-manager.h" // for ConfMan
#include "common/textconsole.h" // for warning, error
#include "common/util.h" // for ARRAYSIZE
@@ -31,7 +32,7 @@
#include "sci/engine/vm_types.h" // for reg_t
#include "sci/event.h" // for SciEvent, EventManager, SCI_...
#include "sci/graphics/celobj32.h" // for CelInfo32, ::kLowResX, ::kLo...
-#include "sci/graphics/cursor.h" // for GfxCursor
+#include "sci/graphics/cursor32.h" // for GfxCursor32
#include "sci/graphics/frameout.h" // for GfxFrameout
#include "sci/graphics/helpers.h" // for Color, Palette
#include "sci/graphics/palette32.h" // for GfxPalette32
@@ -269,6 +270,11 @@ void AVIPlayer::init() {
g_sci->_gfxFrameout->addScreenItem(*_screenItem);
g_sci->_gfxFrameout->frameOut(true);
} else {
+ // Attempting to draw a palettized cursor into a 24bpp surface will
+ // cause memory corruption, so hide the cursor in this mode (SCI did not
+ // have a 24bpp mode but just directed VFW to display videos instead)
+ g_sci->_gfxCursor32->hide();
+
const Buffer &currentBuffer = g_sci->_gfxFrameout->getCurrentBuffer();
const Graphics::PixelFormat format = _decoder->getPixelFormat();
initGraphics(currentBuffer.screenWidth, currentBuffer.screenHeight, g_sci->_gfxFrameout->_isHiRes, &format);
@@ -326,6 +332,7 @@ AVIPlayer::IOStatus AVIPlayer::close() {
const Buffer &currentBuffer = g_sci->_gfxFrameout->getCurrentBuffer();
const Graphics::PixelFormat format = Graphics::PixelFormat::createFormatCLUT8();
initGraphics(currentBuffer.screenWidth, currentBuffer.screenHeight, isHiRes, &format);
+ g_sci->_gfxCursor32->unhide();
}
_decoder->close();
@@ -597,7 +604,7 @@ VMDPlayer::IOStatus VMDPlayer::close() {
}
if (!_showCursor) {
- g_sci->_gfxCursor->kernelShow();
+ g_sci->_gfxCursor32->unhide();
}
_lastYieldedFrameNo = 0;
@@ -606,6 +613,22 @@ VMDPlayer::IOStatus VMDPlayer::close() {
return kIOSuccess;
}
+VMDPlayer::VMDStatus VMDPlayer::getStatus() const {
+ if (!_isOpen) {
+ return kVMDNotOpen;
+ }
+ if (_decoder->isPaused()) {
+ return kVMDPaused;
+ }
+ if (_decoder->isPlaying()) {
+ return kVMDPlaying;
+ }
+ if (_decoder->endOfVideo()) {
+ return kVMDFinished;
+ }
+ return kVMDOpen;
+}
+
VMDPlayer::EventFlags VMDPlayer::kernelPlayUntilEvent(const EventFlags flags, const int16 lastFrameNo, const int16 yieldInterval) {
assert(lastFrameNo >= -1);
@@ -658,7 +681,7 @@ VMDPlayer::EventFlags VMDPlayer::playUntilEvent(const EventFlags flags) {
_isInitialized = true;
if (!_showCursor) {
- g_sci->_gfxCursor->kernelHide();
+ g_sci->_gfxCursor32->hide();
}
Common::Rect vmdRect(_x,
diff --git a/engines/sci/graphics/video32.h b/engines/sci/graphics/video32.h
index 0496f61d5d..75b8fb2d21 100644
--- a/engines/sci/graphics/video32.h
+++ b/engines/sci/graphics/video32.h
@@ -27,6 +27,7 @@
#include "common/scummsys.h" // for int16, uint8, uint16, int32
#include "common/str.h" // for String
#include "sci/engine/vm_types.h" // for reg_t
+#include "sci/video/robot_decoder.h" // for RobotDecoder
namespace Video {
class AdvancedVMDDecoder;
@@ -267,6 +268,15 @@ public:
kEventFlagReverse = 0x80
};
+ enum VMDStatus {
+ kVMDNotOpen = 0,
+ kVMDOpen = 1,
+ kVMDPlaying = 2,
+ kVMDPaused = 3,
+ kVMDStopped = 4,
+ kVMDFinished = 5
+ };
+
VMDPlayer(SegManager *segMan, EventManager *eventMan);
~VMDPlayer();
@@ -294,6 +304,11 @@ public:
*/
IOStatus close();
+ /**
+ * Gets the playback status of the VMD player.
+ */
+ VMDStatus getStatus() const;
+
// NOTE: Was WaitForEvent in SSCI
EventFlags kernelPlayUntilEvent(const EventFlags flags, const int16 lastFrameNo, const int16 yieldInterval);
@@ -505,16 +520,19 @@ public:
Video32(SegManager *segMan, EventManager *eventMan) :
_SEQPlayer(segMan),
_AVIPlayer(segMan, eventMan),
- _VMDPlayer(segMan, eventMan) {}
+ _VMDPlayer(segMan, eventMan),
+ _robotPlayer(segMan) {}
SEQPlayer &getSEQPlayer() { return _SEQPlayer; }
AVIPlayer &getAVIPlayer() { return _AVIPlayer; }
VMDPlayer &getVMDPlayer() { return _VMDPlayer; }
+ RobotDecoder &getRobotPlayer() { return _robotPlayer; }
private:
SEQPlayer _SEQPlayer;
AVIPlayer _AVIPlayer;
VMDPlayer _VMDPlayer;
+ RobotDecoder _robotPlayer;
};
} // End of namespace Sci
diff --git a/engines/sci/graphics/view.cpp b/engines/sci/graphics/view.cpp
index 1939e66179..0c09fcbb30 100644
--- a/engines/sci/graphics/view.cpp
+++ b/engines/sci/graphics/view.cpp
@@ -351,18 +351,6 @@ void GfxView::initData(GuiResourceId resourceId) {
celData += celSize;
}
}
-#ifdef ENABLE_SCI32
- // adjust width/height returned to scripts
- if (_sci2ScaleRes != SCI_VIEW_NATIVERES_NONE) {
- for (loopNo = 0; loopNo < _loopCount; loopNo++)
- for (celNo = 0; celNo < _loop[loopNo].celCount; celNo++)
- _screen->adjustBackUpscaledCoordinates(_loop[loopNo].cel[celNo].scriptWidth, _loop[loopNo].cel[celNo].scriptHeight, _sci2ScaleRes);
- } else if ((getSciVersion() >= SCI_VERSION_2_1_EARLY) && (getSciVersion() <= SCI_VERSION_2_1_LATE)) {
- for (loopNo = 0; loopNo < _loopCount; loopNo++)
- for (celNo = 0; celNo < _loop[loopNo].celCount; celNo++)
- _coordAdjuster->fromDisplayToScript(_loop[loopNo].cel[celNo].scriptHeight, _loop[loopNo].cel[celNo].scriptWidth);
- }
-#endif
break;
default:
diff --git a/engines/sci/graphics/view.h b/engines/sci/graphics/view.h
index 96b48c0477..5e422468b5 100644
--- a/engines/sci/graphics/view.h
+++ b/engines/sci/graphics/view.h
@@ -92,7 +92,7 @@ private:
void unditherBitmap(byte *bitmap, int16 width, int16 height, byte clearKey);
ResourceManager *_resMan;
- GfxCoordAdjuster *_coordAdjuster;
+ GfxCoordAdjuster16 *_coordAdjuster;
GfxScreen *_screen;
GfxPalette *_palette;
diff --git a/engines/sci/module.mk b/engines/sci/module.mk
index 18d97ea57e..eb2c6a148b 100644
--- a/engines/sci/module.mk
+++ b/engines/sci/module.mk
@@ -93,6 +93,7 @@ MODULE_OBJS += \
graphics/text32.o \
graphics/transitions32.o \
graphics/video32.o \
+ graphics/cursor32.o \
sound/audio32.o \
sound/decoders/sol.o \
video/robot_decoder.o
diff --git a/engines/sci/resource.cpp b/engines/sci/resource.cpp
index 48278e35a7..2bd941b11a 100644
--- a/engines/sci/resource.cpp
+++ b/engines/sci/resource.cpp
@@ -573,6 +573,9 @@ Resource *ResourceManager::testResource(ResourceId id) {
}
int ResourceManager::addAppropriateSources() {
+#ifdef ENABLE_SCI32
+ _multiDiscAudio = false;
+#endif
if (Common::File::exists("resource.map")) {
// SCI0-SCI2 file naming scheme
ResourceSource *map = addExternalMap("resource.map");
@@ -615,6 +618,10 @@ int ResourceManager::addAppropriateSources() {
if (mapFiles.empty() || files.empty() || mapFiles.size() != files.size())
return 0;
+ if (Common::File::exists("resaud.001")) {
+ _multiDiscAudio = true;
+ }
+
for (Common::ArchiveMemberList::const_iterator mapIterator = mapFiles.begin(); mapIterator != mapFiles.end(); ++mapIterator) {
Common::String mapName = (*mapIterator)->getName();
int mapNumber = atoi(strrchr(mapName.c_str(), '.') + 1);
@@ -859,6 +866,13 @@ void ResourceManager::addResourcesFromChunk(uint16 id) {
scanNewSources();
}
+void ResourceManager::findDisc(const int16 discNo) {
+ // Since all resources are expected to be copied from the original discs
+ // into a single game directory, this call just records the number of the CD
+ // that the game has requested
+ _currentDiscNo = discNo;
+}
+
#endif
void ResourceManager::freeResourceSources() {
@@ -878,7 +892,9 @@ void ResourceManager::init() {
_LRU.clear();
_resMap.clear();
_audioMapSCI1 = NULL;
-
+#ifdef ENABLE_SCI32
+ _currentDiscNo = 1;
+#endif
// FIXME: put this in an Init() function, so that we can error out if detection fails completely
_mapVersion = detectMapVersion();
@@ -1477,6 +1493,12 @@ void ResourceManager::readResourcePatchesBase36() {
for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) {
name = (*x)->getName();
+ // The S/T prefixes often conflict with non-patch files and generate
+ // spurious warnings about invalid patches
+ if (name.hasSuffix(".DLL") || name.hasSuffix(".EXE") || name.hasSuffix(".TXT")) {
+ continue;
+ }
+
ResourceId resource36 = convertPatchNameBase36((ResourceType)i, name);
/*
@@ -1738,11 +1760,42 @@ int ResourceManager::readResourceMapSCI1(ResourceSource *map) {
// if we use the first entries in the resource file, half of the
// game will be English and umlauts will also be missing :P
if (resource->_source->getSourceType() == kSourceVolume) {
+ // Maps are read during the scanning process (below), so
+ // need to be treated as unallocated in order for the new
+ // data from this volume to be picked up and used
+ if (resId.getType() == kResourceTypeMap) {
+ resource->_status = kResStatusNoMalloc;
+ }
resource->_source = source;
resource->_fileOffset = fileOffset;
resource->size = 0;
}
}
+
+#ifdef ENABLE_SCI32
+ // Different CDs may have different audio maps on each disc. The
+ // ResourceManager does not know how to deal with this; it expects
+ // each resource ID to be unique across an entire game. To work
+ // around this problem, all audio maps from this disc must be
+ // processed immediately, since they will be replaced by the audio
+ // map from the next disc on the next call to readResourceMapSCI1
+ if (_multiDiscAudio && resId.getType() == kResourceTypeMap) {
+ IntMapResourceSource *audioMap = static_cast<IntMapResourceSource *>(addSource(new IntMapResourceSource("MAP", mapVolumeNr, resId.getNumber())));
+ Common::String volumeName;
+ if (resId.getNumber() == 65535) {
+ volumeName = Common::String::format("RESSFX.%03d", mapVolumeNr);
+ } else {
+ volumeName = Common::String::format("RESAUD.%03d", mapVolumeNr);
+ }
+
+ ResourceSource *audioVolume = addSource(new AudioVolumeResourceSource(this, volumeName, audioMap, mapVolumeNr));
+ if (!audioMap->_scanned) {
+ audioVolume->_scanned = true;
+ audioMap->_scanned = true;
+ audioMap->scanSource(this);
+ }
+ }
+#endif
}
}
diff --git a/engines/sci/resource.h b/engines/sci/resource.h
index f70bf48bd4..70db5909b7 100644
--- a/engines/sci/resource.h
+++ b/engines/sci/resource.h
@@ -296,6 +296,7 @@ protected:
typedef Common::HashMap<ResourceId, Resource *, ResourceIdHash> ResourceMap;
+class IntMapResourceSource;
class ResourceManager {
// FIXME: These 'friend' declarations are meant to be a temporary hack to
// ease transition to the ResourceSource class system.
@@ -397,6 +398,30 @@ public:
* resource manager.
*/
void addResourcesFromChunk(uint16 id);
+
+ /**
+ * Updates the currently active disc number.
+ */
+ void findDisc(const int16 discNo);
+
+ /**
+ * Gets the currently active disc number.
+ */
+ int16 getCurrentDiscNo() const { return _currentDiscNo; }
+
+private:
+ /**
+ * The currently active disc number.
+ */
+ int16 _currentDiscNo;
+
+ /**
+ * If true, the game has multiple audio volumes that contain different
+ * audio files for each disc.
+ */
+ bool _multiDiscAudio;
+
+public:
#endif
bool detectHires();
@@ -520,7 +545,7 @@ protected:
* @param map The map
* @return 0 on success, an SCI_ERROR_* code otherwise
*/
- int readAudioMapSCI11(ResourceSource *map);
+ int readAudioMapSCI11(IntMapResourceSource *map);
/**
* Reads SCI1 audio map files.
diff --git a/engines/sci/resource_audio.cpp b/engines/sci/resource_audio.cpp
index 5ab443a16d..cbc4a02739 100644
--- a/engines/sci/resource_audio.cpp
+++ b/engines/sci/resource_audio.cpp
@@ -277,7 +277,7 @@ void ResourceManager::removeAudioResource(ResourceId resId) {
// w syncSize (iff seq has bit 7 set)
// w syncAscSize (iff seq has bit 6 set)
-int ResourceManager::readAudioMapSCI11(ResourceSource *map) {
+int ResourceManager::readAudioMapSCI11(IntMapResourceSource *map) {
#ifndef ENABLE_SCI32
// SCI32 support is not built in. Check if this is a SCI32 game
// and if it is abort here.
@@ -286,17 +286,19 @@ int ResourceManager::readAudioMapSCI11(ResourceSource *map) {
#endif
uint32 offset = 0;
- Resource *mapRes = findResource(ResourceId(kResourceTypeMap, map->_volumeNumber), false);
+ Resource *mapRes = findResource(ResourceId(kResourceTypeMap, map->_mapNumber), false);
if (!mapRes) {
- warning("Failed to open %i.MAP", map->_volumeNumber);
+ warning("Failed to open %i.MAP", map->_mapNumber);
return SCI_ERROR_RESMAP_NOT_FOUND;
}
- ResourceSource *src = findVolume(map, 0);
+ ResourceSource *src = findVolume(map, map->_volumeNumber);
- if (!src)
+ if (!src) {
+ warning("Failed to find volume for %i.MAP", map->_mapNumber);
return SCI_ERROR_NO_RESOURCE_FILES_FOUND;
+ }
byte *ptr = mapRes->data;
@@ -309,7 +311,7 @@ int ResourceManager::readAudioMapSCI11(ResourceSource *map) {
break;
}
- if (map->_volumeNumber == 65535) {
+ if (map->_mapNumber == 65535) {
while (ptr < mapRes->data + mapRes->size) {
uint16 n = READ_LE_UINT16(ptr);
ptr += 2;
@@ -327,7 +329,7 @@ int ResourceManager::readAudioMapSCI11(ResourceSource *map) {
addResource(ResourceId(kResourceTypeAudio, n), src, offset);
}
- } else if (map->_volumeNumber == 0 && entrySize == 10 && ptr[3] == 0) {
+ } else if (map->_mapNumber == 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) {
@@ -344,7 +346,7 @@ int ResourceManager::readAudioMapSCI11(ResourceSource *map) {
addResource(ResourceId(kResourceTypeAudio, n), src, offset, size);
}
- } else if (map->_volumeNumber == 0 && entrySize == 8 && READ_LE_UINT16(ptr + 2) == 0xffff) {
+ } else if (map->_mapNumber == 0 && entrySize == 8 && READ_LE_UINT16(ptr + 2) == 0xffff) {
// LB2 Floppy/Mother Goose SCI1.1 format
Common::SeekableReadStream *stream = getVolumeFile(src);
@@ -400,7 +402,7 @@ int ResourceManager::readAudioMapSCI11(ResourceSource *map) {
// FIXME: The sync36 resource seems to be two bytes too big in KQ6CD
// (bytes taken from the RAVE resource right after it)
if (syncSize > 0)
- addResource(ResourceId(kResourceTypeSync36, map->_volumeNumber, n & 0xffffff3f), src, offset, syncSize);
+ addResource(ResourceId(kResourceTypeSync36, map->_mapNumber, n & 0xffffff3f), src, offset, syncSize);
}
if (n & 0x40) {
@@ -410,12 +412,12 @@ int ResourceManager::readAudioMapSCI11(ResourceSource *map) {
ptr += 2;
if (kq6HiresSyncSize > 0) {
- addResource(ResourceId(kResourceTypeRave, map->_volumeNumber, n & 0xffffff3f), src, offset + syncSize, kq6HiresSyncSize);
+ addResource(ResourceId(kResourceTypeRave, map->_mapNumber, n & 0xffffff3f), src, offset + syncSize, kq6HiresSyncSize);
syncSize += kq6HiresSyncSize;
}
}
- addResource(ResourceId(kResourceTypeAudio36, map->_volumeNumber, n & 0xffffff3f), src, offset + syncSize);
+ addResource(ResourceId(kResourceTypeAudio36, map->_mapNumber, n & 0xffffff3f), src, offset + syncSize);
}
}
@@ -937,13 +939,21 @@ void AudioVolumeResourceSource::loadResource(ResourceManager *resMan, Resource *
}
bool ResourceManager::addAudioSources() {
+#ifdef ENABLE_SCI32
+ // Multi-disc audio is added during addAppropriateSources for those titles
+ // that require it
+ if (_multiDiscAudio) {
+ return true;
+ }
+#endif
+
Common::List<ResourceId> resources = listResources(kResourceTypeMap);
Common::List<ResourceId>::iterator itr;
for (itr = resources.begin(); itr != resources.end(); ++itr) {
- ResourceSource *src = addSource(new IntMapResourceSource("MAP", itr->getNumber()));
+ ResourceSource *src = addSource(new IntMapResourceSource("MAP", 0, itr->getNumber()));
- if ((itr->getNumber() == 65535) && Common::File::exists("RESOURCE.SFX"))
+ 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(new AudioVolumeResourceSource(this, "RESOURCE.AUD", src, 0));
@@ -991,7 +1001,7 @@ void ResourceManager::changeAudioDirectory(Common::String path) {
if ((it->getNumber() == 65535))
continue;
- ResourceSource *src = addSource(new IntMapResourceSource(mapName, it->getNumber()));
+ ResourceSource *src = addSource(new IntMapResourceSource(mapName, 0, it->getNumber()));
addSource(new AudioVolumeResourceSource(this, audioResourceName, src, 0));
}
diff --git a/engines/sci/resource_intern.h b/engines/sci/resource_intern.h
index 461d684005..fe4b0a97f4 100644
--- a/engines/sci/resource_intern.h
+++ b/engines/sci/resource_intern.h
@@ -134,8 +134,9 @@ public:
class IntMapResourceSource : public ResourceSource {
public:
- IntMapResourceSource(const Common::String &name, int volNum)
- : ResourceSource(kSourceIntMap, name, volNum) {
+ uint16 _mapNumber;
+ IntMapResourceSource(const Common::String &name, int volNum, int mapNum)
+ : ResourceSource(kSourceIntMap, name, volNum), _mapNumber(mapNum) {
}
virtual void scanSource(ResourceManager *resMan);
diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp
index 2a098ad975..6c51060296 100644
--- a/engines/sci/sci.cpp
+++ b/engines/sci/sci.cpp
@@ -65,6 +65,7 @@
#ifdef ENABLE_SCI32
#include "sci/graphics/controls32.h"
+#include "sci/graphics/cursor32.h"
#include "sci/graphics/frameout.h"
#include "sci/graphics/palette32.h"
#include "sci/graphics/remap32.h"
@@ -72,17 +73,12 @@
#include "sci/graphics/transitions32.h"
#include "sci/graphics/video32.h"
#include "sci/sound/audio32.h"
-// TODO: Move this to video32
-#include "sci/video/robot_decoder.h"
#endif
namespace Sci {
SciEngine *g_sci = 0;
-
-class GfxDriver;
-
SciEngine::SciEngine(OSystem *syst, const ADGameDescription *desc, SciGameId gameId)
: Engine(syst), _gameDescription(desc), _gameId(gameId), _rng("sci") {
@@ -96,6 +92,7 @@ SciEngine::SciEngine(OSystem *syst, const ADGameDescription *desc, SciGameId gam
#ifdef ENABLE_SCI32
_audio32 = nullptr;
_video32 = nullptr;
+ _gfxCursor32 = nullptr;
#endif
_features = 0;
_resMan = 0;
@@ -130,6 +127,7 @@ SciEngine::SciEngine(OSystem *syst, const ADGameDescription *desc, SciGameId gam
DebugMan.addDebugChannel(kDebugLevelScripts, "Scripts", "Notifies when scripts are unloaded");
DebugMan.addDebugChannel(kDebugLevelScriptPatcher, "ScriptPatcher", "Notifies when scripts are patched");
DebugMan.addDebugChannel(kDebugLevelWorkarounds, "Workarounds", "Notifies when workarounds are triggered");
+ DebugMan.addDebugChannel(kDebugLevelVideo, "Video", "Video (SEQ, VMD, RBT) debugging");
DebugMan.addDebugChannel(kDebugLevelGC, "GC", "Garbage Collector debugging");
DebugMan.addDebugChannel(kDebugLevelResMan, "ResMan", "Resource manager debugging");
DebugMan.addDebugChannel(kDebugLevelOnStartup, "OnStartup", "Enter debugger at start of game");
@@ -171,11 +169,11 @@ SciEngine::~SciEngine() {
delete _gfxControls32;
delete _gfxPaint32;
delete _gfxText32;
- delete _robotDecoder;
// GfxFrameout and GfxPalette32 must be deleted after Video32 since
// destruction of screen items in the Video32 destructor relies on these
// components
delete _video32;
+ delete _gfxCursor32;
delete _gfxPalette32;
delete _gfxTransitions32;
delete _gfxFrameout;
@@ -262,9 +260,13 @@ Common::Error SciEngine::run() {
_forceHiresGraphics = ConfMan.getBool("enable_high_resolution_graphics");
}
- // Initialize the game screen
- _gfxScreen = new GfxScreen(_resMan);
- _gfxScreen->enableUndithering(ConfMan.getBool("disable_dithering"));
+ if (getSciVersion() < SCI_VERSION_2) {
+ // Initialize the game screen
+ _gfxScreen = new GfxScreen(_resMan);
+ _gfxScreen->enableUndithering(ConfMan.getBool("disable_dithering"));
+ } else {
+ _gfxScreen = nullptr;
+ }
_kernel = new Kernel(_resMan, segMan);
_kernel->init();
@@ -701,12 +703,12 @@ void SciEngine::initGraphics() {
#ifdef ENABLE_SCI32
_gfxControls32 = 0;
_gfxText32 = 0;
- _robotDecoder = 0;
_gfxFrameout = 0;
_gfxPaint32 = 0;
_gfxPalette32 = 0;
_gfxRemap32 = 0;
_gfxTransitions32 = 0;
+ _gfxCursor32 = 0;
#endif
if (hasMacIconBar())
@@ -726,24 +728,23 @@ void SciEngine::initGraphics() {
#endif
_gfxCache = new GfxCache(_resMan, _gfxScreen, _gfxPalette16);
- _gfxCursor = new GfxCursor(_resMan, _gfxPalette16, _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, _gfxCache, _gfxScreen, _gfxCoordAdjuster);
+ _gfxCursor32 = new GfxCursor32();
+ _gfxCompare = new GfxCompare(_gamestate->_segMan, _gfxCache, nullptr, _gfxCoordAdjuster);
_gfxPaint32 = new GfxPaint32(_gamestate->_segMan);
- _robotDecoder = new RobotDecoder(getPlatform() == Common::kPlatformMacintosh);
_gfxTransitions32 = new GfxTransitions32(_gamestate->_segMan);
- _gfxFrameout = new GfxFrameout(_gamestate->_segMan, _resMan, _gfxCoordAdjuster, _gfxScreen, _gfxPalette32, _gfxTransitions32);
+ _gfxFrameout = new GfxFrameout(_gamestate->_segMan, _gfxPalette32, _gfxTransitions32, _gfxCursor32);
+ _gfxCursor32->init(_gfxFrameout->getCurrentBuffer());
_gfxText32 = new GfxText32(_gamestate->_segMan, _gfxCache);
_gfxControls32 = new GfxControls32(_gamestate->_segMan, _gfxCache, _gfxText32);
_gfxFrameout->run();
} else {
#endif
// SCI0-SCI1.1 graphic objects creation
+ _gfxCursor = new GfxCursor(_resMan, _gfxPalette16, _gfxScreen);
_gfxPorts = new GfxPorts(_gamestate->_segMan, _gfxScreen);
_gfxCoordAdjuster = new GfxCoordAdjuster16(_gfxPorts);
_gfxCursor->init(_gfxCoordAdjuster, _eventMan);
diff --git a/engines/sci/sci.h b/engines/sci/sci.h
index 0425d21564..b336eb8cce 100644
--- a/engines/sci/sci.h
+++ b/engines/sci/sci.h
@@ -75,7 +75,7 @@ class GfxCache;
class GfxCompare;
class GfxControls16;
class GfxControls32;
-class GfxCoordAdjuster;
+class GfxCoordAdjuster16;
class GfxCursor;
class GfxMacIconBar;
class GfxMenu;
@@ -92,12 +92,11 @@ class GfxText32;
class GfxTransitions;
#ifdef ENABLE_SCI32
-// TODO: Move RobotDecoder to Video32
-class RobotDecoder;
class GfxFrameout;
class Audio32;
class Video32;
class GfxTransitions32;
+class GfxCursor32;
#endif
// our engine debug levels
@@ -125,7 +124,8 @@ enum kDebugLevels {
kDebugLevelOnStartup = 1 << 20,
kDebugLevelDebugMode = 1 << 21,
kDebugLevelScriptPatcher = 1 << 22,
- kDebugLevelWorkarounds = 1 << 23
+ kDebugLevelWorkarounds = 1 << 23,
+ kDebugLevelVideo = 1 << 24
};
enum SciGameId {
@@ -369,7 +369,7 @@ public:
GfxCompare *_gfxCompare;
GfxControls16 *_gfxControls16; // Controls for 16-bit gfx
GfxControls32 *_gfxControls32; // Controls for 32-bit gfx
- GfxCoordAdjuster *_gfxCoordAdjuster;
+ GfxCoordAdjuster16 *_gfxCoordAdjuster;
GfxCursor *_gfxCursor;
GfxMenu *_gfxMenu; // Menu for 16-bit gfx
GfxPalette *_gfxPalette16;
@@ -388,9 +388,9 @@ public:
#ifdef ENABLE_SCI32
Audio32 *_audio32;
Video32 *_video32;
- RobotDecoder *_robotDecoder;
GfxFrameout *_gfxFrameout; // kFrameout and the like for 32-bit gfx
GfxTransitions32 *_gfxTransitions32;
+ GfxCursor32 *_gfxCursor32;
#endif
AudioPlayer *_audio;
diff --git a/engines/sci/sound/audio32.cpp b/engines/sci/sound/audio32.cpp
index 288b7c00f5..4af474b918 100644
--- a/engines/sci/sound/audio32.cpp
+++ b/engines/sci/sound/audio32.cpp
@@ -164,7 +164,7 @@ Audio32::~Audio32() {
#pragma mark -
#pragma mark AudioStream implementation
-int Audio32::writeAudioInternal(Audio::RewindableAudioStream *const sourceStream, Audio::RateConverter *const converter, Audio::st_sample_t *targetBuffer, const int numSamples, const Audio::st_volume_t leftVolume, const Audio::st_volume_t rightVolume, const bool loop) {
+int Audio32::writeAudioInternal(Audio::AudioStream *const sourceStream, Audio::RateConverter *const converter, Audio::st_sample_t *targetBuffer, const int numSamples, const Audio::st_volume_t leftVolume, const Audio::st_volume_t rightVolume, const bool loop) {
int samplesToRead = numSamples;
// The parent rate converter will request N * 2
@@ -182,7 +182,8 @@ int Audio32::writeAudioInternal(Audio::RewindableAudioStream *const sourceStream
do {
if (loop && sourceStream->endOfStream()) {
- sourceStream->rewind();
+ Audio::RewindableAudioStream *rewindableStream = dynamic_cast<Audio::RewindableAudioStream *>(sourceStream);
+ rewindableStream->rewind();
}
const int loopSamplesWritten = converter->flow(*sourceStream, targetBuffer, samplesToRead, leftVolume, rightVolume);
@@ -305,7 +306,14 @@ int Audio32::readBuffer(Audio::st_sample_t *buffer, const int numSamples) {
}
if (channel.robot) {
- // TODO: Robot audio into output buffer
+ if (channel.stream->endOfStream()) {
+ stop(channelIndex--);
+ } else {
+ const int channelSamplesWritten = writeAudioInternal(channel.stream, channel.converter, buffer, numSamples, kMaxVolume, kMaxVolume, channel.loop);
+ if (channelSamplesWritten > maxSamplesWritten) {
+ maxSamplesWritten = channelSamplesWritten;
+ }
+ }
continue;
}
@@ -443,9 +451,9 @@ void Audio32::freeUnusedChannels() {
Common::StackLock lock(_mutex);
for (int channelIndex = 0; channelIndex < _numActiveChannels; ++channelIndex) {
const AudioChannel &channel = getChannel(channelIndex);
- if (channel.stream->endOfStream()) {
+ if (!channel.robot && channel.stream->endOfStream()) {
if (channel.loop) {
- channel.stream->rewind();
+ dynamic_cast<Audio::SeekableAudioStream *>(channel.stream)->rewind();
} else {
stop(channelIndex--);
}
@@ -466,21 +474,29 @@ void Audio32::freeChannel(const int16 channelIndex) {
Common::StackLock lock(_mutex);
AudioChannel &channel = getChannel(channelIndex);
- // We cannot unlock resources from the audio thread
- // because ResourceManager is not thread-safe; instead,
- // we just record that the resource needs unlocking and
- // unlock it whenever we are on the main thread again
- if (_inAudioThread) {
- _resourcesToUnlock.push_back(channel.resource);
+ // Robots have no corresponding resource to free
+ if (channel.robot) {
+ delete channel.stream;
+ channel.stream = nullptr;
+ channel.robot = false;
} else {
- _resMan->unlockResource(channel.resource);
+ // We cannot unlock resources from the audio thread
+ // because ResourceManager is not thread-safe; instead,
+ // we just record that the resource needs unlocking and
+ // unlock it whenever we are on the main thread again
+ if (_inAudioThread) {
+ _resourcesToUnlock.push_back(channel.resource);
+ } else {
+ _resMan->unlockResource(channel.resource);
+ }
+
+ channel.resource = nullptr;
+ delete channel.stream;
+ channel.stream = nullptr;
+ delete channel.resourceStream;
+ channel.resourceStream = nullptr;
}
- channel.resource = nullptr;
- delete channel.stream;
- channel.stream = nullptr;
- delete channel.resourceStream;
- channel.resourceStream = nullptr;
delete channel.converter;
channel.converter = nullptr;
@@ -527,6 +543,111 @@ void Audio32::setNumOutputChannels(int16 numChannels) {
}
#pragma mark -
+#pragma mark Robot
+
+int16 Audio32::findRobotChannel() const {
+ Common::StackLock lock(_mutex);
+ for (int16 i = 0; i < _numActiveChannels; ++i) {
+ if (_channels[i].robot) {
+ return i;
+ }
+ }
+
+ return kNoExistingChannel;
+}
+
+bool Audio32::playRobotAudio(const RobotAudioStream::RobotAudioPacket &packet) {
+ // Stop immediately
+ if (packet.dataSize == 0) {
+ warning("Stopping robot stream by zero-length packet");
+ return stopRobotAudio();
+ }
+
+ // Flush and then stop
+ if (packet.dataSize == -1) {
+ warning("Stopping robot stream by negative-length packet");
+ return finishRobotAudio();
+ }
+
+ Common::StackLock lock(_mutex);
+ int16 channelIndex = findRobotChannel();
+
+ bool isNewChannel = false;
+ if (channelIndex == kNoExistingChannel) {
+ if (_numActiveChannels == _channels.size()) {
+ return false;
+ }
+
+ channelIndex = _numActiveChannels++;
+ isNewChannel = true;
+ }
+
+ AudioChannel &channel = getChannel(channelIndex);
+
+ if (isNewChannel) {
+ channel.id = ResourceId();
+ channel.resource = nullptr;
+ channel.loop = false;
+ channel.robot = true;
+ channel.fadeStartTick = 0;
+ channel.pausedAtTick = 0;
+ channel.soundNode = NULL_REG;
+ channel.volume = kMaxVolume;
+ // TODO: SCI3 introduces stereo audio
+ channel.pan = -1;
+ channel.converter = Audio::makeRateConverter(RobotAudioStream::kRobotSampleRate, getRate(), false);
+ // The RobotAudioStream buffer size is
+ // ((bytesPerSample * channels * sampleRate * 2000ms) / 1000ms) & ~3
+ // where bytesPerSample = 2, channels = 1, and sampleRate = 22050
+ channel.stream = new RobotAudioStream(88200);
+ _robotAudioPaused = false;
+
+ if (_numActiveChannels == 1) {
+ _startedAtTick = g_sci->getTickCount();
+ }
+ }
+
+ return static_cast<RobotAudioStream *>(channel.stream)->addPacket(packet);
+}
+
+bool Audio32::queryRobotAudio(RobotAudioStream::StreamState &status) const {
+ Common::StackLock lock(_mutex);
+
+ const int16 channelIndex = findRobotChannel();
+ if (channelIndex == kNoExistingChannel) {
+ status.bytesPlaying = 0;
+ return false;
+ }
+
+ status = static_cast<RobotAudioStream *>(getChannel(channelIndex).stream)->getStatus();
+ return true;
+}
+
+bool Audio32::finishRobotAudio() {
+ Common::StackLock lock(_mutex);
+
+ const int16 channelIndex = findRobotChannel();
+ if (channelIndex == kNoExistingChannel) {
+ return false;
+ }
+
+ static_cast<RobotAudioStream *>(getChannel(channelIndex).stream)->finish();
+ return true;
+}
+
+bool Audio32::stopRobotAudio() {
+ Common::StackLock lock(_mutex);
+
+ const int16 channelIndex = findRobotChannel();
+ if (channelIndex == kNoExistingChannel) {
+ return false;
+ }
+
+ stop(channelIndex);
+ return true;
+}
+
+#pragma mark -
#pragma mark Playback
uint16 Audio32::play(int16 channelIndex, const ResourceId resourceId, const bool autoPlay, const bool loop, const int16 volume, const reg_t soundNode, const bool monitor) {
@@ -536,14 +657,15 @@ uint16 Audio32::play(int16 channelIndex, const ResourceId resourceId, const bool
if (channelIndex != kNoExistingChannel) {
AudioChannel &channel = getChannel(channelIndex);
+ Audio::SeekableAudioStream *stream = dynamic_cast<Audio::SeekableAudioStream *>(channel.stream);
if (channel.pausedAtTick) {
resume(channelIndex);
- return MIN(65534, 1 + channel.stream->getLength().msecs() * 60 / 1000);
+ return MIN(65534, 1 + stream->getLength().msecs() * 60 / 1000);
}
warning("Tried to resume channel %s that was not paused", channel.id.toString().c_str());
- return MIN(65534, 1 + channel.stream->getLength().msecs() * 60 / 1000);
+ return MIN(65534, 1 + stream->getLength().msecs() * 60 / 1000);
}
if (_numActiveChannels == _channels.size()) {
@@ -642,7 +764,7 @@ uint16 Audio32::play(int16 channelIndex, const ResourceId resourceId, const bool
// use audio streams, and allocate and fill the monitoring buffer
// when reading audio data from the stream.
- channel.duration = /* round up */ 1 + (channel.stream->getLength().msecs() * 60 / 1000);
+ channel.duration = /* round up */ 1 + (dynamic_cast<Audio::SeekableAudioStream *>(channel.stream)->getLength().msecs() * 60 / 1000);
const uint32 now = g_sci->getTickCount();
channel.pausedAtTick = autoPlay ? 0 : now;
@@ -687,8 +809,6 @@ bool Audio32::resume(const int16 channelIndex) {
if (channel.robot) {
channel.startedAtTick += now - channel.pausedAtTick;
channel.pausedAtTick = 0;
- // TODO: Robot
- // StartRobot();
return true;
}
}
diff --git a/engines/sci/sound/audio32.h b/engines/sci/sound/audio32.h
index ac3176cc5a..a9905ab6bf 100644
--- a/engines/sci/sound/audio32.h
+++ b/engines/sci/sound/audio32.h
@@ -30,8 +30,10 @@
#include "common/scummsys.h" // for int16, uint8, uint32, uint16
#include "engines/sci/resource.h" // for ResourceId
#include "sci/engine/vm_types.h" // for reg_t, NULL_REG
+#include "sci/video/robot_decoder.h" // for RobotAudioStream
namespace Sci {
+#pragma mark AudioChannel
/**
* An audio channel used by the software SCI mixer.
@@ -53,14 +55,11 @@ struct AudioChannel {
Common::SeekableReadStream *resourceStream;
/**
- * The audio stream loaded into this channel.
- * `SeekableAudioStream` is used here instead of
- * `RewindableAudioStream` because
- * `RewindableAudioStream` does not include the
- * `getLength` function, which is needed to tell the
- * game engine the duration of audio streams.
+ * The audio stream loaded into this channel. Can cast
+ * to `SeekableAudioStream` for normal channels and
+ * `RobotAudioStream` for robot channels.
*/
- Audio::SeekableAudioStream *stream;
+ Audio::AudioStream *stream;
/**
* The converter used to transform and merge the input
@@ -188,7 +187,7 @@ private:
* Mixes audio from the given source stream into the
* target buffer using the given rate converter.
*/
- int writeAudioInternal(Audio::RewindableAudioStream *const sourceStream, Audio::RateConverter *const converter, Audio::st_sample_t *targetBuffer, const int numSamples, const Audio::st_volume_t leftVolume, const Audio::st_volume_t rightVolume, const bool loop);
+ int writeAudioInternal(Audio::AudioStream *const sourceStream, Audio::RateConverter *const converter, Audio::st_sample_t *targetBuffer, const int numSamples, const Audio::st_volume_t leftVolume, const Audio::st_volume_t rightVolume, const bool loop);
#pragma mark -
#pragma mark Channel management
@@ -395,9 +394,18 @@ private:
#pragma mark -
#pragma mark Robot
public:
+ bool playRobotAudio(const RobotAudioStream::RobotAudioPacket &packet);
+ bool queryRobotAudio(RobotAudioStream::StreamState &outStatus) const;
+ bool finishRobotAudio();
+ bool stopRobotAudio();
private:
/**
+ * Finds a channel that is configured for robot playback.
+ */
+ int16 findRobotChannel() const;
+
+ /**
* When true, channels marked as robot audio will not be
* played.
*/
diff --git a/engines/sci/sound/decoders/sol.cpp b/engines/sci/sound/decoders/sol.cpp
index e445403120..ee1ba35406 100644
--- a/engines/sci/sound/decoders/sol.cpp
+++ b/engines/sci/sound/decoders/sol.cpp
@@ -21,6 +21,7 @@
*/
#include "audio/audiostream.h"
+#include "audio/rate.h"
#include "audio/decoders/raw.h"
#include "common/substream.h"
#include "common/util.h"
@@ -52,7 +53,7 @@ static const byte tableDPCM8[8] = { 0, 1, 2, 3, 6, 10, 15, 21 };
* Decompresses 16-bit DPCM compressed audio. Each byte read
* outputs one sample into the decompression buffer.
*/
-static void deDPCM16(int16 *out, Common::ReadStream &audioStream, uint32 numBytes, int16 &sample) {
+static void deDPCM16(int16 *out, Common::ReadStream &audioStream, const uint32 numBytes, int16 &sample) {
for (uint32 i = 0; i < numBytes; ++i) {
const uint8 delta = audioStream.readByte();
if (delta & 0x80) {
@@ -65,6 +66,19 @@ static void deDPCM16(int16 *out, Common::ReadStream &audioStream, uint32 numByte
}
}
+void deDPCM16(int16 *out, const byte *in, const uint32 numBytes, int16 &sample) {
+ for (uint32 i = 0; i < numBytes; ++i) {
+ const uint8 delta = *in++;
+ if (delta & 0x80) {
+ sample -= tableDPCM16[delta & 0x7f];
+ } else {
+ sample += tableDPCM16[delta];
+ }
+ sample = CLIP<int16>(sample, -32768, 32767);
+ *out++ = TO_LE_16(sample);
+ }
+}
+
/**
* Decompresses one half of an 8-bit DPCM compressed audio
* byte.
@@ -178,7 +192,7 @@ int SOLStream<STEREO, S16BIT>::getRate() const {
template <bool STEREO, bool S16BIT>
bool SOLStream<STEREO, S16BIT>::endOfData() const {
- return _stream->eos() || _stream->pos() >= _dataOffset + _rawDataSize;
+ return _stream->eos() || _stream->pos() >= _rawDataSize;
}
template <bool STEREO, bool S16BIT>
@@ -269,5 +283,4 @@ Audio::SeekableAudioStream *makeSOLStream(Common::SeekableReadStream *headerStre
return Audio::makeRawStream(dataStream, sampleRate, rawFlags, disposeAfterUse);
}
-
}
diff --git a/engines/sci/video/robot_decoder.cpp b/engines/sci/video/robot_decoder.cpp
index a2795d21f9..f3354f9e44 100644
--- a/engines/sci/video/robot_decoder.cpp
+++ b/engines/sci/video/robot_decoder.cpp
@@ -20,391 +20,1598 @@
*
*/
-#include "common/archive.h"
-#include "common/stream.h"
-#include "common/substream.h"
-#include "common/system.h"
-#include "common/textconsole.h"
-#include "common/util.h"
-
-#include "graphics/surface.h"
-#include "audio/audiostream.h"
-#include "audio/decoders/raw.h"
-
-#include "sci/resource.h"
-#include "sci/util.h"
-#include "sci/sound/audio.h"
#include "sci/video/robot_decoder.h"
+#include "common/archive.h" // for SearchMan
+#include "common/debug.h" // for debugC
+#include "common/endian.h" // for MKTAG
+#include "common/memstream.h" // for MemoryReadStream
+#include "common/platform.h" // for Platform::kPlatformMacintosh
+#include "common/rational.h" // for operator*, Rational
+#include "common/str.h" // for String
+#include "common/stream.h" // for SeekableReadStream
+#include "common/substream.h" // for SeekableSubReadStreamEndian
+#include "common/textconsole.h" // for error, warning
+#include "common/types.h" // for Flag::NO, Flag::YES
+#include "sci/engine/seg_manager.h" // for SegManager
+#include "sci/graphics/celobj32.h" // for Ratio, ::kLowResX, ::kLowResY
+#include "sci/graphics/text32.h" // for BitmapResource
+#include "sci/sound/audio32.h" // for Audio32
+#include "sci/sci.h" // for kDebugLevels::kDebugLevelVideo
+#include "sci/util.h" // for READ_SCI11ENDIAN_UINT16, READ_SC...
namespace Sci {
-// TODO:
-// - Positioning
-// - Proper handling of frame scaling - scaled frames look squashed
-// (probably because both dimensions should be scaled)
-// - Transparency support
-// - Timing - the arbitrary 100ms delay between each frame is not quite right
-// - Proper handling of sound chunks in some cases, so that the frame size
-// table can be ignored (it's only used to determine the correct sound chunk
-// size at the moment, cause it can be wrong in some cases)
-// - Fix audio "hiccups" - probably data that shouldn't be in the audio frames
-
-
-// Some non technical information on robot files, from an interview with
-// Greg Tomko-Pavia of Sierra On-Line
-// Taken from http://anthonylarme.tripod.com/phantas/phintgtp.html
-//
-// (...) What we needed was a way of playing video, but have it blend into
-// normal room art instead of occupying its own rectangular area. Room art
-// consists of a background pic overlaid with various animating cels
-// (traditional lingo: sprites). The cels each have a priority that determines
-// who is on top and who is behind in the drawing order. Cels are read from
-// *.v56 files (another proprietary format). A Robot is video frames with
-// transparent background including priority and x,y information. Thus, it is
-// like a cel, except it comes from an RBT - not a v56. Because it blends into
-// our graphics engine, it looks just like a part of the room. A RBT can move
-// around the screen and go behind other objects. (...)
-
-enum RobotPalTypes {
- kRobotPalVariable = 0,
- kRobotPalConstant = 1
-};
-
-RobotDecoder::RobotDecoder(bool isBigEndian) {
- _fileStream = 0;
- _pos = Common::Point(0, 0);
- _isBigEndian = isBigEndian;
- _frameTotalSize = 0;
+#pragma mark RobotAudioStream
+
+extern void deDPCM16(int16 *out, const byte *in, const uint32 numBytes, int16 &sample);
+
+RobotAudioStream::RobotAudioStream(const int32 bufferSize) :
+ _loopBuffer((byte *)malloc(bufferSize)),
+ _loopBufferSize(bufferSize),
+ _decompressionBuffer(nullptr),
+ _decompressionBufferSize(0),
+ _decompressionBufferPosition(-1),
+ _waiting(true),
+ _finished(false),
+ _firstPacketPosition(-1) {}
+
+RobotAudioStream::~RobotAudioStream() {
+ free(_loopBuffer);
+ free(_decompressionBuffer);
}
-RobotDecoder::~RobotDecoder() {
- close();
+static void interpolateChannel(int16 *buffer, int32 numSamples, const int8 bufferIndex) {
+ if (numSamples <= 0) {
+ return;
+ }
+
+ if (bufferIndex) {
+ int16 lastSample = *buffer;
+ int sample = lastSample;
+ int16 *target = buffer + 1;
+ const int16 *source = buffer + 2;
+ --numSamples;
+
+ while (numSamples--) {
+ sample = *source + lastSample;
+ lastSample = *source;
+ sample /= 2;
+ *target = sample;
+ source += 2;
+ target += 2;
+ }
+
+ *target = sample;
+ } else {
+ int16 *target = buffer;
+ const int16 *source = buffer + 1;
+ int16 lastSample = *source;
+
+ while (numSamples--) {
+ int sample = *source + lastSample;
+ lastSample = *source;
+ sample /= 2;
+ *target = sample;
+ source += 2;
+ target += 2;
+ }
+ }
}
-bool RobotDecoder::loadStream(Common::SeekableReadStream *stream) {
- close();
+static void copyEveryOtherSample(int16 *out, const int16 *in, int numSamples) {
+ while (numSamples--) {
+ *out = *in++;
+ out += 2;
+ }
+}
+
+bool RobotAudioStream::addPacket(const RobotAudioPacket &packet) {
+ Common::StackLock lock(_mutex);
+
+ if (_finished) {
+ warning("Packet %d sent to finished robot audio stream", packet.position);
+ return false;
+ }
+
+ // `packet.position` is the decompressed (doubled) position of the packet,
+ // so values of `position` will always be divisible either by 2 (even) or by
+ // 4 (odd).
+ const int8 bufferIndex = packet.position % 4 ? 1 : 0;
+
+ // Packet 0 is the first primer, packet 2 is the second primer,
+ // packet 4+ are regular audio data
+ if (packet.position <= 2 && _firstPacketPosition == -1) {
+ _readHead = 0;
+ _readHeadAbs = 0;
+ _maxWriteAbs = _loopBufferSize;
+ _writeHeadAbs = 2;
+ _jointMin[0] = 0;
+ _jointMin[1] = 2;
+ _waiting = true;
+ _finished = false;
+ _firstPacketPosition = packet.position;
+ fillRobotBuffer(packet, bufferIndex);
+ return true;
+ }
- _fileStream = new Common::SeekableSubReadStreamEndian(stream, 0, stream->size(), _isBigEndian, DisposeAfterUse::YES);
+ const int32 packetEndByte = packet.position + (packet.dataSize * sizeof(int16) * kEOSExpansion);
- readHeaderChunk();
+ // Already read all the way past this packet (or already wrote valid samples
+ // to this channel all the way past this packet), so discard it
+ if (packetEndByte <= MAX(_readHeadAbs, _jointMin[bufferIndex])) {
+ debugC(kDebugLevelVideo, "Rejecting packet %d, read past %d / %d", packet.position, _readHeadAbs, _jointMin[bufferIndex]);
+ return true;
+ }
- // There are several versions of robot files, ranging from 3 to 6.
- // v3: no known examples
- // v4: PQ:SWAT demo
- // v5: SCI2.1 and SCI3 games
- // v6: SCI3 games
- if (_header.version < 4 || _header.version > 6)
- error("Unknown robot version: %d", _header.version);
+ // The loop buffer is full, so tell the caller to send the packet again
+ // later
+ if (_maxWriteAbs <= _jointMin[bufferIndex]) {
+ debugC(kDebugLevelVideo, "Rejecting packet %d, full buffer", packet.position);
+ return false;
+ }
- RobotVideoTrack *videoTrack = new RobotVideoTrack(_header.frameCount);
- addTrack(videoTrack);
+ fillRobotBuffer(packet, bufferIndex);
- if (_header.hasSound)
- addTrack(new RobotAudioTrack());
+ // This packet is the second primer, so allow playback to begin
+ if (_firstPacketPosition != -1 && _firstPacketPosition != packet.position) {
+ debugC(kDebugLevelVideo, "Done waiting. Robot audio begins");
+ _waiting = false;
+ _firstPacketPosition = -1;
+ }
- videoTrack->readPaletteChunk(_fileStream, _header.paletteDataSize);
- readFrameSizesChunk();
- videoTrack->calculateVideoDimensions(_fileStream, _frameTotalSize);
+ // Only part of the packet could be read into the loop buffer before it was
+ // full, so tell the caller to send the packet again later
+ if (packetEndByte > _maxWriteAbs) {
+ debugC(kDebugLevelVideo, "Partial read of packet %d (%d / %d)", packet.position, packetEndByte - _maxWriteAbs, packetEndByte - packet.position);
+ return false;
+ }
+
+ // The entire packet was successfully read into the loop buffer
return true;
}
-bool RobotDecoder::load(GuiResourceId id) {
- // TODO: RAMA's robot 1003 cannot be played (shown at the menu screen) -
- // its drawn at odd coordinates. SV can't play it either (along with some
- // others), so it must be some new functionality added in RAMA's robot
- // videos. Skip it for now.
- if (g_sci->getGameId() == GID_RAMA && id == 1003)
- return false;
+void RobotAudioStream::fillRobotBuffer(const RobotAudioPacket &packet, const int8 bufferIndex) {
+ int32 sourceByte = 0;
- // Robots for the options in the RAMA menu
- if (g_sci->getGameId() == GID_RAMA && (id >= 1004 && id <= 1009))
- return false;
+ const int32 decompressedSize = packet.dataSize * sizeof(int16);
+ if (_decompressionBufferPosition != packet.position) {
+ if (decompressedSize != _decompressionBufferSize) {
+ _decompressionBuffer = (byte *)realloc(_decompressionBuffer, decompressedSize);
+ _decompressionBufferSize = decompressedSize;
+ }
- // TODO: The robot video in the Lighthouse demo gets stuck
- if (g_sci->getGameId() == GID_LIGHTHOUSE && id == 16)
- return false;
+ int16 carry = 0;
+ deDPCM16((int16 *)_decompressionBuffer, packet.data, packet.dataSize, carry);
+ _decompressionBufferPosition = packet.position;
+ }
+
+ int32 numBytes = decompressedSize;
+ int32 packetPosition = packet.position;
+ int32 endByte = packet.position + decompressedSize * kEOSExpansion;
+ int32 startByte = MAX(_readHeadAbs + bufferIndex * 2, _jointMin[bufferIndex]);
+ int32 maxWriteByte = _maxWriteAbs + bufferIndex * 2;
+ if (packetPosition < startByte) {
+ sourceByte = (startByte - packetPosition) / kEOSExpansion;
+ numBytes -= sourceByte;
+ packetPosition = startByte;
+ }
+ if (packetPosition > maxWriteByte) {
+ numBytes += (packetPosition - maxWriteByte) / kEOSExpansion;
+ packetPosition = maxWriteByte;
+ }
+ if (endByte > maxWriteByte) {
+ numBytes -= (endByte - maxWriteByte) / kEOSExpansion;
+ endByte = maxWriteByte;
+ }
- Common::String fileName = Common::String::format("%d.rbt", id);
+ const int32 maxJointMin = MAX(_jointMin[0], _jointMin[1]);
+ if (endByte > maxJointMin) {
+ _writeHeadAbs += endByte - maxJointMin;
+ }
+
+ if (packetPosition > _jointMin[bufferIndex]) {
+ int32 packetEndByte = packetPosition % _loopBufferSize;
+ int32 targetBytePosition;
+ int32 numBytesToEnd;
+ if ((packetPosition & ~3) > (_jointMin[1 - bufferIndex] & ~3)) {
+ targetBytePosition = _jointMin[1 - bufferIndex] % _loopBufferSize;
+ if (targetBytePosition >= packetEndByte) {
+ numBytesToEnd = _loopBufferSize - targetBytePosition;
+ memset(_loopBuffer + targetBytePosition, 0, numBytesToEnd);
+ targetBytePosition = (1 - bufferIndex) ? 2 : 0;
+ }
+ numBytesToEnd = packetEndByte - targetBytePosition;
+ if (numBytesToEnd > 0) {
+ memset(_loopBuffer + targetBytePosition, 0, numBytesToEnd);
+ }
+ }
+ targetBytePosition = _jointMin[bufferIndex] % _loopBufferSize;
+ if (targetBytePosition >= packetEndByte) {
+ numBytesToEnd = _loopBufferSize - targetBytePosition;
+ interpolateChannel((int16 *)(_loopBuffer + targetBytePosition), numBytesToEnd / sizeof(int16) / kEOSExpansion, 0);
+ targetBytePosition = bufferIndex ? 2 : 0;
+ }
+ numBytesToEnd = packetEndByte - targetBytePosition;
+ if (numBytesToEnd > 0) {
+ interpolateChannel((int16 *)(_loopBuffer + targetBytePosition), numBytesToEnd / sizeof(int16) / kEOSExpansion, 0);
+ }
+ }
+
+ if (numBytes > 0) {
+ int32 targetBytePosition = packetPosition % _loopBufferSize;
+ int32 packetEndByte = endByte % _loopBufferSize;
+ int32 numBytesToEnd = 0;
+ if (targetBytePosition >= packetEndByte) {
+ numBytesToEnd = (_loopBufferSize - (targetBytePosition & ~3)) / kEOSExpansion;
+ copyEveryOtherSample((int16 *)(_loopBuffer + targetBytePosition), (int16 *)(_decompressionBuffer + sourceByte), numBytesToEnd / kEOSExpansion);
+ targetBytePosition = bufferIndex ? 2 : 0;
+ }
+ copyEveryOtherSample((int16 *)(_loopBuffer + targetBytePosition), (int16 *)(_decompressionBuffer + sourceByte + numBytesToEnd), (packetEndByte - targetBytePosition) / sizeof(int16) / kEOSExpansion);
+ }
+ _jointMin[bufferIndex] = endByte;
+}
+
+void RobotAudioStream::interpolateMissingSamples(int32 numSamples) {
+ int32 numBytes = numSamples * sizeof(int16) * kEOSExpansion;
+ int32 targetPosition = _readHead;
+
+ if (_readHeadAbs > _jointMin[1]) {
+ if (_readHeadAbs > _jointMin[0]) {
+ if (targetPosition + numBytes >= _loopBufferSize) {
+ const int32 numBytesToEdge = (_loopBufferSize - targetPosition);
+ memset(_loopBuffer + targetPosition, 0, numBytesToEdge);
+ numBytes -= numBytesToEdge;
+ targetPosition = 0;
+ }
+ memset(_loopBuffer + targetPosition, 0, numBytes);
+ _jointMin[0] += numBytes;
+ _jointMin[1] += numBytes;
+ } else {
+ if (targetPosition + numBytes >= _loopBufferSize) {
+ const int32 numSamplesToEdge = (_loopBufferSize - targetPosition) / sizeof(int16) / kEOSExpansion;
+ interpolateChannel((int16 *)(_loopBuffer + targetPosition), numSamplesToEdge, 1);
+ numSamples -= numSamplesToEdge;
+ targetPosition = 0;
+ }
+ interpolateChannel((int16 *)(_loopBuffer + targetPosition), numSamples, 1);
+ _jointMin[1] += numBytes;
+ }
+ } else if (_readHeadAbs > _jointMin[0]) {
+ if (targetPosition + numBytes >= _loopBufferSize) {
+ const int32 numSamplesToEdge = (_loopBufferSize - targetPosition) / sizeof(int16) / kEOSExpansion;
+ interpolateChannel((int16 *)(_loopBuffer + targetPosition), numSamplesToEdge, 0);
+ numSamples -= numSamplesToEdge;
+ targetPosition = 2;
+ }
+ interpolateChannel((int16 *)(_loopBuffer + targetPosition), numSamples, 0);
+ _jointMin[0] += numBytes;
+ }
+}
+
+void RobotAudioStream::finish() {
+ Common::StackLock lock(_mutex);
+ _finished = true;
+}
+
+RobotAudioStream::StreamState RobotAudioStream::getStatus() const {
+ Common::StackLock lock(_mutex);
+ StreamState status;
+ status.bytesPlaying = _readHeadAbs;
+ status.rate = getRate();
+ status.bits = 8 * sizeof(int16);
+ return status;
+}
+
+int RobotAudioStream::readBuffer(Audio::st_sample_t *outBuffer, int numSamples) {
+ Common::StackLock lock(_mutex);
+
+ if (_waiting) {
+ return 0;
+ }
+
+ assert(!((_writeHeadAbs - _readHeadAbs) & 1));
+ const int maxNumSamples = (_writeHeadAbs - _readHeadAbs) / sizeof(Audio::st_sample_t);
+ numSamples = MIN(numSamples, maxNumSamples);
+
+ if (!numSamples) {
+ return 0;
+ }
+
+ interpolateMissingSamples(numSamples);
+
+ Audio::st_sample_t *inBuffer = (Audio::st_sample_t *)(_loopBuffer + _readHead);
+
+ assert(!((_loopBufferSize - _readHead) & 1));
+ const int numSamplesToEnd = (_loopBufferSize - _readHead) / sizeof(Audio::st_sample_t);
+
+ int numSamplesToRead = MIN(numSamples, numSamplesToEnd);
+ Common::copy(inBuffer, inBuffer + numSamplesToRead, outBuffer);
+
+ if (numSamplesToRead < numSamples) {
+ inBuffer = (Audio::st_sample_t *)_loopBuffer;
+ outBuffer += numSamplesToRead;
+ numSamplesToRead = numSamples - numSamplesToRead;
+ Common::copy(inBuffer, inBuffer + numSamplesToRead, outBuffer);
+ }
+
+ const int32 numBytes = numSamples * sizeof(Audio::st_sample_t);
+
+ _readHead += numBytes;
+ if (_readHead > _loopBufferSize) {
+ _readHead -= _loopBufferSize;
+ }
+ _readHeadAbs += numBytes;
+ _maxWriteAbs += numBytes;
+ assert(!(_readHead & 1));
+ assert(!(_readHeadAbs & 1));
+
+ return numSamples;
+}
+
+#pragma mark -
+#pragma mark RobotDecoder
+
+RobotDecoder::RobotDecoder(SegManager *segMan) :
+ _delayTime(this),
+ _segMan(segMan),
+ _status(kRobotStatusUninitialized),
+ _audioBuffer(nullptr),
+ _rawPalette((uint8 *)malloc(kRawPaletteSize)) {}
+
+RobotDecoder::~RobotDecoder() {
+ close();
+ free(_rawPalette);
+ free(_audioBuffer);
+}
+
+#pragma mark -
+#pragma mark RobotDecoder - Initialization
+
+void RobotDecoder::initStream(const GuiResourceId robotId) {
+ const Common::String fileName = Common::String::format("%d.rbt", robotId);
Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(fileName);
+ _fileOffset = 0;
- if (!stream) {
- warning("Unable to open robot file %s", fileName.c_str());
- return false;
+ if (stream == nullptr) {
+ error("Unable to open robot file %s", fileName.c_str());
+ }
+
+ const uint16 id = stream->readUint16LE();
+ if (id != 0x16) {
+ error("Invalid robot file %s", fileName.c_str());
+ }
+
+ // TODO: Mac version not tested, so this could be totally wrong
+ _stream = new Common::SeekableSubReadStreamEndian(stream, 0, stream->size(), g_sci->getPlatform() == Common::kPlatformMacintosh, DisposeAfterUse::YES);
+ _stream->seek(2, SEEK_SET);
+ if (_stream->readUint32BE() != MKTAG('S', 'O', 'L', 0)) {
+ error("Resource %s is not Robot type!", fileName.c_str());
+ }
+}
+
+void RobotDecoder::initPlayback() {
+ _startFrameNo = 0;
+ _startTime = -1;
+ _startingFrameNo = -1;
+ _cueForceShowFrame = -1;
+ _previousFrameNo = -1;
+ _currentFrameNo = 0;
+ _status = kRobotStatusPaused;
+}
+
+void RobotDecoder::initAudio() {
+ _syncFrame = true;
+
+ _audioRecordInterval = RobotAudioStream::kRobotSampleRate / _frameRate;
+
+ // TODO: Might actually be for all games newer than Lighthouse; check to
+ // see which games have this condition.
+ if (g_sci->getGameId() != GID_LIGHTHOUSE && !(_audioRecordInterval & 1)) {
+ ++_audioRecordInterval;
+ }
+
+ _expectedAudioBlockSize = _audioBlockSize - kAudioBlockHeaderSize;
+ _audioBuffer = (byte *)realloc(_audioBuffer, kRobotZeroCompressSize + _expectedAudioBlockSize);
+
+ if (_primerReservedSize != 0) {
+ const int32 primerHeaderPosition = _stream->pos();
+ _totalPrimerSize = _stream->readSint32();
+ const int16 compressionType = _stream->readSint16();
+ _evenPrimerSize = _stream->readSint32();
+ _oddPrimerSize = _stream->readSint32();
+ _primerPosition = _stream->pos();
+
+ if (compressionType) {
+ error("Unknown audio header compression type %d", compressionType);
+ }
+
+ if (_evenPrimerSize + _oddPrimerSize != _primerReservedSize) {
+ _stream->seek(primerHeaderPosition + _primerReservedSize, SEEK_SET);
+ }
+ } else if (_primerZeroCompressFlag) {
+ _evenPrimerSize = 19922;
+ _oddPrimerSize = 21024;
+ }
+
+ _firstAudioRecordPosition = _evenPrimerSize * 2;
+
+ const int usedEachFrame = (RobotAudioStream::kRobotSampleRate / 2) / _frameRate;
+ _maxSkippablePackets = MAX(0, _audioBlockSize / usedEachFrame - 1);
+}
+
+void RobotDecoder::initVideo(const int16 x, const int16 y, const int16 scale, const reg_t plane, const bool hasPalette, const uint16 paletteSize) {
+ _position = Common::Point(x, y);
+
+ if (scale != 128) {
+ _scaleInfo.x = scale;
+ _scaleInfo.y = scale;
+ _scaleInfo.signal = kScaleSignalDoScaling32;
+ }
+
+ _plane = g_sci->_gfxFrameout->getPlanes().findByObject(plane);
+ if (_plane == nullptr) {
+ error("Invalid plane %04x:%04x passed to RobotDecoder::open", PRINT_REG(plane));
+ }
+
+ _minFrameRate = _frameRate - kMaxFrameRateDrift;
+ _maxFrameRate = _frameRate + kMaxFrameRateDrift;
+
+ if (_xResolution == 0 || _yResolution == 0) {
+ // TODO: Default values were taken from RESOURCE.CFG hires property
+ // if it exists, so need to check games' configuration files for those
+ _xResolution = g_sci->_gfxFrameout->getCurrentBuffer().screenWidth;
+ _yResolution = g_sci->_gfxFrameout->getCurrentBuffer().screenHeight;
}
- return loadStream(stream);
+ if (hasPalette) {
+ _stream->read(_rawPalette, paletteSize);
+ } else {
+ _stream->seek(paletteSize, SEEK_CUR);
+ }
+
+ _screenItemList.reserve(kScreenItemListSize);
+ _maxCelArea.reserve(kFixedCelListSize);
+
+ // Fixed cel buffers are for version 5 and newer
+ _fixedCels.reserve(MIN(_maxCelsPerFrame, (int16)kFixedCelListSize));
+ _celDecompressionBuffer.reserve(_maxCelArea[0] + SciBitmap::getBitmapHeaderSize() + kRawPaletteSize);
+ _celDecompressionArea = _maxCelArea[0];
+}
+
+void RobotDecoder::initRecordAndCuePositions() {
+ PositionList recordSizes;
+ _videoSizes.reserve(_numFramesTotal);
+ _recordPositions.reserve(_numFramesTotal);
+ recordSizes.reserve(_numFramesTotal);
+
+ switch(_version) {
+ case 5: // 16-bit sizes and positions
+ for (int i = 0; i < _numFramesTotal; ++i) {
+ _videoSizes.push_back(_stream->readUint16());
+ }
+ for (int i = 0; i < _numFramesTotal; ++i) {
+ recordSizes.push_back(_stream->readUint16());
+ }
+ break;
+ case 6: // 32-bit sizes and positions
+ for (int i = 0; i < _numFramesTotal; ++i) {
+ _videoSizes.push_back(_stream->readSint32());
+ }
+ for (int i = 0; i < _numFramesTotal; ++i) {
+ recordSizes.push_back(_stream->readSint32());
+ }
+ break;
+ default:
+ error("Unknown Robot version %d", _version);
+ }
+
+ for (int i = 0; i < kCueListSize; ++i) {
+ _cueTimes[i] = _stream->readSint32();
+ }
+
+ for (int i = 0; i < kCueListSize; ++i) {
+ _cueValues[i] = _stream->readUint16();
+ }
+
+ Common::copy(_cueTimes, _cueTimes + kCueListSize, _masterCueTimes);
+
+ int bytesRemaining = (_stream->pos() - _fileOffset) % kRobotFrameSize;
+ if (bytesRemaining != 0) {
+ _stream->seek(kRobotFrameSize - bytesRemaining, SEEK_CUR);
+ }
+
+ int position = _stream->pos();
+ _recordPositions.push_back(position);
+ for (int i = 0; i < _numFramesTotal - 1; ++i) {
+ position += recordSizes[i];
+ _recordPositions.push_back(position);
+ }
+}
+
+#pragma mark -
+#pragma mark RobotDecoder - Playback
+
+void RobotDecoder::open(const GuiResourceId robotId, const reg_t plane, const int16 priority, const int16 x, const int16 y, const int16 scale) {
+ if (_status != kRobotStatusUninitialized) {
+ warning("Last robot was not closed");
+ close();
+ }
+
+ initStream(robotId);
+
+ _version = _stream->readUint16();
+
+ // TODO: Version 4 for PQ:SWAT demo?
+ if (_version < 5 || _version > 6) {
+ error("Unsupported version %d of Robot resource", _version);
+ }
+
+ debugC(kDebugLevelVideo, "Opening version %d robot %d", _version, robotId);
+
+ initPlayback();
+
+ _audioBlockSize = _stream->readUint16();
+ _primerZeroCompressFlag = _stream->readSint16();
+ _stream->seek(2, SEEK_CUR); // unused
+ _numFramesTotal = _stream->readUint16();
+ const uint16 paletteSize = _stream->readUint16();
+ _primerReservedSize = _stream->readUint16();
+ _xResolution = _stream->readSint16();
+ _yResolution = _stream->readSint16();
+ const bool hasPalette = (bool)_stream->readByte();
+ _hasAudio = (bool)_stream->readByte();
+ _stream->seek(2, SEEK_CUR); // unused
+ _frameRate = _normalFrameRate = _stream->readSint16();
+ _isHiRes = (bool)_stream->readSint16();
+ _maxSkippablePackets = _stream->readSint16();
+ _maxCelsPerFrame = _stream->readSint16();
+
+ // used for memory preallocation of fixed cels
+ _maxCelArea.push_back(_stream->readSint32());
+ _maxCelArea.push_back(_stream->readSint32());
+ _maxCelArea.push_back(_stream->readSint32());
+ _maxCelArea.push_back(_stream->readSint32());
+ _stream->seek(8, SEEK_CUR); // reserved
+
+ if (_hasAudio) {
+ initAudio();
+ } else {
+ _stream->seek(_primerReservedSize, SEEK_CUR);
+ }
+
+ _priority = priority;
+ initVideo(x, y, scale, plane, hasPalette, paletteSize);
+ initRecordAndCuePositions();
}
void RobotDecoder::close() {
- VideoDecoder::close();
+ if (_status == kRobotStatusUninitialized) {
+ return;
+ }
+
+ debugC(kDebugLevelVideo, "Closing robot");
- delete _fileStream;
- _fileStream = 0;
+ _status = kRobotStatusUninitialized;
+ _videoSizes.clear();
+ _recordPositions.clear();
+ _celDecompressionBuffer.clear();
+ _doVersion5Scratch.clear();
+ delete _stream;
+ _stream = nullptr;
+
+ for (CelHandleList::size_type i = 0; i < _celHandles.size(); ++i) {
+ if (_celHandles[i].status == CelHandleInfo::kFrameLifetime) {
+ _segMan->freeBitmap(_celHandles[i].bitmapId);
+ }
+ }
+ _celHandles.clear();
+
+ for (FixedCelsList::size_type i = 0; i < _fixedCels.size(); ++i) {
+ _segMan->freeBitmap(_fixedCels[i]);
+ }
+ _fixedCels.clear();
+
+ if (g_sci->_gfxFrameout->getPlanes().findByObject(_plane->_object) != nullptr) {
+ for (RobotScreenItemList::size_type i = 0; i < _screenItemList.size(); ++i) {
+ if (_screenItemList[i] != nullptr) {
+ g_sci->_gfxFrameout->deleteScreenItem(*_screenItemList[i]);
+ }
+ }
+ }
+ _screenItemList.clear();
- delete[] _frameTotalSize;
- _frameTotalSize = 0;
+ if (_hasAudio) {
+ _audioList.reset();
+ }
}
-void RobotDecoder::readNextPacket() {
- // Get our track
- RobotVideoTrack *videoTrack = (RobotVideoTrack *)getTrack(0);
- videoTrack->increaseCurFrame();
- Graphics::Surface *surface = videoTrack->getSurface();
+void RobotDecoder::pause() {
+ if (_status != kRobotStatusPlaying) {
+ return;
+ }
+
+ if (_hasAudio) {
+ _audioList.stopAudioNow();
+ }
+
+ _status = kRobotStatusPaused;
+ _frameRate = _normalFrameRate;
+}
- if (videoTrack->endOfTrack())
+void RobotDecoder::resume() {
+ if (_status != kRobotStatusPaused) {
return;
+ }
+
+ _startingFrameNo = _currentFrameNo;
+ _status = kRobotStatusPlaying;
+ if (_hasAudio) {
+ primeAudio(_currentFrameNo * 60 / _frameRate);
+ _syncFrame = true;
+ }
+
+ setRobotTime(_currentFrameNo);
+ for (int i = 0; i < kCueListSize; ++i) {
+ if (_masterCueTimes[i] != -1 && _masterCueTimes[i] < _currentFrameNo) {
+ _cueTimes[i] = -1;
+ } else {
+ _cueTimes[i] = _masterCueTimes[i];
+ }
+ }
+}
- // Read frame image header (24 bytes)
- _fileStream->skip(3);
- byte frameScale = _fileStream->readByte();
- uint16 frameWidth = _fileStream->readUint16();
- uint16 frameHeight = _fileStream->readUint16();
- _fileStream->skip(4); // unknown, almost always 0
- uint16 frameX = _fileStream->readUint16();
- uint16 frameY = _fileStream->readUint16();
-
- // TODO: In v4 robot files, frameX and frameY have a different meaning.
- // Set them both to 0 for v4 for now, so that robots in PQ:SWAT show up
- // correctly.
- if (_header.version == 4)
- frameX = frameY = 0;
-
- uint16 compressedSize = _fileStream->readUint16();
- uint16 frameFragments = _fileStream->readUint16();
- _fileStream->skip(4); // unknown
- uint32 decompressedSize = frameWidth * frameHeight * frameScale / 100;
-
- // FIXME: A frame's height + position can go off limits... why? With the
- // following, we cut the contents to fit the frame
- uint16 scaledHeight = CLIP<uint16>(decompressedSize / frameWidth, 0, surface->h - frameY);
-
- // FIXME: Same goes for the frame's width + position. In this case, we
- // modify the position to fit the contents on screen.
- if (frameWidth + frameX > surface->w)
- frameX = surface->w - frameWidth;
-
- assert(frameWidth + frameX <= surface->w && scaledHeight + frameY <= surface->h);
-
- DecompressorLZS lzs;
- byte *decompressedFrame = new byte[decompressedSize];
- byte *outPtr = decompressedFrame;
-
- if (_header.version == 4) {
- // v4 has just the one fragment, it seems, and ignores the fragment count
- Common::SeekableSubReadStream fragmentStream(_fileStream, _fileStream->pos(), _fileStream->pos() + compressedSize);
- lzs.unpack(&fragmentStream, outPtr, compressedSize, decompressedSize);
+void RobotDecoder::showFrame(const uint16 frameNo, const uint16 newX, const uint16 newY, const uint16 newPriority) {
+ debugC(kDebugLevelVideo, "Show frame %d (%d %d %d)", frameNo, newX, newY, newPriority);
+
+ if (newX != kUnspecified) {
+ _position.x = newX;
+ }
+
+ if (newY != kUnspecified) {
+ _position.y = newY;
+ }
+
+ if (newPriority != kUnspecified) {
+ _priority = newPriority;
+ }
+
+ _currentFrameNo = frameNo;
+ pause();
+
+ if (frameNo != _previousFrameNo) {
+ seekToFrame(frameNo);
+ doVersion5(false);
} else {
- for (uint16 i = 0; i < frameFragments; ++i) {
- uint32 compressedFragmentSize = _fileStream->readUint32();
- uint32 decompressedFragmentSize = _fileStream->readUint32();
- uint16 compressionType = _fileStream->readUint16();
-
- if (compressionType == 0) {
- Common::SeekableSubReadStream fragmentStream(_fileStream, _fileStream->pos(), _fileStream->pos() + compressedFragmentSize);
- lzs.unpack(&fragmentStream, outPtr, compressedFragmentSize, decompressedFragmentSize);
- } else if (compressionType == 2) { // untested
- _fileStream->read(outPtr, compressedFragmentSize);
+ for (RobotScreenItemList::size_type i = 0; i < _screenItemList.size(); ++i) {
+ if (_isHiRes) {
+ SciBitmap &bitmap = *_segMan->lookupBitmap(_celHandles[i].bitmapId);
+
+ const int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth;
+ const int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight;
+ const int16 screenWidth = g_sci->_gfxFrameout->getCurrentBuffer().screenWidth;
+ const int16 screenHeight = g_sci->_gfxFrameout->getCurrentBuffer().screenHeight;
+
+ if (scriptWidth == kLowResX && scriptHeight == kLowResY) {
+ const Ratio lowResToScreenX(screenWidth, kLowResX);
+ const Ratio lowResToScreenY(screenHeight, kLowResY);
+ const Ratio screenToLowResX(kLowResX, screenWidth);
+ const Ratio screenToLowResY(kLowResY, screenHeight);
+
+ const int16 scaledX = _originalScreenItemX[i] + (_position.x * lowResToScreenX).toInt();
+ const int16 scaledY1 = _originalScreenItemY[i] + (_position.y * lowResToScreenY).toInt();
+ const int16 scaledY2 = scaledY1 + bitmap.getHeight() - 1;
+
+ const int16 lowResX = (scaledX * screenToLowResX).toInt();
+ const int16 lowResY = (scaledY2 * screenToLowResY).toInt();
+
+ bitmap.setDisplace(Common::Point(
+ (scaledX - (lowResX * lowResToScreenX).toInt()) * -1,
+ (lowResY * lowResToScreenY).toInt() - scaledY1
+ ));
+
+ _screenItemX[i] = lowResX;
+ _screenItemY[i] = lowResY;
+ } else {
+ const int16 scaledX = _originalScreenItemX[i] + _position.x;
+ const int16 scaledY = _originalScreenItemY[i] + _position.y + bitmap.getHeight() - 1;
+ bitmap.setDisplace(Common::Point(0, bitmap.getHeight() - 1));
+ _screenItemX[i] = scaledX;
+ _screenItemY[i] = scaledY;
+ }
+ } else {
+ _screenItemX[i] = _originalScreenItemX[i] + _position.x;
+ _screenItemY[i] = _originalScreenItemY[i] + _position.y;
+ }
+
+ if (_screenItemList[i] == nullptr) {
+ CelInfo32 celInfo;
+ celInfo.type = kCelTypeMem;
+ celInfo.bitmap = _celHandles[i].bitmapId;
+ ScreenItem *screenItem = new ScreenItem(_plane->_object, celInfo);
+ _screenItemList[i] = screenItem;
+ screenItem->_position = Common::Point(_screenItemX[i], _screenItemY[i]);
+ if (_priority == -1) {
+ screenItem->_fixedPriority = false;
+ } else {
+ screenItem->_priority = _priority;
+ screenItem->_fixedPriority = true;
+ }
+ g_sci->_gfxFrameout->addScreenItem(*screenItem);
} else {
- error("Unknown frame compression found: %d", compressionType);
+ ScreenItem *screenItem = _screenItemList[i];
+ screenItem->_celInfo.bitmap = _celHandles[i].bitmapId;
+ screenItem->_position = Common::Point(_screenItemX[i], _screenItemY[i]);
+ if (_priority == -1) {
+ screenItem->_fixedPriority = false;
+ } else {
+ screenItem->_priority = _priority;
+ screenItem->_fixedPriority = true;
+ }
+ g_sci->_gfxFrameout->updateScreenItem(*screenItem);
+ }
+ }
+ }
+
+ _previousFrameNo = frameNo;
+}
+
+int16 RobotDecoder::getCue() const {
+ if (_status == kRobotStatusUninitialized ||
+ _status == kRobotStatusPaused ||
+ _syncFrame) {
+ return 0;
+ }
+
+ if (_status == kRobotStatusEnd) {
+ return -1;
+ }
+
+ const uint16 estimatedNextFrameNo = MIN(calculateNextFrameNo(_delayTime.predictedTicks()), _numFramesTotal);
+
+ for (int i = 0; i < kCueListSize; ++i) {
+ if (_cueTimes[i] != -1 && _cueTimes[i] <= estimatedNextFrameNo) {
+ if (_cueTimes[i] >= _previousFrameNo) {
+ _cueForceShowFrame = _cueTimes[i] + 1;
}
- outPtr += decompressedFragmentSize;
+ _cueTimes[i] = -1;
+ return _cueValues[i];
}
}
- // Copy over the decompressed frame
- byte *inFrame = decompressedFrame;
- byte *outFrame = (byte *)surface->getPixels();
+ return 0;
+}
- // Black out the surface
- memset(outFrame, 0, surface->w * surface->h);
+int16 RobotDecoder::getFrameNo() const {
+ if (_status == kRobotStatusUninitialized) {
+ return 0;
+ }
- // Move to the correct y coordinate
- outFrame += surface->w * frameY;
+ return _currentFrameNo;
+}
+
+RobotDecoder::RobotStatus RobotDecoder::getStatus() const {
+ return _status;
+}
- for (uint16 y = 0; y < scaledHeight; y++) {
- memcpy(outFrame + frameX, inFrame, frameWidth);
- inFrame += frameWidth;
- outFrame += surface->w;
+bool RobotDecoder::seekToFrame(const int frameNo) {
+ return _stream->seek(_recordPositions[frameNo], SEEK_SET);
+}
+
+void RobotDecoder::setRobotTime(const int frameNo) {
+ _startTime = getTickCount();
+ _startFrameNo = frameNo;
+}
+
+#pragma mark -
+#pragma mark RobotDecoder - Timing
+
+RobotDecoder::DelayTime::DelayTime(RobotDecoder *decoder) :
+ _decoder(decoder) {
+ for (int i = 0; i < kDelayListSize; ++i) {
+ _timestamps[i] = i;
+ _delays[i] = 0;
}
- delete[] decompressedFrame;
+ _oldestTimestamp = 0;
+ _newestTimestamp = kDelayListSize - 1;
+ _startTime = 0;
+}
- uint32 audioChunkSize = _frameTotalSize[videoTrack->getCurFrame()] - (24 + compressedSize);
+void RobotDecoder::DelayTime::startTiming() {
+ _startTime = _decoder->getTickCount();
+}
-// TODO: The audio chunk size below is usually correct, but there are some
-// exceptions (e.g. robot 4902 in Phantasmagoria, towards its end)
-#if 0
- // Read frame audio header (14 bytes)
- _fileStream->skip(2); // buffer position
- _fileStream->skip(2); // unknown (usually 1)
- _fileStream->skip(2); /*uint16 audioChunkSize = _fileStream->readUint16() + 8;*/
- _fileStream->skip(2);
-#endif
+void RobotDecoder::DelayTime::endTiming() {
+ const int timeDelta = _decoder->getTickCount() - _startTime;
+ for (uint i = 0; i < kDelayListSize; ++i) {
+ if (_timestamps[i] == _oldestTimestamp) {
+ _timestamps[i] = ++_newestTimestamp;
+ _delays[i] = timeDelta;
+ break;
+ }
+ }
+ ++_newestTimestamp;
+ _startTime = 0;
+ sortList();
+}
- // Queue the next audio frame
- // FIXME: For some reason, there are audio hiccups/gaps
- if (_header.hasSound) {
- RobotAudioTrack *audioTrack = (RobotAudioTrack *)getTrack(1);
- _fileStream->skip(8); // header
- audioChunkSize -= 8;
- audioTrack->queueBuffer(g_sci->_audio->getDecodedRobotAudioFrame(_fileStream, audioChunkSize), audioChunkSize * 2);
+bool RobotDecoder::DelayTime::timingInProgress() const {
+ return _startTime != 0;
+}
+
+int RobotDecoder::DelayTime::predictedTicks() const {
+ return _delays[kDelayListSize / 2];
+}
+
+void RobotDecoder::DelayTime::sortList() {
+ for (uint i = 0; i < kDelayListSize - 1; ++i) {
+ int smallestDelay = _delays[i];
+ uint smallestIndex = i;
+
+ for (uint j = i + 1; j < kDelayListSize - 1; ++j) {
+ if (_delays[j] < smallestDelay) {
+ smallestDelay = _delays[j];
+ smallestIndex = j;
+ }
+ }
+
+ if (smallestIndex != i) {
+ SWAP(_delays[i], _delays[smallestIndex]);
+ SWAP(_timestamps[i], _timestamps[smallestIndex]);
+ }
+ }
+}
+
+uint16 RobotDecoder::calculateNextFrameNo(const uint32 extraTicks) const {
+ return ticksToFrames(getTickCount() + extraTicks - _startTime) + _startFrameNo;
+}
+
+uint32 RobotDecoder::ticksToFrames(const uint32 ticks) const {
+ return (ticks * _frameRate) / 60;
+}
+
+uint32 RobotDecoder::getTickCount() const {
+ return g_sci->getTickCount();
+}
+
+#pragma mark -
+#pragma mark RobotDecoder - Audio
+
+RobotDecoder::AudioList::AudioList() :
+ _blocks(),
+ _blocksSize(0),
+ _oldestBlockIndex(0),
+ _newestBlockIndex(0),
+ _startOffset(0),
+ _status(kRobotAudioReady) {}
+
+void RobotDecoder::AudioList::startAudioNow() {
+ submitDriverMax();
+ g_sci->_audio32->resume(kRobotChannel);
+ _status = kRobotAudioPlaying;
+}
+
+void RobotDecoder::AudioList::stopAudio() {
+ g_sci->_audio32->finishRobotAudio();
+ freeAudioBlocks();
+ _status = kRobotAudioStopping;
+}
+
+void RobotDecoder::AudioList::stopAudioNow() {
+ if (_status == kRobotAudioPlaying || _status == kRobotAudioStopping || _status == kRobotAudioPaused) {
+ g_sci->_audio32->stopRobotAudio();
+ _status = kRobotAudioStopped;
+ }
+
+ freeAudioBlocks();
+}
+
+void RobotDecoder::AudioList::submitDriverMax() {
+ while (_blocksSize != 0) {
+ if (!_blocks[_oldestBlockIndex]->submit(_startOffset)) {
+ return;
+ }
+
+ delete _blocks[_oldestBlockIndex];
+ _blocks[_oldestBlockIndex] = nullptr;
+ ++_oldestBlockIndex;
+ if (_oldestBlockIndex == kAudioListSize) {
+ _oldestBlockIndex = 0;
+ }
+
+ --_blocksSize;
+ }
+}
+
+void RobotDecoder::AudioList::addBlock(const int position, const int size, const byte *data) {
+ assert(data != nullptr);
+ assert(size >= 0);
+ assert(position >= -1);
+
+ if (_blocksSize == kAudioListSize) {
+ delete _blocks[_oldestBlockIndex];
+ _blocks[_oldestBlockIndex] = nullptr;
+ ++_oldestBlockIndex;
+ if (_oldestBlockIndex == kAudioListSize) {
+ _oldestBlockIndex = 0;
+ }
+ --_blocksSize;
+ }
+
+ if (_blocksSize == 0) {
+ _oldestBlockIndex = _newestBlockIndex = 0;
} else {
- _fileStream->skip(audioChunkSize);
- }
-}
-
-void RobotDecoder::readHeaderChunk() {
- // Header (60 bytes)
- _fileStream->skip(6);
- _header.version = _fileStream->readUint16();
- _header.audioChunkSize = _fileStream->readUint16();
- _header.audioSilenceSize = _fileStream->readUint16();
- _fileStream->skip(2);
- _header.frameCount = _fileStream->readUint16();
- _header.paletteDataSize = _fileStream->readUint16();
- _header.unkChunkDataSize = _fileStream->readUint16();
- _fileStream->skip(5);
- _header.hasSound = _fileStream->readByte();
- _fileStream->skip(34);
-
- // Some videos (e.g. robot 1305 in Phantasmagoria and
- // robot 184 in Lighthouse) have an unknown chunk before
- // the palette chunk (probably used for sound preloading).
- // Skip it here.
- if (_header.unkChunkDataSize)
- _fileStream->skip(_header.unkChunkDataSize);
-}
-
-void RobotDecoder::readFrameSizesChunk() {
- // The robot video file contains 2 tables, with one entry for each frame:
- // - A table containing the size of the image in each video frame
- // - A table containing the total size of each video frame.
- // In v5 robots, the tables contain 16-bit integers, whereas in v6 robots,
- // they contain 32-bit integers.
-
- _frameTotalSize = new uint32[_header.frameCount];
-
- // TODO: The table reading code can probably be removed once the
- // audio chunk size is figured out (check the TODO inside processNextFrame())
-#if 0
- // We don't need any of the two tables to play the video, so we ignore
- // both of them.
- uint16 wordSize = _header.version == 6 ? 4 : 2;
- _fileStream->skip(_header.frameCount * wordSize * 2);
-#else
- switch (_header.version) {
- case 4:
- case 5: // sizes are 16-bit integers
- // Skip table with frame image sizes, as we don't need it
- _fileStream->skip(_header.frameCount * 2);
- for (int i = 0; i < _header.frameCount; ++i)
- _frameTotalSize[i] = _fileStream->readUint16();
- break;
- case 6: // sizes are 32-bit integers
- // Skip table with frame image sizes, as we don't need it
- _fileStream->skip(_header.frameCount * 4);
- for (int i = 0; i < _header.frameCount; ++i)
- _frameTotalSize[i] = _fileStream->readUint32();
- break;
- default:
- error("Can't yet handle index table for robot version %d", _header.version);
+ ++_newestBlockIndex;
+ if (_newestBlockIndex == kAudioListSize) {
+ _newestBlockIndex = 0;
+ }
}
-#endif
- // 2 more unknown tables
- _fileStream->skip(1024 + 512);
+ _blocks[_newestBlockIndex] = new AudioBlock(position, size, data);
+ ++_blocksSize;
+}
- // Pad to nearest 2 kilobytes
- uint32 curPos = _fileStream->pos();
- if (curPos & 0x7ff)
- _fileStream->seek((curPos & ~0x7ff) + 2048);
+void RobotDecoder::AudioList::reset() {
+ stopAudioNow();
+ _startOffset = 0;
+ _status = kRobotAudioReady;
}
-RobotDecoder::RobotVideoTrack::RobotVideoTrack(int frameCount) : _frameCount(frameCount) {
- _surface = new Graphics::Surface();
- _curFrame = -1;
- _dirtyPalette = false;
+void RobotDecoder::AudioList::prepareForPrimer() {
+ g_sci->_audio32->pause(kRobotChannel);
+ _status = kRobotAudioPaused;
}
-RobotDecoder::RobotVideoTrack::~RobotVideoTrack() {
- _surface->free();
- delete _surface;
+void RobotDecoder::AudioList::setAudioOffset(const int offset) {
+ _startOffset = offset;
}
-uint16 RobotDecoder::RobotVideoTrack::getWidth() const {
- return _surface->w;
+RobotDecoder::AudioList::AudioBlock::AudioBlock(const int position, const int size, const byte* const data) :
+ _position(position),
+ _size(size) {
+ _data = (byte *)malloc(size);
+ memcpy(_data, data, size);
}
-uint16 RobotDecoder::RobotVideoTrack::getHeight() const {
- return _surface->h;
+RobotDecoder::AudioList::AudioBlock::~AudioBlock() {
+ free(_data);
}
-Graphics::PixelFormat RobotDecoder::RobotVideoTrack::getPixelFormat() const {
- return _surface->format;
+bool RobotDecoder::AudioList::AudioBlock::submit(const int startOffset) {
+ assert(_data != nullptr);
+ RobotAudioStream::RobotAudioPacket packet(_data, _size, (_position - startOffset) * 2);
+ return g_sci->_audio32->playRobotAudio(packet);
}
-void RobotDecoder::RobotVideoTrack::readPaletteChunk(Common::SeekableSubReadStreamEndian *stream, uint16 chunkSize) {
- byte *paletteData = new byte[chunkSize];
- stream->read(paletteData, chunkSize);
+void RobotDecoder::AudioList::freeAudioBlocks() {
+ while (_blocksSize != 0) {
+ delete _blocks[_oldestBlockIndex];
+ _blocks[_oldestBlockIndex] = nullptr;
+ ++_oldestBlockIndex;
+ if (_oldestBlockIndex == kAudioListSize) {
+ _oldestBlockIndex = 0;
+ }
+
+ --_blocksSize;
+ }
+}
- // SCI1.1 palette
- byte palFormat = paletteData[32];
- uint16 palColorStart = paletteData[25];
- uint16 palColorCount = READ_SCI11ENDIAN_UINT16(paletteData + 29);
+bool RobotDecoder::primeAudio(const uint32 startTick) {
+ bool success = true;
+ _audioList.reset();
+
+ if (startTick == 0) {
+ _audioList.prepareForPrimer();
+ byte *evenPrimerBuff = new byte[_evenPrimerSize];
+ byte *oddPrimerBuff = new byte[_oddPrimerSize];
+
+ success = readPrimerData(evenPrimerBuff, oddPrimerBuff);
+ if (success) {
+ if (_evenPrimerSize != 0) {
+ _audioList.addBlock(0, _evenPrimerSize, evenPrimerBuff);
+ }
+ if (_oddPrimerSize != 0) {
+ _audioList.addBlock(1, _oddPrimerSize, oddPrimerBuff);
+ }
+ }
+
+ delete[] evenPrimerBuff;
+ delete[] oddPrimerBuff;
+ } else {
+ assert(_evenPrimerSize * 2 >= _audioRecordInterval || _oddPrimerSize * 2 >= _audioRecordInterval);
+
+ int audioStartFrame = 0;
+ int videoStartFrame = startTick * _frameRate / 60;
+ assert(videoStartFrame < _numFramesTotal);
+
+ int audioStartPosition = (startTick * RobotAudioStream::kRobotSampleRate) / 60;
+ if (audioStartPosition & 1) {
+ audioStartPosition--;
+ }
+ _audioList.setAudioOffset(audioStartPosition);
+ _audioList.prepareForPrimer();
+
+ if (audioStartPosition < _evenPrimerSize * 2 ||
+ audioStartPosition + 1 < _oddPrimerSize * 2) {
+
+ byte *evenPrimerBuffer = new byte[_evenPrimerSize];
+ byte *oddPrimerBuffer = new byte[_oddPrimerSize];
+ success = readPrimerData(evenPrimerBuffer, oddPrimerBuffer);
+ if (success) {
+ int halfAudioStartPosition = audioStartPosition / 2;
+ if (audioStartPosition < _evenPrimerSize * 2) {
+ _audioList.addBlock(audioStartPosition, _evenPrimerSize - halfAudioStartPosition, &evenPrimerBuffer[halfAudioStartPosition]);
+ }
+
+ if (audioStartPosition + 1 < _oddPrimerSize * 2) {
+ _audioList.addBlock(audioStartPosition + 1, _oddPrimerSize - halfAudioStartPosition, &oddPrimerBuffer[halfAudioStartPosition]);
+ }
+ }
+
+ delete[] evenPrimerBuffer;
+ delete[] oddPrimerBuffer;
+ }
- int palOffset = 37;
- memset(_palette, 0, 256 * 3);
+ if (audioStartPosition >= _firstAudioRecordPosition) {
+ int audioRecordSize = _expectedAudioBlockSize;
+ assert(audioRecordSize > 0);
+ assert(_audioRecordInterval > 0);
+ assert(_firstAudioRecordPosition >= 0);
- for (uint16 colorNo = palColorStart; colorNo < palColorStart + palColorCount; colorNo++) {
- if (palFormat == kRobotPalVariable)
- palOffset++;
- _palette[colorNo * 3 + 0] = paletteData[palOffset++];
- _palette[colorNo * 3 + 1] = paletteData[palOffset++];
- _palette[colorNo * 3 + 2] = paletteData[palOffset++];
+ audioStartFrame = (audioStartPosition - _firstAudioRecordPosition) / _audioRecordInterval;
+ assert(audioStartFrame < videoStartFrame);
+
+ if (audioStartFrame > 0) {
+ int lastAudioFrame = audioStartFrame - 1;
+ int oddRemainder = lastAudioFrame & 1;
+ int audioRecordStart = (lastAudioFrame * _audioRecordInterval) + oddRemainder + _firstAudioRecordPosition;
+ int audioRecordEnd = (audioRecordStart + ((audioRecordSize - 1) * 2)) + oddRemainder + _firstAudioRecordPosition;
+
+ if (audioStartPosition >= audioRecordStart && audioStartPosition <= audioRecordEnd) {
+ --audioStartFrame;
+ }
+ }
+
+ assert(!(audioStartPosition & 1));
+ if (audioStartFrame & 1) {
+ ++audioStartPosition;
+ }
+
+ if (!readPartialAudioRecordAndSubmit(audioStartFrame, audioStartPosition)) {
+ return false;
+ }
+
+ ++audioStartFrame;
+ assert(audioStartFrame < videoStartFrame);
+
+ int oddRemainder = audioStartFrame & 1;
+ int audioRecordStart = (audioStartFrame * _audioRecordInterval) + oddRemainder + _firstAudioRecordPosition;
+ int audioRecordEnd = (audioRecordStart + ((audioRecordSize - 1) * 2)) + oddRemainder + _firstAudioRecordPosition;
+
+ if (audioStartPosition >= audioRecordStart && audioStartPosition <= audioRecordEnd) {
+ if (!readPartialAudioRecordAndSubmit(audioStartFrame, audioStartPosition + 1)) {
+ return false;
+ }
+
+ ++audioStartFrame;
+ }
+ }
+
+ int audioPosition, audioSize;
+ for (int i = audioStartFrame; i < videoStartFrame; i++) {
+ if (!readAudioDataFromRecord(i, _audioBuffer, audioPosition, audioSize)) {
+ break;
+ }
+
+ _audioList.addBlock(audioPosition, audioSize, _audioBuffer);
+ }
}
- _dirtyPalette = true;
- delete[] paletteData;
+ return success;
}
-void RobotDecoder::RobotVideoTrack::calculateVideoDimensions(Common::SeekableSubReadStreamEndian *stream, uint32 *frameSizes) {
- // This is an O(n) operation, as each frame has a different size.
- // We need to know the actual frame size to have a constant video size.
- uint32 pos = stream->pos();
+bool RobotDecoder::readPrimerData(byte *outEvenBuffer, byte *outOddBuffer) {
+ if (_primerReservedSize != 0) {
+ if (_totalPrimerSize != 0) {
+ _stream->seek(_primerPosition, SEEK_SET);
+ if (_evenPrimerSize > 0) {
+ _stream->read(outEvenBuffer, _evenPrimerSize);
+ }
+
+ if (_oddPrimerSize > 0) {
+ _stream->read(outOddBuffer, _oddPrimerSize);
+ }
+ }
+ } else if (_primerZeroCompressFlag) {
+ memset(outEvenBuffer, 0, _evenPrimerSize);
+ memset(outOddBuffer, 0, _oddPrimerSize);
+ } else {
+ error("ReadPrimerData - Flags corrupt");
+ }
+
+ return !_stream->err();
+}
+
+bool RobotDecoder::readAudioDataFromRecord(const int frameNo, byte *outBuffer, int &outAudioPosition, int &outAudioSize) {
+ _stream->seek(_recordPositions[frameNo] + _videoSizes[frameNo], SEEK_SET);
+ _audioList.submitDriverMax();
+
+ // Compressed absolute position of the audio block in the audio stream
+ const int position = _stream->readSint32();
- uint16 width = 0, height = 0;
+ // Size of the block of audio, excluding the audio block header
+ int size = _stream->readSint32();
- for (int curFrame = 0; curFrame < _frameCount; curFrame++) {
- stream->skip(4);
- uint16 frameWidth = stream->readUint16();
- uint16 frameHeight = stream->readUint16();
- if (frameWidth > width)
- width = frameWidth;
- if (frameHeight > height)
- height = frameHeight;
- stream->skip(frameSizes[curFrame] - 8);
+ assert(size <= _expectedAudioBlockSize);
+
+ if (position == 0) {
+ return false;
}
- stream->seek(pos);
+ if (size != _expectedAudioBlockSize) {
+ memset(outBuffer, 0, kRobotZeroCompressSize);
+ _stream->read(outBuffer + kRobotZeroCompressSize, size);
+ size += kRobotZeroCompressSize;
+ } else {
+ _stream->read(outBuffer, size);
+ }
- _surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8());
+ outAudioPosition = position;
+ outAudioSize = size;
+ return !_stream->err();
}
-RobotDecoder::RobotAudioTrack::RobotAudioTrack() {
- _audioStream = Audio::makeQueuingAudioStream(11025, false);
+bool RobotDecoder::readPartialAudioRecordAndSubmit(const int startFrame, const int startPosition) {
+ int audioPosition, audioSize;
+ bool success = readAudioDataFromRecord(startFrame, _audioBuffer, audioPosition, audioSize);
+ if (success) {
+ const int relativeStartOffset = (startPosition - audioPosition) / 2;
+ _audioList.addBlock(startPosition, audioSize - relativeStartOffset, _audioBuffer + relativeStartOffset);
+ }
+
+ return success;
}
-RobotDecoder::RobotAudioTrack::~RobotAudioTrack() {
- delete _audioStream;
+#pragma mark -
+#pragma mark RobotDecoder - Rendering
+
+uint16 RobotDecoder::getFrameSize(Common::Rect &outRect) const {
+ outRect.clip(0, 0);
+ for (RobotScreenItemList::size_type i = 0; i < _screenItemList.size(); ++i) {
+ ScreenItem &screenItem = *_screenItemList[i];
+ outRect.extend(screenItem.getNowSeenRect(*_plane));
+ }
+
+ return _numFramesTotal;
}
-void RobotDecoder::RobotAudioTrack::queueBuffer(byte *buffer, int size) {
- _audioStream->queueBuffer(buffer, size, DisposeAfterUse::YES, Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN);
+void RobotDecoder::doRobot() {
+ if (_status != kRobotStatusPlaying) {
+ return;
+ }
+
+ if (!_syncFrame) {
+ if (_cueForceShowFrame != -1) {
+ _currentFrameNo = _cueForceShowFrame;
+ _cueForceShowFrame = -1;
+ } else {
+ const int nextFrameNo = calculateNextFrameNo(_delayTime.predictedTicks());
+ if (nextFrameNo < _currentFrameNo) {
+ return;
+ }
+ _currentFrameNo = nextFrameNo;
+ }
+ }
+
+ if (_currentFrameNo >= _numFramesTotal) {
+ const int finalFrameNo = _numFramesTotal - 1;
+ if (_previousFrameNo == finalFrameNo) {
+ _status = kRobotStatusEnd;
+ if (_hasAudio) {
+ _audioList.stopAudio();
+ _frameRate = _normalFrameRate;
+ _hasAudio = false;
+ }
+ return;
+ } else {
+ _currentFrameNo = finalFrameNo;
+ }
+ }
+
+ if (_currentFrameNo == _previousFrameNo) {
+ _audioList.submitDriverMax();
+ return;
+ }
+
+ if (_hasAudio) {
+ for (int candidateFrameNo = _previousFrameNo + _maxSkippablePackets + 1; candidateFrameNo < _currentFrameNo; candidateFrameNo += _maxSkippablePackets + 1) {
+
+ _audioList.submitDriverMax();
+
+ int audioPosition, audioSize;
+ if (readAudioDataFromRecord(candidateFrameNo, _audioBuffer, audioPosition, audioSize)) {
+ _audioList.addBlock(audioPosition, audioSize, _audioBuffer);
+ }
+ }
+ _audioList.submitDriverMax();
+ }
+
+ _delayTime.startTiming();
+ seekToFrame(_currentFrameNo);
+ doVersion5();
+ if (_hasAudio) {
+ _audioList.submitDriverMax();
+ }
+}
+
+void RobotDecoder::frameAlmostVisible() {
+ if (_status == kRobotStatusPlaying && !_syncFrame) {
+ if (_previousFrameNo != _currentFrameNo) {
+ while (calculateNextFrameNo() < _currentFrameNo) {
+ _audioList.submitDriverMax();
+ }
+ }
+ }
+}
+
+void RobotDecoder::frameNowVisible() {
+ if (_status != kRobotStatusPlaying) {
+ return;
+ }
+
+ if (_syncFrame) {
+ _syncFrame = false;
+ if (_hasAudio) {
+ _audioList.startAudioNow();
+ _checkAudioSyncTime = _startTime + kAudioSyncCheckInterval;
+ }
+
+ setRobotTime(_currentFrameNo);
+ }
+
+ if (_delayTime.timingInProgress()) {
+ _delayTime.endTiming();
+ }
+
+ if (_hasAudio) {
+ _audioList.submitDriverMax();
+ }
+
+ if (_previousFrameNo != _currentFrameNo) {
+ _previousFrameNo = _currentFrameNo;
+ }
+
+ if (!_syncFrame && _hasAudio && getTickCount() >= _checkAudioSyncTime) {
+ RobotAudioStream::StreamState status;
+ const bool success = g_sci->_audio32->queryRobotAudio(status);
+ if (!success) {
+ return;
+ }
+
+ const int bytesPerFrame = status.rate / _normalFrameRate * (status.bits == 16 ? 2 : 1);
+ // check again in 1/3rd second
+ _checkAudioSyncTime = getTickCount() + 60 / 3;
+
+ const int currentVideoFrameNo = calculateNextFrameNo() - _startingFrameNo;
+ const int currentAudioFrameNo = status.bytesPlaying / bytesPerFrame;
+ debugC(kDebugLevelVideo, "Video frame %d %s audio frame %d", currentVideoFrameNo, currentVideoFrameNo == currentAudioFrameNo ? "=" : currentVideoFrameNo < currentAudioFrameNo ? "<" : ">", currentAudioFrameNo);
+ if (currentVideoFrameNo < _numFramesTotal &&
+ currentAudioFrameNo < _numFramesTotal) {
+
+ bool shouldResetRobotTime = false;
+
+ if (currentAudioFrameNo < currentVideoFrameNo - 1 && _frameRate != _minFrameRate) {
+ debugC(kDebugLevelVideo, "[v] Reducing frame rate");
+ _frameRate = _minFrameRate;
+ shouldResetRobotTime = true;
+ } else if (currentAudioFrameNo > currentVideoFrameNo + 1 && _frameRate != _maxFrameRate) {
+ debugC(kDebugLevelVideo, "[^] Increasing frame rate");
+ _frameRate = _maxFrameRate;
+ shouldResetRobotTime = true;
+ } else if (_frameRate != _normalFrameRate) {
+ debugC(kDebugLevelVideo, "[=] Setting to normal frame rate");
+ _frameRate = _normalFrameRate;
+ shouldResetRobotTime = true;
+ }
+
+ if (shouldResetRobotTime) {
+ if (currentAudioFrameNo < _currentFrameNo) {
+ setRobotTime(_currentFrameNo);
+ } else {
+ setRobotTime(currentAudioFrameNo);
+ }
+ }
+ }
+ }
+}
+
+void RobotDecoder::expandCel(byte* target, const byte* source, const int16 celWidth, const int16 celHeight) const {
+ assert(source != nullptr && target != nullptr);
+
+ const int sourceHeight = (celHeight * _verticalScaleFactor) / 100;
+ assert(sourceHeight > 0);
+
+ const int16 numerator = celHeight;
+ const int16 denominator = sourceHeight;
+ int remainder = 0;
+ for (int16 y = sourceHeight - 1; y >= 0; --y) {
+ remainder += numerator;
+ int16 linesToDraw = remainder / denominator;
+ remainder %= denominator;
+
+ while (linesToDraw--) {
+ memcpy(target, source, celWidth);
+ target += celWidth;
+ }
+
+ source += celWidth;
+ }
+}
+
+void RobotDecoder::setPriority(const int16 newPriority) {
+ _priority = newPriority;
+}
+
+void RobotDecoder::doVersion5(const bool shouldSubmitAudio) {
+ const RobotScreenItemList::size_type oldScreenItemCount = _screenItemList.size();
+ const int videoSize = _videoSizes[_currentFrameNo];
+ _doVersion5Scratch.resize(videoSize);
+
+ byte *videoFrameData = _doVersion5Scratch.begin();
+
+ if (!_stream->read(videoFrameData, videoSize)) {
+ error("RobotDecoder::doVersion5: Read error");
+ }
+
+ const RobotScreenItemList::size_type screenItemCount = READ_SCI11ENDIAN_UINT16(videoFrameData);
+
+ if (screenItemCount > kScreenItemListSize) {
+ return;
+ }
+
+ if (_hasAudio &&
+ (getSciVersion() < SCI_VERSION_3 || shouldSubmitAudio)) {
+ int audioPosition, audioSize;
+ if (readAudioDataFromRecord(_currentFrameNo, _audioBuffer, audioPosition, audioSize)) {
+ _audioList.addBlock(audioPosition, audioSize, _audioBuffer);
+ }
+ }
+
+ if (screenItemCount > oldScreenItemCount) {
+ _screenItemList.resize(screenItemCount);
+ _screenItemX.resize(screenItemCount);
+ _screenItemY.resize(screenItemCount);
+ _originalScreenItemX.resize(screenItemCount);
+ _originalScreenItemY.resize(screenItemCount);
+ }
+
+ createCels5(videoFrameData + 2, screenItemCount, true);
+ for (RobotScreenItemList::size_type i = 0; i < screenItemCount; ++i) {
+ Common::Point position(_screenItemX[i], _screenItemY[i]);
+
+// TODO: Version 6 robot?
+// int scaleXRemainder;
+ if (_scaleInfo.signal == kScaleSignalDoScaling32) {
+ position.x = (position.x * _scaleInfo.x) / 128;
+// TODO: Version 6 robot?
+// scaleXRemainder = (position.x * _scaleInfo.x) % 128;
+ position.y = (position.y * _scaleInfo.y) / 128;
+ }
+
+ if (_screenItemList[i] == nullptr) {
+ CelInfo32 celInfo;
+ celInfo.bitmap = _celHandles[i].bitmapId;
+ ScreenItem *screenItem = new ScreenItem(_plane->_object, celInfo, position, _scaleInfo);
+ _screenItemList[i] = screenItem;
+ // TODO: Version 6 robot?
+ // screenItem->_field_30 = scaleXRemainder;
+
+ if (_priority == -1) {
+ screenItem->_fixedPriority = false;
+ } else {
+ screenItem->_fixedPriority = true;
+ screenItem->_priority = _priority;
+ }
+ g_sci->_gfxFrameout->addScreenItem(*screenItem);
+ } else {
+ ScreenItem *screenItem = _screenItemList[i];
+ screenItem->_celInfo.bitmap = _celHandles[i].bitmapId;
+ screenItem->_position = position;
+ // TODO: Version 6 robot?
+ // screenItem->_field_30 = scaleXRemainder;
+
+ if (_priority == -1) {
+ screenItem->_fixedPriority = false;
+ } else {
+ screenItem->_fixedPriority = true;
+ screenItem->_priority = _priority;
+ }
+ g_sci->_gfxFrameout->updateScreenItem(*screenItem);
+ }
+ }
+
+ for (RobotScreenItemList::size_type i = screenItemCount; i < oldScreenItemCount; ++i) {
+ if (_screenItemList[i] != nullptr) {
+ g_sci->_gfxFrameout->deleteScreenItem(*_screenItemList[i]);
+ _screenItemList[i] = nullptr;
+ }
+ }
+}
+
+void RobotDecoder::createCels5(const byte *rawVideoData, const int16 numCels, const bool usePalette) {
+ preallocateCelMemory(rawVideoData, numCels);
+ for (int16 i = 0; i < numCels; ++i) {
+ rawVideoData += createCel5(rawVideoData, i, usePalette);
+ }
+}
+
+uint32 RobotDecoder::createCel5(const byte *rawVideoData, const int16 screenItemIndex, const bool usePalette) {
+ _verticalScaleFactor = rawVideoData[1];
+ const int16 celWidth = (int16)READ_SCI11ENDIAN_UINT16(rawVideoData + 2);
+ const int16 celHeight = (int16)READ_SCI11ENDIAN_UINT16(rawVideoData + 4);
+ const Common::Point celPosition((int16)READ_SCI11ENDIAN_UINT16(rawVideoData + 10),
+ (int16)READ_SCI11ENDIAN_UINT16(rawVideoData + 12));
+ const uint16 dataSize = READ_SCI11ENDIAN_UINT16(rawVideoData + 14);
+ const int16 numDataChunks = (int16)READ_SCI11ENDIAN_UINT16(rawVideoData + 16);
+
+ rawVideoData += kCelHeaderSize;
+
+ const int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth;
+ const int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight;
+ const int16 screenWidth = g_sci->_gfxFrameout->getCurrentBuffer().screenWidth;
+ const int16 screenHeight = g_sci->_gfxFrameout->getCurrentBuffer().screenHeight;
+
+ Common::Point displace;
+ if (scriptWidth == kLowResX && scriptHeight == kLowResY) {
+ const Ratio lowResToScreenX(screenWidth, kLowResX);
+ const Ratio lowResToScreenY(screenHeight, kLowResY);
+ const Ratio screenToLowResX(kLowResX, screenWidth);
+ const Ratio screenToLowResY(kLowResY, screenHeight);
+
+ const int16 scaledX = celPosition.x + (_position.x * lowResToScreenX).toInt();
+ const int16 scaledY1 = celPosition.y + (_position.y * lowResToScreenY).toInt();
+ const int16 scaledY2 = scaledY1 + celHeight - 1;
+
+ const int16 lowResX = (scaledX * screenToLowResX).toInt();
+ const int16 lowResY = (scaledY2 * screenToLowResY).toInt();
+
+ displace.x = (scaledX - (lowResX * lowResToScreenX).toInt()) * -1;
+ displace.y = (lowResY * lowResToScreenY).toInt() - scaledY1;
+ _screenItemX[screenItemIndex] = lowResX;
+ _screenItemY[screenItemIndex] = lowResY;
+
+ debugC(kDebugLevelVideo, "Low resolution position c: %d %d l: %d/%d %d/%d d: %d %d s: %d/%d %d/%d x: %d y: %d", celPosition.x, celPosition.y, lowResX, scriptWidth, lowResY, scriptHeight, displace.x, displace.y, scaledX, screenWidth, scaledY2, screenHeight, scaledX - displace.x, scaledY2 - displace.y);
+ } else {
+ const int16 highResX = celPosition.x + _position.x;
+ const int16 highResY = celPosition.y + _position.y + celHeight - 1;
+
+ displace.x = 0;
+ displace.y = celHeight - 1;
+ _screenItemX[screenItemIndex] = highResX;
+ _screenItemY[screenItemIndex] = highResY;
+
+ debugC(kDebugLevelVideo, "High resolution position c: %d %d s: %d %d d: %d %d", celPosition.x, celPosition.y, highResX, highResY, displace.x, displace.y);
+ }
+
+ _originalScreenItemX[screenItemIndex] = celPosition.x;
+ _originalScreenItemY[screenItemIndex] = celPosition.y;
+
+ assert(_celHandles[screenItemIndex].area >= celWidth * celHeight);
+
+ SciBitmap &bitmap = *_segMan->lookupBitmap(_celHandles[screenItemIndex].bitmapId);
+ assert(bitmap.getWidth() == celWidth && bitmap.getHeight() == celHeight);
+ assert(bitmap.getScaledWidth() == _xResolution && bitmap.getScaledHeight() == _yResolution);
+ assert(bitmap.getHunkPaletteOffset() == (uint32)bitmap.getWidth() * bitmap.getHeight() + SciBitmap::getBitmapHeaderSize());
+ bitmap.setDisplace(displace);
+
+ byte *targetBuffer = nullptr;
+ if (_verticalScaleFactor == 100) {
+ // direct copy to bitmap
+ targetBuffer = bitmap.getPixels();
+ } else {
+ // go through squashed cel decompressor
+ _celDecompressionBuffer.resize(_celDecompressionArea >= celWidth * (celHeight * _verticalScaleFactor / 100));
+ targetBuffer = _celDecompressionBuffer.begin();
+ }
+
+ for (int i = 0; i < numDataChunks; ++i) {
+ uint compressedSize = READ_SCI11ENDIAN_UINT32(rawVideoData);
+ uint decompressedSize = READ_SCI11ENDIAN_UINT32(rawVideoData + 4);
+ uint16 compressionType = READ_SCI11ENDIAN_UINT16(rawVideoData + 8);
+ rawVideoData += 10;
+
+ switch (compressionType) {
+ case kCompressionLZS: {
+ Common::MemoryReadStream videoDataStream(rawVideoData, compressedSize, DisposeAfterUse::NO);
+ _decompressor.unpack(&videoDataStream, targetBuffer, compressedSize, decompressedSize);
+ break;
+ }
+ case kCompressionNone:
+ Common::copy(rawVideoData, rawVideoData + decompressedSize, targetBuffer);
+ break;
+ default:
+ error("Unknown compression type %d!", compressionType);
+ }
+
+ rawVideoData += compressedSize;
+ targetBuffer += decompressedSize;
+ }
+
+ if (_verticalScaleFactor != 100) {
+ expandCel(bitmap.getPixels(), _celDecompressionBuffer.begin(), celWidth, celHeight);
+ }
+
+ if (usePalette) {
+ Common::copy(_rawPalette, _rawPalette + kRawPaletteSize, bitmap.getHunkPalette());
+ }
+
+ return kCelHeaderSize + dataSize;
}
-Audio::AudioStream *RobotDecoder::RobotAudioTrack::getAudioStream() const {
- return _audioStream;
+void RobotDecoder::preallocateCelMemory(const byte *rawVideoData, const int16 numCels) {
+ for (CelHandleList::size_type i = 0; i < _celHandles.size(); ++i) {
+ CelHandleInfo &celHandle = _celHandles[i];
+
+ if (celHandle.status == CelHandleInfo::kFrameLifetime) {
+ _segMan->freeBitmap(celHandle.bitmapId);
+ celHandle.bitmapId = NULL_REG;
+ celHandle.status = CelHandleInfo::kNoCel;
+ celHandle.area = 0;
+ }
+ }
+ _celHandles.resize(numCels);
+
+ const int numFixedCels = MIN(numCels, (int16)kFixedCelListSize);
+ for (int i = 0; i < numFixedCels; ++i) {
+ CelHandleInfo &celHandle = _celHandles[i];
+
+ // NOTE: There was a check to see if the cel handle was not allocated
+ // here, for some reason, which would mean that nothing was ever
+ // allocated from fixed cels, because the _celHandles array just got
+ // deleted and recreated...
+ if (celHandle.bitmapId == NULL_REG) {
+ break;
+ }
+
+ celHandle.bitmapId = _fixedCels[i];
+ celHandle.status = CelHandleInfo::kRobotLifetime;
+ celHandle.area = _maxCelArea[i];
+ }
+
+ uint maxFrameArea = 0;
+ for (int i = 0; i < numCels; ++i) {
+ const int16 celWidth = (int16)READ_SCI11ENDIAN_UINT16(rawVideoData + 2);
+ const int16 celHeight = (int16)READ_SCI11ENDIAN_UINT16(rawVideoData + 4);
+ const uint16 dataSize = READ_SCI11ENDIAN_UINT16(rawVideoData + 14);
+ const uint area = celWidth * celHeight;
+
+ if (area > maxFrameArea) {
+ maxFrameArea = area;
+ }
+
+ CelHandleInfo &celHandle = _celHandles[i];
+ if (celHandle.status == CelHandleInfo::kRobotLifetime) {
+ if (_maxCelArea[i] < area) {
+ _segMan->freeBitmap(celHandle.bitmapId);
+ _segMan->allocateBitmap(&celHandle.bitmapId, celWidth, celHeight, 255, 0, 0, _xResolution, _yResolution, kRawPaletteSize, false, false);
+ celHandle.area = area;
+ celHandle.status = CelHandleInfo::kFrameLifetime;
+ }
+ } else if (celHandle.status == CelHandleInfo::kNoCel) {
+ _segMan->allocateBitmap(&celHandle.bitmapId, celWidth, celHeight, 255, 0, 0, _xResolution, _yResolution, kRawPaletteSize, false, false);
+ celHandle.area = area;
+ celHandle.status = CelHandleInfo::kFrameLifetime;
+ } else {
+ error("Cel Handle has bad status");
+ }
+
+ rawVideoData += kCelHeaderSize + dataSize;
+ }
+
+ if (maxFrameArea > _celDecompressionBuffer.size()) {
+ _celDecompressionBuffer.resize(maxFrameArea);
+ }
}
} // End of namespace Sci
diff --git a/engines/sci/video/robot_decoder.h b/engines/sci/video/robot_decoder.h
index 4faea5008a..5fd6ad49c4 100644
--- a/engines/sci/video/robot_decoder.h
+++ b/engines/sci/video/robot_decoder.h
@@ -20,109 +20,1407 @@
*
*/
-#ifndef SCI_VIDEO_ROBOT_DECODER_H
-#define SCI_VIDEO_ROBOT_DECODER_H
+#ifndef SCI_SOUND_DECODERS_ROBOT_H
+#define SCI_SOUND_DECODERS_ROBOT_H
-#include "common/rational.h"
-#include "common/rect.h"
-#include "video/video_decoder.h"
+#include "audio/audiostream.h" // for AudioStream
+#include "audio/rate.h" // for st_sample_t
+#include "common/array.h" // for Array
+#include "common/mutex.h" // for StackLock, Mutex
+#include "common/rect.h" // for Point, Rect (ptr only)
+#include "common/scummsys.h" // for int16, int32, byte, uint16
+#include "sci/engine/vm_types.h" // for NULL_REG, reg_t
+#include "sci/graphics/helpers.h" // for GuiResourceId
+#include "sci/graphics/screen_item32.h" // for ScaleInfo, ScreenItem (ptr o...
-namespace Audio {
-class QueuingAudioStream;
-}
+namespace Common { class SeekableSubReadStreamEndian; }
+namespace Sci {
+class Plane;
+class SegManager;
-namespace Common {
-class SeekableSubReadStreamEndian;
-}
+// Notes on Robot v5/v6 format:
+//
+// Robot is a packetized streaming AV format that encodes multiple bitmaps +
+// positioning data, plus synchronised audio, for rendering in the SCI graphics
+// system.
+//
+// Unlike traditional AV formats, Robot videos almost always require playback
+// within the game engine because certain information (like the resolution of
+// the Robot coordinates and the background for the video) is dependent on data
+// that does not exist within the Robot file itself.
+//
+// The Robot container consists of a file header, an optional primer audio
+// section, an optional colour palette, a frame seek index, a set of cuepoints,
+// and variable-sized packets of compressed video+audio data.
+//
+// Integers in Robot files are coded using native endianness (LSB for x86
+// versions, MSB for 68k/PPC versions).
+//
+// Robot video coding is a relatively simple variable-length compression with no
+// interframe compression. Each cel in a frame is constructed from multiple
+// contiguous data blocks, each of which can be independently compressed with
+// LZS or left uncompressed. An entire cel can also be line decimated, where
+// lines are deleted from the source bitmap at compression time and are
+// reconstructed by decompression using line doubling. Each cel also includes
+// coordinates where it should be placed within the video frame, relative to the
+// top-left corner of the frame.
+//
+// Audio coding is fixed-length, and all audio blocks except for the primer
+// audio are the same size. Audio is encoded with Sierra SOL DPCM16 compression,
+// and is split into two channels ('even' and 'odd'), each at a 11025Hz sample
+// rate. The original signal is restored by interleaving samples from the two
+// channels together. Channel packets are 'even' if they have an ''absolute
+// position of audio'' that is evenly divisible by 2; otherwise, they are 'odd'.
+// Because the channels use DPCM compression, there is an 8-byte runway at the
+// start of every audio block that is never written to the output stream, which
+// is used to move the signal to the correct location by the 9th sample.
+//
+// File header (v5/v6):
+//
+// byte | description
+// 0 | signature 0x16
+// 1 | unused
+// 2-5 | signature 'SOL\0'
+// 6-7 | version (4, 5, and 6 are the only known versions)
+// 8-9 | size of audio blocks
+// 10-11 | primer is compressed flag
+// 12-13 | unused
+// 14-15 | total number of video frames
+// 16-17 | embedded palette size, in bytes
+// 18-19 | primer reserved size
+// 20-21 | coordinate X-resolution (if 0, uses game coordinates)
+// 22-23 | coordinate Y-resolution (if 0, uses game coordinates)
+// 24 | if non-zero, Robot includes a palette
+// 25 | if non-zero, Robot includes audio
+// 26-27 | unused
+// 28-29 | the frame rate, in frames per second
+// 30-31 | coordinate conversion flag; if true, screen item coordinates
+// | from the robot should be used as-is with NO conversion when
+// | explicitly displaying a specific frame
+// 32-33 | the maximum number of packets that can be skipped without causing
+// | audio drop-out
+// 34-35 | the maximum possible number of cels that will be displayed in any
+// | frame of the robot
+// 36-39 | the maximum possible size, in bytes, of the first fixed cel
+// 40-43 | the maximum possible size, in bytes, of the second fixed cel
+// 44-47 | the maximum possible size, in bytes, of the third fixed cel
+// 48-51 | the maximum possible size, in bytes, of the fourth fixed cel
+// 52-59 | unused
+//
+// If the ''file includes audio'' flag is false, seek ''primer reserved size''
+// bytes from the end of the file header to get past a padding zone.
+//
+// If the ''file includes audio'' flag is true, and the ''primer reserved size''
+// is not zero, the data immediately after the file header consists of an audio
+// primer header plus compressed audio data:
+//
+// Audio primer header:
+//
+// byte | description
+// 0-3 | the size, in bytes, of the entire primer audio section
+// 4-5 | the compression format of the primer audio (must be zero)
+// 6-9 | the size, in bytes, of the "even" primer
+// 10-13 | the size, in bytes, of the "odd" primer
+//
+// If the combined sizes of the even and odd primers do not match the ''primer
+// reserved size'', the next header block can be found ''primer reserved size''
+// bytes from the *start* of the audio primer header.
+//
+// Otherwise, if the Robot has audio, and the ''primer reserved size'' is zero,
+// and the ''primer is compressed flag'' is set, the "even" primer size is
+// 19922, the "odd" primer size is 21024, and the "even" and "odd" buffers
+// should be zero-filled.
+//
+// Any other combination of these flags is an error.
+//
+// If the Robot has a palette, the next ''palette size'' bytes should be read
+// as a SCI HunkPalette. Otherwise, seek ''palette size'' bytes from the current
+// position to get to the frame index.
+//
+// The next section of the Robot is the video frame size index. In version 5
+// robots, read ''total number of frames'' 16-bit integers to get the size of
+// the compressed video for each frame. For version 6 robots, use 32-bit
+// integers.
+//
+// The next section of the Robot is the packet size index (combined compressed
+// size of video + audio for each frame). In version 5 Robots, read ''total
+// number of frames'' 16-bit integers. In version 6 robots, use 32-bit integers.
+//
+// The next section of the Robot is the cue times index. Read 256 32-bit
+// integers, which represent the number of ticks from the start of playback that
+// the given cue point falls on.
+//
+// The next section of the Robot is the cue values index. Read 256 16-bit
+// integers, which represent the actual cue values that will be passed back to
+// the game engine when a cue is requested.
+//
+// Finally, to get to the first frame packet, seek from the current position to
+// the start of the next 2048-byte-aligned sector.
+//
+// Frame packet:
+//
+// byte | description
+// 0..n | video data (size is in the ''video frame size index'')
+// n+1.. | optional audio data (size is ''size of audio blocks'')
+//
+// Video data:
+//
+// byte | description
+// 0-2 | number of cels in the frame (max 10)
+// 3..n | cels
+//
+// Cel:
+//
+// 0-17 | cel header
+// 18..n | data chunks
+//
+// Cel header:
+//
+// byte | description
+// 0 | unused
+// 1 | vertical scale factor, in percent decimation (100 = no decimation,
+// | 50 = 50% of lines were removed)
+// 2-3 | cel width
+// 4-5 | cel height
+// 6-9 | unused
+// 10-11 | cel x-position, in Robot coordinates
+// 12-13 | cel y-position, in Robot coordinates
+// 14-15 | cel total data chunk size, in bytes
+// 16-17 | number of data chunks
+//
+// Cel data chunk:
+//
+// 0-9 | cel data chunk header
+// 10..n | cel data
+//
+// Cel data chunk header:
+//
+// byte | description
+// 0-3 | compressed size
+// 4-7 | decompressed size
+// 8-9 | compression type (0 = LZS, 2 = uncompressed)
+//
+// Random frame seeking can be done by calculating the address of the frame
+// packet by adding up the ''packet size index'' entries up to the current
+// frame. This will normally disable audio playback, as audio data in a packet
+// does not correspond to the video in the same packet.
+//
+// Audio data is placed immediately after the end of the video data in a packet,
+// and consists of an audio header plus compressed audio data:
+//
+// Audio data:
+//
+// byte | description
+// 0-7 | audio data header
+// 8-15 | DPCM runway
+// 16..n | compressed audio data
+//
+// Audio data header:
+//
+// byte | description
+// 0-3 | absolute position of audio in the audio stream
+// 4-7 | the size of the audio block, excluding the header
+//
+// When a block of audio is processed, first check to ensure that the
+// decompressed audio block's `position * 2 + length * 4` runs past the end of
+// the last packet of the same evenness/oddness. Discard the audio block
+// entirely if data has already been written past the end of this block for this
+// channel, or if the read head has already read past the end of this audio
+// block.
+//
+// If the block is not discarded, apply DPCM decompression to the entire block,
+// starting from beginning of the DPCM runway, using an initial sample value of
+// 0. Then, copy every sample from the decompressed source outside of the DPCM
+// runway into every *other* sample of the final audio buffer (1 -> 2, 2 -> 4,
+// 3 -> 6, etc.).
+//
+// Finally, for any skipped samples where the opposing (even/odd) channel did
+// not yet write, interpolate the skipped areas by adding together the
+// neighbouring samples from this audio block and dividing by two. (This allows
+// the audio quality to degrade to 11kHz in case it takes too long to decode all
+// the frames in the stream). Interpolated samples must not be written on top of
+// true data from the opposing channel. Audio from later packets must also not
+// be written on top of data in the same channel that was already written by an
+// earlier packet, in particular because the first 8 bytes of the next packet
+// are garbage data used to move the waveform to the correct position (due to
+// the use of DPCM compression).
-namespace Sci {
+#pragma mark -
+#pragma mark RobotAudioStream
+
+/**
+ * A Robot audio stream is a simple loop buffer
+ * that accepts audio blocks from the Robot engine.
+ */
+class RobotAudioStream : public Audio::AudioStream {
+public:
+ enum {
+ /**
+ * The sample rate used for all robot audio.
+ */
+ kRobotSampleRate = 22050,
+
+ /**
+ * Multiplier for the size of a packet that
+ * is being expanded by writing to every other
+ * byte of the target buffer.
+ */
+ kEOSExpansion = 2
+ };
+
+ /**
+ * Playback state information. Used for framerate
+ * calculation.
+ */
+ struct StreamState {
+ /**
+ * The current position of the read head of
+ * the audio stream.
+ */
+ int bytesPlaying;
+
+ /**
+ * The sample rate of the audio stream.
+ * Always 22050.
+ */
+ uint16 rate;
+
+ /**
+ * The bit depth of the audio stream.
+ * Always 16.
+ */
+ uint8 bits;
+ };
+
+ /**
+ * A single packet of compressed audio from a
+ * Robot data stream.
+ */
+ struct RobotAudioPacket {
+ /**
+ * Raw DPCM-compressed audio data.
+ */
+ byte *data;
+
+ /**
+ * The size of the compressed audio data,
+ * in bytes.
+ */
+ int dataSize;
+
+ /**
+ * The uncompressed, file-relative position
+ * of this audio packet.
+ */
+ int position;
+
+ RobotAudioPacket(byte *data_, const int dataSize_, const int position_) :
+ data(data_), dataSize(dataSize_), position(position_) {}
+ };
+
+ RobotAudioStream(const int32 bufferSize);
+ virtual ~RobotAudioStream();
+
+ /**
+ * Adds a new audio packet to the stream.
+ * @returns `true` if the audio packet was fully
+ * consumed, otherwise `false`.
+ */
+ bool addPacket(const RobotAudioPacket &packet);
+
+ /**
+ * Prevents any additional audio packets from
+ * being added to the audio stream.
+ */
+ void finish();
+
+ /**
+ * Returns the current status of the audio
+ * stream.
+ */
+ StreamState getStatus() const;
+
+private:
+ Common::Mutex _mutex;
+
+ /**
+ * Loop buffer for playback. Contains decompressed
+ * 16-bit PCM samples.
+ */
+ byte *_loopBuffer;
+
+ /**
+ * The size of the loop buffer, in bytes.
+ */
+ int32 _loopBufferSize;
+
+ /**
+ * The position of the read head within the loop
+ * buffer, in bytes.
+ */
+ int32 _readHead;
+
+ /**
+ * The lowest file position that can be buffered,
+ * in uncompressed bytes.
+ */
+ int32 _readHeadAbs;
+
+ /**
+ * The highest file position that can be buffered,
+ * in uncompressed bytes.
+ */
+ int32 _maxWriteAbs;
+
+ /**
+ * The highest file position, in uncompressed bytes,
+ * that has been written to the stream.
+ * Different from `_maxWriteAbs`, which is the highest
+ * uncompressed position which *can* be written right
+ * now.
+ */
+ int32 _writeHeadAbs;
+
+ /**
+ * The highest file position, in uncompressed bytes,
+ * that has been written to the even & odd sides of
+ * the stream.
+ *
+ * Index 0 corresponds to the 'even' side; index
+ * 1 correspond to the 'odd' side.
+ */
+ int32 _jointMin[2];
+
+ /**
+ * When `true`, the stream is waiting for all primer
+ * blocks to be received before allowing playback to
+ * begin.
+ */
+ bool _waiting;
+
+ /**
+ * When `true`, the stream will accept no more audio
+ * blocks.
+ */
+ bool _finished;
+
+ /**
+ * The uncompressed position of the first packet of
+ * robot data. Used to decide whether all primer
+ * blocks have been received and the stream should
+ * be started.
+ */
+ int32 _firstPacketPosition;
+
+ /**
+ * Decompression buffer, used to temporarily store
+ * an uncompressed block of audio data.
+ */
+ byte *_decompressionBuffer;
+
+ /**
+ * The size of the decompression buffer, in bytes.
+ */
+ int32 _decompressionBufferSize;
+
+ /**
+ * The position of the packet currently in the
+ * decompression buffer. Used to avoid
+ * re-decompressing audio data that has already
+ * been decompressed during a partial packet read.
+ */
+ int32 _decompressionBufferPosition;
+
+ /**
+ * Calculates the absolute ranges for new fills
+ * into the loop buffer.
+ */
+ void fillRobotBuffer(const RobotAudioPacket &packet, const int8 bufferIndex);
+
+ /**
+ * Interpolates `numSamples` samples from the read
+ * head, if no true samples were written for one
+ * (or both) of the joint channels.
+ */
+ void interpolateMissingSamples(const int32 numSamples);
+
+#pragma mark -
+#pragma mark RobotAudioStream - AudioStream implementation
+public:
+ int readBuffer(Audio::st_sample_t *outBuffer, int numSamples) override;
+ virtual bool isStereo() const override { return false; };
+ virtual int getRate() const override { return 22050; };
+ virtual bool endOfData() const override {
+ Common::StackLock lock(_mutex);
+ return _readHeadAbs >= _writeHeadAbs;
+ };
+ virtual bool endOfStream() const override {
+ Common::StackLock lock(_mutex);
+ return _finished && endOfData();
+ }
+};
+
+#pragma mark -
+#pragma mark RobotDecoder
+
+/**
+ * RobotDecoder implements the logic required
+ * for Robot animations.
+ *
+ * @note A paused or finished RobotDecoder was
+ * classified as serializable in SCI3, but the
+ * save/load code would attempt to use uninitialised
+ * values, so it seems that robots were not ever
+ * actually able to be saved.
+ */
+class RobotDecoder {
+public:
+ RobotDecoder(SegManager *segMan);
+ ~RobotDecoder();
+
+private:
+ SegManager *_segMan;
+
+#pragma mark Constants
+public:
+ /**
+ * The playback status of the robot.
+ */
+ enum RobotStatus {
+ kRobotStatusUninitialized = 0,
+ kRobotStatusPlaying = 1,
+ kRobotStatusEnd = 2,
+ kRobotStatusPaused = 3
+ };
+
+ enum {
+ // Special high value used to represent
+ // parameters that should be left unchanged
+ // when calling `showFrame`
+ kUnspecified = 50000
+ };
+
+private:
+ enum {
+ /**
+ * Maximum number of on-screen screen items.
+ */
+ kScreenItemListSize = 10,
+
+ /**
+ * Maximum number of queued audio blocks.
+ */
+ kAudioListSize = 10,
+
+ /**
+ * Maximum number of samples used for frame timing.
+ */
+ kDelayListSize = 10,
+
+ /**
+ * Maximum number of cues.
+ */
+ kCueListSize = 256,
+
+ /**
+ * Maximum number of 'fixed' cels that never
+ * change for the duration of a robot.
+ */
+ kFixedCelListSize = 4,
+
+ /**
+ * The size of a hunk palette in the Robot stream.
+ */
+ kRawPaletteSize = 1200,
+
+ /**
+ * The size of a frame of Robot data. This
+ * value was used to align the first block of
+ * data after the main Robot header to the next
+ * CD sector.
+ */
+ kRobotFrameSize = 2048,
+
+ /**
+ * The size of a block of zero-compressed
+ * audio. Used to fill audio when the size of
+ * an audio packet does not match the expected
+ * packet size.
+ */
+ kRobotZeroCompressSize = 2048,
-class RobotDecoder : public Video::VideoDecoder {
+ /**
+ * The size of the audio block header, in bytes.
+ * The audio block header consists of the
+ * compressed size of the audio in the record,
+ * plus the position of the audio in the
+ * compressed data stream.
+ */
+ kAudioBlockHeaderSize = 8,
+
+ /**
+ * The size of a Robot cel header, in bytes.
+ */
+ kCelHeaderSize = 22,
+
+ /**
+ * The maximum amount that the frame rate is
+ * allowed to drift from the nominal frame rate
+ * in order to correct for AV drift or slow
+ * playback.
+ */
+ kMaxFrameRateDrift = 1
+ };
+
+ /**
+ * The version number for the currently loaded
+ * robot.
+ *
+ * There are several known versions of robot:
+ *
+ * v2: before Nov 1994; no known examples
+ * v3: before Nov 1994; no known examples
+ * v4: Jan 1995; PQ:SWAT demo
+ * v5: Mar 1995; SCI2.1 and SCI3 games
+ * v6: SCI3 games
+ */
+ uint16 _version;
+
+#pragma mark -
+#pragma mark Initialisation
+private:
+ /**
+ * Sets up the read stream for the robot.
+ */
+ void initStream(const GuiResourceId robotId);
+
+ /**
+ * Sets up the initial values for playback control.
+ */
+ void initPlayback();
+
+ /**
+ * Sets up the initial values for audio decoding.
+ */
+ void initAudio();
+
+ /**
+ * Sets up the initial values for video rendering.
+ */
+ void initVideo(const int16 x, const int16 y, const int16 scale, const reg_t plane, const bool hasPalette, const uint16 paletteSize);
+
+ /**
+ * Sets up the robot's data record and cue positions.
+ */
+ void initRecordAndCuePositions();
+
+#pragma mark -
+#pragma mark Playback
public:
- RobotDecoder(bool isBigEndian);
- virtual ~RobotDecoder();
+ /**
+ * Opens a robot file for playback.
+ * Newly opened robots are paused by default.
+ */
+ void open(const GuiResourceId robotId, const reg_t plane, const int16 priority, const int16 x, const int16 y, const int16 scale);
- bool loadStream(Common::SeekableReadStream *stream);
- bool load(GuiResourceId id);
+ /**
+ * Closes the currently open robot file.
+ */
void close();
- void setPos(uint16 x, uint16 y) { _pos = Common::Point(x, y); }
- Common::Point getPos() const { return _pos; }
+ /**
+ * Pauses the robot. Once paused, the audio for a robot
+ * is disabled until the end of playback.
+ */
+ void pause();
+
+ /**
+ * Resumes a paused robot.
+ */
+ void resume();
+
+ /**
+ * Moves robot to the specified frame and pauses playback.
+ *
+ * @note Called DisplayFrame in SSCI.
+ */
+ void showFrame(const uint16 frameNo, const uint16 newX, const uint16 newY, const uint16 newPriority);
+
+ /**
+ * Retrieves the value associated with the
+ * current cue point.
+ */
+ int16 getCue() const;
+
+ /**
+ * Gets the currently displayed frame.
+ */
+ int16 getFrameNo() const;
+
+ /**
+ * Gets the playback status of the player.
+ */
+ RobotStatus getStatus() const;
+
+private:
+ /**
+ * The read stream containing raw robot data.
+ */
+ Common::SeekableSubReadStreamEndian *_stream;
+
+ /**
+ * The current status of the player.
+ */
+ RobotStatus _status;
+
+ typedef Common::Array<int> PositionList;
+
+ /**
+ * A map of frame numbers to byte offsets within `_stream`.
+ */
+ PositionList _recordPositions;
+
+ /**
+ * The offset of the Robot file within a
+ * resource bundle.
+ */
+ int32 _fileOffset;
+
+ /**
+ * A list of cue times that is updated to
+ * prevent earlier cue values from being
+ * given to the game more than once.
+ */
+ mutable int32 _cueTimes[kCueListSize];
+
+ /**
+ * The original list of cue times from the
+ * raw Robot data.
+ */
+ int32 _masterCueTimes[kCueListSize];
+
+ /**
+ * The list of values to provide to a game
+ * when a cue value is requested.
+ */
+ int32 _cueValues[kCueListSize];
+
+ /**
+ * The current playback frame rate.
+ */
+ int16 _frameRate;
+
+ /**
+ * The nominal playback frame rate.
+ */
+ int16 _normalFrameRate;
+
+ /**
+ * The minimal playback frame rate. Used to
+ * correct for AV sync drift when the video
+ * is more than one frame ahead of the audio.
+ */
+ int16 _minFrameRate;
+
+ /**
+ * The maximum playback frame rate. Used to
+ * correct for AV sync drift when the video
+ * is more than one frame behind the audio.
+ */
+ int16 _maxFrameRate;
+
+ /**
+ * The maximum number of record blocks that
+ * can be skipped without causing audio to
+ * drop out.
+ */
+ int16 _maxSkippablePackets;
+
+ /**
+ * The currently displayed frame number.
+ */
+ int _currentFrameNo;
+
+ /**
+ * The last displayed frame number.
+ */
+ int _previousFrameNo;
+
+ /**
+ * The time, in ticks, when the robot was
+ * last started or resumed.
+ */
+ int32 _startTime;
+
+ /**
+ * The first frame displayed when the
+ * robot was resumed.
+ */
+ int32 _startFrameNo;
+
+ /**
+ * The last frame displayed when the robot
+ * was resumed.
+ */
+ int32 _startingFrameNo;
+
+ /**
+ * Seeks the raw data stream to the record for
+ * the given frame number.
+ */
+ bool seekToFrame(const int frameNo);
-protected:
- void readNextPacket();
+ /**
+ * Sets the start time and frame of the robot
+ * when the robot is started or resumed.
+ */
+ void setRobotTime(const int frameNo);
+#pragma mark -
+#pragma mark Timing
private:
- class RobotVideoTrack : public FixedRateVideoTrack {
+ /**
+ * This class tracks the amount of time it takes for
+ * a frame of robot animation to be rendered. This
+ * information is used by the player to speculatively
+ * skip rendering of future frames to keep the
+ * animation in sync with the robot audio.
+ */
+ class DelayTime {
public:
- RobotVideoTrack(int frameCount);
- ~RobotVideoTrack();
-
- uint16 getWidth() const;
- uint16 getHeight() const;
- Graphics::PixelFormat getPixelFormat() const;
- int getCurFrame() const { return _curFrame; }
- int getFrameCount() const { return _frameCount; }
- const Graphics::Surface *decodeNextFrame() { return _surface; }
- const byte *getPalette() const { _dirtyPalette = false; return _palette; }
- bool hasDirtyPalette() const { return _dirtyPalette; }
-
- void readPaletteChunk(Common::SeekableSubReadStreamEndian *stream, uint16 chunkSize);
- void calculateVideoDimensions(Common::SeekableSubReadStreamEndian *stream, uint32 *frameSizes);
- Graphics::Surface *getSurface() { return _surface; }
- void increaseCurFrame() { _curFrame++; }
-
- protected:
- Common::Rational getFrameRate() const { return Common::Rational(60, 10); }
+ DelayTime(RobotDecoder *decoder);
+
+ /**
+ * Starts performance timing.
+ */
+ void startTiming();
+
+ /**
+ * Ends performance timing.
+ */
+ void endTiming();
+
+ /**
+ * Returns whether or not timing is currently in
+ * progress.
+ */
+ bool timingInProgress() const;
+
+ /**
+ * Returns the median time, in ticks, of the
+ * currently stored timing samples.
+ */
+ int predictedTicks() const;
private:
- int _frameCount;
- int _curFrame;
- byte _palette[256 * 3];
- mutable bool _dirtyPalette;
- Graphics::Surface *_surface;
+ RobotDecoder *_decoder;
+
+ /**
+ * The start time, in ticks, of the current timing
+ * loop. If no loop is in progress, the value is 0.
+ *
+ * @note This is slightly different than SSCI where
+ * the not-timing value was -1.
+ */
+ uint32 _startTime;
+
+ /**
+ * A sorted list containing the timing data for
+ * the last `kDelayListSize` frames, in ticks.
+ */
+ int _delays[kDelayListSize];
+
+ /**
+ * A list of monotonically increasing identifiers
+ * used to identify and replace the oldest sample
+ * in the `_delays` array when finishing the
+ * next timing operation.
+ */
+ uint _timestamps[kDelayListSize];
+
+ /**
+ * The identifier of the oldest timing.
+ */
+ uint _oldestTimestamp;
+
+ /**
+ * The identifier of the newest timing.
+ */
+ uint _newestTimestamp;
+
+ /**
+ * Sorts the list of timings.
+ */
+ void sortList();
};
- class RobotAudioTrack : public AudioTrack {
+ /**
+ * Calculates the next frame number that needs
+ * to be rendered, using the timing data
+ * collected by DelayTime.
+ */
+ uint16 calculateNextFrameNo(const uint32 extraTicks = 0) const;
+
+ /**
+ * Calculates and returns the number of frames
+ * that should be rendered in `ticks` time,
+ * according to the current target frame rate
+ * of the robot.
+ */
+ uint32 ticksToFrames(const uint32 ticks) const;
+
+ /**
+ * Gets the current game time, in ticks.
+ */
+ uint32 getTickCount() const;
+
+ /**
+ * The performance timer for the robot.
+ */
+ DelayTime _delayTime;
+
+#pragma mark -
+#pragma mark Audio
+private:
+ enum {
+ /**
+ * The number of ticks that should elapse
+ * between each AV sync check.
+ */
+ kAudioSyncCheckInterval = 5 * 60 /* 5 seconds */
+ };
+
+ /**
+ * The status of the audio track of a Robot
+ * animation.
+ */
+ enum RobotAudioStatus {
+ kRobotAudioReady = 1,
+ kRobotAudioStopped = 2,
+ kRobotAudioPlaying = 3,
+ kRobotAudioPaused = 4,
+ kRobotAudioStopping = 5
+ };
+
+#pragma mark -
+#pragma mark Audio - AudioList
+private:
+ /**
+ * This class manages packetized audio playback
+ * for robots.
+ */
+ class AudioList {
public:
- RobotAudioTrack();
- ~RobotAudioTrack();
+ AudioList();
+
+ /**
+ * Starts playback of robot audio.
+ */
+ void startAudioNow();
+
+ /**
+ * Stops playback of robot audio, allowing
+ * any queued audio to finish playing back.
+ */
+ void stopAudio();
+
+ /**
+ * Stops playback of robot audio immediately.
+ */
+ void stopAudioNow();
+
+ /**
+ * Submits as many blocks of audio as possible
+ * to the audio engine.
+ */
+ void submitDriverMax();
+
+ /**
+ * Adds a new AudioBlock to the queue.
+ *
+ * @param position The absolute position of the
+ * audio for the block, in compressed bytes.
+ * @param size The size of the buffer.
+ * @param buffer A pointer to compressed audio
+ * data that will be copied into the new
+ * AudioBlock.
+ */
+ void addBlock(const int position, const int size, const byte *buffer);
- Audio::Mixer::SoundType getSoundType() const { return Audio::Mixer::kMusicSoundType; }
+ /**
+ * Immediately stops any active playback and
+ * purges all audio data in the audio list.
+ */
+ void reset();
- void queueBuffer(byte *buffer, int size);
+ /**
+ * Pauses the robot audio channel in
+ * preparation for the first block of audio
+ * data to be read.
+ */
+ void prepareForPrimer();
- protected:
- Audio::AudioStream *getAudioStream() const;
+ /**
+ * Sets the audio offset which is used to
+ * offset the position of audio packets
+ * sent to the audio stream.
+ */
+ void setAudioOffset(const int offset);
+
+#pragma mark -
+#pragma mark Audio - AudioList - AudioBlock
private:
- Audio::QueuingAudioStream *_audioStream;
+ /**
+ * AudioBlock represents a block of audio
+ * from the Robot's audio track.
+ */
+ class AudioBlock {
+ public:
+ AudioBlock(const int position, const int size, const byte *const data);
+ ~AudioBlock();
+
+ /**
+ * Submits the block of audio to the
+ * audio manager.
+ * @returns true if the block was fully
+ * read, or false if the block was not
+ * read or only partially read.
+ */
+ bool submit(const int startOffset);
+
+ private:
+ /**
+ * The absolute position, in compressed
+ * bytes, of this audio block's audio
+ * data in the audio stream.
+ */
+ int _position;
+
+ /**
+ * The compressed size, in bytes, of
+ * this audio block's audio data.
+ */
+ int _size;
+
+ /**
+ * A buffer containing raw
+ * SOL-compressed audio data.
+ */
+ byte *_data;
+ };
+
+ /**
+ * The list of compressed audio blocks
+ * submitted for playback.
+ */
+ AudioBlock *_blocks[kAudioListSize];
+
+ /**
+ * The number of blocks in `_blocks` that are
+ * ready to be submitted.
+ */
+ uint8 _blocksSize;
+
+ /**
+ * The index of the oldest submitted audio block.
+ */
+ uint8 _oldestBlockIndex;
+
+ /**
+ * The index of the newest submitted audio block.
+ */
+ uint8 _newestBlockIndex;
+
+ /**
+ * The offset used when sending packets to the
+ * audio stream.
+ */
+ int _startOffset;
+
+ /**
+ * The status of robot audio playback.
+ */
+ RobotAudioStatus _status;
+
+ /**
+ * Frees all audio blocks in the `_blocks` list.
+ */
+ void freeAudioBlocks();
};
- struct RobotHeader {
- // 6 bytes, identifier bytes
- uint16 version;
- uint16 audioChunkSize;
- uint16 audioSilenceSize;
- // 2 bytes, unknown
- uint16 frameCount;
- uint16 paletteDataSize;
- uint16 unkChunkDataSize;
- // 5 bytes, unknown
- byte hasSound;
- // 34 bytes, unknown
- } _header;
-
- void readHeaderChunk();
- void readFrameSizesChunk();
-
- Common::Point _pos;
- bool _isBigEndian;
- uint32 *_frameTotalSize;
-
- Common::SeekableSubReadStreamEndian *_fileStream;
-};
+ /**
+ * Whether or not this robot animation has
+ * an audio track.
+ */
+ bool _hasAudio;
+
+ /**
+ * The audio list for the current robot.
+ */
+ AudioList _audioList;
+
+ /**
+ * The size, in bytes, of a block of audio data,
+ * excluding the audio block header.
+ */
+ uint16 _audioBlockSize;
+
+ /**
+ * The expected size of a block of audio data,
+ * in bytes, excluding the audio block header.
+ */
+ int16 _expectedAudioBlockSize;
+
+ /**
+ * The number of compressed audio bytes that are
+ * needed per frame to fill the audio buffer
+ * without causing audio to drop out.
+ */
+ int16 _audioRecordInterval;
+
+ /**
+ * If true, primer audio buffers should be filled
+ * with silence instead of trying to read buffers
+ * from the Robot data.
+ */
+ uint16 _primerZeroCompressFlag;
+
+ /**
+ * The size, in bytes, of the primer audio in the
+ * Robot, including any extra alignment padding.
+ */
+ uint16 _primerReservedSize;
+
+ /**
+ * The combined size, in bytes, of the even and odd
+ * primer channels.
+ */
+ int32 _totalPrimerSize;
+
+ /**
+ * The absolute offset of the primer audio data in
+ * the robot data stream.
+ */
+ int32 _primerPosition;
+
+ /**
+ * The size, in bytes, of the even primer.
+ */
+ int32 _evenPrimerSize;
+
+ /**
+ * The size, in bytes, of the odd primer.
+ */
+ int32 _oddPrimerSize;
+
+ /**
+ * The absolute position in the audio stream of
+ * the first audio packet.
+ */
+ int32 _firstAudioRecordPosition;
-} // End of namespace Sci
+ /**
+ * A temporary buffer used to hold one frame of
+ * raw (DPCM-compressed) audio when reading audio
+ * records from the robot stream.
+ */
+ byte *_audioBuffer;
+ /**
+ * The next tick count when AV sync should be
+ * checked and framerate adjustments made, if
+ * necessary.
+ */
+ uint32 _checkAudioSyncTime;
+
+ /**
+ * Primes the audio buffer with the first frame
+ * of audio data.
+ *
+ * @note `primeAudio` was `InitAudio` in SSCI
+ */
+ bool primeAudio(const uint32 startTick);
+
+ /**
+ * Reads primer data from the robot data stream
+ * and puts it into the given buffers.
+ */
+ bool readPrimerData(byte *outEvenBuffer, byte *outOddBuffer);
+
+ /**
+ * Reads audio data for the given frame number
+ * into the given buffer.
+ *
+ * @param outAudioPosition The position of the
+ * audio, in compressed bytes, in the data stream.
+ * @param outAudioSize The size of the audio data,
+ * in compressed bytes.
+ */
+ bool readAudioDataFromRecord(const int frameNo, byte *outBuffer, int &outAudioPosition, int &outAudioSize);
+
+ /**
+ * Submits part of the audio packet of the given
+ * frame to the audio list, starting `startPosition`
+ * bytes into the audio.
+ */
+ bool readPartialAudioRecordAndSubmit(const int startFrame, const int startPosition);
+
+#pragma mark -
+#pragma mark Rendering
+public:
+ /**
+ * Puts the current dimensions of the robot, in game script
+ * coordinates, into the given rect, and returns the total
+ * number of frames in the robot animation.
+ */
+ uint16 getFrameSize(Common::Rect &outRect) const;
+
+ /**
+ * Pumps the robot player for the next frame of video.
+ * This is the main rendering function.
+ */
+ void doRobot();
+
+ /**
+ * Submits any outstanding audio blocks that should
+ * be added to the queue before the robot frame
+ * becomes visible.
+ */
+ void frameAlmostVisible();
+
+ /**
+ * Evaluates frame drift and makes modifications to
+ * the player in order to ensure that future frames
+ * will arrive on time.
+ */
+ void frameNowVisible();
+
+ /**
+ * Scales a vertically compressed cel to its original
+ * uncompressed dimensions.
+ */
+ void expandCel(byte *target, const byte* source, const int16 celWidth, const int16 celHeight) const;
+
+ /**
+ * Sets the visual priority of the robot.
+ * @see Plane::_priority
+ */
+ void setPriority(const int16 newPriority);
+
+private:
+ enum CompressionType {
+ kCompressionLZS = 0,
+ kCompressionNone = 2
+ };
+
+ /**
+ * Describes the state of a Robot video cel.
+ */
+ struct CelHandleInfo {
+ /**
+ * The persistence level of Robot cels.
+ */
+ enum CelHandleLifetime {
+ kNoCel = 0,
+ kFrameLifetime = 1,
+ kRobotLifetime = 2
+ };
+
+ /**
+ * A reg_t pointer to an in-memory
+ * bitmap containing the cel.
+ */
+ reg_t bitmapId;
+
+ /**
+ * The lifetime of the cel, either just
+ * for this frame or for the entire
+ * duration of the robot playback.
+ */
+ CelHandleLifetime status;
+
+ /**
+ * The size, in pixels, of the decompressed
+ * cel.
+ */
+ int area;
+
+ CelHandleInfo() : bitmapId(NULL_REG), status(kNoCel), area(0) {}
+ };
+
+ typedef Common::Array<ScreenItem *> RobotScreenItemList;
+ typedef Common::Array<CelHandleInfo> CelHandleList;
+ typedef Common::Array<int> VideoSizeList;
+ typedef Common::Array<uint> MaxCelAreaList;
+ typedef Common::Array<reg_t> FixedCelsList;
+ typedef Common::Array<Common::Point> CelPositionsList;
+ typedef Common::Array<byte> ScratchMemory;
+
+ /**
+ * Renders a version 5/6 robot frame.
+ */
+ void doVersion5(const bool shouldSubmitAudio = true);
+
+ /**
+ * Creates screen items for a version 5/6 robot.
+ */
+ void createCels5(const byte *rawVideoData, const int16 numCels, const bool usePalette);
+
+ /**
+ * Creates a single screen item for a cel in a
+ * version 5/6 robot.
+ *
+ * Returns the size, in bytes, of the raw cel data.
+ */
+ uint32 createCel5(const byte *rawVideoData, const int16 screenItemIndex, const bool usePalette);
+
+ /**
+ * Preallocates memory for the next `numCels` cels
+ * in the robot data stream.
+ */
+ void preallocateCelMemory(const byte *rawVideoData, const int16 numCels);
+
+ /**
+ * The decompressor for LZS-compressed cels.
+ */
+ DecompressorLZS _decompressor;
+
+ /**
+ * The origin of the robot animation, in screen
+ * coordinates.
+ */
+ Common::Point _position;
+
+ /**
+ * Global scaling applied to the robot.
+ */
+ ScaleInfo _scaleInfo;
+
+ /**
+ * The native resolution of the robot.
+ */
+ int16 _xResolution, _yResolution;
+
+ /**
+ * Whether or not the coordinates read from robot
+ * data are high resolution.
+ */
+ bool _isHiRes;
+
+ /**
+ * The maximum number of cels that will be rendered
+ * on any given frame in this robot. Used for
+ * preallocation of cel memory.
+ */
+ int16 _maxCelsPerFrame;
+
+ /**
+ * The maximum areas, in pixels, for each of
+ * the fixed cels in the robot. Used for
+ * preallocation of cel memory.
+ */
+ MaxCelAreaList _maxCelArea;
+
+ /**
+ * The hunk palette to use when rendering the
+ * current frame, if the `usePalette` flag was set
+ * in the robot header.
+ */
+ uint8 *_rawPalette;
+
+ /**
+ * A list of the raw video data sizes, in bytes,
+ * for each frame of the robot.
+ */
+ VideoSizeList _videoSizes;
+
+ /**
+ * A list of cels that will be present for the
+ * entire duration of the robot animation.
+ */
+ FixedCelsList _fixedCels;
+
+ /**
+ * A list of handles for each cel in the current
+ * frame.
+ */
+ CelHandleList _celHandles;
+
+ /**
+ * Scratch memory used to temporarily store
+ * decompressed cel data for vertically squashed
+ * cels.
+ */
+ ScratchMemory _celDecompressionBuffer;
+
+ /**
+ * The size, in bytes, of the squashed cel
+ * decompression buffer.
+ */
+ int _celDecompressionArea;
+
+ /**
+ * If true, the robot just started playing and
+ * is awaiting output for the first frame.
+ */
+ bool _syncFrame;
+
+ /**
+ * Scratch memory used to store the compressed robot
+ * video data for the current frame.
+ */
+ ScratchMemory _doVersion5Scratch;
+
+ /**
+ * When set to a non-negative value, forces the next
+ * call to doRobot to render the given frame number
+ * instead of whatever frame would have normally been
+ * rendered.
+ */
+ mutable int _cueForceShowFrame;
+
+ /**
+ * The plane where the robot animation will be drawn.
+ */
+ Plane *_plane;
+
+ /**
+ * A list of pointers to ScreenItems used by the robot.
+ */
+ RobotScreenItemList _screenItemList;
+
+ /**
+ * The positions of the various screen items in this
+ * robot, in screen coordinates.
+ */
+ Common::Array<int16> _screenItemX, _screenItemY;
+
+ /**
+ * The raw position values from the cel header for
+ * each screen item currently on-screen.
+ */
+ Common::Array<int16> _originalScreenItemX, _originalScreenItemY;
+
+ /**
+ * The duration of the current robot, in frames.
+ */
+ uint16 _numFramesTotal;
+
+ /**
+ * The screen priority of the video.
+ * @see ScreenItem::_priority
+ */
+ int16 _priority;
+
+ /**
+ * The amount of visual vertical compression applied
+ * to the current cel. A value of 100 means no
+ * compression; a value above 100 indicates how much
+ * the cel needs to be scaled along the y-axis to
+ * return to its original dimensions.
+ */
+ uint8 _verticalScaleFactor;
+};
+} // end of namespace Sci
#endif
diff --git a/engines/titanic/carry/chicken.h b/engines/titanic/carry/chicken.h
index 65fe30fd81..e64ae458a4 100644
--- a/engines/titanic/carry/chicken.h
+++ b/engines/titanic/carry/chicken.h
@@ -41,7 +41,7 @@ class CChicken : public CCarry {
bool MouseDragEndMsg(CMouseDragEndMsg *msg);
bool PETObjectStateMsg(CPETObjectStateMsg *msg);
bool PETLostObjectMsg(CPETLostObjectMsg *msg);
-private:
+public:
static int _v1;
public:
int _field12C;
diff --git a/engines/titanic/carry/crushed_tv.cpp b/engines/titanic/carry/crushed_tv.cpp
index a265b611a9..486537d28e 100644
--- a/engines/titanic/carry/crushed_tv.cpp
+++ b/engines/titanic/carry/crushed_tv.cpp
@@ -76,5 +76,4 @@ bool CCrushedTV::MouseDragStartMsg(CMouseDragStartMsg *msg) {
return CCarry::MouseDragStartMsg(msg);
}
-
} // End of namespace Titanic
diff --git a/engines/titanic/core/click_responder.cpp b/engines/titanic/core/click_responder.cpp
index f9694557df..9a0e0de7ab 100644
--- a/engines/titanic/core/click_responder.cpp
+++ b/engines/titanic/core/click_responder.cpp
@@ -24,20 +24,33 @@
namespace Titanic {
+BEGIN_MESSAGE_MAP(CClickResponder, CGameObject)
+ ON_MESSAGE(MouseButtonDownMsg)
+END_MESSAGE_MAP()
+
void CClickResponder::save(SimpleFile *file, int indent) {
file->writeNumberLine(1, indent);
- file->writeQuotedLine(_string1, indent);
- file->writeQuotedLine(_string2, indent);
+ file->writeQuotedLine(_message, indent);
+ file->writeQuotedLine(_soundName, indent);
CGameObject::save(file, indent);
}
void CClickResponder::load(SimpleFile *file) {
file->readNumber();
- _string1 = file->readString();
- _string2 = file->readString();
+ _message = file->readString();
+ _soundName = file->readString();
CGameObject::load(file);
}
+bool CClickResponder::MouseButtonDownMsg(CMouseButtonDownMsg *msg) {
+ if (!_soundName.empty())
+ playSound(_soundName);
+ if (!_message.empty())
+ petDisplayMessage(_message);
+
+ return true;
+}
+
} // End of namespace Titanic
diff --git a/engines/titanic/core/click_responder.h b/engines/titanic/core/click_responder.h
index 78381b9948..40f22d7906 100644
--- a/engines/titanic/core/click_responder.h
+++ b/engines/titanic/core/click_responder.h
@@ -28,8 +28,10 @@
namespace Titanic {
class CClickResponder : public CGameObject {
+ DECLARE_MESSAGE_MAP;
+ bool MouseButtonDownMsg(CMouseButtonDownMsg *msg);
protected:
- CString _string1, _string2;
+ CString _message, _soundName;
public:
CLASSDEF;
diff --git a/engines/titanic/game/bridge_view.cpp b/engines/titanic/game/bridge_view.cpp
index 9854969494..466480a64c 100644
--- a/engines/titanic/game/bridge_view.cpp
+++ b/engines/titanic/game/bridge_view.cpp
@@ -24,16 +24,92 @@
namespace Titanic {
+BEGIN_MESSAGE_MAP(CBridgeView, CBackground)
+ ON_MESSAGE(ActMsg)
+ ON_MESSAGE(MovieEndMsg)
+END_MESSAGE_MAP()
+
void CBridgeView::save(SimpleFile *file, int indent) {
file->writeNumberLine(1, indent);
- file->writeNumberLine(_fieldE0, indent);
+ file->writeNumberLine(_mode, indent);
CBackground::save(file, indent);
}
void CBridgeView::load(SimpleFile *file) {
file->readNumber();
- _fieldE0 = file->readNumber();
+ _mode = file->readNumber();
CBackground::load(file);
}
+bool CBridgeView::ActMsg(CActMsg *msg) {
+ CTurnOn onMsg;
+ CSetVolumeMsg volumeMsg;
+ volumeMsg._secondsTransition = 1;
+
+ if (msg->_action == "End") {
+ _mode = 4;
+ petLockInput();
+ petHide();
+ setVisible(true);
+ playMovie(MOVIE_NOTIFY_OBJECT);
+ } else if (msg->_action == "Go") {
+ _mode = 1;
+ setVisible(true);
+ volumeMsg._volume = 100;
+ volumeMsg.execute("EngineSounds");
+ onMsg.execute("EngineSounds");
+ playMovie(MOVIE_NOTIFY_OBJECT);
+ } else {
+ volumeMsg._volume = 50;
+ volumeMsg.execute("EngineSounds");
+ onMsg.execute("EngineSounds");
+
+ if (msg->_action == "Cruise") {
+ _mode = 2;
+ setVisible(true);
+ playMovie(MOVIE_NOTIFY_OBJECT);
+ } else if (msg->_action == "GoENd") {
+ _mode = 3;
+ setVisible(true);
+ CChangeMusicMsg musicMsg;
+ musicMsg._flags = 1;
+ musicMsg.execute("BridgeAutoMusicPlayer");
+ playSound("a#42.wav");
+ playMovie(MOVIE_NOTIFY_OBJECT);
+ }
+ }
+
+ return true;
+}
+
+bool CBridgeView::MovieEndMsg(CMovieEndMsg *msg) {
+ CTurnOff offMsg;
+ offMsg.execute("EngineSounds");
+
+ switch (_mode) {
+ case 0:
+ case 1:
+ setVisible(false);
+ dec54();
+ break;
+
+ case 2: {
+ setVisible(false);
+ CActMsg actMsg("End");
+ actMsg.execute("HomeSequence");
+ break;
+ }
+
+ case 3:
+ setVisible(false);
+ changeView("TheEnd.Node 3.N");
+ break;
+
+ default:
+ break;
+ }
+
+ return true;
+}
+
} // End of namespace Titanic
diff --git a/engines/titanic/game/bridge_view.h b/engines/titanic/game/bridge_view.h
index d7c7c35aa9..45cfa3f4c8 100644
--- a/engines/titanic/game/bridge_view.h
+++ b/engines/titanic/game/bridge_view.h
@@ -28,11 +28,14 @@
namespace Titanic {
class CBridgeView : public CBackground {
+ DECLARE_MESSAGE_MAP;
+ bool ActMsg(CActMsg *msg);
+ bool MovieEndMsg(CMovieEndMsg *msg);
public:
- int _fieldE0;
+ int _mode;
public:
CLASSDEF;
- CBridgeView() : CBackground(), _fieldE0(0) {}
+ CBridgeView() : CBackground(), _mode(0) {}
/**
* Save the data for the class to file
diff --git a/engines/titanic/game/chicken_cooler.cpp b/engines/titanic/game/chicken_cooler.cpp
index 29232e10bf..d10405de38 100644
--- a/engines/titanic/game/chicken_cooler.cpp
+++ b/engines/titanic/game/chicken_cooler.cpp
@@ -21,9 +21,15 @@
*/
#include "titanic/game/chicken_cooler.h"
+#include "titanic/carry/chicken.h"
namespace Titanic {
+BEGIN_MESSAGE_MAP(CChickenCooler, CGameObject)
+ ON_MESSAGE(EnterRoomMsg)
+ ON_MESSAGE(EnterViewMsg)
+END_MESSAGE_MAP()
+
void CChickenCooler::save(SimpleFile *file, int indent) {
file->writeNumberLine(1, indent);
file->writeNumberLine(_fieldBC, indent);
@@ -41,7 +47,32 @@ void CChickenCooler::load(SimpleFile *file) {
}
bool CChickenCooler::EnterRoomMsg(CEnterRoomMsg *msg) {
- warning("CChickenCoolor::handlEvent");
+ if (_fieldC0) {
+ CGameObject *obj = getMailManFirstObject();
+ if (obj) {
+ // WORKAROUND: Redundant loop for chicken in originalhere
+ } else {
+ getNextMail(nullptr);
+ if (CChicken::_v1 > _fieldBC)
+ CChicken::_v1 = _fieldBC;
+ }
+ }
+
+ return true;
+}
+
+bool CChickenCooler::EnterViewMsg(CEnterViewMsg *msg) {
+ if (!_fieldC0) {
+ for (CGameObject *obj = getMailManFirstObject(); obj;
+ obj = getNextMail(obj)) {
+ if (obj->isEquals("Chicken"))
+ return true;
+ }
+
+ if (CChicken::_v1 > _fieldBC)
+ CChicken::_v1 = _fieldBC;
+ }
+
return true;
}
diff --git a/engines/titanic/game/chicken_cooler.h b/engines/titanic/game/chicken_cooler.h
index 724727b905..54dba90686 100644
--- a/engines/titanic/game/chicken_cooler.h
+++ b/engines/titanic/game/chicken_cooler.h
@@ -29,7 +29,9 @@
namespace Titanic {
class CChickenCooler : public CGameObject {
+ DECLARE_MESSAGE_MAP;
bool EnterRoomMsg(CEnterRoomMsg *msg);
+ bool EnterViewMsg(CEnterViewMsg *msg);
private:
int _fieldBC;
int _fieldC0;
diff --git a/engines/titanic/game/chicken_dispensor.cpp b/engines/titanic/game/chicken_dispensor.cpp
index a9bf576765..7fb8fefcda 100644
--- a/engines/titanic/game/chicken_dispensor.cpp
+++ b/engines/titanic/game/chicken_dispensor.cpp
@@ -21,9 +21,21 @@
*/
#include "titanic/game/chicken_dispensor.h"
+#include "titanic/core/project_item.h"
+#include "titanic/pet_control/pet_control.h"
namespace Titanic {
+BEGIN_MESSAGE_MAP(CChickenDispensor, CBackground)
+ ON_MESSAGE(StatusChangeMsg)
+ ON_MESSAGE(MovieEndMsg)
+ ON_MESSAGE(ActMsg)
+ ON_MESSAGE(LeaveViewMsg)
+ ON_MESSAGE(EnterViewMsg)
+ ON_MESSAGE(MouseDragStartMsg)
+ ON_MESSAGE(TurnOff)
+END_MESSAGE_MAP()
+
CChickenDispensor::CChickenDispensor() : CBackground(),
_fieldE0(0), _fieldE4(0), _fieldE8(0) {
}
@@ -45,4 +57,133 @@ void CChickenDispensor::load(SimpleFile *file) {
CBackground::load(file);
}
+bool CChickenDispensor::StatusChangeMsg(CStatusChangeMsg *msg) {
+ msg->execute("SGTRestLeverAnimation");
+ int v1 = _fieldE8 ? 0 : _fieldE4;
+ CPetControl *pet = getPetControl();
+ CGameObject *obj;
+
+ for (obj = pet->getFirstObject(); obj; obj = pet->getNextObject(obj)) {
+ if (obj->isEquals("Chicken")) {
+ petDisplayMessage(1, "Chickens are allocated on a one-per-customer basis.");
+ return true;
+ }
+ }
+
+ for (obj = getMailManFirstObject(); obj; obj = getNextMail(obj)) {
+ if (obj->isEquals("Chicken")) {
+ petDisplayMessage(1, "Chickens are allocated on a one-per-customer basis.");
+ return true;
+ }
+ }
+
+ if (v1 == 1 || v1 == 2)
+ _fieldE8 = 1;
+
+ switch (v1) {
+ case 0:
+ petDisplayMessage(1, "Only one piece of chicken per passenger. Thank you.");
+ break;
+ case 1:
+ setVisible(true);
+ if (_fieldE0) {
+ playMovie(0, 12, MOVIE_NOTIFY_OBJECT | MOVIE_GAMESTATE);
+ playSound("z#400.wav");
+ _fieldE4 = 0;
+ } else {
+ playMovie(12, 16, MOVIE_NOTIFY_OBJECT | MOVIE_GAMESTATE);
+ _fieldE8 = 1;
+ _fieldE4 = 0;
+ }
+ break;
+
+ case 2:
+ setVisible(true);
+ if (_fieldE0) {
+ playMovie(0, 12, MOVIE_NOTIFY_OBJECT | MOVIE_GAMESTATE);
+ playSound("z#400.wav");
+ } else {
+ playMovie(12, 16, MOVIE_NOTIFY_OBJECT | MOVIE_GAMESTATE);
+ _fieldE8 = 1;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return true;
+}
+
+bool CChickenDispensor::MovieEndMsg(CMovieEndMsg *msg) {
+ if (getMovieFrame() == 16) {
+ playSound("b#50.wav", 50);
+ CActMsg actMsg("Dispense Chicken");
+ actMsg.execute("Chicken");
+ } else if (_fieldE8) {
+ _cursorId = CURSOR_ARROW;
+ loadFrame(0);
+ setVisible(false);
+ if (_fieldE4 == 2)
+ _fieldE8 = 0;
+ } else {
+ loadFrame(0);
+ setVisible(false);
+ changeView("SgtLobby.Node 1.N");
+ }
+
+ return true;
+}
+
+bool CChickenDispensor::ActMsg(CActMsg *msg) {
+ if (msg->_action == "EnableObject")
+ _fieldE0 = 0;
+ else if (msg->_action == "DisableObject")
+ _fieldE0 = 1;
+ else if (msg->_action == "IncreaseQuantity")
+ _fieldE4 = 2;
+ else if (msg->_action == "DecreaseQuantity")
+ _fieldE4 = 1;
+
+ return true;
+}
+
+bool CChickenDispensor::LeaveViewMsg(CLeaveViewMsg *msg) {
+ return true;
+}
+
+bool CChickenDispensor::EnterViewMsg(CEnterViewMsg *msg) {
+ playSound("b#51.wav");
+ _fieldE8 = 0;
+ _cursorId = CURSOR_ARROW;
+ return true;
+}
+
+bool CChickenDispensor::MouseDragStartMsg(CMouseDragStartMsg *msg) {
+ if (getMovieFrame() == 16) {
+ setVisible(false);
+ loadFrame(0);
+ _cursorId = CURSOR_ARROW;
+ _fieldE8 = 1;
+
+ CVisibleMsg visibleMsg;
+ visibleMsg.execute("Chicken");
+ CPassOnDragStartMsg passMsg(msg->_mousePos, 1);
+ passMsg.execute("Chicken");
+
+ msg->_dragItem = getRoot()->findByName("Chicken");
+ }
+
+ return true;
+}
+
+bool CChickenDispensor::TurnOff(CTurnOff *msg) {
+ if (getMovieFrame() == 16)
+ setVisible(false);
+ playMovie(16, 12, MOVIE_NOTIFY_OBJECT | MOVIE_GAMESTATE);
+ _fieldE8 = 0;
+
+ return true;
+}
+
} // End of namespace Titanic
diff --git a/engines/titanic/game/chicken_dispensor.h b/engines/titanic/game/chicken_dispensor.h
index d86b850871..5e3ba47ee8 100644
--- a/engines/titanic/game/chicken_dispensor.h
+++ b/engines/titanic/game/chicken_dispensor.h
@@ -28,6 +28,14 @@
namespace Titanic {
class CChickenDispensor : public CBackground {
+ DECLARE_MESSAGE_MAP;
+ bool StatusChangeMsg(CStatusChangeMsg *msg);
+ bool MovieEndMsg(CMovieEndMsg *msg);
+ bool ActMsg(CActMsg *msg);
+ bool LeaveViewMsg(CLeaveViewMsg *msg);
+ bool EnterViewMsg(CEnterViewMsg *msg);
+ bool MouseDragStartMsg(CMouseDragStartMsg *msg);
+ bool TurnOff(CTurnOff *msg);
public:
int _fieldE0;
int _fieldE4;
diff --git a/engines/titanic/game/close_broken_pel.cpp b/engines/titanic/game/close_broken_pel.cpp
index d27441ac96..c234590849 100644
--- a/engines/titanic/game/close_broken_pel.cpp
+++ b/engines/titanic/game/close_broken_pel.cpp
@@ -24,16 +24,26 @@
namespace Titanic {
+BEGIN_MESSAGE_MAP(CCloseBrokenPel, CBackground)
+ ON_MESSAGE(MouseButtonDownMsg)
+END_MESSAGE_MAP()
+
void CCloseBrokenPel::save(SimpleFile *file, int indent) {
file->writeNumberLine(1, indent);
- file->writeQuotedLine(_string3, indent);
+ file->writeQuotedLine(_target, indent);
CBackground::save(file, indent);
}
void CCloseBrokenPel::load(SimpleFile *file) {
file->readNumber();
- _string3 = file->readString();
+ _target = file->readString();
CBackground::load(file);
}
+bool CCloseBrokenPel::MouseButtonDownMsg(CMouseButtonDownMsg *msg) {
+ CActMsg actMsg("Close");
+ actMsg.execute(_target);
+ return true;
+}
+
} // End of namespace Titanic
diff --git a/engines/titanic/game/close_broken_pel.h b/engines/titanic/game/close_broken_pel.h
index aacda6c002..4bd66255df 100644
--- a/engines/titanic/game/close_broken_pel.h
+++ b/engines/titanic/game/close_broken_pel.h
@@ -28,8 +28,10 @@
namespace Titanic {
class CCloseBrokenPel : public CBackground {
+ DECLARE_MESSAGE_MAP;
+ bool MouseButtonDownMsg(CMouseButtonDownMsg *msg);
public:
- CString _string3;
+ CString _target;
public:
CLASSDEF;
diff --git a/engines/titanic/game/cookie.cpp b/engines/titanic/game/cookie.cpp
index 915bb93b4a..96edca4058 100644
--- a/engines/titanic/game/cookie.cpp
+++ b/engines/titanic/game/cookie.cpp
@@ -24,6 +24,11 @@
namespace Titanic {
+BEGIN_MESSAGE_MAP(CCookie, CGameObject)
+ ON_MESSAGE(LeaveNodeMsg)
+ ON_MESSAGE(FreshenCookieMsg)
+END_MESSAGE_MAP()
+
void CCookie::save(SimpleFile *file, int indent) {
file->writeNumberLine(1, indent);
file->writeNumberLine(_value1, indent);
@@ -40,4 +45,16 @@ void CCookie::load(SimpleFile *file) {
CGameObject::load(file);
}
+bool CCookie::LeaveNodeMsg(CLeaveNodeMsg *msg) {
+ if (_value2)
+ _value1 = 1;
+ return true;
+}
+
+bool CCookie::FreshenCookieMsg(CFreshenCookieMsg *msg) {
+ _value1 = msg->_value2;
+ _value2 = msg->_value1;
+ return true;
+}
+
} // End of namespace Titanic
diff --git a/engines/titanic/game/cookie.h b/engines/titanic/game/cookie.h
index 7ae04f1144..2018deeb3e 100644
--- a/engines/titanic/game/cookie.h
+++ b/engines/titanic/game/cookie.h
@@ -28,6 +28,9 @@
namespace Titanic {
class CCookie : public CGameObject {
+ DECLARE_MESSAGE_MAP;
+ bool LeaveNodeMsg(CLeaveNodeMsg *msg);
+ bool FreshenCookieMsg(CFreshenCookieMsg *msg);
public:
int _value1;
int _value2;
diff --git a/engines/titanic/game/credits.cpp b/engines/titanic/game/credits.cpp
index 7078d41a17..d9149f6dd2 100644
--- a/engines/titanic/game/credits.cpp
+++ b/engines/titanic/game/credits.cpp
@@ -24,6 +24,11 @@
namespace Titanic {
+BEGIN_MESSAGE_MAP(CCredits, CGameObject)
+ ON_MESSAGE(SignalObject)
+ ON_MESSAGE(TimerMsg)
+END_MESSAGE_MAP()
+
CCredits::CCredits() : CGameObject(), _fieldBC(-1), _fieldC0(1) {
}
@@ -43,4 +48,34 @@ void CCredits::load(SimpleFile *file) {
CGameObject::load(file);
}
+bool CCredits::SignalObject(CSignalObject *msg) {
+ petHide();
+ disableMouse();
+ addTimer(50);
+ return true;
+}
+
+bool CCredits::TimerMsg(CTimerMsg *msg) {
+ stopGlobalSound(true, -1);
+ setVisible(true);
+ loadSound("a#16.wav");
+ loadSound("a#24.wav");
+
+ playCutscene(0, 18);
+ playGlobalSound("a#16.wav", -1, false, false, 0);
+ playCutscene(19, 642);
+ playSound("a#24.wav");
+ playCutscene(643, 750);
+
+ COpeningCreditsMsg creditsMsg;
+ creditsMsg.execute("Service Elevator Entity");
+ changeView("EmbLobby.Node 6.S");
+
+ setVisible(false);
+ petShow();
+ enableMouse();
+ stopGlobalSound(true, -1);
+ return true;
+}
+
} // End of namespace Titanic
diff --git a/engines/titanic/game/credits.h b/engines/titanic/game/credits.h
index fa9794b6de..23fd25584d 100644
--- a/engines/titanic/game/credits.h
+++ b/engines/titanic/game/credits.h
@@ -28,6 +28,9 @@
namespace Titanic {
class CCredits : public CGameObject {
+ DECLARE_MESSAGE_MAP;
+ bool SignalObject(CSignalObject *msg);
+ bool TimerMsg(CTimerMsg *msg);
public:
int _fieldBC, _fieldC0;
public:
diff --git a/engines/titanic/game/credits_button.cpp b/engines/titanic/game/credits_button.cpp
index 90bb1b5ebe..ee8f7bb329 100644
--- a/engines/titanic/game/credits_button.cpp
+++ b/engines/titanic/game/credits_button.cpp
@@ -24,6 +24,11 @@
namespace Titanic {
+BEGIN_MESSAGE_MAP(CCreditsButton, CBackground)
+ ON_MESSAGE(MouseButtonUpMsg)
+ ON_MESSAGE(MouseButtonDownMsg)
+END_MESSAGE_MAP()
+
CCreditsButton::CCreditsButton() : CBackground(), _fieldE0(1) {
}
@@ -39,4 +44,19 @@ void CCreditsButton::load(SimpleFile *file) {
CBackground::load(file);
}
+bool CCreditsButton::MouseButtonUpMsg(CMouseButtonUpMsg *msg) {
+ return true;
+}
+
+bool CCreditsButton::MouseButtonDownMsg(CMouseButtonDownMsg *msg) {
+ if (_fieldE0) {
+ playSound("a#20.wav");
+ CSignalObject signalMsg;
+ signalMsg._numValue = 1;
+ signalMsg.execute("CreditsPlayer");
+ }
+
+ return true;
+}
+
} // End of namespace Titanic
diff --git a/engines/titanic/game/credits_button.h b/engines/titanic/game/credits_button.h
index 5e0bf96677..4a53083195 100644
--- a/engines/titanic/game/credits_button.h
+++ b/engines/titanic/game/credits_button.h
@@ -28,6 +28,9 @@
namespace Titanic {
class CCreditsButton : public CBackground {
+ DECLARE_MESSAGE_MAP;
+ bool MouseButtonUpMsg(CMouseButtonUpMsg *msg);
+ bool MouseButtonDownMsg(CMouseButtonDownMsg *msg);
public:
int _fieldE0;
public:
diff --git a/engines/titanic/game/desk_click_responder.cpp b/engines/titanic/game/desk_click_responder.cpp
index d9b2cb64b4..2cd5d1e83b 100644
--- a/engines/titanic/game/desk_click_responder.cpp
+++ b/engines/titanic/game/desk_click_responder.cpp
@@ -21,13 +21,19 @@
*/
#include "titanic/game/desk_click_responder.h"
+#include "titanic/titanic.h"
namespace Titanic {
+BEGIN_MESSAGE_MAP(CDeskClickResponder, CClickResponder)
+ ON_MESSAGE(MouseButtonDownMsg)
+ ON_MESSAGE(LoadSuccessMsg)
+END_MESSAGE_MAP()
+
void CDeskClickResponder::save(SimpleFile *file, int indent) {
file->writeNumberLine(1, indent);
file->writeNumberLine(_fieldD4, indent);
- file->writeNumberLine(_fieldD8, indent);
+ file->writeNumberLine(_ticks, indent);
CClickResponder::save(file, indent);
}
@@ -35,9 +41,28 @@ void CDeskClickResponder::save(SimpleFile *file, int indent) {
void CDeskClickResponder::load(SimpleFile *file) {
file->readNumber();
_fieldD4 = file->readNumber();
- _fieldD8 = file->readNumber();
+ _ticks = file->readNumber();
CClickResponder::load(file);
}
+bool CDeskClickResponder::MouseButtonDownMsg(CMouseButtonDownMsg *msg) {
+ _fieldD4 = (_fieldD4 + 1) % 3;
+ if (_fieldD4)
+ return CClickResponder::MouseButtonDownMsg(msg);
+
+ uint ticks = g_vm->_events->getTicksCount();
+ if (!_ticks || ticks > (_ticks + 4000)) {
+ playSound("a#22.wav");
+ _ticks = ticks;
+ }
+
+ return true;
+}
+
+bool CDeskClickResponder::LoadSuccessMsg(CLoadSuccessMsg *msg) {
+ _ticks = 0;
+ return true;
+}
+
} // End of namespace Titanic
diff --git a/engines/titanic/game/desk_click_responder.h b/engines/titanic/game/desk_click_responder.h
index 12825ba9de..13cf7f4b87 100644
--- a/engines/titanic/game/desk_click_responder.h
+++ b/engines/titanic/game/desk_click_responder.h
@@ -28,9 +28,12 @@
namespace Titanic {
class CDeskClickResponder : public CClickResponder {
+ DECLARE_MESSAGE_MAP;
+ bool MouseButtonDownMsg(CMouseButtonDownMsg *msg);
+ bool LoadSuccessMsg(CLoadSuccessMsg *msg);
protected:
int _fieldD4;
- int _fieldD8;
+ uint _ticks;
public:
CLASSDEF;
diff --git a/engines/titanic/game/null_port_hole.cpp b/engines/titanic/game/null_port_hole.cpp
index e651b1b59f..b1514c7cbf 100644
--- a/engines/titanic/game/null_port_hole.cpp
+++ b/engines/titanic/game/null_port_hole.cpp
@@ -27,22 +27,22 @@ namespace Titanic {
EMPTY_MESSAGE_MAP(CNullPortHole, CClickResponder);
CNullPortHole::CNullPortHole() : CClickResponder() {
- _string1 = "For a better view, why not visit the Promenade Deck?";
- _string2 = "b#48.wav";
+ _message = "For a better view, why not visit the Promenade Deck?";
+ _soundName = "b#48.wav";
}
void CNullPortHole::save(SimpleFile *file, int indent) {
file->writeNumberLine(1, indent);
- file->writeQuotedLine(_string2, indent);
- file->writeQuotedLine(_string1, indent);
+ file->writeQuotedLine(_soundName, indent);
+ file->writeQuotedLine(_message, indent);
CClickResponder::save(file, indent);
}
void CNullPortHole::load(SimpleFile *file) {
file->readNumber();
- _string2 = file->readString();
- _string1 = file->readString();
+ _soundName = file->readString();
+ _message = file->readString();
CClickResponder::load(file);
}
diff --git a/engines/titanic/game/sgt/desk.cpp b/engines/titanic/game/sgt/desk.cpp
index 4dd0fdab92..51be14adb9 100644
--- a/engines/titanic/game/sgt/desk.cpp
+++ b/engines/titanic/game/sgt/desk.cpp
@@ -24,6 +24,12 @@
namespace Titanic {
+BEGIN_MESSAGE_MAP(CDesk, CSGTStateRoom)
+ ON_MESSAGE(TurnOn)
+ ON_MESSAGE(TurnOff)
+ ON_MESSAGE(MovieEndMsg)
+END_MESSAGE_MAP()
+
void CDesk::save(SimpleFile *file, int indent) {
file->writeNumberLine(1, indent);
CSGTStateRoom::save(file, indent);
@@ -34,4 +40,44 @@ void CDesk::load(SimpleFile *file) {
CSGTStateRoom::load(file);
}
+bool CDesk::TurnOn(CTurnOn *msg) {
+ if (_statics->_v5 == "Closed" && _statics->_v1 == "RestingG"
+ && _statics->_v1 == "OpenWrong") {
+ _statics->_v5 = "Open";
+ _fieldE0 = false;
+ _startFrame = 1;
+ _endFrame = 26;
+ playMovie(1, 26, MOVIE_NOTIFY_OBJECT | MOVIE_GAMESTATE);
+ playSound("b#12.wav");
+ }
+
+ return true;
+}
+
+bool CDesk::TurnOff(CTurnOff *msg) {
+ if (_statics->_v5 == "Open" && _statics->_v6 == "Closed"
+ && _statics->_v1 == "Open") {
+ CVisibleMsg visibleMsg(false);
+ visibleMsg.execute("ChestOfDrawers");
+
+ _statics->_v5 = "Closed";
+ _fieldE0 = true;
+ _startFrame = 26;
+ _endFrame = 51;
+ playMovie(26, 51, MOVIE_GAMESTATE);
+ playSound("b#9.wav");
+ }
+
+ return true;
+}
+
+bool CDesk::MovieEndMsg(CMovieEndMsg *msg) {
+ if (_statics->_v5 == "Open") {
+ CVisibleMsg visibleMsg(true);
+ visibleMsg.execute("ChestOfDrawers");
+ }
+
+ return true;
+}
+
} // End of namespace Titanic
diff --git a/engines/titanic/game/sgt/desk.h b/engines/titanic/game/sgt/desk.h
index 77b5fa17af..8b9e1fe841 100644
--- a/engines/titanic/game/sgt/desk.h
+++ b/engines/titanic/game/sgt/desk.h
@@ -28,6 +28,10 @@
namespace Titanic {
class CDesk : public CSGTStateRoom {
+ DECLARE_MESSAGE_MAP;
+ bool TurnOn(CTurnOn *msg);
+ bool TurnOff(CTurnOff *msg);
+ bool MovieEndMsg(CMovieEndMsg *msg);
public:
CLASSDEF;
diff --git a/engines/titanic/game/sgt/deskchair.cpp b/engines/titanic/game/sgt/deskchair.cpp
index a4a2badeb0..7f64c2ee34 100644
--- a/engines/titanic/game/sgt/deskchair.cpp
+++ b/engines/titanic/game/sgt/deskchair.cpp
@@ -24,6 +24,13 @@
namespace Titanic {
+BEGIN_MESSAGE_MAP(CDeskchair, CSGTStateRoom)
+ ON_MESSAGE(TurnOn)
+ ON_MESSAGE(TurnOff)
+ ON_MESSAGE(ActMsg)
+ ON_MESSAGE(MovieEndMsg)
+END_MESSAGE_MAP()
+
void CDeskchair::save(SimpleFile *file, int indent) {
file->writeNumberLine(1, indent);
CSGTStateRoom::save(file, indent);
@@ -34,4 +41,49 @@ void CDeskchair::load(SimpleFile *file) {
CSGTStateRoom::load(file);
}
+bool CDeskchair::TurnOn(CTurnOn *msg) {
+ if (_statics->_v8 == "Closed" && _statics->_v9 == "Closed") {
+ setVisible(true);
+ _statics->_v9 = "Open";
+ _fieldE0 = false;
+ _startFrame = 0;
+ _endFrame = 16;
+ playMovie(0, 16, MOVIE_GAMESTATE);
+ playSound("b#8.wav");
+ }
+
+ return true;
+}
+
+bool CDeskchair::TurnOff(CTurnOff *msg) {
+ if (_statics->_v9 == "Open") {
+ _statics->_v9 = "Closed";
+ _fieldE0 = true;
+ _startFrame = 16;
+ _endFrame = 32;
+ playMovie(16, 32, MOVIE_NOTIFY_OBJECT | MOVIE_GAMESTATE);
+ playSound("b#2.wav");
+ }
+
+ return true;
+}
+
+bool CDeskchair::ActMsg(CActMsg *msg) {
+ if (msg->_action == "Smash") {
+ setVisible(false);
+ _statics->_v9 = "Closed";
+ _fieldE0 = true;
+ loadFrame(0);
+ return true;
+ } else {
+ return CSGTStateRoom::ActMsg(msg);
+ }
+}
+
+bool CDeskchair::MovieEndMsg(CMovieEndMsg *msg) {
+ if (_statics->_v9 == "Closed")
+ setVisible(false);
+ return true;
+}
+
} // End of namespace Titanic
diff --git a/engines/titanic/game/sgt/deskchair.h b/engines/titanic/game/sgt/deskchair.h
index 5181b650d2..6e7bbe4169 100644
--- a/engines/titanic/game/sgt/deskchair.h
+++ b/engines/titanic/game/sgt/deskchair.h
@@ -28,6 +28,11 @@
namespace Titanic {
class CDeskchair : public CSGTStateRoom {
+ DECLARE_MESSAGE_MAP;
+ bool TurnOn(CTurnOn *msg);
+ bool TurnOff(CTurnOff *msg);
+ bool ActMsg(CActMsg *msg);
+ bool MovieEndMsg(CMovieEndMsg *msg);
public:
CLASSDEF;
diff --git a/engines/titanic/game/sgt/sgt_state_room.cpp b/engines/titanic/game/sgt/sgt_state_room.cpp
index 55f08de8b4..c089e401b8 100644
--- a/engines/titanic/game/sgt/sgt_state_room.cpp
+++ b/engines/titanic/game/sgt/sgt_state_room.cpp
@@ -21,17 +21,22 @@
*/
#include "titanic/game/sgt/sgt_state_room.h"
+#include "titanic/pet_control/pet_control.h"
namespace Titanic {
BEGIN_MESSAGE_MAP(CSGTStateRoom, CBackground)
+ ON_MESSAGE(ActMsg)
+ ON_MESSAGE(VisibleMsg)
ON_MESSAGE(EnterRoomMsg)
+ ON_MESSAGE(LeaveRoomMsg)
END_MESSAGE_MAP()
CSGTStateRoomStatics *CSGTStateRoom::_statics;
void CSGTStateRoom::init() {
_statics = new CSGTStateRoomStatics();
+ _statics->_v1 = "Closed";
}
void CSGTStateRoom::deinit() {
@@ -94,8 +99,80 @@ void CSGTStateRoom::load(SimpleFile *file) {
CBackground::load(file);
}
+bool CSGTStateRoom::ActMsg(CActMsg *msg) {
+ CPetControl *pet = getPetControl();
+ uint roomFlags = pet->getRoomFlags();
+ uint assignedRoom = pet->getAssignedRoomFlags();
+
+ if (roomFlags != assignedRoom) {
+ petDisplayMessage("This is not your assigned room. Please do not enjoy.");
+ } else if (_fieldE0) {
+ CTurnOn onMsg;
+ onMsg.execute(this);
+ } else {
+ CTurnOff offMsg;
+ offMsg.execute(this);
+ }
+
+ return true;
+}
+
+bool CSGTStateRoom::VisibleMsg(CVisibleMsg *msg) {
+ setVisible(msg->_visible);
+ return true;
+}
+
bool CSGTStateRoom::EnterRoomMsg(CEnterRoomMsg *msg) {
- warning("CSGTStateRoom::handleEvent");
+ CPetControl *pet = getPetControl();
+ uint roomFlags = pet->getRoomFlags();
+ uint assignedRoom = pet->getAssignedRoomFlags();
+
+ if (roomFlags == assignedRoom) {
+ loadFrame(_fieldE8);
+ _fieldE0 = _fieldEC;
+ setVisible(_fieldF0);
+
+ if (isEquals("Desk") && _statics->_v5 == "Closed")
+ loadFrame(1);
+ }
+
+ if (isEquals("Drawer")) {
+ petSetArea(PET_REMOTE);
+ if (roomFlags == assignedRoom && getPassengerClass() == 3
+ && _statics->_v13) {
+ playSound("b#21.wav");
+ _statics->_v13 = 0;
+ }
+
+ _statics->_v7 = "Closed";
+ setVisible(false);
+ _fieldE0 = true;
+ } else if (roomFlags != assignedRoom) {
+ loadFrame(0);
+ if (_fieldE4) {
+ setVisible(true);
+ if (isEquals("Desk"))
+ loadFrame(1);
+ } else {
+ setVisible(false);
+ }
+ }
+
+ return true;
+}
+
+bool CSGTStateRoom::LeaveRoomMsg(CLeaveRoomMsg *msg) {
+ CPetControl *pet = getPetControl();
+ uint roomFlags = pet->getRoomFlags();
+ uint assignedRoom = pet->getAssignedRoomFlags();
+
+ if (roomFlags == assignedRoom) {
+ _fieldE8 = getMovieFrame();
+ _fieldEC = _fieldE0;
+ _fieldF0 = _visible;
+ }
+
+ _statics->_v14 = roomFlags;
return true;
}
diff --git a/engines/titanic/game/sgt/sgt_state_room.h b/engines/titanic/game/sgt/sgt_state_room.h
index d9ffdb8e9e..3975f7b59b 100644
--- a/engines/titanic/game/sgt/sgt_state_room.h
+++ b/engines/titanic/game/sgt/sgt_state_room.h
@@ -47,15 +47,18 @@ struct CSGTStateRoomStatics {
class CSGTStateRoom : public CBackground {
DECLARE_MESSAGE_MAP;
+ bool ActMsg(CActMsg *msg);
+ bool VisibleMsg(CVisibleMsg *msg);
bool EnterRoomMsg(CEnterRoomMsg *msg);
+ bool LeaveRoomMsg(CLeaveRoomMsg *msg);
protected:
static CSGTStateRoomStatics *_statics;
protected:
- int _fieldE0;
+ bool _fieldE0;
int _fieldE4;
int _fieldE8;
- int _fieldEC;
- int _fieldF0;
+ bool _fieldEC;
+ bool _fieldF0;
public:
CLASSDEF;
CSGTStateRoom();
diff --git a/engines/titanic/game/sgt/sgt_upper_doors_sound.cpp b/engines/titanic/game/sgt/sgt_upper_doors_sound.cpp
index ed37b0a5c7..72cd7f9037 100644
--- a/engines/titanic/game/sgt/sgt_upper_doors_sound.cpp
+++ b/engines/titanic/game/sgt/sgt_upper_doors_sound.cpp
@@ -25,19 +25,19 @@
namespace Titanic {
CSGTUpperDoorsSound::CSGTUpperDoorsSound() {
- _string2 = "b#53.wav";
+ _soundName = "b#53.wav";
}
void CSGTUpperDoorsSound::save(SimpleFile *file, int indent) {
file->writeNumberLine(1, indent);
- file->writeQuotedLine(_string2, indent);
+ file->writeQuotedLine(_soundName, indent);
CClickResponder::save(file, indent);
}
void CSGTUpperDoorsSound::load(SimpleFile *file) {
file->readNumber();
- _string2 = file->readString();
+ _soundName = file->readString();
CClickResponder::load(file);
}
diff --git a/engines/titanic/gfx/chev_switch.cpp b/engines/titanic/gfx/chev_switch.cpp
index a6ce93098c..177f0ada76 100644
--- a/engines/titanic/gfx/chev_switch.cpp
+++ b/engines/titanic/gfx/chev_switch.cpp
@@ -24,7 +24,13 @@
namespace Titanic {
-CChevSwitch::CChevSwitch() : CToggleSwitch() {
+BEGIN_MESSAGE_MAP(CChevSwitch, CToggleSwitch)
+ ON_MESSAGE(MouseButtonUpMsg)
+ ON_MESSAGE(SetChevButtonImageMsg)
+ ON_MESSAGE(MouseButtonDownMsg)
+END_MESSAGE_MAP()
+
+CChevSwitch::CChevSwitch() : CToggleSwitch(), _value(0) {
}
void CChevSwitch::save(SimpleFile *file, int indent) {
@@ -37,4 +43,36 @@ void CChevSwitch::load(SimpleFile *file) {
CToggleSwitch::load(file);
}
+bool CChevSwitch::MouseButtonUpMsg(CMouseButtonUpMsg *msg) {
+ return true;
+}
+
+bool CChevSwitch::SetChevButtonImageMsg(CSetChevButtonImageMsg *msg) {
+ if (msg->_value2 && getParent()) {
+ error("TODO: Don't know parent type");
+ }
+
+ _fieldBC = msg->_value1;
+ if (_fieldBC) {
+ loadImage((_value & 1) ? "on_odd.tga" : "on_even.tga");
+ } else {
+ loadImage((_value & 1) ? "off_odd.tga" : "off_even.tga");
+ }
+
+ return true;
+}
+
+bool CChevSwitch::MouseButtonDownMsg(CMouseButtonDownMsg *msg) {
+ _fieldBC ^= 1;
+ if (getParent()) {
+ CSetChevPanelBitMsg bitMsg(_value, _fieldBC);
+ bitMsg.execute(getParent());
+ }
+
+ CSetChevButtonImageMsg chevMsg(_fieldBC, 0);
+ chevMsg.execute(this);
+
+ return true;
+}
+
} // End of namespace Titanic
diff --git a/engines/titanic/gfx/chev_switch.h b/engines/titanic/gfx/chev_switch.h
index 0305a6ca83..01da53c854 100644
--- a/engines/titanic/gfx/chev_switch.h
+++ b/engines/titanic/gfx/chev_switch.h
@@ -28,6 +28,12 @@
namespace Titanic {
class CChevSwitch : public CToggleSwitch {
+ DECLARE_MESSAGE_MAP;
+ bool MouseButtonUpMsg(CMouseButtonUpMsg *msg);
+ bool SetChevButtonImageMsg(CSetChevButtonImageMsg *msg);
+ bool MouseButtonDownMsg(CMouseButtonDownMsg *msg);
+public:
+ int _value;
public:
CLASSDEF;
CChevSwitch();
diff --git a/engines/titanic/gfx/toggle_switch.h b/engines/titanic/gfx/toggle_switch.h
index ae96c75ebd..8e7d057d8c 100644
--- a/engines/titanic/gfx/toggle_switch.h
+++ b/engines/titanic/gfx/toggle_switch.h
@@ -28,7 +28,7 @@
namespace Titanic {
class CToggleSwitch : public CGameObject {
-private:
+protected:
int _fieldBC;
Point _pos1;
public:
diff --git a/engines/titanic/messages/door_auto_sound_event.cpp b/engines/titanic/messages/door_auto_sound_event.cpp
index b9cedae6de..7618577e50 100644
--- a/engines/titanic/messages/door_auto_sound_event.cpp
+++ b/engines/titanic/messages/door_auto_sound_event.cpp
@@ -24,6 +24,12 @@
namespace Titanic {
+BEGIN_MESSAGE_MAP(CDoorAutoSoundEvent, CAutoSoundEvent)
+ ON_MESSAGE(PreEnterNodeMsg)
+ ON_MESSAGE(LeaveNodeMsg)
+ ON_MESSAGE(TimerMsg)
+END_MESSAGE_MAP()
+
void CDoorAutoSoundEvent::save(SimpleFile *file, int indent) {
file->writeNumberLine(1, indent);
file->writeQuotedLine(_string1, indent);
@@ -44,4 +50,16 @@ void CDoorAutoSoundEvent::load(SimpleFile *file) {
CAutoSoundEvent::load(file);
}
+bool CDoorAutoSoundEvent::PreEnterNodeMsg(CPreEnterNodeMsg *msg) {
+ return true;
+}
+
+bool CDoorAutoSoundEvent::LeaveNodeMsg(CLeaveNodeMsg *msg) {
+ return true;
+}
+
+bool CDoorAutoSoundEvent::TimerMsg(CTimerMsg *msg) {
+ return true;
+}
+
} // End of namespace Titanic
diff --git a/engines/titanic/messages/door_auto_sound_event.h b/engines/titanic/messages/door_auto_sound_event.h
index e6ea1b0f98..8b064a7221 100644
--- a/engines/titanic/messages/door_auto_sound_event.h
+++ b/engines/titanic/messages/door_auto_sound_event.h
@@ -28,6 +28,10 @@
namespace Titanic {
class CDoorAutoSoundEvent : public CAutoSoundEvent {
+ DECLARE_MESSAGE_MAP;
+ bool PreEnterNodeMsg(CPreEnterNodeMsg *msg);
+ bool LeaveNodeMsg(CLeaveNodeMsg *msg);
+ bool TimerMsg(CTimerMsg *msg);
public:
CString _string1;
CString _string2;