1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 *
22 * Based on the original sources
23 * Faery Tale II -- The Halls of the Dead
24 * (c) 1993-1996 The Wyrmkeep Entertainment Co.
25 */
26
27 #include "common/debug.h"
28
29 #include "saga2/saga2.h"
30 #include "saga2/fta.h"
31 #include "saga2/script.h"
32 #include "saga2/code.h"
33 #include "saga2/tile.h"
34 #include "saga2/mission.h"
35 #include "saga2/hresmgr.h"
36 #include "saga2/saveload.h"
37 #include "saga2/actor.h"
38
39 namespace Saga2 {
40
41 #define IMMED_WORD(w) ((w = *pc++),(w |= (*pc++)<<8)); \
42 debugC(3, kDebugScripts, "IMMED_WORD(%d 0x%04x)", w, w)
43 #define BRANCH(w) pc = codeSeg + (w); \
44 debugC(3, kDebugScripts, "BRANCH(%ld 0x%04lx)", pc - codeSeg, pc - codeSeg)
45
46 const uint32 sagaID = MKTAG('S', 'A', 'G', 'A'),
47 dataSegID = MKTAG('_', '_', 'D', 'A'),
48 exportSegID = MKTAG('_', 'E', 'X', 'P');
49
50 const int initialStackFrameSize = 10;
51
52 static bool lookupExport(uint16 entry, uint16 &segNum, uint16 &segOff);
53 uint8 *segmentAddress(uint16 segment, uint16 offset);
54
55 Thread *thisThread;
56
57 struct ModuleEntry *moduleList; // loaded from resource
58 int16 moduleBaseResource,
59 moduleCount;
60
61 uint16 dataSegIndex; // saved index of data seg
62 byte *dataSegment, // loaded in data
63 *exportSegment; // export table from SAGA
64
65 int32 dataSegSize; // bytes in data segment
66
67 long exportCount; // number of exported syms
68
69 // An extended script is running -- suspend all background processing.
70 int16 extendedThreadLevel;
71
72 int16 lastExport;
73
74 extern hResource *scriptResFile; // script resources
75 hResContext *scriptRes; // script resource handle
76
script_error(const char * msg)77 void script_error(const char *msg) {
78 thisThread->flags |= Thread::aborted;
79 WriteStatusF(0, msg);
80 }
81
seg2str(int16 segment)82 static Common::String seg2str(int16 segment) {
83 switch (segment) {
84 case builtinTypeObject:
85 return "GameObject";
86
87 case builtinTypeTAG:
88 return "TAG";
89
90 case builtinAbstract:
91 return Common::String::format("Abstract%d", segment);
92
93 case builtinTypeMission:
94 return "Mission";
95
96 default:
97 return Common::String::format("%d", segment);
98 }
99 }
100
101 //-----------------------------------------------------------------------
102 // Return the address of a builtin object, such as an Actor or a TAG,
103 // given a segment number and an index
builtinObjectAddress(int16 segment,uint16 index)104 uint8 *builtinObjectAddress(int16 segment, uint16 index) {
105 uint16 segNum, segOff;
106
107 switch (segment) {
108 case builtinTypeObject:
109 return (uint8 *)(&GameObject::objectAddress(index)->_data);
110
111 case builtinTypeTAG:
112 return (uint8 *)(&ActiveItem::activeItemAddress(index)->_data);
113
114 case builtinAbstract:
115 assert(index > 0);
116 if (lookupExport(index, segNum, segOff) == false)
117 error("SAGA: Cannot take address of abtract class");
118
119 return segmentAddress(segNum, segOff);
120
121 case builtinTypeMission:
122 return (uint8 *)(&ActiveMission::missionAddress(index)->_data);
123
124 default:
125 error("Invalid builtin object segment number: %d\n", segment);
126 }
127 }
128
129 //-----------------------------------------------------------------------
130 // Given the builtin object type (SAGA segment number), and the adress
131 // from builtinObjectAddress(), return the address of the virtual
132 // function table for the class associated with this object. Also
133 // return the address of the C function call table for this builtin
134 // class.
builtinVTableAddress(int16 btype,uint8 * addr,CallTable ** callTab)135 uint16 *builtinVTableAddress(int16 btype, uint8 *addr, CallTable **callTab) {
136 GameObject *obj;
137 ActiveItem *aItem;
138 ActiveMission *aMission;
139 uint16 script,
140 vtSeg,
141 vtOffset;
142
143 switch (btype) {
144 case builtinTypeObject:
145
146 // Get the address of a game object using the ID
147 obj = ((ObjectData *)addr)->obj;
148 script = obj->scriptClass();
149 *callTab = &actorCFuncs;
150
151 if (script <= 0)
152 error("SAGA failure: GameObject %d (%s) has no script.\n", obj->thisID(), obj->proto() ? obj->objName() : "Unknown");
153
154 break;
155
156 case builtinTypeTAG:
157 aItem = ((ActiveItemData *)addr)->aItem;
158 script = aItem->_data.scriptClassID;
159 *callTab = &tagCFuncs;
160
161 if (script <= 0)
162 error("SAGA failure: TAG has no script.\n");
163
164 break;
165
166 case builtinTypeMission:
167 aMission = ((ActiveMissionData *)addr)->aMission;
168 script = aMission->getScript();
169 *callTab = &missionCFuncs;
170
171 if (script <= 0)
172 error("SAGA failure: Mission Object has no script.\n");
173
174 break;
175
176 case builtinAbstract:
177 *callTab = NULL;
178
179 return (uint16 *)addr;
180
181 default:
182 error("SAGA Failure: Attempt to call member function of invalid builtin type.\n");
183 }
184
185 // Look up the vtable in the export table.
186 if (script != 0 && lookupExport(script, vtSeg, vtOffset)) {
187 return (uint16 *)segmentAddress(vtSeg, vtOffset);
188 } else
189 return NULL;
190 }
191
segmentAddress(uint16 segment,uint16 offset)192 uint8 *segmentAddress(uint16 segment, uint16 offset) {
193 byte *segHandle = nullptr;
194
195 // A segment number of less than zero means that this is
196 // a "builtin" object, in other words the game engine itself
197 if ((int16)segment < 0)
198 return builtinObjectAddress(segment, offset);
199
200 segHandle = scriptRes->loadIndexResource(segment, "object segment");
201 if (segHandle == nullptr)
202 return nullptr;
203
204 return segHandle + offset;
205 }
206
segmentArrayAddress(uint16 segment,uint16 index)207 uint8 *segmentArrayAddress(uint16 segment, uint16 index) {
208 byte *segHandle = nullptr;
209
210 if ((int16)segment < 0)
211 return builtinObjectAddress(segment, index);
212
213 segHandle = scriptRes->loadIndexResource(segment, "object array segment");
214 if (segHandle == nullptr)
215 return nullptr;
216
217 return segHandle + sizeof(uint16) + (uint16)(index * READ_LE_INT16(segHandle));
218 }
219
220 // Returns the address of a byte given an addressing mode
221
byteAddress(Thread * th,uint8 ** pcPtr)222 uint8 *byteAddress(Thread *th, uint8 **pcPtr) {
223 uint8 *pc = *pcPtr,
224 *addr;
225 uint16 seg,
226 offset, offset2,
227 index,
228 *arg;
229
230 switch (*pc++) {
231 case addr_data:
232 IMMED_WORD(offset);
233 debugC(3, kDebugScripts, "byteAddress: data[%d] = %d", offset, dataSegment[offset]);
234 *pcPtr = pc;
235 return &dataSegment[offset];
236
237 case addr_near:
238 IMMED_WORD(offset);
239 debugC(3, kDebugScripts, "byteAddress: near[%d] = %d", offset, th->codeSeg[offset]);
240 *pcPtr = pc;
241 return th->codeSeg + offset;
242
243 case addr_far:
244 IMMED_WORD(seg);
245 IMMED_WORD(offset);
246 debugC(3, kDebugScripts, "byteAddress: far[%s:%d] = %d", seg2str(seg).c_str(), offset, *segmentAddress(seg, offset));
247 *pcPtr = pc;
248
249 // FIXME: WORKAROUND: Fixes Captain Navis (5299, 17715, 80) in Maldavith not allowing passage to the Tamnath Ruins through sail even if Muybridge is dead.
250 if (seg == 130 && offset == 2862) {
251 warning("WORKAROUND: byteAddress: far");
252 Actor *boss = (Actor *)GameObject::objectAddress(32880);
253 if (boss->isDead())
254 return segmentAddress(130, 0);
255 }
256
257 return segmentAddress(seg, offset);
258
259 case addr_array:
260 IMMED_WORD(seg);
261 IMMED_WORD(offset);
262 addr = segmentArrayAddress(seg, offset);
263 IMMED_WORD(offset2);
264 debugC(3, kDebugScripts, "byteAddress: array[%s:%d:%d] = %d", seg2str(seg).c_str(), offset, offset2, addr[offset2]);
265 *pcPtr = pc;
266 return addr + offset2;
267
268 case addr_stack:
269 IMMED_WORD(offset);
270 debugC(3, kDebugScripts, "byteAddress: stack[%d] = %d", offset, *(th->stackBase + th->framePtr + (int16)offset));
271 *pcPtr = pc;
272 return th->stackBase + th->framePtr + (int16)offset;
273
274 case addr_thread:
275 IMMED_WORD(offset);
276 debugC(3, kDebugScripts, "byteAddress: thread[%d] = %d", offset, *((uint8 *)&th->threadArgs + offset));
277 *pcPtr = pc;
278 return (uint8 *)&th->threadArgs + offset;
279
280 case addr_this:
281 IMMED_WORD(offset);
282 arg = (uint16 *)(th->stackBase + th->framePtr + 8);
283 *pcPtr = pc;
284 if (arg[0] == dataSegIndex) {
285 debugC(3, kDebugScripts, "byteAddress: thisD[%d:%d] = %d", arg[1], offset, dataSegment[arg[1] + offset]);
286 return &dataSegment[arg[1] + offset];
287 }
288 debugC(3, kDebugScripts, "byteAddress: thisS[%s:%d:%d] = %d", seg2str(arg[0]).c_str(), arg[1], offset, *(segmentArrayAddress(arg[0], arg[1]) + offset));
289 return segmentArrayAddress(arg[0], arg[1]) + offset;
290
291 case addr_deref:
292
293 // First, get the address of the reference.
294 *pcPtr = pc;
295 addr = byteAddress(th, pcPtr);
296 pc = *pcPtr;
297
298 // Get the offset from the reference variable.
299 index = *(uint16 *)addr;
300
301 // Get the segment to dereference from, and the offset
302 // within the object.
303 IMMED_WORD(seg);
304 IMMED_WORD(offset);
305 debugC(3, kDebugScripts, "byteAddress: deref[%s:%d:%d] = %d", seg2str(seg).c_str(), index, offset, *(segmentAddress(seg, index) + offset));
306 *pcPtr = pc;
307
308 // Compute address of object
309 return segmentAddress(seg, index) + offset;
310 }
311
312 error("byteAddress: Invalid addressing mode: %d.\n", **pcPtr);
313 }
314
315 // Returns the address of an object given an addressing mode
316
objectAddress(Thread * th,uint8 ** pcPtr,uint16 & segNum,uint16 & offs)317 uint8 *objectAddress(
318 Thread *th,
319 uint8 **pcPtr,
320 uint16 &segNum, // segment of start of object
321 uint16 &offs) { // offset of start of object
322 uint8 *pc = *pcPtr,
323 *addr;
324 uint16 seg,
325 offset = 0,
326 index,
327 *arg;
328
329 switch (*pc++) {
330 case addr_data:
331 IMMED_WORD(index);
332 seg = dataSegIndex;
333 addr = &dataSegment[index];
334 debugC(3, kDebugScripts, "objectAddress: data[%s:%d] = %d", seg2str(seg).c_str(), index, *addr);
335 break;
336
337 case addr_far:
338 IMMED_WORD(seg);
339 IMMED_WORD(index);
340 addr = segmentAddress(seg, index);
341 debugC(3, kDebugScripts, "objectAddress: far[%s:%d] = %d", seg2str(seg).c_str(), index, *addr);
342 break;
343
344 case addr_array:
345 IMMED_WORD(seg);
346 IMMED_WORD(index);
347 IMMED_WORD(offset);
348 addr = segmentArrayAddress(seg, index) + offset;
349 debugC(3, kDebugScripts, "objectAddress: array[%s:%d:%d] = %d", seg2str(seg).c_str(), index, offset, *addr);
350 break;
351
352 case addr_this:
353 IMMED_WORD(offset);
354 arg = (uint16 *)(th->stackBase + th->framePtr + 8);
355 seg = arg[0];
356 index = arg[1];
357 if (seg == dataSegIndex) {
358 debugC(3, kDebugScripts, "objectAddress: thisD[%d:%d] = %d", index, offset, dataSegment[index + offset]);
359 return &dataSegment[index + offset];
360 }
361 addr = segmentArrayAddress(seg, index) + offset;
362 debugC(3, kDebugScripts, "objectAddress: thisS[%s:%d:%d] = %d", seg2str(seg).c_str(), index, offset, *addr);
363 break;
364
365 case addr_deref:
366
367 // First, get the address of the reference.
368 *pcPtr = pc;
369 addr = byteAddress(th, pcPtr);
370 pc = *pcPtr;
371
372 // Get the offset from the reference variable.
373 index = *(uint16 *)addr;
374
375 // Get the segment to dereference from, and the offset
376 // within the object.
377 IMMED_WORD(seg);
378 IMMED_WORD(offset);
379
380 // Compute address of object
381 addr = segmentAddress(seg, index) + offset;
382 debugC(3, kDebugScripts, "objectAddress: deref[%s:%d:%d] = %d", seg2str(seg).c_str(), index, offset, *addr);
383 break;
384
385 default:
386 error("objectAddress: Invalid addressing mode: %d.\n", **pcPtr);
387 }
388
389 offs = index;
390 segNum = seg;
391 *pcPtr = pc;
392 return addr;
393 }
394
395 // Returns the address and access mask of a bit, given addressing mode
bitAddress(Thread * th,uint8 ** pcPtr,int16 * mask)396 uint8 *bitAddress(Thread *th, uint8 **pcPtr, int16 *mask) {
397 uint8 *pc = *pcPtr,
398 *addr;
399 uint16 seg,
400 offset;
401
402 switch (*pc++) {
403 case addr_data:
404 IMMED_WORD(offset);
405 *pcPtr = pc;
406 *mask = (1 << (offset & 7));
407 debugC(3, kDebugScripts, "bitAddress: data[%d] = %d", offset, (dataSegment[offset >> 3] & *mask) != 0);
408 return &dataSegment[(offset >> 3)];
409
410 case addr_near:
411 IMMED_WORD(offset);
412 *pcPtr = pc;
413 *mask = (1 << (offset & 7));
414 debugC(3, kDebugScripts, "bitAddress: near[%d] = %d", offset, (*(th->codeSeg + (offset >> 3)) & *mask) != 0);
415 return th->codeSeg + (offset >> 3);
416
417 case addr_far:
418 IMMED_WORD(seg);
419 IMMED_WORD(offset);
420 *pcPtr = pc;
421 *mask = (1 << (offset & 7));
422 debugC(3, kDebugScripts, "bitAddress: far[%s:%d] = %d", seg2str(seg).c_str(), offset, (*segmentAddress(seg, offset >> 3) & *mask) != 0);
423 return segmentAddress(seg, (offset >> 3));
424
425 case addr_array:
426 IMMED_WORD(seg);
427 IMMED_WORD(offset);
428 addr = segmentArrayAddress(seg, offset);
429 IMMED_WORD(offset);
430 *pcPtr = pc;
431 *mask = (1 << (offset & 7));
432 debugC(3, kDebugScripts, "bitAddress: array[%s:%d:%d] = %d", seg2str(seg).c_str(), offset, offset, (addr[offset >> 3] & *mask) != 0);
433 return addr + (offset >> 3);
434
435 case addr_stack:
436 IMMED_WORD(offset);
437 *pcPtr = pc;
438 *mask = (1 << (offset & 7));
439 debugC(3, kDebugScripts, "bitAddress: stack[%d] = %d", offset, (*(th->stackBase + th->framePtr + (offset >>3)) & *mask) != 0);
440 return th->stackBase + th->framePtr + (offset >> 3);
441
442 case addr_thread:
443 IMMED_WORD(offset);
444 *pcPtr = pc;
445 *mask = (1 << (offset & 7));
446 debugC(3, kDebugScripts, "bitAddress: thread[%d] = %d", offset, (*((uint8 *)&th->threadArgs + (offset >> 3)) & *mask) != 0);
447 return (uint8 *)&th->threadArgs + (offset >> 3);
448
449 case addr_this:
450 error("Addressing relative to 'this' not supported just yet.\n");
451
452 }
453 error("bitAddress: Invalid addressing mode: %d.\n", **pcPtr);
454 }
455
456 // Returns the address of a string
457
strAddress(int strNum)458 uint8 *Thread::strAddress(int strNum) {
459 uint16 seg = READ_LE_INT16(codeSeg + 2);
460 uint16 offset = READ_LE_INT16(codeSeg + 4);
461 uint8 *strSeg = segmentAddress(seg, offset);
462
463 assert(strNum >= 0);
464 assert(codeSeg);
465 assert(strSeg);
466
467 return strSeg + (uint16)READ_LE_INT16(strSeg + 2 * strNum);
468 }
469
470 //-----------------------------------------------------------------------
471 // RandomGenerator class - a random number generator class for function
472 // objects which each maintain a local seed.
473 class RandomGenerator {
474 uint32 a; // seed
475 static const uint32 b; // arbitrary constant
476
477 public:
RandomGenerator(void)478 RandomGenerator(void) : a(1) {
479 }
RandomGenerator(uint16 seed)480 RandomGenerator(uint16 seed) {
481 a = (uint32)seed << 16;
482 }
483
seed(uint16 seed)484 void seed(uint16 seed) {
485 a = (uint32)seed << 16;
486 }
487
operator ()(void)488 uint16 operator()(void) {
489 a = (a * b) + 1;
490 return a >> 16;
491 }
492 };
493
494 const uint32 RandomGenerator::b = 31415821;
495
496 //-----------------------------------------------------------------------
497 // A restricted random function
RRandom(int16 c,int16 s,int16 id)498 int16 RRandom(int16 c, int16 s, int16 id) {
499 // Create a local random number generator with a seed calculated
500 // with a non-deterministic portion generated by the standard
501 // library rand() function and a deterministic potion based upon
502 // the "id" argument
503 RandomGenerator rnd(g_vm->_rnd->getRandomNumber(s - 1) + (id * s));
504 return rnd() % c;
505 }
506
507 /* ============================================================================ *
508 Main interpreter
509 * ============================================================================ */
print_script_name(uint8 * codePtr,const char * descr=NULL)510 void print_script_name(uint8 *codePtr, const char *descr = NULL) {
511 char scriptName[32];
512 uint8 *sym = codePtr - 1;
513 uint8 length = MIN<uint>(*sym, sizeof scriptName - 1);
514
515 memcpy(scriptName, sym - *sym, length);
516 scriptName[length] = '\0';
517
518 if (descr)
519 debugC(1, kDebugScripts, "Scripts: %d op_enter: [%s].%s ", lastExport, descr, scriptName);
520 else
521 debugC(1, kDebugScripts, "Scripts: %d op_enter: ::%s ", lastExport, scriptName);
522 }
523
objectName(int16 segNum,uint16 segOff)524 const char *objectName(int16 segNum, uint16 segOff) {
525 //static nameBuf[64];
526
527 if (segNum >= 0)
528 return "SagaObject";
529
530 switch (segNum) {
531 case builtinTypeObject:
532 return GameObject::objectAddress(segOff)->objName();
533
534 case builtinTypeTAG:
535 return "Tag";
536
537 case builtinAbstract:
538 return "@";
539
540 case builtinTypeMission:
541 return "Mission";
542 }
543 return "???";
544 }
545
546 #define STACK_PRINT_DEPTH 30
547
print_stack(int16 * stackBase,int16 * stack)548 static void print_stack(int16 *stackBase, int16 *stack) {
549 int16 *end = (int16 *)((byte *)stackBase + kStackSize - initialStackFrameSize);
550 int size = end - stack;
551
552 if (size > STACK_PRINT_DEPTH)
553 end = stack + STACK_PRINT_DEPTH;
554
555 debugCN(3, kDebugScripts, "stack size: %d: [", size);
556 for (int16 *i = stack; i < end; i++)
557 debugCN(3, kDebugScripts, "%d ", *i);
558 if (size > STACK_PRINT_DEPTH)
559 debugCN(3, kDebugScripts, "... ");
560
561 debugC(3, kDebugScripts, "]");
562 }
563
564 #define D_OP(x) debugC(1, kDebugScripts, "[%04ld 0x%04lx]: %s", (pc - codeSeg - 1), (pc - codeSeg - 1), #x)
565 #define D_OP1(x) debugC(1, kDebugScripts, "[%04ld 0x%04lx]: %s = %d", (pc - codeSeg - 1), (pc - codeSeg - 1), #x, *stack)
566 #define D_OP2(x) debugC(1, kDebugScripts, "[%04ld 0x%04lx]: %s [%p] = %d", (pc - codeSeg - 1), (pc - codeSeg - 1), #x, (void *)addr, *stack)
567 #define D_OP3(x) debugC(1, kDebugScripts, "[%04ld 0x%04lx]: %s [%p] %d", (pc - codeSeg - 1), (pc - codeSeg - 1), #x, (void *)addr, *addr)
568
interpret(void)569 bool Thread::interpret(void) {
570 uint8 *pc,
571 *addr;
572 int16 *stack = (int16 *)stackPtr;
573 int16 instruction_count;
574 uint8 op;
575 int16 w,
576 n;
577 C_Call *cfunc;
578
579 pc = (codeSeg) + programCounter.offset;
580
581 thisThread = this; // set current thread address
582
583 for (instruction_count = 0; instruction_count < maxTimeSlice; instruction_count++) {
584 print_stack((int16 *)stackBase, stack);
585
586 switch (op = *pc++) {
587 case op_dup:
588 --stack;
589 *stack = stack[1]; // duplicate value on stack
590 D_OP1(op_dup);
591 break;
592
593 case op_drop: // drop word on stack
594 D_OP(op_drop);
595 stack++;
596 break;
597
598 case op_zero: // constant integer of zero
599 D_OP(op_zero);
600 *--stack = 0; // push integer on stack
601 break;
602
603 case op_one: // constant integer of one
604 D_OP(op_one);
605 *--stack = 1; // push integer on stack
606 break;
607
608 case op_strlit: // string literal (also pushes word)
609 case op_constint: // constant integer
610 IMMED_WORD(w); // pick up word after opcode
611 *--stack = w; // push integer on stack
612
613 if (op == op_strlit)
614 D_OP1(op_strlit);
615 else
616 D_OP1(op_constint);
617 break;
618
619 case op_getflag: // get a flag
620 addr = bitAddress(this, &pc, &w); // get address of bit
621 *--stack = ((*addr) & w) ? 1 : 0; // true or false if bit set
622 D_OP2(op_getflag);
623 break;
624
625 case op_getint: // read from integer field (mode)
626 addr = byteAddress(this, &pc); // get address of integer
627 *--stack = *(uint16 *)addr; // get integer from address
628 D_OP2(op_getint);
629 break;
630
631 case op_getbyte: // read from integer field (mode)
632 addr = byteAddress(this, &pc); // get address of integer
633 *--stack = *addr; // get byte from address
634 D_OP2(op_getbyte);
635 break;
636
637 // Note that in the current implementation, "put" ops leave
638 // the value that was stored on the stack. We mat also do a
639 // 'vput' which consumes the variable.
640
641 case op_putflag: // put to flag bit (mode)
642 addr = bitAddress(this, &pc, &w); // get address of bit
643 if (*stack) *addr |= w; // set bit if stack non-zero
644 else *addr &= ~w; // else clear it
645 D_OP3(op_putflag);
646 break;
647
648 case op_putflag_v: // put to flag bit (mode)
649 addr = bitAddress(this, &pc, &w); // get address of bit
650 if (*stack++) *addr |= w; // set bit if stack non-zero
651 else *addr &= ~w; // else clear it
652 D_OP3(op_putflag_v);
653 break;
654
655 case op_putint: // put to integer field (mode)
656 addr = byteAddress(this, &pc); // get address of integer
657 *(uint16 *)addr = *stack; // put integer to address
658 D_OP3(op_putint);
659 break;
660
661 case op_putint_v: // put to integer field (mode)
662 addr = byteAddress(this, &pc); // get address of integer
663 *(uint16 *)addr = *stack++; // put integer to address
664 D_OP3(op_putint_v);
665 break;
666
667 case op_putbyte: // put to byte field (mode)
668 addr = byteAddress(this, &pc); // get address of integer
669 *addr = *stack; // put integer to address
670 D_OP3(op_putbyte);
671 break;
672
673 case op_putbyte_v: // put to byte field (mode)
674 addr = byteAddress(this, &pc); // get address of integer
675 *addr = *stack++; // put integer to address
676 D_OP3(op_putbyte_v);
677 break;
678
679 case op_enter:
680 D_OP(op_enter);
681 print_script_name(pc - 1);
682 *--stack = framePtr; // save old frame ptr on stack
683 framePtr = (uint8 *)stack - stackBase; // new frame pointer
684 IMMED_WORD(w); // pick up word after address
685 stack -= w / 2; // make room for the locals!
686 break;
687
688 // function calls
689
690 case op_return: // return with value
691 D_OP(op_return);
692 returnVal = *stack++;
693 // fall through
694
695 case op_return_v: // return with void
696 D_OP(op_return_v);
697 stack = (int16 *)(stackBase + framePtr); // pop autos
698 framePtr = *stack++; // restore frame pointer
699
700 if (stack >= (int16 *)(stackBase + stackSize - initialStackFrameSize)) {
701 // Halt the thread here, wait for death
702 programCounter.offset = (pc - (codeSeg));
703 stackPtr = (uint8 *)stack;
704 flags |= finished;
705 return true;
706 } else {
707 programCounter.segment = *stack++;
708 programCounter.offset = *stack++;
709
710 //RUnlockHandle((RHANDLE)codeSeg);
711 codeSeg = scriptRes->loadIndexResource(programCounter.segment, "saga code segment");
712 pc = (codeSeg) + programCounter.offset;
713
714 n = *stack++; // get argument count from call
715 stack += n; // pop that many args
716
717 if (op == op_return) // if not void
718 *--stack = returnVal;// push return value
719 }
720 break;
721
722 case op_call_near: // call function in same seg
723 D_OP(op_call_near);
724
725 n = *pc++; // get argument count
726
727 programCounter.offset = (pc + 2 - codeSeg);
728
729 *--stack = n; // push number of args (16 bits)
730 // push the program counter
731 *--stack = programCounter.offset;
732 *--stack = programCounter.segment;
733
734 IMMED_WORD(w); // pick up segment offset
735 programCounter.offset = w; // store into pc
736
737 pc = codeSeg + w; // calculate PC address
738
739 print_script_name(pc);
740 break;
741
742 case op_call_far: // call function in other seg
743 D_OP(op_call_far);
744
745 n = *pc++; // get argument count
746
747 programCounter.offset = (pc + 4 - codeSeg);
748
749 *--stack = n; // push number of args (16 bits)
750 // push the program counter
751 *--stack = programCounter.offset;
752 *--stack = programCounter.segment;
753
754 IMMED_WORD(w); // pick up segment number
755 programCounter.segment = w; // set current segment
756 //RUnlockHandle((RHANDLE)codeSeg);
757 codeSeg = scriptRes->loadIndexResource(w, "saga code segment");
758 IMMED_WORD(w); // pick up segment offset
759 programCounter.offset = w; // store into pc
760
761 pc = codeSeg + w; // calculate PC address
762 print_script_name(pc);
763 break;
764
765 case op_ccall: // call C function
766 case op_ccall_v: // call C function
767 if (op == op_ccall)
768 D_OP(op_ccall);
769 else
770 D_OP(op_call_v);
771
772 n = *pc++; // get argument count
773 IMMED_WORD(w); // get function number
774 if (w < 0 || w >= globalCFuncs.numEntries)
775 error("Invalid function number");
776
777 cfunc = globalCFuncs.table[w];
778 argCount = n;
779 returnVal = cfunc(stack); // call the function
780
781 stack += n; // pop args of of the stack
782
783 if (op == op_ccall) { // push the return value
784 *--stack = returnVal; // onto the stack
785 flags |= expectResult; // script expecting result
786 } else flags &= ~expectResult; // script not expecting result
787
788 // if the thread is asleep, then no more instructions
789 if (flags & asleep)
790 instruction_count = maxTimeSlice; // break out of loop!
791
792 break;
793
794 case op_call_member: // call member function
795 case op_call_member_v: // call member function (void)
796 if (op == op_call_member)
797 D_OP(op_call_member);
798 else
799 D_OP(op_call_member_v);
800
801 n = *pc++; // get argument count
802 w = *pc++; // index of member function
803
804 {
805 uint16 *vtable,
806 *vtableEntry,
807 seg,
808 offset;
809
810 // REM: We need a more deterministic way to
811 // set up the c function tables.
812
813 CallTable *callTab = &globalCFuncs;
814
815 // Get the address of the object
816 addr = objectAddress(this, &pc, seg, offset);
817
818 // Handle the case of a builtin object which computes the
819 // vtable address in a different way.
820
821 if ((int16)seg < 0) {
822 vtable = builtinVTableAddress((int16)seg, addr, &callTab);
823 } else {
824 vtable = (uint16 *)segmentAddress(((int16 *)addr)[0],
825 ((int16 *)addr)[1]);
826 }
827
828 vtableEntry = vtable + (w * 2);
829
830 if (vtable == NULL) {
831 // Do nothing...
832 } else if (vtableEntry[0] != 0xffff) { // It's a SAGA func
833 programCounter.offset = (pc - codeSeg);
834
835 // Push the address of the object
836 *--stack = offset;
837 *--stack = seg;
838 // Push number of args. including 'this'
839 *--stack = n + 2;
840
841 // push the program counter
842 *--stack = programCounter.offset;
843 *--stack = programCounter.segment;
844
845 // Get the segment of the member function, and
846 // determine it's real address (save segment number
847 // into thread).
848 w = vtableEntry[0];
849 programCounter.segment = w;
850 //RUnlockHandle((RHANDLE)codeSeg);
851 codeSeg = scriptRes->loadIndexResource(w, "saga code segment");
852
853 // store pc-offset into pc
854 programCounter.offset = vtableEntry[1];
855
856 // calculate PC address
857 pc = (codeSeg) + programCounter.offset;
858 print_script_name(pc, objectName(seg, offset));
859
860 break;
861 } else if (vtableEntry[1] != 0xffff) { // It's a C func
862 // Save the ID of the invoked object
863 ObjectID saveID = threadArgs.invokedObject;
864
865 // Get the function number
866 w = vtableEntry[1];
867 if (w < 0 || w >= callTab->numEntries)
868 error("Invalid member function number");
869
870 // Set up thread-specific vars
871 thisObject = addr;
872 argCount = n;
873 threadArgs.invokedObject = offset;
874
875 // Get address of function and call it.
876 cfunc = callTab->table[w];
877 returnVal = cfunc(stack); // call the function
878
879 // Restore object ID from thread args
880 threadArgs.invokedObject = saveID;
881
882 // Pop args off of the stack
883 stack += n;
884
885 // Push the return value onto the stack if it's
886 // not a 'void' call.
887 if (op == op_call_member) {
888 *--stack = returnVal; // onto the stack
889 flags |= expectResult; // script expecting result
890 } else flags &= ~expectResult; // script not expecting result
891
892 // if the thread is asleep, then break interpret loop
893 if (flags & asleep) instruction_count = maxTimeSlice;
894 break;
895 }
896 // else it's a NULL function (i.e. pure virtual)
897 }
898
899 // REM: Call the member function
900
901 if (op == op_call_member) // push the return value
902 *--stack = 0; // onto the stack
903
904 break;
905
906 case op_jmp_true_v:
907 D_OP(op_jmp_true_v);
908 IMMED_WORD(w); // pick up word after address
909 if (*stack++ != 0) {
910 BRANCH(w); // if stack is non-zero, jump
911 }
912 break;
913
914 case op_jmp_false_v:
915 D_OP(op_jmp_false_v);
916 IMMED_WORD(w); // pick up word after address
917 if (*stack++ == 0) {
918 BRANCH(w); // if stack is zero, jump
919 }
920 break;
921
922 case op_jmp_true:
923 D_OP(op_true);
924 IMMED_WORD(w); // pick up word after address
925 if (*stack != 0) {
926 BRANCH(w); // if stack is non-zero. jump
927 }
928 break;
929
930 case op_jmp_false:
931 D_OP(op_false);
932 IMMED_WORD(w); // pick up word after address
933 if (*stack == 0) {
934 BRANCH(w); // if stack is zero, jump
935 }
936 break;
937
938 case op_jmp:
939 D_OP(op_jmp);
940 IMMED_WORD(w); // pick up word after address
941 BRANCH(w); // jump relative to module
942 break;
943
944 case op_jmp_switch:
945 D_OP(op_jmp_switch);
946 IMMED_WORD(n); // n = number of cases
947 w = *stack++; // w = value on stack
948 {
949 uint16 val,
950 jmp;
951
952 while (n--) {
953 IMMED_WORD(val); // val = case value
954 IMMED_WORD(jmp); // jmp = address to jump to
955 debugC(3, kDebugScripts, "Case %d: jmp %d", val, jmp);
956
957 if (w == val) { // if case values match
958 BRANCH(jmp); // jump to case
959 break;
960 }
961 }
962 if (n < 0) {
963 IMMED_WORD(jmp); // def = jump offset for default
964 BRANCH(jmp); // take default jump
965 }
966 }
967 break;
968
969 case op_jmp_seedrandom: // seeded random jump
970 case op_jmp_random: // random jump
971 if (op == op_jmp_seedrandom)
972 D_OP(op_jmp_seedrandom);
973 else
974 D_OP(op_random);
975
976 if (op == op_jmp_random) {
977 IMMED_WORD(n); // n = number of cases
978 IMMED_WORD(n); // total probability
979 n = (uint16)(g_vm->_rnd->getRandomNumber(n - 1)); // random number between 0 and n-1
980 } else {
981 int16 seed,
982 r;
983
984 seed = *stack++; // the seed value
985 IMMED_WORD(r); // n = restriction
986 IMMED_WORD(n); // n = number of cases
987 IMMED_WORD(n); // total probability
988
989 n = RRandom(n, r, seed);
990 }
991
992 for (;;) {
993 uint16 val,
994 jmp;
995
996 IMMED_WORD(val); // val = probability of this case
997 IMMED_WORD(jmp); // jmp = address to jump to
998
999 n -= val; // subtract prob from overall prob
1000 if (n < 0) { // if number within range
1001 BRANCH(jmp); // jump to prob
1002 break;
1003 }
1004 }
1005 break;
1006
1007 case op_negate:
1008 D_OP(op_negate);
1009 *stack = - *stack;
1010 break; // negate TOS
1011 case op_not:
1012 D_OP(op_not);
1013 *stack = ! *stack;
1014 break; // not TOS
1015 case op_compl:
1016 D_OP(op_compl);
1017 *stack = ~ *stack;
1018 break; // complement TOS
1019
1020 case op_inc_v:
1021 D_OP(op_inc_v);
1022 addr = byteAddress(this, &pc); // get address of integer
1023 *(uint16 *)addr += 1; // bump value by one
1024 break;
1025
1026 case op_dec_v:
1027 D_OP(op_dec_v);
1028 addr = byteAddress(this, &pc); // get address of integer
1029 *(uint16 *)addr -= 1; // bump value by one
1030 break;
1031
1032 case op_postinc:
1033 D_OP(op_postinc);
1034 addr = byteAddress(this, &pc); // get address of integer
1035 *--stack = *(uint16 *)addr; // get integer from address
1036 *(uint16 *)addr += 1; // bump value by one
1037 break;
1038
1039 case op_postdec:
1040 D_OP(op_postdec);
1041 addr = byteAddress(this, &pc); // get address of integer
1042 *--stack = *(uint16 *)addr; // get integer from address
1043 *(uint16 *)addr -= 1; // bump value by one
1044 break;
1045
1046 // Binary ops. Since I don't know the order of evaluation of
1047 // These C operations, I use a temp variable. Note that
1048 // stack is incremented before storing to skip over the
1049 // dropped variable.
1050
1051 case op_add:
1052 D_OP(op_add);
1053 w = (stack[1] + stack[0]);
1054 *++stack = w;
1055 break;
1056 case op_sub:
1057 D_OP(op_sub);
1058 w = (stack[1] - stack[0]);
1059 *++stack = w;
1060 break;
1061 case op_mul:
1062 D_OP(op_mul);
1063 w = (stack[1] * stack[0]);
1064 *++stack = w;
1065 break;
1066 case op_div:
1067 D_OP(op_div);
1068 w = (stack[1] / stack[0]);
1069 *++stack = w;
1070 break;
1071 case op_mod:
1072 D_OP(op_mod);
1073 w = (stack[1] % stack[0]);
1074 *++stack = w;
1075 break;
1076 case op_eq:
1077 D_OP(op_eq);
1078 w = (stack[1] == stack[0]);
1079 *++stack = w;
1080 break;
1081 case op_ne:
1082 D_OP(op_ne);
1083 w = (stack[1] != stack[0]);
1084 *++stack = w;
1085 break;
1086 case op_gt:
1087 D_OP(op_gt);
1088 w = (stack[1] > stack[0]);
1089 *++stack = w;
1090 break;
1091 case op_lt:
1092 D_OP(op_lt);
1093 w = (stack[1] < stack[0]);
1094 *++stack = w;
1095 break;
1096 case op_ge:
1097 D_OP(op_ge);
1098 w = (stack[1] >= stack[0]);
1099 *++stack = w;
1100 break;
1101 case op_le:
1102 D_OP(op_le);
1103 w = (stack[1] <= stack[0]);
1104 *++stack = w;
1105 break;
1106 case op_rsh:
1107 D_OP(op_rsh);
1108 w = (stack[1] >> stack[0]);
1109 *++stack = w;
1110 break;
1111 case op_lsh:
1112 D_OP(op_lsh);
1113 w = (stack[1] << stack[0]);
1114 *++stack = w;
1115 break;
1116 case op_and:
1117 D_OP(op_and);
1118 w = (stack[1] & stack[0]);
1119 *++stack = w;
1120 break;
1121 case op_or:
1122 D_OP(op_or);
1123 w = (stack[1] | stack[0]);
1124 *++stack = w;
1125 break;
1126 case op_xor:
1127 D_OP(op_xor);
1128 w = (stack[1] ^ stack[0]);
1129 *++stack = w;
1130 break;
1131 case op_land:
1132 D_OP(op_land);
1133 w = (stack[1] && stack[0]);
1134 *++stack = w;
1135 break;
1136 case op_lor:
1137 D_OP(op_lor);
1138 w = (stack[1] || stack[0]);
1139 *++stack = w;
1140 break;
1141 case op_lxor:
1142 D_OP(op_lxor);
1143 w = (stack[1] && !stack[0]) || (!stack[1] && stack[0]);
1144 *++stack = w;
1145 break;
1146
1147 case op_speak:
1148 case op_dialog_begin:
1149 case op_dialog_end:
1150 case op_reply:
1151 case op_animate:
1152 script_error("Feature not implemented.\n");
1153 break;
1154
1155 default:
1156 script_error("fatal error: undefined opcode");
1157 break;
1158 }
1159 }
1160
1161 programCounter.offset = (pc - (codeSeg));
1162 stackPtr = (uint8 *)stack;
1163
1164 return false;
1165 }
1166
1167 /* ============================================================================ *
1168 ThreadList class
1169 * ============================================================================ */
1170
1171 class ThreadList {
1172 enum {
1173 kNumThreads = 25
1174 };
1175
1176 Thread *_list[kNumThreads];
1177
1178 public:
1179 // Constructor
ThreadList(void)1180 ThreadList(void) {
1181 for (uint i = 0; i < kNumThreads; i++)
1182 _list[i] = nullptr;
1183 }
1184
1185 void read(Common::InSaveFile *in);
1186
1187 // Return the number of bytes needed to archive this thread list
1188 // in an archive buffer
1189 int32 archiveSize(void);
1190
1191 void write(Common::MemoryWriteStreamDynamic *out);
1192
1193 // Cleanup the active threads
1194 void cleanup(void);
1195
1196 // Place a thread back into the inactive list
1197 void deleteThread(Thread *p);
1198
1199 void newThread(Thread *p, ThreadID id);
1200
1201 void newThread(Thread *p);
1202
1203 // Return the specified thread's ID
getThreadID(Thread * thread)1204 ThreadID getThreadID(Thread *thread) {
1205 for (uint i = 0; i < kNumThreads; i++) {
1206 if (_list[i] == thread)
1207 return i;
1208 }
1209
1210 error("Unknown thread address: %p", (void *)thread);
1211 }
1212
1213 // Return a pointer to a thread, given an ID
getThreadAddress(ThreadID id)1214 Thread *getThreadAddress(ThreadID id) {
1215 return _list[id];
1216 }
1217
1218 // Return a pointer to the first active thread
1219 Thread *first(void);
1220
1221 Thread *next(Thread *thread);
1222 };
1223
read(Common::InSaveFile * in)1224 void ThreadList::read(Common::InSaveFile *in) {
1225 int16 threadCount;
1226
1227 // Get the count of threads and increment the buffer pointer
1228 threadCount = in->readSint16LE();
1229 debugC(3, kDebugSaveload, "... threadCount = %d", threadCount);
1230
1231 // Iterate through the archive data, reconstructing the Threads
1232 for (int i = 0; i < threadCount; i++) {
1233 debugC(3, kDebugSaveload, "Saving Thread %d", i);
1234 ThreadID id;
1235
1236 // Retreive the Thread's id number
1237 id = in->readSint16LE();
1238 debugC(4, kDebugSaveload, "...... id = %d", id);
1239
1240 new Thread(in, id);
1241 }
1242 }
1243
archiveSize(void)1244 int32 ThreadList::archiveSize(void) {
1245 int32 size = sizeof(int16);
1246
1247 for (uint i = 0; i < kNumThreads; i++) {
1248 if (_list[i])
1249 size += sizeof(ThreadID) + _list[i]->archiveSize();
1250 }
1251
1252 return size;
1253 }
1254
write(Common::MemoryWriteStreamDynamic * out)1255 void ThreadList::write(Common::MemoryWriteStreamDynamic *out) {
1256 int16 threadCount = 0;
1257 Thread *th;
1258
1259 // Count the active threads
1260 for (th = first(); th; th = next(th))
1261 threadCount++;
1262
1263 // Store the thread count in the archive buffer
1264 out->writeSint16LE(threadCount);
1265 debugC(3, kDebugSaveload, "... threadCount = %d", threadCount);
1266
1267 // Iterate through the threads, archiving each
1268 for (th = first(); th; th = next(th)) {
1269 debugC(3, kDebugSaveload, "Loading ThreadID %d", getThreadID(th));
1270 // Store the Thread's id number
1271 out->writeSint16LE(getThreadID(th));
1272
1273 th->write(out);
1274 }
1275 }
1276
1277 //-------------------------------------------------------------------
1278 // Cleanup the active threads
1279
cleanup(void)1280 void ThreadList::cleanup(void) {
1281 for (uint i = 0; i < kNumThreads; i++) {
1282 delete _list[i];
1283 _list[i] = nullptr;
1284 }
1285 }
1286
1287 //-------------------------------------------------------------------
1288 // Place a thread back into the inactive list
1289
deleteThread(Thread * p)1290 void ThreadList::deleteThread(Thread *p) {
1291 for (uint i = 0; i < kNumThreads; i++) {
1292 if (_list[i] == p) {
1293 _list[i] = nullptr;
1294 }
1295 }
1296 }
1297
newThread(Thread * p,ThreadID id)1298 void ThreadList::newThread(Thread *p, ThreadID id) {
1299 if (_list[id])
1300 error("Thread %d already exists", id);
1301
1302 _list[id] = p;
1303 }
1304
newThread(Thread * p)1305 void ThreadList::newThread(Thread *p) {
1306 for (uint i = 0; i < kNumThreads; i++) {
1307 if (!_list[i]) {
1308 _list[i] = p;
1309 return;
1310 }
1311 }
1312
1313 error("ThreadList::newThread(): Too many threads");
1314 }
1315
1316 //-------------------------------------------------------------------
1317 // Return a pointer to the first active thread
1318
first(void)1319 Thread *ThreadList::first(void) {
1320 for (uint i = 0; i < kNumThreads; i++)
1321 if (_list[i])
1322 return _list[i];
1323
1324 return nullptr;
1325 }
1326
next(Thread * thread)1327 Thread *ThreadList::next(Thread *thread) {
1328 uint i;
1329 for (i = 0; i < kNumThreads; i++)
1330 if (_list[i] == thread)
1331 break;
1332
1333 i++;
1334 if (i >= kNumThreads)
1335 return nullptr;
1336
1337 for (; i < kNumThreads; i++)
1338 if (_list[i])
1339 return _list[i];
1340
1341 return nullptr;
1342 }
1343
1344
1345 /* ===================================================================== *
1346 Global thread list instantiation
1347 * ===================================================================== */
1348
1349 // The thread list is instantiated like this in order to keep the
1350 // constructor from being called until it is explicitly called with
1351 // the overloaded new operator.
1352
1353 static uint8 threadListBuffer[sizeof(ThreadList)];
1354 static ThreadList &threadList = *((ThreadList *)threadListBuffer);
1355
1356 /* ============================================================================ *
1357 ThreadList management functions
1358 * ============================================================================ */
1359
1360 //-------------------------------------------------------------------
1361 // Initialize the SAGA thread list
1362
initSAGAThreads(void)1363 void initSAGAThreads(void) {
1364 // Simply call the Thread List default constructor
1365 }
1366
saveSAGAThreads(Common::OutSaveFile * outS)1367 void saveSAGAThreads(Common::OutSaveFile *outS) {
1368 debugC(2, kDebugSaveload, "Saving SAGA Threads");
1369
1370 outS->write("SAGA", 4);
1371 CHUNK_BEGIN;
1372 threadList.write(out);
1373 CHUNK_END;
1374 }
1375
loadSAGAThreads(Common::InSaveFile * in,int32 chunkSize)1376 void loadSAGAThreads(Common::InSaveFile *in, int32 chunkSize) {
1377 debugC(2, kDebugSaveload, "Loading SAGA Threads");
1378
1379 if (chunkSize == 0) {
1380 return;
1381 }
1382
1383 // Reconstruct stackList from archived data
1384 threadList.read(in);
1385 }
1386
1387 //-------------------------------------------------------------------
1388 // Dispose of the active SAGA threads
1389
cleanupSAGAThreads(void)1390 void cleanupSAGAThreads(void) {
1391 // Simply call the ThreadList cleanup() function
1392 threadList.cleanup();
1393 }
1394
1395 //-------------------------------------------------------------------
1396 // Dispose of an active SAGA thread
1397
deleteThread(Thread * thread)1398 void deleteThread(Thread *thread) {
1399 threadList.deleteThread(thread);
1400 }
1401
newThread(Thread * p,ThreadID id)1402 void newThread(Thread *p, ThreadID id) {
1403 threadList.newThread(p, id);
1404 }
1405
newThread(Thread * thread)1406 void newThread(Thread *thread) {
1407 threadList.newThread(thread);
1408 }
1409
1410 //-------------------------------------------------------------------
1411 // Return the ID of the specified SAGA thread
1412
getThreadID(Thread * thread)1413 ThreadID getThreadID(Thread *thread) {
1414 return threadList.getThreadID(thread);
1415 }
1416
1417 //-------------------------------------------------------------------
1418 // Return a pointer to a SAGA thread, given a thread ID
1419
getThreadAddress(ThreadID id)1420 Thread *getThreadAddress(ThreadID id) {
1421 return threadList.getThreadAddress(id);
1422 }
1423
1424 /* ============================================================================ *
1425 Thread member functions
1426 * ============================================================================ */
1427
1428 //-----------------------------------------------------------------------
1429 // Thread constructor
1430
Thread(uint16 segNum,uint16 segOff,scriptCallFrame & args)1431 Thread::Thread(uint16 segNum, uint16 segOff, scriptCallFrame &args) {
1432 codeSeg = scriptRes->loadIndexResource(segNum, "saga code segment");
1433
1434 // initialize the thread
1435 stackSize = kStackSize;
1436 flags = 0;
1437 returnVal = 0;
1438 programCounter.segment = segNum;
1439 programCounter.offset = segOff;
1440 threadArgs = args;
1441 stackBase = (byte *)malloc(stackSize);
1442 stackPtr = stackBase + stackSize - initialStackFrameSize;
1443 ((uint16 *)stackPtr)[0] = 0; // 0 args
1444 ((uint16 *)stackPtr)[1] = 0; // dummy return address
1445 ((uint16 *)stackPtr)[2] = 0; // dummy return address
1446 framePtr = stackSize;
1447 _valid = true;
1448
1449 if ((codeSeg)[programCounter.offset] != op_enter) {
1450 //warning("SAGA failure: Invalid script entry point (export=%d) [segment=%d:%d]\n", lastExport, segNum, segOff);
1451 _valid = false;
1452 }
1453
1454 newThread(this);
1455 }
1456
Thread(Common::SeekableReadStream * stream,ThreadID id)1457 Thread::Thread(Common::SeekableReadStream *stream, ThreadID id) {
1458 int16 stackOffset;
1459
1460 programCounter.segment = stream->readUint16LE();
1461 programCounter.offset = stream->readUint16LE();
1462
1463 stackSize = stream->readSint16LE();
1464 flags = stream->readSint16LE();
1465 framePtr = stream->readSint16LE();
1466 returnVal = stream->readSint16LE();
1467
1468 waitAlarm.read(stream);
1469
1470 stackOffset = stream->readSint16LE();
1471
1472 debugC(4, kDebugSaveload, "...... stackSize = %d", stackSize);
1473 debugC(4, kDebugSaveload, "...... flags = %d", flags);
1474 debugC(4, kDebugSaveload, "...... framePtr = %d", framePtr);
1475 debugC(4, kDebugSaveload, "...... returnVal = %d", returnVal);
1476 debugC(4, kDebugSaveload, "...... stackOffset = %d", stackOffset);
1477
1478 codeSeg = scriptRes->loadIndexResource(programCounter.segment, "saga code segment");
1479
1480 stackBase = (byte *)malloc(stackSize);
1481 stackPtr = stackBase + stackSize - stackOffset;
1482
1483 stream->read(stackPtr, stackOffset);
1484
1485 newThread(this, id);
1486 }
1487
1488 //-----------------------------------------------------------------------
1489 // Thread destructor
1490
~Thread()1491 Thread::~Thread() {
1492 // Clear extended bit if it was set
1493 clearExtended();
1494
1495 // Free the thread's code segment
1496 //RUnlockHandle((RHANDLE)codeSeg);
1497
1498 // Deallocate the thread stack
1499 free(stackBase);
1500
1501 deleteThread(this);
1502 }
1503
1504 //-----------------------------------------------------------------------
1505 // Return the number of bytes need to archive this thread in an arhive
1506 // buffer
1507
archiveSize(void)1508 int32 Thread::archiveSize(void) {
1509 return sizeof(programCounter)
1510 + sizeof(stackSize)
1511 + sizeof(flags)
1512 + sizeof(framePtr)
1513 + sizeof(returnVal)
1514 + sizeof(waitAlarm)
1515 + sizeof(int16) // stack offset
1516 + (stackBase + stackSize) - stackPtr;
1517 }
1518
write(Common::MemoryWriteStreamDynamic * out)1519 void Thread::write(Common::MemoryWriteStreamDynamic *out) {
1520 int16 stackOffset;
1521
1522 out->writeUint16LE(programCounter.segment);
1523 out->writeUint16LE(programCounter.offset);
1524
1525 out->writeSint16LE(stackSize);
1526 out->writeSint16LE(flags);
1527 out->writeSint16LE(framePtr);
1528 out->writeSint16LE(returnVal);
1529
1530 waitAlarm.write(out);
1531
1532 warning("STUB: Thread::write: Pointer arithmetic");
1533 stackOffset = (stackBase + stackSize) - stackPtr;
1534 out->writeSint16LE(stackOffset);
1535
1536 out->write(stackPtr, stackOffset);
1537
1538 debugC(4, kDebugSaveload, "...... stackSize = %d", stackSize);
1539 debugC(4, kDebugSaveload, "...... flags = %d", flags);
1540 debugC(4, kDebugSaveload, "...... framePtr = %d", framePtr);
1541 debugC(4, kDebugSaveload, "...... returnVal = %d", returnVal);
1542 debugC(4, kDebugSaveload, "...... stackOffset = %d", stackOffset);
1543 }
1544
1545 //-----------------------------------------------------------------------
1546 // Thread dispatcher
1547
dispatch(void)1548 void Thread::dispatch(void) {
1549 Thread *th,
1550 *nextThread;
1551
1552 int numThreads = 0,
1553 numExecute = 0,
1554 numWaitDelay = 0,
1555 numWaitFrames = 0,
1556 numWaitSemi = 0,
1557 numWaitOther = 0;
1558
1559 for (th = threadList.first(); th; th = threadList.next(th)) {
1560 if (th->flags & waiting) {
1561 switch (th->waitType) {
1562
1563 case waitDelay:
1564 numWaitDelay++;
1565 break;
1566 case waitFrameDelay:
1567 numWaitFrames++;
1568 break;
1569 case waitTagSemaphore:
1570 numWaitSemi++;
1571 break;
1572 default:
1573 numWaitOther++;
1574 break;
1575 }
1576 } else numExecute++;
1577 numThreads++;
1578 }
1579
1580 debugC(9, kDebugScripts, "Threads:%d X:%d D:%d F:%d T:%d O:%d", numThreads, numExecute, numWaitDelay, numWaitFrames, numWaitSemi, numWaitOther);
1581
1582 for (th = threadList.first(); th; th = nextThread) {
1583 nextThread = threadList.next(th);
1584
1585 if (th->flags & (finished | aborted)) {
1586 delete th;
1587 continue;
1588 }
1589
1590 if (th->flags & waiting) {
1591 switch (th->waitType) {
1592
1593 case waitDelay:
1594
1595 // Wake up the thread!
1596
1597 if (th->waitAlarm.check())
1598 th->flags &= ~waiting;
1599 break;
1600
1601 case waitFrameDelay:
1602
1603 if (th->waitFrameAlarm.check())
1604 th->flags &= ~waiting;
1605 break;
1606
1607 case waitTagSemaphore:
1608 if (th->waitParam->isExclusive() == false) {
1609 th->flags &= ~waiting;
1610 th->waitParam->setExclusive(true);
1611 }
1612 break;
1613 default:
1614 break;
1615 }
1616 }
1617
1618 do {
1619 if (th->flags & (waiting | finished | aborted))
1620 break;
1621
1622 if (th->interpret())
1623 goto break_thread_loop;
1624 } while (th->flags & synchronous);
1625 }
1626 break_thread_loop:
1627 ;
1628 }
1629
1630 //-----------------------------------------------------------------------
1631 // Run scripts which are on the queue
1632
dispatchScripts(void)1633 void dispatchScripts(void) {
1634 Thread::dispatch();
1635 }
1636
1637 //-----------------------------------------------------------------------
1638 // Run a script until finished
1639
run(void)1640 scriptResult Thread::run(void) {
1641 int i = 4000;
1642
1643 while (i--) {
1644 // If script stopped, then return
1645 if (flags & (waiting | finished | aborted)) {
1646 if (flags & finished) return scriptResultFinished;
1647 if (flags & waiting) return scriptResultAsync;
1648 return scriptResultAborted;
1649 // can't ever fall thru here...
1650 }
1651
1652 // run the script some more...
1653 interpret();
1654 }
1655 error("Thread timed out!\n");
1656 }
1657
1658 //-----------------------------------------------------------------------
1659 // Convert to extended thread
1660
setExtended(void)1661 void Thread::setExtended(void) {
1662 if (!(flags & extended)) {
1663 flags |= extended;
1664 extendedThreadLevel++;
1665 }
1666 }
1667
1668 //-----------------------------------------------------------------------
1669 // Convert back to regular thread
1670
clearExtended(void)1671 void Thread::clearExtended(void) {
1672 if (flags & extended) {
1673 flags &= ~extended;
1674 extendedThreadLevel--;
1675 }
1676 }
1677
1678 /* ============================================================================ *
1679 Script Management functions
1680 * ============================================================================ */
initScripts(void)1681 void initScripts(void) {
1682 // Open the script resource group
1683 scriptRes = scriptResFile->newContext(sagaID, "script resources");
1684 if (scriptRes == NULL)
1685 error("Unable to open script resource file!\n");
1686
1687 // Load the data segment
1688 dataSegment = scriptRes->loadResource(dataSegID, "saga data segment");
1689
1690 if (dataSegment == NULL)
1691 error("Unable to load the SAGA data segment");
1692
1693 dataSegSize = scriptRes->getSize(dataSegID, "saga data segment");
1694 debugC(2, kDebugScripts, "dataSegment loaded at %p: size: %d", (void*)dataSegment, dataSegSize);
1695
1696 // Common::hexdump(dataSegment, dataSegSize);
1697
1698 exportSegment = scriptRes->loadResource(exportSegID, "saga export segment");
1699 assert(exportSegment != NULL);
1700
1701 // Common::hexdump(exportSegment, scriptRes->getSize(exportSegID, "saga export segment"));
1702
1703 exportCount = (scriptRes->getSize(exportSegID, "saga export segment") / sizeof(uint32)) + 1;
1704 debugC(2, kDebugScripts, "exportSegment loaded at %p: size: %d, exportCount: %ld",
1705 (void*)exportSegment, scriptRes->getSize(exportSegID, "saga export segment"), exportCount);
1706 }
1707
cleanupScripts(void)1708 void cleanupScripts(void) {
1709 if (exportSegment)
1710 free(exportSegment);
1711
1712 if (dataSegment)
1713 free(dataSegment);
1714
1715 if (scriptRes)
1716 scriptResFile->disposeContext(scriptRes);
1717 scriptRes = NULL;
1718 }
1719
1720 //-----------------------------------------------------------------------
1721 // Load the SAGA data segment from the resource file
1722
initSAGADataSeg(void)1723 void initSAGADataSeg(void) {
1724 // Load the data segment
1725 scriptRes->seek(dataSegID);
1726 scriptRes->read(dataSegment, dataSegSize);
1727 }
1728
saveSAGADataSeg(Common::OutSaveFile * outS)1729 void saveSAGADataSeg(Common::OutSaveFile *outS) {
1730 debugC(2, kDebugSaveload, "Saving Data Segment");
1731
1732 outS->write("SDTA", 4);
1733 CHUNK_BEGIN;
1734 out->write(dataSegment, dataSegSize);
1735 CHUNK_END;
1736 }
1737
loadSAGADataSeg(Common::InSaveFile * in)1738 void loadSAGADataSeg(Common::InSaveFile *in) {
1739 in->read(dataSegment, dataSegSize);
1740 }
1741
1742 //-----------------------------------------------------------------------
1743 // Look up an entry in the SAGA export table
1744
lookupExport(uint16 entry,uint16 & segNum,uint16 & segOff)1745 static bool lookupExport(
1746 uint16 entry,
1747 uint16 &segNum,
1748 uint16 &segOff) {
1749 uint32 segRef;
1750
1751 assert(entry > 0);
1752 assert(entry <= exportCount);
1753
1754 segRef = READ_LE_INT32(exportSegment + 4 * entry - 2);
1755 segOff = segRef >> 16,
1756 segNum = segRef & 0x0000ffff;
1757
1758 lastExport = entry;
1759 if (segNum > 1000)
1760 error("SAGA failure: Bad data in export table entry #%d (see scripts.r)", entry);
1761
1762 return true;
1763 }
1764
1765 //-----------------------------------------------------------------------
1766 // Run a script to completion (or until it forks)
1767
runScript(uint16 exportEntryNum,scriptCallFrame & args)1768 scriptResult runScript(uint16 exportEntryNum, scriptCallFrame &args) {
1769 uint16 segNum,
1770 segOff;
1771 Thread *th;
1772 scriptResult result;
1773 Thread *saveThread = thisThread;
1774
1775 assert(exportEntryNum > 0);
1776 lookupExport(exportEntryNum, segNum, segOff);
1777
1778 // Create a new thread
1779 th = new Thread(segNum, segOff, args);
1780 thisThread = th;
1781 // FIXME: We should probably just use an error(), but this will work for mass debugging
1782 if (th == nullptr) {
1783 debugC(4, kDebugScripts, "Couldn't allocate memory for Thread(%d, %d)", segNum, segOff);
1784 return scriptResultNoScript;
1785 } else if (!th->_valid) {
1786 debugC(4, kDebugScripts, "Scripts: %d is not valid", lastExport);
1787 return scriptResultNoScript;
1788 }
1789 print_script_name((th->codeSeg) + th->programCounter.offset, objectName(segNum, segOff));
1790
1791 // Run the thread to completion
1792 result = th->run();
1793 args.returnVal = th->returnVal;
1794
1795 // If the thread is not still running, then delete it
1796 if (result != scriptResultAsync) delete th;
1797
1798 // restore "thisThread" ptr.
1799 thisThread = saveThread;
1800 return result;
1801 }
1802
1803 //-----------------------------------------------------------------------
1804 // Run a class member function to completion (or until it forks)
1805
runMethod(uint16 scriptClassID,int16 bType,uint16 index,uint16 methodNum,scriptCallFrame & args)1806 scriptResult runMethod(
1807 uint16 scriptClassID, // which script class
1808 int16 bType, // builtin type
1809 uint16 index, // object index
1810 uint16 methodNum,
1811 scriptCallFrame &args) {
1812 uint16 segNum,
1813 segOff;
1814 uint16 *vTable;
1815 Thread *th;
1816 scriptResult result = scriptResultNoScript;
1817 Thread *saveThread = thisThread;
1818
1819 // For abstract classes, the object index is also the class
1820 // index.
1821 if (bType == builtinAbstract)
1822 index = scriptClassID;
1823
1824 lookupExport(scriptClassID, segNum, segOff);
1825
1826 // Get address of class function table
1827 vTable = (uint16 *)
1828 segmentAddress(segNum, segOff + methodNum * sizeof(uint32));
1829
1830 segNum = vTable[0];
1831 segOff = vTable[1];
1832
1833 if (segNum == 0xffff) { // it's a CFUNC or NULL func
1834 if (segOff == 0xffff) { // it's a NULL function
1835 return scriptResultNoScript;
1836 } else { // It's a C function
1837 int16 funcNum = segOff; // function number
1838 int16 stack[1]; // dummy stack argument
1839 C_Call *cfunc;
1840
1841 // Make sure the C function number is OK
1842 assert(funcNum >= 0);
1843 assert(funcNum < globalCFuncs.numEntries);
1844 cfunc = globalCFuncs.table[funcNum];
1845
1846 // Build a temporary dummy thread
1847 th = new Thread(0, 0, args);
1848 thisThread = th;
1849 if (th == nullptr)
1850 return scriptResultNoScript;
1851 else if (!th->_valid)
1852 return scriptResultNoScript;
1853
1854 result = (scriptResult)cfunc(stack); // call the function
1855 delete th;
1856 }
1857 } else {
1858 // Create a new thread
1859 th = new Thread(segNum, segOff, args);
1860 thisThread = th;
1861 if (th == nullptr) {
1862 debugC(3, kDebugScripts, "Couldn't allocate memory for Thread(%d, %d)", segNum, segOff);
1863 return scriptResultNoScript;
1864 } else if (!th->_valid) {
1865 debugC(3, kDebugScripts, "Scripts: %d is not valid", lastExport);
1866 return scriptResultNoScript;
1867 }
1868 print_script_name((th->codeSeg) + th->programCounter.offset, objectName(bType, index));
1869
1870 // Put the object segment and ID onto the dummy stack frame
1871 ((uint16 *)th->stackPtr)[3] = bType;
1872 ((uint16 *)th->stackPtr)[4] = index;
1873
1874 // Run the thread to completion
1875 result = th->run();
1876 args.returnVal = th->returnVal;
1877 debugC(3, kDebugScripts, "return: %d", th->returnVal);
1878
1879 if (result != scriptResultAsync) delete th;
1880 }
1881
1882 thisThread = saveThread; // restore "thisThread" ptr.
1883 return result;
1884 }
1885
1886 //-----------------------------------------------------------------------
1887 // Run a class member function to completion (or until it forks)
1888
runObjectMethod(ObjectID id,uint16 methodNum,scriptCallFrame & args)1889 scriptResult runObjectMethod(
1890 ObjectID id,
1891 uint16 methodNum,
1892 scriptCallFrame &args) {
1893 GameObject *obj;
1894
1895 obj = GameObject::objectAddress(id);
1896
1897 return runMethod(obj->scriptClass(),
1898 builtinTypeObject,
1899 id,
1900 methodNum,
1901 args);
1902 }
1903
1904 //-----------------------------------------------------------------------
1905 // Run a class member function to completion (or until it forks)
1906
runTagMethod(uint16 index,uint16 methodNum,scriptCallFrame & args)1907 scriptResult runTagMethod(
1908 uint16 index, // tag number
1909 uint16 methodNum,
1910 scriptCallFrame &args) {
1911 ActiveItemPtr aItem;
1912
1913 aItem = ActiveItem::activeItemAddress(index);
1914 if (!aItem->_data.scriptClassID)
1915 return scriptResultNoScript;
1916
1917 return runMethod(aItem->_data.scriptClassID,
1918 builtinTypeTAG,
1919 index,
1920 methodNum,
1921 args);
1922 }
1923
1924 //-----------------------------------------------------------------------
1925 // Wake up a thread unconditionally
1926
wakeUpThread(ThreadID id)1927 void wakeUpThread(ThreadID id) {
1928 if (id != NoThread) {
1929 Thread *thread = getThreadAddress(id);
1930
1931 thread->flags &= ~Thread::waiting;
1932 }
1933 }
1934
wakeUpThread(ThreadID id,int16 returnVal)1935 void wakeUpThread(ThreadID id, int16 returnVal) {
1936 if (id != NoThread) {
1937 Thread *thread = getThreadAddress(id);
1938
1939 if (thread->flags & Thread::expectResult) {
1940 WriteStatusF(8, "Result %d", returnVal);
1941 thread->returnVal = returnVal;
1942 *(int16 *)thread->stackPtr = returnVal;
1943 } else WriteStatusF(8, "Thread not expecting result!");
1944
1945 thread->flags &= ~(Thread::waiting | Thread::expectResult);
1946 }
1947 }
1948
1949 } // end of namespace Saga2
1950