aboutsummaryrefslogtreecommitdiff
path: root/engines/scumm/he
diff options
context:
space:
mode:
authorEugene Sandulenko2006-02-15 00:57:50 +0000
committerEugene Sandulenko2006-02-15 00:57:50 +0000
commit10c7835cfcdd8f0eb1eadf52ac9cfd39d2d6be61 (patch)
treedc3e12a02eb6befdf76c5fc0110f4cd9be82f569 /engines/scumm/he
parent0bea9cf47b027ad8936751f48779046ca0a48bf9 (diff)
downloadscummvm-rg350-10c7835cfcdd8f0eb1eadf52ac9cfd39d2d6be61.tar.gz
scummvm-rg350-10c7835cfcdd8f0eb1eadf52ac9cfd39d2d6be61.tar.bz2
scummvm-rg350-10c7835cfcdd8f0eb1eadf52ac9cfd39d2d6be61.zip
Moved all he-specific source files to engines/scumm/he/ subdirectory
svn-id: r20696
Diffstat (limited to 'engines/scumm/he')
-rw-r--r--engines/scumm/he/floodfill_he.cpp293
-rw-r--r--engines/scumm/he/floodfill_he.h67
-rw-r--r--engines/scumm/he/intern_he.h606
-rw-r--r--engines/scumm/he/logic_he.cpp836
-rw-r--r--engines/scumm/he/logic_he.h124
-rw-r--r--engines/scumm/he/palette_he.cpp317
-rw-r--r--engines/scumm/he/resource_v7he.cpp1912
-rw-r--r--engines/scumm/he/resource_v7he.h556
-rw-r--r--engines/scumm/he/script_v100he.cpp2978
-rw-r--r--engines/scumm/he/script_v6he.cpp1276
-rw-r--r--engines/scumm/he/script_v72he.cpp2368
-rw-r--r--engines/scumm/he/script_v7he.cpp1153
-rw-r--r--engines/scumm/he/script_v80he.cpp811
-rw-r--r--engines/scumm/he/script_v90he.cpp2636
-rw-r--r--engines/scumm/he/sound_he.cpp516
-rw-r--r--engines/scumm/he/sprite_he.cpp1440
-rw-r--r--engines/scumm/he/sprite_he.h222
-rw-r--r--engines/scumm/he/wiz_he.cpp2088
-rw-r--r--engines/scumm/he/wiz_he.h211
19 files changed, 20410 insertions, 0 deletions
diff --git a/engines/scumm/he/floodfill_he.cpp b/engines/scumm/he/floodfill_he.cpp
new file mode 100644
index 0000000000..3736cb4d36
--- /dev/null
+++ b/engines/scumm/he/floodfill_he.cpp
@@ -0,0 +1,293 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "scumm/he/floodfill_he.h"
+#include "scumm/he/intern_he.h"
+#include "scumm/resource.h"
+#include "scumm/scumm.h"
+
+namespace Scumm {
+
+static bool floodFillPixelCheck(int x, int y, const FloodFillState *ffs) {
+ int diffColor = ffs->color1 - ffs->color2;
+ if (x >= 0 && x < ffs->dst_w && y >= 0 && y < ffs->dst_h) {
+ uint8 color = *(ffs->dst + y * ffs->dst_w + x);
+ diffColor = color - ffs->color1;
+ }
+ return diffColor == 0;
+}
+
+static void floodFillProcessRect(FloodFillState *ffs, const Common::Rect *r) {
+ Common::Rect *dr = &ffs->dstBox;
+ if (dr->right >= dr->left && dr->top <= dr->bottom) {
+ int rw = r->right - r->left + 1;
+ int rh = r->bottom - r->top + 1;
+ assert(r->top + rh <= ffs->dst_h);
+ assert(r->left + rw <= ffs->dst_w);
+ uint8 *dst = ffs->dst + r->top * ffs->dst_w + r->left;
+ if (rw <= 1) {
+ --rh;
+ while (rh >= 0) {
+ *dst = ffs->color2;
+ dst += ffs->dst_w;
+ --rh;
+ }
+ } else {
+ --rh;
+ while (rh >= 0) {
+ memset(dst, ffs->color2, rw);
+ dst += ffs->dst_w;
+ --rh;
+ }
+ }
+ dr->extend(*r);
+ } else {
+ *dr = *r;
+ }
+}
+
+static void floodFillAddLine(FloodFillLine **ffl, int y, int x1, int x2, int dy) {
+ (*ffl)->y = y;
+ (*ffl)->x1 = x1;
+ (*ffl)->x2 = x2;
+ (*ffl)->inc = dy;
+ (*ffl)++;
+}
+
+static void floodFillProcess(int x, int y, FloodFillState *ffs, FloodFillPixelCheckCallback pixelCheckCallback) {
+ ffs->dstBox.left = ffs->dstBox.top = 12345;
+ ffs->dstBox.right = ffs->dstBox.bottom = -12345;
+
+ FloodFillLine **fillLineCur = &ffs->fillLineTableCur;
+ FloodFillLine **fillLineEnd = &ffs->fillLineTableEnd;
+
+ assert(*fillLineCur < *fillLineEnd);
+ if (ffs->srcBox.top <= y + 1 && ffs->srcBox.bottom >= y + 1) {
+ (*fillLineCur)->y = y;
+ (*fillLineCur)->x1 = x;
+ (*fillLineCur)->x2 = x;
+ (*fillLineCur)->inc = 1;
+ (*fillLineCur)++;
+ }
+
+ assert(*fillLineCur < *fillLineEnd);
+ if (ffs->srcBox.top <= y && ffs->srcBox.bottom >= y) {
+ (*fillLineCur)->y = y + 1;
+ (*fillLineCur)->x1 = x;
+ (*fillLineCur)->x2 = x;
+ (*fillLineCur)->inc = -1;
+ (*fillLineCur)++;
+ }
+
+ assert(ffs->fillLineTable <= *fillLineCur);
+ FloodFillLine **fillLineStart = fillLineCur;
+
+ while (ffs->fillLineTable < *fillLineStart) {
+ Common::Rect r;
+ int x_start;
+ FloodFillLine *fflCur = --(*fillLineCur);
+ int dy = fflCur->inc;
+ int x_end = fflCur->x2;
+ int x1 = fflCur->x1;
+ int x2 = fflCur->x1 + 1;
+ r.bottom = r.top = y = fflCur->y + fflCur->inc;
+ r.left = x2;
+ r.right = x1;
+ x = x1;
+ while (ffs->srcBox.left <= x) {
+ if (!(*pixelCheckCallback)(x, y, ffs)) {
+ break;
+ }
+ r.left = x;
+ --x;
+ }
+ if (r.right >= r.left && r.top <= r.bottom) {
+ floodFillProcessRect(ffs, &r);
+ }
+ if (x >= x1) goto skip;
+ x_start = x + 1;
+ if (x1 > x_start) {
+ assert(*fillLineEnd > *fillLineCur);
+ if (ffs->srcBox.top <= y - dy && ffs->srcBox.bottom >= y - dy) {
+ --x1;
+ floodFillAddLine(fillLineCur, y, x_start, x1, -dy);
+ }
+ }
+ x = x2;
+ while (x_start <= x_end) {
+ r.left = x;
+ r.top = y;
+ r.right = x - 1;
+ r.bottom = y;
+ while (ffs->srcBox.right >= x) {
+ if (!(*pixelCheckCallback)(x, y, ffs)) {
+ break;
+ }
+ r.right = x;
+ ++x;
+ }
+ if (r.right >= r.left && r.top <= r.bottom) {
+ floodFillProcessRect(ffs, &r);
+ }
+ assert(ffs->fillLineTableCur < ffs->fillLineTableEnd);
+ if (ffs->srcBox.top <= y + dy && ffs->srcBox.bottom >= y + dy) {
+ floodFillAddLine(&ffs->fillLineTableCur, y, x_start, x - 1, dy);
+ }
+ x_start = x_end + 1;
+ if (x > x_start) {
+ assert(ffs->fillLineTableCur < ffs->fillLineTableEnd);
+ if (ffs->srcBox.top <= y - dy && ffs->srcBox.bottom >= y - dy) {
+ floodFillAddLine(&ffs->fillLineTableCur, y, x_start, x - 1, -dy);
+ }
+ }
+skip:
+ ++x;
+ while (x <= x_end) {
+ if ((*pixelCheckCallback)(x, y, ffs)) {
+ break;
+ }
+ ++x;
+ }
+ x_start = x;
+ }
+ }
+}
+
+void floodFill(FloodFillParameters *ffp, ScummEngine_v90he *vm) {
+ uint8 *dst;
+ VirtScreen *vs = &vm->virtscr[kMainVirtScreen];
+ if (ffp->flags & 0x8000) {
+ dst = vs->getBackPixels(0, vs->topline);
+ } else {
+ dst = vs->getPixels(0, vs->topline);
+ }
+ uint8 color = ffp->flags & 0xFF;
+
+ Common::Rect r;
+ r.left = r.top = 12345;
+ r.right = r.bottom = -12345;
+
+ FloodFillState *ffs = new FloodFillState;
+ ffs->fillLineTableCount = vs->h * 2;
+ ffs->fillLineTable = new FloodFillLine[ffs->fillLineTableCount];
+ ffs->color2 = color;
+ ffs->dst = dst;
+ ffs->dst_w = vs->w;
+ ffs->dst_h = vs->h;
+ ffs->srcBox = ffp->box;
+ ffs->fillLineTableCur = &ffs->fillLineTable[0];
+ ffs->fillLineTableEnd = &ffs->fillLineTable[ffs->fillLineTableCount];
+
+ if (ffp->x < 0 || ffp->y < 0 || ffp->x >= vs->w || ffp->y >= vs->h) {
+ ffs->color1 = color;
+ } else {
+ ffs->color1 = *(dst + ffp->y * vs->w + ffp->x);
+ }
+
+ debug(5, "floodFill() x=%d y=%d color1=%d ffp->flags=0x%X", ffp->x, ffp->y, ffs->color1, ffp->flags);
+ if (ffs->color1 != color) {
+ floodFillProcess(ffp->x, ffp->y, ffs, floodFillPixelCheck);
+ r = ffs->dstBox;
+ }
+ r.debugPrint(5, "floodFill() dirty_rect");
+
+ delete[] ffs->fillLineTable;
+ delete ffs;
+
+ vm->VAR(119) = 1;
+
+ if (r.left <= r.right && r.top <= r.bottom) {
+ if (ffp->flags & 0x8000) {
+ vm->gdi.copyVirtScreenBuffers(r);
+ } else {
+ ++r.bottom;
+ vm->markRectAsDirty(kMainVirtScreen, r);
+ }
+ }
+}
+
+void Wiz::fillWizFlood(const WizParameters *params) {
+ if (params->processFlags & kWPFClipBox2) {
+ int px = params->box2.left;
+ int py = params->box2.top;
+ uint8 *dataPtr = _vm->getResourceAddress(rtImage, params->img.resNum);
+ if (dataPtr) {
+ int state = 0;
+ if (params->processFlags & kWPFNewState) {
+ state = params->img.state;
+ }
+ uint8 *wizh = _vm->findWrappedBlock(MKID('WIZH'), dataPtr, state, 0);
+ assert(wizh);
+ int c = READ_LE_UINT32(wizh + 0x0);
+ int w = READ_LE_UINT32(wizh + 0x4);
+ int h = READ_LE_UINT32(wizh + 0x8);
+ assert(c == 0);
+ Common::Rect imageRect(w, h);
+ if (params->processFlags & kWPFClipBox) {
+ if (!imageRect.intersects(params->box)) {
+ return;
+ }
+ imageRect.clip(params->box);
+ }
+ uint8 color = _vm->VAR(93);
+ if (params->processFlags & kWPFFillColor) {
+ color = params->fillColor;
+ }
+ if (imageRect.contains(px, py)) {
+ uint8 *wizd = _vm->findWrappedBlock(MKID('WIZD'), dataPtr, state, 0);
+ assert(wizd);
+
+ FloodFillState *ffs = new FloodFillState;
+ ffs->fillLineTableCount = h * 2;
+ ffs->fillLineTable = new FloodFillLine[ffs->fillLineTableCount];
+ ffs->color2 = color;
+ ffs->dst = wizd;
+ ffs->dst_w = w;
+ ffs->dst_h = h;
+ ffs->srcBox = imageRect;
+ ffs->fillLineTableCur = &ffs->fillLineTable[0];
+ ffs->fillLineTableEnd = &ffs->fillLineTable[ffs->fillLineTableCount];
+
+ if (px < 0 || py < 0 || px >= w || py >= h) {
+ ffs->color1 = color;
+ } else {
+ ffs->color1 = *(wizd + py * w + px);
+ }
+
+ debug(0, "floodFill() x=%d y=%d color1=%d", px, py, ffs->color1);
+
+ if (ffs->color1 != color) {
+ floodFillProcess(px, py, ffs, floodFillPixelCheck);
+ }
+
+ delete[] ffs->fillLineTable;
+ delete ffs;
+ }
+ }
+ }
+ _vm->res.setModified(rtImage, params->img.resNum);
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/he/floodfill_he.h b/engines/scumm/he/floodfill_he.h
new file mode 100644
index 0000000000..7d0fa7073f
--- /dev/null
+++ b/engines/scumm/he/floodfill_he.h
@@ -0,0 +1,67 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#if !defined(FLOODFILL_HE_H) && !defined(DISABLE_HE)
+#define FLOODFILL_HE_H
+
+#include "common/rect.h"
+
+namespace Scumm {
+
+struct FloodFillParameters {
+ Common::Rect box;
+ int32 x;
+ int32 y;
+ int32 flags;
+};
+
+struct FloodFillLine {
+ int y;
+ int x1;
+ int x2;
+ int inc;
+};
+
+struct FloodFillState {
+ FloodFillLine *fillLineTable;
+ FloodFillLine *fillLineTableEnd;
+ FloodFillLine *fillLineTableCur;
+ Common::Rect dstBox;
+ Common::Rect srcBox;
+ uint8 *dst;
+ int dst_w;
+ int dst_h;
+ int color1;
+ int color2;
+ int fillLineTableCount;
+};
+
+class ScummEngine_v90he;
+
+typedef bool (*FloodFillPixelCheckCallback)(int x, int y, const FloodFillState *ffs);
+
+void floodFill(FloodFillParameters *ffp, ScummEngine_v90he *vm);
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/he/intern_he.h b/engines/scumm/he/intern_he.h
new file mode 100644
index 0000000000..da14983e21
--- /dev/null
+++ b/engines/scumm/he/intern_he.h
@@ -0,0 +1,606 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef SCUMM_INTERN_HE_H
+#define SCUMM_INTERN_HE_H
+
+#include "scumm/intern.h"
+#ifndef DISABLE_HE
+#include "scumm/he/floodfill_he.h"
+#include "scumm/he/wiz_he.h"
+#endif
+
+namespace Scumm {
+
+#ifndef DISABLE_HE
+class ResExtractor;
+class LogicHE;
+class Sprite;
+#endif
+
+class ScummEngine_v60he : public ScummEngine_v6 {
+protected:
+ typedef void (ScummEngine_v60he::*OpcodeProcv60he)();
+ struct OpcodeEntryv60he {
+ OpcodeProcv60he proc;
+ const char *desc;
+ };
+
+ const OpcodeEntryv60he *_opcodesv60he;
+
+ Common::File _hFileTable[17];
+
+public:
+ ScummEngine_v60he(GameDetector *detector, OSystem *syst, const ScummGameSettings &gs, uint8 md5sum[16], int substResFileNameIndex) : ScummEngine_v6(detector, syst, gs, md5sum, substResFileNameIndex) {}
+
+ virtual void scummInit();
+
+protected:
+ virtual void setupOpcodes();
+ virtual void executeOpcode(byte i);
+ virtual const char *getOpcodeDesc(byte i);
+
+ virtual void saveOrLoad(Serializer *s);
+
+ void localizeArray(int slot, byte scriptSlot);
+ void redimArray(int arrayId, int newX, int newY, int d);
+ int readFileToArray(int slot, int32 size);
+ void writeFileFromArray(int slot, int resID);
+ int virtScreenSave(byte *dst, int x1, int y1, int x2, int y2);
+ void virtScreenLoad(int resIdx, int x1, int y1, int x2, int y2);
+ virtual void decodeParseString(int a, int b);
+ void swapObjects(int object1, int object2);
+
+ /* HE version 60 script opcodes */
+ void o60_setState();
+ void o60_roomOps();
+ void o60_actorOps();
+ void o60_kernelSetFunctions();
+ void o60_kernelGetFunctions();
+ void o60_openFile();
+ void o60_closeFile();
+ void o60_deleteFile();
+ void o60_readFile();
+ void o60_rename();
+ void o60_writeFile();
+ void o60_soundOps();
+ void o60_seekFilePos();
+ void o60_localizeArrayToScript();
+ void o60_redimArray();
+ void o60_readFilePos();
+};
+
+#ifndef DISABLE_HE
+class ScummEngine_v70he : public ScummEngine_v60he {
+ friend class ResExtractor;
+ friend class Wiz;
+
+protected:
+ typedef void (ScummEngine_v70he::*OpcodeProcv70he)();
+ struct OpcodeEntryv70he {
+ OpcodeProcv70he proc;
+ const char *desc;
+ };
+
+ const OpcodeEntryv70he *_opcodesv70he;
+
+ ResExtractor *_resExtractor;
+
+ byte *_heV7RoomOffsets;
+
+ int32 _heSndSoundId, _heSndOffset, _heSndChannel, _heSndFlags, _heSndSoundFreq;
+
+ bool _skipProcessActors;
+
+public:
+ ScummEngine_v70he(GameDetector *detector, OSystem *syst, const ScummGameSettings &gs, uint8 md5sum[16], int substResFileNameIndex);
+ ~ScummEngine_v70he();
+
+ Wiz *_wiz;
+
+ byte *heFindResourceData(uint32 tag, byte *ptr);
+ byte *heFindResource(uint32 tag, byte *ptr);
+ byte *findWrappedBlock(uint32 tag, byte *ptr, int state, bool flagError);
+
+protected:
+ virtual void setupOpcodes();
+ virtual void executeOpcode(byte i);
+ virtual const char *getOpcodeDesc(byte i);
+
+ virtual void setupScummVars();
+ virtual void initScummVars();
+
+ virtual void saveOrLoad(Serializer *s);
+
+ virtual void readRoomsOffsets();
+ virtual void readGlobalObjects();
+ virtual void readIndexBlock(uint32 blocktype, uint32 itemsize);
+
+ virtual int getActorFromPos(int x, int y);
+
+ int getStringCharWidth(byte chr);
+ virtual int setupStringArray(int size);
+ void appendSubstring(int dst, int src, int len2, int len);
+
+ virtual void setCursorFromImg(uint img, uint room, uint imgindex);
+
+ virtual void clearDrawQueues();
+
+ void remapHEPalette(const uint8 *src, uint8 *dst);
+
+ /* HE version 70 script opcodes */
+ void o70_startSound();
+ void o70_pickupObject();
+ void o70_getActorRoom();
+ void o70_resourceRoutines();
+ void o70_systemOps();
+ void o70_kernelSetFunctions();
+ void o70_seekFilePos();
+ void o70_copyString();
+ void o70_getStringWidth();
+ void o70_getStringLen();
+ void o70_appendString();
+ void o70_concatString();
+ void o70_compareString();
+ void o70_isResourceLoaded();
+ void o70_readINI();
+ void o70_writeINI();
+ void o70_getStringLenForWidth();
+ void o70_getCharIndexInString();
+ void o70_setFilePath();
+ void o70_setSystemMessage();
+ void o70_polygonOps();
+ void o70_polygonHit();
+
+ byte VAR_NUM_SOUND_CHANNELS;
+ byte VAR_WIZ_TCOLOR;
+};
+
+class ScummEngine_v71he : public ScummEngine_v70he {
+public:
+ ScummEngine_v71he(GameDetector *detector, OSystem *syst, const ScummGameSettings &gs, uint8 md5sum[16], int substResFileNameIndex);
+
+protected:
+ virtual void saveOrLoad(Serializer *s);
+
+ virtual void redrawBGAreas();
+
+ virtual void processActors();
+ void preProcessAuxQueue();
+ void postProcessAuxQueue();
+
+public:
+ /* Actor AuxQueue stuff (HE) */
+ AuxBlock _auxBlocks[16];
+ uint16 _auxBlocksNum;
+ AuxEntry _auxEntries[16];
+ uint16 _auxEntriesNum;
+
+ void queueAuxBlock(Actor *a);
+ void queueAuxEntry(int actorNum, int subIndex);
+};
+
+class ScummEngine_v72he : public ScummEngine_v71he {
+protected:
+ typedef void (ScummEngine_v72he::*OpcodeProcV72he)();
+ struct OpcodeEntryV72he {
+ OpcodeProcV72he proc;
+ const char *desc;
+ };
+
+#if !defined(__GNUC__)
+ #pragma START_PACK_STRUCTS
+#endif
+
+ struct ArrayHeader {
+ int32 type; //0
+ int32 dim1start; //4
+ int32 dim1end; //8
+ int32 dim2start; //0C
+ int32 dim2end; //10
+ byte data[1]; //14
+ } GCC_PACK;
+
+#if !defined(__GNUC__)
+ #pragma END_PACK_STRUCTS
+#endif
+
+ const OpcodeEntryV72he *_opcodesV72he;
+
+ int _stringLength;
+ byte _stringBuffer[4096];
+
+ WizParameters _wizParams;
+
+public:
+ ScummEngine_v72he(GameDetector *detector, OSystem *syst, const ScummGameSettings &gs, uint8 md5sum[16], int substResFileNameIndex);
+
+ virtual void scummInit();
+
+protected:
+ virtual void setupOpcodes();
+ virtual void executeOpcode(byte i);
+ virtual const char *getOpcodeDesc(byte i);
+
+ virtual void setupScummVars();
+ virtual void initScummVars();
+ virtual void readArrayFromIndexFile();
+
+ virtual byte *getStringAddress(int i);
+ virtual void readMAXS(int blockSize);
+
+ virtual void redrawBGAreas();
+
+ ArrayHeader *defineArray(int array, int type, int dim2start, int dim2end, int dim1start, int dim1end);
+ virtual int readArray(int array, int idx2, int idx1);
+ virtual void writeArray(int array, int idx2, int idx1, int value);
+ void redimArray(int arrayId, int newDim2start, int newDim2end,
+ int newDim1start, int newDim1end, int type);
+ void checkArrayLimits(int array, int dim2start, int dim2end, int dim1start, int dim1end);
+ void copyArray(int array1, int a1_dim2start, int a1_dim2end, int a1_dim1start, int a1_dim1end,
+ int array2, int a2_dim2start, int a2_dim2end, int a2_dim1start, int a2_dim1end);
+ void copyArrayHelper(ArrayHeader *ah, int idx2, int idx1, int len1, byte **data, int *size, int *num);
+ virtual int setupStringArray(int size);
+ int readFileToArray(int slot, int32 size);
+ void writeFileFromArray(int slot, int32 resID);
+
+ virtual void decodeParseString(int a, int b);
+ void decodeScriptString(byte *dst, bool scriptString = false);
+ void copyScriptString(byte *dst, int dstSize);
+ int convertFilePath(byte *dst, bool setFilePath = false);
+
+ int findObject(int x, int y, int num, int *args);
+ int getSoundResourceSize(int id);
+
+ virtual bool handleNextCharsetCode(Actor *a, int *c);
+
+ /* HE version 72 script opcodes */
+ void o72_pushDWord();
+ void o72_getScriptString();
+ void o72_isAnyOf();
+ void o72_resetCutscene();
+ void o72_findObjectWithClassOf();
+ void o72_getObjectImageX();
+ void o72_getObjectImageY();
+ void o72_captureWizImage();
+ void o72_getTimer();
+ void o72_setTimer();
+ void o72_getSoundPosition();
+ void o72_startScript();
+ void o72_startObject();
+ void o72_drawObject();
+ void o72_printWizImage();
+ void o72_getArrayDimSize();
+ void o72_getNumFreeArrays();
+ void o72_roomOps();
+ void o72_actorOps();
+ void o72_verbOps();
+ void o72_findObject();
+ void o72_arrayOps();
+ void o72_systemOps();
+ void o72_talkActor();
+ void o72_talkEgo();
+ void o72_dimArray();
+ void o72_dim2dimArray();
+ void o72_traceStatus();
+ void o72_debugInput();
+ void o72_drawWizImage();
+ void o72_kernelGetFunctions();
+ void o72_jumpToScript();
+ void o72_openFile();
+ void o72_readFile();
+ void o72_writeFile();
+ void o72_findAllObjects();
+ void o72_deleteFile();
+ void o72_rename();
+ void o72_getPixel();
+ void o72_pickVarRandom();
+ void o72_redimArray();
+ void o72_readINI();
+ void o72_writeINI();
+ void o72_getResourceSize();
+ void o72_setFilePath();
+ void o72_setSystemMessage();
+
+ byte VAR_NUM_ROOMS;
+ byte VAR_NUM_SCRIPTS;
+ byte VAR_NUM_SOUNDS;
+ byte VAR_NUM_COSTUMES;
+ byte VAR_NUM_IMAGES;
+ byte VAR_NUM_CHARSETS;
+
+ byte VAR_POLYGONS_ONLY;
+};
+
+class ScummEngine_v80he : public ScummEngine_v72he {
+protected:
+ typedef void (ScummEngine_v80he::*OpcodeProcV80he)();
+ struct OpcodeEntryV80he {
+ OpcodeProcV80he proc;
+ const char *desc;
+ };
+
+ const OpcodeEntryV80he *_opcodesV80he;
+
+ int32 _heSndResId, _curSndId, _sndPtrOffs, _sndTmrOffs;
+
+public:
+ ScummEngine_v80he(GameDetector *detector, OSystem *syst, const ScummGameSettings &gs, uint8 md5sum[16], int substResFileNameIndex);
+
+protected:
+ virtual void setupOpcodes();
+ virtual void executeOpcode(byte i);
+ virtual const char *getOpcodeDesc(byte i);
+
+ virtual void setupScummVars();
+ virtual void initScummVars();
+
+ virtual void initCharset(int charset);
+
+ virtual void clearDrawQueues();
+
+ void createSound(int snd1id, int snd2id);
+
+ void drawLine(int x1, int y1, int x, int unk1, int unk2, int type, int id);
+ void drawPixel(int x, int y, int flags);
+
+ /* HE version 80 script opcodes */
+ void o80_createSound();
+ void o80_getFileSize();
+ void o80_stringToInt();
+ void o80_getSoundVar();
+ void o80_localizeArrayToRoom();
+ void o80_sourceDebug();
+ void o80_readConfigFile();
+ void o80_writeConfigFile();
+ void o80_cursorCommand();
+ void o80_setState();
+ void o80_drawWizPolygon();
+ void o80_drawLine();
+ void o80_pickVarRandom();
+
+ byte VAR_PLATFORM;
+ byte VAR_WINDOWS_VERSION;
+ byte VAR_CURRENT_CHARSET;
+ byte VAR_COLOR_DEPTH;
+};
+
+class ScummEngine_v90he : public ScummEngine_v80he {
+ friend class LogicHE;
+ friend class Sprite;
+
+protected:
+ typedef void (ScummEngine_v90he::*OpcodeProcV90he)();
+ struct OpcodeEntryV90he {
+ OpcodeProcV90he proc;
+ const char *desc;
+ };
+
+ const OpcodeEntryV90he *_opcodesV90he;
+
+ FloodFillParameters _floodFillParams;
+
+ struct VideoParameters {
+ byte filename[260];
+ int32 status;
+ int32 flags;
+ int32 unk2;
+ int32 wizResNum;
+ };
+
+ VideoParameters _videoParams;
+
+ int32 _heObject, _heObjectNum;
+ int32 _hePaletteNum;
+
+ int32 _curMaxSpriteId;
+ int32 _curSpriteId;
+ int32 _curSpriteGroupId;
+
+public:
+ ScummEngine_v90he(GameDetector *detector, OSystem *syst, const ScummGameSettings &gs, uint8 md5sum[16], int substResFileNameIndex);
+ ~ScummEngine_v90he();
+
+ virtual void scummInit();
+
+ LogicHE *_logicHE;
+ Sprite *_sprite;
+
+protected:
+ virtual void allocateArrays();
+ virtual void setupOpcodes();
+ virtual void executeOpcode(byte i);
+ virtual const char *getOpcodeDesc(byte i);
+
+ virtual void setupScummVars();
+ virtual void initScummVars();
+
+ virtual void saveOrLoad(Serializer *s);
+
+ virtual void readMAXS(int blockSize);
+
+ virtual void processActors();
+
+ int computeWizHistogram(int resnum, int state, int x, int y, int w, int h);
+ void getArrayDim(int array, int *dim2start, int *dim2end, int *dim1start, int *dim1end);
+ void sortArray(int array, int dim2start, int dim2end, int dim1start, int dim1end, int sortOrder);
+
+public:
+ int getGroupSpriteArray(int spriteGroupId);
+
+protected:
+ uint8 *getHEPaletteIndex(int palSlot);
+ int getHEPaletteColor(int palSlot, int color);
+ int getHEPaletteSimilarColor(int palSlot, int red, int green, int start, int end);
+ int getHEPaletteColorComponent(int palSlot, int color, int component);
+ void setHEPaletteColor(int palSlot, uint8 color, uint8 r, uint8 g, uint8 b);
+ void setHEPaletteFromPtr(int palSlot, const uint8 *palData);
+ void setHEPaletteFromCostume(int palSlot, int resId);
+ void setHEPaletteFromImage(int palSlot, int resId, int state);
+ void setHEPaletteFromRoom(int palSlot, int resId, int state);
+ void restoreHEPalette(int palSlot);
+ void copyHEPalette(int dstPalSlot, int srcPalSlot);
+ void copyHEPaletteColor(int palSlot, uint8 dstColor, uint8 srcColor);
+
+
+ void setDefaultCursor();
+
+protected:
+ /* HE version 90 script opcodes */
+ void o90_dup_n();
+ void o90_min();
+ void o90_max();
+ void o90_sin();
+ void o90_cos();
+ void o90_sqrt();
+ void o90_atan2();
+ void o90_getSegmentAngle();
+ void o90_getActorData();
+ void o90_startScriptUnk();
+ void o90_jumpToScriptUnk();
+ void o90_videoOps();
+ void o90_getVideoData();
+ void o90_wizImageOps();
+ void o90_getDistanceBetweenPoints();
+ void o90_getSpriteInfo();
+ void o90_setSpriteInfo();
+ void o90_getSpriteGroupInfo();
+ void o90_setSpriteGroupInfo();
+ void o90_getWizData();
+ void o90_floodFill();
+ void o90_mod();
+ void o90_shl();
+ void o90_shr();
+ void o90_xor();
+ void o90_findAllObjectsWithClassOf();
+ void o90_getPolygonOverlap();
+ void o90_cond();
+ void o90_dim2dim2Array();
+ void o90_redim2dimArray();
+ void o90_getLinesIntersectionPoint();
+ void o90_sortArray();
+ void o90_getObjectData();
+ void o90_getPaletteData();
+ void o90_paletteOps();
+ void o90_fontUnk();
+ void o90_getActorAnimProgress();
+ void o90_kernelGetFunctions();
+ void o90_kernelSetFunctions();
+
+ byte VAR_NUM_SPRITE_GROUPS;
+ byte VAR_NUM_SPRITES;
+ byte VAR_NUM_PALETTES;
+ byte VAR_NUM_UNK;
+
+ byte VAR_U32_VERSION;
+ byte VAR_U32_ARRAY_UNK;
+};
+
+class ScummEngine_v99he : public ScummEngine_v90he {
+public:
+ ScummEngine_v99he(GameDetector *detector, OSystem *syst, const ScummGameSettings &gs, uint8 md5sum[16], int substResFileNameIndex) : ScummEngine_v90he(detector, syst, gs, md5sum, substResFileNameIndex) {}
+
+ virtual void scummInit();
+
+protected:
+ virtual void initScummVars();
+
+ virtual void readMAXS(int blockSize);
+
+ virtual void saveOrLoad(Serializer *s);
+
+ virtual void copyPalColor(int dst, int src);
+ virtual void darkenPalette(int redScale, int greenScale, int blueScale, int startColor, int endColor);
+ virtual void setPaletteFromPtr(const byte *ptr, int numcolor = -1);
+ virtual void setPalColor(int index, int r, int g, int b);
+ virtual void updatePalette();
+};
+
+class ScummEngine_v100he : public ScummEngine_v99he {
+protected:
+ typedef void (ScummEngine_v100he::*OpcodeProcV100he)();
+ struct OpcodeEntryV100he {
+ OpcodeProcV100he proc;
+ const char *desc;
+ };
+
+ int32 _heResId, _heResType;
+
+ const OpcodeEntryV100he *_opcodesV100he;
+
+public:
+ ScummEngine_v100he(GameDetector *detector, OSystem *syst, const ScummGameSettings &gs, uint8 md5sum[16], int substResFileNameIndex) : ScummEngine_v99he(detector, syst, gs, md5sum, substResFileNameIndex) {}
+
+protected:
+ virtual void setupOpcodes();
+ virtual void executeOpcode(byte i);
+ virtual const char *getOpcodeDesc(byte i);
+
+ virtual void saveOrLoad(Serializer *s);
+
+ virtual void decodeParseString(int a, int b);
+
+ /* HE version 100 script opcodes */
+ void o100_actorOps();
+ void o100_arrayOps();
+ void o100_dim2dimArray();
+ void o100_redim2dimArray();
+ void o100_dimArray();
+ void o100_drawLine();
+ void o100_drawObject();
+ void o100_floodFill();
+ void o100_setSpriteGroupInfo();
+ void o100_resourceRoutines();
+ void o100_wizImageOps();
+ void o100_jumpToScript();
+ void o100_createSound();
+ void o100_dim2dim2Array();
+ void o100_paletteOps();
+ void o100_jumpToScriptUnk();
+ void o100_startScriptUnk();
+ void o100_redimArray();
+ void o100_roomOps();
+ void o100_setSystemMessage();
+ void o100_startSound();
+ void o100_setSpriteInfo();
+ void o100_startScript();
+ void o100_systemOps();
+ void o100_cursorCommand();
+ void o100_videoOps();
+ void o100_wait();
+ void o100_writeFile();
+ void o100_isResourceLoaded();
+ void o100_getResourceSize();
+ void o100_getSpriteGroupInfo();
+ void o100_getPaletteData();
+ void o100_readFile();
+ void o100_getSpriteInfo();
+ void o100_getWizData();
+ void o100_getVideoData();
+};
+#endif
+
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/he/logic_he.cpp b/engines/scumm/he/logic_he.cpp
new file mode 100644
index 0000000000..eb9e4edf07
--- /dev/null
+++ b/engines/scumm/he/logic_he.cpp
@@ -0,0 +1,836 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "scumm/he/intern_he.h"
+#include "scumm/he/logic_he.h"
+
+namespace Scumm {
+
+LogicHE::LogicHE(ScummEngine_v90he *vm) : _vm(vm) {
+ // Originally it used 0x930 and stored both floats and doubles inside
+ _userData = (float *)calloc(550, sizeof(float));
+ _userDataD = (double *)calloc(30, sizeof(double));
+}
+
+LogicHE::~LogicHE() {
+ free(_userData);
+ free(_userDataD);
+}
+
+void LogicHE::writeScummVar(int var, int32 value) {
+ _vm->writeVar(var, value);
+}
+
+static int32 scumm_round(double arg) {
+ return (int32)(arg + 0.5);
+}
+
+int LogicHE::versionID() {
+ return 1;
+}
+
+int LogicHE::getFromArray(int arg0, int idx2, int idx1) {
+ _vm->VAR(_vm->VAR_U32_ARRAY_UNK) = arg0;
+ return _vm->readArray(116, idx2, idx1);
+}
+
+void LogicHE::putInArray(int arg0, int idx2, int idx1, int val) {
+ _vm->VAR(_vm->VAR_U32_ARRAY_UNK) = arg0;
+ _vm->writeArray(116, idx2, idx1, val);
+}
+
+int32 LogicHE::dispatch(int op, int numArgs, int32 *args) {
+#if 1
+ char tmp[32], str[256];
+
+ if (numArgs > 0)
+ snprintf(tmp, 32, "%d", args[0]);
+ else
+ *tmp = 0;
+
+ snprintf(str, 256, "LogicHE::dispatch(%d, %d, [%s", op, numArgs, tmp);
+
+ for (int i = 1; i < numArgs; i++) {
+ snprintf(tmp, 32, ", %d", args[i]);
+ strncat(str, tmp, 256);
+ }
+ strncat(str, "])", 256);
+
+ debug(0, str);
+#else
+ // Used for parallel trace utility
+ for (int i = 0; i < numArgs; i++)
+ debug(0, "args[%d] = %d;", i, args[i]);
+
+ debug(0, "dispatch(%d, %d, args);", op, numArgs);
+
+#endif
+
+ return 1;
+}
+
+/***********************
+ * Putt-Putt Joins the Race
+ *
+ */
+
+int LogicHErace::versionID() {
+ return 1;
+}
+
+int32 LogicHErace::dispatch(int op, int numArgs, int32 *args) {
+ int32 res;
+
+ switch (op) {
+ case 1003:
+ res = op_1003(args);
+ break;
+
+ case 1004:
+ res = op_1004(args);
+ break;
+
+ case 1100:
+ res = op_1100(args);
+ break;
+
+ case 1101:
+ res = op_1101(args);
+ break;
+
+ case 1102:
+ res = op_1102(args);
+ break;
+
+ case 1103:
+ res = op_1103(args);
+ break;
+
+ case 1110:
+ res = op_1110();
+ break;
+
+ case 1120:
+ res = op_1120(args);
+ break;
+
+ case 1130:
+ res = op_1130(args);
+ break;
+
+ case 1140:
+ res = op_1140(args);
+ break;
+
+ default:
+ res = 0;
+ break;
+
+ }
+
+ return res;
+}
+
+#define RAD2DEG (180 / PI)
+#define DEG2RAD (PI / 180)
+
+int32 LogicHErace::op_1003(int32 *args) {
+ int value = args[2] ? args[2] : 1;
+
+ writeScummVar(108, (int32)(atan2((double)args[0], (double)args[1]) * RAD2DEG * value));
+
+ return 1;
+}
+
+int32 LogicHErace::op_1004(int32 *args) {
+ int value = args[1] ? args[1] : 1;
+
+ writeScummVar(108, (int32)(sqrt((float)args[0]) * value));
+
+ return 1;
+}
+
+int32 LogicHErace::op_1100(int32 *args) {
+ _userData[516] = (float)args[0] / args[10];
+ _userData[517] = (float)args[1] / args[10];
+ _userData[518] = (float)args[2] / args[10];
+ _userData[519] = (float)args[3] / args[10];
+ _userData[520] = (float)args[4] / args[10];
+
+ op_sub1(_userData[520]);
+
+ _userData[521] = (float)args[5] / args[10];
+
+ op_sub2(_userData[521]);
+
+ _userData[532] = (float)args[10];
+
+ _userData[524] = (float)args[8];
+ _userData[525] = (float)args[9];
+ _userData[522] = (float)args[6] / args[10];
+ _userData[523] = (float)args[7] / args[10];
+ _userData[526] = (float)args[6] / args[8] / args[10];
+ _userData[527] = (float)args[7] / args[9] / args[10];
+
+ writeScummVar(108, (int32)((float)args[6] / args[8] * args[10]));
+
+ writeScummVar(109, (int32)((float)args[7] / args[9] * args[10]));
+
+ _userData[528] = (float)(_userData[519] - _userData[523] * 0.5);
+ _userData[529] = (float)(_userData[519] + _userData[523] * 0.5);
+
+ writeScummVar(110, (int32)(_userData[528] * args[10]));
+ writeScummVar(111, (int32)(_userData[529] * args[10]));
+
+ _userData[530] = (float)(_userData[517] / tan(_userData[529] * DEG2RAD));
+ _userData[531] = (float)(_userData[517] / tan(_userData[528] * DEG2RAD));
+
+ writeScummVar(112, (int32)(_userData[530] * args[10]));
+ writeScummVar(113, (int32)(_userData[531] * args[10]));
+
+ return 1;
+}
+
+int32 LogicHErace::op_1101(int32 *args) {
+ int32 retval;
+ float temp;
+
+ temp = args[0] / _userData[532];
+
+ if (_userData[519] == temp) {
+ retval = (int32)temp;
+ } else {
+ _userData[519] = temp;
+ op_sub3(temp);
+ retval = 1;
+ }
+
+ temp = args[1] / _userData[532];
+
+ if (_userData[520] != temp) {
+ _userData[520] = temp;
+ op_sub1(temp);
+ retval = 1;
+ }
+
+ temp = args[2] / _userData[532];
+
+ if (_userData[521] != temp) {
+ _userData[521] = temp;
+ op_sub2(temp);
+ retval = 1;
+ }
+
+ return retval;
+}
+
+int32 LogicHErace::op_1102(int32 *args) {
+ int32 retval;
+ float temp;
+
+ temp = args[0] / _userData[532];
+ if (_userData[516] != temp) {
+ _userData[516] = temp;
+ retval = 1;
+ } else {
+ retval = (int32)_userData[532];
+ }
+
+ temp = args[1] / _userData[532];
+ if (_userData[517] != temp) {
+ _userData[517] = temp;
+ retval = 1;
+ }
+
+ temp = args[2] / _userData[532];
+ if (_userData[518] != temp) {
+ _userData[518] = temp;
+ retval = 1;
+ }
+
+ return retval;
+}
+
+int32 LogicHErace::op_1103(int32 *args) {
+ double angle = args[0] / args[1] * DEG2RAD;
+
+ writeScummVar(108, (int32)(sin(angle) * args[2]));
+ writeScummVar(109, (int32)(cos(angle) * args[2]));
+
+ return 1;
+}
+
+int32 LogicHErace::op_1110() {
+ writeScummVar(108, (int32)(_userData[526] * _userData[532] * _userData[532]));
+ writeScummVar(109, (int32)(_userData[527] * _userData[532] * _userData[532]));
+ writeScummVar(110, (int32)(_userData[532]));
+
+ return 1;
+}
+
+int32 LogicHErace::op_1120(int32 *args) {
+ double a0, a1, a2, expr;
+ double res1, res2;
+
+ a0 = args[0] / _userData[532] - _userData[516];
+ a1 = args[1] / _userData[532] - _userData[517];
+ a2 = args[2] / _userData[532] - _userData[518];
+
+ expr = a2 * _userDataD[17] + a1 * _userDataD[14] + a0 * _userDataD[11];
+
+ res1 = (atan2(a2 * _userDataD[15] + a1 * _userDataD[12] + a0 * _userDataD[9], expr) * RAD2DEG)
+ / _userData[526];
+ res2 = (atan2(a2 * _userDataD[16] + a1 * _userDataD[13] + a0 * _userDataD[10], expr) * RAD2DEG
+ - _userData[528]) / _userData[527];
+
+ writeScummVar(108, (int32)res1);
+ writeScummVar(109, (int32)res2);
+
+ return 1;
+}
+
+int32 LogicHErace::op_1130(int32 *args) {
+ double cs = cos(args[0] / _userData[532] * DEG2RAD);
+ double sn = sin(args[0] / _userData[532] * DEG2RAD);
+
+ writeScummVar(108, (int32)(cs * args[1] + sn * args[2]));
+
+ writeScummVar(109, (int32)(cs * args[2] - sn * args[1]));
+
+ return 1;
+}
+
+int32 LogicHErace::op_1140(int32 *args) {
+ double arg2 = -args[2] * args[2];
+ double arg3 = -args[3] * args[3];
+ double sq = sqrt(arg2 + arg3);
+ double res;
+
+ arg2 = arg2 / sq;
+ arg3 = arg3 / sq;
+
+ res = (args[0] - 2 * (arg2 * args[0] + arg3 * args[1]) * arg2) * 0.86956525;
+
+ writeScummVar(108, (int32)res);
+
+ res = args[1] - 2 * (arg2 * args[0] + arg3 * args[1]) * arg3;
+
+ if (-args[3] * args[3] >= 0)
+ res *= 0.83333331f;
+
+ writeScummVar(109, (int32)res);
+
+ return 1;
+}
+
+void LogicHErace::op_sub1(float arg) {
+ _userDataD[10] = _userDataD[12] = _userDataD[14] = _userDataD[16] = 0;
+ _userDataD[13] = 1;
+
+ _userDataD[9] = cos(arg * DEG2RAD);
+ _userDataD[15] = sin(arg * DEG2RAD);
+ _userDataD[11] = -_userDataD[15];
+ _userDataD[17] = _userDataD[9];
+}
+
+void LogicHErace::op_sub2(float arg) {
+ _userDataD[20] = _userDataD[21] = _userDataD[24] = _userDataD[25] = 0;
+ _userDataD[26] = 1;
+
+ _userDataD[19] = sin(arg * DEG2RAD);
+ _userDataD[18] = cos(arg * DEG2RAD);
+ _userDataD[21] = -_userDataD[19];
+ _userDataD[22] = _userDataD[18];
+}
+
+void LogicHErace::op_sub3(float arg) {
+ _userDataD[1] = _userDataD[2] = _userDataD[3] = _userDataD[6] = 0;
+ _userDataD[0] = 1;
+
+ _userDataD[4] = cos(arg * DEG2RAD);
+ _userDataD[5] = sin(arg * DEG2RAD);
+ _userDataD[7] = -_userDataD[5];
+ _userDataD[8] = _userDataD[4];
+}
+
+/***********************
+ * Freddi Fish's One-Stop Fun Shop
+ * Pajama Sam's One-Stop Fun Shop
+ * Putt-Putt's One-Stop Fun Shop
+ *
+ */
+
+int LogicHEfunshop::versionID() {
+ return 1;
+}
+
+int32 LogicHEfunshop::dispatch(int op, int numArgs, int32 *args) {
+ switch (op) {
+ case 1004:
+ op_1004(args);
+ break;
+
+ case 1005:
+ op_1005(args);
+ break;
+
+ default:
+ break;
+
+ }
+
+ return 0;
+}
+
+void LogicHEfunshop::op_1004(int32 *args) {
+ double data[8], at, sq;
+ int32 x, y;
+ int i=0;
+
+ for (i = 0; i <= 6; i += 2) {
+ data[i] = getFromArray(args[0], 0, 519 + i);
+ data[i + 1] = getFromArray(args[0], 0, 519 + i + 1);
+ }
+ int s = checkShape((int32)data[0], (int32)data[1], (int32)data[4], (int32)data[5],
+ (int32)data[2], (int32)data[3], (int32)data[6], (int32)data[7], &x, &y);
+
+ if (s != 1) {
+ error("LogicHEfunshop::op_1004: Your shape has defied the laws of physics\n");
+ return;
+ }
+
+ for (i = 0; i <= 6; i += 2) {
+ data[i] -= (double)x;
+ data[i + 1] -= (double)y;
+ }
+
+ double a1 = (double)args[1] * DEG2RAD;
+
+ for (i = 0; i <= 6; i += 2) {
+ at = atan2(data[i + 1], data[i]);
+ sq = sqrt(data[i + 1] * data[i + 1] + data[i] * data[i]);
+
+ if (at <= 0)
+ at += 2 * PI;
+
+ data[i] = cos(at + a1) * sq;
+ data[i + 1] = sin(at + a1) * sq;
+ }
+
+ int minx = 2;
+ int miny = 3;
+
+ for (i = 0; i <= 6; i += 2) {
+ if (data[i] < data[minx])
+ minx = i;
+ if (data[i + 1] < data[miny])
+ miny = i + 1;
+ }
+
+ for (i = 0; i <= 6; i += 2) {
+ data[i] -= data[minx];
+ data[i + 1] -= data[miny];
+
+ putInArray(args[0], 0, 519 + i, scumm_round(data[i]));
+ putInArray(args[0], 0, 519 + i + 1, scumm_round(data[i + 1]));
+ }
+}
+
+void LogicHEfunshop::op_1005(int32 *args) {
+ double data[8];
+ double args1, args2;
+ int i=0;
+ for (i = 520; i <= 526; i += 2) {
+ data[i - 520] = getFromArray(args[0], 0, i - 1);
+ data[i - 520 + 1] = getFromArray(args[0], 0, i);
+ }
+
+ args1 = args[1] * 0.01 + 1;
+ args2 = args[2] * 0.01 + 1;
+
+ for (i = 0; i < 4; i++) {
+ data[2 * i] *= args1;
+ data[2 * i + 1] *= args2;
+ }
+
+ for (i = 520; i <= 526; i += 2) {
+ putInArray(args[0], 0, i - 1, scumm_round(data[i - 520]));
+ putInArray(args[0], 0, i, scumm_round(data[i - 520 + 1]));
+ }
+}
+
+int LogicHEfunshop::checkShape(int32 data0, int32 data1, int32 data4, int32 data5, int32 data2, int32 data3, int32 data6, int32 data7, int32 *x, int32 *y) {
+ int32 diff5_1, diff0_4, diff7_3, diff2_6;
+ int32 diff1, diff2;
+ int32 delta, delta2;
+ int32 sum1, sum2;
+
+ diff0_4 = data0 - data4;
+ diff5_1 = data5 - data1;
+ diff1 = data1 * data4 - data0 * data5;
+ sum1 = diff0_4 * data3 + diff1 + diff5_1 * data2;
+ sum2 = diff0_4 * data7 + diff1 + diff5_1 * data6;
+
+ if (sum1 != 0 && sum2 != 0) {
+ sum2 ^= sum1;
+
+ if (sum2 >= 0)
+ return 0;
+ }
+
+ diff2_6 = data2 - data6;
+ diff7_3 = data7 - data3;
+ diff2 = data3 * data6 - data2 * data7;
+ sum1 = diff2_6 * data1 + diff2 + diff7_3 * data0;
+ sum2 = diff2_6 * data5 + diff2 + diff7_3 * data4;;
+
+ if (sum1 != 0 && sum2 != 0) {
+ sum2 ^= sum1;
+
+ if (sum2 >= 0)
+ return 0;
+ }
+
+ delta = diff2_6 * diff5_1 - diff0_4 * diff7_3;
+
+ if (delta == 0) {
+ return 2;
+ }
+
+ if (delta < 0) {
+ data7 = -((delta + 1) >> 1);
+ } else {
+ data7 = delta >> 1;
+ }
+
+ delta2 = diff2 * diff0_4 - diff1 * diff2_6;
+
+ if (delta2 < 0) {
+ delta2 -= data7;
+ } else {
+ delta2 += data7;
+ }
+
+ *x = delta2 / delta;
+
+ delta2 = diff1 * diff7_3 - diff2 * diff5_1;
+
+ if (delta2 < 0) {
+ delta2 -= data7;
+ } else {
+ delta2 += data7;
+ }
+
+ *y = delta2 / delta;
+
+ return 1;
+}
+
+/***********************
+ * Backyard Football
+ * Backyard Football Demo
+ *
+ */
+
+int LogicHEfootball::versionID() {
+ return 1;
+}
+
+int32 LogicHEfootball::dispatch(int op, int numArgs, int32 *args) {
+ int res = 0;
+
+ switch (op) {
+ case 1004:
+ res = op_1004(args);
+ break;
+
+ case 1006:
+ res = op_1006(args);
+ break;
+
+ case 1007:
+ res = op_1007(args);
+ break;
+
+ case 1010:
+ res = op_1010(args);
+ break;
+
+ case 1022:
+ res = op_1022(args);
+ break;
+
+ case 1023:
+ res = op_1023(args);
+ break;
+
+ case 1024:
+ res = op_1024(args);
+ break;
+
+ case 8221968:
+ // Someone had a fun and used his birthday as opcode number
+ res = getFromArray(args[0], args[1], args[2]);
+ break;
+
+ case 1492: case 1493: case 1494: case 1495: case 1496:
+ case 1497: case 1498: case 1499: case 1500: case 1501:
+ case 1502: case 1503: case 1504: case 1505: case 1506:
+ case 1507: case 1508: case 1509: case 1510: case 1511:
+ case 1512: case 1513: case 1514: case 1555:
+ // DirectPlay-related
+ // 1513: initialize
+ // 1555: set fake lag
+ break;
+
+ case 2200: case 2201: case 2202: case 2203: case 2204:
+ case 2205: case 2206: case 2207: case 2208: case 2209:
+ case 2210: case 2211: case 2212: case 2213: case 2214:
+ case 2215: case 2216: case 2217: case 2218: case 2219:
+ case 2220: case 2221: case 2222: case 2223: case 2224:
+ case 2225: case 2226: case 2227: case 2228:
+ // Boneyards-related
+ break;
+
+ case 3000: case 3001: case 3002: case 3003: case 3004:
+ // Internet-related
+ // 3000: check for updates
+ // 3001: check network status
+ // 3002: autoupdate
+ // 3003: close connection
+ break;
+
+ default:
+ LogicHE::dispatch(op, numArgs, args);
+ error("Tell sev how to reproduce it");
+ }
+
+ return res;
+}
+
+int LogicHEfootball::op_1004(int32 *args) {
+ double res, a2, a4, a5;
+
+ a5 = ((double)args[4] - (double)args[1]) / ((double)args[5] - (double)args[2]);
+ a4 = ((double)args[3] - (double)args[0]) / ((double)args[5] - (double)args[2]);
+ a2 = (double)args[2] - (double)args[0] * a4 - args[1] * a5;
+
+ res = (double)args[6] * a4 + (double)args[7] * a5 + a2;
+ writeScummVar(108, (int32)res);
+
+ writeScummVar(109, (int32)a2);
+ writeScummVar(110, (int32)a5);
+ writeScummVar(111, (int32)a4);
+
+ return 1;
+}
+
+int LogicHEfootball::op_1006(int32 *args) {
+ double res;
+
+ res = (1.0 - args[1] * 2.9411764e-4 * 5.3050399e-2) * args[0] * 1.2360656e-1 +
+ args[1] * 1.1764706e-2 + 46;
+ writeScummVar(108, (int32)res);
+
+ res = 640.0 - args[2] * 1.2360656e-1 - args[1] * 1.1588235e-1 - 26;
+ writeScummVar(109, (int32)res);
+
+ return 1;
+}
+
+int LogicHEfootball::op_1007(int32 *args) {
+ double res, temp;
+
+ temp = (double)args[1] * 0.32;
+
+ if (temp > 304.0)
+ res = -args[2] * 0.142;
+ else
+ res = args[2] * 0.142;
+
+ res += temp;
+
+ writeScummVar(108, (int32)res);
+
+ res = (1000.0 - args[2]) * 0.48;
+
+ writeScummVar(109, (int32)res);
+
+ return 1;
+}
+
+int LogicHEfootball::op_1010(int32 *args) {
+ double a1 = (640.0 - (double)args[1] - 26.0) * 8.6294413;
+ double res;
+
+ res = ((double)args[0] - 46 - a1 * 1.1764706e-2) /
+ ((1.0 - a1 * 2.9411764e-4 * 5.3050399e-2) * 1.2360656e-1);
+ writeScummVar(108, (int32)res);
+
+ writeScummVar(109, (int32)a1);
+
+ return 1;
+}
+
+int LogicHEfootball::op_1022(int32 *args) {
+ double res;
+ double var10 = args[4] - args[1];
+ double var8 = args[5] - args[2];
+ double var6 = args[3] - args[0];
+
+ res = sqrt(var8 * var8 + var6 * var6 + var10 * var10);
+
+ if (res >= (double)args[6]) {
+ var8 = (double)args[6] * var8 / res;
+ var10 = (double)args[6] * var10 / res;
+ res = (double)args[6] * var6 / res;
+ }
+
+ writeScummVar(108, (int32)res);
+ writeScummVar(109, (int32)var10);
+ writeScummVar(110, (int32)var8);
+
+ return 1;
+}
+
+int LogicHEfootball::op_1023(int32 *args) {
+ double var10, var18, var20, var28, var30, var30_;
+ double argf[7];
+
+ for (int i = 0; i < 7; i++)
+ argf[i] = args[i];
+
+ var10 = (argf[3] - argf[1]) / (argf[2] - argf[0]);
+ var28 = var10 * var10 + 1;
+ var20 = argf[0] * var10;
+ var18 = (argf[5] + argf[1] + var20) * argf[4] * var10 * 2 +
+ argf[6] * argf[6] * var28 + argf[4] * argf[4] -
+ argf[0] * argf[0] * var10 * var10 -
+ argf[5] * argf[0] * var10 * 2 -
+ argf[5] * argf[1] * 2 -
+ argf[1] * argf[1] - argf[5] * argf[5];
+
+ if (var18 >= 0) {
+ var18 = sqrt(var18);
+
+ var30_ = argf[4] + argf[5] * var10 + argf[1] * var10 + argf[0] * var10 * var10;
+ var30 = (var30_ - var18) / var28;
+ var18 = (var30_ + var18) / var28;
+
+ if ((argf[0] - var30 < 0) && (argf[0] - var18 < 0)) {
+ var30_ = var30;
+ var30 = var18;
+ var18 = var30_;
+ }
+ var28 = var18 * var10 - var20 - argf[1];
+ var20 = var30 * var10 - var20 - argf[1];
+ } else {
+ var18 = 0;
+ var20 = 0;
+ var28 = 0;
+ var30 = 0;
+ }
+
+ writeScummVar(108, (int32)var18);
+ writeScummVar(109, (int32)var28);
+ writeScummVar(110, (int32)var30);
+ writeScummVar(111, (int32)var20);
+
+ return 1;
+}
+int LogicHEfootball::op_1024(int32 *args) {
+ writeScummVar(108, 0);
+ writeScummVar(109, 0);
+ writeScummVar(110, 0);
+ writeScummVar(111, 0);
+
+ return 1;
+}
+
+
+/***********************
+ * Backyard Soccer
+ *
+ */
+
+int LogicHEsoccer::versionID() {
+ return 1;
+}
+
+int32 LogicHEsoccer::dispatch(int op, int numArgs, int32 *args) {
+ int res = 0;
+
+ switch (op) {
+ case 1001:
+ res = op_1001(args);
+ break;
+
+ case 1002:
+ res = op_1002(args);
+ break;
+
+ case 1004:
+ res = op_1004(args);
+ break;
+
+ case 8221968:
+ // Someone had a fun and used his birthday as opcode number
+ res = getFromArray(args[0], args[1], args[2]);
+ break;
+
+ default:
+ // original range is 1001 - 1021
+ LogicHE::dispatch(op, numArgs, args);
+ warning("Tell sev how to reproduce it");
+ }
+
+ return res;
+}
+
+int LogicHEsoccer::op_1001(int32 *args) {
+ return args[0] * sin(args[1]);
+}
+
+int LogicHEsoccer::op_1002(int32 *args) {
+ return _vm->VAR(2) * args[0];
+}
+
+int LogicHEsoccer::op_1004(int32 *args) {
+ double res, a2, a4, a5;
+
+ a5 = ((double)args[4] - (double)args[1]) / ((double)args[5] - (double)args[2]);
+ a4 = ((double)args[3] - (double)args[0]) / ((double)args[5] - (double)args[2]);
+ a2 = (double)args[2] - (double)args[0] * a4 - args[1] * a5;
+
+ res = (double)args[6] * a4 + (double)args[7] * a5 + a2;
+ writeScummVar(108, (int32)res);
+
+ writeScummVar(109, (int32)a2);
+ writeScummVar(110, (int32)a5);
+ writeScummVar(111, (int32)a4);
+
+ return 1;
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/he/logic_he.h b/engines/scumm/he/logic_he.h
new file mode 100644
index 0000000000..59476f2e3b
--- /dev/null
+++ b/engines/scumm/he/logic_he.h
@@ -0,0 +1,124 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2005-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#if !defined(LOGIC_HE_H) && !defined(DISABLE_HE)
+#define LOGIC_HE_H
+
+#include "common/stdafx.h"
+
+namespace Scumm {
+
+class ScummEngine_v90he;
+
+class LogicHE {
+public:
+ float *_userData;
+ double *_userDataD;
+ ScummEngine_v90he *_vm;
+
+ LogicHE(ScummEngine_v90he *vm);
+ virtual ~LogicHE();
+
+ void writeScummVar(int var, int32 value);
+ int getFromArray(int arg0, int idx2, int idx1);
+ void putInArray(int arg0, int idx2, int idx1, int val);
+
+ void beforeBootScript(void) {};
+ void initOnce() {};
+ void startOfFrame() {};
+ void endOfFrame() {};
+ void processKeyStroke(int keyPressed) {};
+
+ virtual int versionID();
+ virtual int32 dispatch(int op, int numArgs, int32 *args);
+};
+
+class LogicHErace : public LogicHE {
+public:
+ LogicHErace(ScummEngine_v90he *vm) : LogicHE(vm) {}
+
+ int versionID();
+ int32 dispatch(int op, int numArgs, int32 *args);
+
+private:
+ int32 op_1003(int32 *args);
+ int32 op_1004(int32 *args);
+ int32 op_1100(int32 *args);
+ int32 op_1101(int32 *args);
+ int32 op_1102(int32 *args);
+ int32 op_1103(int32 *args);
+ int32 op_1110();
+ int32 op_1120(int32 *args);
+ int32 op_1130(int32 *args);
+ int32 op_1140(int32 *args);
+
+ void op_sub1(float arg);
+ void op_sub2(float arg);
+ void op_sub3(float arg);
+};
+
+class LogicHEfunshop : public LogicHE {
+public:
+ LogicHEfunshop(ScummEngine_v90he *vm) : LogicHE(vm) {}
+
+ int versionID();
+ int32 dispatch(int op, int numArgs, int32 *args);
+
+private:
+ void op_1004(int32 *args);
+ void op_1005(int32 *args);
+ int checkShape(int32 data0, int32 data1, int32 data4, int32 data5, int32 data2, int32 data3, int32 data6, int32 data7, int32 *x, int32 *y);
+};
+
+class LogicHEfootball : public LogicHE {
+public:
+ LogicHEfootball(ScummEngine_v90he *vm) : LogicHE(vm) {}
+
+ int versionID();
+ int32 dispatch(int op, int numArgs, int32 *args);
+
+private:
+ int op_1004(int32 *args);
+ int op_1006(int32 *args);
+ int op_1007(int32 *args);
+ int op_1010(int32 *args);
+ int op_1022(int32 *args);
+ int op_1023(int32 *args);
+ int op_1024(int32 *args);
+};
+
+class LogicHEsoccer : public LogicHE {
+public:
+ LogicHEsoccer(ScummEngine_v90he *vm) : LogicHE(vm) {}
+
+ int versionID();
+ int32 dispatch(int op, int numArgs, int32 *args);
+
+private:
+ int op_1001(int32 *args);
+ int op_1002(int32 *args);
+ int op_1004(int32 *args);
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/he/palette_he.cpp b/engines/scumm/he/palette_he.cpp
new file mode 100644
index 0000000000..85df92cdb7
--- /dev/null
+++ b/engines/scumm/he/palette_he.cpp
@@ -0,0 +1,317 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "common/system.h"
+#include "scumm/scumm.h"
+#include "scumm/he/intern_he.h"
+#include "scumm/resource.h"
+#include "scumm/util.h"
+
+namespace Scumm {
+
+void ScummEngine_v70he::remapHEPalette(const uint8 *src, uint8 *dst) {
+ int r, g, b, sum, bestitem, bestsum;
+ int ar, ag, ab;
+ uint8 *palPtr;
+ src += 30;
+
+ if (_heversion >= 99) {
+ palPtr = _hePalettes + 1024 + 30;
+ } else {
+ palPtr = _currentPalette + 30;
+ }
+
+ for (int j = 10; j < 246; j++) {
+ bestitem = 0xFFFF;
+ bestsum = 0xFFFF;
+
+ r = *src++;
+ g = *src++;
+ b = *src++;
+
+ uint8 *curPal = palPtr;
+
+ for (int k = 10; k < 246; k++) {
+ ar = r - *curPal++;
+ ag = g - *curPal++;
+ ab = b - *curPal++;
+
+ sum = (ar * ar) + (ag * ag) + (ab * ab);
+
+ if (bestitem == 0xFFFF || sum <= bestsum) {
+ bestitem = k;
+ bestsum = sum;
+ }
+ }
+
+ dst[j] = bestitem;
+ }
+}
+
+uint8 *ScummEngine_v90he::getHEPaletteIndex(int palSlot) {
+ if (palSlot) {
+ assert(palSlot >= 1 && palSlot <= _numPalettes);
+ return _hePalettes + palSlot * 1024;
+ } else {
+ return _hePalettes + 1024;
+ }
+}
+
+int ScummEngine_v90he::getHEPaletteSimilarColor(int palSlot, int red, int green, int start, int end) {
+ checkRange(_numPalettes, 1, palSlot, "Invalid palette %d");
+ checkRange(255, 0, start, "Invalid palette slot %d");
+ checkRange(255, 0, end, "Invalid palette slot %d");
+
+ uint8 *pal = _hePalettes + palSlot * 1024 + start * 3;
+
+ int bestsum = 0xFFFFFFFF;
+ int bestitem = start;
+
+ for (int i = start; i <= end; i++) {
+ int dr = red - pal[0];
+ int dg = green - pal[1];
+ int sum = dr * dr + dg * dg * 2;
+ if (sum == 0) {
+ return i;
+ }
+ if (sum < bestsum) {
+ bestsum = sum;
+ bestitem = i;
+ }
+ pal += 3;
+ }
+ return bestitem;
+}
+
+int ScummEngine_v90he::getHEPaletteColorComponent(int palSlot, int color, int component) {
+ checkRange(_numPalettes, 1, palSlot, "Invalid palette %d");
+ checkRange(255, 0, color, "Invalid palette slot %d");
+
+ return _hePalettes[palSlot * 1024 + color * 3 + component % 3];
+}
+
+int ScummEngine_v90he::getHEPaletteColor(int palSlot, int color) {
+ checkRange(_numPalettes, 1, palSlot, "Invalid palette %d");
+ checkRange(255, 0, color, "Invalid palette slot %d");
+
+ return _hePalettes[palSlot * 1024 + 768 + color];
+}
+
+void ScummEngine_v90he::setHEPaletteColor(int palSlot, uint8 color, uint8 r, uint8 g, uint8 b) {
+ debug(7, "setHEPaletteColor(%d, %d, %d, %d, %d)", palSlot, color, r, g, b);
+ checkRange(_numPalettes, 1, palSlot, "Invalid palette %d");
+ uint8 *p = _hePalettes + palSlot * 1024 + color * 3;
+ *(p + 0) = r;
+ *(p + 1) = g;
+ *(p + 2) = b;
+ _hePalettes[palSlot * 1024 + 768 + color] = color;
+}
+
+void ScummEngine_v90he::setHEPaletteFromPtr(int palSlot, const uint8 *palData) {
+ checkRange(_numPalettes, 1, palSlot, "Invalid palette %d");
+ uint8 *pc = _hePalettes + palSlot * 1024;
+ uint8 *pi = pc + 768;
+ for (int i = 0; i < 256; ++i) {
+ *pc++ = *palData++;
+ *pc++ = *palData++;
+ *pc++ = *palData++;
+ *pi++ = i;
+ }
+}
+
+void ScummEngine_v90he::setHEPaletteFromCostume(int palSlot, int resId) {
+ debug(7, "setHEPaletteFromCostume(%d, %d)", palSlot, resId);
+ checkRange(_numPalettes, 1, palSlot, "Invalid palette %d");
+ const uint8 *data = getResourceAddress(rtCostume, resId);
+ assert(data);
+ const uint8 *rgbs = findResourceData(MKID('RGBS'), data);
+ assert(rgbs);
+ setHEPaletteFromPtr(palSlot, rgbs);
+}
+
+void ScummEngine_v90he::setHEPaletteFromImage(int palSlot, int resId, int state) {
+ debug(7, "setHEPaletteFromImage(%d, %d, %d)", palSlot, resId, state);
+ checkRange(_numPalettes, 1, palSlot, "Invalid palette %d");
+ uint8 *data = getResourceAddress(rtImage, resId);
+ assert(data);
+ const uint8 *rgbs = findWrappedBlock(MKID('RGBS'), data, state, 0);
+ assert(rgbs);
+ setHEPaletteFromPtr(palSlot, rgbs);
+}
+
+void ScummEngine_v90he::setHEPaletteFromRoom(int palSlot, int resId, int state) {
+ debug(7, "setHEPaletteFromRoom(%d, %d, %d)", palSlot, resId, state);
+ checkRange(_numPalettes, 1, palSlot, "Invalid palette %d");
+ const uint8 *data = getResourceAddress(rtRoom, resId);
+ assert(data);
+ const uint8 *pals = findResourceData(MKID('PALS'), data);
+ assert(pals);
+ const uint8 *rgbs = findPalInPals(pals, state);
+ assert(rgbs);
+ setHEPaletteFromPtr(palSlot, rgbs);
+}
+
+void ScummEngine_v90he::restoreHEPalette(int palSlot) {
+ debug(7, "restoreHEPalette(%d)", palSlot);
+ checkRange(_numPalettes, 1, palSlot, "Invalid palette %d");
+ if (palSlot != 1) {
+ memcpy(_hePalettes + palSlot * 1024, _hePalettes + 1024, 1024);
+ }
+}
+
+void ScummEngine_v90he::copyHEPalette(int dstPalSlot, int srcPalSlot) {
+ debug(7, "copyHEPalette(%d, %d)", dstPalSlot, srcPalSlot);
+ assert(dstPalSlot >= 1 && dstPalSlot <= _numPalettes);
+ assert(srcPalSlot >= 1 && srcPalSlot <= _numPalettes);
+ if (dstPalSlot != srcPalSlot) {
+ memcpy(_hePalettes + dstPalSlot * 1024, _hePalettes + srcPalSlot * 1024, 1024);
+ }
+}
+
+void ScummEngine_v90he::copyHEPaletteColor(int palSlot, uint8 dstColor, uint8 srcColor) {
+ debug(7, "copyHEPaletteColor(%d, %d, %d)", palSlot, dstColor, srcColor);
+ checkRange(_numPalettes, 1, palSlot, "Invalid palette %d");
+ uint8 *dstPal = _hePalettes + palSlot * 1024 + dstColor * 3;
+ uint8 *srcPal = _hePalettes + 1024 + srcColor * 3;
+ memcpy(dstPal, srcPal, 3);
+ _hePalettes[palSlot * 1024 + 768 + dstColor] = srcColor;
+}
+
+void ScummEngine_v99he::setPaletteFromPtr(const byte *ptr, int numcolor) {
+ int i;
+ byte *dest, r, g, b;
+
+ if (numcolor < 0) {
+ numcolor = getResourceDataSize(ptr) / 3;
+ }
+
+ checkRange(256, 0, numcolor, "Too many colors (%d) in Palette");
+
+ dest = _hePalettes + 1024;
+
+ for (i = 0; i < numcolor; i++) {
+ r = *ptr++;
+ g = *ptr++;
+ b = *ptr++;
+
+ if (i == 15 || r < 252 || g < 252 || b < 252) {
+ *dest++ = r;
+ *dest++ = g;
+ *dest++ = b;
+ _hePalettes[1792 + i] = i;
+ } else {
+ dest += 3;
+ }
+ }
+
+ memcpy(_hePalettes, _hePalettes + 1024, 768);
+
+ for (i = 0; i < 10; ++i)
+ _hePalettes[1792 + i] = i;
+ for (i = 246; i < 256; ++i)
+ _hePalettes[1792 + i] = i;
+
+ setDirtyColors(0, numcolor - 1);
+}
+
+void ScummEngine_v99he::darkenPalette(int redScale, int greenScale, int blueScale, int startColor, int endColor) {
+ uint8 *src, *dst;
+ int color, j;
+
+ src = _hePalettes + startColor * 3;
+ dst = _hePalettes + 1024 + startColor * 3;
+ for (j = startColor; j <= endColor; j++) {
+ color = *src++;
+ color = color * redScale / 0xFF;
+ if (color > 255)
+ color = 255;
+ *dst++ = color;
+
+ color = *src++;
+ color = color * greenScale / 0xFF;
+ if (color > 255)
+ color = 255;
+ *dst++ = color;
+
+ color = *src++;
+ color = color * blueScale / 0xFF;
+ if (color > 255)
+ color = 255;
+ *dst++ = color;
+
+ _hePalettes[1792 + j] = j;
+ setDirtyColors(j, endColor);
+ }
+}
+
+void ScummEngine_v99he::copyPalColor(int dst, int src) {
+ byte *dp, *sp;
+
+ if ((uint) dst >= 256 || (uint) src >= 256)
+ error("copyPalColor: invalid values, %d, %d", dst, src);
+
+ dp = &_hePalettes[1024 + dst * 3];
+ sp = &_hePalettes[1024 + src * 3];
+
+ dp[0] = sp[0];
+ dp[1] = sp[1];
+ dp[2] = sp[2];
+ _hePalettes[1792 + dst] = dst;
+
+ setDirtyColors(dst, dst);
+}
+
+void ScummEngine_v99he::setPalColor(int idx, int r, int g, int b) {
+ _hePalettes[1024 + idx * 3 + 0] = r;
+ _hePalettes[1024 + idx * 3 + 1] = g;
+ _hePalettes[1024 + idx * 3 + 2] = b;
+ _hePalettes[1792 + idx] = idx;
+ setDirtyColors(idx, idx);
+}
+
+void ScummEngine_v99he::updatePalette() {
+ if (_palDirtyMax == -1)
+ return;
+
+ int num = _palDirtyMax - _palDirtyMin + 1;
+ int i;
+
+ byte palette_colors[1024];
+ byte *p = palette_colors;
+
+ for (i = _palDirtyMin; i <= _palDirtyMax; i++) {
+ byte *data = _hePalettes + 1024 + i * 3;
+
+ *p++ = data[0];
+ *p++ = data[1];
+ *p++ = data[2];
+ *p++ = 0;
+ }
+
+ _system->setPalette(palette_colors, _palDirtyMin, num);
+
+ _palDirtyMax = -1;
+ _palDirtyMin = 256;
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/he/resource_v7he.cpp b/engines/scumm/he/resource_v7he.cpp
new file mode 100644
index 0000000000..95c3c23f2b
--- /dev/null
+++ b/engines/scumm/he/resource_v7he.cpp
@@ -0,0 +1,1912 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * Parts of code heavily based on:
+ * icoutils - A set of programs dealing with MS Windows icons and cursors.
+ * Copyright (C) 1998-2001 Oskar Liljeblad
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "scumm/scumm.h"
+#include "scumm/he/intern_he.h"
+#include "scumm/resource.h"
+#include "scumm/he/resource_v7he.h"
+#include "scumm/sound.h"
+#include "scumm/util.h"
+#include "sound/wave.h"
+
+#include "common/stream.h"
+#include "common/system.h"
+
+namespace Scumm {
+
+ResExtractor::ResExtractor(ScummEngine_v70he *scumm)
+ : _vm(scumm) {
+
+ _fileName[0] = 0;
+ memset(_cursorCache, 0, sizeof(_cursorCache));
+}
+
+ResExtractor::~ResExtractor() {
+ for (int i = 0; i < MAX_CACHED_CURSORS; ++i) {
+ CachedCursor *cc = &_cursorCache[i];
+ if (cc->valid) {
+ free(cc->bitmap);
+ free(cc->palette);
+ }
+ }
+ memset(_cursorCache, 0, sizeof(_cursorCache));
+}
+
+ResExtractor::CachedCursor *ResExtractor::findCachedCursor(int id) {
+ for (int i = 0; i < MAX_CACHED_CURSORS; ++i) {
+ CachedCursor *cc = &_cursorCache[i];
+ if (cc->valid && cc->id == id) {
+ return cc;
+ }
+ }
+ return NULL;
+}
+
+ResExtractor::CachedCursor *ResExtractor::getCachedCursorSlot() {
+ uint32 min_last_used = 0;
+ CachedCursor *r = NULL;
+ for (int i = 0; i < MAX_CACHED_CURSORS; ++i) {
+ CachedCursor *cc = &_cursorCache[i];
+ if (!cc->valid) {
+ return cc;
+ } else {
+ if (min_last_used == 0 || cc->last_used < min_last_used) {
+ min_last_used = cc->last_used;
+ r = cc;
+ }
+ }
+ }
+ assert(r);
+ free(r->bitmap);
+ free(r->palette);
+ memset(r, 0, sizeof(CachedCursor));
+ return r;
+}
+
+void ResExtractor::setCursor(int id) {
+ byte *cursorRes = 0;
+ int cursorsize;
+ int keycolor = 0;
+ CachedCursor *cc = findCachedCursor(id);
+ if (cc != NULL) {
+ debug(7, "Found cursor %d in cache slot %d", id, cc - _cursorCache);
+ } else {
+ cc = getCachedCursorSlot();
+ assert(cc && !cc->valid);
+ cursorsize = extractResource(id, &cursorRes);
+ convertIcons(cursorRes, cursorsize, &cc->bitmap, &cc->w, &cc->h, &cc->hotspot_x, &cc->hotspot_y, &keycolor, &cc->palette, &cc->palSize);
+ debug(7, "Adding cursor %d to cache slot %d", id, cc - _cursorCache);
+ free(cursorRes);
+ cc->valid = true;
+ cc->id = id;
+ cc->last_used = g_system->getMillis();
+ }
+
+ if (_vm->_system->hasFeature(OSystem::kFeatureCursorHasPalette) && cc->palette)
+ _vm->_system->setCursorPalette(cc->palette, 0, cc->palSize);
+
+ _vm->setCursorHotspot(cc->hotspot_x, cc->hotspot_y);
+ _vm->setCursorFromBuffer(cc->bitmap, cc->w, cc->h, cc->w);
+}
+
+
+/*
+ * Static variables
+ */
+const char *res_types[] = {
+ /* 0x01: */
+ "cursor", "bitmap", "icon", "menu", "dialog", "string",
+ "fontdir", "font", "accelerator", "rcdata", "messagelist",
+ "group_cursor", NULL, "group_icon", NULL,
+ /* the following are not defined in winbase.h, but found in wrc. */
+ /* 0x10: */
+ "version", "dlginclude", NULL, "plugplay", "vxd",
+ "anicursor", "aniicon"
+};
+#define RES_TYPE_COUNT (sizeof(res_types)/sizeof(char *))
+
+Win32ResExtractor::Win32ResExtractor(ScummEngine_v70he *scumm) : ResExtractor(scumm) {
+}
+
+int Win32ResExtractor::extractResource(int resId, byte **data) {
+ char buf[20];
+
+ snprintf(buf, sizeof(buf), "%d", resId);
+
+ return extractResource_("group_cursor", buf, data);
+}
+
+int Win32ResExtractor::extractResource_(const char *resType, char *resName, byte **data) {
+ char *arg_language = NULL;
+ const char *arg_type = resType;
+ char *arg_name = resName;
+ int arg_action = ACTION_LIST;
+ int ressize = 0;
+
+ _arg_raw = false;
+
+ /* translate --type option from resource type string to integer */
+ arg_type = res_type_string_to_id(arg_type);
+
+ WinLibrary fi;
+
+ /* initiate stuff */
+ fi.memory = NULL;
+ fi.file = new Common::File;
+
+ if (!_fileName[0]) { // We are running for the first time
+ snprintf(_fileName, 256, "%s.he3", _vm->getBaseName());
+
+ if (_vm->_substResFileNameIndex > 0) {
+ char buf1[128];
+
+ _vm->generateSubstResFileName(_fileName, buf1, sizeof(buf1));
+ strcpy(_fileName, buf1);
+ }
+ }
+
+
+ /* get file size */
+ fi.file->open(_fileName);
+ if (!fi.file->isOpen()) {
+ error("Cannot open file %s", _fileName);
+ }
+
+ fi.total_size = fi.file->size();
+ if (fi.total_size == -1) {
+ error("Cannot get size of file %s", fi.file->name());
+ goto cleanup;
+ }
+ if (fi.total_size == 0) {
+ error("%s: file has a size of 0", fi.file->name());
+ goto cleanup;
+ }
+
+ /* read all of file */
+ fi.memory = (byte *)malloc(fi.total_size);
+ if (fi.file->read(fi.memory, fi.total_size) == 0) {
+ error("Cannot read from file %s", fi.file->name());
+ goto cleanup;
+ }
+
+ /* identify file and find resource table */
+ if (!read_library(&fi)) {
+ /* error reported by read_library */
+ goto cleanup;
+ }
+
+ // verbose_printf("file is a %s\n",
+ // fi.is_PE_binary ? "Windows NT `PE' binary" : "Windows 3.1 `NE' binary");
+
+ /* errors will be printed by the callback */
+ ressize = do_resources(&fi, arg_type, arg_name, arg_language, arg_action, data);
+
+ /* free stuff and close file */
+ cleanup:
+ if (fi.file != NULL)
+ fi.file->close();
+ if (fi.memory != NULL)
+ free(fi.memory);
+
+ return ressize;
+}
+
+
+/* res_type_id_to_string:
+ * Translate a numeric resource type to it's corresponding string type.
+ * (For informative-ness.)
+ */
+const char *Win32ResExtractor::res_type_id_to_string(int id) {
+ if (id == 241)
+ return "toolbar";
+ if (id > 0 && id <= (int)RES_TYPE_COUNT)
+ return res_types[id-1];
+ return NULL;
+}
+
+/* res_type_string_to_id:
+ * Translate a resource type string to integer.
+ * (Used to convert the --type option.)
+ */
+const char *Win32ResExtractor::res_type_string_to_id(const char *type) {
+ static const char *res_type_ids[] = {
+ "-1", "-2", "-3", "-4", "-5", "-6", "-7", "-8", "-9", "-10",
+ "-11", "-12", NULL, "-14", NULL, "-16", "-17", NULL, "-19",
+ "-20", "-21", "-22"
+ };
+ int c;
+
+ if (type == NULL)
+ return NULL;
+
+ for (c = 0 ; c < (int)RES_TYPE_COUNT ; c++) {
+ if (res_types[c] != NULL && !scumm_stricmp(type, res_types[c]))
+ return res_type_ids[c];
+ }
+
+ return type;
+}
+
+int Win32ResExtractor::extract_resources(WinLibrary *fi, WinResource *wr,
+ WinResource *type_wr, WinResource *name_wr,
+ WinResource *lang_wr, byte **data) {
+ int size;
+ bool free_it;
+ const char *type;
+ int32 id;
+
+ if (*data) {
+ error("Win32ResExtractor::extract_resources() more than one cursor");
+ return 0;
+ }
+
+ *data = extract_resource(fi, wr, &size, &free_it, type_wr->id, (lang_wr == NULL ? NULL : lang_wr->id), _arg_raw);
+
+ if (data == NULL) {
+ error("Win32ResExtractor::extract_resources() problem with resource extraction");
+ return 0;
+ }
+
+ /* get named resource type if possible */
+ type = NULL;
+ if ((id = strtol(type_wr->id, 0, 10)) != 0)
+ type = res_type_id_to_string(id);
+
+ debugC(DEBUG_RESOURCE, "extractCursor(). Found cursor name: %s%s%s [size=%d]",
+ get_resource_id_quoted(name_wr),
+ (lang_wr->id[0] != '\0' ? " language: " : ""),
+ get_resource_id_quoted(lang_wr), size);
+
+ return size;
+}
+
+/* extract_resource:
+ * Extract a resource, returning pointer to data.
+ */
+byte *Win32ResExtractor::extract_resource(WinLibrary *fi, WinResource *wr, int *size,
+ bool *free_it, char *type, char *lang, bool raw) {
+ char *str;
+ int32 intval;
+
+ /* just return pointer to data if raw */
+ if (raw) {
+ *free_it = false;
+ /* get_resource_entry will print possible error */
+ return get_resource_entry(fi, wr, size);
+ }
+
+ /* find out how to extract */
+ str = type;
+ if (str != NULL && (intval = strtol(STRIP_RES_ID_FORMAT(str), 0, 10))) {
+ if (intval == (int)RT_GROUP_ICON) {
+ *free_it = true;
+ return extract_group_icon_cursor_resource(fi, wr, lang, size, true);
+ }
+ if (intval == (int)RT_GROUP_CURSOR) {
+ *free_it = true;
+ return extract_group_icon_cursor_resource(fi, wr, lang, size, false);
+ }
+ }
+
+ return NULL;
+}
+
+/* extract_group_icon_resource:
+ * Create a complete RT_GROUP_ICON resource, that can be written to
+ * an `.ico' file without modifications. Returns an allocated
+ * memory block that should be freed with free() once used.
+ *
+ * `root' is the offset in file that specifies the resource.
+ * `base' is the offset that string pointers are calculated from.
+ * `ressize' should point to an integer variable where the size of
+ * the returned memory block will be placed.
+ * `is_icon' indicates whether resource to be extracted is icon
+ * or cursor group.
+ */
+byte *Win32ResExtractor::extract_group_icon_cursor_resource(WinLibrary *fi, WinResource *wr, char *lang,
+ int *ressize, bool is_icon) {
+ Win32CursorIconDir *icondir;
+ Win32CursorIconFileDir *fileicondir;
+ byte *memory;
+ int c, offset, skipped;
+ int size;
+
+ /* get resource data and size */
+ icondir = (Win32CursorIconDir *)get_resource_entry(fi, wr, &size);
+ if (icondir == NULL) {
+ /* get_resource_entry will print error */
+ return NULL;
+ }
+
+ /* calculate total size of output file */
+ RETURN_IF_BAD_POINTER(NULL, icondir->count);
+ skipped = 0;
+ for (c = 0 ; c < icondir->count ; c++) {
+ int level;
+ int iconsize;
+ char name[14];
+ WinResource *fwr;
+
+ RETURN_IF_BAD_POINTER(NULL, icondir->entries[c]);
+ /*printf("%d. bytes_in_res=%d width=%d height=%d planes=%d bit_count=%d\n", c,
+ icondir->entries[c].bytes_in_res,
+ (is_icon ? icondir->entries[c].res_info.icon.width : icondir->entries[c].res_info.cursor.width),
+ (is_icon ? icondir->entries[c].res_info.icon.height : icondir->entries[c].res_info.cursor.height),
+ icondir->entries[c].plane_count,
+ icondir->entries[c].bit_count);*/
+
+ /* find the corresponding icon resource */
+ snprintf(name, sizeof(name)/sizeof(char), "-%d", icondir->entries[c].res_id);
+ fwr = find_resource(fi, (is_icon ? "-3" : "-1"), name, lang, &level);
+ if (fwr == NULL) {
+ error("%s: could not find `%s' in `%s' resource.",
+ fi->file->name(), &name[1], (is_icon ? "group_icon" : "group_cursor"));
+ return NULL;
+ }
+
+ if (get_resource_entry(fi, fwr, &iconsize) != NULL) {
+ if (iconsize == 0) {
+ debugC(DEBUG_RESOURCE, "%s: icon resource `%s' is empty, skipping", fi->file->name(), name);
+ skipped++;
+ continue;
+ }
+ if ((uint32)iconsize != icondir->entries[c].bytes_in_res) {
+ debugC(DEBUG_RESOURCE, "%s: mismatch of size in icon resource `%s' and group (%d != %d)",
+ fi->file->name(), name, iconsize, icondir->entries[c].bytes_in_res);
+ }
+ size += iconsize; /* size += icondir->entries[c].bytes_in_res; */
+
+ /* cursor resources have two additional WORDs that contain
+ * hotspot info */
+ if (!is_icon)
+ size -= sizeof(uint16)*2;
+ }
+ }
+ offset = sizeof(Win32CursorIconFileDir) + (icondir->count-skipped) * sizeof(Win32CursorIconFileDirEntry);
+ size += offset;
+ *ressize = size;
+
+ /* allocate that much memory */
+ memory = (byte *)malloc(size);
+ fileicondir = (Win32CursorIconFileDir *)memory;
+
+ /* transfer Win32CursorIconDir structure members */
+ fileicondir->reserved = icondir->reserved;
+ fileicondir->type = icondir->type;
+ fileicondir->count = icondir->count - skipped;
+
+ /* transfer each cursor/icon: Win32CursorIconDirEntry and data */
+ skipped = 0;
+ for (c = 0 ; c < icondir->count ; c++) {
+ int level;
+ char name[14];
+ WinResource *fwr;
+ byte *data;
+
+ /* find the corresponding icon resource */
+ snprintf(name, sizeof(name)/sizeof(char), "-%d", icondir->entries[c].res_id);
+ fwr = find_resource(fi, (is_icon ? "-3" : "-1"), name, lang, &level);
+ if (fwr == NULL) {
+ error("%s: could not find `%s' in `%s' resource.",
+ fi->file->name(), &name[1], (is_icon ? "group_icon" : "group_cursor"));
+ return NULL;
+ }
+
+ /* get data and size of that resource */
+ data = (byte *)get_resource_entry(fi, fwr, &size);
+ if (data == NULL) {
+ /* get_resource_entry has printed error */
+ return NULL;
+ }
+ if (size == 0) {
+ skipped++;
+ continue;
+ }
+
+ /* copy ICONDIRENTRY (not including last dwImageOffset) */
+ memcpy(&fileicondir->entries[c-skipped], &icondir->entries[c],
+ sizeof(Win32CursorIconFileDirEntry)-sizeof(uint32));
+
+ /* special treatment for cursors */
+ if (!is_icon) {
+ fileicondir->entries[c-skipped].width = icondir->entries[c].res_info.cursor.width;
+ fileicondir->entries[c-skipped].height = icondir->entries[c].res_info.cursor.height / 2;
+ fileicondir->entries[c-skipped].color_count = 0;
+ fileicondir->entries[c-skipped].reserved = 0;
+ }
+
+ /* set image offset and increase it */
+ fileicondir->entries[c-skipped].dib_offset = offset;
+
+ /* transfer resource into file memory */
+ if (is_icon) {
+ memcpy(&memory[offset], data, icondir->entries[c].bytes_in_res);
+ } else {
+ fileicondir->entries[c-skipped].hotspot_x = ((uint16 *) data)[0];
+ fileicondir->entries[c-skipped].hotspot_y = ((uint16 *) data)[1];
+ memcpy(&memory[offset], data+sizeof(uint16)*2,
+ icondir->entries[c].bytes_in_res-sizeof(uint16)*2);
+ offset -= sizeof(uint16)*2;
+ }
+
+ /* increase the offset pointer */
+ offset += icondir->entries[c].bytes_in_res;
+ }
+
+ return memory;
+}
+
+/* check_offset:
+ * Check if a chunk of data (determined by offset and size)
+ * is within the bounds of the WinLibrary file.
+ * Usually not called directly.
+ */
+bool Win32ResExtractor::check_offset(byte *memory, int total_size, const char *name, void *offset, int size) {
+ int need_size = (int)((byte *)offset - memory + size);
+
+ debugC(DEBUG_RESOURCE, "check_offset: size=%x vs %x offset=%x size=%x",
+ need_size, total_size, (byte *)offset - memory, size);
+
+ if (need_size < 0 || need_size > total_size) {
+ error("%s: premature end", name);
+ return false;
+ }
+
+ return true;
+}
+
+
+/* do_resources:
+ * Do something for each resource matching type, name and lang.
+ */
+int Win32ResExtractor::do_resources(WinLibrary *fi, const char *type, char *name, char *lang, int action, byte **data) {
+ WinResource *type_wr;
+ WinResource *name_wr;
+ WinResource *lang_wr;
+ int size;
+
+ type_wr = (WinResource *)calloc(sizeof(WinResource)*3, 1);
+ name_wr = type_wr + 1;
+ lang_wr = type_wr + 2;
+
+ size = do_resources_recurs(fi, NULL, type_wr, name_wr, lang_wr, type, name, lang, action, data);
+
+ free(type_wr);
+
+ return size;
+}
+
+/* what is each entry in this directory level for? type, name or language? */
+#define WINRESOURCE_BY_LEVEL(x) ((x)==0 ? type_wr : ((x)==1 ? name_wr : lang_wr))
+
+/* does the id of this entry match the specified id? */
+#define LEVEL_MATCHES(x) (x == NULL || x ## _wr->id[0] == '\0' || compare_resource_id(x ## _wr, x))
+
+int Win32ResExtractor::do_resources_recurs(WinLibrary *fi, WinResource *base,
+ WinResource *type_wr, WinResource *name_wr, WinResource *lang_wr,
+ const char *type, char *name, char *lang, int action, byte **data) {
+ int c, rescnt;
+ WinResource *wr;
+ uint32 size = 0;
+
+ /* get a list of all resources at this level */
+ wr = list_resources(fi, base, &rescnt);
+ if (wr == NULL)
+ if (size != 0)
+ return size;
+ else
+ return 0;
+
+ /* process each resource listed */
+ for (c = 0 ; c < rescnt ; c++) {
+ /* (over)write the corresponding WinResource holder with the current */
+ memcpy(WINRESOURCE_BY_LEVEL(wr[c].level), wr+c, sizeof(WinResource));
+
+ /* go deeper unless there is something that does NOT match */
+ if (LEVEL_MATCHES(type) && LEVEL_MATCHES(name) && LEVEL_MATCHES(lang)) {
+ if (wr->is_directory)
+ size = do_resources_recurs(fi, wr+c, type_wr, name_wr, lang_wr, type, name, lang, action, data);
+ else
+ size = extract_resources(fi, wr+c, type_wr, name_wr, lang_wr, data);
+ }
+ }
+
+ /* since we're moving back one level after this, unset the
+ * WinResource holder used on this level */
+ memset(WINRESOURCE_BY_LEVEL(wr[0].level), 0, sizeof(WinResource));
+
+ return size;
+}
+
+/* return the resource id quoted if it's a string, otherwise just return it */
+char *Win32ResExtractor::get_resource_id_quoted(WinResource *wr) {
+ static char tmp[WINRES_ID_MAXLEN+2];
+
+ if (wr->numeric_id || wr->id[0] == '\0')
+ return wr->id;
+
+ sprintf(tmp, "'%s'", wr->id);
+ return tmp;
+}
+
+bool Win32ResExtractor::compare_resource_id(WinResource *wr, const char *id) {
+ if (wr->numeric_id) {
+ int32 cmp1, cmp2;
+ if (id[0] == '+')
+ return false;
+ if (id[0] == '-')
+ id++;
+ if (!(cmp1 = strtol(wr->id, 0, 10)) || !(cmp2 = strtol(id, 0, 10)) || cmp1 != cmp2)
+ return false;
+ } else {
+ if (id[0] == '-')
+ return false;
+ if (id[0] == '+')
+ id++;
+ if (strcmp(wr->id, id))
+ return false;
+ }
+
+ return true;
+}
+
+bool Win32ResExtractor::decode_pe_resource_id(WinLibrary *fi, WinResource *wr, uint32 value) {
+ if (value & IMAGE_RESOURCE_NAME_IS_STRING) { /* numeric id */
+ int c, len;
+ uint16 *mem = (uint16 *)
+ (fi->first_resource + (value & ~IMAGE_RESOURCE_NAME_IS_STRING));
+
+ /* copy each char of the string, and terminate it */
+ RETURN_IF_BAD_POINTER(false, *mem);
+ len = mem[0];
+ RETURN_IF_BAD_OFFSET(false, &mem[1], sizeof(uint16) * len);
+
+ len = MIN(mem[0], (uint16)WINRES_ID_MAXLEN);
+ for (c = 0 ; c < len ; c++)
+ wr->id[c] = mem[c+1] & 0x00FF;
+ wr->id[len] = '\0';
+ } else { /* Unicode string id */
+ /* translate id into a string */
+ snprintf(wr->id, WINRES_ID_MAXLEN, "%d", value);
+ }
+
+ wr->numeric_id = (value & IMAGE_RESOURCE_NAME_IS_STRING ? false:true);
+ return true;
+}
+
+byte *Win32ResExtractor::get_resource_entry(WinLibrary *fi, WinResource *wr, int *size) {
+ if (fi->is_PE_binary) {
+ Win32ImageResourceDataEntry *dataent;
+
+ dataent = (Win32ImageResourceDataEntry *) wr->children;
+ RETURN_IF_BAD_POINTER(NULL, *dataent);
+ *size = dataent->size;
+ RETURN_IF_BAD_OFFSET(NULL, fi->memory + dataent->offset_to_data, *size);
+
+ return fi->memory + dataent->offset_to_data;
+ } else {
+ Win16NENameInfo *nameinfo;
+ int sizeshift;
+
+ nameinfo = (Win16NENameInfo *) wr->children;
+ sizeshift = *((uint16 *) fi->first_resource - 1);
+ *size = nameinfo->length << sizeshift;
+ RETURN_IF_BAD_OFFSET(NULL, fi->memory + (nameinfo->offset << sizeshift), *size);
+
+ return fi->memory + (nameinfo->offset << sizeshift);
+ }
+}
+
+bool Win32ResExtractor::decode_ne_resource_id(WinLibrary *fi, WinResource *wr, uint16 value) {
+ if (value & NE_RESOURCE_NAME_IS_NUMERIC) { /* numeric id */
+ /* translate id into a string */
+ snprintf(wr->id, WINRES_ID_MAXLEN, "%d", value & ~NE_RESOURCE_NAME_IS_NUMERIC);
+ } else { /* ASCII string id */
+ int len;
+ char *mem = (char *)NE_HEADER(fi->memory)
+ + NE_HEADER(fi->memory)->rsrctab
+ + value;
+
+ /* copy each char of the string, and terminate it */
+ RETURN_IF_BAD_POINTER(false, *mem);
+ len = mem[0];
+ RETURN_IF_BAD_OFFSET(false, &mem[1], sizeof(char) * len);
+ memcpy(wr->id, &mem[1], len);
+ wr->id[len] = '\0';
+ }
+
+ wr->numeric_id = (value & NE_RESOURCE_NAME_IS_NUMERIC ? true:false);
+ return true;
+}
+
+Win32ResExtractor::WinResource *Win32ResExtractor::list_pe_resources(WinLibrary *fi, Win32ImageResourceDirectory *pe_res, int level, int *count) {
+ WinResource *wr;
+ int c, rescnt;
+ Win32ImageResourceDirectoryEntry *dirent
+ = (Win32ImageResourceDirectoryEntry *)(pe_res + 1);
+
+ /* count number of `type' resources */
+ RETURN_IF_BAD_POINTER(NULL, *dirent);
+ rescnt = pe_res->number_of_named_entries + pe_res->number_of_id_entries;
+ *count = rescnt;
+
+ /* allocate WinResource's */
+ wr = (WinResource *)malloc(sizeof(WinResource) * rescnt);
+
+ /* fill in the WinResource's */
+ for (c = 0 ; c < rescnt ; c++) {
+ RETURN_IF_BAD_POINTER(NULL, dirent[c]);
+ wr[c].this_ = pe_res;
+ wr[c].level = level;
+ wr[c].is_directory = (dirent[c].u2.s.data_is_directory);
+ wr[c].children = fi->first_resource + dirent[c].u2.s.offset_to_directory;
+
+ /* fill in wr->id, wr->numeric_id */
+ if (!decode_pe_resource_id (fi, wr + c, dirent[c].u1.name))
+ return NULL;
+ }
+
+ return wr;
+}
+
+Win32ResExtractor::WinResource *Win32ResExtractor::list_ne_name_resources(WinLibrary *fi, WinResource *typeres, int *count) {
+ int c, rescnt;
+ WinResource *wr;
+ Win16NETypeInfo *typeinfo = (Win16NETypeInfo *) typeres->this_;
+ Win16NENameInfo *nameinfo = (Win16NENameInfo *) typeres->children;
+
+ /* count number of `type' resources */
+ RETURN_IF_BAD_POINTER(NULL, typeinfo->count);
+ *count = rescnt = typeinfo->count;
+
+ /* allocate WinResource's */
+ wr = (WinResource *)malloc(sizeof(WinResource) * rescnt);
+
+ /* fill in the WinResource's */
+ for (c = 0 ; c < rescnt ; c++) {
+ RETURN_IF_BAD_POINTER(NULL, nameinfo[c]);
+ wr[c].this_ = nameinfo+c;
+ wr[c].is_directory = false;
+ wr[c].children = nameinfo+c;
+ wr[c].level = 1;
+
+ /* fill in wr->id, wr->numeric_id */
+ if (!decode_ne_resource_id(fi, wr + c, (nameinfo+c)->id))
+ return NULL;
+ }
+
+ return wr;
+}
+
+Win32ResExtractor::WinResource *Win32ResExtractor::list_ne_type_resources(WinLibrary *fi, int *count) {
+ int c, rescnt;
+ WinResource *wr;
+ Win16NETypeInfo *typeinfo;
+
+ /* count number of `type' resources */
+ typeinfo = (Win16NETypeInfo *) fi->first_resource;
+ RETURN_IF_BAD_POINTER(NULL, *typeinfo);
+ for (rescnt = 0 ; typeinfo->type_id != 0 ; rescnt++) {
+ typeinfo = NE_TYPEINFO_NEXT(typeinfo);
+ RETURN_IF_BAD_POINTER(NULL, *typeinfo);
+ }
+ *count = rescnt;
+
+ /* allocate WinResource's */
+ wr = (WinResource *)malloc(sizeof(WinResource) * rescnt);
+
+ /* fill in the WinResource's */
+ typeinfo = (Win16NETypeInfo *) fi->first_resource;
+ for (c = 0 ; c < rescnt ; c++) {
+ wr[c].this_ = typeinfo;
+ wr[c].is_directory = (typeinfo->count != 0);
+ wr[c].children = typeinfo+1;
+ wr[c].level = 0;
+
+ /* fill in wr->id, wr->numeric_id */
+ if (!decode_ne_resource_id(fi, wr + c, typeinfo->type_id))
+ return NULL;
+
+ typeinfo = NE_TYPEINFO_NEXT(typeinfo);
+ }
+
+ return wr;
+}
+
+/* list_resources:
+ * Return an array of WinResource's in the current
+ * resource level specified by res.
+ */
+Win32ResExtractor::WinResource *Win32ResExtractor::list_resources(WinLibrary *fi, WinResource *res, int *count) {
+ if (res != NULL && !res->is_directory)
+ return NULL;
+
+ if (fi->is_PE_binary) {
+ return list_pe_resources(fi, (Win32ImageResourceDirectory *)
+ (res == NULL ? fi->first_resource : res->children),
+ (res == NULL ? 0 : res->level+1),
+ count);
+ } else {
+ return (res == NULL
+ ? list_ne_type_resources(fi, count)
+ : list_ne_name_resources(fi, res, count));
+ }
+}
+
+/* read_library:
+ * Read header and get resource directory offset in a Windows library
+ * (AKA module).
+ *
+ */
+bool Win32ResExtractor::read_library(WinLibrary *fi) {
+ /* check for DOS header signature `MZ' */
+ RETURN_IF_BAD_POINTER(false, MZ_HEADER(fi->memory)->magic);
+ if (MZ_HEADER(fi->memory)->magic == IMAGE_DOS_SIGNATURE) {
+ DOSImageHeader *mz_header = MZ_HEADER(fi->memory);
+
+ RETURN_IF_BAD_POINTER(false, mz_header->lfanew);
+ if (mz_header->lfanew < sizeof(DOSImageHeader)) {
+ error("%s: not a Windows library", fi->file->name());
+ return false;
+ }
+ }
+
+ /* check for OS2 (Win16) header signature `NE' */
+ RETURN_IF_BAD_POINTER(false, NE_HEADER(fi->memory)->magic);
+ if (NE_HEADER(fi->memory)->magic == IMAGE_OS2_SIGNATURE) {
+ OS2ImageHeader *header = NE_HEADER(fi->memory);
+
+ RETURN_IF_BAD_POINTER(false, header->rsrctab);
+ RETURN_IF_BAD_POINTER(false, header->restab);
+ if (header->rsrctab >= header->restab) {
+ error("%s: no resource directory found", fi->file->name());
+ return false;
+ }
+
+ fi->is_PE_binary = false;
+ fi->first_resource = (byte *) NE_HEADER(fi->memory)
+ + header->rsrctab + sizeof(uint16);
+ RETURN_IF_BAD_POINTER(false, *(Win16NETypeInfo *) fi->first_resource);
+
+ return true;
+ }
+
+ /* check for NT header signature `PE' */
+ RETURN_IF_BAD_POINTER(false, PE_HEADER(fi->memory)->signature);
+ if (PE_HEADER(fi->memory)->signature == IMAGE_NT_SIGNATURE) {
+ Win32ImageSectionHeader *pe_sec;
+ Win32ImageDataDirectory *dir;
+ Win32ImageNTHeaders *pe_header;
+ int d;
+
+ /* allocate new memory */
+ fi->total_size = calc_vma_size(fi);
+ if (fi->total_size == 0) {
+ /* calc_vma_size has reported error */
+ return false;
+ }
+ fi->memory = (byte *)realloc(fi->memory, fi->total_size);
+
+ /* relocate memory, start from last section */
+ pe_header = PE_HEADER(fi->memory);
+ RETURN_IF_BAD_POINTER(false, pe_header->file_header.number_of_sections);
+
+ /* we don't need to do OFFSET checking for the sections.
+ * calc_vma_size has already done that */
+ for (d = pe_header->file_header.number_of_sections - 1; d >= 0 ; d--) {
+ pe_sec = PE_SECTIONS(fi->memory) + d;
+
+ if (pe_sec->characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA)
+ continue;
+
+ //if (pe_sec->virtual_address + pe_sec->size_of_raw_data > fi->total_size)
+
+ RETURN_IF_BAD_OFFSET(0, fi->memory + pe_sec->virtual_address, pe_sec->size_of_raw_data);
+ RETURN_IF_BAD_OFFSET(0, fi->memory + pe_sec->pointer_to_raw_data, pe_sec->size_of_raw_data);
+ if (pe_sec->virtual_address != pe_sec->pointer_to_raw_data) {
+ memmove(fi->memory + pe_sec->virtual_address,
+ fi->memory + pe_sec->pointer_to_raw_data,
+ pe_sec->size_of_raw_data);
+ }
+ }
+
+ /* find resource directory */
+ RETURN_IF_BAD_POINTER(false, pe_header->optional_header.data_directory[IMAGE_DIRECTORY_ENTRY_RESOURCE]);
+ dir = pe_header->optional_header.data_directory + IMAGE_DIRECTORY_ENTRY_RESOURCE;
+ if (dir->size == 0) {
+ error("%s: file contains no resources", fi->file->name());
+ return false;
+ }
+
+ fi->first_resource = fi->memory + dir->virtual_address;
+ fi->is_PE_binary = true;
+ return true;
+ }
+
+ /* other (unknown) header signature was found */
+ error("%s: not a Windows library", fi->file->name());
+ return false;
+}
+
+/* calc_vma_size:
+ * Calculate the total amount of memory needed for a 32-bit Windows
+ * module. Returns -1 if file was too small.
+ */
+int Win32ResExtractor::calc_vma_size(WinLibrary *fi) {
+ Win32ImageSectionHeader *seg;
+ int c, segcount, size;
+
+ size = 0;
+ RETURN_IF_BAD_POINTER(-1, PE_HEADER(fi->memory)->file_header.number_of_sections);
+ segcount = PE_HEADER(fi->memory)->file_header.number_of_sections;
+
+ /* If there are no segments, just process file like it is.
+ * This is (probably) not the right thing to do, but problems
+ * will be delt with later anyway.
+ */
+ if (segcount == 0)
+ return fi->total_size;
+
+ seg = PE_SECTIONS(fi->memory);
+ RETURN_IF_BAD_POINTER(-1, *seg);
+ for (c = 0 ; c < segcount ; c++) {
+ RETURN_IF_BAD_POINTER(0, *seg);
+
+ size = MAX((uint32)size, seg->virtual_address + seg->size_of_raw_data);
+ /* I have no idea what misc.virtual_size is for... */
+ size = MAX((uint32)size, seg->virtual_address + seg->misc.virtual_size);
+ seg++;
+ }
+
+ return size;
+}
+
+Win32ResExtractor::WinResource *Win32ResExtractor::find_with_resource_array(WinLibrary *fi, WinResource *wr, const char *id) {
+ int c, rescnt;
+ WinResource *return_wr;
+
+ wr = list_resources(fi, wr, &rescnt);
+ if (wr == NULL)
+ return NULL;
+
+ for (c = 0 ; c < rescnt ; c++) {
+ if (compare_resource_id(&wr[c], id)) {
+ /* duplicate WinResource and return it */
+ return_wr = (WinResource *)malloc(sizeof(WinResource));
+ memcpy(return_wr, &wr[c], sizeof(WinResource));
+
+ /* free old WinResource */
+ free(wr);
+ return return_wr;
+ }
+ }
+
+ return NULL;
+}
+
+Win32ResExtractor::WinResource *Win32ResExtractor::find_resource(WinLibrary *fi, const char *type, const char *name, const char *language, int *level) {
+ WinResource *wr;
+
+ *level = 0;
+ if (type == NULL)
+ return NULL;
+ wr = find_with_resource_array(fi, NULL, type);
+ if (wr == NULL || !wr->is_directory)
+ return wr;
+
+ *level = 1;
+ if (name == NULL)
+ return wr;
+ wr = find_with_resource_array(fi, wr, name);
+ if (wr == NULL || !wr->is_directory)
+ return wr;
+
+ *level = 2;
+ if (language == NULL)
+ return wr;
+ wr = find_with_resource_array(fi, wr, language);
+ return wr;
+}
+
+#define ROW_BYTES(bits) ((((bits) + 31) >> 5) << 2)
+
+
+int Win32ResExtractor::convertIcons(byte *data, int datasize, byte **cursor, int *w, int *h,
+ int *hotspot_x, int *hotspot_y, int *keycolor, byte **pal, int *palSize) {
+ Win32CursorIconFileDir dir;
+ Win32CursorIconFileDirEntry *entries = NULL;
+ uint32 offset;
+ uint32 c, d;
+ int completed;
+ int matched = 0;
+ MemoryReadStream *in = new MemoryReadStream(data, datasize);
+
+ if (!in->read(&dir, sizeof(Win32CursorIconFileDir)- sizeof(Win32CursorIconFileDirEntry)))
+ goto cleanup;
+ fix_win32_cursor_icon_file_dir_endian(&dir);
+
+ if (dir.reserved != 0) {
+ error("not an icon or cursor file (reserved non-zero)");
+ goto cleanup;
+ }
+ if (dir.type != 1 && dir.type != 2) {
+ error("not an icon or cursor file (wrong type)");
+ goto cleanup;
+ }
+
+ entries = (Win32CursorIconFileDirEntry *)malloc(dir.count * sizeof(Win32CursorIconFileDirEntry));
+ for (c = 0; c < dir.count; c++) {
+ if (!in->read(&entries[c], sizeof(Win32CursorIconFileDirEntry)))
+ goto cleanup;
+ fix_win32_cursor_icon_file_dir_entry_endian(&entries[c]);
+ if (entries[c].reserved != 0)
+ error("reserved is not zero");
+ }
+
+ offset = sizeof(Win32CursorIconFileDir) + (dir.count - 1) * (sizeof(Win32CursorIconFileDirEntry));
+
+ for (completed = 0; completed < dir.count; ) {
+ uint32 min_offset = 0x7fffffff;
+ int previous = completed;
+
+ for (c = 0; c < dir.count; c++) {
+ if (entries[c].dib_offset == offset) {
+ Win32BitmapInfoHeader bitmap;
+ Win32RGBQuad *palette = NULL;
+ uint32 palette_count = 0;
+ uint32 image_size, mask_size;
+ uint32 width, height;
+ byte *image_data = NULL, *mask_data = NULL;
+ byte *row = NULL;
+
+ if (!in->read(&bitmap, sizeof(Win32BitmapInfoHeader)))
+ goto local_cleanup;
+
+ fix_win32_bitmap_info_header_endian(&bitmap);
+ if (bitmap.size < sizeof(Win32BitmapInfoHeader)) {
+ error("bitmap header is too short");
+ goto local_cleanup;
+ }
+ if (bitmap.compression != 0) {
+ error("compressed image data not supported");
+ goto local_cleanup;
+ }
+ if (bitmap.x_pels_per_meter != 0)
+ error("x_pels_per_meter field in bitmap should be zero");
+ if (bitmap.y_pels_per_meter != 0)
+ error("y_pels_per_meter field in bitmap should be zero");
+ if (bitmap.clr_important != 0)
+ error("clr_important field in bitmap should be zero");
+ if (bitmap.planes != 1)
+ error("planes field in bitmap should be one");
+ if (bitmap.size != sizeof(Win32BitmapInfoHeader)) {
+ uint32 skip = bitmap.size - sizeof(Win32BitmapInfoHeader);
+ error("skipping %d bytes of extended bitmap header", skip);
+ in->seek(skip, SEEK_CUR);
+ }
+ offset += bitmap.size;
+
+ if (bitmap.clr_used != 0 || bitmap.bit_count < 24) {
+ palette_count = (bitmap.clr_used != 0 ? bitmap.clr_used : 1 << bitmap.bit_count);
+ palette = (Win32RGBQuad *)malloc(sizeof(Win32RGBQuad) * palette_count);
+ if (!in->read(palette, sizeof(Win32RGBQuad) * palette_count))
+ goto local_cleanup;
+ offset += sizeof(Win32RGBQuad) * palette_count;
+ }
+
+ width = bitmap.width;
+ height = ABS(bitmap.height)/2;
+
+ image_size = height * ROW_BYTES(width * bitmap.bit_count);
+ mask_size = height * ROW_BYTES(width);
+
+ if (entries[c].dib_size != bitmap.size + image_size + mask_size + palette_count * sizeof(Win32RGBQuad))
+ debugC(DEBUG_RESOURCE, "incorrect total size of bitmap (%d specified; %d real)",
+ entries[c].dib_size,
+ bitmap.size + image_size + mask_size + palette_count * sizeof(Win32RGBQuad)
+ );
+
+ image_data = (byte *)malloc(image_size);
+ if (!in->read(image_data, image_size))
+ goto local_cleanup;
+
+ mask_data = (byte *)malloc(mask_size);
+ if (!in->read(mask_data, mask_size))
+ goto local_cleanup;
+
+ offset += image_size;
+ offset += mask_size;
+ completed++;
+ matched++;
+
+ *hotspot_x = entries[c].hotspot_x;
+ *hotspot_y = entries[c].hotspot_y;
+ *w = width;
+ *h = height;
+ *keycolor = 0;
+ *cursor = (byte *)malloc(width * height);
+
+ row = (byte *)malloc(width * 4);
+
+ for (d = 0; d < height; d++) {
+ uint32 x;
+ uint32 y = (bitmap.height < 0 ? d : height - d - 1);
+ uint32 imod = y * (image_size / height) * 8 / bitmap.bit_count;
+ //uint32 mmod = y * (mask_size / height) * 8;
+
+ for (x = 0; x < width; x++) {
+
+ uint32 color = simple_vec(image_data, x + imod, bitmap.bit_count);
+
+ // FIXME?: This works only with b/w cursors and white index may be
+ // different. But now it's enough.
+ if (color) {
+ cursor[0][width * d + x] = 15; // white in SCUMM
+ } else {
+ cursor[0][width * d + x] = 255; // transparent
+ }
+ /*
+
+ if (bitmap.bit_count <= 16) {
+ if (color >= palette_count) {
+ error("color out of range in image data");
+ goto local_cleanup;
+ }
+ row[4*x+0] = palette[color].red;
+ row[4*x+1] = palette[color].green;
+ row[4*x+2] = palette[color].blue;
+
+ } else {
+ row[4*x+0] = (color >> 16) & 0xFF;
+ row[4*x+1] = (color >> 8) & 0xFF;
+ row[4*x+2] = (color >> 0) & 0xFF;
+ }
+ if (bitmap.bit_count == 32)
+ row[4*x+3] = (color >> 24) & 0xFF;
+ else
+ row[4*x+3] = simple_vec(mask_data, x + mmod, 1) ? 0 : 0xFF;
+ */
+ }
+
+ }
+
+ if (row != NULL)
+ free(row);
+ if (palette != NULL)
+ free(palette);
+ if (image_data != NULL) {
+ free(image_data);
+ free(mask_data);
+ }
+ continue;
+
+ local_cleanup:
+
+ if (row != NULL)
+ free(row);
+ if (palette != NULL)
+ free(palette);
+ if (image_data != NULL) {
+ free(image_data);
+ free(mask_data);
+ }
+ goto cleanup;
+ } else {
+ if (entries[c].dib_offset > offset)
+ min_offset = MIN(min_offset, entries[c].dib_offset);
+ }
+ }
+
+ if (previous == completed) {
+ if (min_offset < offset) {
+ error("offset of bitmap header incorrect (too low)");
+ goto cleanup;
+ }
+ debugC(DEBUG_RESOURCE, "skipping %d bytes of garbage at %d", min_offset-offset, offset);
+ in->seek(min_offset - offset, SEEK_CUR);
+ offset = min_offset;
+ }
+ }
+
+ free(entries);
+ return matched;
+
+cleanup:
+
+ free(entries);
+ return -1;
+}
+
+uint32 Win32ResExtractor::simple_vec(byte *data, uint32 ofs, byte size) {
+ switch (size) {
+ case 1:
+ return (data[ofs/8] >> (7 - ofs%8)) & 1;
+ case 2:
+ return (data[ofs/4] >> ((3 - ofs%4) << 1)) & 3;
+ case 4:
+ return (data[ofs/2] >> ((1 - ofs%2) << 2)) & 15;
+ case 8:
+ return data[ofs];
+ case 16:
+ return data[2*ofs] | data[2*ofs+1] << 8;
+ case 24:
+ return data[3*ofs] | data[3*ofs+1] << 8 | data[3*ofs+2] << 16;
+ case 32:
+ return data[4*ofs] | data[4*ofs+1] << 8 | data[4*ofs+2] << 16 | data[4*ofs+3] << 24;
+ }
+
+ return 0;
+}
+
+#define LE16(x) ((x) = TO_LE_16(x))
+#define LE32(x) ((x) = TO_LE_32(x))
+
+void Win32ResExtractor::fix_win32_cursor_icon_file_dir_endian(Win32CursorIconFileDir *obj) {
+ LE16(obj->reserved);
+ LE16(obj->type);
+ LE16(obj->count);
+}
+
+void Win32ResExtractor::fix_win32_bitmap_info_header_endian(Win32BitmapInfoHeader *obj) {
+ LE32(obj->size);
+ LE32(obj->width);
+ LE32(obj->height);
+ LE16(obj->planes);
+ LE16(obj->bit_count);
+ LE32(obj->compression);
+ LE32(obj->size_image);
+ LE32(obj->x_pels_per_meter);
+ LE32(obj->y_pels_per_meter);
+ LE32(obj->clr_used);
+ LE32(obj->clr_important);
+}
+
+void Win32ResExtractor::fix_win32_cursor_icon_file_dir_entry_endian(Win32CursorIconFileDirEntry *obj) {
+ LE16(obj->hotspot_x);
+ LE16(obj->hotspot_y);
+ LE32(obj->dib_size);
+ LE32(obj->dib_offset);
+}
+
+void Win32ResExtractor::fix_win32_image_section_header(Win32ImageSectionHeader *obj) {
+ LE32(obj->misc.physical_address);
+ LE32(obj->virtual_address);
+ LE32(obj->size_of_raw_data);
+ LE32(obj->pointer_to_raw_data);
+ LE32(obj->pointer_to_relocations);
+ LE32(obj->pointer_to_linenumbers);
+ LE16(obj->number_of_relocations);
+ LE16(obj->number_of_linenumbers);
+ LE32(obj->characteristics);
+}
+
+void Win32ResExtractor::fix_os2_image_header_endian(OS2ImageHeader *obj) {
+ LE16(obj->magic);
+ LE16(obj->enttab);
+ LE16(obj->cbenttab);
+ LE32(obj->crc);
+ LE16(obj->flags);
+ LE16(obj->autodata);
+ LE16(obj->heap);
+ LE16(obj->stack);
+ LE32(obj->csip);
+ LE32(obj->sssp);
+ LE16(obj->cseg);
+ LE16(obj->cmod);
+ LE16(obj->cbnrestab);
+ LE16(obj->segtab);
+ LE16(obj->rsrctab);
+ LE16(obj->restab);
+ LE16(obj->modtab);
+ LE16(obj->imptab);
+ LE32(obj->nrestab);
+ LE16(obj->cmovent);
+ LE16(obj->align);
+ LE16(obj->cres);
+ LE16(obj->fastload_offset);
+ LE16(obj->fastload_length);
+ LE16(obj->swaparea);
+ LE16(obj->expver);
+}
+
+/* fix_win32_image_header_endian:
+ * NOTE: This assumes that the optional header is always available.
+ */
+void Win32ResExtractor::fix_win32_image_header_endian(Win32ImageNTHeaders *obj) {
+ LE32(obj->signature);
+ LE16(obj->file_header.machine);
+ LE16(obj->file_header.number_of_sections);
+ LE32(obj->file_header.time_date_stamp);
+ LE32(obj->file_header.pointer_to_symbol_table);
+ LE32(obj->file_header.number_of_symbols);
+ LE16(obj->file_header.size_of_optional_header);
+ LE16(obj->file_header.characteristics);
+ LE16(obj->optional_header.magic);
+ LE32(obj->optional_header.size_of_code);
+ LE32(obj->optional_header.size_of_initialized_data);
+ LE32(obj->optional_header.size_of_uninitialized_data);
+ LE32(obj->optional_header.address_of_entry_point);
+ LE32(obj->optional_header.base_of_code);
+ LE32(obj->optional_header.base_of_data);
+ LE32(obj->optional_header.image_base);
+ LE32(obj->optional_header.section_alignment);
+ LE32(obj->optional_header.file_alignment);
+ LE16(obj->optional_header.major_operating_system_version);
+ LE16(obj->optional_header.minor_operating_system_version);
+ LE16(obj->optional_header.major_image_version);
+ LE16(obj->optional_header.minor_image_version);
+ LE16(obj->optional_header.major_subsystem_version);
+ LE16(obj->optional_header.minor_subsystem_version);
+ LE32(obj->optional_header.win32_version_value);
+ LE32(obj->optional_header.size_of_image);
+ LE32(obj->optional_header.size_of_headers);
+ LE32(obj->optional_header.checksum);
+ LE16(obj->optional_header.subsystem);
+ LE16(obj->optional_header.dll_characteristics);
+ LE32(obj->optional_header.size_of_stack_reserve);
+ LE32(obj->optional_header.size_of_stack_commit);
+ LE32(obj->optional_header.size_of_heap_reserve);
+ LE32(obj->optional_header.size_of_heap_commit);
+ LE32(obj->optional_header.loader_flags);
+ LE32(obj->optional_header.number_of_rva_and_sizes);
+}
+
+void Win32ResExtractor::fix_win32_image_data_directory(Win32ImageDataDirectory *obj) {
+ LE32(obj->virtual_address);
+ LE32(obj->size);
+}
+
+
+MacResExtractor::MacResExtractor(ScummEngine_v70he *scumm) : ResExtractor(scumm) {
+ _resOffset = -1;
+}
+
+int MacResExtractor::extractResource(int id, byte **buf) {
+ Common::File in;
+ int size;
+
+ if (!_fileName[0]) // We are running for the first time
+ if (_vm->_substResFileNameIndex > 0) {
+ char buf1[128];
+
+ snprintf(buf1, 128, "%s.he3", _vm->getBaseName());
+ _vm->generateSubstResFileName(buf1, _fileName, sizeof(buf1));
+
+ // Some programs write it as .bin. Try that too
+ if (!in.exists(_fileName)) {
+ strcpy(buf1, _fileName);
+ snprintf(_fileName, 128, "%s.bin", buf1);
+
+ if (!in.exists(_fileName)) {
+ // And finally check if we have dumped resource fork
+ snprintf(_fileName, 128, "%s.rsrc", buf1);
+ if (!in.exists(_fileName)) {
+ error("Cannot open file any of files '%s', '%s.bin', '%s.rsrc",
+ buf1, buf1, buf1);
+ }
+ }
+ }
+ }
+
+ in.open(_fileName);
+ if (!in.isOpen()) {
+ error("Cannot open file %s", _fileName);
+ }
+
+ // we haven't calculated it
+ if (_resOffset == -1) {
+ if (!init(in))
+ error("Resource fork is missing in file '%s'", _fileName);
+ in.close();
+ in.open(_fileName);
+ }
+
+ *buf = getResource(in, "crsr", 1000 + id, &size);
+
+ in.close();
+
+ if (*buf == NULL)
+ error("There is no cursor ID #%d", 1000 + id);
+
+ return size;
+}
+
+#define MBI_INFOHDR 128
+#define MBI_ZERO1 0
+#define MBI_NAMELEN 1
+#define MBI_ZERO2 74
+#define MBI_ZERO3 82
+#define MBI_DFLEN 83
+#define MBI_RFLEN 87
+#define MAXNAMELEN 63
+
+bool MacResExtractor::init(Common::File in) {
+ byte infoHeader[MBI_INFOHDR];
+ int32 data_size, rsrc_size;
+ int32 data_size_pad, rsrc_size_pad;
+ int filelen;
+
+ filelen = in.size();
+ in.read(infoHeader, MBI_INFOHDR);
+
+ // Maybe we have MacBinary?
+ if (infoHeader[MBI_ZERO1] == 0 && infoHeader[MBI_ZERO2] == 0 &&
+ infoHeader[MBI_ZERO3] == 0 && infoHeader[MBI_NAMELEN] <= MAXNAMELEN) {
+
+ // Pull out fork lengths
+ data_size = READ_BE_UINT32(infoHeader + MBI_DFLEN);
+ rsrc_size = READ_BE_UINT32(infoHeader + MBI_RFLEN);
+
+ data_size_pad = (((data_size + 127) >> 7) << 7);
+ rsrc_size_pad = (((rsrc_size + 127) >> 7) << 7);
+
+ // Length check
+ int sumlen = MBI_INFOHDR + data_size_pad + rsrc_size_pad;
+
+ if (sumlen == filelen)
+ _resOffset = MBI_INFOHDR + data_size_pad;
+ }
+
+ if (_resOffset == -1) // MacBinary check is failed
+ _resOffset = 0; // Maybe we have dumped fork?
+
+ in.seek(_resOffset);
+
+ _dataOffset = in.readUint32BE() + _resOffset;
+ _mapOffset = in.readUint32BE() + _resOffset;
+ _dataLength = in.readUint32BE();
+ _mapLength = in.readUint32BE();
+
+ // do sanity check
+ if (_dataOffset >= filelen || _mapOffset >= filelen ||
+ _dataLength + _mapLength > filelen) {
+ _resOffset = -1;
+ return false;
+ }
+
+ debug(7, "got header: data %d [%d] map %d [%d]",
+ _dataOffset, _dataLength, _mapOffset, _mapLength);
+
+ readMap(in);
+
+ return true;
+}
+
+byte *MacResExtractor::getResource(Common::File in, const char *typeID, int16 resID, int *size) {
+ int i;
+ int typeNum = -1;
+ int resNum = -1;
+ byte *buf;
+ int len;
+
+ for (i = 0; i < _resMap.numTypes; i++)
+ if (strcmp(_resTypes[i].id, typeID) == 0) {
+ typeNum = i;
+ break;
+ }
+
+ if (typeNum == -1)
+ return NULL;
+
+ for (i = 0; i < _resTypes[typeNum].items; i++)
+ if (_resLists[typeNum][i].id == resID) {
+ resNum = i;
+ break;
+ }
+
+ if (resNum == -1)
+ return NULL;
+
+ in.seek(_dataOffset + _resLists[typeNum][resNum].dataOffset);
+
+ len = in.readUint32BE();
+ buf = (byte *)malloc(len);
+
+ in.read(buf, len);
+
+ *size = len;
+
+ return buf;
+}
+
+void MacResExtractor::readMap(Common::File in) {
+ int i, j, len;
+
+ in.seek(_mapOffset + 22);
+
+ _resMap.resAttr = in.readUint16BE();
+ _resMap.typeOffset = in.readUint16BE();
+ _resMap.nameOffset = in.readUint16BE();
+ _resMap.numTypes = in.readUint16BE();
+ _resMap.numTypes++;
+
+ in.seek(_mapOffset + _resMap.typeOffset + 2);
+ _resTypes = new ResType[_resMap.numTypes];
+
+ for (i = 0; i < _resMap.numTypes; i++) {
+ in.read(_resTypes[i].id, 4);
+ _resTypes[i].id[4] = 0;
+ _resTypes[i].items = in.readUint16BE();
+ _resTypes[i].offset = in.readUint16BE();
+ _resTypes[i].items++;
+ }
+
+ _resLists = new ResPtr[_resMap.numTypes];
+
+ for (i = 0; i < _resMap.numTypes; i++) {
+ _resLists[i] = new Resource[_resTypes[i].items];
+ in.seek(_resTypes[i].offset + _mapOffset + _resMap.typeOffset);
+
+ for (j = 0; j < _resTypes[i].items; j++) {
+ ResPtr resPtr = _resLists[i] + j;
+
+ resPtr->id = in.readUint16BE();
+ resPtr->nameOffset = in.readUint16BE();
+ resPtr->dataOffset = in.readUint32BE();
+ in.readUint32BE();
+ resPtr->name = 0;
+
+ resPtr->attr = resPtr->dataOffset >> 24;
+ resPtr->dataOffset &= 0xFFFFFF;
+ }
+
+ for (j = 0; j < _resTypes[i].items; j++) {
+ if (_resLists[i][j].nameOffset != -1) {
+ in.seek(_resLists[i][j].nameOffset + _mapOffset + _resMap.nameOffset);
+
+ len = in.readByte();
+ _resLists[i][j].name = new byte[len + 1];
+ _resLists[i][j].name[len] = 0;
+ in.read(_resLists[i][j].name, len);
+ }
+ }
+ }
+}
+
+int MacResExtractor::convertIcons(byte *data, int datasize, byte **cursor, int *w, int *h,
+ int *hotspot_x, int *hotspot_y, int *keycolor, byte **palette, int *palSize) {
+ Common::MemoryReadStream dis(data, datasize);
+ int i, b;
+ byte imageByte;
+ byte *iconData;
+ int numBytes;
+ int pixelsPerByte, bpp;
+ int ctSize;
+ byte bitmask;
+ int iconRowBytes, iconBounds[4];
+ int ignored;
+ int iconDataSize;
+
+ dis.readUint16BE(); // type
+ dis.readUint32BE(); // offset to pixel map
+ dis.readUint32BE(); // offset to pixel data
+ dis.readUint32BE(); // expanded cursor data
+ dis.readUint16BE(); // expanded data depth
+ dis.readUint32BE(); // reserved
+
+ // Grab B/W icon data
+ *cursor = (byte *)malloc(16 * 16);
+ for (i = 0; i < 32; i++) {
+ imageByte = dis.readByte();
+ for (b = 0; b < 8; b++)
+ cursor[0][i*8+b] = (byte)((imageByte &
+ (0x80 >> b)) > 0? 0x0F: 0x00);
+ }
+
+ // Apply mask data
+ for (i = 0; i < 32; i++) {
+ imageByte = dis.readByte();
+ for (b = 0; b < 8; b++)
+ if ((imageByte & (0x80 >> b)) == 0)
+ cursor[0][i*8+b] = 0xff;
+ }
+
+ *hotspot_y = dis.readUint16BE();
+ *hotspot_x = dis.readUint16BE();
+ *w = *h = 16;
+
+ // Use b/w cursor on backends which don't support cursor palettes
+ if (!_vm->_system->hasFeature(OSystem::kFeatureCursorHasPalette))
+ return 1;
+
+ dis.readUint32BE(); // reserved
+ dis.readUint32BE(); // cursorID
+
+ // Color version of cursor
+ dis.readUint32BE(); // baseAddr
+
+ // Keep only lowbyte for now
+ dis.readByte();
+ iconRowBytes = dis.readByte();
+
+ if (!iconRowBytes)
+ return 1;
+
+ iconBounds[0] = dis.readUint16BE();
+ iconBounds[1] = dis.readUint16BE();
+ iconBounds[2] = dis.readUint16BE();
+ iconBounds[3] = dis.readUint16BE();
+
+ dis.readUint16BE(); // pmVersion
+ dis.readUint16BE(); // packType
+ dis.readUint32BE(); // packSize
+
+ dis.readUint32BE(); // hRes
+ dis.readUint32BE(); // vRes
+
+ dis.readUint16BE(); // pixelType
+ dis.readUint16BE(); // pixelSize
+ dis.readUint16BE(); // cmpCount
+ dis.readUint16BE(); // cmpSize
+
+ dis.readUint32BE(); // planeByte
+ dis.readUint32BE(); // pmTable
+ dis.readUint32BE(); // reserved
+
+ // Pixel data for cursor
+ iconDataSize = iconRowBytes * (iconBounds[3] - iconBounds[1]);
+ iconData = (byte *)malloc(iconDataSize);
+ dis.read(iconData, iconDataSize);
+
+ // Color table
+ dis.readUint32BE(); // ctSeed
+ dis.readUint16BE(); // ctFlag
+ ctSize = dis.readUint16BE() + 1;
+
+ *palette = (byte *)malloc(ctSize * 4);
+
+ // Read just high byte of 16-bit color
+ for (int c = 0; c < ctSize; c++) {
+ // We just use indices 0..ctSize, so ignore color ID
+ dis.readUint16BE(); // colorID[c]
+
+ palette[0][c * 4 + 0] = dis.readByte();
+ ignored = dis.readByte();
+
+ palette[0][c * 4 + 1] = dis.readByte();
+ ignored = dis.readByte();
+
+ palette[0][c * 4 + 2] = dis.readByte();
+ ignored = dis.readByte();
+
+ palette[0][c * 4 + 3] = 0;
+ }
+
+ *palSize = ctSize;
+
+ numBytes =
+ (iconBounds[2] - iconBounds[0]) *
+ (iconBounds[3] - iconBounds[1]);
+
+ pixelsPerByte = (iconBounds[2] - iconBounds[0]) / iconRowBytes;
+ bpp = 8 / pixelsPerByte;
+
+ // build a mask to make sure the pixels are properly shifted out
+ bitmask = 0;
+ for (int m = 0; m < bpp; m++) {
+ bitmask <<= 1;
+ bitmask |= 1;
+ }
+
+ // Extract pixels from bytes
+ for (int j = 0; j < iconDataSize; j++)
+ for (b = 0; b < pixelsPerByte; b++) {
+ int idx = j * pixelsPerByte + (pixelsPerByte - 1 - b);
+
+ if (cursor[0][idx] != 0xff) // if mask is not there
+ cursor[0][idx] = (byte)((iconData[j] >> (b * bpp)) & bitmask);
+ }
+
+ free(iconData);
+
+ assert(datasize - dis.pos() == 0);
+
+ return 1;
+}
+
+
+
+void ScummEngine_v70he::readRoomsOffsets() {
+ int num, i;
+ byte *ptr;
+
+ debug(9, "readRoomOffsets()");
+
+ num = READ_LE_UINT16(_heV7RoomOffsets);
+ ptr = _heV7RoomOffsets + 2;
+ for (i = 0; i < num; i++) {
+ res.roomoffs[rtRoom][i] = READ_LE_UINT32(ptr);
+ ptr += 4;
+ }
+}
+
+void ScummEngine_v70he::readGlobalObjects() {
+ int num = _fileHandle->readUint16LE();
+ assert(num == _numGlobalObjects);
+
+ _fileHandle->read(_objectStateTable, num);
+ _fileHandle->read(_objectOwnerTable, num);
+ _fileHandle->read(_objectRoomTable, num);
+
+ _fileHandle->read(_classData, num * sizeof(uint32));
+
+#if defined(SCUMM_BIG_ENDIAN)
+ // Correct the endianess if necessary
+ for (int i = 0; i != num; i++)
+ _classData[i] = FROM_LE_32(_classData[i]);
+#endif
+}
+
+void ScummEngine_v99he::readMAXS(int blockSize) {
+ debug(0, "ScummEngine_v99he readMAXS: MAXS has blocksize %d", blockSize);
+
+ _numVariables = _fileHandle->readUint16LE();
+ _fileHandle->readUint16LE();
+ _numRoomVariables = _fileHandle->readUint16LE();
+ _numLocalObjects = _fileHandle->readUint16LE();
+ _numArray = _fileHandle->readUint16LE();
+ _fileHandle->readUint16LE();
+ _fileHandle->readUint16LE();
+ _numFlObject = _fileHandle->readUint16LE();
+ _numInventory = _fileHandle->readUint16LE();
+ _numRooms = _fileHandle->readUint16LE();
+ _numScripts = _fileHandle->readUint16LE();
+ _numSounds = _fileHandle->readUint16LE();
+ _numCharsets = _fileHandle->readUint16LE();
+ _numCostumes = _fileHandle->readUint16LE();
+ _numGlobalObjects = _fileHandle->readUint16LE();
+ _numImages = _fileHandle->readUint16LE();
+ _numSprites = _fileHandle->readUint16LE();
+ _numLocalScripts = _fileHandle->readUint16LE();
+ _HEHeapSize = _fileHandle->readUint16LE();
+ _numPalettes = _fileHandle->readUint16LE();
+ _numUnk = _fileHandle->readUint16LE();
+ _numTalkies = _fileHandle->readUint16LE();
+ _numNewNames = 10;
+
+ _objectRoomTable = (byte *)calloc(_numGlobalObjects, 1);
+ _numGlobalScripts = 2048;
+}
+
+void ScummEngine_v90he::readMAXS(int blockSize) {
+ debug(0, "ScummEngine_v90he readMAXS: MAXS has blocksize %d", blockSize);
+
+ _numVariables = _fileHandle->readUint16LE();
+ _fileHandle->readUint16LE();
+ _numRoomVariables = _fileHandle->readUint16LE();
+ _numLocalObjects = _fileHandle->readUint16LE();
+ _numArray = _fileHandle->readUint16LE();
+ _fileHandle->readUint16LE();
+ _fileHandle->readUint16LE();
+ _numFlObject = _fileHandle->readUint16LE();
+ _numInventory = _fileHandle->readUint16LE();
+ _numRooms = _fileHandle->readUint16LE();
+ _numScripts = _fileHandle->readUint16LE();
+ _numSounds = _fileHandle->readUint16LE();
+ _numCharsets = _fileHandle->readUint16LE();
+ _numCostumes = _fileHandle->readUint16LE();
+ _numGlobalObjects = _fileHandle->readUint16LE();
+ _numImages = _fileHandle->readUint16LE();
+ _numSprites = _fileHandle->readUint16LE();
+ _numLocalScripts = _fileHandle->readUint16LE();
+ _HEHeapSize = _fileHandle->readUint16LE();
+ _numNewNames = 10;
+
+ _objectRoomTable = (byte *)calloc(_numGlobalObjects, 1);
+ if (_features & GF_HE_985)
+ _numGlobalScripts = 2048;
+ else
+ _numGlobalScripts = 200;
+}
+
+void ScummEngine_v72he::readMAXS(int blockSize) {
+ debug(0, "ScummEngine_v72he readMAXS: MAXS has blocksize %d", blockSize);
+
+ _numVariables = _fileHandle->readUint16LE();
+ _fileHandle->readUint16LE();
+ _numBitVariables = _numRoomVariables = _fileHandle->readUint16LE();
+ _numLocalObjects = _fileHandle->readUint16LE();
+ _numArray = _fileHandle->readUint16LE();
+ _fileHandle->readUint16LE();
+ _numVerbs = _fileHandle->readUint16LE();
+ _numFlObject = _fileHandle->readUint16LE();
+ _numInventory = _fileHandle->readUint16LE();
+ _numRooms = _fileHandle->readUint16LE();
+ _numScripts = _fileHandle->readUint16LE();
+ _numSounds = _fileHandle->readUint16LE();
+ _numCharsets = _fileHandle->readUint16LE();
+ _numCostumes = _fileHandle->readUint16LE();
+ _numGlobalObjects = _fileHandle->readUint16LE();
+ _numImages = _fileHandle->readUint16LE();
+ _numNewNames = 10;
+
+ _objectRoomTable = (byte *)calloc(_numGlobalObjects, 1);
+ _numGlobalScripts = 200;
+}
+
+byte *ScummEngine_v72he::getStringAddress(int i) {
+ byte *addr = getResourceAddress(rtString, i);
+ if (addr == NULL)
+ return NULL;
+ return ((ScummEngine_v72he::ArrayHeader *)addr)->data;
+}
+
+int ScummEngine_v72he::getSoundResourceSize(int id) {
+ const byte *ptr;
+ int offs, size;
+
+ if (id > _numSounds) {
+ if (!_sound->getHEMusicDetails(id, offs, size)) {
+ debug(0, "getSoundResourceSize: musicID %d not found", id);
+ return 0;
+ }
+ } else {
+ ptr = getResourceAddress(rtSound, id);
+ if (!ptr)
+ return 0;
+
+ if (READ_UINT32(ptr) == MKID('RIFF')) {
+ byte flags;
+ int rate;
+
+ size = READ_BE_UINT32(ptr + 4);
+ Common::MemoryReadStream stream(ptr, size);
+
+ if (!loadWAVFromStream(stream, size, rate, flags)) {
+ error("getSoundResourceSize: Not a valid WAV file");
+ }
+ } else {
+ ptr += 8 + READ_BE_UINT32(ptr + 12);
+ if (READ_UINT32(ptr) == MKID('SBNG')) {
+ ptr += READ_BE_UINT32(ptr + 4);
+ }
+
+ assert(READ_UINT32(ptr) == MKID('SDAT'));
+ size = READ_BE_UINT32(ptr + 4) - 8;
+ }
+ }
+
+ return size;
+}
+
+void ScummEngine_v80he::createSound(int snd1id, int snd2id) {
+ debug(0, "createSound: snd1id %d snd2id %d", snd1id, snd2id);
+
+ byte *snd1Ptr, *snd2Ptr;
+ byte *sbng1Ptr, *sbng2Ptr;
+ byte *sdat1Ptr, *sdat2Ptr;
+ byte *src, *dst, *tmp;
+ int len, offs, size;
+ int sdat1size, sdat2size;
+
+ if (snd2id == -1) {
+ _sndPtrOffs = 0;
+ _sndTmrOffs = 0;
+ return;
+ }
+
+ if (snd1id != _curSndId) {
+ _curSndId = snd1id;
+ _sndPtrOffs = 0;
+ _sndTmrOffs = 0;
+ }
+
+ snd1Ptr = getResourceAddress(rtSound, snd1id);
+ assert(snd1Ptr);
+ snd2Ptr = getResourceAddress(rtSound, snd2id);
+ assert(snd2Ptr);
+
+ int i;
+ int chan = -1;
+ for (i = 0; i < ARRAYSIZE(_sound->_heChannel); i++) {
+ if (_sound->_heChannel[i].sound == snd1id)
+ chan = i;
+ }
+
+ sbng1Ptr = heFindResource(MKID('SBNG'), snd1Ptr);
+ sbng2Ptr = heFindResource(MKID('SBNG'), snd2Ptr);
+
+ if (sbng1Ptr != NULL && sbng2Ptr != NULL) {
+ if (chan != -1 && _sound->_heChannel[chan].codeOffs > 0) {
+ int curOffs = _sound->_heChannel[chan].codeOffs;
+
+ src = snd1Ptr + curOffs;
+ dst = sbng1Ptr + 8;
+ size = READ_BE_UINT32(sbng1Ptr + 4);
+ len = sbng1Ptr - snd1Ptr + size - curOffs;
+
+ byte *data = (byte *)malloc(len);
+ memcpy(data, src, len);
+ memcpy(dst, data, len);
+ free(data);
+
+ dst = sbng1Ptr + 8;
+ while ((size = READ_LE_UINT16(dst)) != 0)
+ dst += size;
+ } else {
+ dst = sbng1Ptr + 8;
+ }
+
+ _sound->_heChannel[chan].codeOffs = sbng1Ptr - snd1Ptr + 8;
+
+ tmp = sbng2Ptr + 8;
+ while ((offs = READ_LE_UINT16(tmp)) != 0) {
+ tmp += offs;
+ }
+
+ src = sbng2Ptr + 8;
+ len = tmp - sbng2Ptr - 6;
+ memcpy(dst, src, len);
+
+ int32 time;
+ while ((size = READ_LE_UINT16(dst)) != 0) {
+ time = READ_LE_UINT32(dst + 2);
+ time += _sndTmrOffs;
+ WRITE_LE_UINT32(dst + 2, time);
+ dst += size;
+ }
+ }
+
+ sdat1Ptr = heFindResource(MKID('SDAT'), snd1Ptr);
+ assert(sdat1Ptr);
+ sdat2Ptr = heFindResource(MKID('SDAT'), snd2Ptr);
+ assert(sdat2Ptr);
+
+ sdat1size = READ_BE_UINT32(sdat1Ptr + 4) - 8 - _sndPtrOffs;
+ sdat2size = READ_BE_UINT32(sdat2Ptr + 4) - 8;
+
+ debug(0, "SDAT size1 %d size2 %d", sdat1size, sdat2size);
+ if (sdat2size < sdat1size) {
+ src = sdat2Ptr + 8;
+ dst = sdat1Ptr + 8 + _sndPtrOffs;
+ len = sdat2size;
+
+ memcpy(dst, src, len);
+
+ _sndPtrOffs += sdat2size;
+ _sndTmrOffs += sdat2size;
+ } else {
+ src = sdat2Ptr + 8;
+ dst = sdat1Ptr + 8 + _sndPtrOffs;
+ len = sdat1size;
+
+ memcpy(dst, src, len);
+
+ if (sdat2size != sdat1size) {
+ src = sdat2Ptr + 8 + sdat1size;
+ dst = sdat1Ptr + 8;
+ len = sdat2size - sdat1size;
+
+ memcpy(dst, src, len);
+ }
+
+ _sndPtrOffs = sdat2size - sdat1size;
+ _sndTmrOffs += sdat2size;
+ }
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/he/resource_v7he.h b/engines/scumm/he/resource_v7he.h
new file mode 100644
index 0000000000..1496aa3d7f
--- /dev/null
+++ b/engines/scumm/he/resource_v7he.h
@@ -0,0 +1,556 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2004-2006 The ScummVM project
+ *
+ * Parts of code heavily based on:
+ * icoutils - A set of programs dealing with MS Windows icons and cursors.
+ * Copyright (C) 1998-2001 Oskar Liljeblad
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#if !defined(RESOURCE_V7HE_H) && !defined(DISABLE_HE)
+#define RESOURCE_V7HE_H
+
+namespace Scumm {
+
+#define WINRES_ID_MAXLEN (256)
+
+/*
+ * Definitions
+ */
+
+#define ACTION_LIST 1 /* command: list resources */
+#define ACTION_EXTRACT 2 /* command: extract resources */
+#define CALLBACK_STOP 0 /* results of ResourceCallback */
+#define CALLBACK_CONTINUE 1
+#define CALLBACK_CONTINUE_RECURS 2
+
+#define MZ_HEADER(x) ((DOSImageHeader *)(x))
+#define NE_HEADER(x) ((OS2ImageHeader *)PE_HEADER(x))
+#define NE_TYPEINFO_NEXT(x) ((Win16NETypeInfo *)((byte *)(x) + sizeof(Win16NETypeInfo) + \
+ ((Win16NETypeInfo *)x)->count * sizeof(Win16NENameInfo)))
+#define NE_RESOURCE_NAME_IS_NUMERIC (0x8000)
+
+#define STRIP_RES_ID_FORMAT(x) (x != NULL && (x[0] == '-' || x[0] == '+') ? ++x : x)
+
+#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16
+#define IMAGE_SIZEOF_SHORT_NAME 8
+
+#define IMAGE_RESOURCE_NAME_IS_STRING 0x80000000
+#define IMAGE_RESOURCE_DATA_IS_DIRECTORY 0x80000000
+
+#define PE_HEADER(module) \
+ ((Win32ImageNTHeaders*)((byte *)(module) + \
+ (((DOSImageHeader*)(module))->lfanew)))
+
+#define PE_SECTIONS(module) \
+ ((Win32ImageSectionHeader *)((byte *) &PE_HEADER(module)->optional_header + \
+ PE_HEADER(module)->file_header.size_of_optional_header))
+
+#define IMAGE_DOS_SIGNATURE 0x5A4D /* MZ */
+#define IMAGE_OS2_SIGNATURE 0x454E /* NE */
+#define IMAGE_OS2_SIGNATURE_LE 0x454C /* LE */
+#define IMAGE_OS2_SIGNATURE_LX 0x584C /* LX */
+#define IMAGE_VXD_SIGNATURE 0x454C /* LE */
+#define IMAGE_NT_SIGNATURE 0x00004550 /* PE00 */
+
+#if !defined (WIN32)
+#define IMAGE_SCN_CNT_CODE 0x00000020
+#define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040
+#define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080
+#endif
+
+#define IMAGE_DIRECTORY_ENTRY_EXPORT 0
+#define IMAGE_DIRECTORY_ENTRY_IMPORT 1
+#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2
+#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3
+#define IMAGE_DIRECTORY_ENTRY_SECURITY 4
+#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5
+#define IMAGE_DIRECTORY_ENTRY_DEBUG 6
+#define IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7
+#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 /* (MIPS GP) */
+#define IMAGE_DIRECTORY_ENTRY_TLS 9
+#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10
+#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11
+#define IMAGE_DIRECTORY_ENTRY_IAT 12 /* Import Address Table */
+#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13
+#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14
+
+#if !defined (WIN32)
+#define RT_CURSOR 1
+#define RT_BITMAP 2
+#define RT_ICON 3
+#define RT_MENU 4
+#define RT_DIALOG 5
+#define RT_STRING 6
+#define RT_FONTDIR 7
+#define RT_FONT 8
+#define RT_ACCELERATOR 9
+#define RT_RCDATA 10
+#define RT_MESSAGELIST 11
+#define RT_GROUP_CURSOR 12
+#define RT_GROUP_ICON 14
+#endif
+
+#define RETURN_IF_BAD_POINTER(r, x) \
+ if (!check_offset(fi->memory, fi->total_size, fi->file->name(), &(x), sizeof(x))) \
+ return (r);
+#define RETURN_IF_BAD_OFFSET(r, x, s) \
+ if (!check_offset(fi->memory, fi->total_size, fi->file->name(), x, s)) \
+ return (r);
+
+class ScummEngine_v70he;
+
+class ResExtractor {
+public:
+ ResExtractor(ScummEngine_v70he *scumm);
+ virtual ~ResExtractor();
+
+ void setCursor(int id);
+
+ virtual int extractResource(int id, byte **buf) { return 0; };
+ virtual int convertIcons(byte *data, int datasize, byte **cursor, int *w, int *h,
+ int *hotspot_x, int *hotspot_y, int *keycolor,
+ byte **palette, int *palSize) { return 0; };
+
+ enum {
+ MAX_CACHED_CURSORS = 10
+ };
+
+ struct CachedCursor {
+ bool valid;
+ int id;
+ byte *bitmap;
+ int w, h;
+ int hotspot_x, hotspot_y;
+ uint32 last_used;
+ byte *palette;
+ int palSize;
+ };
+
+ ScummEngine_v70he *_vm;
+
+ ResExtractor::CachedCursor *findCachedCursor(int id);
+ ResExtractor::CachedCursor *getCachedCursorSlot();
+
+ bool _arg_raw;
+ char _fileName[256];
+ CachedCursor _cursorCache[MAX_CACHED_CURSORS];
+
+ typedef Common::MemoryReadStream MemoryReadStream;
+
+};
+
+class Win32ResExtractor : public ResExtractor {
+ public:
+ Win32ResExtractor(ScummEngine_v70he *scumm);
+ ~Win32ResExtractor() {};
+ int extractResource(int id, byte **data);
+ void setCursor(int id);
+ int convertIcons(byte *data, int datasize, byte **cursor, int *w, int *h,
+ int *hotspot_x, int *hotspot_y, int *keycolor, byte **palette, int *palSize);
+
+ private:
+ int extractResource_(const char *resType, char *resName, byte **data);
+/*
+ * Structures
+ */
+
+#if !defined(__GNUC__)
+ #pragma START_PACK_STRUCTS
+#endif
+
+ struct WinLibrary {
+ Common::File *file;
+ byte *memory;
+ byte *first_resource;
+ bool is_PE_binary;
+ int total_size;
+ };
+
+ struct WinResource {
+ char id[256];
+ void *this_;
+ void *children;
+ int level;
+ bool numeric_id;
+ bool is_directory;
+ };
+
+
+ struct Win32IconResDir {
+ byte width;
+ byte height;
+ byte color_count;
+ byte reserved;
+ };
+
+ struct Win32CursorDir {
+ uint16 width;
+ uint16 height;
+ };
+
+ struct Win32CursorIconDirEntry {
+ union {
+ Win32IconResDir icon;
+ Win32CursorDir cursor;
+ } res_info;
+ uint16 plane_count;
+ uint16 bit_count;
+ uint32 bytes_in_res;
+ uint16 res_id;
+ };
+
+ struct Win32CursorIconDir {
+ uint16 reserved;
+ uint16 type;
+ uint16 count;
+ Win32CursorIconDirEntry entries[1] GCC_PACK;
+ };
+
+ struct Win32CursorIconFileDirEntry {
+ byte width;
+ byte height;
+ byte color_count;
+ byte reserved;
+ uint16 hotspot_x;
+ uint16 hotspot_y;
+ uint32 dib_size;
+ uint32 dib_offset;
+ };
+
+ struct Win32CursorIconFileDir {
+ uint16 reserved;
+ uint16 type;
+ uint16 count;
+ Win32CursorIconFileDirEntry entries[1];
+ };
+
+ struct Win32BitmapInfoHeader {
+ uint32 size;
+ int32 width;
+ int32 height;
+ uint16 planes;
+ uint16 bit_count;
+ uint32 compression;
+ uint32 size_image;
+ int32 x_pels_per_meter;
+ int32 y_pels_per_meter;
+ uint32 clr_used;
+ uint32 clr_important;
+ };
+
+ struct Win32RGBQuad {
+ byte blue;
+ byte green;
+ byte red;
+ byte reserved;
+ };
+
+ struct Win32ImageResourceDirectoryEntry {
+ union {
+ struct {
+ #ifdef SCUMM_BIGENDIAN
+ unsigned name_is_string:1;
+ unsigned name_offset:31;
+ #else
+ unsigned name_offset:31;
+ unsigned name_is_string:1;
+ #endif
+ } s1;
+ uint32 name;
+ struct {
+ #ifdef SCUMM_BIG_ENDIAN
+ uint16 __pad;
+ uint16 id;
+ #else
+ uint16 id;
+ uint16 __pad;
+ #endif
+ } s2;
+ } u1;
+ union {
+ uint32 offset_to_data;
+ struct {
+ #ifdef SCUMM_BIG_ENDIAN
+ unsigned data_is_directory:1;
+ unsigned offset_to_directory:31;
+ #else
+ unsigned offset_to_directory:31;
+ unsigned data_is_directory:1;
+ #endif
+ } s;
+ } u2;
+ };
+
+ struct Win16NETypeInfo {
+ uint16 type_id;
+ uint16 count;
+ uint32 resloader; // FARPROC16 - smaller? uint16?
+ };
+
+ struct Win16NENameInfo {
+ uint16 offset;
+ uint16 length;
+ uint16 flags;
+ uint16 id;
+ uint16 handle;
+ uint16 usage;
+ };
+
+ struct OS2ImageHeader {
+ uint16 magic;
+ byte ver;
+ byte rev;
+ uint16 enttab;
+ uint16 cbenttab;
+ int32 crc;
+ uint16 flags;
+ uint16 autodata;
+ uint16 heap;
+ uint16 stack;
+ uint32 csip;
+ uint32 sssp;
+ uint16 cseg;
+ uint16 cmod;
+ uint16 cbnrestab;
+ uint16 segtab;
+ uint16 rsrctab;
+ uint16 restab;
+ uint16 modtab;
+ uint16 imptab;
+ uint32 nrestab;
+ uint16 cmovent;
+ uint16 align;
+ uint16 cres;
+ byte exetyp;
+ byte flagsothers;
+ uint16 fastload_offset;
+ uint16 fastload_length;
+ uint16 swaparea;
+ uint16 expver;
+ };
+
+ struct DOSImageHeader {
+ uint16 magic;
+ uint16 cblp;
+ uint16 cp;
+ uint16 crlc;
+ uint16 cparhdr;
+ uint16 minalloc;
+ uint16 maxalloc;
+ uint16 ss;
+ uint16 sp;
+ uint16 csum;
+ uint16 ip;
+ uint16 cs;
+ uint16 lfarlc;
+ uint16 ovno;
+ uint16 res[4];
+ uint16 oemid;
+ uint16 oeminfo;
+ uint16 res2[10];
+ uint32 lfanew;
+ };
+
+ struct Win32ImageFileHeader {
+ uint16 machine;
+ uint16 number_of_sections;
+ uint32 time_date_stamp;
+ uint32 pointer_to_symbol_table;
+ uint32 number_of_symbols;
+ uint16 size_of_optional_header;
+ uint16 characteristics;
+ };
+
+ struct Win32ImageDataDirectory {
+ uint32 virtual_address;
+ uint32 size;
+ };
+
+ struct Win32ImageOptionalHeader {
+ uint16 magic;
+ byte major_linker_version;
+ byte minor_linker_version;
+ uint32 size_of_code;
+ uint32 size_of_initialized_data;
+ uint32 size_of_uninitialized_data;
+ uint32 address_of_entry_point;
+ uint32 base_of_code;
+ uint32 base_of_data;
+ uint32 image_base;
+ uint32 section_alignment;
+ uint32 file_alignment;
+ uint16 major_operating_system_version;
+ uint16 minor_operating_system_version;
+ uint16 major_image_version;
+ uint16 minor_image_version;
+ uint16 major_subsystem_version;
+ uint16 minor_subsystem_version;
+ uint32 win32_version_value;
+ uint32 size_of_image;
+ uint32 size_of_headers;
+ uint32 checksum;
+ uint16 subsystem;
+ uint16 dll_characteristics;
+ uint32 size_of_stack_reserve;
+ uint32 size_of_stack_commit;
+ uint32 size_of_heap_reserve;
+ uint32 size_of_heap_commit;
+ uint32 loader_flags;
+ uint32 number_of_rva_and_sizes;
+ Win32ImageDataDirectory data_directory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
+ };
+
+ struct Win32ImageNTHeaders {
+ uint32 signature;
+ Win32ImageFileHeader file_header;
+ Win32ImageOptionalHeader optional_header;
+ };
+
+ struct Win32ImageSectionHeader {
+ byte name[IMAGE_SIZEOF_SHORT_NAME];
+ union {
+ uint32 physical_address;
+ uint32 virtual_size;
+ } misc;
+ uint32 virtual_address;
+ uint32 size_of_raw_data;
+ uint32 pointer_to_raw_data;
+ uint32 pointer_to_relocations;
+ uint32 pointer_to_linenumbers;
+ uint16 number_of_relocations;
+ uint16 number_of_linenumbers;
+ uint32 characteristics;
+ };
+
+ struct Win32ImageResourceDataEntry {
+ uint32 offset_to_data;
+ uint32 size;
+ uint32 code_page;
+ uint32 resource_handle;
+ };
+
+ struct Win32ImageResourceDirectory {
+ uint32 characteristics;
+ uint32 time_date_stamp;
+ uint16 major_version;
+ uint16 minor_version;
+ uint16 number_of_named_entries;
+ uint16 number_of_id_entries;
+ };
+
+#if !defined(__GNUC__)
+ #pragma END_PACK_STRUCTS
+#endif
+
+/*
+ * Function Prototypes
+ */
+
+ WinResource *list_resources(WinLibrary *, WinResource *, int *);
+ bool read_library(WinLibrary *);
+ WinResource *find_resource(WinLibrary *, const char *, const char *, const char *, int *);
+ byte *get_resource_entry(WinLibrary *, WinResource *, int *);
+ int do_resources(WinLibrary *, const char *, char *, char *, int, byte **);
+ bool compare_resource_id(WinResource *, const char *);
+ const char *res_type_string_to_id(const char *);
+
+ const char *res_type_id_to_string(int);
+ char *get_destination_name(WinLibrary *, char *, char *, char *);
+
+ byte *extract_resource(WinLibrary *, WinResource *, int *, bool *, char *, char *, bool);
+ int extract_resources(WinLibrary *, WinResource *, WinResource *, WinResource *, WinResource *, byte **);
+ byte *extract_group_icon_cursor_resource(WinLibrary *, WinResource *, char *, int *, bool);
+
+ bool decode_pe_resource_id(WinLibrary *, WinResource *, uint32);
+ bool decode_ne_resource_id(WinLibrary *, WinResource *, uint16);
+ WinResource *list_ne_type_resources(WinLibrary *, int *);
+ WinResource *list_ne_name_resources(WinLibrary *, WinResource *, int *);
+ WinResource *list_pe_resources(WinLibrary *, Win32ImageResourceDirectory *, int, int *);
+ int calc_vma_size(WinLibrary *);
+ int do_resources_recurs(WinLibrary *, WinResource *, WinResource *, WinResource *, WinResource *, const char *, char *, char *, int, byte **);
+ char *get_resource_id_quoted(WinResource *);
+ WinResource *find_with_resource_array(WinLibrary *, WinResource *, const char *);
+
+ bool check_offset(byte *, int, const char *, void *, int);
+
+ uint32 simple_vec(byte *data, uint32 ofs, byte size);
+
+ void fix_win32_cursor_icon_file_dir_endian(Win32CursorIconFileDir *obj);
+ void fix_win32_bitmap_info_header_endian(Win32BitmapInfoHeader *obj);
+ void fix_win32_cursor_icon_file_dir_entry_endian(Win32CursorIconFileDirEntry *obj);
+ void fix_win32_image_section_header(Win32ImageSectionHeader *obj);
+ void fix_os2_image_header_endian(OS2ImageHeader *obj);
+ void fix_win32_image_header_endian(Win32ImageNTHeaders *obj);
+ void fix_win32_image_data_directory(Win32ImageDataDirectory *obj);
+};
+
+class MacResExtractor : public ResExtractor {
+
+public:
+ MacResExtractor(ScummEngine_v70he *scumm);
+ ~MacResExtractor() { }
+ void setCursor(int id) ;
+
+private:
+ int extractResource(int id, byte **buf);
+ bool init(Common::File in);
+ void readMap(Common::File in);
+ byte *getResource(Common::File in, const char *typeID, int16 resID, int *size);
+ int convertIcons(byte *data, int datasize, byte **cursor, int *w, int *h,
+ int *hotspot_x, int *hotspot_y, int *keycolor, byte **palette, int *palSize);
+
+ struct ResMap {
+ int16 resAttr;
+ int16 typeOffset;
+ int16 nameOffset;
+ int16 numTypes;
+ };
+
+ struct ResType {
+ char id[5];
+ int16 items;
+ int16 offset;
+ };
+
+ struct Resource {
+ int16 id;
+ int16 nameOffset;
+ byte attr;
+ int32 dataOffset;
+ byte *name;
+ };
+
+ typedef Resource *ResPtr;
+
+private:
+ int _resOffset;
+ int32 _dataOffset;
+ int32 _dataLength;
+ int32 _mapOffset;
+ int32 _mapLength;
+ ResMap _resMap;
+ ResType *_resTypes;
+ ResPtr *_resLists;
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/he/script_v100he.cpp b/engines/scumm/he/script_v100he.cpp
new file mode 100644
index 0000000000..c9bfeade67
--- /dev/null
+++ b/engines/scumm/he/script_v100he.cpp
@@ -0,0 +1,2978 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "common/system.h"
+
+#include "scumm/actor.h"
+#include "scumm/charset.h"
+#include "scumm/he/intern_he.h"
+#include "scumm/object.h"
+#include "scumm/resource.h"
+#include "scumm/he/resource_v7he.h"
+#include "scumm/scumm.h"
+#include "scumm/sound.h"
+#include "scumm/he/sprite_he.h"
+#include "scumm/util.h"
+
+namespace Scumm {
+
+#define OPCODE(x) _OPCODE(ScummEngine_v100he, x)
+
+void ScummEngine_v100he::setupOpcodes() {
+ static const OpcodeEntryV100he opcodes[256] = {
+ /* 00 */
+ OPCODE(o100_actorOps),
+ OPCODE(o6_add),
+ OPCODE(o6_faceActor),
+ OPCODE(o90_sortArray),
+ /* 04 */
+ OPCODE(o100_arrayOps),
+ OPCODE(o6_band),
+ OPCODE(o6_bor),
+ OPCODE(o6_breakHere),
+ /* 08 */
+ OPCODE(o6_delayFrames),
+ OPCODE(o90_shl),
+ OPCODE(o90_shr),
+ OPCODE(o90_xor),
+ /* 0C */
+ OPCODE(o6_setCameraAt),
+ OPCODE(o6_actorFollowCamera),
+ OPCODE(o6_loadRoom),
+ OPCODE(o6_panCameraTo),
+ /* 10 */
+ OPCODE(o72_captureWizImage),
+ OPCODE(o100_jumpToScript),
+ OPCODE(o6_setClass),
+ OPCODE(o60_closeFile),
+ /* 14 */
+ OPCODE(o6_loadRoomWithEgo),
+ OPCODE(o6_invalid),
+ OPCODE(o72_setFilePath),
+ OPCODE(o100_createSound),
+ /* 18 */
+ OPCODE(o6_cutscene),
+ OPCODE(o6_pop),
+ OPCODE(o72_traceStatus),
+ OPCODE(o6_wordVarDec),
+ /* 1C */
+ OPCODE(o6_wordArrayDec),
+ OPCODE(o72_deleteFile),
+ OPCODE(o100_dim2dimArray),
+ OPCODE(o100_dimArray),
+ /* 20 */
+ OPCODE(o6_div),
+ OPCODE(o6_animateActor),
+ OPCODE(o6_doSentence),
+ OPCODE(o6_drawBox),
+ /* 24 */
+ OPCODE(o72_drawWizImage),
+ OPCODE(o80_drawWizPolygon),
+ OPCODE(o100_drawLine),
+ OPCODE(o100_drawObject),
+ /* 28 */
+ OPCODE(o6_dup),
+ OPCODE(o90_dup_n),
+ OPCODE(o6_endCutscene),
+ OPCODE(o6_stopObjectCode),
+ /* 2C */
+ OPCODE(o6_stopObjectCode),
+ OPCODE(o6_eq),
+ OPCODE(o100_floodFill),
+ OPCODE(o6_freezeUnfreeze),
+ /* 30 */
+ OPCODE(o6_ge),
+ OPCODE(o6_getDateTime),
+ OPCODE(o100_setSpriteGroupInfo),
+ OPCODE(o6_gt),
+ /* 34 */
+ OPCODE(o100_resourceRoutines),
+ OPCODE(o6_if),
+ OPCODE(o6_ifNot),
+ OPCODE(o100_wizImageOps),
+ /* 38 */
+ OPCODE(o72_isAnyOf),
+ OPCODE(o6_wordVarInc),
+ OPCODE(o6_wordArrayInc),
+ OPCODE(o6_jump),
+ /* 3C */
+ OPCODE(o90_kernelSetFunctions),
+ OPCODE(o6_land),
+ OPCODE(o6_le),
+ OPCODE(o60_localizeArrayToScript),
+ /* 40 */
+ OPCODE(o6_wordArrayRead),
+ OPCODE(o6_wordArrayIndexedRead),
+ OPCODE(o6_lor),
+ OPCODE(o6_lt),
+ /* 44 */
+ OPCODE(o90_mod),
+ OPCODE(o6_mul),
+ OPCODE(o6_neq),
+ OPCODE(o100_dim2dim2Array),
+ /* 48 */
+ OPCODE(o6_setObjectName),
+ OPCODE(o100_redim2dimArray),
+ OPCODE(o6_not),
+ OPCODE(o6_invalid),
+ /* 4C */
+ OPCODE(o6_beginOverride),
+ OPCODE(o6_endOverride),
+ OPCODE(o72_resetCutscene),
+ OPCODE(o6_setOwner),
+ /* 50 */
+ OPCODE(o100_paletteOps),
+ OPCODE(o70_pickupObject),
+ OPCODE(o70_polygonOps),
+ OPCODE(o6_pop),
+ /* 54 */
+ OPCODE(o6_printDebug),
+ OPCODE(o72_printWizImage),
+ OPCODE(o6_printLine),
+ OPCODE(o6_printSystem),
+ /* 58 */
+ OPCODE(o6_printText),
+ OPCODE(o100_jumpToScriptUnk),
+ OPCODE(o100_startScriptUnk),
+ OPCODE(o6_pseudoRoom),
+ /* 5C */
+ OPCODE(o6_pushByte),
+ OPCODE(o72_pushDWord),
+ OPCODE(o72_getScriptString),
+ OPCODE(o6_pushWord),
+ /* 60 */
+ OPCODE(o6_pushWordVar),
+ OPCODE(o6_putActorAtObject),
+ OPCODE(o6_putActorAtXY),
+ OPCODE(o6_invalid),
+ /* 64 */
+ OPCODE(o100_redimArray),
+ OPCODE(o72_rename),
+ OPCODE(o6_stopObjectCode),
+ OPCODE(o80_localizeArrayToRoom),
+ /* 68 */
+ OPCODE(o100_roomOps),
+ OPCODE(o6_printActor),
+ OPCODE(o6_printEgo),
+ OPCODE(o72_talkActor),
+ /* 6C */
+ OPCODE(o72_talkEgo),
+ OPCODE(o6_invalid),
+ OPCODE(o70_seekFilePos),
+ OPCODE(o6_setBoxFlags),
+ /* 70 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_setBoxSet),
+ OPCODE(o100_setSystemMessage),
+ OPCODE(o6_shuffle),
+ /* 74 */
+ OPCODE(o6_delay),
+ OPCODE(o6_delayMinutes),
+ OPCODE(o6_delaySeconds),
+ OPCODE(o100_startSound),
+ /* 78 */
+ OPCODE(o80_sourceDebug),
+ OPCODE(o100_setSpriteInfo),
+ OPCODE(o6_stampObject),
+ OPCODE(o72_startObject),
+ /* 7C */
+ OPCODE(o100_startScript),
+ OPCODE(o6_startScriptQuick),
+ OPCODE(o80_setState),
+ OPCODE(o6_stopObjectScript),
+ /* 80 */
+ OPCODE(o6_stopScript),
+ OPCODE(o6_stopSentence),
+ OPCODE(o6_stopSound),
+ OPCODE(o6_stopTalking),
+ /* 84 */
+ OPCODE(o6_writeWordVar),
+ OPCODE(o6_wordArrayWrite),
+ OPCODE(o6_wordArrayIndexedWrite),
+ OPCODE(o6_sub),
+ /* 88 */
+ OPCODE(o100_systemOps),
+ OPCODE(o6_invalid),
+ OPCODE(o72_setTimer),
+ OPCODE(o100_cursorCommand),
+ /* 8C */
+ OPCODE(o100_videoOps),
+ OPCODE(o100_wait),
+ OPCODE(o6_walkActorToObj),
+ OPCODE(o6_walkActorTo),
+ /* 90 */
+ OPCODE(o100_writeFile),
+ OPCODE(o72_writeINI),
+ OPCODE(o80_writeConfigFile),
+ OPCODE(o6_abs),
+ /* 94 */
+ OPCODE(o6_getActorWalkBox),
+ OPCODE(o6_getActorCostume),
+ OPCODE(o6_getActorElevation),
+ OPCODE(o6_getObjectOldDir),
+ /* 98 */
+ OPCODE(o6_getActorMoving),
+ OPCODE(o90_getActorData),
+ OPCODE(o6_getActorRoom),
+ OPCODE(o6_getActorScaleX),
+ /* 9C */
+ OPCODE(o6_getAnimateVariable),
+ OPCODE(o6_getActorWidth),
+ OPCODE(o6_getObjectX),
+ OPCODE(o6_getObjectY),
+ /* A0 */
+ OPCODE(o90_atan2),
+ OPCODE(o90_getSegmentAngle),
+ OPCODE(o90_getActorAnimProgress),
+ OPCODE(o90_getDistanceBetweenPoints),
+ /* A4 */
+ OPCODE(o6_ifClassOfIs),
+ OPCODE(o6_invalid),
+ OPCODE(o90_cond),
+ OPCODE(o90_cos),
+ /* A8 */
+ OPCODE(o6_invalid),
+ OPCODE(o80_getFileSize),
+ OPCODE(o6_getActorFromXY),
+ OPCODE(o72_findAllObjects),
+ /* AC */
+ OPCODE(o90_findAllObjectsWithClassOf),
+ OPCODE(o6_invalid),
+ OPCODE(o6_findInventory),
+ OPCODE(o72_findObject),
+ /* B0 */
+ OPCODE(o72_findObjectWithClassOf),
+ OPCODE(o70_polygonHit),
+ OPCODE(o90_getLinesIntersectionPoint),
+ OPCODE(o90_fontUnk),
+ /* B4 */
+ OPCODE(o72_getNumFreeArrays),
+ OPCODE(o72_getArrayDimSize),
+ OPCODE(o100_isResourceLoaded),
+ OPCODE(o100_getResourceSize),
+ /* B8 */
+ OPCODE(o100_getSpriteGroupInfo),
+ OPCODE(o6_invalid),
+ OPCODE(o100_getWizData),
+ OPCODE(o6_isActorInBox),
+ /* BC */
+ OPCODE(o6_isAnyOf),
+ OPCODE(o6_getInventoryCount),
+ OPCODE(o90_kernelGetFunctions),
+ OPCODE(o90_max),
+ /* C0 */
+ OPCODE(o90_min),
+ OPCODE(o72_getObjectImageX),
+ OPCODE(o72_getObjectImageY),
+ OPCODE(o6_isRoomScriptRunning),
+ /* C4 */
+ OPCODE(o90_getObjectData),
+ OPCODE(o72_openFile),
+ OPCODE(o90_getPolygonOverlap),
+ OPCODE(o6_getOwner),
+ /* C8 */
+ OPCODE(o100_getPaletteData),
+ OPCODE(o6_pickOneOf),
+ OPCODE(o6_pickOneOfDefault),
+ OPCODE(o80_pickVarRandom),
+ /* CC */
+ OPCODE(o72_getPixel),
+ OPCODE(o6_distObjectObject),
+ OPCODE(o6_distObjectPt),
+ OPCODE(o6_distPtPt),
+ /* D0 */
+ OPCODE(o6_getRandomNumber),
+ OPCODE(o6_getRandomNumberRange),
+ OPCODE(o6_invalid),
+ OPCODE(o100_readFile),
+ /* D4 */
+ OPCODE(o72_readINI),
+ OPCODE(o80_readConfigFile),
+ OPCODE(o6_isScriptRunning),
+ OPCODE(o90_sin),
+ /* D8 */
+ OPCODE(o72_getSoundPosition),
+ OPCODE(o6_isSoundRunning),
+ OPCODE(o80_getSoundVar),
+ OPCODE(o100_getSpriteInfo),
+ /* DC */
+ OPCODE(o90_sqrt),
+ OPCODE(o6_startObjectQuick),
+ OPCODE(o6_startScriptQuick2),
+ OPCODE(o6_getState),
+ /* E0 */
+ OPCODE(o70_compareString),
+ OPCODE(o70_copyString),
+ OPCODE(o70_appendString),
+ OPCODE(o70_concatString),
+ /* E4 */
+ OPCODE(o70_getStringLen),
+ OPCODE(o70_getStringLenForWidth),
+ OPCODE(o80_stringToInt),
+ OPCODE(o70_getCharIndexInString),
+ /* E8 */
+ OPCODE(o70_getStringWidth),
+ OPCODE(o60_readFilePos),
+ OPCODE(o72_getTimer),
+ OPCODE(o6_getVerbEntrypoint),
+ /* EC */
+ OPCODE(o100_getVideoData),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* F0 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* F4 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* F8 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* FC */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ };
+
+ _opcodesV100he = opcodes;
+}
+
+void ScummEngine_v100he::executeOpcode(byte i) {
+ OpcodeProcV100he op = _opcodesV100he[i].proc;
+ (this->*op) ();
+}
+
+const char *ScummEngine_v100he::getOpcodeDesc(byte i) {
+ return _opcodesV100he[i].desc;
+}
+
+void ScummEngine_v100he::o100_actorOps() {
+ Actor *a;
+ int i, j, k;
+ int args[32];
+ byte string[256];
+
+ byte subOp = fetchScriptByte();
+ if (subOp == 129) {
+ _curActor = pop();
+ return;
+ }
+
+ a = derefActorSafe(_curActor, "o100_actorOps");
+ if (!a)
+ return;
+
+ switch (subOp) {
+ case 0:
+ // freddicove Ru Updated
+ // FIXME: check stack parameters
+ debug(0,"o100_actorOps: case 0 UNHANDLED");
+ break;
+ case 3:
+ pop();
+ pop();
+ pop();
+ break;
+ case 4: // SO_ANIMATION_SPEED
+ a->setAnimSpeed(pop());
+ break;
+ case 6:
+ j = pop();
+ i = pop();
+ a->putActor(i, j, a->_room);
+ break;
+ case 8:
+ a->_drawToBackBuf = false;
+ a->_needRedraw = true;
+ a->_needBgReset = true;
+ break;
+ case 9:
+ a->drawActorToBackBuf(a->_pos.x, a->_pos.y);
+ break;
+ case 14:
+ a->_charset = pop();
+ break;
+ case 18:
+ a->_clipOverride.bottom = pop();
+ a->_clipOverride.right = pop();
+ a->_clipOverride.top = pop();
+ a->_clipOverride.left = pop();
+ break;
+ case 22:
+ k = getStackList(args, ARRAYSIZE(args));
+ for (i = 0; i < k; ++i) {
+ a->setUserCondition(args[i] & 0x7F, args[i] & 0x80);
+ }
+ break;
+ case 25: // SO_COSTUME
+ a->setActorCostume(pop());
+ break;
+ case 27: // SO_DEFAULT
+ a->initActor(0);
+ break;
+ case 32:
+ i = pop();
+ debug(0,"o100_actorOps: case 32 (%d)", i);
+ break;
+ case 52: // SO_ACTOR_NAME
+ copyScriptString(string, sizeof(string));
+ loadPtrToResource(rtActorName, a->_number, string);
+ break;
+ case 53: // SO_ACTOR_NEW
+ a->initActor(2);
+ break;
+ case 57: // SO_PALETTE
+ j = pop();
+ i = pop();
+ checkRange(255, 0, i, "o100_actorOps: Illegal palette slot %d");
+ a->remapActorPaletteColor(i, j);
+ a->_needRedraw = true;
+ break;
+ case 59:
+ // HE games use reverse order of layering, so we adjust
+ a->_layer = -pop();
+ a->_needRedraw = true;
+ break;
+ case 63:
+ a->_hePaletteNum = pop();
+ a->_needRedraw = true;
+ break;
+ case 65: // SO_SCALE
+ i = pop();
+ a->setScale(i, i);
+ break;
+ case 70: // SO_SHADOW
+ a->_heXmapNum = pop();
+ a->_needRedraw = true;
+ break;
+ case 74: // SO_STEP_DIST
+ j = pop();
+ i = pop();
+ a->setActorWalkSpeed(i, j);
+ break;
+ case 78:
+ {
+ copyScriptString(string, sizeof(string));
+ int slot = pop();
+
+ int len = resStrLen(string) + 1;
+ memcpy(a->_heTalkQueue[slot].sentence, string, len);
+
+ a->_heTalkQueue[slot].posX = a->_talkPosX;
+ a->_heTalkQueue[slot].posY = a->_talkPosY;
+ a->_heTalkQueue[slot].color = a->_talkColor;
+ }
+ break;
+ case 83: // SO_ACTOR_VARIABLE
+ i = pop();
+ a->setAnimVar(pop(), i);
+ break;
+ case 87: // SO_ALWAYS_ZCLIP
+ a->_forceClip = pop();
+ break;
+ case 89: // SO_NEVER_ZCLIP
+ a->_forceClip = 0;
+ break;
+ case 128:
+ _actorClipOverride.bottom = pop();
+ _actorClipOverride.right = pop();
+ _actorClipOverride.top = pop();
+ _actorClipOverride.left = pop();
+ break;
+ case 130: // SO_SOUND
+ k = getStackList(args, ARRAYSIZE(args));
+ for (i = 0; i < k; i++)
+ a->_sound[i] = args[i];
+ break;
+ case 131: // SO_ACTOR_WIDTH
+ a->_width = pop();
+ break;
+ case 132: // SO_ANIMATION_DEFAULT
+ a->_initFrame = 1;
+ a->_walkFrame = 2;
+ a->_standFrame = 3;
+ a->_talkStartFrame = 4;
+ a->_talkStopFrame = 5;
+ break;
+ case 133: // SO_ELEVATION
+ a->setElevation(pop());
+ break;
+ case 134: // SO_FOLLOW_BOXES
+ a->_ignoreBoxes = 0;
+ a->_forceClip = 0;
+ if (a->isInCurrentRoom())
+ a->putActor(a->_pos.x, a->_pos.y, a->_room);
+ break;
+ case 135: // SO_IGNORE_BOXES
+ a->_ignoreBoxes = 1;
+ a->_forceClip = 0;
+ if (a->isInCurrentRoom())
+ a->putActor(a->_pos.x, a->_pos.y, a->_room);
+ break;
+ case 136: // SO_ACTOR_IGNORE_TURNS_OFF
+ a->_ignoreTurns = false;
+ break;
+ case 137: // SO_ACTOR_IGNORE_TURNS_ON
+ a->_ignoreTurns = true;
+ break;
+ case 138: // SO_INIT_ANIMATION
+ a->_initFrame = pop();
+ break;
+ case 139: // SO_STAND_ANIMATION
+ a->_standFrame = pop();
+ break;
+ case 140: // SO_TALK_ANIMATION
+ a->_talkStopFrame = pop();
+ a->_talkStartFrame = pop();
+ break;
+ case 141: // SO_TALK_COLOR
+ a->_talkColor = pop();
+ break;
+ case 142:
+ k = pop();
+ if (k == 0)
+ k = _rnd.getRandomNumberRng(1, 10);
+ a->_heNoTalkAnimation = 1;
+ a->setTalkCondition(k);
+ break;
+ case 143: // SO_TEXT_OFFSET
+ a->_talkPosY = pop();
+ a->_talkPosX = pop();
+ break;
+ case 144: // SO_WALK_ANIMATION
+ a->_walkFrame = pop();
+ break;
+ default:
+ error("o100_actorOps: default case %d", subOp);
+ }
+}
+
+void ScummEngine_v100he::o100_arrayOps() {
+ ArrayHeader *ah;
+ byte string[1024];
+ int dim1end, dim1start, dim2end, dim2start;
+ int id, len, b, c, list[128];
+ int offs, tmp, tmp2;
+ uint tmp3;
+
+ byte subOp = fetchScriptByte();
+ int array = fetchScriptWord();
+ debug(9,"o100_arrayOps: array %d case %d", array, subOp);
+
+ switch (subOp) {
+ case 35:
+ decodeScriptString(string);
+ len = resStrLen(string);
+ ah = defineArray(array, kStringArray, 0, 0, 0, len);
+ memcpy(ah->data, string, len);
+ break;
+ case 77: // SO_ASSIGN_STRING
+ copyScriptString(string, sizeof(string));
+ len = resStrLen(string);
+ ah = defineArray(array, kStringArray, 0, 0, 0, len);
+ memcpy(ah->data, string, len);
+ break;
+
+ case 128: // SO_ASSIGN_2DIM_LIST
+ len = getStackList(list, ARRAYSIZE(list));
+ id = readVar(array);
+ if (id == 0)
+ error("Must DIM a two dimensional array before assigning");
+ c = pop();
+ while (--len >= 0) {
+ writeArray(array, c, len, list[len]);
+ }
+ break;
+ case 129: // SO_ASSIGN_INT_LIST
+ b = pop();
+ c = pop();
+ id = readVar(array);
+ if (id == 0) {
+ defineArray(array, kDwordArray, 0, 0, 0, b + c - 1);
+ }
+ while (c--) {
+ writeArray(array, 0, b + c, pop());
+ }
+ break;
+ case 130:
+ len = getStackList(list, ARRAYSIZE(list));
+ dim1end = pop();
+ dim1start = pop();
+ dim2end = pop();
+ dim2start = pop();
+ id = readVar(array);
+ if (id == 0) {
+ defineArray(array, kDwordArray, dim2start, dim2end, dim1start, dim1end);
+ }
+ tmp2 = 0;
+ while (dim2start <= dim2end) {
+ tmp = dim1start;
+ while (tmp <= dim1end) {
+ writeArray(array, dim2start, tmp, list[tmp2++]);
+ if (tmp2 == len)
+ tmp2 = 0;
+ tmp++;
+ }
+ dim2start++;
+ }
+ break;
+ case 131:
+ {
+ int a2_dim1end = pop();
+ int a2_dim1start = pop();
+ int a2_dim2end = pop();
+ int a2_dim2start = pop();
+ int array2 = fetchScriptWord();
+ int a1_dim1end = pop();
+ int a1_dim1start = pop();
+ int a1_dim2end = pop();
+ int a1_dim2start = pop();
+ if (a1_dim1end - a1_dim1start != a2_dim1end - a2_dim1start || a2_dim2end - a2_dim2start != a1_dim2end - a1_dim2start) {
+ error("Source and dest ranges size are mismatched");
+ }
+ copyArray(array, a1_dim2start, a1_dim2end, a1_dim1start, a1_dim1end, array2, a2_dim2start, a2_dim2end, a2_dim1start, a2_dim1end);
+ }
+ break;
+ case 133:
+ b = pop();
+ c = pop();
+ dim1end = pop();
+ dim1start = pop();
+ dim2end = pop();
+ dim2start = pop();
+ id = readVar(array);
+ if (id == 0) {
+ defineArray(array, kDwordArray, dim2start, dim2end, dim1start, dim1end);
+ }
+
+ offs = (b >= c) ? 1 : -1;
+ tmp2 = c;
+ tmp3 = c - b + 1;
+ while (dim2start <= dim2end) {
+ tmp = dim1start;
+ while (tmp <= dim1end) {
+ writeArray(array, dim2start, tmp, tmp2);
+ if (--tmp3 == 0) {
+ tmp2 = c;
+ tmp3 = c - b + 1;
+ } else {
+ tmp2 += offs;
+ }
+ tmp++;
+ }
+ dim2start++;
+ }
+ break;
+ default:
+ error("o100_arrayOps: default case %d (array %d)", subOp, array);
+ }
+}
+
+void ScummEngine_v100he::o100_jumpToScript() {
+ int args[25];
+ int script;
+ byte flags;
+
+ getStackList(args, ARRAYSIZE(args));
+ script = pop();
+ flags = fetchScriptByte();
+ stopObjectCode();
+ runScript(script, (flags == 128 || flags == 129), (flags == 130 || flags == 129), args);
+}
+
+void ScummEngine_v100he::o100_createSound() {
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 0:
+ _heSndResId = pop();
+ break;
+ case 53:
+ createSound(_heSndResId, -1);
+ break;
+ case 92:
+ // dummy case
+ break;
+ case 128:
+ createSound(_heSndResId, pop());
+ break;
+ default:
+ error("o100_createSound: default case %d", subOp);
+ }
+}
+
+void ScummEngine_v100he::o100_dim2dimArray() {
+ int data, dim1end, dim2end;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 41: // SO_BIT_ARRAY
+ data = kBitArray;
+ break;
+ case 42: // SO_INT_ARRAY
+ data = kIntArray;
+ break;
+ case 43:
+ data = kDwordArray;
+ break;
+ case 44: // SO_NIBBLE_ARRAY
+ data = kNibbleArray;
+ break;
+ case 45: // SO_BYTE_ARRAY
+ data = kByteArray;
+ break;
+ case 77: // SO_STRING_ARRAY
+ data = kStringArray;
+ break;
+ default:
+ error("o100_dim2dimArray: default case %d", subOp);
+ }
+
+ dim1end = pop();
+ dim2end = pop();
+ defineArray(fetchScriptWord(), data, 0, dim2end, 0, dim1end);
+}
+
+void ScummEngine_v100he::o100_dimArray() {
+ int data;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 41: // SO_BIT_ARRAY
+ data = kBitArray;
+ break;
+ case 42: // SO_INT_ARRAY
+ data = kIntArray;
+ break;
+ case 43:
+ data = kDwordArray;
+ break;
+ case 44: // SO_NIBBLE_ARRAY
+ data = kNibbleArray;
+ break;
+ case 45: // SO_BYTE_ARRAY
+ data = kByteArray;
+ break;
+ case 77: // SO_STRING_ARRAY
+ data = kStringArray;
+ break;
+ case 135: // SO_UNDIM_ARRAY
+ nukeArray(fetchScriptWord());
+ return;
+ default:
+ error("o100_dimArray: default case %d", subOp);
+ }
+
+ defineArray(fetchScriptWord(), data, 0, 0, 0, pop());
+}
+
+void ScummEngine_v100he::o100_drawLine() {
+ int id, unk1, unk2, x, x1, y1;
+
+ unk2 = pop();
+ id = pop();
+ unk1 = pop();
+ x = pop();
+ y1 = pop();
+ x1 = pop();
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 1:
+ drawLine(x1, y1, x, unk1, unk2, 2, id);
+ break;
+ case 20:
+ drawLine(x1, y1, x, unk1, unk2, 1, id);
+ break;
+ case 40:
+ drawLine(x1, y1, x, unk1, unk2, 3, id);
+ break;
+ default:
+ error("o100_drawLine: default case %d", subOp);
+ }
+}
+
+void ScummEngine_v100he::o100_drawObject() {
+ int state, y, x;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 6:
+ state = 1;
+ y = pop();
+ x = pop();
+ break;
+ case 7:
+ state = pop();
+ y = pop();
+ x = pop();
+ break;
+ case 40:
+ state = pop();
+ if (state == 0)
+ state = 1;
+ y = x = -100;
+ break;
+ default:
+ error("o100_drawObject: default case %d", subOp);
+ }
+
+ int object = pop();
+ int objnum = getObjectIndex(object);
+ if (objnum == -1)
+ return;
+
+ if (y != -100 && x != -100) {
+ _objs[objnum].x_pos = x * 8;
+ _objs[objnum].y_pos = y * 8;
+ }
+
+ if (state != -1) {
+ addObjectToDrawQue(objnum);
+ putState(object, state);
+ }
+}
+
+void ScummEngine_v100he::o100_floodFill() {
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 0:
+ memset(&_floodFillParams, 0, sizeof(_floodFillParams));
+ _floodFillParams.box.left = 0;
+ _floodFillParams.box.top = 0;
+ _floodFillParams.box.right = 639;
+ _floodFillParams.box.bottom = 479;
+ break;
+ case 6:
+ _floodFillParams.y = pop();
+ _floodFillParams.x = pop();
+ break;
+ case 18:
+ _floodFillParams.box.bottom = pop();
+ _floodFillParams.box.right = pop();
+ _floodFillParams.box.top = pop();
+ _floodFillParams.box.left = pop();
+ break;
+ case 20:
+ _floodFillParams.flags = pop();
+ break;
+ case 67:
+ pop();
+ break;
+ case 92:
+ floodFill(&_floodFillParams, this);
+ break;
+ default:
+ error("o100_floodFill: Unknown case %d", subOp);
+ }
+}
+
+void ScummEngine_v100he::o100_setSpriteGroupInfo() {
+ byte string[260];
+ int type, value1, value2, value3, value4;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 0:
+ _curSpriteGroupId = pop();
+ break;
+ case 6:
+ value2 = pop();
+ value1 = pop();
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->setGroupPosition(_curSpriteGroupId, value1, value2);
+ break;
+ case 18:
+ value4 = pop();
+ value3 = pop();
+ value2 = pop();
+ value1 = pop();
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->setGroupBounds(_curSpriteGroupId, value1, value2, value3, value4);
+ break;
+ case 38:
+ type = pop() - 1;
+ switch (type) {
+ case 0:
+ value2 = pop();
+ value1 = pop();
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->moveGroupMembers(_curSpriteGroupId, value1, value2);
+ break;
+ case 1:
+ value1 = pop();
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->setGroupMembersPriority(_curSpriteGroupId, value1);
+ break;
+ case 2:
+ value1 = pop();
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->setGroupMembersGroup(_curSpriteGroupId, value1);
+ break;
+ case 3:
+ value1 = pop();
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->setGroupMembersUpdateType(_curSpriteGroupId, value1);
+ break;
+ case 4:
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->setGroupMembersResetSprite(_curSpriteGroupId);
+ break;
+ case 5:
+ value1 = pop();
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->setGroupMembersAnimationSpeed(_curSpriteGroupId, value1);
+ break;
+ case 6:
+ value1 = pop();
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->setGroupMembersAutoAnimFlag(_curSpriteGroupId, value1);
+ break;
+ case 7:
+ value1 = pop();
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->setGroupMembersShadow(_curSpriteGroupId, value1);
+ break;
+ default:
+ error("o100_setSpriteGroupInfo subOp 38: Unknown case %d", subOp);
+ }
+ break;
+ case 40:
+ value1 = pop();
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->setGroupImage(_curSpriteGroupId, value1);
+ break;
+ case 49:
+ value2 = pop();
+ value1 = pop();
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->moveGroup(_curSpriteGroupId, value1, value2);
+ break;
+ case 52:
+ copyScriptString(string, sizeof(string));
+ break;
+ case 53:
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->resetGroup(_curSpriteGroupId);
+ break;
+ case 54:
+ // dummy case
+ pop();
+ pop();
+ break;
+ case 59:
+ value1 = pop();
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->setGroupPriority(_curSpriteGroupId, value1);
+ break;
+ case 60:
+ type = pop();
+ value1 = pop();
+ if (!_curSpriteGroupId)
+ break;
+
+ switch (type) {
+ case 0:
+ _sprite->setGroupXMul(_curSpriteGroupId, value1);
+ break;
+ case 1:
+ _sprite->setGroupXDiv(_curSpriteGroupId, value1);
+ break;
+ case 2:
+ _sprite->setGroupYMul(_curSpriteGroupId, value1);
+ break;
+ case 3:
+ _sprite->setGroupYDiv(_curSpriteGroupId, value1);
+ break;
+ default:
+ error("o100_setSpriteGroupInfo subOp 60: Unknown case %d", subOp);
+ }
+ break;
+ case 89:
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->resetGroupBounds(_curSpriteGroupId);
+ break;
+ default:
+ error("o100_setSpriteGroupInfo: Unknown case %d", subOp);
+ }
+}
+
+void ScummEngine_v100he::o100_resourceRoutines() {
+ int objidx, room;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 14:
+ _heResType = rtCharset;
+ _heResId = pop();
+ break;
+ case 25:
+ _heResType = rtCostume;
+ _heResId = pop();
+ break;
+ case 34:
+ _heResType = rtFlObject;
+ _heResId = pop();
+ break;
+ case 40:
+ _heResType = rtImage;
+ _heResId = pop();
+ break;
+ case 47:
+ if (_heResType == rtFlObject) {
+ room = getObjectRoom(_heResId);
+ loadFlObject(_heResId, room);
+ } else if (_heResType == rtCharset) {
+ loadCharset(_heResId);
+ } else {
+ ensureResourceLoaded(_heResType, _heResId);
+ }
+ break;
+ case 62:
+ _heResType = rtRoom;
+ _heResId = pop();
+ break;
+ case 66:
+ _heResType = rtScript;
+ _heResId = pop();
+ break;
+ case 72:
+ _heResType = rtSound;
+ _heResId = pop();
+ break;
+ case 128:
+ break;
+ case 132:
+ if (_heResType == rtScript && _heResId >= _numGlobalScripts)
+ break;
+
+ if (_heResType == rtFlObject) {
+ objidx = getObjectIndex(_heResId);
+ if (objidx == -1)
+ break;
+ res.lock(rtFlObject, _objs[objidx].fl_object_index);
+ } else {
+ res.lock(_heResType, _heResId);
+ }
+ break;
+ case 133:
+ if (_heResType == rtCharset)
+ nukeCharset(_heResId);
+ else
+ res.nukeResource(_heResType, _heResId);
+ break;
+ case 134:
+ case 135:
+ // Heap related
+ break;
+ case 136:
+ if (_heResType == rtScript && _heResId >= _numGlobalScripts)
+ break;
+
+ //queueLoadResource(_heResType, _heResId);
+ break;
+ case 137:
+ if (_heResType == rtScript && _heResId >= _numGlobalScripts)
+ break;
+
+ if (_heResType == rtFlObject) {
+ objidx = getObjectIndex(_heResId);
+ if (objidx == -1)
+ break;
+ res.unlock(rtFlObject, _objs[objidx].fl_object_index);
+ } else {
+ res.unlock(_heResType, _heResId);
+ }
+ break;
+ default:
+ error("o100_resourceRoutines: default case %d", subOp);
+ }
+}
+
+void ScummEngine_v100he::o100_wizImageOps() {
+ int a, b;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 0:
+ _wizParams.img.resNum = pop();
+ _wizParams.processMode = 0;
+ _wizParams.processFlags = 0;
+ _wizParams.remapNum = 0;
+ _wizParams.img.flags = 0;
+ _wizParams.field_184 = 0;
+ _wizParams.field_180 = 0;
+ _wizParams.spriteId = 0;
+ _wizParams.spriteGroup = 0;
+ break;
+ case 2:
+ _wizParams.processFlags |= kWPFRotate;
+ _wizParams.angle = pop();
+ break;
+ case 6:
+ case 132:
+ _wizParams.processFlags |= kWPFSetPos;
+ _wizParams.img.y1 = pop();
+ _wizParams.img.x1 = pop();
+ break;
+ case 7:
+ _wizParams.processFlags |= kWPFMaskImg;
+ _wizParams.sourceImage = pop();
+ break;
+ case 11:
+ _wizParams.processFlags |= kWPFClipBox | 0x100;
+ _wizParams.processMode = 2;
+ _wizParams.box.bottom = pop();
+ _wizParams.box.right = pop();
+ _wizParams.box.top = pop();
+ _wizParams.box.left = pop();
+ _wizParams.compType = pop();
+ break;
+ case 18:
+ _wizParams.processFlags |= kWPFClipBox;
+ _wizParams.box.bottom = pop();
+ _wizParams.box.right = pop();
+ _wizParams.box.top = pop();
+ _wizParams.box.left = pop();
+ break;
+ case 21:
+ b = pop();
+ a = pop();
+ _wizParams.processFlags |= kWPFRemapPalette;
+ _wizParams.processMode = 6;
+ if (_wizParams.remapNum == 0) {
+ memset(_wizParams.remapIndex, 0, sizeof(_wizParams.remapIndex));
+ } else {
+ assert(_wizParams.remapNum < ARRAYSIZE(_wizParams.remapIndex));
+ _wizParams.remapIndex[_wizParams.remapNum] = a;
+ _wizParams.remapColor[a] = b;
+ ++_wizParams.remapNum;
+ }
+ break;
+ case 29:
+ _wizParams.processMode = 1;
+ break;
+ case 36:
+ _wizParams.box.bottom = pop();
+ _wizParams.box.right = pop();
+ _wizParams.box.top = pop();
+ _wizParams.box.left = pop();
+ break;
+ case 37:
+ // Dummy case
+ pop();
+ break;
+ case 39:
+ _wizParams.processFlags |= kWPFUseDefImgHeight;
+ _wizParams.resDefImgH = pop();
+ break;
+ case 47:
+ _wizParams.processFlags |= kWPFUseFile;
+ _wizParams.processMode = 3;
+ copyScriptString(_wizParams.filename, sizeof(_wizParams.filename));
+ break;
+ case 53:
+ _wizParams.processMode = 8;
+ break;
+ case 54:
+ _wizParams.processFlags |= 0x100000;
+ _wizParams.field_180 = pop();
+ _wizParams.field_184 = pop();
+ break;
+ case 55:
+ _wizParams.img.flags = pop();
+ _wizParams.img.state = pop();
+ _wizParams.img.y1 = pop();
+ _wizParams.img.x1 = pop();
+ _wizParams.spriteId = 0;
+ _wizParams.spriteGroup = 0;
+ _wizParams.img.resNum = pop();
+ _wiz->displayWizImage(&_wizParams.img);
+ break;
+ case 57:
+ _wizParams.processFlags |= kWPFPaletteNum;
+ _wizParams.img.palette = pop();
+ break;
+ case 58:
+ _wizParams.processFlags |= 0x1000 | 0x100 | 0x2;
+ _wizParams.processMode = 7;
+ _wizParams.field_168 = pop();
+ _wizParams.field_164 = pop();
+ _wizParams.compType = pop();
+ break;
+ case 64:
+ _wizParams.processFlags |= kWPFUseFile;
+ _wizParams.processMode = 4;
+ copyScriptString(_wizParams.filename, sizeof(_wizParams.filename));
+ _wizParams.fileWriteMode = pop();
+ break;
+ case 65:
+ _wizParams.processFlags |= kWPFScaled;
+ _wizParams.scale = pop();
+ break;
+ case 67:
+ _wizParams.processFlags |= kWPFNewFlags;
+ _wizParams.img.flags |= pop();
+ break;
+ case 68:
+ _wizParams.processFlags |= kWPFNewFlags | kWPFSetPos | 2;
+ _wizParams.img.flags |= kWIFIsPolygon;
+ _wizParams.field_164 = _wizParams.img.y1 = _wizParams.img.x1 = pop();
+ break;
+ case 70:
+ _wizParams.processFlags |= kWPFShadow;
+ _wizParams.img.shadow = pop();
+ break;
+ case 73:
+ _wizParams.processFlags |= kWPFNewState;
+ _wizParams.img.state = pop();
+ break;
+ case 84:
+ _wizParams.processFlags |= kWPFUseDefImgWidth;
+ _wizParams.resDefImgW = pop();
+ break;
+ case 92:
+ if (_wizParams.img.resNum)
+ _wiz->processWizImage(&_wizParams);
+ break;
+ case 128:
+ _wizParams.field_239D = pop();
+ _wizParams.field_2399 = pop();
+ _wizParams.field_23A5 = pop();
+ _wizParams.field_23A1 = pop();
+ copyScriptString(_wizParams.string2, sizeof(_wizParams.string2));
+ _wizParams.processMode = 15;
+ break;
+ case 129:
+ _wizParams.processMode = 14;
+ break;
+ case 130:
+ _wizParams.processMode = 16;
+ _wizParams.field_23AD = pop();
+ _wizParams.field_23A9 = pop();
+ copyScriptString(_wizParams.string1, sizeof(_wizParams.string1));
+ break;
+ case 131:
+ _wizParams.processMode = 13;
+ break;
+ case 133:
+ _wizParams.processMode = 17;
+ _wizParams.field_23CD = pop();
+ _wizParams.field_23C9 = pop();
+ _wizParams.field_23C5 = pop();
+ _wizParams.field_23C1 = pop();
+ _wizParams.field_23BD = pop();
+ _wizParams.field_23B9 = pop();
+ _wizParams.field_23B5 = pop();
+ _wizParams.field_23B1 = pop();
+ break;
+ case 134:
+ _wizParams.processFlags |= kWPFFillColor | kWPFClipBox2;
+ _wizParams.processMode = 12;
+ _wizParams.fillColor = pop();
+ _wizParams.box2.top = _wizParams.box2.bottom = pop();
+ _wizParams.box2.left = _wizParams.box2.right = pop();
+ break;
+ case 135:
+ _wizParams.processFlags |= kWPFDstResNum;
+ _wizParams.dstResNum = pop();
+ break;
+ case 136:
+ _wizParams.processFlags |= kWPFFillColor | kWPFClipBox2;
+ _wizParams.processMode = 10;
+ _wizParams.fillColor = pop();
+ _wizParams.box2.bottom = pop();
+ _wizParams.box2.right = pop();
+ _wizParams.box2.top = pop();
+ _wizParams.box2.left = pop();
+ break;
+ case 137:
+ _wizParams.processFlags |= kWPFFillColor | kWPFClipBox2;
+ _wizParams.processMode = 11;
+ _wizParams.fillColor = pop();
+ _wizParams.box2.top = _wizParams.box2.bottom = pop();
+ _wizParams.box2.left = _wizParams.box2.right = pop();
+ break;
+ case 138:
+ _wizParams.processFlags |= kWPFFillColor | kWPFClipBox2;
+ _wizParams.processMode = 9;
+ _wizParams.fillColor = pop();
+ _wizParams.box2.bottom = pop();
+ _wizParams.box2.right = pop();
+ _wizParams.box2.top = pop();
+ _wizParams.box2.left = pop();
+ break;
+ default:
+ error("o100_wizImageOps: Unknown case %d", subOp);
+ }
+}
+
+void ScummEngine_v100he::o100_dim2dim2Array() {
+ int data, dim1start, dim1end, dim2start, dim2end;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 41: // SO_BIT_ARRAY
+ data = kBitArray;
+ break;
+ case 42: // SO_INT_ARRAY
+ data = kIntArray;
+ break;
+ case 43:
+ data = kDwordArray;
+ break;
+ case 44: // SO_NIBBLE_ARRAY
+ data = kNibbleArray;
+ break;
+ case 45: // SO_BYTE_ARRAY
+ data = kByteArray;
+ break;
+ case 77: // SO_STRING_ARRAY
+ data = kStringArray;
+ break;
+ default:
+ error("o100_dim2dim2Array: default case %d", subOp);
+ }
+
+ if (pop() == 2) {
+ dim1end = pop();
+ dim1start = pop();
+ dim2end = pop();
+ dim2start = pop();
+ } else {
+ dim2end = pop();
+ dim2start = pop();
+ dim1end = pop();
+ dim1start = pop();
+ }
+
+ defineArray(fetchScriptWord(), data, dim2start, dim2end, dim1start, dim1end);
+}
+
+void ScummEngine_v100he::o100_redim2dimArray() {
+ int a, b, c, d;
+ d = pop();
+ c = pop();
+ b = pop();
+ a = pop();
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 42:
+ redimArray(fetchScriptWord(), a, b, c, d, kIntArray);
+ break;
+ case 43:
+ redimArray(fetchScriptWord(), a, b, c, d, kDwordArray);
+ break;
+ case 45:
+ redimArray(fetchScriptWord(), a, b, c, d, kByteArray);
+ break;
+ default:
+ error("o100_redim2dimArray: default type %d", subOp);
+ }
+}
+
+void ScummEngine_v100he::o100_paletteOps() {
+ int a, b, c, d, e;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 0:
+ _hePaletteNum = pop();
+ break;
+ case 20:
+ e = pop();
+ d = pop();
+ c = pop();
+ b = pop();
+ a = pop();
+ if (_hePaletteNum != 0) {
+ for (; a <= b; ++a) {
+ setHEPaletteColor(_hePaletteNum, a, c, d, e);
+ }
+ }
+ break;
+ case 25:
+ a = pop();
+ if (_hePaletteNum != 0) {
+ setHEPaletteFromCostume(_hePaletteNum, a);
+ }
+ break;
+ case 40:
+ b = pop();
+ a = pop();
+ if (_hePaletteNum != 0) {
+ setHEPaletteFromImage(_hePaletteNum, a, b);
+ }
+ break;
+ case 53:
+ if (_hePaletteNum != 0) {
+ restoreHEPalette(_hePaletteNum);
+ }
+ break;
+ case 57:
+ a = pop();
+ if (_hePaletteNum != 0) {
+ copyHEPalette(_hePaletteNum, a);
+ }
+ break;
+ case 63:
+ b = pop();
+ a = pop();
+ if (_hePaletteNum != 0) {
+ setHEPaletteFromRoom(_hePaletteNum, a, b);
+ }
+ break;
+ case 81:
+ c = pop();
+ b = pop();
+ a = pop();
+ if (_hePaletteNum) {
+ for (; a <= b; ++a) {
+ copyHEPaletteColor(_hePaletteNum, a, c);
+ }
+ }
+ break;
+ case 92:
+ _hePaletteNum = 0;
+ break;
+ default:
+ error("o100_paletteOps: Unknown case %d", subOp);
+ }
+}
+
+void ScummEngine_v100he::o100_jumpToScriptUnk() {
+ int args[25];
+ int script, cycle;
+ byte flags;
+
+ getStackList(args, ARRAYSIZE(args));
+ cycle = pop();
+ script = pop();
+ flags = fetchScriptByte();
+ stopObjectCode();
+ runScript(script, (flags == 128 || flags == 129), (flags == 130 || flags == 129), args, cycle);
+}
+
+void ScummEngine_v100he::o100_startScriptUnk() {
+ int args[25];
+ int script, cycle;
+ byte flags;
+
+ getStackList(args, ARRAYSIZE(args));
+ cycle = pop();
+ script = pop();
+ flags = fetchScriptByte();
+ runScript(script, (flags == 128 || flags == 129), (flags == 130 || flags == 129), args, cycle);
+}
+
+void ScummEngine_v100he::o100_redimArray() {
+ int newX, newY;
+ newY = pop();
+ newX = pop();
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 42:
+ redimArray(fetchScriptWord(), 0, newX, 0, newY, kIntArray);
+ break;
+ case 43:
+ redimArray(fetchScriptWord(), 0, newX, 0, newY, kDwordArray);
+ break;
+ case 45:
+ redimArray(fetchScriptWord(), 0, newX, 0, newY, kByteArray);
+ break;
+ default:
+ error("o100_redimArray: default type %d", subOp);
+ }
+}
+
+void ScummEngine_v100he::o100_roomOps() {
+ int a, b, c, d, e;
+ byte filename[100];
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 63: // SO_ROOM_PALETTE
+ d = pop();
+ c = pop();
+ b = pop();
+ a = pop();
+ setPalColor(d, a, b, c);
+ break;
+
+ case 129:
+ b = pop();
+ a = pop();
+ swapObjects(a, b);
+ break;
+
+ case 130:
+ a = pop();
+ b = pop();
+ copyPalColor(a, b);
+ break;
+
+ case 131: // SO_ROOM_FADE
+ // Defaults to 1 but doesn't use fade effects
+ a = pop();
+ break;
+
+ case 132: // SO_ROOM_INTENSITY
+ c = pop();
+ b = pop();
+ a = pop();
+ darkenPalette(a, a, a, b, c);
+ break;
+
+ case 133: // SO_RGB_ROOM_INTENSITY
+ e = pop();
+ d = pop();
+ c = pop();
+ b = pop();
+ a = pop();
+ darkenPalette(a, b, c, d, e);
+ break;
+
+ case 134: // SO_ROOM_NEW_PALETTE
+ a = pop();
+ setPalette(a);
+ break;
+
+ case 135:
+ b = pop();
+ a = pop();
+ setRoomPalette(a, b);
+ break;
+
+ case 136: // SO_ROOM_SAVEGAME
+ _saveTemporaryState = true;
+ _saveLoadSlot = pop();
+ _saveLoadFlag = pop();
+ break;
+
+ case 137:
+ copyScriptString(filename, sizeof(filename));
+ _saveLoadFlag = pop();
+ _saveLoadSlot = 1;
+ _saveTemporaryState = true;
+ break;
+
+ case 138: // SO_ROOM_SCREEN
+ b = pop();
+ a = pop();
+ initScreens(a, _screenHeight);
+ break;
+
+ case 139: // SO_ROOM_SCROLL
+ b = pop();
+ a = pop();
+ if (a < (_screenWidth / 2))
+ a = (_screenWidth / 2);
+ if (b < (_screenWidth / 2))
+ b = (_screenWidth / 2);
+ if (a > _roomWidth - (_screenWidth / 2))
+ a = _roomWidth - (_screenWidth / 2);
+ if (b > _roomWidth - (_screenWidth / 2))
+ b = _roomWidth - (_screenWidth / 2);
+ VAR(VAR_CAMERA_MIN_X) = a;
+ VAR(VAR_CAMERA_MAX_X) = b;
+ break;
+
+ default:
+ error("o100_roomOps: default case %d", subOp);
+ }
+}
+
+void ScummEngine_v100he::o100_setSystemMessage() {
+ byte name[1024];
+
+ copyScriptString(name, sizeof(name));
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 80: // Set Window Caption
+ _system->setWindowCaption((const char *)name);
+ break;
+ case 131: // Set Version
+ debug(1,"o100_setSystemMessage: (%d) %s", subOp, name);
+ break;
+ default:
+ error("o100_setSystemMessage: default case %d", subOp);
+ }
+}
+
+void ScummEngine_v100he::o100_startSound() {
+ byte filename[260];
+ int var, value;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 6:
+ _heSndFlags |= 16;
+ _heSndOffset = pop();
+ break;
+ case 47:
+ copyScriptString(filename, sizeof(filename));
+ _heSndSoundId = pop();
+ if (_heSndSoundId)
+ debug(0, "Load sound %d from file %s\n", _heSndSoundId, filename);
+ break;
+ case 55:
+ _heSndFlags |= 8;
+ break;
+ case 83:
+ value = pop();
+ var = pop();
+ _heSndSoundId = pop();
+ _sound->setSoundVar(_heSndSoundId, var, value);
+ break;
+ case 92:
+ _sound->addSoundToQueue(_heSndSoundId, _heSndOffset, _heSndChannel, _heSndFlags);
+ break;
+ case 128:
+ _heSndFlags |= 2;
+ break;
+ case 129:
+ _heSndChannel = pop();
+ break;
+ case 130:
+ _heSndFlags |= 64;
+ pop();
+ break;
+ case 131:
+ _heSndFlags |= 1;
+ break;
+ case 132: // Music
+ case 134: // Sound
+ _heSndSoundId = pop();
+ _heSndOffset = 0;
+ _heSndSoundFreq = 11025;
+ _heSndChannel = VAR(VAR_SOUND_CHANNEL);
+ _heSndFlags = 0;
+ break;
+ case 133:
+ _heSndFlags |= 128;
+ pop();
+ break;
+ case 135:
+ _heSndFlags |= 4;
+ break;
+ case 136:
+ _heSndFlags |= 32;
+ pop();
+ break;
+ default:
+ error("o100_startSound invalid case %d", subOp);
+ }
+}
+
+void ScummEngine_v100he::o100_setSpriteInfo() {
+ int args[16];
+ int spriteId, n;
+ int32 tmp[2];
+ byte string[80];
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 0:
+ _curMaxSpriteId = pop();
+ _curSpriteId = pop();
+
+ if (_curSpriteId > _curMaxSpriteId)
+ SWAP(_curSpriteId, _curMaxSpriteId);
+ break;
+ case 2:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteAngle(spriteId, args[0]);
+ break;
+ case 3:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteFlagAutoAnim(spriteId, args[0]);
+ break;
+ case 4:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteAnimSpeed(spriteId, args[0]);
+ break;
+ case 6:
+ args[1] = pop();
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpritePosition(spriteId, args[0], args[1]);
+ break;
+ case 7:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteSourceImage(spriteId, args[0]);
+ break;
+ case 16:
+ n = getStackList(args, ARRAYSIZE(args));
+ if (_curSpriteId != 0 && _curMaxSpriteId != 0 && n != 0) {
+ int *p = &args[n - 1];
+ do {
+ int code = *p;
+ if (code == 0) {
+ for (int i = _curSpriteId; i <= _curMaxSpriteId; ++i) {
+ _sprite->setSpriteResetClass(i);
+ }
+ } else if (code & 0x80) {
+ for (int i = _curSpriteId; i <= _curMaxSpriteId; ++i) {
+ _sprite->setSpriteSetClass(i, code & 0x7F, 1);
+ }
+ } else {
+ for (int i = _curSpriteId; i <= _curMaxSpriteId; ++i) {
+ _sprite->setSpriteSetClass(i, code & 0x7F, 0);
+ }
+ }
+ --p;
+ } while (--n);
+ }
+ break;
+ case 32:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteFlagEraseType(spriteId, args[0]);
+ break;
+ case 38:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteGroup(spriteId, args[0]);
+ break;
+ case 40:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteImage(spriteId, args[0]);
+ break;
+ case 48:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteMaskImage(spriteId, args[0]);
+ break;
+ case 49:
+ args[1] = pop();
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->moveSprite(spriteId, args[0], args[1]);
+ break;
+ case 52:
+ copyScriptString(string, sizeof(string));
+ break;
+ case 53:
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->resetSprite(spriteId);
+ break;
+ case 54:
+ args[1] = pop();
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteGeneralProperty(spriteId, args[0], args[1]);
+ break;
+ case 57:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpritePalette(spriteId, args[0]);
+ break;
+ case 59:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpritePriority(spriteId, args[0]);
+ break;
+ case 60:
+ args[1] = pop();
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ switch(args[1]) {
+ case 0:
+ _sprite->setSpriteFlagXFlipped(spriteId, args[0]);
+ break;
+ case 1:
+ _sprite->setSpriteFlagYFlipped(spriteId, args[0]);
+ break;
+ case 2:
+ _sprite->setSpriteFlagActive(spriteId, args[0]);
+ break;
+ case 3:
+ _sprite->setSpriteFlagDoubleBuffered(spriteId, args[0]);
+ break;
+ case 4:
+ _sprite->setSpriteFlagRemapPalette(spriteId, args[0]);
+ break;
+ default:
+ break;
+ }
+ break;
+ case 61:
+ _sprite->resetTables(true);
+ break;
+ case 65:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteScale(spriteId, args[0]);
+ break;
+ case 70:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteShadow(spriteId, args[0]);
+ break;
+ case 73:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteImageState(spriteId, args[0]);
+ break;
+ case 74:
+ args[1] = pop();
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteDist(spriteId, args[0], args[1]);
+ break;
+ case 75:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++) {
+ _sprite->getSpriteDist(spriteId, tmp[0], tmp[1]);
+ _sprite->setSpriteDist(spriteId, args[0], tmp[1]);
+ }
+ break;
+ case 76:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++) {
+ _sprite->getSpriteDist(spriteId, tmp[0], tmp[1]);
+ _sprite->setSpriteDist(spriteId, tmp[0], args[0]);
+ }
+ break;
+ case 82:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteFlagUpdateType(spriteId, args[0]);
+ break;
+ case 83:
+ args[1] = pop();
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteUserValue(spriteId, args[0], args[1]);
+ break;
+ case 88:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteField84(spriteId, args[0]);
+ break;
+ case 89:
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteField84(spriteId, 0);
+ break;
+ default:
+ error("o100_setSpriteInfo: Unknown case %d", subOp);
+ }
+}
+
+void ScummEngine_v100he::o100_startScript() {
+ int args[25];
+ int script;
+ byte flags;
+
+ getStackList(args, ARRAYSIZE(args));
+ script = pop();
+ flags = fetchScriptByte();
+ runScript(script, (flags == 128 || flags == 129), (flags == 130 || flags == 129), args);
+}
+
+void ScummEngine_v100he::o100_systemOps() {
+ byte string[1024];
+
+ byte subOp = fetchScriptByte();
+ subOp -= 61;
+
+ switch (subOp) {
+ case 0:
+ restart();
+ break;
+ case 67:
+ clearDrawObjectQueue();
+ break;
+ case 71:
+ // Confirm shutdown
+ shutDown();
+ break;
+ case 72:
+ shutDown();
+ break;
+ case 73:
+ copyScriptString(string, sizeof(string));
+ debug(0, "Start game (%s)", string);
+ break;
+ case 74:
+ copyScriptString(string, sizeof(string));
+ debug(0, "Start executable (%s)", string);
+ break;
+ case 75:
+ gdi.copyVirtScreenBuffers(Common::Rect(_screenWidth, _screenHeight));
+ updatePalette();
+ break;
+ default:
+ error("o100_systemOps invalid case %d", subOp);
+ }
+}
+
+void ScummEngine_v100he::o100_cursorCommand() {
+ int a, i;
+ int args[16];
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 0xE: // SO_CHARSET_SET
+ initCharset(pop());
+ break;
+ case 0xF: // SO_CHARSET_COLOR
+ getStackList(args, ARRAYSIZE(args));
+ for (i = 0; i < 16; i++)
+ _charsetColorMap[i] = _charsetData[_string[1]._default.charset][i] = (unsigned char)args[i];
+ break;
+ case 0x80:
+ case 0x81:
+ a = pop();
+ _wiz->loadWizCursor(a);
+ break;
+ case 0x82:
+ pop();
+ a = pop();
+ _wiz->loadWizCursor(a);
+ break;
+ case 0x86: // SO_CURSOR_ON Turn cursor on
+ _cursor.state = 1;
+ break;
+ case 0x87: // SO_CURSOR_OFF Turn cursor off
+ _cursor.state = 0;
+ break;
+ case 0x88: // SO_CURSOR_SOFT_ON Turn soft cursor on
+ _cursor.state++;
+ if (_cursor.state > 1)
+ error("o100_cursorCommand: Cursor state greater than 1 in script");
+ break;
+
+ case 0x89: // SO_CURSOR_SOFT_OFF Turn soft cursor off
+ _cursor.state--;
+ break;
+ case 0x8B: // SO_USERPUT_ON
+ _userPut = 1;
+ break;
+ case 0x8C: // SO_USERPUT_OFF
+ _userPut = 0;
+ break;
+ case 0x8D: // SO_USERPUT_SOFT_ON
+ _userPut++;
+ break;
+ case 0x8E: // SO_USERPUT_SOFT_OFF
+ _userPut--;
+ break;
+ default:
+ error("o100_cursorCommand: default case %x", subOp);
+ }
+
+ VAR(VAR_CURSORSTATE) = _cursor.state;
+ VAR(VAR_USERPUT) = _userPut;
+}
+
+void ScummEngine_v100he::o100_videoOps() {
+ // Uses Bink video
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 0:
+ memset(_videoParams.filename, 0, sizeof(_videoParams.filename));
+ _videoParams.unk2 = pop();
+ break;
+ case 19:
+ _videoParams.status = 19;
+ break;
+ case 40:
+ _videoParams.wizResNum = pop();
+ if (_videoParams.wizResNum)
+ _videoParams.flags |= 2;
+ break;
+ case 47:
+ copyScriptString(_videoParams.filename, sizeof(_videoParams.filename));
+ _videoParams.status = 47;
+ break;
+ case 67:
+ _videoParams.flags |= pop();
+ break;
+ case 92:
+ if (_videoParams.status == 47) {
+ // Start video
+ if (_videoParams.flags == 0)
+ _videoParams.flags = 4;
+
+ if (_videoParams.flags == 2) {
+ // result = startVideo(_videoParams.filename, _videoParams.flags, _videoParams.wizResNum);
+ // VAR(119) = result;
+ } else {
+ // result = startVideo(_videoParams.filename, _videoParams.flags);
+ // VAR(119) = result;
+ }
+ } else if (_videoParams.status == 19) {
+ // Stop video
+ }
+ break;
+ default:
+ error("o100_videoOps: unhandled case %d", subOp);
+ }
+
+ debug(1,"o100_videoOps stub (%d)", subOp);
+}
+
+void ScummEngine_v100he::o100_wait() {
+ int actnum;
+ int offs = -2;
+ Actor *a;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 128: // SO_WAIT_FOR_ACTOR Wait for actor
+ offs = fetchScriptWordSigned();
+ actnum = pop();
+ a = derefActor(actnum, "o100_wait:168");
+ if (a->_moving)
+ break;
+ return;
+ case 129: // SO_WAIT_FOR_CAMERA Wait for camera
+ if (camera._cur.x / 8 != camera._dest.x / 8)
+ break;
+ return;
+ case 130: // SO_WAIT_FOR_MESSAGE Wait for message
+ if (VAR(VAR_HAVE_MSG))
+ break;
+ return;
+ case 131: // SO_WAIT_FOR_SENTENCE
+ if (_sentenceNum) {
+ if (_sentence[_sentenceNum - 1].freezeCount && !isScriptInUse(VAR(VAR_SENTENCE_SCRIPT)))
+ return;
+ break;
+ }
+ if (!isScriptInUse(VAR(VAR_SENTENCE_SCRIPT)))
+ return;
+ break;
+ default:
+ error("o100_wait: default case 0x%x", subOp);
+ }
+
+ _scriptPointer += offs;
+ o6_breakHere();
+}
+
+void ScummEngine_v100he::o100_writeFile() {
+ int32 resID = pop();
+ int slot = pop();
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 5:
+ fetchScriptByte();
+ writeFileFromArray(slot, resID);
+ break;
+ case 42:
+ _hFileTable[slot].writeUint16LE(resID);
+ break;
+ case 43:
+ _hFileTable[slot].writeUint32LE(resID);
+ break;
+ case 45:
+ _hFileTable[slot].writeByte(resID);
+ break;
+ default:
+ error("o100_writeFile: default case %d", subOp);
+ }
+}
+
+void ScummEngine_v100he::o100_isResourceLoaded() {
+ // Reports percentage of resource loaded by queue
+ int type;
+
+ byte subOp = fetchScriptByte();
+ /* int idx = */ pop();
+
+ switch (subOp) {
+ case 25:
+ type = rtCostume;
+ break;
+ case 40:
+ type = rtImage;
+ break;
+ case 62:
+ type = rtRoom;
+ break;
+ case 66:
+ type = rtScript;
+ break;
+ case 72:
+ type = rtSound;
+ break;
+ default:
+ error("o100_isResourceLoaded: default case %d", subOp);
+ }
+
+ push(100);
+}
+
+void ScummEngine_v100he::o100_getResourceSize() {
+ const byte *ptr;
+ int size, type;
+
+ int resid = pop();
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 25:
+ type = rtCostume;
+ break;
+ case 40:
+ type = rtImage;
+ break;
+ case 62:
+ type = rtRoomImage;
+ break;
+ case 66:
+ type = rtScript;
+ break;
+ case 72:
+ push (getSoundResourceSize(resid));
+ return;
+ default:
+ error("o100_getResourceSize: default type %d", subOp);
+ }
+
+ ptr = getResourceAddress(type, resid);
+ assert(ptr);
+ size = READ_BE_UINT32(ptr + 4) - 8;
+ push(size);
+}
+
+void ScummEngine_v100he::o100_getSpriteGroupInfo() {
+ int32 tx, ty;
+ int spriteGroupId, type;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 5:
+ spriteGroupId = pop();
+ if (spriteGroupId)
+ push(getGroupSpriteArray(spriteGroupId));
+ else
+ push(0);
+ break;
+ case 40:
+ spriteGroupId = pop();
+ if (spriteGroupId)
+ push(_sprite->getGroupDstResNum(spriteGroupId));
+ else
+ push(0);
+ break;
+ case 54:
+ // TODO: U32 related
+ pop();
+ pop();
+ push(0);
+ break;
+ case 59:
+ spriteGroupId = pop();
+ if (spriteGroupId)
+ push(_sprite->getGroupPriority(spriteGroupId));
+ else
+ push(0);
+ break;
+ case 60:
+ type = pop();
+ spriteGroupId = pop();
+ if (spriteGroupId) {
+ switch(type) {
+ case 0:
+ push(_sprite->getGroupXMul(spriteGroupId));
+ break;
+ case 1:
+ push(_sprite->getGroupXDiv(spriteGroupId));
+ break;
+ case 2:
+ push(_sprite->getGroupYMul(spriteGroupId));
+ break;
+ case 3:
+ push(_sprite->getGroupYDiv(spriteGroupId));
+ break;
+ default:
+ push(0);
+ }
+ } else {
+ push(0);
+ }
+ break;
+ case 85:
+ spriteGroupId = pop();
+ if (spriteGroupId) {
+ _sprite->getGroupPosition(spriteGroupId, tx, ty);
+ push(tx);
+ } else {
+ push(0);
+ }
+ break;
+ case 86:
+ spriteGroupId = pop();
+ if (spriteGroupId) {
+ _sprite->getGroupPosition(spriteGroupId, tx, ty);
+ push(ty);
+ } else {
+ push(0);
+ }
+ break;
+ default:
+ error("o100_getSpriteGroupInfo: Unknown case %d", subOp);
+ }
+}
+
+void ScummEngine_v100he::o100_getWizData() {
+ byte filename[4096];
+ int resId, state, type;
+ int32 w, h;
+ int32 x, y;
+
+ byte subOp = fetchScriptByte();
+ subOp -= 20;
+
+ switch (subOp) {
+ case 0:
+ y = pop();
+ x = pop();
+ state = pop();
+ resId = pop();
+ push(_wiz->getWizPixelColor(resId, state, x, y, 0));
+ break;
+ case 6:
+ resId = pop();
+ push(_wiz->getWizImageStates(resId));
+ break;
+ case 13:
+ y = pop();
+ x = pop();
+ state = pop();
+ resId = pop();
+ push(_wiz->isWizPixelNonTransparent(resId, state, x, y, 0));
+ break;
+ case 19:
+ state = pop();
+ resId = pop();
+ _wiz->getWizImageDim(resId, state, w, h);
+ push(h);
+ break;
+ case 34:
+ type = pop();
+ state = pop();
+ resId = pop();
+ push(_wiz->getWizImageData(resId, state, type));
+ break;
+ case 64:
+ state = pop();
+ resId = pop();
+ _wiz->getWizImageDim(resId, state, w, h);
+ push(w);
+ break;
+ case 65:
+ state = pop();
+ resId = pop();
+ _wiz->getWizImageSpot(resId, state, x, y);
+ push(x);
+ break;
+ case 66:
+ state = pop();
+ resId = pop();
+ _wiz->getWizImageSpot(resId, state, x, y);
+ push(y);
+ break;
+ case 111:
+ pop();
+ copyScriptString(filename, sizeof(filename));
+ pop();
+ push(0);
+ debug(0, "o100_getWizData() case 111 unhandled");
+ break;
+ case 112:
+ h = pop();
+ w = pop();
+ y = pop();
+ x = pop();
+ state = pop();
+ resId = pop();
+ if (x == -1 && y == -1 && w == -1 && h == -1) {
+ _wiz->getWizImageDim(resId, state, w, h);
+ x = 0;
+ y = 0;
+ }
+ push(computeWizHistogram(resId, state, x, y, w, h));
+ break;
+ default:
+ error("o100_getWizData: Unknown case %d", subOp);
+ }
+}
+
+void ScummEngine_v100he::o100_getPaletteData() {
+ int b, c, d, e;
+ int palSlot, color;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 13:
+ c = pop();
+ b = pop();
+ push(getHEPaletteColorComponent(1, b, c));
+ break;
+ case 20:
+ color = pop();
+ palSlot = pop();
+ push(getHEPaletteColor(palSlot, color));
+ break;
+ case 33:
+ e = pop();
+ d = pop();
+ palSlot = pop();
+ pop();
+ c = pop();
+ b = pop();
+ push(getHEPaletteSimilarColor(palSlot, b, c, d, e));
+ break;
+ case 53:
+ pop();
+ c = pop();
+ c = MAX(0, c);
+ c = MIN(c, 255);
+ b = pop();
+ b = MAX(0, b);
+ b = MIN(b, 255);
+ push(getHEPaletteSimilarColor(1, b, c, 10, 245));
+ break;
+ case 73:
+ c = pop();
+ b = pop();
+ palSlot = pop();
+ push(getHEPaletteColorComponent(palSlot, b, c));
+ break;
+ default:
+ error("o100_getPaletteData: Unknown case %d", subOp);
+ }
+}
+
+void ScummEngine_v100he::o100_readFile() {
+ int slot, val;
+ int32 size;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 5:
+ fetchScriptByte();
+ size = pop();
+ slot = pop();
+ val = readFileToArray(slot, size);
+ push(val);
+ break;
+ case 42:
+ slot = pop();
+ val = _hFileTable[slot].readUint16LE();
+ push(val);
+ break;
+ case 43:
+ slot = pop();
+ val = _hFileTable[slot].readUint32LE();
+ push(val);
+ break;
+ case 45:
+ slot = pop();
+ val = _hFileTable[slot].readByte();
+ push(val);
+ break;
+ default:
+ error("o100_readFile: default case %d", subOp);
+ }
+}
+
+void ScummEngine_v100he::o100_getSpriteInfo() {
+ int args[16];
+ int spriteId, flags, groupId, type;
+ int32 x, y;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 3:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteFlagAutoAnim(spriteId));
+ else
+ push(0);
+ break;
+ case 4:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteAnimSpeed(spriteId));
+ else
+ push(1);
+ break;
+ case 7:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteSourceImage(spriteId));
+ else
+ push(0);
+ break;
+ case 16:
+ flags = getStackList(args, ARRAYSIZE(args));
+ spriteId = pop();
+ if (spriteId) {
+ push(_sprite->getSpriteClass(spriteId, flags, args));
+ } else {
+ push(0);
+ }
+ break;
+ case 26:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteImageStateCount(spriteId));
+ else
+ push(0);
+ break;
+ case 30:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteDisplayX(spriteId));
+ else
+ push(0);
+ break;
+ case 31:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteDisplayY(spriteId));
+ else
+ push(0);
+ break;
+ case 32:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteFlagEraseType(spriteId));
+ else
+ push(1);
+ break;
+ case 33:
+ flags = getStackList(args, ARRAYSIZE(args));
+ type = pop();
+ groupId = pop();
+ y = pop();
+ x = pop();
+ push(_sprite->findSpriteWithClassOf(x, y, groupId, type, flags, args));
+ break;
+ case 38:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteGroup(spriteId));
+ else
+ push(0);
+ break;
+ case 39:
+ spriteId = pop();
+ if (spriteId) {
+ _sprite->getSpriteImageDim(spriteId, x, y);
+ push(y);
+ } else {
+ push(0);
+ }
+ break;
+ case 40:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteImage(spriteId));
+ else
+ push(0);
+ break;
+ case 48:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteMaskImage(spriteId));
+ else
+ push(0);
+ break;
+ case 54:
+ flags = pop();
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteGeneralProperty(spriteId, flags));
+ else
+ push(0);
+ break;
+ case 57:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpritePalette(spriteId));
+ else
+ push(0);
+ break;
+ case 59:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpritePriority(spriteId));
+ else
+ push(0);
+ break;
+ case 60:
+ flags = pop();
+ spriteId = pop();
+ if (spriteId) {
+ switch(flags) {
+ case 0:
+ push(_sprite->getSpriteFlagXFlipped(spriteId));
+ break;
+ case 1:
+ push(_sprite->getSpriteFlagYFlipped(spriteId));
+ break;
+ case 2:
+ push(_sprite->getSpriteFlagActive(spriteId));
+ break;
+ case 3:
+ push(_sprite->getSpriteFlagDoubleBuffered(spriteId));
+ break;
+ case 4:
+ push(_sprite->getSpriteFlagRemapPalette(spriteId));
+ break;
+ default:
+ push(0);
+ }
+ } else {
+ push(0);
+ }
+ break;
+ case 65:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteScale(spriteId));
+ else
+ push(0);
+ break;
+ case 70:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteShadow(spriteId));
+ else
+ push(0);
+ break;
+ case 73:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteImageState(spriteId));
+ else
+ push(0);
+ break;
+ case 75:
+ spriteId = pop();
+ if (spriteId) {
+ _sprite->getSpriteDist(spriteId, x, y);
+ push(x);
+ } else {
+ push(0);
+ }
+ break;
+ case 76:
+ spriteId = pop();
+ if (spriteId) {
+ _sprite->getSpriteDist(spriteId, x, y);
+ push(y);
+ } else {
+ push(0);
+ }
+ break;
+ case 82:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteFlagUpdateType(spriteId));
+ else
+ push(0);
+ break;
+ case 83:
+ pop();
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteUserValue(spriteId));
+ else
+ push(0);
+ break;
+ case 84:
+ spriteId = pop();
+ if (spriteId) {
+ _sprite->getSpriteImageDim(spriteId, x, y);
+ push(x);
+ } else {
+ push(0);
+ }
+ break;
+ case 85:
+ spriteId = pop();
+ if (spriteId) {
+ _sprite->getSpritePosition(spriteId, x, y);
+ push(x);
+ } else {
+ push(0);
+ }
+ break;
+ case 86:
+ spriteId = pop();
+ if (spriteId) {
+ _sprite->getSpritePosition(spriteId, x, y);
+ push(y);
+ } else {
+ push(0);
+ }
+ break;
+ default:
+ error("o100_getSpriteInfo: Unknown case %d", subOp);
+ }
+}
+
+void ScummEngine_v100he::o100_getVideoData() {
+ // Uses Bink video
+ byte subOp = fetchScriptByte();
+ subOp -= 26;
+
+ switch (subOp) {
+ case 0:
+ pop();
+ break;
+ case 13:
+ pop();
+ break;
+ case 14:
+ pop();
+ break;
+ case 28:
+ pop();
+ pop();
+ break;
+ case 47:
+ pop();
+ break;
+ case 58:
+ pop();
+ break;
+ default:
+ error("o100_getVideoData: unhandled case %d", subOp);
+ }
+
+ push(-1);
+ debug(1,"o100_getVideoData stub (%d)", subOp);
+}
+
+void ScummEngine_v100he::decodeParseString(int m, int n) {
+ Actor *a;
+ int i, colors, size;
+ int args[31];
+ byte name[1024];
+
+ byte b = fetchScriptByte();
+
+ switch (b) {
+ case 6: // SO_AT
+ _string[m].ypos = pop();
+ _string[m].xpos = pop();
+ _string[m].overhead = false;
+ break;
+ case 12: // SO_CENTER
+ _string[m].center = true;
+ _string[m].overhead = false;
+ break;
+ case 18: // SO_CLIPPED
+ _string[m].right = pop();
+ break;
+ case 20: // SO_COLOR
+ _string[m].color = pop();
+ break;
+ case 21:
+ colors = pop();
+ if (colors == 1) {
+ _string[m].color = pop();
+ } else {
+ push(colors);
+ getStackList(args, ARRAYSIZE(args));
+ for (i = 0; i < 16; i++)
+ _charsetColorMap[i] = _charsetData[_string[1]._default.charset][i] = (unsigned char)args[i];
+ _string[m].color = _charsetColorMap[0];
+ }
+ break;
+ case 35:
+ decodeScriptString(name, true);
+ printString(m, name);
+ break;
+ case 46: // SO_LEFT
+ _string[m].center = false;
+ _string[m].overhead = false;
+ break;
+ case 51: // SO_MUMBLE
+ _string[m].no_talk_anim = true;
+ break;
+ case 56: // SO_OVERHEAD
+ _string[m].overhead = true;
+ _string[m].no_talk_anim = false;
+ break;
+ case 78:
+ {
+ byte *dataPtr = getResourceAddress(rtTalkie, pop());
+ byte *text = findWrappedBlock(MKID('TEXT'), dataPtr, 0, 0);
+ size = getResourceDataSize(text);
+ memcpy(name, text, size);
+ printString(m, name);
+ }
+ break;
+ case 79: // SO_TEXTSTRING
+ printString(m, _scriptPointer);
+ _scriptPointer += resStrLen(_scriptPointer) + 1;
+ break;
+ case 91:
+ _string[m].loadDefault();
+ if (n) {
+ _actorToPrintStrFor = pop();
+ if (_actorToPrintStrFor != 0xFF) {
+ a = derefActor(_actorToPrintStrFor, "decodeParseString");
+ _string[0].color = a->_talkColor;
+ }
+ }
+ break;
+ case 92:
+ _string[m].saveDefault();
+ break;
+ default:
+ error("decodeParseString: default case %d", b);
+ }
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/he/script_v6he.cpp b/engines/scumm/he/script_v6he.cpp
new file mode 100644
index 0000000000..9d7de59450
--- /dev/null
+++ b/engines/scumm/he/script_v6he.cpp
@@ -0,0 +1,1276 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "common/savefile.h"
+
+#include "scumm/actor.h"
+#include "scumm/charset.h"
+#include "scumm/imuse.h"
+#include "scumm/he/intern_he.h"
+#include "scumm/object.h"
+#include "scumm/resource.h"
+#include "scumm/scumm.h"
+#include "scumm/sound.h"
+#include "scumm/usage_bits.h"
+#include "scumm/util.h"
+#include "scumm/verbs.h"
+
+namespace Scumm {
+
+struct vsUnpackCtx {
+ uint8 size;
+ uint8 type;
+ uint8 b;
+ uint8 *ptr;
+};
+
+struct vsPackCtx {
+ int size;
+ uint8 buf[256];
+};
+
+static void virtScreenSavePackBuf(vsPackCtx *ctx, uint8 *&dst, int len);
+static void virtScreenSavePackByte(vsPackCtx *ctx, uint8 *&dst, int len, uint8 b);
+static uint8 virtScreenLoadUnpack(vsUnpackCtx *ctx, byte *data);
+static int virtScreenSavePack(byte *dst, byte *src, int len, int unk);
+
+// Compatibility notes:
+//
+// FBEAR (fbear, fbeardemo)
+// transparency in akos.cpp
+// negative size in file read/write
+
+#define OPCODE(x) _OPCODE(ScummEngine_v60he, x)
+
+void ScummEngine_v60he::setupOpcodes() {
+ static const OpcodeEntryv60he opcodes[256] = {
+ /* 00 */
+ OPCODE(o6_pushByte),
+ OPCODE(o6_pushWord),
+ OPCODE(o6_pushByteVar),
+ OPCODE(o6_pushWordVar),
+ /* 04 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_byteArrayRead),
+ OPCODE(o6_wordArrayRead),
+ /* 08 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_byteArrayIndexedRead),
+ OPCODE(o6_wordArrayIndexedRead),
+ /* 0C */
+ OPCODE(o6_dup),
+ OPCODE(o6_not),
+ OPCODE(o6_eq),
+ OPCODE(o6_neq),
+ /* 10 */
+ OPCODE(o6_gt),
+ OPCODE(o6_lt),
+ OPCODE(o6_le),
+ OPCODE(o6_ge),
+ /* 14 */
+ OPCODE(o6_add),
+ OPCODE(o6_sub),
+ OPCODE(o6_mul),
+ OPCODE(o6_div),
+ /* 18 */
+ OPCODE(o6_land),
+ OPCODE(o6_lor),
+ OPCODE(o6_pop),
+ OPCODE(o6_invalid),
+ /* 1C */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 20 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 24 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 28 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 2C */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 30 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 34 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 38 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 3C */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 40 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_writeByteVar),
+ OPCODE(o6_writeWordVar),
+ /* 44 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_byteArrayWrite),
+ OPCODE(o6_wordArrayWrite),
+ /* 48 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_byteArrayIndexedWrite),
+ OPCODE(o6_wordArrayIndexedWrite),
+ /* 4C */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_byteVarInc),
+ OPCODE(o6_wordVarInc),
+ /* 50 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_byteArrayInc),
+ OPCODE(o6_wordArrayInc),
+ /* 54 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_byteVarDec),
+ OPCODE(o6_wordVarDec),
+ /* 58 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_byteArrayDec),
+ OPCODE(o6_wordArrayDec),
+ /* 5C */
+ OPCODE(o6_if),
+ OPCODE(o6_ifNot),
+ OPCODE(o6_startScript),
+ OPCODE(o6_startScriptQuick),
+ /* 60 */
+ OPCODE(o6_startObject),
+ OPCODE(o6_drawObject),
+ OPCODE(o6_drawObjectAt),
+ OPCODE(o6_invalid),
+ /* 64 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_stopObjectCode),
+ OPCODE(o6_stopObjectCode),
+ OPCODE(o6_endCutscene),
+ /* 68 */
+ OPCODE(o6_cutscene),
+ OPCODE(o6_stopMusic),
+ OPCODE(o6_freezeUnfreeze),
+ OPCODE(o6_cursorCommand),
+ /* 6C */
+ OPCODE(o6_breakHere),
+ OPCODE(o6_ifClassOfIs),
+ OPCODE(o6_setClass),
+ OPCODE(o6_getState),
+ /* 70 */
+ OPCODE(o60_setState),
+ OPCODE(o6_setOwner),
+ OPCODE(o6_getOwner),
+ OPCODE(o6_jump),
+ /* 74 */
+ OPCODE(o6_startSound),
+ OPCODE(o6_stopSound),
+ OPCODE(o6_startMusic),
+ OPCODE(o6_stopObjectScript),
+ /* 78 */
+ OPCODE(o6_panCameraTo),
+ OPCODE(o6_actorFollowCamera),
+ OPCODE(o6_setCameraAt),
+ OPCODE(o6_loadRoom),
+ /* 7C */
+ OPCODE(o6_stopScript),
+ OPCODE(o6_walkActorToObj),
+ OPCODE(o6_walkActorTo),
+ OPCODE(o6_putActorAtXY),
+ /* 80 */
+ OPCODE(o6_putActorAtObject),
+ OPCODE(o6_faceActor),
+ OPCODE(o6_animateActor),
+ OPCODE(o6_doSentence),
+ /* 84 */
+ OPCODE(o6_pickupObject),
+ OPCODE(o6_loadRoomWithEgo),
+ OPCODE(o6_invalid),
+ OPCODE(o6_getRandomNumber),
+ /* 88 */
+ OPCODE(o6_getRandomNumberRange),
+ OPCODE(o6_invalid),
+ OPCODE(o6_getActorMoving),
+ OPCODE(o6_isScriptRunning),
+ /* 8C */
+ OPCODE(o6_getActorRoom),
+ OPCODE(o6_getObjectX),
+ OPCODE(o6_getObjectY),
+ OPCODE(o6_getObjectOldDir),
+ /* 90 */
+ OPCODE(o6_getActorWalkBox),
+ OPCODE(o6_getActorCostume),
+ OPCODE(o6_findInventory),
+ OPCODE(o6_getInventoryCount),
+ /* 94 */
+ OPCODE(o6_getVerbFromXY),
+ OPCODE(o6_beginOverride),
+ OPCODE(o6_endOverride),
+ OPCODE(o6_setObjectName),
+ /* 98 */
+ OPCODE(o6_isSoundRunning),
+ OPCODE(o6_setBoxFlags),
+ OPCODE(o6_invalid),
+ OPCODE(o6_resourceRoutines),
+ /* 9C */
+ OPCODE(o60_roomOps),
+ OPCODE(o60_actorOps),
+ OPCODE(o6_verbOps),
+ OPCODE(o6_getActorFromXY),
+ /* A0 */
+ OPCODE(o6_findObject),
+ OPCODE(o6_pseudoRoom),
+ OPCODE(o6_getActorElevation),
+ OPCODE(o6_getVerbEntrypoint),
+ /* A4 */
+ OPCODE(o6_arrayOps),
+ OPCODE(o6_saveRestoreVerbs),
+ OPCODE(o6_drawBox),
+ OPCODE(o6_pop),
+ /* A8 */
+ OPCODE(o6_getActorWidth),
+ OPCODE(o6_wait),
+ OPCODE(o6_getActorScaleX),
+ OPCODE(o6_getActorAnimCounter1),
+ /* AC */
+ OPCODE(o6_invalid),
+ OPCODE(o6_isAnyOf),
+ OPCODE(o6_systemOps),
+ OPCODE(o6_isActorInBox),
+ /* B0 */
+ OPCODE(o6_delay),
+ OPCODE(o6_delaySeconds),
+ OPCODE(o6_delayMinutes),
+ OPCODE(o6_stopSentence),
+ /* B4 */
+ OPCODE(o6_printLine),
+ OPCODE(o6_printText),
+ OPCODE(o6_printDebug),
+ OPCODE(o6_printSystem),
+ /* B8 */
+ OPCODE(o6_printActor),
+ OPCODE(o6_printEgo),
+ OPCODE(o6_talkActor),
+ OPCODE(o6_talkEgo),
+ /* BC */
+ OPCODE(o6_dimArray),
+ OPCODE(o6_stopObjectCode),
+ OPCODE(o6_startObjectQuick),
+ OPCODE(o6_startScriptQuick2),
+ /* C0 */
+ OPCODE(o6_dim2dimArray),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* C4 */
+ OPCODE(o6_abs),
+ OPCODE(o6_distObjectObject),
+ OPCODE(o6_distObjectPt),
+ OPCODE(o6_distPtPt),
+ /* C8 */
+ OPCODE(o60_kernelGetFunctions),
+ OPCODE(o60_kernelSetFunctions),
+ OPCODE(o6_delayFrames),
+ OPCODE(o6_pickOneOf),
+ /* CC */
+ OPCODE(o6_pickOneOfDefault),
+ OPCODE(o6_stampObject),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* D0 */
+ OPCODE(o6_getDateTime),
+ OPCODE(o6_stopTalking),
+ OPCODE(o6_getAnimateVariable),
+ OPCODE(o6_invalid),
+ /* D4 */
+ OPCODE(o6_shuffle),
+ OPCODE(o6_jumpToScript),
+ OPCODE(o6_band),
+ OPCODE(o6_bor),
+ /* D8 */
+ OPCODE(o6_isRoomScriptRunning),
+ OPCODE(o60_closeFile),
+ OPCODE(o60_openFile),
+ OPCODE(o60_readFile),
+ /* DC */
+ OPCODE(o60_writeFile),
+ OPCODE(o6_findAllObjects),
+ OPCODE(o60_deleteFile),
+ OPCODE(o60_rename),
+ /* E0 */
+ OPCODE(o60_soundOps),
+ OPCODE(o6_getPixel),
+ OPCODE(o60_localizeArrayToScript),
+ OPCODE(o6_pickVarRandom),
+ /* E4 */
+ OPCODE(o6_setBoxSet),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* E8 */
+ OPCODE(o6_invalid),
+ OPCODE(o60_seekFilePos),
+ OPCODE(o60_redimArray),
+ OPCODE(o60_readFilePos),
+ /* EC */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* F0 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* F4 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* F8 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* FC */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ };
+
+ _opcodesv60he = opcodes;
+}
+
+void ScummEngine_v60he::executeOpcode(byte i) {
+ OpcodeProcv60he op = _opcodesv60he[i].proc;
+ (this->*op) ();
+}
+
+const char *ScummEngine_v60he::getOpcodeDesc(byte i) {
+ return _opcodesv60he[i].desc;
+}
+
+void ScummEngine_v60he::o60_setState() {
+ int state = pop();
+ int obj = pop();
+
+ if (state & 0x8000) {
+ state &= 0x7FFF;
+ putState(obj, state);
+ if (_heversion >= 72)
+ removeObjectFromDrawQue(obj);
+ } else {
+ putState(obj, state);
+ markObjectRectAsDirty(obj);
+ if (_bgNeedsRedraw)
+ clearDrawObjectQueue();
+ }
+}
+
+void ScummEngine_v60he::o60_roomOps() {
+ int a, b, c, d, e;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 172: // SO_ROOM_SCROLL
+ b = pop();
+ a = pop();
+ if (a < (_screenWidth / 2))
+ a = (_screenWidth / 2);
+ if (b < (_screenWidth / 2))
+ b = (_screenWidth / 2);
+ if (a > _roomWidth - (_screenWidth / 2))
+ a = _roomWidth - (_screenWidth / 2);
+ if (b > _roomWidth - (_screenWidth / 2))
+ b = _roomWidth - (_screenWidth / 2);
+ VAR(VAR_CAMERA_MIN_X) = a;
+ VAR(VAR_CAMERA_MAX_X) = b;
+ break;
+
+ case 174: // SO_ROOM_SCREEN
+ b = pop();
+ a = pop();
+ if (_heversion >= 71)
+ initScreens(a, _screenHeight);
+ else
+ initScreens(a, b);
+ break;
+
+ case 175: // SO_ROOM_PALETTE
+ d = pop();
+ c = pop();
+ b = pop();
+ a = pop();
+ setPalColor(d, a, b, c);
+ break;
+
+ case 176: // SO_ROOM_SHAKE_ON
+ setShake(1);
+ break;
+
+ case 177: // SO_ROOM_SHAKE_OFF
+ setShake(0);
+ break;
+
+ case 179: // SO_ROOM_INTENSITY
+ c = pop();
+ b = pop();
+ a = pop();
+ darkenPalette(a, a, a, b, c);
+ break;
+
+ case 180: // SO_ROOM_SAVEGAME
+ _saveTemporaryState = true;
+ _saveLoadSlot = pop();
+ _saveLoadFlag = pop();
+ break;
+
+ case 181: // SO_ROOM_FADE
+ a = pop();
+ if (_heversion >= 70) {
+ // Defaults to 1 but doesn't use fade effects
+ } else if (a) {
+ _switchRoomEffect = (byte)(a & 0xFF);
+ _switchRoomEffect2 = (byte)(a >> 8);
+ } else {
+ fadeIn(_newEffect);
+ }
+ break;
+
+ case 182: // SO_RGB_ROOM_INTENSITY
+ e = pop();
+ d = pop();
+ c = pop();
+ b = pop();
+ a = pop();
+ darkenPalette(a, b, c, d, e);
+ break;
+
+ case 183: // SO_ROOM_SHADOW
+ e = pop();
+ d = pop();
+ c = pop();
+ b = pop();
+ a = pop();
+ if (_heversion == 60)
+ setupShadowPalette(a, b, c, d, e, 0, 256);
+ break;
+
+ case 186: // SO_ROOM_TRANSFORM
+ d = pop();
+ c = pop();
+ b = pop();
+ a = pop();
+ palManipulateInit(a, b, c, d);
+ break;
+
+ case 187: // SO_CYCLE_SPEED
+ b = pop();
+ a = pop();
+ checkRange(16, 1, a, "o60_roomOps: 187: color cycle out of range (%d)");
+ _colorCycle[a - 1].delay = (b != 0) ? 0x4000 / (b * 0x4C) : 0;
+ break;
+
+ case 213: // SO_ROOM_NEW_PALETTE
+ a = pop();
+ setPalette(a);
+ break;
+ case 220:
+ a = pop();
+ b = pop();
+ copyPalColor(a, b);
+ break;
+ case 221:
+ int len;
+ len = resStrLen(_scriptPointer);
+ _scriptPointer += len + 1;
+ _saveLoadFlag = pop();
+ _saveLoadSlot = 1;
+ _saveTemporaryState = true;
+ break;
+ case 234: // HE 7.2
+ b = pop();
+ a = pop();
+ swapObjects(a, b);
+ break;
+ case 236: // HE 7.2
+ b = pop();
+ a = pop();
+ setRoomPalette(a, b);
+ break;
+ default:
+ error("o60_roomOps: default case %d", subOp);
+ }
+}
+
+void ScummEngine_v60he::swapObjects(int object1, int object2) {
+ int idx1 = -1, idx2 = -1;
+
+ for (int i = 0; i < _numLocalObjects; i++) {
+ if (_objs[i].obj_nr == object1)
+ idx1 = i;
+
+ if (_objs[i].obj_nr == object2)
+ idx2 = i;
+ }
+
+ if (idx1 == -1 || idx2 == -1 || idx1 <= idx2)
+ return;
+
+ stopObjectScript(object1);
+ stopObjectScript(object2);
+
+ ObjectData tmpOd;
+
+ memcpy(&tmpOd, &_objs[idx1], sizeof(tmpOd));
+ memcpy(&_objs[idx1], &_objs[idx2], sizeof(tmpOd));
+ memcpy(&_objs[idx2], &tmpOd, sizeof(tmpOd));
+}
+
+void ScummEngine_v60he::o60_actorOps() {
+ Actor *a;
+ int i, j, k;
+ int args[8];
+
+ byte subOp = fetchScriptByte();
+ if (subOp == 197) {
+ _curActor = pop();
+ return;
+ }
+
+ a = derefActorSafe(_curActor, "o60_actorOps");
+ if (!a)
+ return;
+
+ switch (subOp) {
+ case 30:
+ // _heversion >= 70
+ _actorClipOverride.bottom = pop();
+ _actorClipOverride.right = pop();
+ _actorClipOverride.top = pop();
+ _actorClipOverride.left = pop();
+ break;
+ case 76: // SO_COSTUME
+ a->setActorCostume(pop());
+ break;
+ case 77: // SO_STEP_DIST
+ j = pop();
+ i = pop();
+ a->setActorWalkSpeed(i, j);
+ break;
+ case 78: // SO_SOUND
+ k = getStackList(args, ARRAYSIZE(args));
+ for (i = 0; i < k; i++)
+ a->_sound[i] = args[i];
+ break;
+ case 79: // SO_WALK_ANIMATION
+ a->_walkFrame = pop();
+ break;
+ case 80: // SO_TALK_ANIMATION
+ a->_talkStopFrame = pop();
+ a->_talkStartFrame = pop();
+ break;
+ case 81: // SO_STAND_ANIMATION
+ a->_standFrame = pop();
+ break;
+ case 82: // SO_ANIMATION
+ // dummy case in scumm6
+ pop();
+ pop();
+ pop();
+ break;
+ case 83: // SO_DEFAULT
+ a->initActor(0);
+ break;
+ case 84: // SO_ELEVATION
+ a->setElevation(pop());
+ break;
+ case 85: // SO_ANIMATION_DEFAULT
+ a->_initFrame = 1;
+ a->_walkFrame = 2;
+ a->_standFrame = 3;
+ a->_talkStartFrame = 4;
+ a->_talkStopFrame = 5;
+ break;
+ case 86: // SO_PALETTE
+ j = pop();
+ i = pop();
+ checkRange(255, 0, i, "Illegal palette slot %d");
+ a->remapActorPaletteColor(i, j);
+ a->_needRedraw = true;
+ break;
+ case 87: // SO_TALK_COLOR
+ a->_talkColor = pop();
+ break;
+ case 88: // SO_ACTOR_NAME
+ loadPtrToResource(rtActorName, a->_number, NULL);
+ break;
+ case 89: // SO_INIT_ANIMATION
+ a->_initFrame = pop();
+ break;
+ case 91: // SO_ACTOR_WIDTH
+ a->_width = pop();
+ break;
+ case 92: // SO_SCALE
+ i = pop();
+ a->setScale(i, i);
+ break;
+ case 93: // SO_NEVER_ZCLIP
+ a->_forceClip = 0;
+ break;
+ case 94: // SO_ALWAYS_ZCLIP
+ a->_forceClip = pop();
+ break;
+ case 95: // SO_IGNORE_BOXES
+ a->_ignoreBoxes = 1;
+ a->_forceClip = 0;
+ if (a->isInCurrentRoom())
+ a->putActor(a->_pos.x, a->_pos.y, a->_room);
+ break;
+ case 96: // SO_FOLLOW_BOXES
+ a->_ignoreBoxes = 0;
+ a->_forceClip = 0;
+ if (a->isInCurrentRoom())
+ a->putActor(a->_pos.x, a->_pos.y, a->_room);
+ break;
+ case 97: // SO_ANIMATION_SPEED
+ a->setAnimSpeed(pop());
+ break;
+ case 98: // SO_SHADOW
+ a->_shadowMode = pop();
+ a->_needRedraw = true;
+ break;
+ case 99: // SO_TEXT_OFFSET
+ a->_talkPosY = pop();
+ a->_talkPosX = pop();
+ break;
+ case 156: // HE 7.2
+ a->_charset = pop();
+ break;
+ case 198: // SO_ACTOR_VARIABLE
+ i = pop();
+ a->setAnimVar(pop(), i);
+ break;
+ case 215: // SO_ACTOR_IGNORE_TURNS_ON
+ a->_ignoreTurns = true;
+ break;
+ case 216: // SO_ACTOR_IGNORE_TURNS_OFF
+ a->_ignoreTurns = false;
+ break;
+ case 217: // SO_ACTOR_NEW
+ a->initActor(2);
+ break;
+ case 218:
+ a->drawActorToBackBuf(a->_pos.x, a->_pos.y);
+ break;
+ case 219:
+ a->_drawToBackBuf = false;
+ a->_needRedraw = true;
+ a->_needBgReset = true;
+ break;
+ case 225:
+ {
+ byte string[128];
+ copyScriptString(string);
+ int slot = pop();
+
+ int len = resStrLen(string) + 1;
+ convertMessageToString(string, a->_heTalkQueue[slot].sentence, len);
+
+ a->_heTalkQueue[slot].posX = a->_talkPosX;
+ a->_heTalkQueue[slot].posY = a->_talkPosY;
+ a->_heTalkQueue[slot].color = a->_talkColor;
+ break;
+ }
+ default:
+ error("o60_actorOps: default case %d", subOp);
+ }
+}
+
+void ScummEngine_v60he::o60_kernelSetFunctions() {
+ int args[29];
+ int num;
+
+ num = getStackList(args, ARRAYSIZE(args));
+
+ switch (args[0]) {
+ case 1:
+ // Used to restore images when decorating cake in
+ // Fatty Bear's Birthday Surprise
+ virtScreenLoad(args[1], args[2], args[3], args[4], args[5]);
+ break;
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 8:
+ //Used before mini games in 3DO versions, seems safe to ignore.
+ break;
+ default:
+ error("o60_kernelSetFunctions: default case %d (param count %d)", args[0], num);
+ }
+}
+
+void ScummEngine_v60he::virtScreenLoad(int resIdx, int x1, int y1, int x2, int y2) {
+ vsUnpackCtx ctx;
+ memset(&ctx, 0, sizeof(ctx));
+ VirtScreen &vs = virtscr[kMainVirtScreen];
+
+ ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, resIdx);
+ virtScreenLoadUnpack(&ctx, ah->data);
+ for (int j = y1; j <= y2; ++j) {
+ uint8 *p1 = vs.getPixels(x1, j - vs.topline);
+ uint8 *p2 = vs.getBackPixels(x1, j - vs.topline);
+ if (x2 >= x1) {
+ uint32 w = x2 - x1 + 1;
+ while (w--) {
+ uint8 decByte = virtScreenLoadUnpack(&ctx, 0);
+ *p1++ = decByte;
+ *p2++ = decByte;
+ }
+ }
+ }
+ markRectAsDirty(kMainVirtScreen, x1, x2, y1, y2 + 1, USAGE_BIT_RESTORED);
+}
+
+uint8 virtScreenLoadUnpack(vsUnpackCtx *ctx, byte *data) {
+ uint8 decByte;
+ if (data != 0) {
+ ctx->type = 0;
+ ctx->ptr = data;
+ decByte = 0;
+ } else {
+ uint8 a;
+ if (ctx->type == 0) {
+ a = *(ctx->ptr)++;
+ if (a & 1) {
+ ctx->type = 1;
+ ctx->b = *(ctx->ptr)++;
+ } else {
+ ctx->type = 2;
+ }
+ ctx->size = a;
+ a = (a >> 1) + 1;
+ } else {
+ a = ctx->size;
+ }
+ if (ctx->type == 2) {
+ ctx->b = *(ctx->ptr)++;
+ }
+ ctx->size = a - 1;
+ if (ctx->size == 0) {
+ ctx->type = 0;
+ }
+ decByte = ctx->b;
+ }
+ return decByte;
+}
+
+
+void ScummEngine_v60he::o60_kernelGetFunctions() {
+ int args[29];
+ ArrayHeader *ah;
+ getStackList(args, ARRAYSIZE(args));
+
+ switch (args[0]) {
+ case 1:
+ // Used to store images when decorating cake in
+ // Fatty Bear's Birthday Surprise
+ writeVar(0, 0);
+ ah = defineArray(0, kByteArray, 0, virtScreenSave(0, args[1], args[2], args[3], args[4]));
+ virtScreenSave(ah->data, args[1], args[2], args[3], args[4]);
+ push(readVar(0));
+ break;
+ default:
+ error("o60_kernelGetFunctions: default case %d", args[0]);
+ }
+}
+
+int ScummEngine_v60he::virtScreenSave(byte *dst, int x1, int y1, int x2, int y2) {
+ int packedSize = 0;
+ VirtScreen &vs = virtscr[kMainVirtScreen];
+
+ for (int j = y1; j <= y2; ++j) {
+ uint8 *p = vs.getBackPixels(x1, j - vs.topline);
+
+ int size = virtScreenSavePack(dst, p, x2 - x1 + 1, 0);
+ if (dst != 0) {
+ dst += size;
+ }
+ packedSize += size;
+ }
+ return packedSize;
+}
+
+int virtScreenSavePack(byte *dst, byte *src, int len, int unk) {
+ vsPackCtx ctx;
+ memset(&ctx, 0, sizeof(ctx));
+
+ uint8 prevByte, curByte;
+
+ ctx.buf[0] = prevByte = *src++;
+ int flag = 0;
+ int iend = 1;
+ int ibeg = 0;
+
+ for (--len; len != 0; --len, prevByte = curByte) {
+ bool pass = false;
+
+ assert(iend < 0x100);
+ ctx.buf[iend] = curByte = *src++;
+ ++iend;
+
+ if (flag == 0) {
+ if (iend > 0x80) {
+ virtScreenSavePackBuf(&ctx, dst, iend - 1);
+ ctx.buf[0] = curByte;
+ iend = 1;
+ ibeg = 0;
+ continue;
+ }
+ if (prevByte != curByte) {
+ ibeg = iend - 1;
+ continue;
+ }
+ if (iend - ibeg < 3) {
+ if (ibeg != 0) {
+ pass = true;
+ } else {
+ flag = 1;
+ }
+ } else {
+ if (ibeg > 0) {
+ virtScreenSavePackBuf(&ctx, dst, ibeg);
+ }
+ flag = 1;
+ }
+ }
+ if (flag == 1 || pass) {
+ if (prevByte != curByte || iend - ibeg > 0x80) {
+ virtScreenSavePackByte(&ctx, dst, iend - ibeg - 1, prevByte);
+ ctx.buf[0] = curByte;
+ iend = 1;
+ ibeg = 0;
+ flag = 0;
+ }
+ }
+ }
+
+ if (flag == 0) {
+ virtScreenSavePackBuf(&ctx, dst, iend);
+ } else if (flag == 1) {
+ virtScreenSavePackByte(&ctx, dst, iend - ibeg, prevByte);
+ }
+ return ctx.size;
+}
+
+void virtScreenSavePackBuf(vsPackCtx *ctx, uint8 *&dst, int len) {
+ if (dst) {
+ *dst++ = (len - 1) * 2;
+ }
+ ++ctx->size;
+ if (len > 0) {
+ ctx->size += len;
+ if (dst) {
+ memcpy(dst, ctx->buf, len);
+ dst += len;
+ }
+ }
+}
+
+void virtScreenSavePackByte(vsPackCtx *ctx, uint8 *&dst, int len, uint8 b) {
+ if (dst) {
+ *dst++ = ((len - 1) * 2) | 1;
+ }
+ ++ctx->size;
+ if (dst) {
+ *dst++ = b;
+ }
+ ++ctx->size;
+}
+
+void ScummEngine_v60he::o60_openFile() {
+ int mode, len, slot, l, r;
+ byte filename[100];
+
+ convertMessageToString(_scriptPointer, filename, sizeof(filename));
+
+ len = resStrLen(_scriptPointer);
+ _scriptPointer += len + 1;
+
+ for (r = strlen((char*)filename); r != 0; r--) {
+ if (filename[r - 1] == '\\')
+ break;
+ }
+
+ mode = pop();
+ slot = -1;
+ for (l = 0; l < 17; l++) {
+ if (_hFileTable[l].isOpen() == false) {
+ slot = l;
+ break;
+ }
+ }
+
+ if (slot != -1) {
+ switch(mode) {
+ case 1:
+ _hFileTable[slot].open((char*)filename + r, Common::File::kFileReadMode, _saveFileMan->getSavePath());
+ if (_hFileTable[slot].isOpen() == false)
+ _hFileTable[slot].open((char*)filename + r, Common::File::kFileReadMode);
+ break;
+ case 2:
+ _hFileTable[slot].open((char*)filename + r, Common::File::kFileWriteMode, _saveFileMan->getSavePath());
+ break;
+ default:
+ error("o60_openFile(): wrong open file mode %d", mode);
+ }
+
+ if (_hFileTable[slot].isOpen() == false)
+ slot = -1;
+
+ }
+ push(slot);
+}
+
+void ScummEngine_v60he::o60_closeFile() {
+ int slot = pop();
+ if (slot != -1)
+ _hFileTable[slot].close();
+}
+
+void ScummEngine_v60he::o60_deleteFile() {
+ int len, r;
+ byte filename[100];
+
+ convertMessageToString(_scriptPointer, filename, sizeof(filename));
+
+ len = resStrLen(_scriptPointer);
+ _scriptPointer += len + 1;
+
+ for (r = strlen((char*)filename); r != 0; r--) {
+ if (filename[r - 1] == '\\')
+ break;
+ }
+
+ debug(1, "stub o60_deleteFile(\"%s\")", filename + r);
+}
+
+void ScummEngine_v60he::o60_rename() {
+ int len, r1, r2;
+ byte filename[100],filename2[100];
+
+ convertMessageToString(_scriptPointer, filename, sizeof(filename));
+
+ len = resStrLen(_scriptPointer);
+ _scriptPointer += len + 1;
+
+ for (r1 = strlen((char*)filename); r1 != 0; r1--) {
+ if (filename[r1 - 1] == '\\')
+ break;
+ }
+
+ convertMessageToString(_scriptPointer, filename2, sizeof(filename2));
+
+ len = resStrLen(_scriptPointer);
+ _scriptPointer += len + 1;
+
+ for (r2 = strlen((char*)filename2); r2 != 0; r2--) {
+ if (filename2[r2 - 1] == '\\')
+ break;
+ }
+
+ debug(1, "stub o60_rename(\"%s\" to \"%s\")", filename + r1, filename2 + r2);
+}
+
+int ScummEngine_v60he::readFileToArray(int slot, int32 size) {
+ if (size == 0)
+ size = _hFileTable[slot].size() - _hFileTable[slot].pos();
+
+ writeVar(0, 0);
+
+ ArrayHeader *ah = defineArray(0, kByteArray, 0, size);
+ _hFileTable[slot].read(ah->data, size);
+
+ return readVar(0);
+}
+
+void ScummEngine_v60he::o60_readFile() {
+ int32 size = pop();
+ int slot = pop();
+ int val;
+
+ // Fatty Bear uses positive values
+ if ((_platform == Common::kPlatformPC) && (_gameId == GID_FBEAR))
+ size = -size;
+
+ if (size == -2) {
+ val = _hFileTable[slot].readUint16LE();
+ push(val);
+ } else if (size == -1) {
+ val = _hFileTable[slot].readByte();
+ push(val);
+ } else {
+ val = readFileToArray(slot, size);
+ push(val);
+ }
+}
+
+void ScummEngine_v60he::writeFileFromArray(int slot, int resID) {
+ ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, resID);
+ int32 size = FROM_LE_16(ah->dim1) * FROM_LE_16(ah->dim2);
+
+ _hFileTable[slot].write(ah->data, size);
+}
+
+void ScummEngine_v60he::o60_writeFile() {
+ int32 size = pop();
+ int16 resID = pop();
+ int slot = pop();
+
+ // Fatty Bear uses positive values
+ if ((_platform == Common::kPlatformPC) && (_gameId == GID_FBEAR))
+ size = -size;
+
+ if (size == -2) {
+ _hFileTable[slot].writeUint16LE(resID);
+ } else if (size == -1) {
+ _hFileTable[slot].writeByte(resID);
+ } else {
+ writeFileFromArray(slot, resID);
+ }
+}
+
+void ScummEngine_v60he::o60_soundOps() {
+ byte subOp = fetchScriptByte();
+ int arg = pop();
+
+ switch (subOp) {
+ case 0xde:
+ _imuse->setMusicVolume(arg);
+ break;
+ case 0xdf:
+ // Used in fbear introduction
+ break;
+ case 0xe0:
+ // Fatty Bear's Birthday surprise uses this when playing the
+ // piano, but only when using one of the digitized instruments.
+ // See also o6_startSound().
+ _sound->setOverrideFreq(arg);
+ break;
+ default:
+ error("o60_soundOps: default case 0x%x", subOp);
+ }
+}
+
+void ScummEngine_v60he::localizeArray(int slot, byte scriptSlot) {
+ if (_heversion >= 80)
+ slot &= ~0x33539000;
+
+ if (slot >= _numArray)
+ error("o60_localizeArrayToScript(%d): array slot out of range", slot);
+
+ _arraySlot[slot] = scriptSlot;
+}
+
+void ScummEngine_v60he::o60_localizeArrayToScript() {
+ int slot = pop();
+ localizeArray(slot, _currentScript);
+}
+
+void ScummEngine_v60he::o60_seekFilePos() {
+ int mode, offset, slot;
+
+ mode = pop();
+ offset = pop();
+ slot = pop();
+
+ if (slot == -1)
+ return;
+
+ switch (mode) {
+ case 1:
+ _hFileTable[slot].seek(offset, SEEK_SET);
+ break;
+ case 2:
+ _hFileTable[slot].seek(offset, SEEK_CUR);
+ break;
+ case 3:
+ _hFileTable[slot].seek(offset, SEEK_END);
+ break;
+ default:
+ error("o60_seekFilePos: default case %d", mode);
+ }
+}
+
+void ScummEngine_v60he::o60_readFilePos() {
+ int slot = pop();
+
+ if (slot == -1) {
+ push(0);
+ return;
+ }
+
+ push(_hFileTable[slot].pos());
+}
+
+void ScummEngine_v60he::o60_redimArray() {
+ int newX, newY;
+ newY = pop();
+ newX = pop();
+
+ if (newY == 0)
+ SWAP(newX, newY);
+
+ byte subOp = fetchScriptByte();
+ switch (subOp) {
+ case 199:
+ redimArray(fetchScriptWord(), newX, newY, kIntArray);
+ break;
+ case 202:
+ redimArray(fetchScriptWord(), newX, newY, kByteArray);
+ break;
+ default:
+ error("o60_redimArray: default type %d", subOp);
+ }
+}
+
+void ScummEngine_v60he::redimArray(int arrayId, int newX, int newY, int type) {
+ // Used in mini game at Cosmic Dust Diner in puttmoon
+ int newSize, oldSize;
+
+ if (readVar(arrayId) == 0)
+ error("redimArray: Reference to zeroed array pointer");
+
+ ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, readVar(arrayId));
+
+ if (!ah)
+ error("redimArray: Invalid array (%d) reference", readVar(arrayId));
+
+ newSize = (type == kIntArray) ? 2 : 1;
+ oldSize = (ah->type == kIntArray) ? 2 : 1;
+
+ newSize *= (newX + 1) * (newY + 1);
+ oldSize *= FROM_LE_16(ah->dim1) * FROM_LE_16(ah->dim2);
+
+ if (newSize != oldSize)
+ error("redimArray: array %d redim mismatch", readVar(arrayId));
+
+ ah->type = TO_LE_16(type);
+ ah->dim1 = TO_LE_16(newY + 1);
+ ah->dim2 = TO_LE_16(newX + 1);
+}
+
+void ScummEngine_v60he::decodeParseString(int m, int n) {
+ int i, colors;
+ int args[31];
+
+ byte b = fetchScriptByte();
+
+ switch (b) {
+ case 65: // SO_AT
+ _string[m].ypos = pop();
+ _string[m].xpos = pop();
+ _string[m].overhead = false;
+ break;
+ case 66: // SO_COLOR
+ _string[m].color = pop();
+ break;
+ case 67: // SO_CLIPPED
+ _string[m].right = pop();
+ break;
+ case 69: // SO_CENTER
+ _string[m].center = true;
+ _string[m].overhead = false;
+ break;
+ case 71: // SO_LEFT
+ _string[m].center = false;
+ _string[m].overhead = false;
+ break;
+ case 72: // SO_OVERHEAD
+ _string[m].overhead = true;
+ _string[m].no_talk_anim = false;
+ break;
+ case 74: // SO_MUMBLE
+ _string[m].no_talk_anim = true;
+ break;
+ case 75: // SO_TEXTSTRING
+ printString(m, _scriptPointer);
+ _scriptPointer += resStrLen(_scriptPointer) + 1;
+ break;
+ case 0xF9:
+ colors = pop();
+ if (colors == 1) {
+ _string[m].color = pop();
+ } else {
+ push(colors);
+ getStackList(args, ARRAYSIZE(args));
+ for (i = 0; i < 16; i++)
+ _charsetColorMap[i] = _charsetData[_string[1]._default.charset][i] = (unsigned char)args[i];
+ _string[m].color = _charsetColorMap[0];
+ }
+ break;
+ case 0xFE:
+ _string[m].loadDefault();
+ if (n)
+ _actorToPrintStrFor = pop();
+ break;
+ case 0xFF:
+ _string[m].saveDefault();
+ break;
+ default:
+ error("decodeParseString: default case 0x%x", b);
+ }
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/he/script_v72he.cpp b/engines/scumm/he/script_v72he.cpp
new file mode 100644
index 0000000000..b94cc06b40
--- /dev/null
+++ b/engines/scumm/he/script_v72he.cpp
@@ -0,0 +1,2368 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "common/config-manager.h"
+#include "common/savefile.h"
+#include "common/system.h"
+
+#include "scumm/actor.h"
+#include "scumm/charset.h"
+#include "scumm/he/intern_he.h"
+#include "scumm/object.h"
+#include "scumm/resource.h"
+#include "scumm/he/resource_v7he.h"
+#include "scumm/scumm.h"
+#include "scumm/sound.h"
+#include "scumm/util.h"
+#include "scumm/verbs.h"
+
+namespace Scumm {
+
+#define OPCODE(x) _OPCODE(ScummEngine_v72he, x)
+
+void ScummEngine_v72he::setupOpcodes() {
+ static const OpcodeEntryV72he opcodes[256] = {
+ /* 00 */
+ OPCODE(o6_pushByte),
+ OPCODE(o6_pushWord),
+ OPCODE(o72_pushDWord),
+ OPCODE(o6_pushWordVar),
+ /* 04 */
+ OPCODE(o72_getScriptString),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_wordArrayRead),
+ /* 08 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_wordArrayIndexedRead),
+ /* 0C */
+ OPCODE(o6_dup),
+ OPCODE(o6_not),
+ OPCODE(o6_eq),
+ OPCODE(o6_neq),
+ /* 10 */
+ OPCODE(o6_gt),
+ OPCODE(o6_lt),
+ OPCODE(o6_le),
+ OPCODE(o6_ge),
+ /* 14 */
+ OPCODE(o6_add),
+ OPCODE(o6_sub),
+ OPCODE(o6_mul),
+ OPCODE(o6_div),
+ /* 18 */
+ OPCODE(o6_land),
+ OPCODE(o6_lor),
+ OPCODE(o6_pop),
+ OPCODE(o72_isAnyOf),
+ /* 1C */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 20 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 24 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 28 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 2C */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 30 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 34 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 38 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 3C */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 40 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_writeWordVar),
+ /* 44 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_wordArrayWrite),
+ /* 48 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_wordArrayIndexedWrite),
+ /* 4C */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_wordVarInc),
+ /* 50 */
+ OPCODE(o72_resetCutscene),
+ OPCODE(o6_invalid),
+ OPCODE(o72_findObjectWithClassOf),
+ OPCODE(o6_wordArrayInc),
+ /* 54 */
+ OPCODE(o72_getObjectImageX),
+ OPCODE(o72_getObjectImageY),
+ OPCODE(o72_captureWizImage),
+ OPCODE(o6_wordVarDec),
+ /* 58 */
+ OPCODE(o72_getTimer),
+ OPCODE(o72_setTimer),
+ OPCODE(o72_getSoundPosition),
+ OPCODE(o6_wordArrayDec),
+ /* 5C */
+ OPCODE(o6_if),
+ OPCODE(o6_ifNot),
+ OPCODE(o72_startScript),
+ OPCODE(o6_startScriptQuick),
+ /* 60 */
+ OPCODE(o72_startObject),
+ OPCODE(o72_drawObject),
+ OPCODE(o72_printWizImage),
+ OPCODE(o72_getArrayDimSize),
+ /* 64 */
+ OPCODE(o72_getNumFreeArrays),
+ OPCODE(o6_stopObjectCode),
+ OPCODE(o6_stopObjectCode),
+ OPCODE(o6_endCutscene),
+ /* 68 */
+ OPCODE(o6_cutscene),
+ OPCODE(o6_stopMusic),
+ OPCODE(o6_freezeUnfreeze),
+ OPCODE(o6_cursorCommand),
+ /* 6C */
+ OPCODE(o6_breakHere),
+ OPCODE(o6_ifClassOfIs),
+ OPCODE(o6_setClass),
+ OPCODE(o6_getState),
+ /* 70 */
+ OPCODE(o60_setState),
+ OPCODE(o6_setOwner),
+ OPCODE(o6_getOwner),
+ OPCODE(o6_jump),
+ /* 74 */
+ OPCODE(o70_startSound),
+ OPCODE(o6_stopSound),
+ OPCODE(o6_startMusic),
+ OPCODE(o6_stopObjectScript),
+ /* 78 */
+ OPCODE(o6_panCameraTo),
+ OPCODE(o6_actorFollowCamera),
+ OPCODE(o6_setCameraAt),
+ OPCODE(o6_loadRoom),
+ /* 7C */
+ OPCODE(o6_stopScript),
+ OPCODE(o6_walkActorToObj),
+ OPCODE(o6_walkActorTo),
+ OPCODE(o6_putActorAtXY),
+ /* 80 */
+ OPCODE(o6_putActorAtObject),
+ OPCODE(o6_faceActor),
+ OPCODE(o6_animateActor),
+ OPCODE(o6_doSentence),
+ /* 84 */
+ OPCODE(o70_pickupObject),
+ OPCODE(o6_loadRoomWithEgo),
+ OPCODE(o6_invalid),
+ OPCODE(o6_getRandomNumber),
+ /* 88 */
+ OPCODE(o6_getRandomNumberRange),
+ OPCODE(o6_invalid),
+ OPCODE(o6_getActorMoving),
+ OPCODE(o6_isScriptRunning),
+ /* 8C */
+ OPCODE(o70_getActorRoom),
+ OPCODE(o6_getObjectX),
+ OPCODE(o6_getObjectY),
+ OPCODE(o6_getObjectOldDir),
+ /* 90 */
+ OPCODE(o6_getActorWalkBox),
+ OPCODE(o6_getActorCostume),
+ OPCODE(o6_findInventory),
+ OPCODE(o6_getInventoryCount),
+ /* 94 */
+ OPCODE(o6_getVerbFromXY),
+ OPCODE(o6_beginOverride),
+ OPCODE(o6_endOverride),
+ OPCODE(o6_setObjectName),
+ /* 98 */
+ OPCODE(o6_isSoundRunning),
+ OPCODE(o6_setBoxFlags),
+ OPCODE(o6_invalid),
+ OPCODE(o70_resourceRoutines),
+ /* 9C */
+ OPCODE(o72_roomOps),
+ OPCODE(o72_actorOps),
+ OPCODE(o72_verbOps),
+ OPCODE(o6_getActorFromXY),
+ /* A0 */
+ OPCODE(o72_findObject),
+ OPCODE(o6_pseudoRoom),
+ OPCODE(o6_getActorElevation),
+ OPCODE(o6_getVerbEntrypoint),
+ /* A4 */
+ OPCODE(o72_arrayOps),
+ OPCODE(o6_saveRestoreVerbs),
+ OPCODE(o6_drawBox),
+ OPCODE(o6_pop),
+ /* A8 */
+ OPCODE(o6_getActorWidth),
+ OPCODE(o6_wait),
+ OPCODE(o6_getActorScaleX),
+ OPCODE(o6_getActorAnimCounter1),
+ /* AC */
+ OPCODE(o6_invalid),
+ OPCODE(o6_isAnyOf),
+ OPCODE(o72_systemOps),
+ OPCODE(o6_isActorInBox),
+ /* B0 */
+ OPCODE(o6_delay),
+ OPCODE(o6_delaySeconds),
+ OPCODE(o6_delayMinutes),
+ OPCODE(o6_stopSentence),
+ /* B4 */
+ OPCODE(o6_printLine),
+ OPCODE(o6_printText),
+ OPCODE(o6_printDebug),
+ OPCODE(o6_printSystem),
+ /* B8 */
+ OPCODE(o6_printActor),
+ OPCODE(o6_printEgo),
+ OPCODE(o72_talkActor),
+ OPCODE(o72_talkEgo),
+ /* BC */
+ OPCODE(o72_dimArray),
+ OPCODE(o6_stopObjectCode),
+ OPCODE(o6_startObjectQuick),
+ OPCODE(o6_startScriptQuick2),
+ /* C0 */
+ OPCODE(o72_dim2dimArray),
+ OPCODE(o72_traceStatus),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* C4 */
+ OPCODE(o6_abs),
+ OPCODE(o6_distObjectObject),
+ OPCODE(o6_distObjectPt),
+ OPCODE(o6_distPtPt),
+ /* C8 */
+ OPCODE(o72_kernelGetFunctions),
+ OPCODE(o70_kernelSetFunctions),
+ OPCODE(o6_delayFrames),
+ OPCODE(o6_pickOneOf),
+ /* CC */
+ OPCODE(o6_pickOneOfDefault),
+ OPCODE(o6_stampObject),
+ OPCODE(o72_drawWizImage),
+ OPCODE(o72_debugInput),
+ /* D0 */
+ OPCODE(o6_getDateTime),
+ OPCODE(o6_stopTalking),
+ OPCODE(o6_getAnimateVariable),
+ OPCODE(o6_invalid),
+ /* D4 */
+ OPCODE(o6_shuffle),
+ OPCODE(o72_jumpToScript),
+ OPCODE(o6_band),
+ OPCODE(o6_bor),
+ /* D8 */
+ OPCODE(o6_isRoomScriptRunning),
+ OPCODE(o60_closeFile),
+ OPCODE(o72_openFile),
+ OPCODE(o72_readFile),
+ /* DC */
+ OPCODE(o72_writeFile),
+ OPCODE(o72_findAllObjects),
+ OPCODE(o72_deleteFile),
+ OPCODE(o72_rename),
+ /* E0 */
+ OPCODE(o60_soundOps),
+ OPCODE(o72_getPixel),
+ OPCODE(o60_localizeArrayToScript),
+ OPCODE(o72_pickVarRandom),
+ /* E4 */
+ OPCODE(o6_setBoxSet),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* E8 */
+ OPCODE(o6_invalid),
+ OPCODE(o70_seekFilePos),
+ OPCODE(o72_redimArray),
+ OPCODE(o60_readFilePos),
+ /* EC */
+ OPCODE(o70_copyString),
+ OPCODE(o70_getStringWidth),
+ OPCODE(o70_getStringLen),
+ OPCODE(o70_appendString),
+ /* F0 */
+ OPCODE(o70_concatString),
+ OPCODE(o70_compareString),
+ OPCODE(o70_isResourceLoaded),
+ OPCODE(o72_readINI),
+ /* F4 */
+ OPCODE(o72_writeINI),
+ OPCODE(o70_getStringLenForWidth),
+ OPCODE(o70_getCharIndexInString),
+ OPCODE(o6_invalid),
+ /* F8 */
+ OPCODE(o72_getResourceSize),
+ OPCODE(o72_setFilePath),
+ OPCODE(o72_setSystemMessage),
+ OPCODE(o70_polygonOps),
+ /* FC */
+ OPCODE(o70_polygonHit),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ };
+
+ _opcodesV72he = opcodes;
+}
+
+void ScummEngine_v72he::executeOpcode(byte i) {
+ OpcodeProcV72he op = _opcodesV72he[i].proc;
+ (this->*op) ();
+}
+
+const char *ScummEngine_v72he::getOpcodeDesc(byte i) {
+ return _opcodesV72he[i].desc;
+}
+
+static const int arrayDataSizes[] = { 0, 1, 4, 8, 8, 16, 32 };
+
+ScummEngine_v72he::ArrayHeader *ScummEngine_v72he::defineArray(int array, int type, int dim2start, int dim2end,
+ int dim1start, int dim1end) {
+ int id;
+ int size;
+ ArrayHeader *ah;
+
+ assert(dim2start >= 0 && dim2start <= dim2end);
+ assert(dim1start >= 0 && dim1start <= dim1end);
+ assert(0 <= type && type <= 6);
+
+
+ if (type == kBitArray || type == kNibbleArray)
+ type = kByteArray;
+
+ nukeArray(array);
+
+ id = findFreeArrayId();
+
+ debug(9,"defineArray (array %d, dim2start %d, dim2end %d dim1start %d dim1end %d", id, dim2start, dim2end, dim1start, dim1end);
+
+ if (array & 0x80000000) {
+ error("Can't define bit variable as array pointer");
+ }
+
+ size = arrayDataSizes[type];
+
+ if (_heversion >= 80)
+ id |= 0x33539000;
+
+ writeVar(array, id);
+
+ if (_heversion >= 80)
+ id &= ~0x33539000;
+
+ size *= dim2end - dim2start + 1;
+ size *= dim1end - dim1start + 1;
+ size >>= 3;
+
+ ah = (ArrayHeader *)res.createResource(rtString, id, size + sizeof(ArrayHeader));
+
+ ah->type = TO_LE_32(type);
+ ah->dim1start = TO_LE_32(dim1start);
+ ah->dim1end = TO_LE_32(dim1end);
+ ah->dim2start = TO_LE_32(dim2start);
+ ah->dim2end = TO_LE_32(dim2end);
+
+ return ah;
+}
+
+int ScummEngine_v72he::readArray(int array, int idx2, int idx1) {
+ debug(9, "readArray (array %d, idx2 %d, idx1 %d)", readVar(array), idx2, idx1);
+
+ if (readVar(array) == 0)
+ error("readArray: Reference to zeroed array pointer");
+
+ ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, readVar(array));
+
+ if (ah == NULL || ah->data == NULL)
+ error("readArray: invalid array %d (%d)", array, readVar(array));
+
+ if (idx2 < (int)FROM_LE_32(ah->dim2start) || idx2 > (int)FROM_LE_32(ah->dim2end) ||
+ idx1 < (int)FROM_LE_32(ah->dim1start) || idx1 > (int)FROM_LE_32(ah->dim1end)) {
+ error("readArray: array %d out of bounds: [%d, %d] exceeds [%d..%d, %d..%d]",
+ array, idx1, idx2, FROM_LE_32(ah->dim1start), FROM_LE_32(ah->dim1end),
+ FROM_LE_32(ah->dim2start), FROM_LE_32(ah->dim2end));
+ }
+
+ const int offset = (FROM_LE_32(ah->dim1end) - FROM_LE_32(ah->dim1start) + 1) *
+ (idx2 - FROM_LE_32(ah->dim2start)) - FROM_LE_32(ah->dim1start) + idx1;
+
+ switch (FROM_LE_32(ah->type)) {
+ case kByteArray:
+ case kStringArray:
+ return ah->data[offset];
+
+ case kIntArray:
+ return (int16)READ_LE_UINT16(ah->data + offset * 2);
+
+ case kDwordArray:
+ return (int32)READ_LE_UINT32(ah->data + offset * 4);
+ }
+
+ return 0;
+}
+
+void ScummEngine_v72he::writeArray(int array, int idx2, int idx1, int value) {
+ debug(9, "writeArray (array %d, idx2 %d, idx1 %d, value %d)", readVar(array), idx2, idx1, value);
+
+ if (readVar(array) == 0)
+ error("writeArray: Reference to zeroed array pointer");
+
+ ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, readVar(array));
+
+ if (!ah)
+ error("writeArray: Invalid array (%d) reference", readVar(array));
+
+ if (idx2 < (int)FROM_LE_32(ah->dim2start) || idx2 > (int)FROM_LE_32(ah->dim2end) ||
+ idx1 < (int)FROM_LE_32(ah->dim1start) || idx1 > (int)FROM_LE_32(ah->dim1end)) {
+ error("writeArray: array %d out of bounds: [%d, %d] exceeds [%d..%d, %d..%d]",
+ array, idx1, idx2, FROM_LE_32(ah->dim1start), FROM_LE_32(ah->dim1end),
+ FROM_LE_32(ah->dim2start), FROM_LE_32(ah->dim2end));
+ }
+
+ const int offset = (FROM_LE_32(ah->dim1end) - FROM_LE_32(ah->dim1start) + 1) *
+ (idx2 - FROM_LE_32(ah->dim2start)) - FROM_LE_32(ah->dim1start) + idx1;
+
+ switch (FROM_LE_32(ah->type)) {
+ case kByteArray:
+ case kStringArray:
+ ah->data[offset] = value;
+ break;
+
+ case kIntArray:
+ WRITE_LE_UINT16(ah->data + offset * 2, value);
+ break;
+
+ case kDwordArray:
+ WRITE_LE_UINT32(ah->data + offset * 4, value);
+ break;
+ }
+}
+
+int ScummEngine_v72he::setupStringArray(int size) {
+ writeVar(0, 0);
+ defineArray(0, kStringArray, 0, 0, 0, size + 1);
+ writeArray(0, 0, 0, 0);
+ return readVar(0);
+}
+
+void ScummEngine_v72he::readArrayFromIndexFile() {
+ int num;
+ int a, b, c;
+
+ while ((num = _fileHandle->readUint16LE()) != 0) {
+ a = _fileHandle->readUint16LE();
+ b = _fileHandle->readUint16LE();
+ c = _fileHandle->readUint16LE();
+
+ if (c == 1)
+ defineArray(num, kBitArray, 0, a, 0, b);
+ else
+ defineArray(num, kDwordArray, 0, a, 0, b);
+ }
+}
+
+int ScummEngine_v72he::convertFilePath(byte *dst, bool setFilePath) {
+ debug(1, "convertFilePath: original filePath is %s", dst);
+
+ // Switch all \ to / for portablity
+ int len = resStrLen(dst) + 1;
+ for (int i = 0; i < len; i++) {
+ if (dst[i] == '\\')
+ dst[i] = '/';
+ }
+
+ // Strip path
+ int r = 0;
+ if (dst[0] == '.' && dst[1] == '/') {
+ r = 2;
+ } else if (dst[0] == 'c' && dst[1] == ':') {
+ for (r = len; r != 0; r--) {
+ if (dst[r - 1] == '/')
+ break;
+ }
+ }
+
+ if (setFilePath) {
+ char filePath[256];
+ sprintf(filePath, "%s%s", _gameDataPath.c_str(), dst + r);
+ if (!Common::File::exists(filePath)) {
+ sprintf(filePath, "%s%s", _saveFileMan->getSavePath(), dst + r);
+ }
+ strcpy((char *)dst, filePath);
+ debug(1, "convertFilePath: filePath is %s", dst);
+ }
+
+ return r;
+}
+
+void ScummEngine_v72he::copyScriptString(byte *dst, int dstSize) {
+ byte string[1024];
+ byte chr;
+ int pos = 0;
+
+ int array = pop();
+ if (array == -1) {
+ if (_stringLength == 1)
+ error("String stack underflow");
+
+ _stringLength -= 2;
+ while ((chr = _stringBuffer[_stringLength]) != 0) {
+ string[pos] = chr;
+ pos++;
+
+ if (pos > dstSize)
+ error("String too long to pop");
+
+ _stringLength--;
+ }
+
+ string[pos] = 0;
+ _stringLength++;
+
+ // Reverse string
+ int len = resStrLen(string);
+ while (len--)
+ *dst++ = string[len];
+ } else {
+ writeVar(0, array);
+ while ((chr = readArray(0, 0, pos)) != 0) {
+ *dst++ = chr;
+ pos++;
+ }
+ }
+ *dst = 0;
+}
+
+void ScummEngine_v72he::decodeScriptString(byte *dst, bool scriptString) {
+ const byte *src;
+ int args[31];
+ int num, len, val;
+ byte chr, string[1024];
+ memset(args, 0, sizeof(args));
+ memset(string, 0, sizeof(string));
+
+ // Get stack list, plus one
+ num = pop();
+ for (int i = num; i >= 0; i--)
+ args[i] = pop();
+
+ // Get string
+ if (scriptString) {
+ len = resStrLen(_scriptPointer) + 1;
+ memcpy(string, _scriptPointer, len);
+ _scriptPointer += len;
+ } else {
+ copyScriptString(string, sizeof(string));
+ len = resStrLen(string) + 1;
+ }
+
+ // Decode string
+ num = 0;
+ val = 0;
+ while (len--) {
+ chr = string[num++];
+ if (chr == '%') {
+ chr = string[num++];
+ switch(chr) {
+ case 'b':
+ //dst += sprintf((char *)dst, "%b", args[val++]);
+ break;
+ case 'c':
+ *dst++ = args[val++];
+ break;
+ case 'd':
+ dst += sprintf((char *)dst, "%d", args[val++]);
+ break;
+ case 's':
+ src = getStringAddress(args[val++]);
+ if (src) {
+ while (*src != 0)
+ *dst++ = *src++;
+ }
+ break;
+ case 'x':
+ dst += sprintf((char *)dst, "%x", args[val++]);
+ break;
+ default:
+ *dst++ = '%';
+ num--;
+ break;
+ }
+ } else {
+ *dst++ = chr;
+ }
+ }
+ *dst = 0;
+}
+
+byte *ScummEngine_v70he::heFindResourceData(uint32 tag, byte *ptr) {
+ ptr = heFindResource(tag, ptr);
+
+ if (ptr == NULL)
+ return NULL;
+ return ptr + _resourceHeaderSize;
+}
+
+byte *ScummEngine_v70he::heFindResource(uint32 tag, byte *searchin) {
+ uint32 curpos, totalsize, size;
+
+ debugC(DEBUG_RESOURCE, "heFindResource(%s, %lx)", tag2str(tag), searchin);
+
+ assert(searchin);
+ searchin += 4;
+ _resourceLastSearchSize = totalsize = READ_BE_UINT32(searchin);
+ curpos = 8;
+ searchin += 4;
+
+ while (curpos < totalsize) {
+ if (READ_UINT32(searchin) == tag) {
+ return searchin;
+ }
+
+ size = READ_BE_UINT32(searchin + 4);
+ if ((int32)size <= 0) {
+ error("(%s) Not found in %d... illegal block len %d", tag2str(tag), 0, size);
+ return NULL;
+ }
+
+ curpos += size;
+ searchin += size;
+ }
+
+ return NULL;
+}
+
+byte *ScummEngine_v70he::findWrappedBlock(uint32 tag, byte *ptr, int state, bool errorFlag) {
+ if (READ_UINT32(ptr) == MKID('MULT')) {
+ byte *offs, *wrap;
+ uint32 size;
+
+ wrap = heFindResource(MKID('WRAP'), ptr);
+ if (wrap == NULL)
+ return NULL;
+
+ offs = heFindResourceData(MKID('OFFS'), wrap);
+ if (offs == NULL)
+ return NULL;
+
+ size = getResourceDataSize(offs) / 4;
+ assert((uint32)state <= (uint32)size);
+
+
+ offs += READ_LE_UINT32(offs + state * sizeof(uint32));
+ offs = heFindResourceData(tag, offs - 8);
+ if (offs)
+ return offs;
+
+ offs = heFindResourceData(MKID('DEFA'), ptr);
+ if (offs == NULL)
+ return NULL;
+
+ return heFindResourceData(tag, offs - 8);
+ } else {
+ return heFindResourceData(tag, ptr);
+ }
+}
+
+int ScummEngine_v72he::findObject(int x, int y, int num, int *args) {
+ int b, cls, i, result;
+
+ for (i = 1; i < _numLocalObjects; i++) {
+ result = 0;
+ if ((_objs[i].obj_nr < 1) || getClass(_objs[i].obj_nr, kObjectClassUntouchable))
+ continue;
+
+ // Check polygon bounds
+ if (_wiz->polygonDefined(_objs[i].obj_nr)) {
+ if (_wiz->polygonHit(_objs[i].obj_nr, x, y))
+ result = _objs[i].obj_nr;
+ else if (VAR_POLYGONS_ONLY != 0xFF && VAR(VAR_POLYGONS_ONLY))
+ continue;
+ }
+
+ if (!result) {
+ // Check object bounds
+ if (_objs[i].x_pos <= x && _objs[i].width + _objs[i].x_pos > x &&
+ _objs[i].y_pos <= y && _objs[i].height + _objs[i].y_pos > y)
+ result = _objs[i].obj_nr;
+ }
+
+ if (result) {
+ if (!num)
+ return result;
+
+ // Check object class
+ cls = args[0];
+ b = getClass(_objs[i].obj_nr, cls);
+ if ((cls & 0x80 && b) || (!(cls & 0x80) && !b))
+ return result;
+ }
+ }
+
+ return 0;
+}
+
+void ScummEngine_v72he::o72_pushDWord() {
+ push(fetchScriptDWordSigned());
+}
+
+void ScummEngine_v72he::o72_getScriptString() {
+ byte chr;
+
+ while ((chr = fetchScriptByte()) != 0) {
+ _stringBuffer[_stringLength] = chr;
+ _stringLength++;
+
+ if (_stringLength >= 4096)
+ error("String stack overflow");
+ }
+
+ _stringBuffer[_stringLength] = 0;
+ _stringLength++;
+}
+
+void ScummEngine_v72he::o72_isAnyOf() {
+ int args[128];
+ int num, value;
+
+ num = getStackList(args, ARRAYSIZE(args));
+ value = pop();
+
+ for (int i = 0; i < num; i++) {
+ if (args[i] == value) {
+ push(1);
+ return;
+ }
+ }
+
+ push(0);
+}
+
+void ScummEngine_v72he::o72_resetCutscene() {
+ int idx;
+
+ idx = vm.cutSceneStackPointer;
+ vm.cutSceneStackPointer = 0;
+ vm.cutScenePtr[idx] = 0;
+ vm.cutSceneScript[idx] = 0;
+
+ VAR(VAR_OVERRIDE) = 0;
+}
+
+void ScummEngine_v72he::o72_findObjectWithClassOf() {
+ int args[16], num;
+
+ num = getStackList(args, ARRAYSIZE(args));
+ int y = pop();
+ int x = pop();
+ int r = findObject(x, y, num, args);
+ push(r);
+}
+
+void ScummEngine_v72he::o72_getObjectImageX() {
+ int object = pop();
+ int objnum = getObjectIndex(object);
+
+ if (objnum == -1) {
+ push(0);
+ return;
+ }
+
+ push(_objs[objnum].x_pos / 8);
+}
+
+void ScummEngine_v72he::o72_getObjectImageY() {
+ int object = pop();
+ int objnum = getObjectIndex(object);
+
+ if (objnum == -1) {
+ push(0);
+ return;
+ }
+
+ push(_objs[objnum].y_pos / 8);
+}
+
+void ScummEngine_v72he::o72_captureWizImage() {
+ Common::Rect grab;
+ grab.bottom = pop() + 1;
+ grab.right = pop() + 1;
+ grab.top = pop();
+ grab.left = pop();
+ _wiz->captureWizImage(pop(), grab, false, true);
+}
+
+void ScummEngine_v72he::o72_getTimer() {
+ int timer = pop();
+ byte cmd = fetchScriptByte();
+
+ if (cmd == 10 || cmd == 50) {
+ push(getHETimer(timer));
+ } else {
+ push(0);
+ }
+}
+
+void ScummEngine_v72he::o72_setTimer() {
+ int timer = pop();
+ byte cmd = fetchScriptByte();
+
+ if (cmd == 158 || cmd == 61) {
+ setHETimer(timer);
+ } else {
+ error("TIMER command %d?", cmd);
+ }
+}
+
+void ScummEngine_v72he::o72_getSoundPosition() {
+ int snd = pop();
+ push(_sound->getSoundPos(snd));
+}
+
+void ScummEngine_v72he::o72_startScript() {
+ int args[25];
+ int script;
+ byte flags;
+
+ getStackList(args, ARRAYSIZE(args));
+ script = pop();
+ flags = fetchScriptByte();
+ runScript(script, (flags == 199 || flags == 200), (flags == 195 || flags == 200), args);
+}
+
+void ScummEngine_v72he::o72_startObject() {
+ int args[25];
+ int script, entryp;
+ byte flags;
+
+ getStackList(args, ARRAYSIZE(args));
+ entryp = pop();
+ script = pop();
+ flags = fetchScriptByte();
+ runObjectScript(script, entryp, (flags == 199 || flags == 200), (flags == 195 || flags == 200), args);
+}
+
+void ScummEngine_v72he::o72_drawObject() {
+ byte subOp = fetchScriptByte();
+ int state, y, x;
+
+ switch (subOp) {
+ case 62:
+ state = pop();
+ y = pop();
+ x = pop();
+ break;
+ case 63:
+ state = pop();
+ if (state == 0)
+ state = 1;
+ y = x = -100;
+ break;
+ case 65:
+ state = 1;
+ y = pop();
+ x = pop();
+ break;
+ default:
+ error("o72_drawObject: default case %d", subOp);
+ }
+
+ int object = pop();
+ int objnum = getObjectIndex(object);
+ if (objnum == -1)
+ return;
+
+ if (y != -100 && x != -100) {
+ _objs[objnum].x_pos = x * 8;
+ _objs[objnum].y_pos = y * 8;
+ }
+
+ if (state != -1) {
+ addObjectToDrawQue(objnum);
+ putState(object, state);
+ }
+}
+
+void ScummEngine_v72he::o72_printWizImage() {
+ WizImage wi;
+ wi.resNum = pop();
+ wi.x1 = wi.y1 = 0;
+ wi.state = 0;
+ wi.flags = kWIFPrint;
+ _wiz->displayWizImage(&wi);
+}
+
+void ScummEngine_v72he::o72_getArrayDimSize() {
+ byte subOp = fetchScriptByte();
+ int32 val1, val2;
+ ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, readVar(fetchScriptWord()));
+ if (!ah) {
+ push(0);
+ return;
+ }
+
+ switch (subOp) {
+ case 1:
+ case 3:
+ val1 = FROM_LE_32(ah->dim1end);
+ val2 = FROM_LE_32(ah->dim1start);
+ push(val1 - val2 + 1);
+ break;
+ case 2:
+ val1 = FROM_LE_32(ah->dim2end);
+ val2 = FROM_LE_32(ah->dim2start);
+ push(val1 - val2 + 1);
+ break;
+ case 4:
+ push(FROM_LE_32(ah->dim1start));
+ break;
+ case 5:
+ push(FROM_LE_32(ah->dim1end));
+ break;
+ case 6:
+ push(FROM_LE_32(ah->dim2start));
+ break;
+ case 7:
+ push(FROM_LE_32(ah->dim2end));
+ break;
+ default:
+ error("o72_getArrayDimSize: default case %d", subOp);
+ }
+}
+
+void ScummEngine_v72he::o72_getNumFreeArrays() {
+ byte **addr = res.address[rtString];
+ int i, num = 0;
+
+ for (i = 1; i < _numArray; i++) {
+ if (!addr[i])
+ num++;
+ }
+
+ push (num);
+}
+
+void ScummEngine_v72he::o72_roomOps() {
+ int a, b, c, d, e;
+ byte filename[100];
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 172: // SO_ROOM_SCROLL
+ b = pop();
+ a = pop();
+ if (a < (_screenWidth / 2))
+ a = (_screenWidth / 2);
+ if (b < (_screenWidth / 2))
+ b = (_screenWidth / 2);
+ if (a > _roomWidth - (_screenWidth / 2))
+ a = _roomWidth - (_screenWidth / 2);
+ if (b > _roomWidth - (_screenWidth / 2))
+ b = _roomWidth - (_screenWidth / 2);
+ VAR(VAR_CAMERA_MIN_X) = a;
+ VAR(VAR_CAMERA_MAX_X) = b;
+ break;
+
+ case 174: // SO_ROOM_SCREEN
+ b = pop();
+ a = pop();
+ initScreens(a, _screenHeight);
+ break;
+
+ case 175: // SO_ROOM_PALETTE
+ d = pop();
+ c = pop();
+ b = pop();
+ a = pop();
+ setPalColor(d, a, b, c);
+ break;
+
+ case 179: // SO_ROOM_INTENSITY
+ c = pop();
+ b = pop();
+ a = pop();
+ darkenPalette(a, a, a, b, c);
+ break;
+
+ case 180: // SO_ROOM_SAVEGAME
+ _saveTemporaryState = true;
+ _saveLoadSlot = pop();
+ _saveLoadFlag = pop();
+ break;
+
+ case 181: // SO_ROOM_FADE
+ // Defaults to 1 but doesn't use fade effects
+ a = pop();
+ break;
+
+ case 182: // SO_RGB_ROOM_INTENSITY
+ e = pop();
+ d = pop();
+ c = pop();
+ b = pop();
+ a = pop();
+ darkenPalette(a, b, c, d, e);
+ break;
+
+ case 213: // SO_ROOM_NEW_PALETTE
+ a = pop();
+ setPalette(a);
+ break;
+
+ case 220:
+ a = pop();
+ b = pop();
+ copyPalColor(a, b);
+ break;
+
+ case 221:
+ copyScriptString(filename, sizeof(filename));
+ _saveLoadFlag = pop();
+ _saveLoadSlot = 1;
+ _saveTemporaryState = true;
+ break;
+
+ case 234:
+ b = pop();
+ a = pop();
+ swapObjects(a, b);
+ break;
+
+ case 236:
+ b = pop();
+ a = pop();
+ setRoomPalette(a, b);
+ break;
+
+ default:
+ error("o72_roomOps: default case %d", subOp);
+ }
+}
+
+void ScummEngine_v72he::o72_actorOps() {
+ Actor *a;
+ int i, j, k;
+ int args[32];
+ byte string[256];
+
+ byte subOp = fetchScriptByte();
+ if (subOp == 197) {
+ _curActor = pop();
+ return;
+ }
+
+ a = derefActorSafe(_curActor, "o72_actorOps");
+ if (!a)
+ return;
+
+ switch (subOp) {
+ case 21: // HE 80+
+ k = getStackList(args, ARRAYSIZE(args));
+ for (i = 0; i < k; ++i) {
+ a->setUserCondition(args[i] & 0x7F, args[i] & 0x80);
+ }
+ break;
+ case 24: // HE 80+
+ k = pop();
+ if (k == 0)
+ k = _rnd.getRandomNumberRng(1, 10);
+ a->_heNoTalkAnimation = 1;
+ a->setTalkCondition(k);
+ break;
+ case 43: // HE 90+
+ // HE games use reverse order of layering, so we adjust
+ a->_layer = -pop();
+ a->_needRedraw = true;
+ break;
+ case 64:
+ _actorClipOverride.bottom = pop();
+ _actorClipOverride.right = pop();
+ _actorClipOverride.top = pop();
+ _actorClipOverride.left = pop();
+ break;
+ case 67: // HE 99+
+ a->_clipOverride.bottom = pop();
+ a->_clipOverride.right = pop();
+ a->_clipOverride.top = pop();
+ a->_clipOverride.left = pop();
+ break;
+ case 65: // HE 98+
+ j = pop();
+ i = pop();
+ a->putActor(i, j, a->_room);
+ break;
+ case 68: // HE 90+
+ k = pop();
+ debug(0,"o72_actorOps: case 68 (%d)", k);
+ break;
+ case 76: // SO_COSTUME
+ a->setActorCostume(pop());
+ break;
+ case 77: // SO_STEP_DIST
+ j = pop();
+ i = pop();
+ a->setActorWalkSpeed(i, j);
+ break;
+ case 78: // SO_SOUND
+ k = getStackList(args, ARRAYSIZE(args));
+ for (i = 0; i < k; i++)
+ a->_sound[i] = args[i];
+ break;
+ case 79: // SO_WALK_ANIMATION
+ a->_walkFrame = pop();
+ break;
+ case 80: // SO_TALK_ANIMATION
+ a->_talkStopFrame = pop();
+ a->_talkStartFrame = pop();
+ break;
+ case 81: // SO_STAND_ANIMATION
+ a->_standFrame = pop();
+ break;
+ case 82: // SO_ANIMATION
+ // dummy case in scumm6
+ pop();
+ pop();
+ pop();
+ break;
+ case 83: // SO_DEFAULT
+ a->initActor(0);
+ break;
+ case 84: // SO_ELEVATION
+ a->setElevation(pop());
+ break;
+ case 85: // SO_ANIMATION_DEFAULT
+ a->_initFrame = 1;
+ a->_walkFrame = 2;
+ a->_standFrame = 3;
+ a->_talkStartFrame = 4;
+ a->_talkStopFrame = 5;
+ break;
+ case 86: // SO_PALETTE
+ j = pop();
+ i = pop();
+ checkRange(255, 0, i, "Illegal palette slot %d");
+ a->remapActorPaletteColor(i, j);
+ a->_needRedraw = true;
+ break;
+ case 87: // SO_TALK_COLOR
+ a->_talkColor = pop();
+ break;
+ case 88: // SO_ACTOR_NAME
+ copyScriptString(string, sizeof(string));
+ loadPtrToResource(rtActorName, a->_number, string);
+ break;
+ case 89: // SO_INIT_ANIMATION
+ a->_initFrame = pop();
+ break;
+ case 91: // SO_ACTOR_WIDTH
+ a->_width = pop();
+ break;
+ case 92: // SO_SCALE
+ i = pop();
+ a->setScale(i, i);
+ break;
+ case 93: // SO_NEVER_ZCLIP
+ a->_forceClip = 0;
+ break;
+ case 94: // SO_ALWAYS_ZCLIP
+ a->_forceClip = pop();
+ break;
+ case 95: // SO_IGNORE_BOXES
+ a->_ignoreBoxes = 1;
+ a->_forceClip = 0;
+ if (a->isInCurrentRoom())
+ a->putActor(a->_pos.x, a->_pos.y, a->_room);
+ break;
+ case 96: // SO_FOLLOW_BOXES
+ a->_ignoreBoxes = 0;
+ a->_forceClip = 0;
+ if (a->isInCurrentRoom())
+ a->putActor(a->_pos.x, a->_pos.y, a->_room);
+ break;
+ case 97: // SO_ANIMATION_SPEED
+ a->setAnimSpeed(pop());
+ break;
+ case 98: // SO_SHADOW
+ a->_heXmapNum = pop();
+ a->_needRedraw = true;
+ break;
+ case 99: // SO_TEXT_OFFSET
+ a->_talkPosY = pop();
+ a->_talkPosX = pop();
+ break;
+ case 156: // HE 72+
+ a->_charset = pop();
+ break;
+ case 175: // HE 99+
+ a->_hePaletteNum = pop();
+ a->_needRedraw = true;
+ break;
+ case 198: // SO_ACTOR_VARIABLE
+ i = pop();
+ a->setAnimVar(pop(), i);
+ break;
+ case 215: // SO_ACTOR_IGNORE_TURNS_ON
+ a->_ignoreTurns = true;
+ break;
+ case 216: // SO_ACTOR_IGNORE_TURNS_OFF
+ a->_ignoreTurns = false;
+ break;
+ case 217: // SO_ACTOR_NEW
+ a->initActor(2);
+ break;
+ case 218:
+ a->drawActorToBackBuf(a->_pos.x, a->_pos.y);
+ break;
+ case 219:
+ a->_drawToBackBuf = false;
+ a->_needRedraw = true;
+ a->_needBgReset = true;
+ break;
+ case 225:
+ {
+ copyScriptString(string, sizeof(string));
+ int slot = pop();
+
+ int len = resStrLen(string) + 1;
+ memcpy(a->_heTalkQueue[slot].sentence, string, len);
+
+ a->_heTalkQueue[slot].posX = a->_talkPosX;
+ a->_heTalkQueue[slot].posY = a->_talkPosY;
+ a->_heTalkQueue[slot].color = a->_talkColor;
+ break;
+ }
+ default:
+ error("o72_actorOps: default case %d", subOp);
+ }
+}
+
+void ScummEngine_v72he::o72_verbOps() {
+ int slot, a, b;
+ VerbSlot *vs;
+ byte name[200];
+
+ byte subOp = fetchScriptByte();
+ if (subOp == 196) {
+ _curVerb = pop();
+ _curVerbSlot = getVerbSlot(_curVerb, 0);
+ checkRange(_numVerbs - 1, 0, _curVerbSlot, "Illegal new verb slot %d");
+ return;
+ }
+ vs = &_verbs[_curVerbSlot];
+ slot = _curVerbSlot;
+ switch (subOp) {
+ case 124: // SO_VERB_IMAGE
+ a = pop();
+ if (_curVerbSlot) {
+ setVerbObject(_roomResource, a, slot);
+ vs->type = kImageVerbType;
+ vs->imgindex = a;
+ }
+ break;
+ case 125: // SO_VERB_NAME
+ copyScriptString(name, sizeof(name));
+ loadPtrToResource(rtVerb, slot, name);
+ vs->type = kTextVerbType;
+ vs->imgindex = 0;
+ break;
+ case 126: // SO_VERB_COLOR
+ vs->color = pop();
+ break;
+ case 127: // SO_VERB_HICOLOR
+ vs->hicolor = pop();
+ break;
+ case 128: // SO_VERB_AT
+ vs->curRect.top = pop();
+ vs->curRect.left = pop();
+ break;
+ case 129: // SO_VERB_ON
+ vs->curmode = 1;
+ break;
+ case 130: // SO_VERB_OFF
+ vs->curmode = 0;
+ break;
+ case 131: // SO_VERB_DELETE
+ slot = getVerbSlot(pop(), 0);
+ killVerb(slot);
+ break;
+ case 132: // SO_VERB_NEW
+ slot = getVerbSlot(_curVerb, 0);
+ if (slot == 0) {
+ for (slot = 1; slot < _numVerbs; slot++) {
+ if (_verbs[slot].verbid == 0)
+ break;
+ }
+ if (slot == _numVerbs)
+ error("Too many verbs");
+ _curVerbSlot = slot;
+ }
+ vs = &_verbs[slot];
+ vs->verbid = _curVerb;
+ vs->color = 2;
+ vs->hicolor = 0;
+ vs->dimcolor = 8;
+ vs->type = kTextVerbType;
+ vs->charset_nr = _string[0]._default.charset;
+ vs->curmode = 0;
+ vs->saveid = 0;
+ vs->key = 0;
+ vs->center = 0;
+ vs->imgindex = 0;
+ break;
+ case 133: // SO_VERB_DIMCOLOR
+ vs->dimcolor = pop();
+ break;
+ case 134: // SO_VERB_DIM
+ vs->curmode = 2;
+ break;
+ case 135: // SO_VERB_KEY
+ vs->key = pop();
+ break;
+ case 136: // SO_VERB_CENTER
+ vs->center = 1;
+ break;
+ case 137: // SO_VERB_NAME_STR
+ a = pop();
+ if (a == 0) {
+ loadPtrToResource(rtVerb, slot, (const byte *)"");
+ } else {
+ loadPtrToResource(rtVerb, slot, getStringAddress(a));
+ }
+ vs->type = kTextVerbType;
+ vs->imgindex = 0;
+ break;
+ case 139: // SO_VERB_IMAGE_IN_ROOM
+ b = pop();
+ a = pop();
+
+ if (slot && a != vs->imgindex) {
+ setVerbObject(b, a, slot);
+ vs->type = kImageVerbType;
+ vs->imgindex = a;
+ }
+ break;
+ case 140: // SO_VERB_BAKCOLOR
+ vs->bkcolor = pop();
+ break;
+ case 255:
+ drawVerb(slot, 0);
+ verbMouseOver(0);
+ break;
+ default:
+ error("o72_verbops: default case %d", subOp);
+ }
+}
+
+void ScummEngine_v72he::o72_findObject() {
+ int y = pop();
+ int x = pop();
+ int r = findObject(x, y, 0, 0);
+ push(r);
+}
+
+void ScummEngine_v72he::o72_arrayOps() {
+ ArrayHeader *ah;
+ byte string[1024];
+ int dim1end, dim1start, dim2end, dim2start;
+ int id, len, b, c, list[128];
+ int offs, tmp, tmp2;
+ uint tmp3;
+
+ byte subOp = fetchScriptByte();
+ int array = fetchScriptWord();
+ debug(9,"o72_arrayOps: array %d case %d", array, subOp);
+
+ switch (subOp) {
+ case 7: // SO_ASSIGN_STRING
+ copyScriptString(string, sizeof(string));
+ len = resStrLen(string);
+ ah = defineArray(array, kStringArray, 0, 0, 0, len);
+ memcpy(ah->data, string, len);
+ break;
+
+ case 126:
+ len = getStackList(list, ARRAYSIZE(list));
+ dim1end = pop();
+ dim1start = pop();
+ dim2end = pop();
+ dim2start = pop();
+ id = readVar(array);
+ if (id == 0) {
+ defineArray(array, kDwordArray, dim2start, dim2end, dim1start, dim1end);
+ }
+ tmp2 = 0;
+ while (dim2start <= dim2end) {
+ tmp = dim1start;
+ while (tmp <= dim1end) {
+ writeArray(array, dim2start, tmp, list[tmp2++]);
+ if (tmp2 == len)
+ tmp2 = 0;
+ tmp++;
+ }
+ dim2start++;
+ }
+ break;
+ case 127:
+ {
+ int a2_dim1end = pop();
+ int a2_dim1start = pop();
+ int a2_dim2end = pop();
+ int a2_dim2start = pop();
+ int array2 = fetchScriptWord();
+ int a1_dim1end = pop();
+ int a1_dim1start = pop();
+ int a1_dim2end = pop();
+ int a1_dim2start = pop();
+ if (a1_dim1end - a1_dim1start != a2_dim1end - a2_dim1start || a2_dim2end - a2_dim2start != a1_dim2end - a1_dim2start) {
+ error("Source and dest ranges size are mismatched");
+ }
+ copyArray(array, a1_dim2start, a1_dim2end, a1_dim1start, a1_dim1end, array2, a2_dim2start, a2_dim2end, a2_dim1start, a2_dim1end);
+ }
+ break;
+ case 128:
+ b = pop();
+ c = pop();
+ dim1end = pop();
+ dim1start = pop();
+ dim2end = pop();
+ dim2start = pop();
+ id = readVar(array);
+ if (id == 0) {
+ defineArray(array, kDwordArray, dim2start, dim2end, dim1start, dim1end);
+ }
+
+ offs = (b >= c) ? 1 : -1;
+ tmp2 = c;
+ tmp3 = c - b + 1;
+ while (dim2start <= dim2end) {
+ tmp = dim1start;
+ while (tmp <= dim1end) {
+ writeArray(array, dim2start, tmp, tmp2);
+ if (--tmp3 == 0) {
+ tmp2 = c;
+ tmp3 = c - b + 1;
+ } else {
+ tmp2 += offs;
+ }
+ tmp++;
+ }
+ dim2start++;
+ }
+ break;
+ case 194:
+ decodeScriptString(string);
+ len = resStrLen(string);
+ ah = defineArray(array, kStringArray, 0, 0, 0, len);
+ memcpy(ah->data, string, len);
+ break;
+ case 208: // SO_ASSIGN_INT_LIST
+ b = pop();
+ c = pop();
+ id = readVar(array);
+ if (id == 0) {
+ defineArray(array, kDwordArray, 0, 0, 0, b + c - 1);
+ }
+ while (c--) {
+ writeArray(array, 0, b + c, pop());
+ }
+ break;
+ case 212: // SO_ASSIGN_2DIM_LIST
+ len = getStackList(list, ARRAYSIZE(list));
+ id = readVar(array);
+ if (id == 0)
+ error("Must DIM a two dimensional array before assigning");
+ c = pop();
+ while (--len >= 0) {
+ writeArray(array, c, len, list[len]);
+ }
+ break;
+ default:
+ error("o72_arrayOps: default case %d (array %d)", subOp, array);
+ }
+}
+
+void ScummEngine_v72he::o72_systemOps() {
+ byte string[1024];
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 22: // HE80+
+ clearDrawObjectQueue();
+ break;
+ case 26: // HE80+
+ gdi.copyVirtScreenBuffers(Common::Rect(_screenWidth, _screenHeight));
+ updatePalette();
+ break;
+ case 158:
+ restart();
+ break;
+ case 160:
+ // Confirm shutdown
+ shutDown();
+ break;
+ case 244:
+ shutDown();
+ break;
+ case 251:
+ copyScriptString(string, sizeof(string));
+ debug(0, "Start executable (%s)", string);
+ break;
+ case 252:
+ copyScriptString(string, sizeof(string));
+ debug(0, "Start game (%s)", string);
+ break;
+ default:
+ error("o72_systemOps invalid case %d", subOp);
+ }
+}
+
+void ScummEngine_v72he::o72_talkActor() {
+ Actor *a;
+
+ int act = pop();
+
+ _string[0].loadDefault();
+
+ // A value of 225 can occur when examining the gold in the mine of pajama, after mining the gold.
+ // This is a script bug, the script should set the subtitle color, not actor number.
+ // This script bug was fixed in the updated version of pajama.
+ if (act == 225) {
+ _string[0].color = act;
+ } else {
+ _actorToPrintStrFor = act;
+ if (_actorToPrintStrFor != 0xFF) {
+ a = derefActor(_actorToPrintStrFor, "o72_talkActor");
+ _string[0].color = a->_talkColor;
+ }
+ }
+
+ actorTalk(_scriptPointer);
+
+ _scriptPointer += resStrLen(_scriptPointer) + 1;
+}
+
+void ScummEngine_v72he::o72_talkEgo() {
+ push(VAR(VAR_EGO));
+ o72_talkActor();
+}
+
+void ScummEngine_v72he::o72_dimArray() {
+ int data;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 2: // SO_BIT_ARRAY
+ data = kBitArray;
+ break;
+ case 3: // SO_NIBBLE_ARRAY
+ data = kNibbleArray;
+ break;
+ case 4: // SO_BYTE_ARRAY
+ data = kByteArray;
+ break;
+ case 5: // SO_INT_ARRAY
+ data = kIntArray;
+ break;
+ case 6:
+ data = kDwordArray;
+ break;
+ case 7: // SO_STRING_ARRAY
+ data = kStringArray;
+ break;
+ case 204: // SO_UNDIM_ARRAY
+ nukeArray(fetchScriptWord());
+ return;
+ default:
+ error("o72_dimArray: default case %d", subOp);
+ }
+
+ defineArray(fetchScriptWord(), data, 0, 0, 0, pop());
+}
+
+
+void ScummEngine_v72he::o72_dim2dimArray() {
+ int data, dim1end, dim2end;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 2: // SO_BIT_ARRAY
+ data = kBitArray;
+ break;
+ case 3: // SO_NIBBLE_ARRAY
+ data = kNibbleArray;
+ break;
+ case 4: // SO_BYTE_ARRAY
+ data = kByteArray;
+ break;
+ case 5: // SO_INT_ARRAY
+ data = kIntArray;
+ break;
+ case 6:
+ data = kDwordArray;
+ break;
+ case 7: // SO_STRING_ARRAY
+ data = kStringArray;
+ break;
+ default:
+ error("o72_dim2dimArray: default case %d", subOp);
+ }
+
+ dim1end = pop();
+ dim2end = pop();
+ defineArray(fetchScriptWord(), data, 0, dim2end, 0, dim1end);
+}
+
+void ScummEngine_v72he::o72_traceStatus() {
+ byte string[80];
+
+ copyScriptString(string, sizeof(string));
+ pop();
+}
+
+void ScummEngine_v72he::o72_kernelGetFunctions() {
+ int args[29];
+ ArrayHeader *ah;
+ getStackList(args, ARRAYSIZE(args));
+
+ switch (args[0]) {
+ case 1:
+ writeVar(0, 0);
+ ah = defineArray(0, kByteArray, 0, 0, 0, virtScreenSave(0, args[1], args[2], args[3], args[4]));
+ virtScreenSave(ah->data, args[1], args[2], args[3], args[4]);
+ push(readVar(0));
+ break;
+ default:
+ error("o72_kernelGetFunctions: default case %d", args[0]);
+ }
+}
+
+void ScummEngine_v72he::o72_drawWizImage() {
+ WizImage wi;
+ wi.flags = pop();
+ wi.y1 = pop();
+ wi.x1 = pop();
+ wi.resNum = pop();
+ wi.state = 0;
+ _wiz->displayWizImage(&wi);
+}
+
+void ScummEngine_v72he::o72_debugInput() {
+ byte string[255];
+
+ copyScriptString(string, sizeof(string));
+ int len = resStrLen(string) + 1;
+
+ writeVar(0, 0);
+ ArrayHeader *ah = defineArray(0, kStringArray, 0, 0, 0, len);
+ memcpy(ah->data, string, len);
+ push(readVar(0));
+ debug(1,"o72_debugInput: String %s", string);
+}
+
+void ScummEngine_v72he::o72_jumpToScript() {
+ int args[25];
+ int script;
+ byte flags;
+
+ getStackList(args, ARRAYSIZE(args));
+ script = pop();
+ flags = fetchScriptByte();
+ stopObjectCode();
+ runScript(script, (flags == 199 || flags == 200), (flags == 195 || flags == 200), args);
+}
+
+void ScummEngine_v72he::o72_openFile() {
+ int mode, slot, i;
+ byte filename[256];
+
+ mode = pop();
+ copyScriptString(filename, sizeof(filename));
+
+ debug(1,"Original filename %s", filename);
+
+ // There are Macintosh specific versions of HE7.2 games.
+ if (_heversion >= 80 && _platform == Common::kPlatformMacintosh) {
+ // Work around for filename difference in HE7 file, needs to
+ // open 'Water (7)' instead of 'Water Worries (7)'.
+ if (_gameId == GID_WATER && _heversion == 99 && !strcmp((char *)filename, "Water.he7")) {
+ strcpy((char *)filename, "Water (7)");
+ } else {
+ char buf1[128];
+ buf1[0] = '\0';
+ generateSubstResFileName((char *)filename, buf1, sizeof(buf1));
+ if (buf1[0]) {
+ strcpy((char *)filename, buf1);
+ }
+ }
+ }
+
+ int r = convertFilePath(filename);
+ debug(1,"Final filename to %s", filename + r);
+
+ slot = -1;
+ for (i = 1; i < 17; i++) {
+ if (_hFileTable[i].isOpen() == false) {
+ slot = i;
+ break;
+ }
+ }
+
+ if (slot != -1) {
+ switch(mode) {
+ case 1:
+ _hFileTable[slot].open((char*)filename + r, Common::File::kFileReadMode, _saveFileMan->getSavePath());
+ if (_hFileTable[slot].isOpen() == false)
+ _hFileTable[slot].open((char*)filename + r, Common::File::kFileReadMode, _gameDataPath.c_str());
+ break;
+ case 2:
+ _hFileTable[slot].open((char*)filename + r, Common::File::kFileWriteMode, _saveFileMan->getSavePath());
+ break;
+ default:
+ error("o72_openFile(): wrong open file mode %d", mode);
+ }
+
+ if (_hFileTable[slot].isOpen() == false)
+ slot = -1;
+
+ }
+ debug(1, "o72_openFile: slot %d, mode %d", slot, mode);
+ push(slot);
+}
+
+int ScummEngine_v72he::readFileToArray(int slot, int32 size) {
+ if (size == 0)
+ size = _hFileTable[slot].size() - _hFileTable[slot].pos();
+
+ writeVar(0, 0);
+ ArrayHeader *ah = defineArray(0, kByteArray, 0, 0, 0, size);
+
+ if (_hFileTable[slot].isOpen())
+ _hFileTable[slot].read(ah->data, size + 1);
+
+ return readVar(0);
+}
+
+void ScummEngine_v72he::o72_readFile() {
+ int slot, val;
+ int32 size;
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 4:
+ slot = pop();
+ val = _hFileTable[slot].readByte();
+ push(val);
+ break;
+ case 5:
+ slot = pop();
+ val = _hFileTable[slot].readUint16LE();
+ push(val);
+ break;
+ case 6:
+ slot = pop();
+ val = _hFileTable[slot].readUint32LE();
+ push(val);
+ break;
+ case 8:
+ fetchScriptByte();
+ size = pop();
+ slot = pop();
+ val = readFileToArray(slot, size);
+ push(val);
+ break;
+ default:
+ error("o72_readFile: default case %d", subOp);
+ }
+}
+
+void ScummEngine_v72he::writeFileFromArray(int slot, int32 resID) {
+ ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, resID);
+ int32 size = (FROM_LE_32(ah->dim1end) - FROM_LE_32(ah->dim1start) + 1) *
+ (FROM_LE_32(ah->dim2end) - FROM_LE_32(ah->dim2start) + 1);
+
+ _hFileTable[slot].write(ah->data, size);
+}
+
+void ScummEngine_v72he::o72_writeFile() {
+ int32 resID = pop();
+ int slot = pop();
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 4:
+ _hFileTable[slot].writeByte(resID);
+ break;
+ case 5:
+ _hFileTable[slot].writeUint16LE(resID);
+ break;
+ case 6:
+ _hFileTable[slot].writeUint32LE(resID);
+ break;
+ case 8:
+ fetchScriptByte();
+ writeFileFromArray(slot, resID);
+ break;
+ default:
+ error("o72_writeFile: default case %d", subOp);
+ }
+}
+
+void ScummEngine_v72he::o72_findAllObjects() {
+ int room = pop();
+ int i;
+
+ if (room != _currentRoom)
+ error("o72_findAllObjects: current room is not %d", room);
+
+ writeVar(0, 0);
+ defineArray(0, kDwordArray, 0, 0, 0, _numLocalObjects);
+ writeArray(0, 0, 0, _numLocalObjects);
+
+ for (i = 1; i < _numLocalObjects; i++) {
+ writeArray(0, 0, i, _objs[i].obj_nr);
+ }
+
+ push(readVar(0));
+}
+
+void ScummEngine_v72he::o72_deleteFile() {
+ byte filename[256];
+
+ copyScriptString(filename, sizeof(filename));
+ debug(1, "stub o72_deleteFile(%s)", filename);
+}
+
+void ScummEngine_v72he::o72_rename() {
+ byte oldFilename[100],newFilename[100];
+
+ copyScriptString(newFilename, sizeof(newFilename));
+ copyScriptString(oldFilename, sizeof(oldFilename));
+
+ debug(1, "stub o72_rename(%s to %s)", oldFilename, newFilename);
+}
+
+void ScummEngine_v72he::o72_getPixel() {
+ byte area;
+
+ int y = pop();
+ int x = pop();
+ byte subOp = fetchScriptByte();
+
+ VirtScreen *vs = findVirtScreen(y);
+ if (vs == NULL || x > _screenWidth - 1 || x < 0) {
+ push(-1);
+ return;
+ }
+
+ switch (subOp) {
+ case 9: // HE 100
+ case 218:
+ area = *vs->getBackPixels(x, y - vs->topline);
+ break;
+ case 8: // HE 100
+ case 219:
+ area = *vs->getPixels(x, y - vs->topline);
+ break;
+ default:
+ error("o72_getPixel: default case %d", subOp);
+ }
+ push(area);
+}
+
+void ScummEngine_v72he::o72_pickVarRandom() {
+ int num;
+ int args[100];
+ int32 dim1end;
+
+ num = getStackList(args, ARRAYSIZE(args));
+ int value = fetchScriptWord();
+
+ if (readVar(value) == 0) {
+ defineArray(value, kDwordArray, 0, 0, 0, num);
+ if (num > 0) {
+ int16 counter = 0;
+ do {
+ writeArray(value, 0, counter + 1, args[counter]);
+ } while (++counter < num);
+ }
+
+ shuffleArray(value, 1, num);
+ writeArray(value, 0, 0, 2);
+ push(readArray(value, 0, 1));
+ return;
+ }
+
+ num = readArray(value, 0, 0);
+
+ ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, readVar(value));
+ dim1end = FROM_LE_32(ah->dim1end);
+
+ if (dim1end < num) {
+ int32 var_2 = readArray(value, 0, num - 1);
+ shuffleArray(value, 1, dim1end);
+ if (readArray(value, 0, 1) == var_2) {
+ num = 2;
+ } else {
+ num = 1;
+ }
+ }
+
+ writeArray(value, 0, 0, num + 1);
+ push(readArray(value, 0, num));
+}
+
+void ScummEngine_v72he::o72_redimArray() {
+ int newX, newY;
+ newY = pop();
+ newX = pop();
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 5:
+ redimArray(fetchScriptWord(), 0, newX, 0, newY, kIntArray);
+ break;
+ case 4:
+ redimArray(fetchScriptWord(), 0, newX, 0, newY, kByteArray);
+ break;
+ case 6:
+ redimArray(fetchScriptWord(), 0, newX, 0, newY, kDwordArray);
+ break;
+ default:
+ error("o72_redimArray: default type %d", subOp);
+ }
+}
+
+void ScummEngine_v72he::redimArray(int arrayId, int newDim2start, int newDim2end,
+ int newDim1start, int newDim1end, int type) {
+ int newSize, oldSize;
+
+ if (readVar(arrayId) == 0)
+ error("redimArray: Reference to zeroed array pointer");
+
+ ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, readVar(arrayId));
+
+ if (!ah)
+ error("redimArray: Invalid array (%d) reference", readVar(arrayId));
+
+ newSize = arrayDataSizes[type];
+ oldSize = arrayDataSizes[FROM_LE_32(ah->type)];
+
+ newSize *= (newDim1end - newDim1start + 1) * (newDim2end - newDim2start + 1);
+ oldSize *= (FROM_LE_32(ah->dim1end) - FROM_LE_32(ah->dim1start) + 1) *
+ (FROM_LE_32(ah->dim2end) - FROM_LE_32(ah->dim2start) + 1);
+
+ newSize >>= 3;
+ oldSize >>= 3;
+
+ if (newSize != oldSize)
+ error("redimArray: array %d redim mismatch", readVar(arrayId));
+
+ ah->type = TO_LE_32(type);
+ ah->dim1start = TO_LE_32(newDim1start);
+ ah->dim1end = TO_LE_32(newDim1end);
+ ah->dim2start = TO_LE_32(newDim2start);
+ ah->dim2end = TO_LE_32(newDim2end);
+}
+
+void ScummEngine_v72he::checkArrayLimits(int array, int dim2start, int dim2end, int dim1start, int dim1end) {
+ if (dim1end < dim1start) {
+ error("Across max %d smaller than min %d", dim1end, dim1start);
+ }
+ if (dim2end < dim2start) {
+ error("Down max %d smaller than min %d", dim2end, dim2start);
+ }
+ ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, readVar(array));
+ assert(ah);
+ if (ah->dim2start > dim2start || ah->dim2end < dim2end || ah->dim1start > dim1start || ah->dim1end < dim1end) {
+ error("Invalid array access (%d,%d,%d,%d) limit (%d,%d,%d,%d)", dim2start, dim2end, dim1start, dim1end, ah->dim2start, ah->dim2end, ah->dim1start, ah->dim1end);
+ }
+}
+
+void ScummEngine_v72he::copyArray(int array1, int a1_dim2start, int a1_dim2end, int a1_dim1start, int a1_dim1end,
+ int array2, int a2_dim2start, int a2_dim2end, int a2_dim1start, int a2_dim1end)
+{
+ byte *dst, *src;
+ int dstPitch, srcPitch;
+ int rowSize;
+ checkArrayLimits(array1, a1_dim2start, a1_dim2end, a1_dim1start, a1_dim1end);
+ checkArrayLimits(array2, a2_dim2start, a2_dim2end, a2_dim1start, a2_dim1end);
+ int a12_num = a1_dim2end - a1_dim2start + 1;
+ int a11_num = a1_dim1end - a1_dim1start + 1;
+ int a22_num = a2_dim2end - a2_dim2start + 1;
+ int a21_num = a2_dim1end - a2_dim1start + 1;
+ if (a22_num != a12_num || a21_num != a11_num) {
+ error("Operation size mismatch (%d vs %d)(%d vs %d)", a12_num, a22_num, a11_num, a21_num);
+ }
+
+ if (array1 != array2) {
+ ArrayHeader *ah1 = (ArrayHeader *)getResourceAddress(rtString, readVar(array1));
+ assert(ah1);
+ ArrayHeader *ah2 = (ArrayHeader *)getResourceAddress(rtString, readVar(array2));
+ assert(ah2);
+ if (FROM_LE_32(ah1->type) == FROM_LE_32(ah2->type)) {
+ copyArrayHelper(ah1, a1_dim2start, a1_dim1start, a1_dim1end, &dst, &dstPitch, &rowSize);
+ copyArrayHelper(ah2, a2_dim2start, a2_dim1start, a2_dim1end, &src, &srcPitch, &rowSize);
+ for (; a1_dim2start <= a1_dim2end; ++a1_dim2start) {
+ memcpy(dst, src, rowSize);
+ dst += dstPitch;
+ src += srcPitch;
+ }
+ } else {
+ for (; a1_dim2start <= a1_dim2end; ++a1_dim2start, ++a2_dim2start) {
+ int a2dim1 = a2_dim1start;
+ int a1dim1 = a1_dim1start;
+ for (; a1dim1 <= a1_dim1end; ++a1dim1, ++a2dim1) {
+ int val = readArray(array2, a2_dim2start, a2dim1);
+ writeArray(array1, a1_dim2start, a1dim1, val);
+ }
+ }
+ }
+ } else {
+ if (a2_dim2start != a1_dim2start || a2_dim1start != a1_dim1start) {
+ ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, readVar(array1));
+ assert(ah);
+ if (a2_dim2start > a1_dim2start) {
+ copyArrayHelper(ah, a1_dim2start, a1_dim1start, a1_dim1end, &dst, &dstPitch, &rowSize);
+ copyArrayHelper(ah, a2_dim2start, a2_dim1start, a2_dim1end, &src, &srcPitch, &rowSize);
+ } else {
+ copyArrayHelper(ah, a1_dim2end, a1_dim1start, a1_dim1end, &dst, &dstPitch, &rowSize);
+ copyArrayHelper(ah, a2_dim2end, a2_dim1start, a2_dim1end, &src, &srcPitch, &rowSize);
+ }
+ for (; a1_dim2start <= a1_dim2end; ++a1_dim2start) {
+ memcpy(dst, src, rowSize);
+ dst += dstPitch;
+ src += srcPitch;
+ }
+ }
+ }
+}
+
+void ScummEngine_v72he::copyArrayHelper(ArrayHeader *ah, int idx2, int idx1, int len1, byte **data, int *size, int *num) {
+ const int pitch = FROM_LE_32(ah->dim1end) - FROM_LE_32(ah->dim1start) + 1;
+ const int offset = pitch * (idx2 - FROM_LE_32(ah->dim2start)) + idx1 - FROM_LE_32(ah->dim1start);
+
+ switch (FROM_LE_32(ah->type)) {
+ case kByteArray:
+ case kStringArray:
+ *num = len1 - idx1 + 1;
+ *size = pitch;
+ *data = ah->data + offset;
+ break;
+ case kIntArray:
+ *num = (len1 - idx1) * 2 + 2;
+ *size = pitch * 2;
+ *data = ah->data + offset * 2;
+ break;
+ case kDwordArray:
+ *num = (len1 - idx1) * 4 + 4;
+ *size = pitch * 4;
+ *data = ah->data + offset * 4;
+ break;
+ default:
+ error("Invalid array type", FROM_LE_32(ah->type));
+ }
+}
+
+void ScummEngine_v72he::o72_readINI() {
+ byte option[128];
+ ArrayHeader *ah;
+ const char *entry;
+ int len;
+
+ copyScriptString(option, sizeof(option));
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 43: // HE 100
+ case 6: // number
+ if (!strcmp((char *)option, "NoPrinting")) {
+ push(1);
+ } else if (!strcmp((char *)option, "TextOn")) {
+ push(ConfMan.getBool("subtitles"));
+ } else {
+ push(ConfMan.getInt((char *)option));
+ }
+ break;
+ case 77: // HE 100
+ case 7: // string
+ entry = (ConfMan.get((char *)option).c_str());
+
+ writeVar(0, 0);
+ len = resStrLen((const byte *)entry);
+ ah = defineArray(0, kStringArray, 0, 0, 0, len);
+ memcpy(ah->data, entry, len);
+
+ push(readVar(0));
+ break;
+ default:
+ error("o72_readINI: default type %d", subOp);
+ }
+
+ debug(1, "o72_readINI: Option %s", option);
+}
+
+void ScummEngine_v72he::o72_writeINI() {
+ int value;
+ byte option[256], string[1024];
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 43: // HE 100
+ case 6: // number
+ value = pop();
+ copyScriptString(option, sizeof(option));
+ ConfMan.set((char *)option, value);
+ debug(1, "o72_writeINI: Option %s Value %d", option, value);
+ break;
+ case 77: // HE 100
+ case 7: // string
+ copyScriptString(string, sizeof(string));
+ copyScriptString(option, sizeof(option));
+
+ // Filter out useless setting
+ if (!strcmp((char *)option, "HETest"))
+ return;
+
+ // Filter out confusing subtitle setting
+ if (!strcmp((char *)option, "TextOn"))
+ return;
+
+ // Filter out confusing path settings
+ if (!strcmp((char *)option, "DownLoadPath") || !strcmp((char *)option, "GameResourcePath") || !strcmp((char *)option, "SaveGamePath"))
+ return;
+
+ ConfMan.set((char *)option, (char *)string);
+ debug(1, "o72_writeINI: Option %s String %s", option, string);
+ break;
+ default:
+ error("o72_writeINI: default type %d", subOp);
+ }
+
+ ConfMan.flushToDisk();
+}
+
+void ScummEngine_v72he::o72_getResourceSize() {
+ const byte *ptr;
+ int size, type;
+
+ int resid = pop();
+ if (_heversion == 72) {
+ push(getSoundResourceSize(resid));
+ return;
+ }
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 13:
+ push (getSoundResourceSize(resid));
+ return;
+ case 14:
+ type = rtRoomImage;
+ break;
+ case 15:
+ type = rtImage;
+ break;
+ case 16:
+ type = rtCostume;
+ break;
+ case 17:
+ type = rtScript;
+ break;
+ default:
+ error("o72_getResourceSize: default type %d", subOp);
+ }
+
+ ptr = getResourceAddress(type, resid);
+ assert(ptr);
+ size = READ_BE_UINT32(ptr + 4) - 8;
+ push(size);
+}
+
+void ScummEngine_v72he::o72_setFilePath() {
+ byte filename[255];
+
+ copyScriptString(filename, sizeof(filename));
+ debug(1,"o72_setFilePath: %s", filename);
+}
+
+void ScummEngine_v72he::o72_setSystemMessage() {
+ byte name[1024];
+
+ copyScriptString(name, sizeof(name));
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 240:
+ debug(1,"o72_setSystemMessage: (%d) %s", subOp, name);
+ break;
+ case 241: // Set Version
+ debug(1,"o72_setSystemMessage: (%d) %s", subOp, name);
+ break;
+ case 242:
+ debug(1,"o72_setSystemMessage: (%d) %s", subOp, name);
+ break;
+ case 243: // Set Window Caption
+ _system->setWindowCaption((const char *)name);
+ break;
+ default:
+ error("o72_setSystemMessage: default case %d", subOp);
+ }
+}
+
+void ScummEngine_v72he::decodeParseString(int m, int n) {
+ Actor *a;
+ int i, colors, size;
+ int args[31];
+ byte name[1024];
+
+ byte b = fetchScriptByte();
+
+ switch (b) {
+ case 65: // SO_AT
+ _string[m].ypos = pop();
+ _string[m].xpos = pop();
+ _string[m].overhead = false;
+ break;
+ case 66: // SO_COLOR
+ _string[m].color = pop();
+ break;
+ case 67: // SO_CLIPPED
+ _string[m].right = pop();
+ break;
+ case 69: // SO_CENTER
+ _string[m].center = true;
+ _string[m].overhead = false;
+ break;
+ case 71: // SO_LEFT
+ _string[m].center = false;
+ _string[m].overhead = false;
+ break;
+ case 72: // SO_OVERHEAD
+ _string[m].overhead = true;
+ _string[m].no_talk_anim = false;
+ break;
+ case 73: // SO_SAY_VOICE
+ error("decodeParseString: case 73");
+ break;
+ case 74: // SO_MUMBLE
+ _string[m].no_talk_anim = true;
+ break;
+ case 75: // SO_TEXTSTRING
+ printString(m, _scriptPointer);
+ _scriptPointer += resStrLen(_scriptPointer) + 1;
+ break;
+ case 194:
+ decodeScriptString(name, true);
+ printString(m, name);
+ break;
+ case 0xE1:
+ {
+ byte *dataPtr = getResourceAddress(rtTalkie, pop());
+ byte *text = findWrappedBlock(MKID('TEXT'), dataPtr, 0, 0);
+ size = getResourceDataSize(text);
+ memcpy(name, text, size);
+ printString(m, name);
+ }
+ break;
+ case 0xF9:
+ colors = pop();
+ if (colors == 1) {
+ _string[m].color = pop();
+ } else {
+ push(colors);
+ getStackList(args, ARRAYSIZE(args));
+ for (i = 0; i < 16; i++)
+ _charsetColorMap[i] = _charsetData[_string[1]._default.charset][i] = (unsigned char)args[i];
+ _string[m].color = _charsetColorMap[0];
+ }
+ break;
+ case 0xFE:
+ _string[m].loadDefault();
+ if (n) {
+ _actorToPrintStrFor = pop();
+ if (_actorToPrintStrFor != 0xFF) {
+ a = derefActor(_actorToPrintStrFor, "decodeParseString");
+ _string[0].color = a->_talkColor;
+ }
+ }
+ break;
+ case 0xFF:
+ _string[m].saveDefault();
+ break;
+ default:
+ error("decodeParseString: default case 0x%x", b);
+ }
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/he/script_v7he.cpp b/engines/scumm/he/script_v7he.cpp
new file mode 100644
index 0000000000..400e7c0ad0
--- /dev/null
+++ b/engines/scumm/he/script_v7he.cpp
@@ -0,0 +1,1153 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "common/config-manager.h"
+#include "common/system.h"
+
+#include "scumm/actor.h"
+#include "scumm/charset.h"
+#include "scumm/he/intern_he.h"
+#include "scumm/object.h"
+#include "scumm/resource.h"
+#include "scumm/he/resource_v7he.h"
+#include "scumm/scumm.h"
+#include "scumm/sound.h"
+#include "scumm/verbs.h"
+
+namespace Scumm {
+
+#define OPCODE(x) _OPCODE(ScummEngine_v70he, x)
+
+void ScummEngine_v70he::setupOpcodes() {
+ static const OpcodeEntryv70he opcodes[256] = {
+ /* 00 */
+ OPCODE(o6_pushByte),
+ OPCODE(o6_pushWord),
+ OPCODE(o6_pushByteVar),
+ OPCODE(o6_pushWordVar),
+ /* 04 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_byteArrayRead),
+ OPCODE(o6_wordArrayRead),
+ /* 08 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_byteArrayIndexedRead),
+ OPCODE(o6_wordArrayIndexedRead),
+ /* 0C */
+ OPCODE(o6_dup),
+ OPCODE(o6_not),
+ OPCODE(o6_eq),
+ OPCODE(o6_neq),
+ /* 10 */
+ OPCODE(o6_gt),
+ OPCODE(o6_lt),
+ OPCODE(o6_le),
+ OPCODE(o6_ge),
+ /* 14 */
+ OPCODE(o6_add),
+ OPCODE(o6_sub),
+ OPCODE(o6_mul),
+ OPCODE(o6_div),
+ /* 18 */
+ OPCODE(o6_land),
+ OPCODE(o6_lor),
+ OPCODE(o6_pop),
+ OPCODE(o6_invalid),
+ /* 1C */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 20 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 24 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 28 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 2C */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 30 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 34 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 38 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 3C */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 40 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_writeByteVar),
+ OPCODE(o6_writeWordVar),
+ /* 44 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_byteArrayWrite),
+ OPCODE(o6_wordArrayWrite),
+ /* 48 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_byteArrayIndexedWrite),
+ OPCODE(o6_wordArrayIndexedWrite),
+ /* 4C */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_byteVarInc),
+ OPCODE(o6_wordVarInc),
+ /* 50 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_byteArrayInc),
+ OPCODE(o6_wordArrayInc),
+ /* 54 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_byteVarDec),
+ OPCODE(o6_wordVarDec),
+ /* 58 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_byteArrayDec),
+ OPCODE(o6_wordArrayDec),
+ /* 5C */
+ OPCODE(o6_if),
+ OPCODE(o6_ifNot),
+ OPCODE(o6_startScript),
+ OPCODE(o6_startScriptQuick),
+ /* 60 */
+ OPCODE(o6_startObject),
+ OPCODE(o6_drawObject),
+ OPCODE(o6_drawObjectAt),
+ OPCODE(o6_invalid),
+ /* 64 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_stopObjectCode),
+ OPCODE(o6_stopObjectCode),
+ OPCODE(o6_endCutscene),
+ /* 68 */
+ OPCODE(o6_cutscene),
+ OPCODE(o6_stopMusic),
+ OPCODE(o6_freezeUnfreeze),
+ OPCODE(o6_cursorCommand),
+ /* 6C */
+ OPCODE(o6_breakHere),
+ OPCODE(o6_ifClassOfIs),
+ OPCODE(o6_setClass),
+ OPCODE(o6_getState),
+ /* 70 */
+ OPCODE(o60_setState),
+ OPCODE(o6_setOwner),
+ OPCODE(o6_getOwner),
+ OPCODE(o6_jump),
+ /* 74 */
+ OPCODE(o70_startSound),
+ OPCODE(o6_stopSound),
+ OPCODE(o6_startMusic),
+ OPCODE(o6_stopObjectScript),
+ /* 78 */
+ OPCODE(o6_panCameraTo),
+ OPCODE(o6_actorFollowCamera),
+ OPCODE(o6_setCameraAt),
+ OPCODE(o6_loadRoom),
+ /* 7C */
+ OPCODE(o6_stopScript),
+ OPCODE(o6_walkActorToObj),
+ OPCODE(o6_walkActorTo),
+ OPCODE(o6_putActorAtXY),
+ /* 80 */
+ OPCODE(o6_putActorAtObject),
+ OPCODE(o6_faceActor),
+ OPCODE(o6_animateActor),
+ OPCODE(o6_doSentence),
+ /* 84 */
+ OPCODE(o70_pickupObject),
+ OPCODE(o6_loadRoomWithEgo),
+ OPCODE(o6_invalid),
+ OPCODE(o6_getRandomNumber),
+ /* 88 */
+ OPCODE(o6_getRandomNumberRange),
+ OPCODE(o6_invalid),
+ OPCODE(o6_getActorMoving),
+ OPCODE(o6_isScriptRunning),
+ /* 8C */
+ OPCODE(o70_getActorRoom),
+ OPCODE(o6_getObjectX),
+ OPCODE(o6_getObjectY),
+ OPCODE(o6_getObjectOldDir),
+ /* 90 */
+ OPCODE(o6_getActorWalkBox),
+ OPCODE(o6_getActorCostume),
+ OPCODE(o6_findInventory),
+ OPCODE(o6_getInventoryCount),
+ /* 94 */
+ OPCODE(o6_getVerbFromXY),
+ OPCODE(o6_beginOverride),
+ OPCODE(o6_endOverride),
+ OPCODE(o6_setObjectName),
+ /* 98 */
+ OPCODE(o6_isSoundRunning),
+ OPCODE(o6_setBoxFlags),
+ OPCODE(o6_invalid),
+ OPCODE(o70_resourceRoutines),
+ /* 9C */
+ OPCODE(o60_roomOps),
+ OPCODE(o60_actorOps),
+ OPCODE(o6_verbOps),
+ OPCODE(o6_getActorFromXY),
+ /* A0 */
+ OPCODE(o6_findObject),
+ OPCODE(o6_pseudoRoom),
+ OPCODE(o6_getActorElevation),
+ OPCODE(o6_getVerbEntrypoint),
+ /* A4 */
+ OPCODE(o6_arrayOps),
+ OPCODE(o6_saveRestoreVerbs),
+ OPCODE(o6_drawBox),
+ OPCODE(o6_pop),
+ /* A8 */
+ OPCODE(o6_getActorWidth),
+ OPCODE(o6_wait),
+ OPCODE(o6_getActorScaleX),
+ OPCODE(o6_getActorAnimCounter1),
+ /* AC */
+ OPCODE(o6_invalid),
+ OPCODE(o6_isAnyOf),
+ OPCODE(o70_systemOps),
+ OPCODE(o6_isActorInBox),
+ /* B0 */
+ OPCODE(o6_delay),
+ OPCODE(o6_delaySeconds),
+ OPCODE(o6_delayMinutes),
+ OPCODE(o6_stopSentence),
+ /* B4 */
+ OPCODE(o6_printLine),
+ OPCODE(o6_printText),
+ OPCODE(o6_printDebug),
+ OPCODE(o6_printSystem),
+ /* B8 */
+ OPCODE(o6_printActor),
+ OPCODE(o6_printEgo),
+ OPCODE(o6_talkActor),
+ OPCODE(o6_talkEgo),
+ /* BC */
+ OPCODE(o6_dimArray),
+ OPCODE(o6_stopObjectCode),
+ OPCODE(o6_startObjectQuick),
+ OPCODE(o6_startScriptQuick2),
+ /* C0 */
+ OPCODE(o6_dim2dimArray),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* C4 */
+ OPCODE(o6_abs),
+ OPCODE(o6_distObjectObject),
+ OPCODE(o6_distObjectPt),
+ OPCODE(o6_distPtPt),
+ /* C8 */
+ OPCODE(o60_kernelGetFunctions),
+ OPCODE(o70_kernelSetFunctions),
+ OPCODE(o6_delayFrames),
+ OPCODE(o6_pickOneOf),
+ /* CC */
+ OPCODE(o6_pickOneOfDefault),
+ OPCODE(o6_stampObject),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* D0 */
+ OPCODE(o6_getDateTime),
+ OPCODE(o6_stopTalking),
+ OPCODE(o6_getAnimateVariable),
+ OPCODE(o6_invalid),
+ /* D4 */
+ OPCODE(o6_shuffle),
+ OPCODE(o6_jumpToScript),
+ OPCODE(o6_band),
+ OPCODE(o6_bor),
+ /* D8 */
+ OPCODE(o6_isRoomScriptRunning),
+ OPCODE(o60_closeFile),
+ OPCODE(o60_openFile),
+ OPCODE(o60_readFile),
+ /* DC */
+ OPCODE(o60_writeFile),
+ OPCODE(o6_findAllObjects),
+ OPCODE(o60_deleteFile),
+ OPCODE(o60_rename),
+ /* E0 */
+ OPCODE(o60_soundOps),
+ OPCODE(o6_getPixel),
+ OPCODE(o60_localizeArrayToScript),
+ OPCODE(o6_pickVarRandom),
+ /* E4 */
+ OPCODE(o6_setBoxSet),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* E8 */
+ OPCODE(o6_invalid),
+ OPCODE(o70_seekFilePos),
+ OPCODE(o60_redimArray),
+ OPCODE(o60_readFilePos),
+ /* EC */
+ OPCODE(o70_copyString),
+ OPCODE(o70_getStringWidth),
+ OPCODE(o70_getStringLen),
+ OPCODE(o70_appendString),
+ /* F0 */
+ OPCODE(o70_concatString),
+ OPCODE(o70_compareString),
+ OPCODE(o70_isResourceLoaded),
+ OPCODE(o70_readINI),
+ /* F4 */
+ OPCODE(o70_writeINI),
+ OPCODE(o70_getStringLenForWidth),
+ OPCODE(o70_getCharIndexInString),
+ OPCODE(o6_invalid),
+ /* F8 */
+ OPCODE(o6_invalid),
+ OPCODE(o70_setFilePath),
+ OPCODE(o70_setSystemMessage),
+ OPCODE(o70_polygonOps),
+ /* FC */
+ OPCODE(o70_polygonHit),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ };
+
+ _opcodesv70he = opcodes;
+}
+
+void ScummEngine_v70he::executeOpcode(byte i) {
+ OpcodeProcv70he op = _opcodesv70he[i].proc;
+ (this->*op) ();
+}
+
+const char *ScummEngine_v70he::getOpcodeDesc(byte i) {
+ return _opcodesv70he[i].desc;
+}
+
+int ScummEngine_v70he::getStringCharWidth(byte chr) {
+ int charset = _string[0]._default.charset;
+
+ byte *ptr = getResourceAddress(rtCharset, charset);
+ assert(ptr);
+ ptr += 29;
+
+ int spacing = 0;
+
+ int offs = READ_LE_UINT32(ptr + chr * 4 + 4);
+ if (offs) {
+ spacing = ptr[offs] + (signed char)ptr[offs + 2];
+ }
+
+ return spacing;
+}
+
+int ScummEngine_v70he::setupStringArray(int size) {
+ writeVar(0, 0);
+ defineArray(0, kStringArray, 0, size + 1);
+ writeArray(0, 0, 0, 0);
+ return readVar(0);
+}
+
+void ScummEngine_v70he::appendSubstring(int dst, int src, int srcOffs, int len) {
+ int dstOffs, value;
+ int i = 0;
+
+ if (len == -1) {
+ len = resStrLen(getStringAddress(src));
+ srcOffs = 0;
+ }
+
+ dstOffs = resStrLen(getStringAddress(dst));
+
+ len -= srcOffs;
+ len++;
+
+ while (i < len) {
+ writeVar(0, src);
+ value = readArray(0, 0, srcOffs + i);
+ writeVar(0, dst);
+ writeArray(0, 0, dstOffs + i, value);
+ i++;
+ }
+
+ writeArray(0, 0, dstOffs + i, 0);
+}
+
+void ScummEngine_v70he::o70_startSound() {
+ int var, value;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 9:
+ _heSndFlags |= 4;
+ break;
+ case 23:
+ value = pop();
+ var = pop();
+ _heSndSoundId = pop();
+ _sound->setSoundVar(_heSndSoundId, var, value);
+ break;
+ case 25:
+ value = pop();
+ _heSndSoundId = pop();
+ _sound->addSoundToQueue(_heSndSoundId, 0, 0, 8);
+ case 56:
+ _heSndFlags |= 16;
+ break;
+ case 164:
+ _heSndFlags |= 2;
+ break;
+ case 224:
+ _heSndSoundFreq = pop();
+ break;
+ case 230:
+ _heSndChannel = pop();
+ break;
+ case 231:
+ _heSndOffset = pop();
+ break;
+ case 232:
+ _heSndSoundId = pop();
+ _heSndOffset = 0;
+ _heSndSoundFreq = 11025;
+ _heSndChannel = VAR(VAR_SOUND_CHANNEL);
+ break;
+ case 245:
+ _heSndFlags |= 1;
+ break;
+ case 255:
+ _sound->addSoundToQueue(_heSndSoundId, _heSndOffset, _heSndChannel, _heSndFlags);
+ _heSndFlags = 0;
+ break;
+
+ default:
+ error("o70_startSound invalid case %d", subOp);
+ }
+}
+
+void ScummEngine_v70he::o70_pickupObject() {
+ int obj, room;
+
+ room = pop();
+ obj = pop();
+ if (room == 0)
+ room = getObjectRoom(obj);
+
+ addObjectToInventory(obj, room);
+ putOwner(obj, VAR(VAR_EGO));
+ if (_heversion <= 70) {
+ putClass(obj, kObjectClassUntouchable, 1);
+ putState(obj, 1);
+ markObjectRectAsDirty(obj);
+ clearDrawObjectQueue();
+ }
+ runInventoryScript(obj); /* Difference */
+}
+
+void ScummEngine_v70he::o70_getActorRoom() {
+ int act = pop();
+
+ if (act < _numActors) {
+ Actor *a = derefActor(act, "o70_getActorRoom");
+ push(a->_room);
+ } else
+ push(getObjectRoom(act));
+}
+
+void ScummEngine_v70he::o70_resourceRoutines() {
+ int objidx, resid;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 100: // SO_LOAD_SCRIPT
+ resid = pop();
+ ensureResourceLoaded(rtScript, resid);
+ break;
+ case 101: // SO_LOAD_SOUND
+ resid = pop();
+ ensureResourceLoaded(rtSound, resid);
+ break;
+ case 102: // SO_LOAD_COSTUME
+ resid = pop();
+ ensureResourceLoaded(rtCostume, resid);
+ break;
+ case 103: // SO_LOAD_ROOM
+ resid = pop();
+ ensureResourceLoaded(rtRoomImage, resid);
+ ensureResourceLoaded(rtRoom, resid);
+ break;
+ case 104: // SO_NUKE_SCRIPT
+ resid = pop();
+ res.nukeResource(rtScript, resid);
+ break;
+ case 105: // SO_NUKE_SOUND
+ resid = pop();
+ res.nukeResource(rtSound, resid);
+ break;
+ case 106: // SO_NUKE_COSTUME
+ resid = pop();
+ res.nukeResource(rtCostume, resid);
+ break;
+ case 107: // SO_NUKE_ROOM
+ resid = pop();
+ res.nukeResource(rtRoom, resid);
+ res.nukeResource(rtRoomImage, resid);
+ break;
+ case 108: // SO_LOCK_SCRIPT
+ resid = pop();
+ if (resid >= _numGlobalScripts)
+ break;
+ res.lock(rtScript, resid);
+ break;
+ case 109: // SO_LOCK_SOUND
+ resid = pop();
+ res.lock(rtSound, resid);
+ break;
+ case 110: // SO_LOCK_COSTUME
+ resid = pop();
+ res.lock(rtCostume, resid);
+ break;
+ case 111: // SO_LOCK_ROOM
+ resid = pop();
+ if (_heversion <= 71 && resid > 0x7F)
+ resid = _resourceMapper[resid & 0x7F];
+ res.lock(rtRoom, resid);
+ res.lock(rtRoomImage, resid);
+ break;
+ case 112: // SO_UNLOCK_SCRIPT
+ resid = pop();
+ if (resid >= _numGlobalScripts)
+ break;
+ res.unlock(rtScript, resid);
+ break;
+ case 113: // SO_UNLOCK_SOUND
+ resid = pop();
+ res.unlock(rtSound, resid);
+ break;
+ case 114: // SO_UNLOCK_COSTUME
+ resid = pop();
+ res.unlock(rtCostume, resid);
+ break;
+ case 115: // SO_UNLOCK_ROOM
+ resid = pop();
+ if (_heversion <= 71 && resid > 0x7F)
+ resid = _resourceMapper[resid & 0x7F];
+ res.unlock(rtRoom, resid);
+ res.unlock(rtRoomImage, resid);
+ break;
+ case 116:
+ break;
+ case 117: // SO_LOAD_CHARSET
+ resid = pop();
+ loadCharset(resid);
+ break;
+ case 118: // SO_NUKE_CHARSET
+ resid = pop();
+ nukeCharset(resid);
+ break;
+ case 119: // SO_LOAD_OBJECT
+ {
+ int obj = pop();
+ int room = getObjectRoom(obj);
+ loadFlObject(obj, room);
+ break;
+ }
+ case 120:
+ resid = pop();
+ if (resid >= _numGlobalScripts)
+ break;
+ //queueLoadResource(rtScript, resid);
+ break;
+ case 121:
+ resid = pop();
+ //queueLoadResource(rtSound, resid);
+ break;
+ case 122:
+ resid = pop();
+ //queueLoadResource(rtCostume, resid);
+ break;
+ case 123:
+ resid = pop();
+ //queueLoadResource(rtRoomImage, resid);
+ break;
+ case 159:
+ resid = pop();
+ res.unlock(rtImage, resid);
+ break;
+ case 192:
+ resid = pop();
+ res.nukeResource(rtImage, resid);
+ break;
+ case 201:
+ resid = pop();
+ ensureResourceLoaded(rtImage, resid);
+ break;
+ case 202:
+ resid = pop();
+ res.lock(rtImage, resid);
+ break;
+ case 203:
+ resid = pop();
+ //queueLoadResource(rtImage, resid);
+ break;
+ case 233:
+ resid = pop();
+ objidx = getObjectIndex(resid);
+ if (objidx == -1)
+ break;
+ res.lock(rtFlObject, _objs[objidx].fl_object_index);
+ break;
+ case 235:
+ resid = pop();
+ objidx = getObjectIndex(resid);
+ if (objidx == -1)
+ break;
+ res.unlock(rtFlObject, _objs[objidx].fl_object_index);
+ break;
+ case 239:
+ // Used in airport
+ break;
+ default:
+ error("o70_resourceRoutines: default case %d", subOp);
+ }
+}
+
+void ScummEngine_v70he::o70_systemOps() {
+ byte *src, string[256];
+ int id, len;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 158:
+ restart();
+ break;
+ case 160:
+ // Confirm shutdown
+ shutDown();
+ break;
+ case 244:
+ shutDown();
+ break;
+ case 250:
+ id = pop();
+ src = getStringAddress(id);
+ len = resStrLen(src) + 1;
+ memcpy(string, src, len);
+ debug(0, "Start executable (%s)", string);
+ break;
+ case 251:
+ convertMessageToString(_scriptPointer, string, sizeof(string));
+ len = resStrLen(_scriptPointer);
+ _scriptPointer += len + 1;
+ debug(0, "Start executable (%s)", string);
+ break;
+ case 252:
+ convertMessageToString(_scriptPointer, string, sizeof(string));
+ len = resStrLen(_scriptPointer);
+ _scriptPointer += len + 1;
+ debug(0, "Start game (%s)", string);
+ break;
+ case 253:
+ id = pop();
+ src = getStringAddress(id);
+ len = resStrLen(src) + 1;
+ memcpy(string, src, len);
+ debug(0, "Start game (%s)", string);
+ break;
+ default:
+ error("o70_systemOps invalid case %d", subOp);
+ }
+}
+
+void ScummEngine_v70he::o70_seekFilePos() {
+ int mode, offset, slot;
+ mode = pop();
+ offset = pop();
+ slot = pop();
+
+ if (slot == -1)
+ return;
+
+ switch (mode) {
+ case 1:
+ _hFileTable[slot].seek(offset, SEEK_SET);
+ break;
+ case 2:
+ _hFileTable[slot].seek(offset, SEEK_CUR);
+ break;
+ case 3:
+ _hFileTable[slot].seek(offset, SEEK_END);
+ break;
+ default:
+ error("o70_seekFilePos: default case 0x%x", mode);
+ }
+}
+
+void ScummEngine_v70he::o70_copyString() {
+ int dst, size;
+ int src = pop();
+
+ size = resStrLen(getStringAddress(src)) + 1;
+ dst = setupStringArray(size);
+
+ appendSubstring(dst, src, -1, -1);
+
+ push(dst);
+}
+
+void ScummEngine_v70he::o70_getStringWidth() {
+ int array, pos, len;
+ int chr, width = 0;
+
+ len = pop();
+ pos = pop();
+ array = pop();
+
+ if (len == -1) {
+ pos = 0;
+ len = resStrLen(getStringAddress(array));
+ }
+
+ writeVar(0, array);
+ while (pos <= len) {
+ chr = readArray(0, 0, pos);
+ if (chr == 0)
+ break;
+ width += getStringCharWidth(chr);
+ pos++;
+ }
+
+ push(width);
+}
+
+void ScummEngine_v70he::o70_kernelSetFunctions() {
+ int args[29];
+ int num;
+ Actor *a;
+
+ num = getStackList(args, ARRAYSIZE(args));
+
+ switch (args[0]) {
+ case 1:
+ // Used to restore images when decorating cake in
+ // Fatty Bear's Birthday Surprise
+ virtScreenLoad(args[1], args[2], args[3], args[4], args[5]);
+ break;
+ case 20: // HE72+
+ a = derefActor(args[1], "o70_kernelSetFunctions: 20");
+ ((ScummEngine_v71he *)this)->queueAuxBlock(a);
+ break;
+ case 21:
+ _skipDrawObject = 1;
+ break;
+ case 22:
+ _skipDrawObject = 0;
+ break;
+ case 23:
+ _charset->clearCharsetMask();
+ _fullRedraw = true;
+ break;
+ case 24:
+ _skipProcessActors = 1;
+ redrawAllActors();
+ break;
+ case 25:
+ _skipProcessActors = 0;
+ redrawAllActors();
+ break;
+ case 26:
+ a = derefActor(args[1], "o70_kernelSetFunctions: 26");
+ a->_auxBlock.r.left = 0;
+ a->_auxBlock.r.right = -1;
+ a->_auxBlock.r.top = 0;
+ a->_auxBlock.r.bottom = -2;
+ break;
+ case 30:
+ a = derefActor(args[1], "o70_kernelSetFunctions: 30");
+ a->_clipOverride.bottom = args[2];
+ break;
+ case 42:
+ _wiz->_rectOverrideEnabled = true;
+ _wiz->_rectOverride.left = args[1];
+ _wiz->_rectOverride.top = args[2];
+ _wiz->_rectOverride.right = args[3];
+ _wiz->_rectOverride.bottom = args[4];
+ break;
+ case 43:
+ _wiz->_rectOverrideEnabled = false;
+ break;
+ default:
+ error("o70_kernelSetFunctions: default case %d (param count %d)", args[0], num);
+ }
+}
+
+void ScummEngine_v70he::o70_getStringLen() {
+ int id, len;
+ byte *addr;
+
+ id = pop();
+
+ addr = getStringAddress(id);
+ if (!addr)
+ error("o70_getStringLen: Reference to zeroed array pointer (%d)", id);
+
+ len = resStrLen(getStringAddress(id));
+ push(len);
+}
+
+void ScummEngine_v70he::o70_appendString() {
+ int dst, size;
+
+ int len = pop();
+ int srcOffs = pop();
+ int src = pop();
+
+ size = len - srcOffs + 2;
+ dst = setupStringArray(size);
+
+ appendSubstring(dst, src, srcOffs, len);
+
+ push(dst);
+}
+
+void ScummEngine_v70he::o70_concatString() {
+ int dst, size;
+
+ int src2 = pop();
+ int src1 = pop();
+
+ size = resStrLen(getStringAddress(src1));
+ size += resStrLen(getStringAddress(src2)) + 1;
+ dst = setupStringArray(size);
+
+ appendSubstring(dst, src1, 0, -1);
+ appendSubstring(dst, src2, 0, -1);
+
+ push(dst);
+}
+
+void ScummEngine_v70he::o70_compareString() {
+ int result;
+
+ int array1 = pop();
+ int array2 = pop();
+
+ byte *string1 = getStringAddress(array1);
+ if (!string1)
+ error("o70_compareString: Reference to zeroed array pointer (%d)", array1);
+
+ byte *string2 = getStringAddress(array2);
+ if (!string2)
+ error("o70_compareString: Reference to zeroed array pointer (%d)", array2);
+
+ while (*string1 == *string2) {
+ if (*string2 == 0) {
+ push(0);
+ return;
+ }
+
+ string1++;
+ string2++;
+ }
+
+ result = (*string1 > *string2) ? -1 : 1;
+ push(result);
+}
+
+void ScummEngine_v70he::o70_isResourceLoaded() {
+ // Reports percentage of resource loaded by queue
+ int type;
+
+ byte subOp = fetchScriptByte();
+ /* int idx = */ pop();
+
+ switch (subOp) {
+ case 18:
+ type = rtImage;
+ break;
+ case 226:
+ type = rtRoom;
+ break;
+ case 227:
+ type = rtCostume;
+ break;
+ case 228:
+ type = rtSound;
+ break;
+ case 229:
+ type = rtScript;
+ break;
+ default:
+ error("o70_isResourceLoaded: default case %d", subOp);
+ }
+
+ push(100);
+}
+
+void ScummEngine_v70he::o70_readINI() {
+ byte option[256];
+ ArrayHeader *ah;
+ const char *entry;
+ int len, type;
+
+ convertMessageToString(_scriptPointer, option, sizeof(option));
+ len = resStrLen(_scriptPointer);
+ _scriptPointer += len + 1;
+
+ type = pop();
+ switch (type) {
+ case 1: // number
+ if (!strcmp((char *)option, "NoPrinting")) {
+ push(1);
+ } else if (!strcmp((char *)option, "TextOn")) {
+ push(ConfMan.getBool("subtitles"));
+ } else {
+ push(ConfMan.getInt((char *)option));
+ }
+ break;
+ case 2: // string
+ entry = (ConfMan.get((char *)option).c_str());
+
+ writeVar(0, 0);
+ len = resStrLen((const byte *)entry);
+ ah = defineArray(0, kStringArray, 0, len);
+ memcpy(ah->data, entry, len);
+
+ push(readVar(0));
+ break;
+ default:
+ error("o70_readINI: default type %d", type);
+ }
+ debug(1, "o70_readINI: Option %s", option);
+}
+
+void ScummEngine_v70he::o70_writeINI() {
+ int type, value;
+ byte option[256], string[256];
+ int len;
+
+ type = pop();
+ value = pop();
+
+ convertMessageToString(_scriptPointer, option, sizeof(option));
+ len = resStrLen(_scriptPointer);
+ _scriptPointer += len + 1;
+
+ switch (type) {
+ case 1: // number
+ ConfMan.set((char *)option, value);
+ debug(1, "o70_writeINI: Option %s Value %d", option, value);
+ break;
+ case 2: // string
+ convertMessageToString(_scriptPointer, string, sizeof(string));
+ len = resStrLen(_scriptPointer);
+ _scriptPointer += len + 1;
+ ConfMan.set((char *)option, (char *)string);
+ debug(1, "o70_writeINI: Option %s String %s", option, string);
+ break;
+ default:
+ error("o70_writeINI: default type %d", type);
+ }
+}
+
+void ScummEngine_v70he::o70_getStringLenForWidth() {
+ int chr, max;
+ int array, len, pos, width = 0;
+
+ max = pop();
+ pos = pop();
+ array = pop();
+
+ len = resStrLen(getStringAddress(array));
+
+ writeVar(0, array);
+ while (pos <= len) {
+ chr = readArray(0, 0, pos);
+ width += getStringCharWidth(chr);
+ if (width >= max) {
+ push(pos);
+ return;
+ }
+ pos++;
+ }
+
+ push(len);
+}
+
+void ScummEngine_v70he::o70_getCharIndexInString() {
+ int array, end, len, pos, value;
+
+ value = pop();
+ end = pop();
+ pos = pop();
+ array = pop();
+
+ if (end >= 0) {
+ len = resStrLen(getStringAddress(array));
+ if (len < end)
+ end = len;
+ } else {
+ end = 0;
+ }
+
+ if (pos < 0)
+ pos = 0;
+
+ writeVar(0, array);
+ if (end > pos) {
+ while (end >= pos) {
+ if (readArray(0, 0, pos) == value) {
+ push(pos);
+ return;
+ }
+ pos++;
+ }
+ } else {
+ while (end <= pos) {
+ if (readArray(0, 0, pos) == value) {
+ push(pos);
+ return;
+ }
+ pos--;
+ }
+ }
+
+ push(-1);
+}
+
+void ScummEngine_v70he::o70_setFilePath() {
+ int len;
+ byte filename[100];
+
+ convertMessageToString(_scriptPointer, filename, sizeof(filename));
+ len = resStrLen(_scriptPointer);
+ _scriptPointer += len + 1;
+
+ debug(1,"stub o70_setFilePath(%s)", filename);
+}
+
+void ScummEngine_v70he::o70_setSystemMessage() {
+ int len;
+ byte name[255];
+
+ byte subOp = fetchScriptByte();
+
+ convertMessageToString(_scriptPointer, name, sizeof(name));
+ len = resStrLen(_scriptPointer);
+ _scriptPointer += len + 1;
+
+ switch (subOp) {
+ case 240:
+ debug(1,"o70_setSystemMessage: (%d) %s", subOp, name);
+ break;
+ case 241: // Set Version
+ debug(1,"o70_setSystemMessage: (%d) %s", subOp, name);
+ break;
+ case 242:
+ debug(1,"o70_setSystemMessage: (%d) %s", subOp, name);
+ break;
+ case 243: // Set Window Caption
+ _system->setWindowCaption((const char *)name);
+ break;
+ default:
+ error("o70_setSystemMessage: default case %d", subOp);
+ }
+}
+
+void ScummEngine_v70he::o70_polygonOps() {
+ int vert1x, vert1y, vert2x, vert2y, vert3x, vert3y, vert4x, vert4y;
+ int id, fromId, toId;
+ bool flag;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 68: // HE 100
+ case 69: // HE 100
+ case 246:
+ case 248:
+ vert4y = pop();
+ vert4x = pop();
+ vert3y = pop();
+ vert3x = pop();
+ vert2y = pop();
+ vert2x = pop();
+ vert1y = pop();
+ vert1x = pop();
+ flag = (subOp == 69 || subOp == 248);
+ id = pop();
+ _wiz->polygonStore(id, flag, vert1x, vert1y, vert2x, vert2y, vert3x, vert3y, vert4x, vert4y);
+ break;
+ case 28: // HE 100
+ case 247:
+ toId = pop();
+ fromId = pop();
+ _wiz->polygonErase(fromId, toId);
+ break;
+ default:
+ error("o70_polygonOps: default case %d", subOp);
+ }
+}
+
+void ScummEngine_v70he::o70_polygonHit() {
+ int y = pop();
+ int x = pop();
+ push(_wiz->polygonHit(0, x, y));
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/he/script_v80he.cpp b/engines/scumm/he/script_v80he.cpp
new file mode 100644
index 0000000000..f82aebe050
--- /dev/null
+++ b/engines/scumm/he/script_v80he.cpp
@@ -0,0 +1,811 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "common/config-file.h"
+#include "common/config-manager.h"
+#include "common/str.h"
+
+#include "scumm/actor.h"
+#include "scumm/charset.h"
+#include "scumm/he/intern_he.h"
+#include "scumm/object.h"
+#include "scumm/resource.h"
+#include "scumm/he/resource_v7he.h"
+#include "scumm/scumm.h"
+#include "scumm/sound.h"
+#include "scumm/util.h"
+
+namespace Scumm {
+
+#define OPCODE(x) _OPCODE(ScummEngine_v80he, x)
+
+void ScummEngine_v80he::setupOpcodes() {
+ static const OpcodeEntryV80he opcodes[256] = {
+ /* 00 */
+ OPCODE(o6_pushByte),
+ OPCODE(o6_pushWord),
+ OPCODE(o72_pushDWord),
+ OPCODE(o6_pushWordVar),
+ /* 04 */
+ OPCODE(o72_getScriptString),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_wordArrayRead),
+ /* 08 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_wordArrayIndexedRead),
+ /* 0C */
+ OPCODE(o6_dup),
+ OPCODE(o6_not),
+ OPCODE(o6_eq),
+ OPCODE(o6_neq),
+ /* 10 */
+ OPCODE(o6_gt),
+ OPCODE(o6_lt),
+ OPCODE(o6_le),
+ OPCODE(o6_ge),
+ /* 14 */
+ OPCODE(o6_add),
+ OPCODE(o6_sub),
+ OPCODE(o6_mul),
+ OPCODE(o6_div),
+ /* 18 */
+ OPCODE(o6_land),
+ OPCODE(o6_lor),
+ OPCODE(o6_pop),
+ OPCODE(o72_isAnyOf),
+ /* 1C */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 20 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 24 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 28 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 2C */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 30 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 34 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 38 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 3C */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 40 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_writeWordVar),
+ /* 44 */
+ OPCODE(o6_invalid),
+ OPCODE(o80_createSound),
+ OPCODE(o80_getFileSize),
+ OPCODE(o6_wordArrayWrite),
+ /* 48 */
+ OPCODE(o80_stringToInt),
+ OPCODE(o80_getSoundVar),
+ OPCODE(o80_localizeArrayToRoom),
+ OPCODE(o6_wordArrayIndexedWrite),
+ /* 4C */
+ OPCODE(o80_sourceDebug),
+ OPCODE(o80_readConfigFile),
+ OPCODE(o80_writeConfigFile),
+ OPCODE(o6_wordVarInc),
+ /* 50 */
+ OPCODE(o72_resetCutscene),
+ OPCODE(o6_invalid),
+ OPCODE(o72_findObjectWithClassOf),
+ OPCODE(o6_wordArrayInc),
+ /* 54 */
+ OPCODE(o72_getObjectImageX),
+ OPCODE(o72_getObjectImageY),
+ OPCODE(o72_captureWizImage),
+ OPCODE(o6_wordVarDec),
+ /* 58 */
+ OPCODE(o72_getTimer),
+ OPCODE(o72_setTimer),
+ OPCODE(o72_getSoundPosition),
+ OPCODE(o6_wordArrayDec),
+ /* 5C */
+ OPCODE(o6_if),
+ OPCODE(o6_ifNot),
+ OPCODE(o72_startScript),
+ OPCODE(o6_startScriptQuick),
+ /* 60 */
+ OPCODE(o72_startObject),
+ OPCODE(o72_drawObject),
+ OPCODE(o72_printWizImage),
+ OPCODE(o72_getArrayDimSize),
+ /* 64 */
+ OPCODE(o72_getNumFreeArrays),
+ OPCODE(o6_stopObjectCode),
+ OPCODE(o6_stopObjectCode),
+ OPCODE(o6_endCutscene),
+ /* 68 */
+ OPCODE(o6_cutscene),
+ OPCODE(o6_invalid),
+ OPCODE(o6_freezeUnfreeze),
+ OPCODE(o80_cursorCommand),
+ /* 6C */
+ OPCODE(o6_breakHere),
+ OPCODE(o6_ifClassOfIs),
+ OPCODE(o6_setClass),
+ OPCODE(o6_getState),
+ /* 70 */
+ OPCODE(o80_setState),
+ OPCODE(o6_setOwner),
+ OPCODE(o6_getOwner),
+ OPCODE(o6_jump),
+ /* 74 */
+ OPCODE(o70_startSound),
+ OPCODE(o6_stopSound),
+ OPCODE(o6_invalid),
+ OPCODE(o6_stopObjectScript),
+ /* 78 */
+ OPCODE(o6_panCameraTo),
+ OPCODE(o6_actorFollowCamera),
+ OPCODE(o6_setCameraAt),
+ OPCODE(o6_loadRoom),
+ /* 7C */
+ OPCODE(o6_stopScript),
+ OPCODE(o6_walkActorToObj),
+ OPCODE(o6_walkActorTo),
+ OPCODE(o6_putActorAtXY),
+ /* 80 */
+ OPCODE(o6_putActorAtObject),
+ OPCODE(o6_faceActor),
+ OPCODE(o6_animateActor),
+ OPCODE(o6_doSentence),
+ /* 84 */
+ OPCODE(o70_pickupObject),
+ OPCODE(o6_loadRoomWithEgo),
+ OPCODE(o6_invalid),
+ OPCODE(o6_getRandomNumber),
+ /* 88 */
+ OPCODE(o6_getRandomNumberRange),
+ OPCODE(o6_invalid),
+ OPCODE(o6_getActorMoving),
+ OPCODE(o6_isScriptRunning),
+ /* 8C */
+ OPCODE(o70_getActorRoom),
+ OPCODE(o6_getObjectX),
+ OPCODE(o6_getObjectY),
+ OPCODE(o6_getObjectOldDir),
+ /* 90 */
+ OPCODE(o6_getActorWalkBox),
+ OPCODE(o6_getActorCostume),
+ OPCODE(o6_findInventory),
+ OPCODE(o6_getInventoryCount),
+ /* 94 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_beginOverride),
+ OPCODE(o6_endOverride),
+ OPCODE(o6_setObjectName),
+ /* 98 */
+ OPCODE(o6_isSoundRunning),
+ OPCODE(o6_setBoxFlags),
+ OPCODE(o6_invalid),
+ OPCODE(o70_resourceRoutines),
+ /* 9C */
+ OPCODE(o72_roomOps),
+ OPCODE(o72_actorOps),
+ OPCODE(o6_invalid),
+ OPCODE(o6_getActorFromXY),
+ /* A0 */
+ OPCODE(o72_findObject),
+ OPCODE(o6_pseudoRoom),
+ OPCODE(o6_getActorElevation),
+ OPCODE(o6_getVerbEntrypoint),
+ /* A4 */
+ OPCODE(o72_arrayOps),
+ OPCODE(o6_invalid),
+ OPCODE(o6_drawBox),
+ OPCODE(o6_pop),
+ /* A8 */
+ OPCODE(o6_getActorWidth),
+ OPCODE(o6_wait),
+ OPCODE(o6_getActorScaleX),
+ OPCODE(o6_getActorAnimCounter1),
+ /* AC */
+ OPCODE(o80_drawWizPolygon),
+ OPCODE(o6_isAnyOf),
+ OPCODE(o72_systemOps),
+ OPCODE(o6_isActorInBox),
+ /* B0 */
+ OPCODE(o6_delay),
+ OPCODE(o6_delaySeconds),
+ OPCODE(o6_delayMinutes),
+ OPCODE(o6_stopSentence),
+ /* B4 */
+ OPCODE(o6_printLine),
+ OPCODE(o6_printText),
+ OPCODE(o6_printDebug),
+ OPCODE(o6_printSystem),
+ /* B8 */
+ OPCODE(o6_printActor),
+ OPCODE(o6_printEgo),
+ OPCODE(o72_talkActor),
+ OPCODE(o72_talkEgo),
+ /* BC */
+ OPCODE(o72_dimArray),
+ OPCODE(o6_stopObjectCode),
+ OPCODE(o6_startObjectQuick),
+ OPCODE(o6_startScriptQuick2),
+ /* C0 */
+ OPCODE(o72_dim2dimArray),
+ OPCODE(o72_traceStatus),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* C4 */
+ OPCODE(o6_abs),
+ OPCODE(o6_distObjectObject),
+ OPCODE(o6_distObjectPt),
+ OPCODE(o6_distPtPt),
+ /* C8 */
+ OPCODE(o72_kernelGetFunctions),
+ OPCODE(o70_kernelSetFunctions),
+ OPCODE(o6_delayFrames),
+ OPCODE(o6_pickOneOf),
+ /* CC */
+ OPCODE(o6_pickOneOfDefault),
+ OPCODE(o6_stampObject),
+ OPCODE(o72_drawWizImage),
+ OPCODE(o72_debugInput),
+ /* D0 */
+ OPCODE(o6_getDateTime),
+ OPCODE(o6_stopTalking),
+ OPCODE(o6_getAnimateVariable),
+ OPCODE(o6_invalid),
+ /* D4 */
+ OPCODE(o6_shuffle),
+ OPCODE(o72_jumpToScript),
+ OPCODE(o6_band),
+ OPCODE(o6_bor),
+ /* D8 */
+ OPCODE(o6_isRoomScriptRunning),
+ OPCODE(o60_closeFile),
+ OPCODE(o72_openFile),
+ OPCODE(o72_readFile),
+ /* DC */
+ OPCODE(o72_writeFile),
+ OPCODE(o72_findAllObjects),
+ OPCODE(o72_deleteFile),
+ OPCODE(o72_rename),
+ /* E0 */
+ OPCODE(o80_drawLine),
+ OPCODE(o72_getPixel),
+ OPCODE(o60_localizeArrayToScript),
+ OPCODE(o80_pickVarRandom),
+ /* E4 */
+ OPCODE(o6_setBoxSet),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* E8 */
+ OPCODE(o6_invalid),
+ OPCODE(o70_seekFilePos),
+ OPCODE(o72_redimArray),
+ OPCODE(o60_readFilePos),
+ /* EC */
+ OPCODE(o70_copyString),
+ OPCODE(o70_getStringWidth),
+ OPCODE(o70_getStringLen),
+ OPCODE(o70_appendString),
+ /* F0 */
+ OPCODE(o70_concatString),
+ OPCODE(o70_compareString),
+ OPCODE(o70_isResourceLoaded),
+ OPCODE(o72_readINI),
+ /* F4 */
+ OPCODE(o72_writeINI),
+ OPCODE(o70_getStringLenForWidth),
+ OPCODE(o70_getCharIndexInString),
+ OPCODE(o6_invalid),
+ /* F8 */
+ OPCODE(o72_getResourceSize),
+ OPCODE(o72_setFilePath),
+ OPCODE(o72_setSystemMessage),
+ OPCODE(o70_polygonOps),
+ /* FC */
+ OPCODE(o70_polygonHit),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ };
+
+ _opcodesV80he = opcodes;
+}
+
+void ScummEngine_v80he::executeOpcode(byte i) {
+ OpcodeProcV80he op = _opcodesV80he[i].proc;
+ (this->*op) ();
+}
+
+const char *ScummEngine_v80he::getOpcodeDesc(byte i) {
+ return _opcodesV80he[i].desc;
+}
+
+void ScummEngine_v80he::o80_createSound() {
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 27:
+ createSound(_heSndResId, pop());
+ break;
+ case 217:
+ createSound(_heSndResId, -1);
+ break;
+ case 232:
+ _heSndResId = pop();
+ break;
+ case 255:
+ // dummy case
+ break;
+ default:
+ error("o80_createSound: default case %d", subOp);
+ }
+}
+
+void ScummEngine_v80he::o80_getFileSize() {
+ byte filename[256];
+
+ copyScriptString(filename, sizeof(filename));
+
+ Common::File f;
+ if (!f.open((char *)filename)) {
+ push(-1);
+ } else {
+ push(f.size());
+ f.close();
+ }
+}
+
+void ScummEngine_v80he::o80_stringToInt() {
+ int id, len, val;
+ byte *addr;
+ char string[100];
+
+ id = pop();
+
+ addr = getStringAddress(id);
+ if (!addr)
+ error("o80_stringToInt: Reference to zeroed array pointer (%d)", id);
+
+ len = resStrLen(getStringAddress(id)) + 1;
+ memcpy(string, addr, len);
+ val = atoi(string);
+ push(val);
+}
+
+void ScummEngine_v80he::o80_getSoundVar() {
+ int var = pop();
+ int snd = pop();
+ push(_sound->getSoundVar(snd, var));
+}
+
+void ScummEngine_v80he::o80_localizeArrayToRoom() {
+ int slot = pop();
+ localizeArray(slot, 0xFF);
+}
+
+void ScummEngine_v80he::o80_sourceDebug() {
+ fetchScriptDWord();
+ fetchScriptDWord();
+}
+
+void ScummEngine_v80he::o80_readConfigFile() {
+ byte option[128], section[128], filename[256];
+ ArrayHeader *ah;
+ Common::String entry;
+ int len;
+
+ copyScriptString(option, sizeof(option));
+ copyScriptString(section, sizeof(section));
+ copyScriptString(filename, sizeof(filename));
+ convertFilePath(filename, true);
+
+ Common::ConfigFile ConfFile;
+ ConfFile.loadFromFile((const char *)filename);
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 43: // HE 100
+ case 6: // number
+ ConfFile.getKey((const char *)option, (const char *)section, entry);
+
+ push(atoi(entry.c_str()));
+ break;
+ case 77: // HE 100
+ case 7: // string
+ ConfFile.getKey((const char *)option, (const char *)section, entry);
+
+ writeVar(0, 0);
+ len = resStrLen((const byte *)entry.c_str());
+ ah = defineArray(0, kStringArray, 0, 0, 0, len);
+ memcpy(ah->data, entry.c_str(), len);
+ push(readVar(0));
+ break;
+ default:
+ error("o80_readConfigFile: default type %d", subOp);
+ }
+
+ debug(1, "o80_readConfigFile: Filename %s Section %s Option %s Value %s", filename, section, option, entry.c_str());
+}
+
+void ScummEngine_v80he::o80_writeConfigFile() {
+ byte filename[256], section[256], option[256], string[1024];
+ int value;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 43: // HE 100
+ case 6: // number
+ value = pop();
+ sprintf((char *)string, "%d", value);
+ copyScriptString(option, sizeof(option));
+ copyScriptString(section, sizeof(section));
+ copyScriptString(filename, sizeof(filename));
+ convertFilePath(filename, true);
+ break;
+ case 77: // HE 100
+ case 7: // string
+ copyScriptString(string, sizeof(string));
+ copyScriptString(option, sizeof(option));
+ copyScriptString(section, sizeof(section));
+ copyScriptString(filename, sizeof(filename));
+ convertFilePath(filename, true);
+ break;
+ default:
+ error("o80_writeConfigFile: default type %d", subOp);
+ }
+
+ Common::ConfigFile ConfFile;
+ ConfFile.loadFromFile((const char *)filename);
+ ConfFile.setKey((char *)option, (char *)section, (char *)string);
+ ConfFile.saveToFile((const char *)filename);
+ debug(1,"o80_writeConfigFile: Filename %s Section %s Option %s String %s", filename, section, option, string);
+}
+
+void ScummEngine_v80he::o80_cursorCommand() {
+ int a, i;
+ int args[16];
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 0x13:
+ case 0x14:
+ a = pop();
+ _wiz->loadWizCursor(a);
+ break;
+ case 0x3C:
+ pop();
+ a = pop();
+ _wiz->loadWizCursor(a);
+ break;
+ case 0x90: // SO_CURSOR_ON Turn cursor on
+ _cursor.state = 1;
+ break;
+ case 0x91: // SO_CURSOR_OFF Turn cursor off
+ _cursor.state = 0;
+ break;
+ case 0x92: // SO_USERPUT_ON
+ _userPut = 1;
+ break;
+ case 0x93: // SO_USERPUT_OFF
+ _userPut = 0;
+ break;
+ case 0x94: // SO_CURSOR_SOFT_ON Turn soft cursor on
+ _cursor.state++;
+ if (_cursor.state > 1)
+ error("Cursor state greater than 1 in script");
+ break;
+ case 0x95: // SO_CURSOR_SOFT_OFF Turn soft cursor off
+ _cursor.state--;
+ break;
+ case 0x96: // SO_USERPUT_SOFT_ON
+ _userPut++;
+ break;
+ case 0x97: // SO_USERPUT_SOFT_OFF
+ _userPut--;
+ break;
+ case 0x9C: // SO_CHARSET_SET
+ initCharset(pop());
+ break;
+ case 0x9D: // SO_CHARSET_COLOR
+ getStackList(args, ARRAYSIZE(args));
+ for (i = 0; i < 16; i++)
+ _charsetColorMap[i] = _charsetData[_string[1]._default.charset][i] = (unsigned char)args[i];
+ break;
+ default:
+ error("o80_cursorCommand: default case %x", subOp);
+ }
+
+ VAR(VAR_CURSORSTATE) = _cursor.state;
+ VAR(VAR_USERPUT) = _userPut;
+}
+
+void ScummEngine_v80he::o80_setState() {
+ int state = pop();
+ int obj = pop();
+
+ state &= 0x7FFF;
+ putState(obj, state);
+ removeObjectFromDrawQue(obj);
+}
+
+void ScummEngine_v80he::o80_drawWizPolygon() {
+ WizImage wi;
+ wi.x1 = wi.y1 = pop();
+ wi.resNum = pop();
+ wi.state = 0;
+ wi.flags = kWIFIsPolygon;
+ _wiz->displayWizImage(&wi);
+}
+
+/**
+ * Draw a 'line' between two points.
+ *
+ * @param x1 the starting x coordinate
+ * @param y1 the starting y coordinate
+ * @param x the ending x coordinate
+ * @param y the ending y coordinate
+ * @param step the step size used to render the line, only ever 'step'th point is drawn
+ * @param type the line type -- points are rendered by drawing actors (type == 2),
+ * wiz images (type == 3), or pixels (any other type)
+ * @param id the id of an actor, wizimage or color (low bit) & flag (high bit)
+ */
+void ScummEngine_v80he::drawLine(int x1, int y1, int x, int y, int step, int type, int id) {
+ if (step < 0) {
+ step = -step;
+ }
+ if (step == 0) {
+ step = 1;
+ }
+
+ const int dx = x - x1;
+ const int dy = y - y1;
+
+ const int absDX = ABS(dx);
+ const int absDY = ABS(dy);
+
+ const int maxDist = MAX(absDX, absDY);
+
+ y = y1;
+ x = x1;
+
+
+ if (type == 2) {
+ Actor *a = derefActor(id, "drawLine");
+ a->drawActorToBackBuf(x, y);
+ } else if (type == 3) {
+ WizImage wi;
+ wi.flags = 0;
+ wi.y1 = y;
+ wi.x1 = x;
+ wi.resNum = id;
+ wi.state = 0;
+ _wiz->displayWizImage(&wi);
+ } else {
+ drawPixel(x, y, id);
+ }
+
+ int stepCount = 0;
+ int tmpX = 0;
+ int tmpY = 0;
+ for (int i = 0; i <= maxDist; i++) {
+ tmpX += absDX;
+ tmpY += absDY;
+
+ int drawFlag = 0;
+
+ if (tmpX > maxDist) {
+ drawFlag = 1;
+ tmpX -= maxDist;
+
+ if (dx >= 0) {
+ x++;
+ } else {
+ x--;
+ }
+ }
+ if (tmpY > maxDist) {
+ drawFlag = dy;
+ tmpY -= maxDist;
+
+ if (dy >= 0) {
+ y++;
+ } else {
+ y--;
+ }
+ }
+
+ if (drawFlag == 0)
+ continue;
+
+ if ((stepCount++ % step) != 0 && maxDist != i)
+ continue;
+
+ if (type == 2) {
+ Actor *a = derefActor(id, "drawLine");
+ a->drawActorToBackBuf(x, y);
+ } else if (type == 3) {
+ WizImage wi;
+ wi.flags = 0;
+ wi.y1 = y;
+ wi.x1 = x;
+ wi.resNum = id;
+ wi.state = 0;
+ _wiz->displayWizImage(&wi);
+ } else {
+ drawPixel(x, y, id);
+ }
+ }
+}
+
+void ScummEngine_v80he::drawPixel(int x, int y, int flags) {
+ byte *src, *dst;
+ VirtScreen *vs;
+
+ if (x < 0 || x > 639)
+ return;
+
+ if (y < 0)
+ return;
+
+ if ((vs = findVirtScreen(y)) == NULL)
+ return;
+
+ markRectAsDirty(vs->number, x, y, x, y + 1);
+
+ if ((flags & 0x4000) || (flags & 0x2000000)) {
+ src = vs->getPixels(x, y);
+ dst = vs->getBackPixels(x, y);
+ *dst = *src;
+ } else if ((flags & 0x2000) || (flags & 4000000)) {
+ src = vs->getBackPixels(x, y);
+ dst = vs->getPixels(x, y);
+ *dst = *src;
+ } else if (flags & 0x8000000) {
+ error("drawPixel: unsupported flag 0x%x", flags);
+ } else {
+ dst = vs->getPixels(x, y);
+ *dst = flags;
+ if ((flags & 0x8000) || (flags & 0x1000000)) {
+ dst = vs->getBackPixels(x, y);
+ *dst = flags;
+ }
+ }
+}
+
+void ScummEngine_v80he::o80_drawLine() {
+ int id, step, x, y, x1, y1;
+
+ step = pop();
+ id = pop();
+ y = pop();
+ x = pop();
+ y1 = pop();
+ x1 = pop();
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 55:
+ drawLine(x1, y1, x, y, step, 2, id);
+ break;
+ case 63:
+ drawLine(x1, y1, x, y, step, 3, id);
+ break;
+ case 66:
+ drawLine(x1, y1, x, y, step, 1, id);
+ break;
+ default:
+ error("o80_drawLine: default case %d", subOp);
+ }
+
+}
+
+void ScummEngine_v80he::o80_pickVarRandom() {
+ int num;
+ int args[100];
+ int32 dim1end;
+
+ num = getStackList(args, ARRAYSIZE(args));
+ int value = fetchScriptWord();
+
+ if (readVar(value) == 0) {
+ defineArray(value, kDwordArray, 0, 0, 0, num);
+ if (value & 0x8000)
+ localizeArray(readVar(value), 0xFF);
+ else if (value & 0x4000)
+ localizeArray(readVar(value), _currentScript);
+
+ if (num > 0) {
+ int16 counter = 0;
+ do {
+ writeArray(value, 0, counter + 1, args[counter]);
+ } while (++counter < num);
+ }
+
+ shuffleArray(value, 1, num);
+ writeArray(value, 0, 0, 2);
+ push(readArray(value, 0, 1));
+ return;
+ }
+
+ num = readArray(value, 0, 0);
+
+ ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, readVar(value));
+ dim1end = FROM_LE_32(ah->dim1end);
+
+ if (dim1end < num) {
+ int32 var_2 = readArray(value, 0, num - 1);
+ shuffleArray(value, 1, dim1end);
+ num = 1;
+ if (readArray(value, 0, 1) == var_2 && dim1end >= 3) {
+ int32 tmp = readArray(value, 0, 2);
+ writeArray(value, 0, num, tmp);
+ writeArray(value, 0, 2, var_2);
+ }
+ }
+
+ writeArray(value, 0, 0, num + 1);
+ push(readArray(value, 0, num));
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/he/script_v90he.cpp b/engines/scumm/he/script_v90he.cpp
new file mode 100644
index 0000000000..899bce8036
--- /dev/null
+++ b/engines/scumm/he/script_v90he.cpp
@@ -0,0 +1,2636 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "scumm/actor.h"
+#include "scumm/charset.h"
+#include "scumm/he/intern_he.h"
+#include "scumm/he/logic_he.h"
+#include "scumm/object.h"
+#include "scumm/resource.h"
+#include "scumm/he/resource_v7he.h"
+#include "scumm/scumm.h"
+#include "scumm/sound.h"
+#include "scumm/he/sprite_he.h"
+#include "scumm/util.h"
+
+namespace Scumm {
+
+#define OPCODE(x) _OPCODE(ScummEngine_v90he, x)
+
+void ScummEngine_v90he::setupOpcodes() {
+ static const OpcodeEntryV90he opcodes[256] = {
+ /* 00 */
+ OPCODE(o6_pushByte),
+ OPCODE(o6_pushWord),
+ OPCODE(o72_pushDWord),
+ OPCODE(o6_pushWordVar),
+ /* 04 */
+ OPCODE(o72_getScriptString),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_wordArrayRead),
+ /* 08 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o90_dup_n),
+ OPCODE(o6_wordArrayIndexedRead),
+ /* 0C */
+ OPCODE(o6_dup),
+ OPCODE(o6_not),
+ OPCODE(o6_eq),
+ OPCODE(o6_neq),
+ /* 10 */
+ OPCODE(o6_gt),
+ OPCODE(o6_lt),
+ OPCODE(o6_le),
+ OPCODE(o6_ge),
+ /* 14 */
+ OPCODE(o6_add),
+ OPCODE(o6_sub),
+ OPCODE(o6_mul),
+ OPCODE(o6_div),
+ /* 18 */
+ OPCODE(o6_land),
+ OPCODE(o6_lor),
+ OPCODE(o6_pop),
+ OPCODE(o72_isAnyOf),
+ /* 1C */
+ OPCODE(o90_wizImageOps),
+ OPCODE(o90_min),
+ OPCODE(o90_max),
+ OPCODE(o90_sin),
+ /* 20 */
+ OPCODE(o90_cos),
+ OPCODE(o90_sqrt),
+ OPCODE(o90_atan2),
+ OPCODE(o90_getSegmentAngle),
+ /* 24 */
+ OPCODE(o90_getDistanceBetweenPoints),
+ OPCODE(o90_getSpriteInfo),
+ OPCODE(o90_setSpriteInfo),
+ OPCODE(o90_getSpriteGroupInfo),
+ /* 28 */
+ OPCODE(o90_setSpriteGroupInfo),
+ OPCODE(o90_getWizData),
+ OPCODE(o90_getActorData),
+ OPCODE(o90_startScriptUnk),
+ /* 2C */
+ OPCODE(o90_jumpToScriptUnk),
+ OPCODE(o90_videoOps),
+ OPCODE(o90_getVideoData),
+ OPCODE(o90_floodFill),
+ /* 30 */
+ OPCODE(o90_mod),
+ OPCODE(o90_shl),
+ OPCODE(o90_shr),
+ OPCODE(o90_xor),
+ /* 34 */
+ OPCODE(o90_findAllObjectsWithClassOf),
+ OPCODE(o90_getPolygonOverlap),
+ OPCODE(o90_cond),
+ OPCODE(o90_dim2dim2Array),
+ /* 38 */
+ OPCODE(o90_redim2dimArray),
+ OPCODE(o90_getLinesIntersectionPoint),
+ OPCODE(o90_sortArray),
+ OPCODE(o6_invalid),
+ /* 3C */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* 40 */
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_writeWordVar),
+ /* 44 */
+ OPCODE(o90_getObjectData),
+ OPCODE(o80_createSound),
+ OPCODE(o80_getFileSize),
+ OPCODE(o6_wordArrayWrite),
+ /* 48 */
+ OPCODE(o80_stringToInt),
+ OPCODE(o80_getSoundVar),
+ OPCODE(o80_localizeArrayToRoom),
+ OPCODE(o6_wordArrayIndexedWrite),
+ /* 4C */
+ OPCODE(o80_sourceDebug),
+ OPCODE(o80_readConfigFile),
+ OPCODE(o80_writeConfigFile),
+ OPCODE(o6_wordVarInc),
+ /* 50 */
+ OPCODE(o72_resetCutscene),
+ OPCODE(o6_invalid),
+ OPCODE(o72_findObjectWithClassOf),
+ OPCODE(o6_wordArrayInc),
+ /* 54 */
+ OPCODE(o72_getObjectImageX),
+ OPCODE(o72_getObjectImageY),
+ OPCODE(o72_captureWizImage),
+ OPCODE(o6_wordVarDec),
+ /* 58 */
+ OPCODE(o72_getTimer),
+ OPCODE(o72_setTimer),
+ OPCODE(o72_getSoundPosition),
+ OPCODE(o6_wordArrayDec),
+ /* 5C */
+ OPCODE(o6_if),
+ OPCODE(o6_ifNot),
+ OPCODE(o72_startScript),
+ OPCODE(o6_startScriptQuick),
+ /* 60 */
+ OPCODE(o72_startObject),
+ OPCODE(o72_drawObject),
+ OPCODE(o72_printWizImage),
+ OPCODE(o72_getArrayDimSize),
+ /* 64 */
+ OPCODE(o72_getNumFreeArrays),
+ OPCODE(o6_stopObjectCode),
+ OPCODE(o6_stopObjectCode),
+ OPCODE(o6_endCutscene),
+ /* 68 */
+ OPCODE(o6_cutscene),
+ OPCODE(o6_invalid),
+ OPCODE(o6_freezeUnfreeze),
+ OPCODE(o80_cursorCommand),
+ /* 6C */
+ OPCODE(o6_breakHere),
+ OPCODE(o6_ifClassOfIs),
+ OPCODE(o6_setClass),
+ OPCODE(o6_getState),
+ /* 70 */
+ OPCODE(o80_setState),
+ OPCODE(o6_setOwner),
+ OPCODE(o6_getOwner),
+ OPCODE(o6_jump),
+ /* 74 */
+ OPCODE(o70_startSound),
+ OPCODE(o6_stopSound),
+ OPCODE(o6_invalid),
+ OPCODE(o6_stopObjectScript),
+ /* 78 */
+ OPCODE(o6_panCameraTo),
+ OPCODE(o6_actorFollowCamera),
+ OPCODE(o6_setCameraAt),
+ OPCODE(o6_loadRoom),
+ /* 7C */
+ OPCODE(o6_stopScript),
+ OPCODE(o6_walkActorToObj),
+ OPCODE(o6_walkActorTo),
+ OPCODE(o6_putActorAtXY),
+ /* 80 */
+ OPCODE(o6_putActorAtObject),
+ OPCODE(o6_faceActor),
+ OPCODE(o6_animateActor),
+ OPCODE(o6_doSentence),
+ /* 84 */
+ OPCODE(o70_pickupObject),
+ OPCODE(o6_loadRoomWithEgo),
+ OPCODE(o6_invalid),
+ OPCODE(o6_getRandomNumber),
+ /* 88 */
+ OPCODE(o6_getRandomNumberRange),
+ OPCODE(o6_invalid),
+ OPCODE(o6_getActorMoving),
+ OPCODE(o6_isScriptRunning),
+ /* 8C */
+ OPCODE(o70_getActorRoom),
+ OPCODE(o6_getObjectX),
+ OPCODE(o6_getObjectY),
+ OPCODE(o6_getObjectOldDir),
+ /* 90 */
+ OPCODE(o6_getActorWalkBox),
+ OPCODE(o6_getActorCostume),
+ OPCODE(o6_findInventory),
+ OPCODE(o6_getInventoryCount),
+ /* 94 */
+ OPCODE(o90_getPaletteData),
+ OPCODE(o6_beginOverride),
+ OPCODE(o6_endOverride),
+ OPCODE(o6_setObjectName),
+ /* 98 */
+ OPCODE(o6_isSoundRunning),
+ OPCODE(o6_setBoxFlags),
+ OPCODE(o6_invalid),
+ OPCODE(o70_resourceRoutines),
+ /* 9C */
+ OPCODE(o72_roomOps),
+ OPCODE(o72_actorOps),
+ OPCODE(o90_paletteOps),
+ OPCODE(o6_getActorFromXY),
+ /* A0 */
+ OPCODE(o72_findObject),
+ OPCODE(o6_pseudoRoom),
+ OPCODE(o6_getActorElevation),
+ OPCODE(o6_getVerbEntrypoint),
+ /* A4 */
+ OPCODE(o72_arrayOps),
+ OPCODE(o90_fontUnk),
+ OPCODE(o6_drawBox),
+ OPCODE(o6_pop),
+ /* A8 */
+ OPCODE(o6_getActorWidth),
+ OPCODE(o6_wait),
+ OPCODE(o6_getActorScaleX),
+ OPCODE(o90_getActorAnimProgress),
+ /* AC */
+ OPCODE(o80_drawWizPolygon),
+ OPCODE(o6_isAnyOf),
+ OPCODE(o72_systemOps),
+ OPCODE(o6_isActorInBox),
+ /* B0 */
+ OPCODE(o6_delay),
+ OPCODE(o6_delaySeconds),
+ OPCODE(o6_delayMinutes),
+ OPCODE(o6_stopSentence),
+ /* B4 */
+ OPCODE(o6_printLine),
+ OPCODE(o6_printText),
+ OPCODE(o6_printDebug),
+ OPCODE(o6_printSystem),
+ /* B8 */
+ OPCODE(o6_printActor),
+ OPCODE(o6_printEgo),
+ OPCODE(o72_talkActor),
+ OPCODE(o72_talkEgo),
+ /* BC */
+ OPCODE(o72_dimArray),
+ OPCODE(o6_stopObjectCode),
+ OPCODE(o6_startObjectQuick),
+ OPCODE(o6_startScriptQuick2),
+ /* C0 */
+ OPCODE(o72_dim2dimArray),
+ OPCODE(o72_traceStatus),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* C4 */
+ OPCODE(o6_abs),
+ OPCODE(o6_distObjectObject),
+ OPCODE(o6_distObjectPt),
+ OPCODE(o6_distPtPt),
+ /* C8 */
+ OPCODE(o90_kernelGetFunctions),
+ OPCODE(o90_kernelSetFunctions),
+ OPCODE(o6_delayFrames),
+ OPCODE(o6_pickOneOf),
+ /* CC */
+ OPCODE(o6_pickOneOfDefault),
+ OPCODE(o6_stampObject),
+ OPCODE(o72_drawWizImage),
+ OPCODE(o72_debugInput),
+ /* D0 */
+ OPCODE(o6_getDateTime),
+ OPCODE(o6_stopTalking),
+ OPCODE(o6_getAnimateVariable),
+ OPCODE(o6_invalid),
+ /* D4 */
+ OPCODE(o6_shuffle),
+ OPCODE(o72_jumpToScript),
+ OPCODE(o6_band),
+ OPCODE(o6_bor),
+ /* D8 */
+ OPCODE(o6_isRoomScriptRunning),
+ OPCODE(o60_closeFile),
+ OPCODE(o72_openFile),
+ OPCODE(o72_readFile),
+ /* DC */
+ OPCODE(o72_writeFile),
+ OPCODE(o72_findAllObjects),
+ OPCODE(o72_deleteFile),
+ OPCODE(o72_rename),
+ /* E0 */
+ OPCODE(o80_drawLine),
+ OPCODE(o72_getPixel),
+ OPCODE(o60_localizeArrayToScript),
+ OPCODE(o80_pickVarRandom),
+ /* E4 */
+ OPCODE(o6_setBoxSet),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ /* E8 */
+ OPCODE(o6_invalid),
+ OPCODE(o70_seekFilePos),
+ OPCODE(o72_redimArray),
+ OPCODE(o60_readFilePos),
+ /* EC */
+ OPCODE(o70_copyString),
+ OPCODE(o70_getStringWidth),
+ OPCODE(o70_getStringLen),
+ OPCODE(o70_appendString),
+ /* F0 */
+ OPCODE(o70_concatString),
+ OPCODE(o70_compareString),
+ OPCODE(o70_isResourceLoaded),
+ OPCODE(o72_readINI),
+ /* F4 */
+ OPCODE(o72_writeINI),
+ OPCODE(o70_getStringLenForWidth),
+ OPCODE(o70_getCharIndexInString),
+ OPCODE(o6_invalid),
+ /* F8 */
+ OPCODE(o72_getResourceSize),
+ OPCODE(o72_setFilePath),
+ OPCODE(o72_setSystemMessage),
+ OPCODE(o70_polygonOps),
+ /* FC */
+ OPCODE(o70_polygonHit),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ OPCODE(o6_invalid),
+ };
+
+ _opcodesV90he = opcodes;
+}
+
+void ScummEngine_v90he::executeOpcode(byte i) {
+ OpcodeProcV90he op = _opcodesV90he[i].proc;
+ (this->*op) ();
+}
+
+const char *ScummEngine_v90he::getOpcodeDesc(byte i) {
+ return _opcodesV90he[i].desc;
+}
+
+void ScummEngine_v90he::o90_dup_n() {
+ int num;
+ int args[16];
+
+ push(fetchScriptWord());
+ num = getStackList(args, ARRAYSIZE(args));
+
+ for (int i = 0; i < 2; i++) {
+ for (int j = 0; j < num; j++)
+ push(args[j]);
+ }
+}
+
+void ScummEngine_v90he::o90_min() {
+ int a = pop();
+ int b = pop();
+
+ if (b < a) {
+ push(b);
+ } else {
+ push(a);
+ }
+}
+
+void ScummEngine_v90he::o90_max() {
+ int a = pop();
+ int b = pop();
+
+ if (b > a) {
+ push(b);
+ } else {
+ push(a);
+ }
+}
+
+void ScummEngine_v90he::o90_sin() {
+ double a = pop() * PI / 180.;
+ push((int)(sin(a) * 100000));
+}
+
+void ScummEngine_v90he::o90_cos() {
+ double a = pop() * PI / 180.;
+ push((int)(cos(a) * 100000));
+}
+
+void ScummEngine_v90he::o90_sqrt() {
+ int i = pop();
+ if (i < 2) {
+ push(i);
+ } else {
+ push((int)sqrt((double)(i + 1)));
+ }
+}
+
+void ScummEngine_v90he::o90_atan2() {
+ int y = pop();
+ int x = pop();
+ int a = (int)(atan2((double)y, (double)x) * 180. / PI);
+ if (a < 0) {
+ a += 360;
+ }
+ push(a);
+}
+
+void ScummEngine_v90he::o90_getSegmentAngle() {
+ int y1 = pop();
+ int x1 = pop();
+ int dy = y1 - pop();
+ int dx = x1 - pop();
+ int a = (int)(atan2((double)dy, (double)dx) * 180. / PI);
+ if (a < 0) {
+ a += 360;
+ }
+ push(a);
+}
+
+void ScummEngine_v90he::o90_getActorData() {
+ Actor *a;
+
+ int subOp = pop();
+ int val = pop();
+ int act = pop();
+
+ a = derefActor(act, "o90_getActorData");
+
+ switch (subOp) {
+ case 1:
+ push(a->isUserConditionSet(val));
+ break;
+ case 2:
+ checkRange(15, 0, val, "Limb %d out of range");
+ push(a->_cost.frame[val]);
+ break;
+ case 3:
+ push(a->getAnimSpeed());
+ break;
+ case 4:
+ push(a->_shadowMode);
+ break;
+ case 5:
+ push(a->_layer);
+ break;
+ case 6:
+ push(a->_hePaletteNum);
+ break;
+ default:
+ error("o90_getActorData: Unknown actor property %d", subOp);
+ }
+}
+
+void ScummEngine_v90he::o90_startScriptUnk() {
+ int args[25];
+ int script, cycle;
+ byte flags;
+
+ getStackList(args, ARRAYSIZE(args));
+ cycle = pop();
+ script = pop();
+ flags = fetchScriptByte();
+ runScript(script, (flags == 199 || flags == 200), (flags == 195 || flags == 200), args, cycle);
+}
+
+void ScummEngine_v90he::o90_jumpToScriptUnk() {
+ int args[25];
+ int script, cycle;
+ byte flags;
+
+ getStackList(args, ARRAYSIZE(args));
+ cycle = pop();
+ script = pop();
+ flags = fetchScriptByte();
+ stopObjectCode();
+ runScript(script, (flags == 199 || flags == 200), (flags == 195 || flags == 200), args, cycle);
+}
+
+void ScummEngine_v90he::o90_videoOps() {
+ // Uses Smacker video
+ int status = fetchScriptByte();
+ int subOp = status - 49;
+
+ switch (subOp) {
+ case 0:
+ copyScriptString(_videoParams.filename, sizeof(_videoParams.filename));
+ _videoParams.status = status;
+ break;
+ case 5:
+ _videoParams.flags |= pop();
+ break;
+ case 8:
+ memset(_videoParams.filename, 0, sizeof(_videoParams.filename));
+ _videoParams.unk2 = pop();
+ break;
+ case 14:
+ _videoParams.wizResNum = pop();
+ if (_videoParams.wizResNum)
+ _videoParams.flags |= 2;
+ break;
+ case 116:
+ _videoParams.status = status;
+ break;
+ case 206:
+ if (_videoParams.status == 49) {
+ // Start video
+ if (_videoParams.flags == 0)
+ _videoParams.flags = 4;
+
+ if (_videoParams.flags == 2) {
+ // result = startVideo(_videoParams.filename, _videoParams.flags, _videoParams.wizResNum);
+ // VAR(119) = result;
+ } else {
+ // result = startVideo(_videoParams.filename, _videoParams.flags);
+ // VAR(119) = result;
+ }
+ } else if (_videoParams.status == 165) {
+ // Stop video
+ }
+ break;
+ default:
+ error("o90_videoOps: unhandled case %d", subOp);
+ }
+
+ debug(1, "o90_videoOps stub (%d)", subOp);
+}
+
+void ScummEngine_v90he::o90_getVideoData() {
+ // Uses Smacker video
+ byte subOp = fetchScriptByte();
+ subOp -= 32;
+
+ switch (subOp) {
+ case 0: // Get width
+ pop();
+ break;
+ case 1: // Get height
+ pop();
+ break;
+ case 4: // Get frame count
+ pop();
+ break;
+ case 20: // Get current frame
+ pop();
+ break;
+ case 31: // Get image number
+ pop();
+ break;
+ case 107: // Get statistics
+ pop();
+ pop();
+ break;
+ default:
+ error("o90_getVideoData: unhandled case %d", subOp);
+ }
+
+ push(-1);
+ debug(1, "o90_getVideoData stub (%d)", subOp);
+}
+
+void ScummEngine_v90he::o90_wizImageOps() {
+ int a, b;
+
+ int subOp = fetchScriptByte();
+ subOp -= 46;
+
+ switch (subOp) {
+ case -14: // HE99+
+ _wizParams.processFlags |= kWPFUseDefImgWidth;
+ _wizParams.resDefImgW = pop();
+ break;
+ case -13: // HE99+
+ _wizParams.processFlags |= kWPFUseDefImgHeight;
+ _wizParams.resDefImgH = pop();
+ break;
+ case 0:
+ // Dummy case
+ pop();
+ break;
+ case 1:
+ _wizParams.box.bottom = pop();
+ _wizParams.box.right = pop();
+ _wizParams.box.top = pop();
+ _wizParams.box.left = pop();
+ break;
+ case 2:
+ _wizParams.processMode = 1;
+ break;
+ case 3:
+ _wizParams.processFlags |= kWPFUseFile;
+ _wizParams.processMode = 3;
+ copyScriptString(_wizParams.filename, sizeof(_wizParams.filename));
+ break;
+ case 4:
+ _wizParams.processFlags |= kWPFUseFile;
+ _wizParams.processMode = 4;
+ copyScriptString(_wizParams.filename, sizeof(_wizParams.filename));
+ _wizParams.fileWriteMode = pop();
+ break;
+ case 5:
+ _wizParams.processFlags |= kWPFClipBox | 0x100;
+ _wizParams.processMode = 2;
+ _wizParams.box.bottom = pop();
+ _wizParams.box.right = pop();
+ _wizParams.box.top = pop();
+ _wizParams.box.left = pop();
+ _wizParams.compType = pop();
+ break;
+ case 6:
+ _wizParams.processFlags |= kWPFNewState;
+ _wizParams.img.state = pop();
+ break;
+ case 7:
+ _wizParams.processFlags |= kWPFRotate;
+ _wizParams.angle = pop();
+ break;
+ case 8:
+ _wizParams.processFlags |= kWPFNewFlags;
+ _wizParams.img.flags |= pop();
+ break;
+ case 10:
+ _wizParams.img.flags = pop();
+ _wizParams.img.state = pop();
+ _wizParams.img.y1 = pop();
+ _wizParams.img.x1 = pop();
+ _wizParams.img.resNum = pop();
+ _wiz->displayWizImage(&_wizParams.img);
+ break;
+ case 11:
+ _wizParams.img.resNum = pop();
+ _wizParams.processMode = 0;
+ _wizParams.processFlags = 0;
+ _wizParams.remapNum = 0;
+ _wizParams.img.flags = 0;
+ _wizParams.field_184 = 0;
+ _wizParams.field_180 = 0;
+ _wizParams.spriteId = 0;
+ _wizParams.spriteGroup = 0;
+ break;
+ case 16: // HE99+
+ _wizParams.processFlags |= kWPFMaskImg;
+ _wizParams.sourceImage = pop();
+ break;
+ case 19:
+ case 108:
+ _wizParams.processFlags |= kWPFSetPos;
+ _wizParams.img.y1 = pop();
+ _wizParams.img.x1 = pop();
+ break;
+ case 20:
+ case 203: // HE98+
+ b = pop();
+ a = pop();
+ _wizParams.processFlags |= kWPFRemapPalette;
+ _wizParams.processMode = 6;
+ if (_wizParams.remapNum == 0) {
+ memset(_wizParams.remapIndex, 0, sizeof(_wizParams.remapIndex));
+ } else {
+ assert(_wizParams.remapNum < ARRAYSIZE(_wizParams.remapIndex));
+ _wizParams.remapIndex[_wizParams.remapNum] = a;
+ _wizParams.remapColor[a] = b;
+ ++_wizParams.remapNum;
+ }
+ break;
+ case 21:
+ _wizParams.processFlags |= kWPFClipBox;
+ _wizParams.box.bottom = pop();
+ _wizParams.box.right = pop();
+ _wizParams.box.top = pop();
+ _wizParams.box.left = pop();
+ break;
+ case 40: // HE99+
+ _wizParams.processFlags |= kWPFPaletteNum;
+ _wizParams.img.palette = pop();
+ break;
+ case 46:
+ _wizParams.processFlags |= kWPFScaled;
+ _wizParams.scale = pop();
+ break;
+ case 52:
+ _wizParams.processFlags |= kWPFShadow;
+ _wizParams.img.shadow = pop();
+ break;
+ case 85: // HE99+
+ _wizParams.processFlags |= 0x1000 | 0x100 | 0x2;
+ _wizParams.processMode = 7;
+ _wizParams.field_168 = pop();
+ _wizParams.field_164 = pop();
+ _wizParams.compType = pop();
+ break;
+ case 87: // HE99+
+ _wizParams.processFlags |= kWPFFillColor | kWPFClipBox2;
+ _wizParams.processMode = 9;
+ _wizParams.fillColor = pop();
+ _wizParams.box2.bottom = pop();
+ _wizParams.box2.right = pop();
+ _wizParams.box2.top = pop();
+ _wizParams.box2.left = pop();
+ break;
+ case 88: // HE99+
+ _wizParams.processFlags |= kWPFFillColor | kWPFClipBox2;
+ _wizParams.processMode = 10;
+ _wizParams.fillColor = pop();
+ _wizParams.box2.bottom = pop();
+ _wizParams.box2.right = pop();
+ _wizParams.box2.top = pop();
+ _wizParams.box2.left = pop();
+ break;
+ case 89: // HE99+
+ _wizParams.processFlags |= kWPFFillColor | kWPFClipBox2;
+ _wizParams.processMode = 11;
+ _wizParams.fillColor = pop();
+ _wizParams.box2.top = _wizParams.box2.bottom = pop();
+ _wizParams.box2.left = _wizParams.box2.right = pop();
+ break;
+ case 90: // HE99+
+ _wizParams.processFlags |= kWPFFillColor | kWPFClipBox2;
+ _wizParams.processMode = 12;
+ _wizParams.fillColor = pop();
+ _wizParams.box2.top = _wizParams.box2.bottom = pop();
+ _wizParams.box2.left = _wizParams.box2.right = pop();
+ break;
+ case 91: // HE99+
+ _wizParams.processFlags |= kWPFDstResNum;
+ _wizParams.dstResNum = pop();
+ break;
+ case 93: // HE99+
+ _wizParams.processFlags |= 0x100000;
+ _wizParams.field_180 = pop();
+ _wizParams.field_184 = pop();
+ break;
+ case 95: // HE99+
+ _wizParams.processMode = 13;
+ break;
+ case 96: // HE99+
+ _wizParams.field_239D = pop();
+ _wizParams.field_2399 = pop();
+ _wizParams.field_23A5 = pop();
+ _wizParams.field_23A1 = pop();
+ copyScriptString(_wizParams.string2, sizeof(_wizParams.string2));
+ _wizParams.processMode = 15;
+ break;
+ case 97: // HE99+
+ _wizParams.processMode = 16;
+ _wizParams.field_23AD = pop();
+ _wizParams.field_23A9 = pop();
+ copyScriptString(_wizParams.string1, sizeof(_wizParams.string1));
+ break;
+ case 143: // HE99+
+ _wizParams.processMode = 17;
+ _wizParams.field_23CD = pop();
+ _wizParams.field_23C9 = pop();
+ _wizParams.field_23C5 = pop();
+ _wizParams.field_23C1 = pop();
+ _wizParams.field_23BD = pop();
+ _wizParams.field_23B9 = pop();
+ _wizParams.field_23B5 = pop();
+ _wizParams.field_23B1 = pop();
+ break;
+ case 150: // HE99+
+ _wizParams.processMode = 14;
+ break;
+ case 171: // HE99+
+ _wizParams.processMode = 8;
+ break;
+ case 200:
+ _wizParams.processFlags |= kWPFNewFlags | kWPFSetPos | 2;
+ _wizParams.img.flags |= kWIFIsPolygon;
+ _wizParams.field_164 = _wizParams.img.y1 = _wizParams.img.x1 = pop();
+ break;
+ case 209:
+ if (_wizParams.img.resNum)
+ _wiz->processWizImage(&_wizParams);
+ break;
+ default:
+ error("o90_wizImageOps: unhandled case %d", subOp);
+ }
+}
+
+void ScummEngine_v90he::o90_getDistanceBetweenPoints() {
+ int x1, y1, z1, x2, y2, z2, dx, dy, dz, d;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 23: // HE100
+ case 28:
+ y2 = pop();
+ x2 = pop();
+ y1 = pop();
+ x1 = pop();
+ dx = x2 - x1;
+ dy = y2 - y1;
+ d = dx * dx + dy * dy;
+ if (d < 2) {
+ push(d);
+ } else {
+ push((int)sqrt((double)(d + 1)));
+ }
+ break;
+ case 24: // HE100
+ case 29:
+ z2 = pop();
+ y2 = pop();
+ x2 = pop();
+ z1 = pop();
+ y1 = pop();
+ x1 = pop();
+ dx = x2 - x1;
+ dy = y2 - y1;
+ dz = z2 - z1;
+ d = dx * dx + dy * dy + dz * dz;
+ if (d < 2) {
+ push(d);
+ } else {
+ push((int)sqrt((double)(d + 1)));
+ }
+ break;
+ default:
+ error("o90_getDistanceBetweenPoints: Unknown case %d", subOp);
+ }
+}
+
+void ScummEngine_v90he::o90_getSpriteInfo() {
+ int args[16];
+ int spriteId, flags, groupId, type;
+ int32 x, y;
+
+ byte subOp = fetchScriptByte();
+ subOp -= 30;
+
+ switch (subOp) {
+ case 0:
+ spriteId = pop();
+ if (spriteId) {
+ _sprite->getSpritePosition(spriteId, x, y);
+ push(x);
+ } else {
+ push(0);
+ }
+ break;
+ case 1:
+ spriteId = pop();
+ if (spriteId) {
+ _sprite->getSpritePosition(spriteId, x, y);
+ push(y);
+ } else {
+ push(0);
+ }
+ break;
+ case 2:
+ spriteId = pop();
+ if (spriteId) {
+ _sprite->getSpriteImageDim(spriteId, x, y);
+ push(x);
+ } else {
+ push(0);
+ }
+ break;
+ case 3:
+ spriteId = pop();
+ if (spriteId) {
+ _sprite->getSpriteImageDim(spriteId, x, y);
+ push(y);
+ } else {
+ push(0);
+ }
+ break;
+ case 4:
+ spriteId = pop();
+ if (spriteId) {
+ _sprite->getSpriteDist(spriteId, x, y);
+ push(x);
+ } else {
+ push(0);
+ }
+ break;
+ case 5:
+ spriteId = pop();
+ if (spriteId) {
+ _sprite->getSpriteDist(spriteId, x, y);
+ push(y);
+ } else {
+ push(0);
+ }
+ break;
+ case 6:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteImageStateCount(spriteId));
+ else
+ push(0);
+ break;
+ case 7:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteGroup(spriteId));
+ else
+ push(0);
+ break;
+ case 8:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteDisplayX(spriteId));
+ else
+ push(0);
+ break;
+ case 9:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteDisplayY(spriteId));
+ else
+ push(0);
+ break;
+ case 12:
+ flags = pop();
+ spriteId = pop();
+ if (spriteId) {
+ switch(flags) {
+ case 0:
+ push(_sprite->getSpriteFlagXFlipped(spriteId));
+ break;
+ case 1:
+ push(_sprite->getSpriteFlagYFlipped(spriteId));
+ break;
+ case 2:
+ push(_sprite->getSpriteFlagActive(spriteId));
+ break;
+ case 3:
+ push(_sprite->getSpriteFlagDoubleBuffered(spriteId));
+ break;
+ case 4:
+ push(_sprite->getSpriteFlagRemapPalette(spriteId));
+ break;
+ default:
+ push(0);
+ }
+ } else {
+ push(0);
+ }
+ break;
+ case 13:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpritePriority(spriteId));
+ else
+ push(0);
+ break;
+ case 15:
+ if (_heversion == 99) {
+ flags = getStackList(args, ARRAYSIZE(args));
+ type = pop();
+ groupId = pop();
+ y = pop();
+ x = pop();
+ push(_sprite->findSpriteWithClassOf(x, y, groupId, type, flags, args));
+ } else if (_heversion == 98) {
+ type = pop();
+ groupId = pop();
+ y = pop();
+ x = pop();
+ push(_sprite->findSpriteWithClassOf(x, y, groupId, type, 0, 0));
+ } else {
+ groupId = pop();
+ y = pop();
+ x = pop();
+ push(_sprite->findSpriteWithClassOf(x, y, groupId, 0, 0, 0));
+ }
+ break;
+ case 22:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteImageState(spriteId));
+ else
+ push(0);
+ break;
+ case 32:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteSourceImage(spriteId));
+ else
+ push(0);
+ break;
+ case 33:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteImage(spriteId));
+ else
+ push(0);
+ break;
+ case 38:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteFlagEraseType(spriteId));
+ else
+ push(1);
+ break;
+ case 52:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteFlagAutoAnim(spriteId));
+ else
+ push(0);
+ break;
+ case 56:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpritePalette(spriteId));
+ else
+ push(0);
+ break;
+ case 62:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteScale(spriteId));
+ else
+ push(0);
+ break;
+ case 67:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteAnimSpeed(spriteId));
+ else
+ push(1);
+ break;
+ case 68:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteShadow(spriteId));
+ else
+ push(0);
+ break;
+ case 94:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteFlagUpdateType(spriteId));
+ else
+ push(0);
+ break;
+ case 95:
+ flags = getStackList(args, ARRAYSIZE(args));
+ spriteId = pop();
+ if (spriteId) {
+ push(_sprite->getSpriteClass(spriteId, flags, args));
+ } else {
+ push(0);
+ }
+ break;
+ case 109:
+ flags = pop();
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteGeneralProperty(spriteId, flags));
+ else
+ push(0);
+ break;
+ case 110:
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteMaskImage(spriteId));
+ else
+ push(0);
+ break;
+ case 168:
+ pop();
+ spriteId = pop();
+ if (spriteId)
+ push(_sprite->getSpriteUserValue(spriteId));
+ else
+ push(0);
+ break;
+ default:
+ error("o90_getSpriteInfo: Unknown case %d", subOp);
+ }
+}
+
+void ScummEngine_v90he::o90_setSpriteInfo() {
+ int args[16];
+ int spriteId;
+ int32 tmp[2];
+ int n;
+
+ byte subOp = fetchScriptByte();
+ subOp -= 34;
+
+ switch (subOp) {
+ case 0:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++) {
+ _sprite->getSpriteDist(spriteId, tmp[0], tmp[1]);
+ _sprite->setSpriteDist(spriteId, args[0], tmp[1]);
+ }
+ break;
+ case 1:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++) {
+ _sprite->getSpriteDist(spriteId, tmp[0], tmp[1]);
+ _sprite->setSpriteDist(spriteId, tmp[0], args[0]);
+ }
+ break;
+ case 3:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteGroup(spriteId, args[0]);
+ break;
+ case 8:
+ args[1] = pop();
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ switch(args[1]) {
+ case 0:
+ _sprite->setSpriteFlagXFlipped(spriteId, args[0]);
+ break;
+ case 1:
+ _sprite->setSpriteFlagYFlipped(spriteId, args[0]);
+ break;
+ case 2:
+ _sprite->setSpriteFlagActive(spriteId, args[0]);
+ break;
+ case 3:
+ _sprite->setSpriteFlagDoubleBuffered(spriteId, args[0]);
+ break;
+ case 4:
+ _sprite->setSpriteFlagRemapPalette(spriteId, args[0]);
+ break;
+ default:
+ break;
+ }
+ break;
+ case 9:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpritePriority(spriteId, args[0]);
+ break;
+ case 10:
+ args[1] = pop();
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->moveSprite(spriteId, args[0], args[1]);
+ break;
+ case 18:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteImageState(spriteId, args[0]);
+ break;
+ case 19:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteAngle(spriteId, args[0]);
+ break;
+ case 23:
+ if (_features & GF_HE_985 || _heversion >= 99) {
+ _curMaxSpriteId = pop();
+ _curSpriteId = pop();
+
+ if (_curSpriteId > _curMaxSpriteId)
+ SWAP(_curSpriteId, _curMaxSpriteId);
+ } else {
+ _curSpriteId = pop();
+ _curMaxSpriteId = _curSpriteId; // to make all functions happy
+ }
+ break;
+ case 28: // HE99+
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteSourceImage(spriteId, args[0]);
+ break;
+ case 29:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteImage(spriteId, args[0]);
+ break;
+ case 31:
+ args[1] = pop();
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpritePosition(spriteId, args[0], args[1]);
+ break;
+ case 34:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteFlagEraseType(spriteId, args[0]);
+ break;
+ case 43:
+ args[1] = pop();
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteDist(spriteId, args[0], args[1]);
+ break;
+ case 48:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteFlagAutoAnim(spriteId, args[0]);
+ break;
+ case 52: // HE 98+
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpritePalette(spriteId, args[0]);
+ break;
+ case 58: // HE 99+
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteScale(spriteId, args[0]);
+ break;
+ case 63: // HE 98+
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteAnimSpeed(spriteId, args[0]);
+ break;
+ case 64:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteShadow(spriteId, args[0]);
+ break;
+ case 90:
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteFlagUpdateType(spriteId, args[0]);
+ break;
+ case 91:
+ n = getStackList(args, ARRAYSIZE(args));
+ if (_curSpriteId != 0 && _curMaxSpriteId != 0 && n != 0) {
+ int *p = &args[n - 1];
+ do {
+ int code = *p;
+ if (code == 0) {
+ for (int i = _curSpriteId; i <= _curMaxSpriteId; ++i) {
+ _sprite->setSpriteResetClass(i);
+ }
+ } else if (code & 0x80) {
+ for (int i = _curSpriteId; i <= _curMaxSpriteId; ++i) {
+ _sprite->setSpriteSetClass(i, code & 0x7F, 1);
+ }
+ } else {
+ for (int i = _curSpriteId; i <= _curMaxSpriteId; ++i) {
+ _sprite->setSpriteSetClass(i, code & 0x7F, 0);
+ }
+ }
+ --p;
+ } while (--n);
+ }
+ break;
+ case 105: // HE 99+
+ args[1] = pop();
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteGeneralProperty(spriteId, args[0], args[1]);
+ break;
+ case 106: // HE 99+
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteMaskImage(spriteId, args[0]);
+ break;
+ case 124:
+ _sprite->resetTables(true);
+ break;
+ case 164:
+ args[1] = pop();
+ args[0] = pop();
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->setSpriteUserValue(spriteId, args[0], args[1]);
+ break;
+ case 183:
+ if (_curSpriteId > _curMaxSpriteId)
+ break;
+ spriteId = _curSpriteId;
+ if (!spriteId)
+ spriteId++;
+
+ for (; spriteId <= _curMaxSpriteId; spriteId++)
+ _sprite->resetSprite(spriteId);
+ break;
+ default:
+ error("o90_setSpriteInfo: Unknown case %d", subOp);
+ }
+}
+
+void ScummEngine_v90he::o90_getSpriteGroupInfo() {
+ int32 tx, ty;
+ int spriteGroupId, type;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 8: // HE 99+
+ spriteGroupId = pop();
+ if (spriteGroupId)
+ push(getGroupSpriteArray(spriteGroupId));
+ else
+ push(0);
+ break;
+ case 30:
+ spriteGroupId = pop();
+ if (spriteGroupId) {
+ _sprite->getGroupPosition(spriteGroupId, tx, ty);
+ push(tx);
+ } else {
+ push(0);
+ }
+ break;
+ case 31:
+ spriteGroupId = pop();
+ if (spriteGroupId) {
+ _sprite->getGroupPosition(spriteGroupId, tx, ty);
+ push(ty);
+ } else {
+ push(0);
+ }
+ break;
+ case 42: // HE 99+
+ type = pop();
+ spriteGroupId = pop();
+ if (spriteGroupId) {
+ switch(type) {
+ case 0:
+ push(_sprite->getGroupXMul(spriteGroupId));
+ break;
+ case 1:
+ push(_sprite->getGroupXDiv(spriteGroupId));
+ break;
+ case 2:
+ push(_sprite->getGroupYMul(spriteGroupId));
+ break;
+ case 3:
+ push(_sprite->getGroupYDiv(spriteGroupId));
+ break;
+ default:
+ push(0);
+ }
+ } else {
+ push(0);
+ }
+ break;
+ case 43:
+ spriteGroupId = pop();
+ if (spriteGroupId)
+ push(_sprite->getGroupPriority(spriteGroupId));
+ else
+ push(0);
+ break;
+ case 63: // HE 99+
+ spriteGroupId = pop();
+ if (spriteGroupId)
+ push(_sprite->getGroupDstResNum(spriteGroupId));
+ else
+ push(0);
+ break;
+ case 139: // HE 99+
+ // dummy case
+ pop();
+ pop();
+ push(0);
+ break;
+ default:
+ error("o90_getSpriteGroupInfo: Unknown case %d", subOp);
+ }
+}
+
+void ScummEngine_v90he::o90_setSpriteGroupInfo() {
+ int type, value1, value2, value3, value4;
+
+ byte subOp = fetchScriptByte();
+ subOp -= 37;
+
+ switch (subOp) {
+ case 0:
+ type = pop() - 1;
+ switch (type) {
+ case 0:
+ value2 = pop();
+ value1 = pop();
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->moveGroupMembers(_curSpriteGroupId, value1, value2);
+ break;
+ case 1:
+ value1 = pop();
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->setGroupMembersPriority(_curSpriteGroupId, value1);
+ break;
+ case 2:
+ value1 = pop();
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->setGroupMembersGroup(_curSpriteGroupId, value1);
+ break;
+ case 3:
+ value1 = pop();
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->setGroupMembersUpdateType(_curSpriteGroupId, value1);
+ break;
+ case 4:
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->setGroupMembersResetSprite(_curSpriteGroupId);
+ break;
+ case 5:
+ value1 = pop();
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->setGroupMembersAnimationSpeed(_curSpriteGroupId, value1);
+ break;
+ case 6:
+ value1 = pop();
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->setGroupMembersAutoAnimFlag(_curSpriteGroupId, value1);
+ break;
+ case 7:
+ value1 = pop();
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->setGroupMembersShadow(_curSpriteGroupId, value1);
+ break;
+ default:
+ error("o90_setSpriteGroupInfo subOp 0: Unknown case %d", subOp);
+ }
+ break;
+ case 5:
+ type = pop();
+ value1 = pop();
+ if (!_curSpriteGroupId)
+ break;
+
+ switch (type) {
+ case 0:
+ _sprite->setGroupXMul(_curSpriteGroupId, value1);
+ break;
+ case 1:
+ _sprite->setGroupXDiv(_curSpriteGroupId, value1);
+ break;
+ case 2:
+ _sprite->setGroupYMul(_curSpriteGroupId, value1);
+ break;
+ case 3:
+ _sprite->setGroupYDiv(_curSpriteGroupId, value1);
+ break;
+ default:
+ error("o90_setSpriteGroupInfo subOp 5: Unknown case %d", subOp);
+ }
+ break;
+ case 6:
+ value1 = pop();
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->setGroupPriority(_curSpriteGroupId, value1);
+ break;
+ case 7:
+ value2 = pop();
+ value1 = pop();
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->moveGroup(_curSpriteGroupId, value1, value2);
+ break;
+ case 20:
+ _curSpriteGroupId = pop();
+ break;
+ case 26:
+ value1 = pop();
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->setGroupImage(_curSpriteGroupId, value1);
+ break;
+ case 28:
+ value2 = pop();
+ value1 = pop();
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->setGroupPosition(_curSpriteGroupId, value1, value2);
+ break;
+ case 30:
+ value4 = pop();
+ value3 = pop();
+ value2 = pop();
+ value1 = pop();
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->setGroupBounds(_curSpriteGroupId, value1, value2, value3, value4);
+ break;
+ case 56:
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->resetGroupBounds(_curSpriteGroupId);
+ break;
+ case 180:
+ if (!_curSpriteGroupId)
+ break;
+
+ _sprite->resetGroup(_curSpriteGroupId);
+ break;
+ default:
+ error("o90_setSpriteGroupInfo: Unknown case %d", subOp);
+ }
+}
+
+void ScummEngine_v90he::o90_getWizData() {
+ byte filename[4096];
+ int state, resId;
+ int32 w, h;
+ int32 x, y;
+
+ byte subOp = fetchScriptByte();
+ subOp -= 30;
+
+ switch (subOp) {
+ case 0:
+ state = pop();
+ resId = pop();
+ _wiz->getWizImageSpot(resId, state, x, y);
+ push(x);
+ break;
+ case 1:
+ state = pop();
+ resId = pop();
+ _wiz->getWizImageSpot(resId, state, x, y);
+ push(y);
+ break;
+ case 2:
+ state = pop();
+ resId = pop();
+ _wiz->getWizImageDim(resId, state, w, h);
+ push(w);
+ break;
+ case 3:
+ state = pop();
+ resId = pop();
+ _wiz->getWizImageDim(resId, state, w, h);
+ push(h);
+ break;
+ case 6:
+ resId = pop();
+ push(_wiz->getWizImageStates(resId));
+ break;
+ case 15:
+ y = pop();
+ x = pop();
+ state = pop();
+ resId = pop();
+ push(_wiz->isWizPixelNonTransparent(resId, state, x, y, 0));
+ break;
+ case 36:
+ y = pop();
+ x = pop();
+ state = pop();
+ resId = pop();
+ push(_wiz->getWizPixelColor(resId, state, x, y, 0));
+ break;
+ case 100:
+ h = pop();
+ w = pop();
+ y = pop();
+ x = pop();
+ state = pop();
+ resId = pop();
+ if (x == -1 && y == -1 && w == -1 && h == -1) {
+ _wiz->getWizImageDim(resId, state, w, h);
+ x = 0;
+ y = 0;
+ }
+ push(computeWizHistogram(resId, state, x, y, w, h));
+ break;
+ case 109:
+ pop();
+ pop();
+ push(0);
+ break;
+ case 111:
+ pop();
+ copyScriptString(filename, sizeof(filename));
+ pop();
+ push(0);
+ debug(0, "o90_getWizData() case 111 unhandled");
+ break;
+ default:
+ error("o90_getWizData: Unknown case %d", subOp);
+ }
+}
+
+void ScummEngine_v90he::o90_floodFill() {
+ byte subOp = fetchScriptByte();
+ subOp -= 54;
+
+ switch (subOp) {
+ case 0:
+ pop();
+ break;
+ case 3:
+ memset(&_floodFillParams, 0, sizeof(_floodFillParams));
+ _floodFillParams.box.left = 0;
+ _floodFillParams.box.top = 0;
+ _floodFillParams.box.right = 639;
+ _floodFillParams.box.bottom = 479;
+ break;
+ case 11:
+ _floodFillParams.y = pop();
+ _floodFillParams.x = pop();
+ break;
+ case 12:
+ _floodFillParams.flags = pop();
+ break;
+ case 13:
+ _floodFillParams.box.bottom = pop();
+ _floodFillParams.box.right = pop();
+ _floodFillParams.box.top = pop();
+ _floodFillParams.box.left = pop();
+ break;
+ case 201:
+ floodFill(&_floodFillParams, this);
+ break;
+ default:
+ error("o90_floodFill: Unknown case %d", subOp);
+ }
+}
+
+void ScummEngine_v90he::o90_shl() {
+ int a = pop();
+ push(pop() << a);
+}
+
+void ScummEngine_v90he::o90_shr() {
+ int a = pop();
+ push(pop() >> a);
+}
+
+void ScummEngine_v90he::o90_xor() {
+ int a = pop();
+ push(pop() ^ a);
+}
+
+void ScummEngine_v90he::o90_mod() {
+ int a = pop();
+ if (a == 0)
+ error("modulus by zero");
+ push(pop() % a);
+}
+
+void ScummEngine_v90he::o90_findAllObjectsWithClassOf() {
+ int args[16];
+ int cond, num, cls, tmp;
+ bool b;
+
+ num = getStackList(args, ARRAYSIZE(args));
+ int room = pop();
+ int numObjs = 0;
+
+ if (room != _currentRoom)
+ error("o90_findAllObjectsWithClassOf: current room is not %d", room);
+
+ writeVar(0, 0);
+ defineArray(0, kDwordArray, 0, 0, 0, _numLocalObjects);
+ for (int i = 1; i < _numLocalObjects; i++) {
+ cond = 1;
+ tmp = num;
+ while (--tmp >= 0) {
+ cls = args[tmp];
+ b = getClass(_objs[i].obj_nr, cls);
+ if ((cls & 0x80 && !b) || (!(cls & 0x80) && b))
+ cond = 0;
+ }
+
+ if (cond) {
+ numObjs++;
+ writeArray(0, 0, numObjs, _objs[i].obj_nr);
+ }
+ }
+
+ writeArray(0, 0, 0, numObjs);
+
+ push(readVar(0));
+}
+
+void ScummEngine_v90he::o90_getPolygonOverlap() {
+ int args1[32];
+ int args2[32];
+
+ int n1 = getStackList(args1, ARRAYSIZE(args1));
+ int n2 = getStackList(args2, ARRAYSIZE(args2));
+
+ int subOp = pop();
+
+ switch (subOp) {
+ case 1:
+ {
+ Common::Rect r(args1[0], args1[1], args1[2] + 1, args1[3] + 1);
+ Common::Point p(args2[0], args2[1]);
+ push(r.contains(p) ? 1 : 0);
+ }
+ break;
+ case 2:
+ {
+ int dx = args2[0] - args1[0];
+ int dy = args2[1] - args1[1];
+ int dist = dx * dx + dy * dy;
+ if (dist >= 2) {
+ dist = (int)sqrt((double)(dist + 1));
+ }
+ if (_heversion >= 98) {
+ push((dist <= args1[2]) ? 1 : 0);
+ } else {
+ push((dist > args1[2]) ? 1 : 0);
+ }
+ }
+ break;
+ case 3:
+ {
+ Common::Rect r1(args1[0], args1[1], args1[2] + 1, args1[3] + 1);
+ Common::Rect r2(args2[0], args2[1], args2[2] + 1, args2[3] + 1);
+ push(r2.intersects(r1) ? 1 : 0);
+ }
+ break;
+ case 4:
+ {
+ int dx = args2[0] - args1[0];
+ int dy = args2[1] - args1[1];
+ int dist = dx * dx + dy * dy;
+ if (dist >= 2) {
+ dist = (int)sqrt((double)(dist + 1));
+ }
+ push((dist < args1[2] && dist < args2[2]) ? 1 : 0);
+ }
+ break;
+ case 5:
+ {
+ assert((n1 & 1) == 0);
+ n1 /= 2;
+ if (n1 == 0) {
+ push(0);
+ } else {
+ WizPolygon wp;
+ memset(&wp, 0, sizeof(wp));
+ wp.numVerts = n1;
+ assert(n1 < ARRAYSIZE(wp.vert));
+ for (int i = 0; i < n1; ++i) {
+ wp.vert[i].x = args1[i * 2 + 0];
+ wp.vert[i].y = args1[i * 2 + 1];
+ }
+ push(_wiz->polygonContains(wp, args2[0], args2[1]) ? 1 : 0);
+ }
+ }
+ break;
+ // HE 98+
+ case 6:
+ {
+ Common::Rect r1, r2;
+ _sprite->getSpriteBounds(args2[0], false, r2);
+ _sprite->getSpriteBounds(args1[0], false, r1);
+ if (r2.isValidRect() == false) {
+ push(0);
+ break;
+ }
+
+ if (n2 == 3) {
+ r2.left += args2[1];
+ r2.right += args2[1];
+ r2.top += args2[2];
+ r2.bottom += args2[2];
+ }
+ if (n1 == 3) {
+ r1.left += args1[1];
+ r1.right += args1[1];
+ r1.top += args1[2];
+ r1.bottom += args1[2];
+ }
+ push(r2.intersects(r1) ? 1 : 0);
+ }
+ break;
+ case 7:
+ {
+ Common::Rect r2;
+ _sprite->getSpriteBounds(args2[0], false, r2);
+ Common::Rect r1(args1[0], args1[1], args1[2], args1[3]);
+ if (r2.isValidRect() == false) {
+ push(0);
+ break;
+ }
+
+ if (n2 == 3) {
+ r2.left += args2[1];
+ r2.right += args2[1];
+ r2.top += args2[2];
+ r2.bottom += args2[2];
+ }
+ push(r2.intersects(r1) ? 1 : 0);
+ }
+ break;
+ case 8:
+ case 10: // TODO: Draw sprites to buffer and compare.
+ {
+ Common::Rect r1, r2;
+ _sprite->getSpriteBounds(args2[0], true, r2);
+ _sprite->getSpriteBounds(args1[0], true, r1);
+ if (r2.isValidRect() == false) {
+ push(0);
+ break;
+ }
+
+ if (n2 == 3) {
+ r2.left += args2[1];
+ r2.right += args2[1];
+ r2.top += args2[2];
+ r2.bottom += args2[2];
+ }
+ if (n1 == 3) {
+ r1.left += args1[1];
+ r1.right += args1[1];
+ r1.top += args1[2];
+ r1.bottom += args1[2];
+ }
+ push(r2.intersects(r1) ? 1 : 0);
+ }
+ break;
+ case 9:
+ {
+ Common::Rect r2;
+ _sprite->getSpriteBounds(args2[0], true, r2);
+ Common::Rect r1(args1[0], args1[1], args1[2], args1[3]);
+ if (r2.isValidRect() == false) {
+ push(0);
+ break;
+ }
+
+ if (n2 == 3) {
+ r2.left += args2[1];
+ r2.right += args2[1];
+ r2.top += args2[2];
+ r2.bottom += args2[2];
+ }
+ push(r2.intersects(r1) ? 1 : 0);
+ }
+ break;
+ default:
+ error("o90_getPolygonOverlap: default case %d", subOp);
+ }
+}
+
+void ScummEngine_v90he::o90_cond() {
+ int a = pop();
+ int b = pop();
+ int c = pop();
+
+ if (!c)
+ b = a;
+ push(b);
+}
+
+void ScummEngine_v90he::o90_dim2dim2Array() {
+ int data, dim1start, dim1end, dim2start, dim2end;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 2: // SO_BIT_ARRAY
+ data = kBitArray;
+ break;
+ case 3: // SO_NIBBLE_ARRAY
+ data = kNibbleArray;
+ break;
+ case 4: // SO_BYTE_ARRAY
+ data = kByteArray;
+ break;
+ case 5: // SO_INT_ARRAY
+ data = kIntArray;
+ break;
+ case 6:
+ data = kDwordArray;
+ break;
+ case 7: // SO_STRING_ARRAY
+ data = kStringArray;
+ break;
+ default:
+ error("o90_dim2dim2Array: default case %d", subOp);
+ }
+
+ if (pop() == 2) {
+ dim1end = pop();
+ dim1start = pop();
+ dim2end = pop();
+ dim2start = pop();
+ } else {
+ dim2end = pop();
+ dim2start = pop();
+ dim1end = pop();
+ dim1start = pop();
+ }
+
+ defineArray(fetchScriptWord(), data, dim2start, dim2end, dim1start, dim1end);
+}
+
+void ScummEngine_v90he::o90_redim2dimArray() {
+ int a, b, c, d;
+ d = pop();
+ c = pop();
+ b = pop();
+ a = pop();
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 4:
+ redimArray(fetchScriptWord(), a, b, c, d, kByteArray);
+ break;
+ case 5:
+ redimArray(fetchScriptWord(), a, b, c, d, kIntArray);
+ break;
+ case 6:
+ redimArray(fetchScriptWord(), a, b, c, d, kDwordArray);
+ break;
+ default:
+ error("o90_redim2dimArray: default type %d", subOp);
+ }
+}
+
+void ScummEngine_v90he::o90_getLinesIntersectionPoint() {
+ int var_ix = fetchScriptWord();
+ int var_iy = fetchScriptWord();
+ int line2_y2 = pop();
+ int line2_x2 = pop();
+ int line2_y1 = pop();
+ int line2_x1 = pop();
+ int line1_y2 = pop();
+ int line1_x2 = pop();
+ int line1_y1 = pop();
+ int line1_x1 = pop();
+
+ int result = 0;
+ int ix = 0;
+ int iy = 0;
+
+ bool isLine1Point = (line1_x1 == line1_x2 && line1_y1 == line1_y2);
+ bool isLine2Point = (line2_x1 == line2_x2 && line2_y1 == line2_y2);
+
+ if (isLine1Point) {
+ if (isLine2Point) {
+ if (line1_x1 == line2_x1 && line1_y1 == line2_y2) {
+ ix = line1_x1;
+ iy = line2_x1;
+ result = 1;
+ }
+ } else {
+ // 1 point and 1 line
+ int dx2 = line2_x2 - line2_x1;
+ if (dx2 != 0) {
+ int dy2 = line2_y2 - line2_y1;
+ float y = (float)dy2 / dx2 * (line1_x1 - line2_x1) + line2_y1 + .5f;
+ if (line1_y1 == (int)y) {
+ ix = line1_x1;
+ iy = line1_y1;
+ result = 1;
+ }
+ } else {
+ // vertical line
+ if (line1_x1 == line2_x1) {
+ if (line2_y1 > line2_y2) {
+ if (line1_y1 >= line2_y2 && line1_y1 <= line2_y1) {
+ ix = line1_x1;
+ iy = line1_y1;
+ result = 1;
+ }
+ } else {
+ if (line1_y1 >= line2_y1 && line1_y1 <= line2_y2) {
+ ix = line1_x1;
+ iy = line1_y1;
+ result = 1;
+ }
+ }
+ }
+ }
+ }
+ } else {
+ if (isLine2Point) {
+ // 1 point and 1 line
+ int dx1 = line1_x2 - line1_x1;
+ if (dx1 != 0) {
+ int dy1 = line1_y2 - line1_y1;
+ float y = (float)dy1 / dx1 * (line2_x1 - line1_x1) + line1_y1 + .5f;
+ if (line2_y1 == (int)y) {
+ ix = line2_x1;
+ iy = line2_y1;
+ result = 1;
+ }
+ } else {
+ // vertical line
+ if (line2_x1 == line1_x1) {
+ if (line1_y1 > line1_y2) {
+ if (line2_y1 >= line1_y2 && line2_y1 <= line1_y1) {
+ ix = line2_x1;
+ iy = line2_y1;
+ result = 1;
+ }
+ } else {
+ if (line2_y1 >= line1_y1 && line2_y1 <= line1_y2) {
+ ix = line2_x2;
+ iy = line2_y1;
+ result = 1;
+ }
+ }
+ }
+ }
+ } else {
+ // 2 lines
+ int dy1 = line1_y2 - line1_y1;
+ int dx1 = line1_x2 - line1_x1;
+ int dy2 = line2_y2 - line2_y1;
+ int dx2 = line2_x2 - line2_x1;
+ int det = dx1 * dy2 - dx2 * dy1;
+ int cross_p1 = dx1 * (line1_y1 - line2_y1) - dy1 * (line1_x1 - line2_x1);
+ int cross_p2 = dx2 * (line1_y1 - line2_y1) - dy2 * (line1_x1 - line2_x1);
+ if (det == 0) {
+ // parallel lines
+ if (cross_p2 == 0) {
+ ix = ABS(line2_x2 + line2_x1) / 2;
+ iy = ABS(line2_y2 + line2_y1) / 2;
+ result = 2;
+ }
+ } else {
+ float rcp1 = (float)cross_p1 / det;
+ float rcp2 = (float)cross_p2 / det;
+ if (rcp1 >= 0 && rcp1 <= 1 && rcp2 >= 0 && rcp2 <= 1) {
+ ix = (int)(dx1 * rcp2 + line1_x1 + .5f);
+ iy = (int)(dy1 * rcp2 + line1_y1 + .5f);
+ result = 1;
+ }
+ }
+ }
+ }
+
+ writeVar(var_ix, ix);
+ writeVar(var_iy, iy);
+ push(result);
+}
+
+void ScummEngine_v90he::getArrayDim(int array, int *dim2start, int *dim2end, int *dim1start, int *dim1end) {
+ ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, readVar(array));
+ assert(ah);
+ if (dim2start && *dim2start == -1) {
+ *dim2start = ah->dim2start;
+ }
+ if (dim2end && *dim2end == -1) {
+ *dim2end = ah->dim2end;
+ }
+ if (dim1start && *dim1start == -1) {
+ *dim1start = ah->dim1start;
+ }
+ if (dim1end && *dim1end == -1) {
+ *dim1end = ah->dim1end;
+ }
+}
+
+static int sortArrayOffset;
+
+static int compareByteArray(const void *a, const void *b) {
+ int va = *((const uint8 *)a + sortArrayOffset);
+ int vb = *((const uint8 *)a + sortArrayOffset);
+ return va - vb;
+}
+
+static int compareByteArrayReverse(const void *a, const void *b) {
+ int va = *((const uint8 *)a + sortArrayOffset);
+ int vb = *((const uint8 *)a + sortArrayOffset);
+ return vb - va;
+}
+
+static int compareIntArray(const void *a, const void *b) {
+ int va = (int16)READ_LE_UINT16((const uint8 *)a + sortArrayOffset * 2);
+ int vb = (int16)READ_LE_UINT16((const uint8 *)b + sortArrayOffset * 2);
+ return va - vb;
+}
+
+static int compareIntArrayReverse(const void *a, const void *b) {
+ int va = (int16)READ_LE_UINT16((const uint8 *)a + sortArrayOffset * 2);
+ int vb = (int16)READ_LE_UINT16((const uint8 *)b + sortArrayOffset * 2);
+ return vb - va;
+}
+
+static int compareDwordArray(const void *a, const void *b) {
+ int va = (int32)READ_LE_UINT32((const uint8 *)a + sortArrayOffset * 4);
+ int vb = (int32)READ_LE_UINT32((const uint8 *)b + sortArrayOffset * 4);
+ return va - vb;
+}
+
+static int compareDwordArrayReverse(const void *a, const void *b) {
+ int va = (int32)READ_LE_UINT32((const uint8 *)a + sortArrayOffset * 4);
+ int vb = (int32)READ_LE_UINT32((const uint8 *)b + sortArrayOffset * 4);
+ return vb - va;
+}
+
+void ScummEngine_v90he::sortArray(int array, int dim2start, int dim2end, int dim1start, int dim1end, int sortOrder) {
+ debug(9, "sortArray(%d, [%d,%d,%d,%d], %d)", array, dim2start, dim2end, dim1start, dim1end, sortOrder);
+
+ assert(dim1start == dim1end);
+ checkArrayLimits(array, dim2start, dim2end, dim1start, dim1end);
+ ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, readVar(array));
+ assert(ah);
+
+ const int num = dim2end - dim2start + 1;
+ const int pitch = FROM_LE_32(ah->dim1end) - FROM_LE_32(ah->dim1start) + 1;
+ const int offset = pitch * (dim2start - FROM_LE_32(ah->dim2start));
+ sortArrayOffset = dim1start - FROM_LE_32(ah->dim1start);
+
+ switch (FROM_LE_32(ah->type)) {
+ case kByteArray:
+ case kStringArray:
+ if (sortOrder <= 0) {
+ qsort(ah->data + offset, num, pitch, compareByteArray);
+ } else {
+ qsort(ah->data + offset, num, pitch, compareByteArrayReverse);
+ }
+ break;
+ case kIntArray:
+ if (sortOrder <= 0) {
+ qsort(ah->data + offset * 2, num, pitch * 2, compareIntArray);
+ } else {
+ qsort(ah->data + offset * 2, num, pitch * 2, compareIntArrayReverse);
+ }
+ break;
+ case kDwordArray:
+ if (sortOrder <= 0) {
+ qsort(ah->data + offset * 4, num, pitch * 4, compareDwordArray);
+ } else {
+ qsort(ah->data + offset * 4, num, pitch * 4, compareDwordArrayReverse);
+ }
+ break;
+ default:
+ error("Invalid array type", FROM_LE_32(ah->type));
+ }
+}
+
+void ScummEngine_v90he::o90_sortArray() {
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 129:
+ case 134: // HE100
+ {
+ int array = fetchScriptWord();
+ int sortOrder = pop();
+ int dim1end = pop();
+ int dim1start = pop();
+ int dim2end = pop();
+ int dim2start = pop();
+ getArrayDim(array, &dim2start, &dim2end, &dim1start, &dim1end);
+ sortArray(array, dim2start, dim2end, dim1start, dim1end, sortOrder);
+ }
+ break;
+ default:
+ error("o90_sortArray: Unknown case %d", subOp);
+ }
+}
+
+void ScummEngine_v90he::o90_getObjectData() {
+ byte subOp = fetchScriptByte();
+ subOp -= 32;
+
+ switch (subOp) {
+ case 0:
+ if (_heObjectNum == -1)
+ push(0);
+ else
+ push(_objs[_heObjectNum].width);
+ break;
+ case 1:
+ if (_heObjectNum == -1)
+ push(0);
+ else
+ push(_objs[_heObjectNum].height);
+ break;
+ case 4:
+ push(getObjectImageCount(_heObject));
+ break;
+ case 6:
+ if (_heObjectNum == -1)
+ push(0);
+ else
+ push(_objs[_heObjectNum].x_pos);
+ break;
+ case 7:
+ if (_heObjectNum == -1)
+ push(0);
+ else
+ push(_objs[_heObjectNum].y_pos);
+ break;
+ case 20:
+ push(getState(_heObject));
+ break;
+ case 25:
+ _heObject = pop();
+ _heObjectNum = getObjectIndex(_heObject);
+ break;
+ case 107:
+ // Dummy case
+ pop();
+ push(0);
+ break;
+ default:
+ error("o90_getObjectData: Unknown case %d", subOp);
+ }
+}
+
+void ScummEngine_v90he::o90_getPaletteData() {
+ int b, c, d, e;
+ int palSlot, color;
+
+ byte subOp = fetchScriptByte();
+ subOp -= 45;
+
+ switch (subOp) {
+ case 0:
+ e = pop();
+ d = pop();
+ palSlot = pop();
+ pop();
+ c = pop();
+ b = pop();
+ push(getHEPaletteSimilarColor(palSlot, b, c, d, e));
+ break;
+ case 7:
+ c = pop();
+ b = pop();
+ palSlot = pop();
+ push(getHEPaletteColorComponent(palSlot, b, c));
+ break;
+ case 21:
+ color = pop();
+ palSlot = pop();
+ push(getHEPaletteColor(palSlot, color));
+ break;
+ case 87:
+ c = pop();
+ b = pop();
+ push(getHEPaletteColorComponent(1, b, c));
+ break;
+ case 172:
+ pop();
+ c = pop();
+ c = MAX(0, c);
+ c = MIN(c, 255);
+ b = pop();
+ b = MAX(0, b);
+ b = MIN(b, 255);
+ push(getHEPaletteSimilarColor(1, b, c, 10, 245));
+ break;
+ default:
+ error("o90_getPaletteData: Unknown case %d", subOp);
+ }
+}
+
+void ScummEngine_v90he::o90_paletteOps() {
+ int a, b, c, d, e;
+
+ byte subOp = fetchScriptByte();
+ subOp -= 57;
+
+ switch (subOp) {
+ case 0:
+ _hePaletteNum = pop();
+ break;
+ case 6:
+ b = pop();
+ a = pop();
+ if (_hePaletteNum != 0) {
+ setHEPaletteFromImage(_hePaletteNum, a, b);
+ }
+ break;
+ case 9:
+ e = pop();
+ d = pop();
+ c = pop();
+ b = pop();
+ a = pop();
+ if (_hePaletteNum != 0) {
+ for (; a <= b; ++a) {
+ setHEPaletteColor(_hePaletteNum, a, c, d, e);
+ }
+ }
+ break;
+ case 13:
+ c = pop();
+ b = pop();
+ a = pop();
+ if (_hePaletteNum != 0) {
+ for (; a <= b; ++a) {
+ copyHEPaletteColor(_hePaletteNum, a, c);
+ }
+ }
+ break;
+ case 19: //HE99+
+ a = pop();
+ if (_hePaletteNum != 0) {
+ setHEPaletteFromCostume(_hePaletteNum, a);
+ }
+ break;
+ case 29:
+ a = pop();
+ if (_hePaletteNum != 0) {
+ copyHEPalette(_hePaletteNum, a);
+ }
+ break;
+ case 118:
+ b = pop();
+ a = pop();
+ if (_hePaletteNum != 0) {
+ setHEPaletteFromRoom(_hePaletteNum, a, b);
+ }
+ break;
+ case 160:
+ if (_hePaletteNum != 0) {
+ restoreHEPalette(_hePaletteNum);
+ }
+ break;
+ case 198:
+ _hePaletteNum = 0;
+ break;
+ default:
+ error("o90_paletteOps: Unknown case %d", subOp);
+ }
+}
+
+void ScummEngine_v90he::o90_fontUnk() {
+ // Font related
+ byte string[80];
+ int a;
+
+ byte subOp = fetchScriptByte();
+
+ switch (subOp) {
+ case 60: // HE100
+ case 42:
+ a = pop();
+ if (a == 2) {
+ copyScriptString(string, sizeof(string));
+ push(-1);
+ } else if (a == 1) {
+ pop();
+ writeVar(0, 0);
+ defineArray(0, kStringArray, 0, 0, 0, 0);
+ writeArray(0, 0, 0, 0);
+ push(readVar(0));
+ }
+ break;
+ case 0: // HE100
+ case 57:
+ push(1);
+ break;
+ default:
+ error("o90_fontUnk: Unknown case %d", subOp);
+ }
+
+ debug(1, "o90_fontUnk stub (%d)", subOp);
+}
+
+void ScummEngine_v90he::o90_getActorAnimProgress() {
+ Actor *a = derefActor(pop(), "o90_getActorAnimProgress");
+ push(a->getAnimProgress());
+}
+
+void ScummEngine_v90he::o90_kernelGetFunctions() {
+ int args[29];
+ int num, tmp;
+ Actor *a;
+
+ num = getStackList(args, ARRAYSIZE(args));
+
+ switch (args[0]) {
+ case 1001:
+ {
+ double b = args[1] * PI / 180.;
+ push((int)(sin(b) * 100000));
+ }
+ break;
+ case 1002:
+ {
+ double b = args[1] * PI / 180.;
+ push((int)(cos(b) * 100000));
+ }
+ break;
+ case 1969:
+ a = derefActor(args[1], "o90_kernelGetFunctions: 1969");
+ tmp = a->_heCondMask;
+ tmp &= 0x7FFF0000;
+ push(tmp);
+ break;
+ case 2001:
+ push(_logicHE->dispatch(args[1], num - 2, (int32 *)&args[2]));
+ break;
+ default:
+ error("o90_kernelGetFunctions: default case %d", args[0]);
+ }
+}
+
+void ScummEngine_v90he::o90_kernelSetFunctions() {
+ int args[29];
+ int num, tmp;
+ Actor *a;
+
+ num = getStackList(args, ARRAYSIZE(args));
+
+ switch (args[0]) {
+ case 20:
+ a = derefActor(args[1], "o90_kernelSetFunctions: 20");
+ queueAuxBlock(a);
+ break;
+ case 21:
+ _skipDrawObject = 1;
+ break;
+ case 22:
+ _skipDrawObject = 0;
+ break;
+ case 23:
+ _charset->clearCharsetMask();
+ _fullRedraw = true;
+ break;
+ case 24:
+ _skipProcessActors = 1;
+ redrawAllActors();
+ break;
+ case 25:
+ _skipProcessActors = 0;
+ redrawAllActors();
+ break;
+ case 27:
+ // Used in readdemo
+ break;
+ case 42:
+ _wiz->_rectOverrideEnabled = true;
+ _wiz->_rectOverride.left = args[1];
+ _wiz->_rectOverride.top = args[2];
+ _wiz->_rectOverride.right = args[3];
+ _wiz->_rectOverride.bottom = args[4];
+ break;
+ case 43:
+ _wiz->_rectOverrideEnabled = false;
+ break;
+ case 714:
+ debug(5, "o90_kernelSetFunctions: case 714: type %d resId %d unk1 %d", args[1], args[2], args[3]);
+ break;
+ case 1492:
+ // Remote start script function
+ break;
+ case 1969:
+ a = derefActor(args[1], "o90_kernelSetFunctions: 1969");
+ tmp = a->_heCondMask;
+ tmp ^= args[2];
+ tmp &= 0x7FFF0000;
+ a->_heCondMask ^= tmp;
+ break;
+ case 2001:
+ _logicHE->dispatch(args[1], num - 2, (int32 *)&args[2]);
+ break;
+ default:
+ error("o90_kernelSetFunctions: default case %d (param count %d)", args[0], num);
+ }
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/he/sound_he.cpp b/engines/scumm/he/sound_he.cpp
new file mode 100644
index 0000000000..879e34a99e
--- /dev/null
+++ b/engines/scumm/he/sound_he.cpp
@@ -0,0 +1,516 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+#include "scumm/actor.h"
+#include "scumm/imuse.h"
+#include "scumm/scumm.h"
+#include "scumm/sound.h"
+#include "scumm/util.h"
+
+#include "common/config-manager.h"
+#include "common/timer.h"
+#include "common/util.h"
+
+#include "sound/adpcm.h"
+#include "sound/audiocd.h"
+#include "sound/flac.h"
+#include "sound/mididrv.h"
+#include "sound/mixer.h"
+#include "sound/mp3.h"
+#include "sound/voc.h"
+#include "sound/vorbis.h"
+#include "sound/wave.h"
+
+namespace Scumm {
+
+int Sound::findFreeSoundChannel() {
+ int chan, min;
+
+ min = _vm->VAR(_vm->VAR_RESERVED_SOUND_CHANNELS);
+ if (min == 0) {
+ _vm->VAR(_vm->VAR_RESERVED_SOUND_CHANNELS) = 8;
+ return 1;
+ }
+
+ if (min < 8) {
+ for (chan = min; chan < ARRAYSIZE(_heChannel); chan++) {
+ if (_vm->_mixer->isSoundHandleActive(_heSoundChannels[chan]) == 0)
+ return chan;
+ }
+ } else {
+ return 1;
+ }
+
+ return min;
+}
+
+int Sound::isSoundCodeUsed(int sound) {
+ int chan = -1;
+ for (int i = 0; i < ARRAYSIZE(_heChannel); i ++) {
+ if (_heChannel[i].sound == sound)
+ chan = i;
+ }
+
+ if (chan != -1) {
+ return _heChannel[chan].sbngBlock;
+ } else {
+ return 0;
+ }
+}
+
+int Sound::getSoundPos(int sound) {
+ int chan = -1;
+ for (int i = 0; i < ARRAYSIZE(_heChannel); i ++) {
+ if (_heChannel[i].sound == sound)
+ chan = i;
+ }
+
+ if (chan != -1) {
+ int time = _vm->getHETimer(chan + 4) * 11025 / 1000;
+ return time;
+ } else {
+ return 0;
+ }
+}
+
+int Sound::getSoundVar(int sound, int var) {
+ if (_vm->_heversion >= 90 && var == 26) {
+ return isSoundCodeUsed(sound);
+ }
+
+ checkRange(25, 0, var, "Illegal sound variable %d");
+
+ int chan = -1;
+ for (int i = 0; i < ARRAYSIZE(_heChannel); i ++) {
+ if (_heChannel[i].sound == sound)
+ chan = i;
+ }
+
+ if (chan != -1) {
+ debug(5, "getSoundVar: sound %d var %d result %d", sound, var, _heChannel[chan].soundVars[var]);
+ return _heChannel[chan].soundVars[var];
+ } else {
+ return 0;
+ }
+}
+
+void Sound::setSoundVar(int sound, int var, int val) {
+ checkRange(25, 0, var, "Illegal sound variable %d");
+
+ int chan = -1;
+ for (int i = 0; i < ARRAYSIZE(_heChannel); i ++) {
+ if (_heChannel[i].sound == sound)
+ chan = i;
+ }
+
+ if (chan != -1) {
+ debug(5, "setSoundVar: sound %d var %d val %d", sound, var, val);
+ _heChannel[chan].soundVars[var] = val;
+ }
+}
+
+void Sound::setOverrideFreq(int freq) {
+ _overrideFreq = freq;
+}
+
+void Sound::setupHEMusicFile() {
+ int i, total_size;
+ char buf[32], buf1[128];
+ Common::File musicFile;
+
+ sprintf(buf, "%s.he4", _vm->getBaseName());
+
+ if (_vm->_substResFileNameIndex > 0) {
+ _vm->generateSubstResFileName(buf, buf1, sizeof(buf1));
+ strcpy(buf, buf1);
+ }
+ if (musicFile.open(buf) == true) {
+ musicFile.seek(4, SEEK_SET);
+ total_size = musicFile.readUint32BE();
+ musicFile.seek(16, SEEK_SET);
+ _heMusicTracks = musicFile.readUint32LE();
+ debug(5, "Total music tracks %d", _heMusicTracks);
+
+ int musicStart = (_vm->_heversion >= 80) ? 56 : 20;
+ musicFile.seek(musicStart, SEEK_SET);
+
+ _heMusic = (HEMusic *)malloc((_heMusicTracks + 1) * sizeof(HEMusic));
+ for (i = 0; i < _heMusicTracks; i++) {
+ _heMusic[i].id = musicFile.readUint32LE();
+ _heMusic[i].offset = musicFile.readUint32LE();
+ _heMusic[i].size = musicFile.readUint32LE();
+
+ if (_vm->_heversion >= 80) {
+ musicFile.seek(+9, SEEK_CUR);
+ } else {
+ musicFile.seek(+13, SEEK_CUR);
+ }
+ }
+
+ musicFile.close();
+ }
+}
+
+bool Sound::getHEMusicDetails(int id, int &musicOffs, int &musicSize) {
+ int i;
+
+ for (i = 0; i < _heMusicTracks; i++) {
+ if (_heMusic[i].id == id) {
+ musicOffs = _heMusic[i].offset;
+ musicSize = _heMusic[i].size;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+void Sound::processSoundCode() {
+ byte *codePtr;
+ int chan, tmr, size, time;
+
+ for (chan = 0; chan < ARRAYSIZE(_heChannel); chan++) {
+ if (_heChannel[chan].sound == 0) {
+ continue;
+ }
+
+ if (_heChannel[chan].codeOffs == -1) {
+ continue;
+ }
+
+ tmr = _vm->getHETimer(chan + 4) * 11025 / 1000;
+ tmr += _vm->VAR(_vm->VAR_SOUNDCODE_TMR);
+ if (tmr < 0)
+ tmr = 0;
+
+ if (_heChannel[chan].sound > _vm->_numSounds) {
+ codePtr = _vm->getResourceAddress(rtSpoolBuffer, chan);
+ } else {
+ codePtr = _vm->getResourceAddress(rtSound, _heChannel[chan].sound);
+ }
+ assert(codePtr);
+ codePtr += _heChannel[chan].codeOffs;
+
+ while(1) {
+ size = READ_LE_UINT16(codePtr);
+ time = READ_LE_UINT32(codePtr + 2);
+
+ if (size == 0) {
+ _heChannel[chan].codeOffs = -1;
+ break;
+ }
+
+ debug(5, "Channel %d Timer %d Time %d", chan, tmr, time);
+ if (time >= tmr)
+ break;
+
+ processSoundOpcodes(_heChannel[chan].sound, codePtr + 6, _heChannel[chan].soundVars);
+
+ codePtr += size;
+ _heChannel[chan].codeOffs += size;
+ }
+ }
+}
+
+void Sound::processSoundOpcodes(int sound, byte *codePtr, int *soundVars) {
+ int arg, opcode, var, val;
+
+ while(READ_LE_UINT16(codePtr) != 0) {
+ codePtr += 2;
+ opcode = READ_LE_UINT16(codePtr); codePtr += 2;
+ opcode = (opcode & 0xFFF) >> 4;
+ arg = opcode & 3;
+ opcode &= ~3;
+ debug(5, "processSoundOpcodes: sound %d opcode %d", sound, opcode);
+ switch (opcode) {
+ case 0: // Continue
+ break;
+ case 16: // Set talk state
+ val = READ_LE_UINT16(codePtr); codePtr += 2;
+ setSoundVar(sound, 19, val);
+ break;
+ case 32: // Set var
+ var = READ_LE_UINT16(codePtr); codePtr += 2;
+ val = READ_LE_UINT16(codePtr); codePtr += 2;
+ if (arg == 2) {
+ val = getSoundVar(sound, val);
+ }
+ setSoundVar(sound, var, val);
+ break;
+ case 48: // Add
+ var = READ_LE_UINT16(codePtr); codePtr += 2;
+ val = READ_LE_UINT16(codePtr); codePtr += 2;
+ if (arg == 2) {
+ val = getSoundVar(sound, val);
+ }
+ val = getSoundVar(sound, var) + val;
+ setSoundVar(sound, var, val);
+ break;
+ case 56: // Subtract
+ var = READ_LE_UINT16(codePtr); codePtr += 2;
+ val = READ_LE_UINT16(codePtr); codePtr += 2;
+ if (arg == 2) {
+ val = getSoundVar(sound, val);
+ }
+ val = getSoundVar(sound, var) - val;
+ setSoundVar(sound, var, val);
+ break;
+ case 64: // Multiple
+ var = READ_LE_UINT16(codePtr); codePtr += 2;
+ val = READ_LE_UINT16(codePtr); codePtr += 2;
+ if (arg == 2) {
+ val = getSoundVar(sound, val);
+ }
+ val = getSoundVar(sound, var) * val;
+ setSoundVar(sound, var, val);
+ break;
+ case 80: // Divide
+ var = READ_LE_UINT16(codePtr); codePtr += 2;
+ val = READ_LE_UINT16(codePtr); codePtr += 2;
+ if (arg == 2) {
+ val = getSoundVar(sound, val);
+ }
+ val = getSoundVar(sound, var) / val;
+ setSoundVar(sound, var, val);
+ break;
+ case 96: // Increment
+ var = READ_LE_UINT16(codePtr); codePtr += 2;
+ val = getSoundVar(sound, var) + 1;
+ setSoundVar(sound, var, val);
+ break;
+ case 104: // Decrement
+ var = READ_LE_UINT16(codePtr); codePtr += 2;
+ val = getSoundVar(sound, var) - 1;
+ setSoundVar(sound, var, val);
+ break;
+ default:
+ error("Illegal sound %d opcode %d", sound, opcode);
+ }
+ }
+}
+
+void Sound::playHESound(int soundID, int heOffset, int heChannel, int heFlags) {
+ byte *ptr, *spoolPtr;
+ char *sound;
+ int size = -1;
+ int priority, rate;
+ byte flags = Audio::Mixer::FLAG_UNSIGNED | Audio::Mixer::FLAG_AUTOFREE;
+
+ if (heChannel == -1)
+ heChannel = (_vm->VAR_RESERVED_SOUND_CHANNELS != 0xFF) ? findFreeSoundChannel() : 1;
+
+ debug(5,"playHESound: soundID %d heOffset %d heChannel %d heFlags %d", soundID, heOffset, heChannel, heFlags);
+
+ if (soundID >= 10000) {
+ // Special codes, used in pjgames
+ return;
+ }
+
+ if (soundID > _vm->_numSounds) {
+ int music_offs;
+ char buf[32], buf1[128];
+ Common::File musicFile;
+
+ sprintf(buf, "%s.he4", _vm->getBaseName());
+
+ if (_vm->_substResFileNameIndex > 0) {
+ _vm->generateSubstResFileName(buf, buf1, sizeof(buf1));
+ strcpy(buf, buf1);
+ }
+ if (musicFile.open(buf) == false) {
+ warning("playSound: Can't open music file %s", buf);
+ return;
+ }
+ if (!getHEMusicDetails(soundID, music_offs, size)) {
+ debug(0, "playSound: musicID %d not found", soundID);
+ return;
+ }
+
+ musicFile.seek(music_offs, SEEK_SET);
+
+ if (_vm->_heversion == 70) {
+ spoolPtr = (byte *)malloc(size);
+ musicFile.read(spoolPtr, size);
+ } else {
+ spoolPtr = _vm->res.createResource(rtSpoolBuffer, heChannel, size);
+ assert(spoolPtr);
+ musicFile.read(spoolPtr, size);
+ }
+ musicFile.close();
+
+ if (_vm->_heversion == 70) {
+ _vm->_mixer->stopHandle(_heSoundChannels[heChannel]);
+ _vm->_mixer->playRaw(&_heSoundChannels[heChannel], spoolPtr, size, 11025, flags, soundID);
+ return;
+ }
+ }
+
+ if (soundID > _vm->_numSounds) {
+ ptr = _vm->getResourceAddress(rtSpoolBuffer, heChannel);
+ } else {
+ ptr = _vm->getResourceAddress(rtSound, soundID);
+ }
+
+ if (!ptr) {
+ return;
+ }
+
+ // TODO: Extra sound flags
+ if (heFlags & 1) {
+ flags |= Audio::Mixer::FLAG_LOOP;
+ }
+
+ // Support for sound in later Backyard sports games
+ if (READ_UINT32(ptr) == MKID('RIFF') || READ_UINT32(ptr) == MKID('WSOU')) {
+ uint16 type;
+ int blockAlign;
+
+ if (READ_UINT32(ptr) == MKID('WSOU'))
+ ptr += 8;
+
+ size = READ_LE_UINT32(ptr + 4);
+ Common::MemoryReadStream stream(ptr, size);
+
+ if (!loadWAVFromStream(stream, size, rate, flags, &type, &blockAlign)) {
+ error("playSound: Not a valid WAV file");
+ }
+
+ if (type == 17) {
+ AudioStream *voxStream = new ADPCMInputStream(&stream, size, kADPCMIma, (flags & Audio::Mixer::FLAG_STEREO) ? 2 : 1, blockAlign);
+
+ sound = (char *)malloc(size * 4);
+ size = voxStream->readBuffer((int16*)sound, size * 2);
+ size *= 2; // 16bits.
+ delete voxStream;
+ } else {
+ // Allocate a sound buffer, copy the data into it, and play
+ sound = (char *)malloc(size);
+ memcpy(sound, ptr + stream.pos(), size);
+ }
+ _vm->_mixer->stopHandle(_heSoundChannels[heChannel]);
+ _vm->_mixer->playRaw(&_heSoundChannels[heChannel], sound, size, rate, flags, soundID);
+ }
+ // Support for sound in Humongous Entertainment games
+ else if (READ_UINT32(ptr) == MKID('DIGI') || READ_UINT32(ptr) == MKID('TALK')) {
+ byte *sndPtr = ptr;
+
+ priority = (soundID > _vm->_numSounds) ? 255 : *(ptr + 18);
+ rate = READ_LE_UINT16(ptr + 22);
+ ptr += 8 + READ_BE_UINT32(ptr + 12);
+
+ if (_vm->_mixer->isSoundHandleActive(_heSoundChannels[heChannel])) {
+ int curSnd = _heChannel[heChannel].sound;
+ if (curSnd == 1 && soundID != 1)
+ return;
+ if (curSnd != 0 && curSnd != 1 && soundID != 1 && _heChannel[heChannel].priority > priority)
+ return;
+ }
+
+ int codeOffs = -1;
+ if (READ_UINT32(ptr) == MKID('SBNG')) {
+ codeOffs = ptr - sndPtr + 8;
+ ptr += READ_BE_UINT32(ptr + 4);
+ }
+
+ assert(READ_UINT32(ptr) == MKID('SDAT'));
+ size = READ_BE_UINT32(ptr+4) - 8;
+ if (heOffset < 0 || heOffset > size) {
+ // Occurs when making fireworks in puttmoon
+ debug(0, "playSound: Invalid sound offset (offset %d, size %d) in sound %d", heOffset, size, soundID);
+ heOffset = 0;
+ }
+ size -= heOffset;
+
+ if (_overrideFreq) {
+ // Used by the piano in Fatty Bear's Birthday Surprise
+ rate = _overrideFreq;
+ _overrideFreq = 0;
+ }
+
+ // Allocate a sound buffer, copy the data into it, and play
+ sound = (char *)malloc(size);
+ memcpy(sound, ptr + heOffset + 8, size);
+ _vm->_mixer->stopHandle(_heSoundChannels[heChannel]);
+ _vm->_mixer->playRaw(&_heSoundChannels[heChannel], sound, size, rate, flags, soundID);
+
+ _vm->setHETimer(heChannel + 4);
+ _heChannel[heChannel].sound = soundID;
+ _heChannel[heChannel].priority = priority;
+ _heChannel[heChannel].sbngBlock = (codeOffs != -1) ? 1 : 0;
+ _heChannel[heChannel].codeOffs = codeOffs;
+ memset(_heChannel[heChannel].soundVars, 0, sizeof(_heChannel[heChannel].soundVars));
+ }
+ // Support for PCM music in 3DO versions of Humongous Entertainment games
+ else if (READ_UINT32(ptr) == MKID('MRAW')) {
+ priority = *(ptr + 18);
+ rate = READ_LE_UINT16(ptr + 22);
+ ptr += 8 + READ_BE_UINT32(ptr+12);
+
+ assert(READ_UINT32(ptr) == MKID('SDAT'));
+ size = READ_BE_UINT32(ptr+4) - 8;
+
+ flags = Audio::Mixer::FLAG_AUTOFREE;
+
+ // Allocate a sound buffer, copy the data into it, and play
+ sound = (char *)malloc(size);
+ memcpy(sound, ptr + 8, size);
+ _vm->_mixer->stopID(_currentMusic);
+ _currentMusic = soundID;
+ _vm->_mixer->playRaw(NULL, sound, size, rate, flags, soundID);
+ }
+ else if (READ_UINT32(ptr) == MKID('MIDI')) {
+ if (_vm->_musicEngine) {
+ _vm->_musicEngine->startSound(soundID);
+ }
+ }
+}
+
+void Sound::startHETalkSound(uint32 offset) {
+ byte *ptr;
+ int32 size;
+
+ if (ConfMan.getBool("speech_mute"))
+ return;
+
+ if (!_sfxFile->isOpen()) {
+ error("startHETalkSound: Speech file is not open");
+ return;
+ }
+
+ _sfxMode |= 2;
+ _vm->res.nukeResource(rtSound, 1);
+
+ _sfxFile->seek(offset + 4, SEEK_SET);
+ size = _sfxFile->readUint32BE();
+ _sfxFile->seek(offset, SEEK_SET);
+
+ _vm->res.createResource(rtSound, 1, size);
+ ptr = _vm->getResourceAddress(rtSound, 1);
+ _sfxFile->read(ptr, size);
+
+ int channel = (_vm->VAR_TALK_CHANNEL != 0xFF) ? _vm->VAR(_vm->VAR_TALK_CHANNEL) : 0;
+ addSoundToQueue2(1, 0, channel, 0);
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/he/sprite_he.cpp b/engines/scumm/he/sprite_he.cpp
new file mode 100644
index 0000000000..7de5f0742a
--- /dev/null
+++ b/engines/scumm/he/sprite_he.cpp
@@ -0,0 +1,1440 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "scumm/he/intern_he.h"
+#include "scumm/resource.h"
+#include "scumm/saveload.h"
+#include "scumm/scumm.h"
+#include "scumm/he/sprite_he.h"
+#include "scumm/usage_bits.h"
+#include "scumm/util.h"
+#include "scumm/he/wiz_he.h"
+
+namespace Scumm {
+
+Sprite::Sprite(ScummEngine_v90he *vm) : _vm(vm) {
+}
+
+Sprite::~Sprite() {
+ free(_spriteGroups);
+ free(_spriteTable);
+ free(_activeSpritesTable);
+}
+
+void ScummEngine_v90he::allocateArrays() {
+ ScummEngine::allocateArrays();
+ _sprite->allocTables(_numSprites, MAX(64, _numSprites / 4), 64);
+}
+
+void Sprite::getSpriteBounds(int spriteId, bool checkGroup, Common::Rect &bound) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+ int32 spr_wiz_x, spr_wiz_y;
+ int angle, scale, x1, y1;
+ int32 w, h;
+
+ SpriteInfo *spi = &_spriteTable[spriteId];
+
+ _vm->_wiz->getWizImageSpot(spi->image, spi->imageState, spr_wiz_x, spr_wiz_y);
+ if (checkGroup && spi->group) {
+ SpriteGroup *spg = &_spriteGroups[spi->group];
+
+ if (spg->scaling) {
+ x1 = spi->tx * spg->scale_x_ratio_mul / spg->scale_x_ratio_div - spr_wiz_x + spg->tx;
+ y1 = spi->ty * spg->scale_y_ratio_mul / spg->scale_y_ratio_div - spr_wiz_y + spg->ty;
+ } else {
+ x1 = spi->tx - spr_wiz_x + spg->tx;
+ y1 = spi->ty - spr_wiz_y + spg->ty;
+ }
+ } else {
+ x1 = spi->tx - spr_wiz_x;
+ y1 = spi->ty - spr_wiz_y;
+ }
+
+ if (spi->image) {
+ angle = spi->angle;
+ scale = spi->scale;
+ _vm->_wiz->getWizImageDim(spi->image, spi->imageState, w, h);
+ if (spi->flags & (kSFScaled | kSFRotated)) {
+ Common::Point pts[4];
+ _vm->_wiz->polygonTransform(spi->image, spi->imageState, x1, y1, angle, scale, pts);
+ _vm->_wiz->polygonCalcBoundBox(pts, 4, bound);
+ } else {
+ bound.left = x1;
+ bound.top = y1;
+ bound.right = x1 + w;
+ bound.bottom = y1 + h;
+ }
+ } else {
+ bound.left = 1234;
+ bound.top = 1234;
+ bound.right = -1234;
+ bound.bottom = -1234;
+ }
+}
+
+//
+// spriteInfoGet functions
+//
+int Sprite::findSpriteWithClassOf(int x_pos, int y_pos, int spriteGroupId, int type, int num, int *args) {
+ debug(2, "findSprite: x %d, y %d, spriteGroup %d, type %d, num %d", x_pos, y_pos, spriteGroupId, type, num);
+ Common::Point pos[1];
+ bool cond;
+ int code, classId;
+
+ for (int i = (_numSpritesToProcess - 1); i >= 0; i--) {
+ SpriteInfo *spi = _activeSpritesTable[i];
+ if (!spi->curImage)
+ continue;
+
+ if (spriteGroupId && spi->group != spriteGroupId)
+ continue;
+
+ cond = true;
+ for (int j = 0; j < num; j++) {
+ code = classId = args[j];
+ classId &= 0x7F;
+ checkRange(32, 1, classId, "class %d out of range in statement");
+ if (code & 0x80) {
+ if (!(spi->classFlags & (1 << (classId - 1))))
+ cond = 0;
+ } else {
+ if ((spi->classFlags & (1 << (classId - 1))))
+ cond = 0;
+ }
+ }
+ if (!cond)
+ continue;
+
+ if (type) {
+ if (spi->bbox.left > spi->bbox.right)
+ continue;
+ if (spi->bbox.top > spi->bbox.bottom)
+ continue;
+ if (spi->bbox.left > x_pos)
+ continue;
+ if (spi->bbox.top > y_pos)
+ continue;
+ if (spi->bbox.right < x_pos)
+ continue;
+ if (spi->bbox.bottom < y_pos)
+ continue;
+ return spi->id;
+ } else {
+ int image, imageState, angle, scale;
+ int32 w, h;
+
+ image = spi->curImage;
+ if (spi->maskImage) {
+ int32 x1, x2, y1, y2;
+
+ imageState = spi->curImageState % _vm->_wiz->getWizImageStates(spi->maskImage);
+
+ pos[0].x = x_pos - spi->pos.x;
+ pos[0].y = y_pos - spi->pos.y;
+
+ _vm->_wiz->getWizImageSpot(spi->curImage, imageState, x1, y1);
+ _vm->_wiz->getWizImageSpot(spi->maskImage, imageState, x2, y2);
+
+ pos[0].x += (x2 - x1);
+ pos[0].y += (y2 - y1);
+ } else {
+ if (spi->bbox.left > spi->bbox.right)
+ continue;
+ if (spi->bbox.top > spi->bbox.bottom)
+ continue;
+ if (spi->bbox.left > x_pos)
+ continue;
+ if (spi->bbox.top > y_pos)
+ continue;
+ if (spi->bbox.right < x_pos)
+ continue;
+ if (spi->bbox.bottom < y_pos)
+ continue;
+
+ pos[0].x = x_pos - spi->pos.x;
+ pos[0].y = y_pos - spi->pos.y;
+ imageState = spi->curImageState;
+ }
+
+ angle = spi->curAngle;
+ scale = spi->curScale;
+ if ((spi->flags & kSFScaled) || (spi->flags & kSFRotated)) {
+ if (spi->flags & kSFScaled && scale) {
+ pos[0].x = pos[0].x * 256 / scale;
+ pos[0].y = pos[0].y * 256 / scale;
+ }
+ if (spi->flags & kSFRotated && angle) {
+ angle = (360 - angle) % 360;
+ _vm->_wiz->polygonRotatePoints(pos, 1, angle);
+ }
+
+ _vm->_wiz->getWizImageDim(image, imageState, w, h);
+ pos[0].x += w / 2;
+ pos[0].y += h / 2;
+ }
+
+ if (_vm->_wiz->isWizPixelNonTransparent(image, imageState, pos[0].x, pos[0].y, spi->curImgFlags))
+ return spi->id;
+ }
+ }
+
+ return 0;
+}
+
+int Sprite::getSpriteClass(int spriteId, int num, int *args) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+ int code, classId;
+
+ if (num == 0)
+ return _spriteTable[spriteId].classFlags;
+
+ for (int i = 0; i < num; i++) {
+ code = classId = args[i];
+ classId &= 0x7F;
+ checkRange(32, 1, classId, "class %d out of range in statement");
+ if (code & 0x80) {
+ if (!(_spriteTable[spriteId].classFlags & (1 << (classId - 1))))
+ return 0;
+ } else {
+ if ((_spriteTable[spriteId].classFlags & (1 << (classId - 1))))
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+int Sprite::getSpriteFlagDoubleBuffered(int spriteId) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ return ((_spriteTable[spriteId].flags & kSFDoubleBuffered) != 0) ? 1 : 0;
+}
+
+int Sprite::getSpriteFlagYFlipped(int spriteId) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ return ((_spriteTable[spriteId].flags & kSFYFlipped) != 0) ? 1 : 0;
+}
+
+int Sprite::getSpriteFlagXFlipped(int spriteId) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ return ((_spriteTable[spriteId].flags & kSFXFlipped) != 0) ? 1 : 0;
+}
+
+int Sprite::getSpriteFlagActive(int spriteId) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ return ((_spriteTable[spriteId].flags & kSFActive) != 0) ? 1 : 0;
+}
+
+int Sprite::getSpriteFlagRemapPalette(int spriteId) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ return ((_spriteTable[spriteId].flags & kSFRemapPalette) != 0) ? 1 : 0;
+}
+
+int Sprite::getSpriteFlagAutoAnim(int spriteId) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ return ((_spriteTable[spriteId].flags & kSFAutoAnim) != 0) ? 1 : 0;
+}
+
+int Sprite::getSpriteFlagUpdateType(int spriteId) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ return ((_spriteTable[spriteId].flags & kSFMarkDirty) != 0) ? 1 : 0;
+}
+
+int Sprite::getSpriteFlagEraseType(int spriteId) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ return ((_spriteTable[spriteId].flags & kSFImageless) != 0) ? 1 : 0;
+}
+
+int Sprite::getSpriteImage(int spriteId) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ return _spriteTable[spriteId].image;
+}
+
+int Sprite::getSpriteImageState(int spriteId) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ return _spriteTable[spriteId].imageState;
+}
+
+int Sprite::getSpriteGroup(int spriteId) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ return _spriteTable[spriteId].group;
+}
+
+int Sprite::getSpritePalette(int spriteId) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ return _spriteTable[spriteId].palette;
+}
+
+int Sprite::getSpritePriority(int spriteId) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ return _spriteTable[spriteId].priority;
+}
+
+int Sprite::getSpriteDisplayX(int spriteId) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ if (_spriteTable[spriteId].group)
+ return _spriteTable[spriteId].tx + _spriteGroups[_spriteTable[spriteId].group].tx;
+ else
+ return _spriteTable[spriteId].tx;
+}
+
+int Sprite::getSpriteDisplayY(int spriteId) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ if (_spriteTable[spriteId].group)
+ return _spriteTable[spriteId].ty + _spriteGroups[_spriteTable[spriteId].group].ty;
+ else
+ return _spriteTable[spriteId].ty;
+}
+
+int Sprite::getSpriteUserValue(int spriteId) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ return _spriteTable[spriteId].userValue;
+}
+
+int Sprite::getSpriteShadow(int spriteId) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ return _spriteTable[spriteId].shadow;
+}
+
+int Sprite::getSpriteImageStateCount(int spriteId) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ return _spriteTable[spriteId].imageStateCount;
+}
+
+int Sprite::getSpriteScale(int spriteId) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ return _spriteTable[spriteId].scale;
+}
+
+int Sprite::getSpriteAnimSpeed(int spriteId) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ return _spriteTable[spriteId].animSpeed;
+}
+
+int Sprite::getSpriteSourceImage(int spriteId) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ return _spriteTable[spriteId].sourceImage;
+}
+
+int Sprite::getSpriteMaskImage(int spriteId) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ return _spriteTable[spriteId].maskImage;
+}
+
+int Sprite::getSpriteGeneralProperty(int spriteId, int type) {
+ debug(0, "getSpriteGeneralProperty: spriteId %d type 0x%x", spriteId, type);
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ // XXX U32 related check
+
+ switch(type) {
+ case 0x7B:
+ return _spriteTable[spriteId].imgFlags;
+ case 0x7D:
+ return _spriteTable[spriteId].field_90;
+ case 0x7E:
+ return _spriteTable[spriteId].animProgress;
+ default:
+ error("getSpriteGeneralProperty: Invalid type %d", type);
+ }
+}
+
+void Sprite::getSpriteImageDim(int spriteId, int32 &w, int32 &h) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ if (_spriteTable[spriteId].image) {
+ _vm->_wiz->getWizImageDim(_spriteTable[spriteId].image, _spriteTable[spriteId].imageState, w, h);
+ } else {
+ w = 0;
+ h = 0;
+ }
+}
+
+void Sprite::getSpritePosition(int spriteId, int32 &tx, int32 &ty) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ tx = _spriteTable[spriteId].tx;
+ ty = _spriteTable[spriteId].ty;
+}
+
+void Sprite::getSpriteDist(int spriteId, int32 &dx, int32 &dy) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ dx = _spriteTable[spriteId].dx;
+ dy = _spriteTable[spriteId].dy;
+}
+
+//
+// spriteGroupGet functions
+//
+int ScummEngine_v90he::getGroupSpriteArray(int spriteGroupId) {
+ int i, numSprites = 0;
+
+ checkRange(_sprite->_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+
+ for (i = (_sprite->_varNumSprites - 1); i > 0; i--) {
+ if (_sprite->_spriteTable[i].group == spriteGroupId)
+ numSprites++;
+ }
+
+ if (!numSprites)
+ return 0;
+
+ writeVar(0, 0);
+ defineArray(0, kDwordArray, 0, 0, 0, numSprites);
+ writeArray(0, 0, 0, numSprites);
+
+ numSprites = 1;
+ for (i = (_sprite->_varNumSprites - 1); i > 0; i--) {
+ if (_sprite->_spriteTable[i].group == spriteGroupId) {
+ writeArray(0, 0, numSprites, i);
+ numSprites++;
+ }
+ }
+
+ return readVar(0);
+}
+
+int Sprite::getGroupPriority(int spriteGroupId) {
+ checkRange(_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+
+ return _spriteGroups[spriteGroupId].priority;
+}
+
+int Sprite::getGroupDstResNum(int spriteGroupId) {
+ checkRange(_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+
+ return _spriteGroups[spriteGroupId].image;
+}
+
+int Sprite::getGroupXMul(int spriteGroupId) {
+ checkRange(_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+
+ return _spriteGroups[spriteGroupId].scale_x_ratio_mul;
+}
+
+int Sprite::getGroupXDiv(int spriteGroupId) {
+ checkRange(_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+
+ return _spriteGroups[spriteGroupId].scale_x_ratio_div;
+}
+
+int Sprite::getGroupYMul(int spriteGroupId) {
+ checkRange(_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+
+ return _spriteGroups[spriteGroupId].scale_y_ratio_mul;
+}
+
+int Sprite::getGroupYDiv(int spriteGroupId) {
+ checkRange(_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+
+ return _spriteGroups[spriteGroupId].scale_y_ratio_div;
+}
+
+void Sprite::getGroupPosition(int spriteGroupId, int32 &tx, int32 &ty) {
+ checkRange(_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+
+ tx = _spriteGroups[spriteGroupId].tx;
+ ty = _spriteGroups[spriteGroupId].ty;
+}
+
+//
+// spriteInfoSet functions
+//
+void Sprite::setSpritePalette(int spriteId, int value) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ if (_spriteTable[spriteId].palette != value) {
+ _spriteTable[spriteId].palette = value;
+ _spriteTable[spriteId].flags |= kSFChanged | kSFNeedRedraw;
+ }
+}
+
+void Sprite::setSpriteSourceImage(int spriteId, int value) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ if (_spriteTable[spriteId].sourceImage != value) {
+ _spriteTable[spriteId].sourceImage = value;
+ _spriteTable[spriteId].flags |= kSFChanged | kSFNeedRedraw;
+ }
+}
+
+void Sprite::setSpriteMaskImage(int spriteId, int value) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ _spriteTable[spriteId].maskImage = value;
+}
+
+void Sprite::setSpriteImageState(int spriteId, int state) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ if (_spriteTable[spriteId].image) {
+ int imageStateCount = _spriteTable[spriteId].imageStateCount - 1;
+ state = MAX(0, state);
+ state = MIN(state, imageStateCount);
+
+ if (_spriteTable[spriteId].imageState != state) {
+ _spriteTable[spriteId].imageState = state;
+ _spriteTable[spriteId].flags |= kSFChanged | kSFNeedRedraw;
+ }
+ }
+}
+
+void Sprite::setSpritePosition(int spriteId, int tx, int ty) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ if (_spriteTable[spriteId].tx != tx || _spriteTable[spriteId].ty != ty) {
+ _spriteTable[spriteId].tx = tx;
+ _spriteTable[spriteId].ty = ty;
+ _spriteTable[spriteId].flags |= kSFChanged | kSFNeedRedraw;
+ }
+}
+
+void Sprite::setSpriteGroup(int spriteId, int value) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+ checkRange(_varNumSpriteGroups, 0, value, "Invalid sprite group %d");
+
+ _spriteTable[spriteId].group = value;
+ _spriteTable[spriteId].flags |= kSFChanged | kSFNeedRedraw;
+}
+
+void Sprite::setSpriteDist(int spriteId, int value1, int value2) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ _spriteTable[spriteId].dx = value1;
+ _spriteTable[spriteId].dy = value2;
+}
+
+void Sprite::setSpriteShadow(int spriteId, int value) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ _spriteTable[spriteId].shadow = value;
+ if (_spriteTable[spriteId].image)
+ _spriteTable[spriteId].flags |= kSFChanged | kSFNeedRedraw;
+}
+
+void Sprite::setSpriteUserValue(int spriteId, int value1, int value2) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ _spriteTable[spriteId].userValue = value2;
+}
+
+void Sprite::setSpritePriority(int spriteId, int value) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ _spriteTable[spriteId].priority = value;
+}
+
+void Sprite::moveSprite(int spriteId, int value1, int value2) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ _spriteTable[spriteId].tx += value1;
+ _spriteTable[spriteId].ty += value2;
+
+ if (value1 || value2)
+ _spriteTable[spriteId].flags |= kSFChanged | kSFNeedRedraw;
+}
+
+void Sprite::setSpriteScale(int spriteId, int value) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ _spriteTable[spriteId].flags |= kSFScaled;
+
+ if (_spriteTable[spriteId].scale != value) {
+ _spriteTable[spriteId].scale = value;
+
+ if (_spriteTable[spriteId].image)
+ _spriteTable[spriteId].flags |= kSFChanged | kSFNeedRedraw;
+ }
+}
+
+void Sprite::setSpriteAngle(int spriteId, int value) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ _spriteTable[spriteId].flags |= kSFRotated;
+
+ if (_spriteTable[spriteId].angle != value) {
+ _spriteTable[spriteId].angle = value;
+
+ if (_spriteTable[spriteId].image)
+ _spriteTable[spriteId].flags |= kSFChanged | kSFNeedRedraw;
+ }
+}
+
+void Sprite::setSpriteFlagDoubleBuffered(int spriteId, int value) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ int oldFlags = _spriteTable[spriteId].flags;
+ if (value)
+ _spriteTable[spriteId].flags |= kSFDoubleBuffered;
+ else
+ _spriteTable[spriteId].flags &= ~kSFDoubleBuffered;
+
+ if (_spriteTable[spriteId].image && _spriteTable[spriteId].flags != oldFlags)
+ _spriteTable[spriteId].flags |= kSFChanged | kSFNeedRedraw;
+}
+
+void Sprite::setSpriteFlagYFlipped(int spriteId, int value) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ int oldFlags = _spriteTable[spriteId].flags;
+ if (value)
+ _spriteTable[spriteId].flags |= kSFYFlipped;
+ else
+ _spriteTable[spriteId].flags &= ~kSFYFlipped;
+
+ if (_spriteTable[spriteId].image && _spriteTable[spriteId].flags != oldFlags)
+ _spriteTable[spriteId].flags |= kSFChanged | kSFNeedRedraw;
+}
+
+void Sprite::setSpriteFlagXFlipped(int spriteId, int value) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ int oldFlags = _spriteTable[spriteId].flags;
+ if (value)
+ _spriteTable[spriteId].flags |= kSFXFlipped;
+ else
+ _spriteTable[spriteId].flags &= ~kSFXFlipped;
+
+ if (_spriteTable[spriteId].image && _spriteTable[spriteId].flags != oldFlags)
+ _spriteTable[spriteId].flags |= kSFChanged | kSFNeedRedraw;
+}
+
+void Sprite::setSpriteFlagActive(int spriteId, int value) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ if (value)
+ _spriteTable[spriteId].flags |= kSFActive;
+ else
+ _spriteTable[spriteId].flags &= ~kSFActive;
+}
+
+void Sprite::setSpriteFlagRemapPalette(int spriteId, int value) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ int oldFlags = _spriteTable[spriteId].flags;
+ if (value)
+ _spriteTable[spriteId].flags |= kSFRemapPalette;
+ else
+ _spriteTable[spriteId].flags &= ~kSFRemapPalette;
+
+ if (_spriteTable[spriteId].image && _spriteTable[spriteId].flags != oldFlags)
+ _spriteTable[spriteId].flags |= kSFChanged | kSFNeedRedraw;
+}
+
+void Sprite::setSpriteFlagAutoAnim(int spriteId, int value) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ if (value)
+ _spriteTable[spriteId].flags |= kSFAutoAnim;
+ else
+ _spriteTable[spriteId].flags &= ~kSFAutoAnim;
+}
+
+void Sprite::setSpriteFlagUpdateType(int spriteId, int value) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ switch(value) {
+ case 2:
+ _spriteTable[spriteId].flags &= ~(kSFMarkDirty);
+ _spriteTable[spriteId].flags |= kSFBlitDirectly;
+ break;
+ case 1:
+ _spriteTable[spriteId].flags |= kSFMarkDirty | kSFBlitDirectly;
+ break;
+ case 0:
+ _spriteTable[spriteId].flags &= ~(kSFMarkDirty | kSFBlitDirectly);
+ break;
+ default:
+ error("setSpriteFlagUpdateType: Invalid value %d", value);
+ }
+}
+
+void Sprite::setSpriteFlagEraseType(int spriteId, int value) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ // Note that condition is inverted
+ if (!value)
+ _spriteTable[spriteId].flags |= kSFImageless;
+ else
+ _spriteTable[spriteId].flags &= ~kSFImageless;
+}
+
+void Sprite::setSpriteAnimSpeed(int spriteId, int value) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ _spriteTable[spriteId].animSpeed = value;
+ _spriteTable[spriteId].animProgress = value;
+}
+
+void Sprite::setSpriteSetClass(int spriteId, int classId, int toggle) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+ checkRange(32, 1, classId, "class %d out of range in statement");
+
+ if (toggle) {
+ _spriteTable[spriteId].classFlags |= (1 << (classId - 1));
+ } else {
+ _spriteTable[spriteId].classFlags &= ~(1 << (classId - 1));
+ }
+}
+
+void Sprite::setSpriteResetClass(int spriteId) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ _spriteTable[spriteId].classFlags = 0;
+}
+
+void Sprite::setSpriteField84(int spriteId, int value) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ _spriteTable[spriteId].field_84 = value;
+}
+
+void Sprite::setSpriteGeneralProperty(int spriteId, int type, int value) {
+ debug(0, "setSpriteGeneralProperty: spriteId %d type 0x%x", spriteId, type);
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+ int32 delay;
+
+ // XXX U32 related check
+
+ switch(type) {
+ case 0x7B:
+ _spriteTable[spriteId].imgFlags = value;
+ _spriteTable[spriteId].flags |= kSFChanged | kSFNeedRedraw;
+ break;
+ case 0x7D:
+ _spriteTable[spriteId].field_90 = value;
+ _spriteTable[spriteId].flags |= kSFChanged | kSFNeedRedraw;
+ break;
+ case 0x7E:
+ delay = MAX(0, value);
+ delay = MIN(delay, _spriteTable[spriteId].animSpeed);
+
+ _spriteTable[spriteId].animProgress = delay;
+ break;
+ default:
+ error("setSpriteGeneralProperty: Invalid value %d", type);
+ }
+}
+
+void Sprite::resetSprite(int spriteId) {
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ _spriteTable[spriteId].angle = 0;
+ _spriteTable[spriteId].scale = 0;
+
+ setSpriteImage(spriteId, 0);
+
+ _spriteTable[spriteId].shadow = 0;
+ _spriteTable[spriteId].tx = 0;
+ _spriteTable[spriteId].ty = 0;
+
+ _spriteTable[spriteId].flags &= ~(kSFYFlipped | kSFXFlipped);
+ _spriteTable[spriteId].flags |= kSFChanged | kSFNeedRedraw;
+ _spriteTable[spriteId].dx = 0;
+ _spriteTable[spriteId].dy = 0;
+ _spriteTable[spriteId].userValue = 0;
+ _spriteTable[spriteId].group = 0;
+ _spriteTable[spriteId].animSpeed = 0;
+ _spriteTable[spriteId].animProgress = 0;
+ _spriteTable[spriteId].classFlags = 0;
+ _spriteTable[spriteId].palette = 0;
+ _spriteTable[spriteId].sourceImage = 0;
+ _spriteTable[spriteId].maskImage = 0;
+ _spriteTable[spriteId].priority = 0;
+ _spriteTable[spriteId].field_84 = 0;
+ _spriteTable[spriteId].imgFlags = 0;
+ _spriteTable[spriteId].field_90 = 0;
+}
+
+void Sprite::setSpriteImage(int spriteId, int imageNum) {
+ int origResId, origResWizStates;
+
+ checkRange(_varNumSprites, 1, spriteId, "Invalid sprite %d");
+
+ origResId = _spriteTable[spriteId].image;
+ origResWizStates = _spriteTable[spriteId].imageStateCount;
+
+ _spriteTable[spriteId].image = imageNum;
+ _spriteTable[spriteId].field_74 = 0;
+ _spriteTable[spriteId].imageState = 0;
+
+ if (_spriteTable[spriteId].image) {
+ _spriteTable[spriteId].imageStateCount = _vm->_wiz->getWizImageStates(_spriteTable[spriteId].image);
+ _spriteTable[spriteId].flags |= kSFActive | kSFAutoAnim | kSFMarkDirty | kSFBlitDirectly;
+
+ if (_spriteTable[spriteId].image != origResId || _spriteTable[spriteId].imageStateCount != origResWizStates)
+ _spriteTable[spriteId].flags |= kSFChanged | kSFNeedRedraw;
+ } else {
+ if (_spriteTable[spriteId].flags & kSFImageless)
+ _spriteTable[spriteId].flags = 0;
+ else
+ _spriteTable[spriteId].flags = kSFChanged | kSFBlitDirectly;
+ _spriteTable[spriteId].curImage = 0;
+ _spriteTable[spriteId].curImageState = 0;
+ _spriteTable[spriteId].imageStateCount = 0;
+ }
+}
+
+//
+// spriteGroupSet functions
+//
+void Sprite::redrawSpriteGroup(int spriteGroupId) {
+ for (int i = 0; i < _numSpritesToProcess; ++i) {
+ SpriteInfo *spi = _activeSpritesTable[i];
+ if (spi->group == spriteGroupId) {
+ spi->flags |= kSFChanged | kSFNeedRedraw;
+ }
+ }
+}
+
+void Sprite::moveGroupMembers(int spriteGroupId, int value1, int value2) {
+ checkRange(_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+
+ for (int i = 1; i < _varNumSprites; i++) {
+ if (_spriteTable[i].group == spriteGroupId) {
+ _spriteTable[i].tx += value1;
+ _spriteTable[i].ty += value2;
+
+ if (value1 || value2)
+ _spriteTable[i].flags |= kSFChanged | kSFNeedRedraw;
+ }
+ }
+}
+
+void Sprite::setGroupMembersPriority(int spriteGroupId, int value) {
+ checkRange(_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+
+ for (int i = 1; i < _varNumSprites; i++) {
+ if (_spriteTable[i].group == spriteGroupId)
+ _spriteTable[i].priority = value;
+ }
+}
+
+void Sprite::setGroupMembersGroup(int spriteGroupId, int value) {
+ checkRange(_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+
+ for (int i = 1; i < _varNumSprites; i++) {
+ if (_spriteTable[i].group == spriteGroupId) {
+ _spriteTable[i].group = value;
+ _spriteTable[i].flags |= kSFChanged | kSFNeedRedraw;
+ }
+ }
+}
+
+void Sprite::setGroupMembersUpdateType(int spriteGroupId, int value) {
+ checkRange(_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+
+ for (int i = 1; i < _varNumSprites; i++) {
+ if (_spriteTable[i].group == spriteGroupId)
+ setSpriteFlagUpdateType(i, value);
+ }
+}
+
+void Sprite::setGroupMembersResetSprite(int spriteGroupId) {
+ checkRange(_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+
+ for (int i = 1; i < _varNumSprites; i++) {
+ if (_spriteTable[i].group == spriteGroupId)
+ resetSprite(i);
+ }
+}
+
+void Sprite::setGroupMembersAnimationSpeed(int spriteGroupId, int value) {
+ checkRange(_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+
+ for (int i = 1; i < _varNumSprites; i++) {
+ if (_spriteTable[i].group == spriteGroupId) {
+ _spriteTable[i].animSpeed = value;
+ _spriteTable[i].animProgress = value;
+ }
+ }
+}
+
+void Sprite::setGroupMembersAutoAnimFlag(int spriteGroupId, int value) {
+ checkRange(_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+
+ for (int i = 1; i < _varNumSprites; i++) {
+ if (_spriteTable[i].group == spriteGroupId) {
+ if (value)
+ _spriteTable[i].flags |= kSFAutoAnim;
+ else
+ _spriteTable[i].flags &= ~kSFAutoAnim;
+ }
+ }
+}
+
+void Sprite::setGroupMembersShadow(int spriteGroupId, int value) {
+ checkRange(_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+
+ for (int i = 1; i < _varNumSprites; i++) {
+ if (_spriteTable[i].group == spriteGroupId) {
+ _spriteTable[i].shadow = value;
+ if (_spriteTable[i].image)
+ _spriteTable[i].flags |= kSFChanged | kSFNeedRedraw;
+ }
+ }
+}
+
+void Sprite::setGroupBounds(int spriteGroupId, int x1, int y1, int x2, int y2) {
+ checkRange(_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+
+ _spriteGroups[spriteGroupId].flags |= kSGFClipBox;
+ _spriteGroups[spriteGroupId].bbox.left = x1;
+ _spriteGroups[spriteGroupId].bbox.top = y1;
+ _spriteGroups[spriteGroupId].bbox.right = x2;
+ _spriteGroups[spriteGroupId].bbox.bottom = y2;
+
+ redrawSpriteGroup(spriteGroupId);
+}
+
+void Sprite::setGroupPriority(int spriteGroupId, int value) {
+ checkRange(_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+
+ if (_spriteGroups[spriteGroupId].priority != value) {
+ _spriteGroups[spriteGroupId].priority = value;
+ redrawSpriteGroup(spriteGroupId);
+ }
+}
+
+void Sprite::setGroupPosition(int spriteGroupId, int value1, int value2) {
+ checkRange(_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+
+ if (_spriteGroups[spriteGroupId].tx != value1 || _spriteGroups[spriteGroupId].ty != value2) {
+ _spriteGroups[spriteGroupId].tx = value1;
+ _spriteGroups[spriteGroupId].ty = value2;
+ redrawSpriteGroup(spriteGroupId);
+ }
+}
+
+void Sprite::moveGroup(int spriteGroupId, int value1, int value2) {
+ checkRange(_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+
+ if (value1 || value2) {
+ _spriteGroups[spriteGroupId].tx += value1;
+ _spriteGroups[spriteGroupId].ty += value2;
+ redrawSpriteGroup(spriteGroupId);
+ }
+}
+
+void Sprite::setGroupImage(int spriteGroupId, int value) {
+ checkRange(_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+
+ if (_spriteGroups[spriteGroupId].image != value) {
+ _spriteGroups[spriteGroupId].image = value;
+ redrawSpriteGroup(spriteGroupId);
+ }
+}
+
+void Sprite::setGroupScaling(int spriteGroupId) {
+ if ((_spriteGroups[spriteGroupId].scale_x_ratio_mul != _spriteGroups[spriteGroupId].scale_x_ratio_div) || (_spriteGroups[spriteGroupId].scale_y_ratio_mul != _spriteGroups[spriteGroupId].scale_y_ratio_div))
+ _spriteGroups[spriteGroupId].scaling = 1;
+ else
+ _spriteGroups[spriteGroupId].scaling = 0;
+
+}
+
+void Sprite::setGroupXMul(int spriteGroupId, int value) {
+ checkRange(_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+
+ if (_spriteGroups[spriteGroupId].scale_x_ratio_mul != value) {
+ _spriteGroups[spriteGroupId].scale_x_ratio_mul = value;
+ setGroupScaling(spriteGroupId);
+ redrawSpriteGroup(spriteGroupId);
+ }
+}
+
+void Sprite::setGroupXDiv(int spriteGroupId, int value) {
+ checkRange(_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+
+ if (value == 0)
+ error("setGroupXDiv: Divisor must not be 0");
+
+ if (_spriteGroups[spriteGroupId].scale_x_ratio_div != value) {
+ _spriteGroups[spriteGroupId].scale_x_ratio_div = value;
+ setGroupScaling(spriteGroupId);
+ redrawSpriteGroup(spriteGroupId);
+ }
+}
+
+void Sprite::setGroupYMul(int spriteGroupId, int value) {
+ checkRange(_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+
+ if (_spriteGroups[spriteGroupId].scale_y_ratio_mul != value) {
+ _spriteGroups[spriteGroupId].scale_y_ratio_mul = value;
+ setGroupScaling(spriteGroupId);
+ redrawSpriteGroup(spriteGroupId);
+ }
+}
+
+void Sprite::setGroupYDiv(int spriteGroupId, int value) {
+ checkRange(_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+
+ if (value == 0)
+ error("setGroupYDiv: Divisor must not be 0");
+
+ if (_spriteGroups[spriteGroupId].scale_y_ratio_div != value) {
+ _spriteGroups[spriteGroupId].scale_y_ratio_div = value;
+ setGroupScaling(spriteGroupId);
+ redrawSpriteGroup(spriteGroupId);
+ }
+}
+
+void Sprite::resetGroupBounds(int spriteGroupId) {
+ checkRange(_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+
+ _spriteGroups[spriteGroupId].flags &= ~(kSGFClipBox);
+ redrawSpriteGroup(spriteGroupId);
+}
+
+void Sprite::allocTables(int numSprites, int numGroups, int numMaxSprites) {
+ _varNumSpriteGroups = numGroups;
+ _numSpritesToProcess = 0;
+ _varNumSprites = numSprites;
+ _varMaxSprites = numMaxSprites;
+ _spriteGroups = (SpriteGroup *)malloc((_varNumSpriteGroups + 1) * sizeof(SpriteGroup));
+ _spriteTable = (SpriteInfo *)malloc((_varNumSprites + 1) * sizeof(SpriteInfo));
+ _activeSpritesTable = (SpriteInfo **)malloc((_varNumSprites + 1) * sizeof(SpriteInfo *));
+}
+
+void Sprite::resetGroup(int spriteGroupId) {
+ checkRange(_varNumSpriteGroups, 1, spriteGroupId, "Invalid sprite group %d");
+ SpriteGroup *spg = &_spriteGroups[spriteGroupId];
+
+ spg->priority = 0;
+ spg->tx = spg->ty = 0;
+
+ spg->flags &= ~kSGFClipBox;
+ redrawSpriteGroup(spriteGroupId);
+
+ spg->image = 0;
+ spg->scaling = 0;
+ spg->scale_x_ratio_mul = 1;
+ spg->scale_x_ratio_div = 1;
+ spg->scale_y_ratio_mul = 1;
+ spg->scale_y_ratio_div = 1;
+}
+
+void Sprite::resetTables(bool refreshScreen) {
+ memset(_spriteTable, 0, (_varNumSprites + 1) * sizeof(SpriteInfo));
+ memset(_spriteGroups, 0, (_varNumSpriteGroups + 1) * sizeof(SpriteGroup));
+ for (int curGrp = 1; curGrp < _varNumSpriteGroups; ++curGrp)
+ resetGroup(curGrp);
+
+ if (refreshScreen) {
+ _vm->gdi.copyVirtScreenBuffers(Common::Rect(_vm->_screenWidth, _vm->_screenHeight));
+ }
+ _numSpritesToProcess = 0;
+}
+
+void Sprite::resetBackground() {
+ int xmin, xmax, ymin, ymax;
+ xmin = ymin = 1234;
+ xmax = ymax = -1234;
+ bool firstLoop = true;
+ bool refreshScreen = false;
+
+ for (int i = 0; i < _numSpritesToProcess; ++i) {
+ SpriteInfo *spi = _activeSpritesTable[i];
+ if (!(spi->flags & kSFImageless) && (spi->flags & kSFChanged)) {
+ spi->flags &= ~kSFChanged;
+ if (spi->bbox.left <= spi->bbox.right && spi->bbox.top <= spi->bbox.bottom) {
+ if (spi->flags & kSFBlitDirectly) {
+ _vm->gdi.copyVirtScreenBuffers(spi->bbox, USAGE_BIT_RESTORED);
+ } else if (firstLoop) {
+ xmin = spi->bbox.left;
+ ymin = spi->bbox.top;
+ xmax = spi->bbox.right;
+ ymax = spi->bbox.bottom;
+ firstLoop = false;
+ refreshScreen = true;
+ } else {
+ if (xmin > spi->bbox.left) {
+ xmin = spi->bbox.left;
+ }
+ if (ymin > spi->bbox.top) {
+ ymin = spi->bbox.top;
+ }
+ if (xmax < spi->bbox.right) {
+ xmax = spi->bbox.right;
+ }
+ if (ymax < spi->bbox.bottom) {
+ ymax = spi->bbox.bottom;
+ }
+ refreshScreen = true;
+ }
+ if (!(spi->flags & kSFNeedRedraw) && spi->image)
+ spi->flags |= kSFNeedRedraw;
+ }
+ }
+ }
+ if (refreshScreen) {
+ _vm->gdi.copyVirtScreenBuffers(Common::Rect(xmin, ymin, xmax, ymax), USAGE_BIT_RESTORED);
+ }
+}
+
+void Sprite::setRedrawFlags(bool checkZOrder) {
+ VirtScreen *vs = &_vm->virtscr[kMainVirtScreen];
+ for (int i = 0; i < _numSpritesToProcess; ++i) {
+ SpriteInfo *spi = _activeSpritesTable[i];
+ if (!(spi->flags & kSFNeedRedraw)) {
+ if ((!checkZOrder || spi->priority >= 0) && (spi->flags & kSFMarkDirty)) {
+ int lp = spi->bbox.left / 8;
+ lp = MAX(0, lp);
+ lp = MIN(lp, 79);
+ int rp = (spi->bbox.right + 7) / 8;
+ rp = MAX(0, rp);
+ rp = MIN(rp, 79);
+ for (; lp <= rp; ++lp) {
+ if (vs->tdirty[lp] < vs->h && spi->bbox.bottom >= vs->tdirty[lp] && spi->bbox.top <= vs->bdirty[lp]) {
+ spi->flags |= kSFNeedRedraw;
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+
+void Sprite::updateImages() {
+ for (int i = 0; i < _numSpritesToProcess; ++i) {
+ SpriteInfo *spi = _activeSpritesTable[i];
+ if (spi->dx || spi->dy) {
+ int tx = spi->tx;
+ int ty = spi->ty;
+ spi->tx += spi->dx;
+ spi->ty += spi->dy;
+ if (tx != spi->tx || ty != spi->ty) {
+ spi->flags |= kSFChanged | kSFNeedRedraw;
+ }
+ }
+ if (spi->flags & kSFAutoAnim) {
+ if (spi->animSpeed) {
+ --spi->animProgress;
+ if (spi->animProgress)
+ continue;
+
+ spi->animProgress = spi->animSpeed;
+ }
+ int imageState = spi->imageState;
+ ++spi->imageState;
+ if (spi->imageState >= spi->imageStateCount) {
+ spi->imageState = 0;
+ if (imageState == 0)
+ continue;
+ }
+ spi->flags |= kSFChanged | kSFNeedRedraw;
+ }
+ }
+}
+
+static int compareSprTable(const void *a, const void *b) {
+ const SpriteInfo *spr1 = *(const SpriteInfo *const*)a;
+ const SpriteInfo *spr2 = *(const SpriteInfo *const*)b;
+
+ if (spr1->zorder > spr2->zorder)
+ return 1;
+
+ if (spr1->zorder < spr2->zorder)
+ return -1;
+
+ return 0;
+}
+
+void Sprite::sortActiveSprites() {
+ int groupZorder;
+
+ _numSpritesToProcess = 0;
+
+ if (_varNumSprites <= 1)
+ return;
+
+ for (int i = 1; i < _varNumSprites; i++) {
+ SpriteInfo *spi = &_spriteTable[i];
+
+ if (spi->flags & kSFActive) {
+ if (!(spi->flags & kSFMarkDirty)) {
+ spi->flags |= kSFNeedRedraw;
+ if (!(spi->flags & kSFImageless))
+ spi->flags |= kSFChanged;
+ }
+ if (spi->group)
+ groupZorder = _spriteGroups[spi->group].priority;
+ else
+ groupZorder = 0;
+
+ spi->id = i;
+ spi->zorder = spi->priority + groupZorder;
+
+ _activeSpritesTable[_numSpritesToProcess++] = spi;
+ }
+ }
+
+ if (_numSpritesToProcess < 2)
+ return;
+
+ qsort(_activeSpritesTable, _numSpritesToProcess, sizeof(SpriteInfo *), compareSprTable);
+}
+
+void Sprite::processImages(bool arg) {
+ int spr_flags;
+ int32 spr_wiz_x, spr_wiz_y;
+ int image, imageState;
+ Common::Rect *bboxPtr;
+ int angle, scale;
+ int32 w, h;
+ WizParameters wiz;
+
+ for (int i = 0; i < _numSpritesToProcess; i++) {
+ SpriteInfo *spi = _activeSpritesTable[i];
+
+ if (!(spi->flags & kSFNeedRedraw))
+ continue;
+
+ spr_flags = spi->flags;
+
+ if (arg) {
+ if (spi->zorder >= 0)
+ return;
+ } else {
+ if (spi->zorder < 0)
+ continue;
+ }
+
+ spi->flags &= ~kSFNeedRedraw;
+ image = spi->image;
+ imageState = spi->imageState;
+ _vm->_wiz->getWizImageSpot(spi->image, spi->imageState, spr_wiz_x, spr_wiz_y);
+
+ if (spi->group) {
+ SpriteGroup *spg = &_spriteGroups[spi->group];
+
+ if (spg->scaling) {
+ wiz.img.x1 = spi->tx * spg->scale_x_ratio_mul / spg->scale_x_ratio_div - spr_wiz_x + spg->tx;
+ wiz.img.y1 = spi->ty * spg->scale_y_ratio_mul / spg->scale_y_ratio_div - spr_wiz_y + spg->ty;
+ } else {
+ wiz.img.x1 = spi->tx - spr_wiz_x + spg->tx;
+ wiz.img.y1 = spi->ty - spr_wiz_y + spg->ty;
+ }
+ } else {
+ wiz.img.x1 = spi->tx - spr_wiz_x;
+ wiz.img.y1 = spi->ty - spr_wiz_y;
+ }
+
+ wiz.spriteId = spi->id;
+ wiz.spriteGroup = spi->group;
+ wiz.field_23EA = spi->field_90;
+ spi->curImageState = wiz.img.state = imageState;
+ spi->curImage = wiz.img.resNum = image;
+ wiz.processFlags = kWPFNewState | kWPFSetPos;
+ spi->curAngle = spi->angle;
+ spi->curScale = spi->scale;
+ spi->pos.x = wiz.img.x1;
+ spi->pos.y = wiz.img.y1;
+ bboxPtr = &spi->bbox;
+ if (image) {
+ angle = spi->angle;
+ scale = spi->scale;
+ _vm->_wiz->getWizImageDim(image, imageState, w, h);
+ if (spi->flags & (kSFScaled | kSFRotated)) {
+ Common::Point pts[4];
+ _vm->_wiz->polygonTransform(image, imageState, wiz.img.x1, wiz.img.y1, angle, scale, pts);
+ _vm->_wiz->polygonCalcBoundBox(pts, 4, spi->bbox);
+ } else {
+ bboxPtr->left = wiz.img.x1;
+ bboxPtr->top = wiz.img.y1;
+ bboxPtr->right = wiz.img.x1 + w;
+ bboxPtr->bottom = wiz.img.y1 + h;
+ }
+ } else {
+ bboxPtr->left = 1234;
+ bboxPtr->top = 1234;
+ bboxPtr->right = -1234;
+ bboxPtr->bottom = -1234;
+ }
+
+ wiz.img.flags = kWIFMarkBufferDirty;
+ wiz.img.zorder = 0;
+ if (spr_flags & kSFXFlipped)
+ wiz.img.flags |= kWIFFlipX;
+ if (spr_flags & kSFYFlipped)
+ wiz.img.flags |= kWIFFlipY;
+ if (spr_flags & kSFDoubleBuffered) {
+ wiz.img.flags &= ~kWIFMarkBufferDirty;
+ wiz.img.flags |= kWIFBlitToFrontVideoBuffer;
+ }
+ if (spi->shadow) {
+ wiz.img.flags |= 0x200;
+ wiz.processFlags |= kWPFShadow;
+ wiz.img.shadow = spi->shadow;
+ }
+ if (spr_flags & kSFRemapPalette)
+ wiz.img.flags |= kWIFRemapPalette;
+ if (spi->field_84) {
+ wiz.processFlags |= 0x200000;
+ wiz.img.field_390 = spi->field_84;
+ wiz.img.zorder = spi->priority;
+ }
+ if (spi->sourceImage) {
+ wiz.processFlags |= kWPFMaskImg;
+ wiz.sourceImage = spi->sourceImage;
+ }
+ wiz.processFlags |= kWPFNewFlags;
+ wiz.img.flags |= spi->imgFlags;
+
+ if (spr_flags & kSFRotated) {
+ wiz.processFlags |= kWPFRotate;
+ wiz.angle = spi->angle;
+ }
+ if (spr_flags & kSFScaled) {
+ wiz.processFlags |= kWPFScaled;
+ wiz.scale = spi->scale;
+ }
+ spi->curImgFlags = wiz.img.flags;
+
+ if (spi->group && (_spriteGroups[spi->group].flags & kSGFClipBox)) {
+ Common::Rect &spgBbox = _spriteGroups[spi->group].bbox;
+ if (spgBbox.isValidRect() && spi->bbox.intersects(spgBbox)) {
+ spi->bbox.clip(spgBbox);
+ wiz.processFlags |= kWPFClipBox;
+ wiz.box = spi->bbox;
+ } else {
+ bboxPtr->left = 1234;
+ bboxPtr->top = 1234;
+ bboxPtr->right = -1234;
+ bboxPtr->bottom = -1234;
+ continue;
+ }
+ }
+ if (spi->palette) {
+ wiz.processFlags |= kWPFPaletteNum;
+ wiz.img.palette = spi->palette;
+ }
+ if (spi->image && spi->group && _spriteGroups[spi->group].image) {
+ wiz.processFlags |= kWPFDstResNum;
+ wiz.dstResNum = _spriteGroups[spi->group].image;
+ }
+ _vm->_wiz->displayWizComplexImage(&wiz);
+ }
+}
+
+void Sprite::saveOrLoadSpriteData(Serializer *s) {
+ static const SaveLoadEntry spriteEntries[] = {
+ MKLINE(SpriteInfo, id, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, zorder, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, flags, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, image, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, imageState, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, group, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, palette, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, priority, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, bbox.left, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, bbox.top, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, bbox.right, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, bbox.bottom, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, dx, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, dy, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, pos.x, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, pos.y, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, tx, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, ty, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, userValue, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, curImageState, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, curImage, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, imglistNum, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, shadow, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, imageStateCount, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, angle, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, scale, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, animProgress, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, curAngle, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, curScale, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, curImgFlags, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, field_74, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, animSpeed, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, sourceImage, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, maskImage, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, field_84, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, classFlags, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, imgFlags, sleInt32, VER(48)),
+ MKLINE(SpriteInfo, field_90, sleInt32, VER(48)),
+ MKEND()
+ };
+
+ static const SaveLoadEntry spriteGroupEntries[] = {
+ MKLINE(SpriteGroup, bbox.left, sleInt32, VER(48)),
+ MKLINE(SpriteGroup, bbox.top, sleInt32, VER(48)),
+ MKLINE(SpriteGroup, bbox.right, sleInt32, VER(48)),
+ MKLINE(SpriteGroup, bbox.bottom, sleInt32, VER(48)),
+ MKLINE(SpriteGroup, priority, sleInt32, VER(48)),
+ MKLINE(SpriteGroup, flags, sleInt32, VER(48)),
+ MKLINE(SpriteGroup, tx, sleInt32, VER(48)),
+ MKLINE(SpriteGroup, ty, sleInt32, VER(48)),
+ MKLINE(SpriteGroup, image, sleInt32, VER(48)),
+ MKLINE(SpriteGroup, scaling, sleInt32, VER(48)),
+ MKLINE(SpriteGroup, scale_x_ratio_mul, sleInt32, VER(48)),
+ MKLINE(SpriteGroup, scale_x_ratio_div, sleInt32, VER(48)),
+ MKLINE(SpriteGroup, scale_y_ratio_mul, sleInt32, VER(48)),
+ MKLINE(SpriteGroup, scale_y_ratio_div, sleInt32, VER(48)),
+ MKEND()
+ };
+
+ if (s->getVersion() >= VER(64)) {
+ s->saveLoadArrayOf(_spriteTable, _varNumSprites + 1, sizeof(_spriteTable[0]), spriteEntries);
+ s->saveLoadArrayOf(_spriteGroups, _varNumSpriteGroups + 1, sizeof(_spriteGroups[0]), spriteGroupEntries);
+ } else {
+ s->saveLoadArrayOf(_activeSpritesTable, _varNumSprites, sizeof(_activeSpritesTable[0]), spriteEntries);
+ s->saveLoadArrayOf(_spriteTable, _varNumSprites, sizeof(_spriteTable[0]), spriteEntries);
+ s->saveLoadArrayOf(_spriteGroups, _varNumSpriteGroups, sizeof(_spriteGroups[0]), spriteGroupEntries);
+ }
+
+ // Reset active sprite table
+ if (s->isLoading())
+ _numSpritesToProcess = 0;
+
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/he/sprite_he.h b/engines/scumm/he/sprite_he.h
new file mode 100644
index 0000000000..5396c1fed4
--- /dev/null
+++ b/engines/scumm/he/sprite_he.h
@@ -0,0 +1,222 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#if !defined(SPRITE_HE_H) && !defined(DISABLE_HE)
+#define SPRITE_HE_H
+
+namespace Scumm {
+
+enum SpriteFlags {
+ kSFChanged = 0x1,
+ kSFNeedRedraw = 0x2,
+ kSFScaled = 0x10,
+ kSFRotated = 0x20,
+ kSFDoubleBuffered = 0x1000,
+ kSFYFlipped = 0x2000,
+ kSFXFlipped = 0x4000,
+ kSFActive = 0x8000,
+ kSFRemapPalette = 0x80000,
+ kSFAutoAnim = 0x200000,
+ kSFMarkDirty = 0x400000,
+ kSFBlitDirectly = 0x2000000,
+ kSFImageless = 0x40000000
+};
+
+enum SpriteGroupFlags {
+ kSGFClipBox = (1 << 0)
+};
+
+struct SpriteInfo {
+ int32 id;
+ int32 zorder;
+ int32 flags;
+ int32 image;
+ int32 imageState;
+ int32 group;
+ int32 palette;
+ int32 priority;
+ Common::Rect bbox;
+ int32 dx;
+ int32 dy;
+ Common::Point pos;
+ int32 tx;
+ int32 ty;
+ int32 userValue;
+ int32 curImageState;
+ int32 curImage;
+ int32 imglistNum;
+ int32 shadow;
+ int32 imageStateCount;
+ int32 angle;
+ int32 scale;
+ int32 animProgress;
+ int32 curAngle;
+ int32 curScale;
+ int32 curImgFlags;
+ int32 field_74;
+ int32 animSpeed;
+ int32 sourceImage;
+ int32 maskImage;
+ int32 field_84;
+ int32 classFlags;
+ int32 imgFlags;
+ int32 field_90;
+};
+
+struct SpriteGroup {
+ Common::Rect bbox;
+ int32 priority;
+ int32 flags;
+ int32 tx;
+ int32 ty;
+ int32 image;
+ int32 scaling;
+ int32 scale_x_ratio_mul;
+ int32 scale_x_ratio_div;
+ int32 scale_y_ratio_mul;
+ int32 scale_y_ratio_div;
+};
+
+class ScummEngine_v90he;
+
+class Sprite {
+public:
+ Sprite(ScummEngine_v90he *vm);
+ virtual ~Sprite();
+
+ SpriteInfo *_spriteTable;
+ SpriteGroup *_spriteGroups;
+ SpriteInfo **_activeSpritesTable;
+
+ int32 _numSpritesToProcess;
+ int32 _varNumSpriteGroups;
+ int32 _varNumSprites;
+ int32 _varMaxSprites;
+
+ void saveOrLoadSpriteData(Serializer *s);
+ void resetBackground();
+ void setRedrawFlags(bool checkZOrder);
+ void sortActiveSprites();
+ void processImages(bool arg);
+ void updateImages();
+
+ int findSpriteWithClassOf(int x, int y, int spriteGroupId, int d, int num, int *args);
+ int getSpriteClass(int spriteId, int num, int *args);
+ int getSpriteFlagDoubleBuffered(int spriteId);
+ int getSpriteFlagYFlipped(int spriteId);
+ int getSpriteFlagXFlipped(int spriteId);
+ int getSpriteFlagActive(int spriteId);
+ int getSpriteFlagRemapPalette(int spriteId);
+ int getSpriteFlagAutoAnim(int spriteId);
+ int getSpriteFlagUpdateType(int spriteId);
+ int getSpriteFlagEraseType(int spriteId);
+ int getSpriteImage(int spriteId);
+ int getSpriteImageState(int spriteId);
+ int getSpriteGroup(int spriteId);
+ int getSpritePalette(int spriteId);
+ int getSpritePriority(int spriteId);
+ int getSpriteDisplayX(int spriteId);
+ int getSpriteDisplayY(int spriteId);
+ int getSpriteUserValue(int spriteId);
+ int getSpriteShadow(int spriteId);
+ int getSpriteImageStateCount(int spriteId);
+ int getSpriteScale(int spriteId);
+ int getSpriteAnimSpeed(int spriteId);
+ int getSpriteSourceImage(int spriteId);
+ int getSpriteMaskImage(int spriteId);
+ int getSpriteGeneralProperty(int spriteId, int type);
+ void getSpriteBounds(int spriteId, bool checkGroup, Common::Rect &bound);
+ void getSpriteImageDim(int spriteId, int32 &w, int32 &h);
+ void getSpritePosition(int spriteId, int32 &tx, int32 &ty);
+ void getSpriteDist(int spriteId, int32 &dx, int32 &dy);
+
+ int getGroupPriority(int spriteGroupId);
+ int getGroupDstResNum(int spriteGroupId);
+ int getGroupXMul(int spriteGroupId);
+ int getGroupXDiv(int spriteGroupId);
+ int getGroupYMul(int spriteGroupId);
+ int getGroupYDiv(int spriteGroupId);
+ void getGroupPosition(int spriteGroupId, int32 &tx, int32 &ty);
+
+ void setSpritePalette(int spriteId, int value);
+ void setSpriteSourceImage(int spriteId, int value);
+ void setSpriteMaskImage(int spriteId, int value);
+ void resetSprite(int spriteId);
+ void setSpriteImageState(int spriteId, int value);
+ void setSpritePosition(int spriteId, int value1, int value2);
+ void setSpriteGroup(int spriteId, int value);
+ void setSpriteDist(int spriteId, int value1, int value2);
+ void setSpriteShadow(int spriteId, int value);
+ void setSpriteUserValue(int spriteId, int value1, int value2);
+ void setSpritePriority(int spriteId, int value);
+ void moveSprite(int spriteId, int value1, int value2);
+ void setSpriteScale(int spriteId, int value);
+ void setSpriteAngle(int spriteId, int value);
+ void setSpriteFlagDoubleBuffered(int spriteId, int value);
+ void setSpriteFlagYFlipped(int spriteId, int value);
+ void setSpriteFlagXFlipped(int spriteId, int value);
+ void setSpriteFlagActive(int spriteId, int value);
+ void setSpriteFlagRemapPalette(int spriteId, int value);
+ void setSpriteFlagAutoAnim(int spriteId, int value);
+ void setSpriteFlagUpdateType(int spriteId, int value);
+ void setSpriteFlagEraseType(int spriteId, int value);
+ void setSpriteAnimSpeed(int spriteId, int value);
+ void setSpriteSetClass(int spriteId, int classId, int toggle);
+ void setSpriteResetClass(int spriteId);
+ void setSpriteField84(int spriteId, int value);
+ void setSpriteGeneralProperty(int spriteId, int type, int value);
+
+ void moveGroupMembers(int spriteGroupId, int value1, int value2);
+ void redrawSpriteGroup(int spriteGroupId);
+ void setGroupMembersPriority(int spriteGroupId, int value);
+ void setGroupMembersGroup(int spriteGroupId, int value);
+ void setGroupMembersUpdateType(int spriteGroupId, int value);
+ void setGroupMembersResetSprite(int spriteGroupId);
+ void setGroupMembersAnimationSpeed(int spriteGroupId, int value);
+ void setGroupMembersAutoAnimFlag(int spriteGroupId, int value);
+ void setGroupMembersShadow(int spriteGroupId, int value);
+
+ void moveGroup(int spriteGroupId, int value1, int value2);
+ void setGroupBounds(int spriteGroupId, int x1, int y1, int x2, int y2);
+ void setGroupPriority(int spriteGroupId, int value);
+ void setGroupPosition(int spriteGroupId, int value1, int value2);
+ void setGroupImage(int spriteGroupId, int value);
+ void setGroupScaling(int spriteGroupId);
+ void setGroupXMul(int spriteGroupId, int value);
+ void setGroupXDiv(int spriteGroupId, int value);
+ void setGroupYMul(int spriteGroupId, int value);
+ void setGroupYDiv(int spriteGroupId, int value);
+ void resetGroupBounds(int spriteGroupId);
+
+ void allocTables(int numSprites, int numGroups, int numMaxSprites);
+ void resetGroup(int spriteGroupId);
+ void resetTables(bool refreshScreen);
+ void setSpriteImage(int spriteId, int imageNum);
+private:
+ ScummEngine_v90he *_vm;
+};
+
+} // End of namespace Scumm
+
+#endif
+
diff --git a/engines/scumm/he/wiz_he.cpp b/engines/scumm/he/wiz_he.cpp
new file mode 100644
index 0000000000..1d3baa74d2
--- /dev/null
+++ b/engines/scumm/he/wiz_he.cpp
@@ -0,0 +1,2088 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "scumm/he/intern_he.h"
+#include "scumm/resource.h"
+#include "scumm/scumm.h"
+#include "scumm/he/wiz_he.h"
+
+namespace Scumm {
+
+Wiz::Wiz(ScummEngine_v70he *vm) : _vm(vm) {
+ _imagesNum = 0;
+ memset(&_images, 0, sizeof(_images));
+ memset(&_polygons, 0, sizeof(_polygons));
+ _rectOverrideEnabled = false;
+}
+
+void Wiz::clearWizBuffer() {
+ _imagesNum = 0;
+}
+
+void Wiz::polygonClear() {
+ for (int i = 0; i < ARRAYSIZE(_polygons); i++) {
+ if (_polygons[i].flag == 1)
+ memset(&_polygons[i], 0, sizeof(WizPolygon));
+ }
+}
+
+void Wiz::polygonLoad(const uint8 *polData) {
+ int slots = READ_LE_UINT32(polData);
+ polData += 4;
+
+ bool flag = 1;
+ int id, points, vert1x, vert1y, vert2x, vert2y, vert3x, vert3y, vert4x, vert4y;
+ while (slots--) {
+ id = READ_LE_UINT32(polData);
+ points = READ_LE_UINT32(polData + 4);
+ if (points != 4)
+ error("Illegal polygon with %d points", points);
+ vert1x = READ_LE_UINT32(polData + 8);
+ vert1y = READ_LE_UINT32(polData + 12);
+ vert2x = READ_LE_UINT32(polData + 16);
+ vert2y = READ_LE_UINT32(polData + 20);
+ vert3x = READ_LE_UINT32(polData + 24);
+ vert3y = READ_LE_UINT32(polData + 28);
+ vert4x = READ_LE_UINT32(polData + 32);
+ vert4y = READ_LE_UINT32(polData + 36);
+
+ polData += 40;
+ polygonStore(id, flag, vert1x, vert1y, vert2x, vert2y, vert3x, vert3y, vert4x, vert4y);
+ }
+}
+
+void Wiz::polygonStore(int id, bool flag, int vert1x, int vert1y, int vert2x, int vert2y, int vert3x, int vert3y, int vert4x, int vert4y) {
+ WizPolygon *wp = NULL;
+ for (int i = 0; i < ARRAYSIZE(_polygons); ++i) {
+ if (_polygons[i].id == 0) {
+ wp = &_polygons[i];
+ break;
+ }
+ }
+ if (!wp) {
+ error("Wiz::polygonStore: out of polygon slot, max = %d", ARRAYSIZE(_polygons));
+ }
+
+ wp->vert[0].x = vert1x;
+ wp->vert[0].y = vert1y;
+ wp->vert[1].x = vert2x;
+ wp->vert[1].y = vert2y;
+ wp->vert[2].x = vert3x;
+ wp->vert[2].y = vert3y;
+ wp->vert[3].x = vert4x;
+ wp->vert[3].y = vert4y;
+ wp->vert[4].x = vert1x;
+ wp->vert[4].y = vert1y;
+ wp->id = id;
+ wp->numVerts = 5;
+ wp->flag = flag;
+
+ polygonCalcBoundBox(wp->vert, wp->numVerts, wp->bound);
+}
+
+void Wiz::polygonRotatePoints(Common::Point *pts, int num, int angle) {
+ double alpha = angle * PI / 180.;
+ double cos_alpha = cos(alpha);
+ double sin_alpha = sin(alpha);
+
+ for (int i = 0; i < num; ++i) {
+ int16 x = pts[i].x;
+ int16 y = pts[i].y;
+ pts[i].x = (int16)(x * cos_alpha - y * sin_alpha);
+ pts[i].y = (int16)(y * cos_alpha + x * sin_alpha);
+ }
+}
+
+void Wiz::polygonTransform(int resNum, int state, int po_x, int po_y, int angle, int scale, Common::Point *pts) {
+ int32 w, h;
+
+ getWizImageDim(resNum, state, w, h);
+
+ // set the transformation origin to the center of the image
+ if (_vm->_heversion >= 99) {
+ pts[0].x = pts[3].x = -(w / 2);
+ pts[1].x = pts[2].x = w / 2 - 1;
+ pts[0].y = pts[1].y = -(h / 2);
+ pts[2].y = pts[3].y = h / 2 - 1;
+ } else {
+ pts[1].x = pts[2].x = w / 2 - 1;
+ pts[0].x = pts[0].y = pts[1].y = pts[3].x = -(w / 2);
+ pts[2].y = pts[3].y = h / 2 - 1;
+ }
+
+ // scale
+ if (scale != 0 && scale != 256) {
+ for (int i = 0; i < 4; ++i) {
+ pts[i].x = pts[i].x * scale / 256;
+ pts[i].y = pts[i].y * scale / 256;
+ }
+ }
+
+ // rotate
+ if (angle != 0)
+ polygonRotatePoints(pts, 4, angle);
+
+ // translate
+ for (int i = 0; i < 4; ++i) {
+ pts[i].x += po_x;
+ pts[i].y += po_y;
+ }
+}
+
+void Wiz::polygonCalcBoundBox(Common::Point *vert, int numVerts, Common::Rect &bound) {
+ bound.left = 10000;
+ bound.top = 10000;
+ bound.right = -10000;
+ bound.bottom = -10000;
+
+ // compute bounding box
+ for (int j = 0; j < numVerts; j++) {
+ Common::Rect r(vert[j].x, vert[j].y, vert[j].x + 1, vert[j].y + 1);
+ bound.extend(r);
+ }
+}
+
+void Wiz::polygonErase(int fromId, int toId) {
+ for (int i = 0; i < ARRAYSIZE(_polygons); i++) {
+ if (_polygons[i].id >= fromId && _polygons[i].id <= toId)
+ memset(&_polygons[i], 0, sizeof(WizPolygon));
+ }
+}
+
+int Wiz::polygonHit(int id, int x, int y) {
+ for (int i = 0; i < ARRAYSIZE(_polygons); i++) {
+ if ((id == 0 || _polygons[i].id == id) && _polygons[i].bound.contains(x, y)) {
+ if (polygonContains(_polygons[i], x, y)) {
+ return _polygons[i].id;
+ }
+ }
+ }
+ return 0;
+}
+
+bool Wiz::polygonDefined(int id) {
+ for (int i = 0; i < ARRAYSIZE(_polygons); i++)
+ if (_polygons[i].id == id)
+ return true;
+ return false;
+}
+
+bool Wiz::polygonContains(const WizPolygon &pol, int x, int y) {
+ int pi = pol.numVerts - 1;
+ bool diry = (y < pol.vert[pi].y);
+ bool curdir;
+ bool r = false;
+
+ for (int i = 0; i < pol.numVerts; i++) {
+ curdir = (y < pol.vert[i].y);
+
+ if (curdir != diry) {
+ if (((pol.vert[pi].y - pol.vert[i].y) * (pol.vert[i].x - x) <
+ (pol.vert[pi].x - pol.vert[i].x) * (pol.vert[i].y - y)) == diry)
+ r = !r;
+ }
+
+ pi = i;
+ diry = curdir;
+ }
+
+ // HE80+
+ int a, b;
+ pi = pol.numVerts - 1;
+ if (r == 0) {
+ for (int i = 0; i < pol.numVerts; i++) {
+ if (pol.vert[i].y == y && pol.vert[i].y == pol.vert[pi].y) {
+
+ a = pol.vert[i].x;
+ b = pol.vert[pi].x;
+
+ if (pol.vert[i].x >= pol.vert[pi].x)
+ a = pol.vert[pi].x;
+
+ if (pol.vert[i].x > pol.vert[pi].x)
+ b = pol.vert[i].x;
+
+ if (x >= a && x <= b)
+ return 1;
+
+ } else if (pol.vert[i].x == x && pol.vert[i].x == pol.vert[pi].x) {
+
+ a = pol.vert[i].y;
+ b = pol.vert[i].y;
+
+ if (pol.vert[i].y >= pol.vert[pi].y)
+ a = pol.vert[pi].y;
+
+ if (pol.vert[i].y <= pol.vert[pi].y)
+ b = pol.vert[pi].y;
+
+ if (y >= a && y <= b)
+ return 1;
+ }
+ pi = i;
+ }
+ }
+
+ return r;
+}
+
+void Wiz::copyAuxImage(uint8 *dst1, uint8 *dst2, const uint8 *src, int dstw, int dsth, int srcx, int srcy, int srcw, int srch) {
+ Common::Rect dstRect(srcx, srcy, srcx + srcw, srcy + srch);
+ dstRect.clip(dstw, dsth);
+
+ int rw = dstRect.width();
+ int rh = dstRect.height();
+ if (rh <= 0 || rw <= 0)
+ return;
+
+ uint8 *dst1Ptr = dst1 + dstRect.left + dstRect.top * dstw;
+ uint8 *dst2Ptr = dst2 + dstRect.left + dstRect.top * dstw;
+ const uint8 *dataPtr = src;
+
+ while (rh--) {
+ uint16 off = READ_LE_UINT16(dataPtr); dataPtr += 2;
+ const uint8 *dataPtrNext = off + dataPtr;
+ uint8 *dst1PtrNext = dst1Ptr + dstw;
+ uint8 *dst2PtrNext = dst2Ptr + dstw;
+ if (off != 0) {
+ int w = rw;
+ while (w > 0) {
+ uint8 code = *dataPtr++;
+ if (code & 1) {
+ code >>= 1;
+ dst1Ptr += code;
+ dst2Ptr += code;
+ w -= code;
+ } else if (code & 2) {
+ code = (code >> 2) + 1;
+ w -= code;
+ if (w >= 0) {
+ memset(dst1Ptr, *dataPtr++, code);
+ dst1Ptr += code;
+ dst2Ptr += code;
+ } else {
+ code += w;
+ memset(dst1Ptr, *dataPtr, code);
+ }
+ } else {
+ code = (code >> 2) + 1;
+ w -= code;
+ if (w >= 0) {
+ memcpy(dst1Ptr, dst2Ptr, code);
+ dst1Ptr += code;
+ dst2Ptr += code;
+ } else {
+ code += w;
+ memcpy(dst1Ptr, dst2Ptr, code);
+ }
+ }
+ }
+ }
+ dataPtr = dataPtrNext;
+ dst1Ptr = dst1PtrNext;
+ dst2Ptr = dst2PtrNext;
+ }
+}
+
+static bool calcClipRects(int dst_w, int dst_h, int src_x, int src_y, int src_w, int src_h, const Common::Rect *rect, Common::Rect &srcRect, Common::Rect &dstRect) {
+ srcRect = Common::Rect(src_w, src_h);
+ dstRect = Common::Rect(src_x, src_y, src_x + src_w, src_y + src_h);
+ Common::Rect r3;
+ int diff;
+
+ if (rect) {
+ r3 = *rect;
+ Common::Rect r4(dst_w, dst_h);
+ if (r3.intersects(r4)) {
+ r3.clip(r4);
+ } else {
+ return false;
+ }
+ } else {
+ r3 = Common::Rect(dst_w, dst_h);
+ }
+ diff = dstRect.left - r3.left;
+ if (diff < 0) {
+ srcRect.left -= diff;
+ dstRect.left -= diff;
+ }
+ diff = dstRect.right - r3.right;
+ if (diff > 0) {
+ srcRect.right -= diff;
+ dstRect.right -= diff;
+ }
+ diff = dstRect.top - r3.top;
+ if (diff < 0) {
+ srcRect.top -= diff;
+ dstRect.top -= diff;
+ }
+ diff = dstRect.bottom - r3.bottom;
+ if (diff > 0) {
+ srcRect.bottom -= diff;
+ dstRect.bottom -= diff;
+ }
+
+ return srcRect.isValidRect() && dstRect.isValidRect();
+}
+
+void Wiz::copyWizImage(uint8 *dst, const uint8 *src, int dstw, int dsth, int srcx, int srcy, int srcw, int srch, const Common::Rect *rect, int flags, const uint8 *palPtr, const uint8 *xmapPtr) {
+ Common::Rect r1, r2;
+ if (calcClipRects(dstw, dsth, srcx, srcy, srcw, srch, rect, r1, r2)) {
+ dst += r2.left + r2.top * dstw;
+ decompressWizImage(dst, dstw, r2, src, r1, flags, palPtr, xmapPtr);
+ }
+}
+
+void Wiz::copyRaw16BitWizImage(uint8 *dst, const uint8 *src, int dstw, int dsth, int srcx, int srcy, int srcw, int srch, const Common::Rect *rect, int flags, const uint8 *palPtr, int transColor) {
+ // RAW 16 bits in 555 format
+
+ // HACK: Skip every second bit for now
+ Common::Rect r1, r2;
+ if (calcClipRects(dstw, dsth, srcx, srcy, srcw, srch, rect, r1, r2)) {
+ if (flags & kWIFFlipX) {
+ int l = r1.left;
+ int r = r1.right;
+ r1.left = srcw - r;
+ r1.right = srcw - l;
+ }
+ if (flags & kWIFFlipY) {
+ int t = r1.top;
+ int b = r1.bottom;
+ r1.top = srch - b;
+ r1.bottom = srch - t;
+ }
+ byte imagePal[256];
+ if (!palPtr) {
+ for (int i = 0; i < 256; i++) {
+ imagePal[i] = i;
+ }
+ palPtr = imagePal;
+ }
+
+ int h = r1.height();
+ int w = r1.width();
+ src += r1.left + r1.top * srcw * 2;
+ dst += r2.left + r2.top * dstw;
+
+ while (h--) {
+ const uint8 *p = src;
+ for (int i = 0; i < w; ++i) {
+ uint8 col = *p;
+ if (transColor == -1 || transColor != col) {
+ dst[i] = palPtr[col];
+ }
+ p += 2;
+ }
+ src += srcw * 2;
+ dst += dstw;
+ }
+
+ }
+}
+
+void Wiz::copyRawWizImage(uint8 *dst, const uint8 *src, int dstw, int dsth, int srcx, int srcy, int srcw, int srch, const Common::Rect *rect, int flags, const uint8 *palPtr, int transColor) {
+ Common::Rect r1, r2;
+ if (calcClipRects(dstw, dsth, srcx, srcy, srcw, srch, rect, r1, r2)) {
+ if (flags & kWIFFlipX) {
+ int l = r1.left;
+ int r = r1.right;
+ r1.left = srcw - r;
+ r1.right = srcw - l;
+ }
+ if (flags & kWIFFlipY) {
+ int t = r1.top;
+ int b = r1.bottom;
+ r1.top = srch - b;
+ r1.bottom = srch - t;
+ }
+ byte imagePal[256];
+ if (!palPtr) {
+ for (int i = 0; i < 256; i++) {
+ imagePal[i] = i;
+ }
+ palPtr = imagePal;
+ }
+ int h = r1.height();
+ int w = r1.width();
+ src += r1.left + r1.top * srcw;
+ dst += r2.left + r2.top * dstw;
+ while (h--) {
+ const uint8 *p = src;
+ for (int i = 0; i < w; ++i) {
+ uint8 col = *p++;
+ if (transColor == -1 || transColor != col) {
+ dst[i] = palPtr[col];
+ }
+ }
+ src += srcw;
+ dst += dstw;
+ }
+ }
+}
+
+void Wiz::decompressWizImage(uint8 *dst, int dstPitch, const Common::Rect &dstRect, const uint8 *src, const Common::Rect &srcRect, int flags, const uint8 *palPtr, const uint8 *xmapPtr) {
+ if (flags & kWIFFlipX) {
+ debug(1, "decompressWizImage: Unhandled flag kWIFFlipX");
+ }
+ if (flags & kWIFFlipY) {
+ debug(1, "decompressWizImage: Unhandled flag kWIFFlipY");
+ }
+
+ const uint8 *dataPtr, *dataPtrNext;
+ uint8 *dstPtr, *dstPtrNext;
+ uint32 code;
+ uint8 databit;
+ int h, w, xoff;
+ uint16 off;
+
+ byte imagePal[256];
+ if (!palPtr) {
+ for (int i = 0; i < 256; i++) {
+ imagePal[i] = i;
+ }
+ palPtr = imagePal;
+ }
+
+ dstPtr = dst;
+ dataPtr = src;
+
+ // Skip over the first 'srcRect->top' lines in the data
+ h = srcRect.top;
+ while (h--) {
+ dataPtr += READ_LE_UINT16(dataPtr) + 2;
+ }
+ h = srcRect.height();
+ w = srcRect.width();
+ if (h <= 0 || w <= 0)
+ return;
+
+ while (h--) {
+ xoff = srcRect.left;
+ off = READ_LE_UINT16(dataPtr);
+ w = srcRect.right - srcRect.left;
+ dstPtrNext = dstPitch + dstPtr;
+ dataPtrNext = off + 2 + dataPtr;
+ dataPtr += 2;
+ if (off == 0)
+ goto dec_next;
+
+ // Skip over the leftmost 'srcRect->left' pixels.
+ // TODO: This code could be merged (at a loss of efficency) with the
+ // loop below which does the actual drawing.
+ while (xoff > 0) {
+ code = *dataPtr++;
+ databit = code & 1;
+ code >>= 1;
+ if (databit) {
+ xoff -= code;
+ if (xoff < 0) {
+ code = -xoff;
+ goto dec_sub1;
+ }
+ } else {
+ databit = code & 1;
+ code = (code >> 1) + 1;
+ if (databit) {
+ ++dataPtr;
+ xoff -= code;
+ if (xoff < 0) {
+ code = -xoff;
+ --dataPtr;
+ goto dec_sub2;
+ }
+ } else {
+ dataPtr += code;
+ xoff -= code;
+ if (xoff < 0) {
+ dataPtr += xoff;
+ code = -xoff;
+ goto dec_sub3;
+ }
+ }
+ }
+ }
+
+ while (w > 0) {
+ code = *dataPtr++;
+ databit = code & 1;
+ code >>= 1;
+ if (databit) {
+dec_sub1: dstPtr += code;
+ w -= code;
+ } else {
+ databit = code & 1;
+ code = (code >> 1) + 1;
+ if (databit) {
+dec_sub2: w -= code;
+ if (w < 0) {
+ code += w;
+ }
+ while (code--) {
+ if (xmapPtr) {
+ *dstPtr = xmapPtr[palPtr[*dataPtr] * 256 + *dstPtr];
+ dstPtr++;
+ } else {
+ *dstPtr++ = palPtr[*dataPtr];
+ }
+ }
+ dataPtr++;
+ } else {
+dec_sub3: w -= code;
+ if (w < 0) {
+ code += w;
+ }
+ while (code--) {
+ if (xmapPtr) {
+ *dstPtr = xmapPtr[palPtr[*dataPtr++] * 256 + *dstPtr];
+ dstPtr++;
+ } else {
+ *dstPtr++ = palPtr[*dataPtr++];
+ }
+ }
+ }
+ }
+ }
+dec_next:
+ dataPtr = dataPtrNext;
+ dstPtr = dstPtrNext;
+ }
+}
+
+int Wiz::isWizPixelNonTransparent(const uint8 *data, int x, int y, int w, int h) {
+ if (x < 0 || x >= w || y < 0 || y >= h) {
+ return 0;
+ }
+ while (y != 0) {
+ data += READ_LE_UINT16(data) + 2;
+ --y;
+ }
+ uint16 off = READ_LE_UINT16(data); data += 2;
+ if (off == 0) {
+ return 0;
+ }
+ while (x > 0) {
+ uint8 code = *data++;
+ if (code & 1) {
+ code >>= 1;
+ if (code > x) {
+ return 0;
+ }
+ x -= code;
+ } else if (code & 2) {
+ code = (code >> 2) + 1;
+ if (code > x) {
+ return 1;
+ }
+ x -= code;
+ ++data;
+ } else {
+ code = (code >> 2) + 1;
+ if (code > x) {
+ return 1;
+ }
+ x -= code;
+ data += code;
+ }
+ }
+ return (~data[0]) & 1;
+}
+
+uint8 Wiz::getWizPixelColor(const uint8 *data, int x, int y, int w, int h, uint8 color) {
+ if (x < 0 || x >= w || y < 0 || y >= h) {
+ return color;
+ }
+ while (y != 0) {
+ data += READ_LE_UINT16(data) + 2;
+ --y;
+ }
+ uint16 off = READ_LE_UINT16(data); data += 2;
+ if (off == 0) {
+ return color;
+ }
+ while (x > 0) {
+ uint8 code = *data++;
+ if (code & 1) {
+ code >>= 1;
+ if (code > x) {
+ return color;
+ }
+ x -= code;
+ } else if (code & 2) {
+ code = (code >> 2) + 1;
+ if (code > x) {
+ return data[0];
+ }
+ x -= code;
+ ++data;
+ } else {
+ code = (code >> 2) + 1;
+ if (code > x) {
+ return data[x];
+ }
+ x -= code;
+ data += code;
+ }
+ }
+ return (data[0] & 1) ? color : data[1];
+}
+
+uint8 Wiz::getRawWizPixelColor(const uint8 *data, int x, int y, int w, int h, uint8 color) {
+ if (x < 0 || x >= w || y < 0 || y >= h) {
+ return color;
+ }
+ return data[y * w + x];
+}
+
+void Wiz::computeWizHistogram(uint32 *histogram, const uint8 *data, const Common::Rect &rCapt) {
+ int y = rCapt.top;
+ while (y != 0) {
+ data += READ_LE_UINT16(data) + 2;
+ --y;
+ }
+ int ih = rCapt.height();
+ while (ih--) {
+ uint16 off = READ_LE_UINT16(data); data += 2;
+ if (off != 0) {
+ const uint8 *p = data;
+ int x1 = rCapt.left;
+ int x2 = rCapt.right;
+ uint8 code;
+ while (x1 > 0) {
+ code = *p++;
+ if (code & 1) {
+ code >>= 1;
+ if (code > x1) {
+ code -= x1;
+ x2 -= code;
+ break;
+ }
+ x1 -= code;
+ } else if (code & 2) {
+ code = (code >> 2) + 1;
+ if (code > x1) {
+ code -= x1;
+ goto dec_sub2;
+ }
+ x1 -= code;
+ ++p;
+ } else {
+ code = (code >> 2) + 1;
+ if (code > x1) {
+ code -= x1;
+ p += x1;
+ goto dec_sub3;
+ }
+ x1 -= code;
+ p += code;
+ }
+ }
+ while (x2 > 0) {
+ code = *p++;
+ if (code & 1) {
+ code >>= 1;
+ x2 -= code;
+ } else if (code & 2) {
+ code = (code >> 2) + 1;
+dec_sub2: x2 -= code;
+ if (x2 < 0) {
+ code += x2;
+ }
+ histogram[*p++] += code;
+ } else {
+ code = (code >> 2) + 1;
+dec_sub3: x2 -= code;
+ if (x2 < 0) {
+ code += x2;
+ }
+ int n = code;
+ while (n--) {
+ ++histogram[*p++];
+ }
+ }
+ }
+ data += off;
+ }
+ }
+}
+
+void Wiz::computeRawWizHistogram(uint32 *histogram, const uint8 *data, int srcPitch, const Common::Rect &rCapt) {
+ data += rCapt.top * srcPitch + rCapt.left;
+ int iw = rCapt.width();
+ int ih = rCapt.height();
+ while (ih--) {
+ for (int i = 0; i < iw; ++i) {
+ ++histogram[data[i]];
+ }
+ data += srcPitch;
+ }
+}
+
+static int wizPackType1(uint8 *dst, const uint8 *src, int srcPitch, const Common::Rect& rCapt, uint8 transColor) {
+ debug(9, "wizPackType1(%d, [%d,%d,%d,%d])", transColor, rCapt.left, rCapt.top, rCapt.right, rCapt.bottom);
+ src += rCapt.top * srcPitch + rCapt.left;
+ int w = rCapt.width();
+ int h = rCapt.height();
+ int dataSize = 0;
+ while (h--) {
+ uint8 *dstLine = dst;
+ if (dst) {
+ dst += 2;
+ }
+ uint8 diffBuffer[0x40];
+ int runCountSame = 0;
+ int runCountDiff = 0;
+ uint8 prevColor = src[0];
+ for (int i = 1; i < w; ) {
+ uint8 color = src[i++];
+ if (i == 2) {
+ if (prevColor == color) {
+ runCountSame = 1;
+ } else {
+ diffBuffer[0] = prevColor;
+ runCountDiff = 1;
+ }
+ }
+ if (prevColor == color) {
+ if (runCountDiff != 0) {
+ runCountSame = 1;
+ if (runCountDiff > 1) {
+ --runCountDiff;
+ if (dst) {
+ *dst++ = ((runCountDiff - 1) << 2) | 0;
+ memcpy(dst, diffBuffer, runCountDiff);
+ dst += runCountDiff;
+ }
+ dataSize += runCountDiff + 1;
+ }
+ runCountDiff = 0;
+ }
+ ++runCountSame;
+ if (prevColor == transColor) {
+ if (runCountSame == 0x7F) {
+ if (dst) {
+ *dst++ = (runCountSame << 1) | 1;
+ }
+ ++dataSize;
+ runCountSame = 0;
+ }
+ } else {
+ if (runCountSame == 0x40) {
+ if (dst) {
+ *dst++ = ((runCountSame - 1) << 2) | 2;
+ *dst++ = prevColor;
+ }
+ dataSize += 2;
+ runCountSame = 0;
+ }
+ }
+ } else {
+ if (runCountSame != 0) {
+ if (prevColor == transColor) {
+ if (dst) {
+ *dst++ = (runCountSame << 1) | 1;
+ }
+ ++dataSize;
+ } else {
+ if (dst) {
+ *dst++ = ((runCountSame - 1) << 2) | 2;
+ *dst++ = prevColor;
+ }
+ dataSize += 2;
+ }
+ runCountSame = 0;
+ }
+ assert(runCountDiff < ARRAYSIZE(diffBuffer));
+ diffBuffer[runCountDiff++] = color;
+ if (runCountDiff == 0x40) {
+ if (dst) {
+ *dst++ = ((runCountDiff - 1) << 2) | 0;
+ memcpy(dst, diffBuffer, runCountDiff);
+ dst += runCountDiff + 1;
+ }
+ dataSize += runCountDiff + 1;
+ runCountDiff = 0;
+ }
+ }
+ prevColor = color;
+ }
+ if (runCountSame != 0) {
+ if (prevColor == transColor) {
+ if (dst) {
+ *dst++ = (runCountSame << 1) | 1;
+ }
+ ++dataSize;
+ } else {
+ if (dst) {
+ *dst++ = ((runCountSame - 1) << 2) | 2;
+ *dst++ = prevColor;
+ }
+ dataSize += 2;
+ }
+ }
+ if (runCountDiff != 0) {
+ if (dst) {
+ *dst++ = ((runCountDiff - 1) << 2) | 0;
+ memcpy(dst, diffBuffer, runCountDiff);
+ dst += runCountDiff;
+ }
+ dataSize += runCountDiff + 1;
+ }
+ if (dst) {
+ WRITE_LE_UINT16(dstLine, dst - dstLine - 2);
+ }
+ dataSize += 2;
+ src += srcPitch;
+ }
+ return dataSize;
+}
+
+static int wizPackType0(uint8 *dst, const uint8 *src, int srcPitch, const Common::Rect& rCapt) {
+ debug(9, "wizPackType0([%d,%d,%d,%d])", rCapt.left, rCapt.top, rCapt.right, rCapt.bottom);
+ int w = rCapt.width();
+ int h = rCapt.height();
+ int size = w * h;
+ if (dst) {
+ src += rCapt.top * srcPitch + rCapt.left;
+ while (h--) {
+ memcpy(dst, src, w);
+ dst += w;
+ src += srcPitch;
+ }
+ }
+ return size;
+}
+
+void Wiz::captureWizImage(int resNum, const Common::Rect& r, bool backBuffer, int compType) {
+ debug(5, "ScummEngine_v72he::captureWizImage(%d, %d, [%d,%d,%d,%d])", resNum, compType, r.left, r.top, r.right, r.bottom);
+ uint8 *src = NULL;
+ VirtScreen *pvs = &_vm->virtscr[kMainVirtScreen];
+ if (backBuffer) {
+ src = pvs->getBackPixels(0, 0);
+ } else {
+ src = pvs->getPixels(0, 0);
+ }
+ Common::Rect rCapt(pvs->w, pvs->h);
+ if (rCapt.intersects(r)) {
+ rCapt.clip(r);
+ const uint8 *palPtr;
+ if (_vm->_heversion >= 99) {
+ palPtr = _vm->_hePalettes + 1024;
+ } else {
+ palPtr = _vm->_currentPalette;
+ }
+
+ int w = rCapt.width();
+ int h = rCapt.height();
+ int transColor = (_vm->VAR_WIZ_TCOLOR != 0xFF) ? _vm->VAR(_vm->VAR_WIZ_TCOLOR) : 5;
+
+ // compute compressed size
+ int dataSize = 0;
+ int headerSize = palPtr ? 1080 : 36;
+ switch (compType) {
+ case 0:
+ dataSize = wizPackType0(0, src, pvs->pitch, rCapt);
+ break;
+ case 1:
+ dataSize = wizPackType1(0, src, pvs->pitch, rCapt, transColor);
+ break;
+ default:
+ error("unhandled compression type %d", compType);
+ break;
+ }
+
+ // alignment
+ dataSize = (dataSize + 1) & ~1;
+ int wizSize = headerSize + dataSize;
+ // write header
+ uint8 *wizImg = _vm->res.createResource(rtImage, resNum, dataSize + headerSize);
+ WRITE_BE_UINT32(wizImg + 0x00, 'AWIZ');
+ WRITE_BE_UINT32(wizImg + 0x04, wizSize);
+ WRITE_BE_UINT32(wizImg + 0x08, 'WIZH');
+ WRITE_BE_UINT32(wizImg + 0x0C, 0x14);
+ WRITE_LE_UINT32(wizImg + 0x10, compType);
+ WRITE_LE_UINT32(wizImg + 0x14, w);
+ WRITE_LE_UINT32(wizImg + 0x18, h);
+ int curSize = 0x1C;
+ if (palPtr) {
+ WRITE_BE_UINT32(wizImg + 0x1C, 'RGBS');
+ WRITE_BE_UINT32(wizImg + 0x20, 0x308);
+ memcpy(wizImg + 0x24, palPtr, 0x300);
+ WRITE_BE_UINT32(wizImg + 0x324, 'RMAP');
+ WRITE_BE_UINT32(wizImg + 0x328, 0x10C);
+ WRITE_BE_UINT32(wizImg + 0x32C, 0);
+ curSize = 0x330;
+ for (int i = 0; i < 256; ++i) {
+ wizImg[curSize] = i;
+ ++curSize;
+ }
+ }
+ WRITE_BE_UINT32(wizImg + curSize + 0x0, 'WIZD');
+ WRITE_BE_UINT32(wizImg + curSize + 0x4, dataSize + 8);
+ curSize += 8;
+
+ // write compressed data
+ switch (compType) {
+ case 0:
+ wizPackType0(wizImg + headerSize, src, pvs->pitch, rCapt);
+ break;
+ case 1:
+ wizPackType1(wizImg + headerSize, src, pvs->pitch, rCapt, transColor);
+ break;
+ default:
+ break;
+ }
+ }
+ _vm->res.setModified(rtImage, resNum);
+}
+
+void Wiz::displayWizImage(WizImage *pwi) {
+ if (_vm->_fullRedraw) {
+ assert(_imagesNum < ARRAYSIZE(_images));
+ WizImage *wi = &_images[_imagesNum];
+ wi->resNum = pwi->resNum;
+ wi->x1 = pwi->x1;
+ wi->y1 = pwi->y1;
+ wi->zorder = 0;
+ wi->state = pwi->state;
+ wi->flags = pwi->flags;
+ wi->shadow = 0;
+ wi->field_390 = 0;
+ wi->palette = 0;
+ ++_imagesNum;
+ } else if (pwi->flags & kWIFIsPolygon) {
+ drawWizPolygon(pwi->resNum, pwi->state, pwi->x1, pwi->flags, 0, 0, 0);
+ } else {
+ const Common::Rect *r = NULL;
+ drawWizImage(pwi->resNum, pwi->state, pwi->x1, pwi->y1, 0, 0, 0, r, pwi->flags, 0, 0);
+ }
+}
+
+uint8 *Wiz::drawWizImage(int resNum, int state, int x1, int y1, int zorder, int shadow, int field_390, const Common::Rect *clipBox, int flags, int dstResNum, int palette) {
+ debug(2, "drawWizImage(resNum %d, x1 %d y1 %d flags 0x%X zorder %d shadow %d field_390 %d dstResNum %d palette %d)", resNum, x1, y1, flags, zorder, shadow, field_390, dstResNum, palette);
+ uint8 *dataPtr;
+ uint8 *dst = NULL;
+
+ const uint8 *palPtr = NULL;
+ if (_vm->_heversion >= 99) {
+ if (palette) {
+ palPtr = _vm->_hePalettes + palette * 1024 + 768;
+ } else {
+ palPtr = _vm->_hePalettes + 1792;
+ }
+ }
+
+ const uint8 *xmapPtr = NULL;
+ if (shadow) {
+ dataPtr = _vm->getResourceAddress(rtImage, shadow);
+ assert(dataPtr);
+ xmapPtr = _vm->findResourceData(MKID('XMAP'), dataPtr);
+ assert(xmapPtr);
+ }
+
+ dataPtr = _vm->getResourceAddress(rtImage, resNum);
+ assert(dataPtr);
+
+ uint8 *wizh = _vm->findWrappedBlock(MKID('WIZH'), dataPtr, state, 0);
+ assert(wizh);
+ uint32 comp = READ_LE_UINT32(wizh + 0x0);
+ uint32 width = READ_LE_UINT32(wizh + 0x4);
+ uint32 height = READ_LE_UINT32(wizh + 0x8);
+ debug(2, "wiz_header.comp = %d wiz_header.w = %d wiz_header.h = %d", comp, width, height);
+
+ uint8 *wizd = _vm->findWrappedBlock(MKID('WIZD'), dataPtr, state, 0);
+ assert(wizd);
+
+ if (flags & kWIFHasPalette) {
+ uint8 *pal = _vm->findWrappedBlock(MKID('RGBS'), dataPtr, state, 0);
+ assert(pal);
+ _vm->setPaletteFromPtr(pal, 256);
+ }
+
+ uint8 *rmap = NULL;
+ if (flags & kWIFRemapPalette) {
+ rmap = _vm->findWrappedBlock(MKID('RMAP'), dataPtr, state, 0);
+ assert(rmap);
+ if (_vm->_heversion <= 80 || READ_BE_UINT32(rmap) != 0x01234567) {
+ uint8 *rgbs = _vm->findWrappedBlock(MKID('RGBS'), dataPtr, state, 0);
+ assert(rgbs);
+ _vm->remapHEPalette(rgbs, rmap + 4);
+ }
+ }
+
+ if (flags & kWIFPrint) {
+ error("WizImage printing is unimplemented");
+ }
+
+ int32 cw, ch;
+ if (flags & kWIFBlitToMemBuffer) {
+ dst = (uint8 *)malloc(width * height);
+ int transColor = (_vm->VAR_WIZ_TCOLOR != 0xFF) ? (_vm->VAR(_vm->VAR_WIZ_TCOLOR)) : 5;
+ memset(dst, transColor, width * height);
+ cw = width;
+ ch = height;
+ } else {
+ if (dstResNum) {
+ uint8 *dstPtr = _vm->getResourceAddress(rtImage, dstResNum);
+ assert(dstPtr);
+ dst = _vm->findWrappedBlock(MKID('WIZD'), dstPtr, 0, 0);
+ assert(dst);
+ getWizImageDim(dstResNum, 0, cw, ch);
+ } else {
+ VirtScreen *pvs = &_vm->virtscr[kMainVirtScreen];
+ if (flags & kWIFMarkBufferDirty) {
+ dst = pvs->getPixels(0, pvs->topline);
+ } else {
+ dst = pvs->getBackPixels(0, pvs->topline);
+ }
+ cw = pvs->w;
+ ch = pvs->h;
+ }
+ }
+
+ Common::Rect rScreen(cw, ch);
+ if (clipBox) {
+ Common::Rect clip(clipBox->left, clipBox->top, clipBox->right, clipBox->bottom);
+ if (rScreen.intersects(clip)) {
+ rScreen.clip(clip);
+ } else {
+ return 0;
+ }
+ } else if (_rectOverrideEnabled) {
+ if (rScreen.intersects(_rectOverride)) {
+ rScreen.clip(_rectOverride);
+ } else {
+ return 0;
+ }
+ }
+
+ if (flags & kWIFRemapPalette) {
+ palPtr = rmap + 4;
+ }
+
+ int transColor = -1;
+ if (_vm->VAR_WIZ_TCOLOR != 0xFF) {
+ uint8 *trns = _vm->findWrappedBlock(MKID('TRNS'), dataPtr, state, 0);
+ transColor = (trns == NULL) ? _vm->VAR(_vm->VAR_WIZ_TCOLOR) : -1;
+ }
+
+ switch (comp) {
+ case 0:
+ copyRawWizImage(dst, wizd, cw, ch, x1, y1, width, height, &rScreen, flags, palPtr, transColor);
+ break;
+ case 1:
+ // TODO Adding masking for flags 0x80 and 0x100
+ if (flags & 0x80)
+ // Used in maze
+ debug(0, "drawWizImage: Unhandled flag 0x80");
+ if (flags & 0x100) {
+ // Used in readdemo
+ debug(0, "drawWizImage: Unhandled flag 0x100");
+ }
+ copyWizImage(dst, wizd, cw, ch, x1, y1, width, height, &rScreen, flags, palPtr, xmapPtr);
+ break;
+ case 2:
+ copyRaw16BitWizImage(dst, wizd, cw, ch, x1, y1, width, height, &rScreen, flags, palPtr, transColor);
+ break;
+ default:
+ error("drawWizImage: Unhandled wiz compression type %d", comp);
+ }
+
+ if (!(flags & kWIFBlitToMemBuffer) && dstResNum == 0) {
+ Common::Rect rImage(x1, y1, x1 + width, y1 + height);
+ if (rImage.intersects(rScreen)) {
+ rImage.clip(rScreen);
+ if (!(flags & kWIFBlitToFrontVideoBuffer) && (flags & (kWIFBlitToFrontVideoBuffer | kWIFMarkBufferDirty))) {
+ ++rImage.bottom;
+ _vm->markRectAsDirty(kMainVirtScreen, rImage);
+ } else {
+ _vm->gdi.copyVirtScreenBuffers(rImage);
+ }
+ }
+ }
+
+ return dst;
+}
+
+struct PolygonDrawData {
+ struct PolygonArea {
+ int32 xmin;
+ int32 xmax;
+ int32 x1;
+ int32 y1;
+ int32 x2;
+ int32 y2;
+ };
+ struct ResultArea {
+ int32 dst_offs;
+ int32 x_step;
+ int32 y_step;
+ int32 x_s;
+ int32 y_s;
+ int32 w;
+ };
+ Common::Point mat[4];
+ PolygonArea *pa;
+ ResultArea *ra;
+ int rAreasNum;
+ int pAreasNum;
+
+ PolygonDrawData(int n) {
+ memset(mat, 0, sizeof(mat));
+ pa = new PolygonArea[n];
+ for (int i = 0; i < n; ++i) {
+ pa[i].xmin = 0x7FFFFFFF;
+ pa[i].xmax = 0x80000000;
+ }
+ ra = new ResultArea[n];
+ rAreasNum = 0;
+ pAreasNum = n;
+ }
+
+ ~PolygonDrawData() {
+ delete[] pa;
+ delete[] ra;
+ }
+
+ void transform(const Common::Point *tp1, const Common::Point *tp2, const Common::Point *sp1, const Common::Point *sp2) {
+ int32 tx_acc = tp1->x << 16;
+ int32 sx_acc = sp1->x << 16;
+ int32 sy_acc = sp1->y << 16;
+ uint16 dy = ABS(tp2->y - tp1->y) + 1;
+ int32 tx_step = ((tp2->x - tp1->x) << 16) / dy;
+ int32 sx_step = ((sp2->x - sp1->x) << 16) / dy;
+ int32 sy_step = ((sp2->y - sp1->y) << 16) / dy;
+
+ int y = tp1->y - mat[0].y;
+ while (dy--) {
+ assert(y >= 0 && y < pAreasNum);
+ PolygonArea *ppa = &pa[y];
+ int32 ttx = tx_acc >> 16;
+ int32 tsx = sx_acc >> 16;
+ int32 tsy = sy_acc >> 16;
+
+ if (ppa->xmin > ttx) {
+ ppa->xmin = ttx;
+ ppa->x1 = tsx;
+ ppa->y1 = tsy;
+ }
+ if (ppa->xmax < ttx) {
+ ppa->xmax = ttx;
+ ppa->x2 = tsx;
+ ppa->y2 = tsy;
+ }
+
+ tx_acc += tx_step;
+ sx_acc += sx_step;
+ sy_acc += sy_step;
+
+ if (tp2->y <= tp1->y) {
+ --y;
+ } else {
+ ++y;
+ }
+ }
+ }
+};
+
+void Wiz::drawWizComplexPolygon(int resNum, int state, int po_x, int po_y, int shadow, int angle, int scale, const Common::Rect *r, int flags, int dstResNum, int palette) {
+ Common::Point pts[4];
+
+ polygonTransform(resNum, state, po_x, po_y, angle, scale, pts);
+ drawWizPolygonTransform(resNum, state, pts, flags, shadow, dstResNum, palette);
+}
+
+void Wiz::drawWizPolygon(int resNum, int state, int id, int flags, int shadow, int dstResNum, int palette) {
+ int i;
+ WizPolygon *wp = NULL;
+ for (i = 0; i < ARRAYSIZE(_polygons); ++i) {
+ if (_polygons[i].id == id) {
+ wp = &_polygons[i];
+ break;
+ }
+ }
+ if (!wp) {
+ error("Polygon %d is not defined", id);
+ }
+ if (wp->numVerts != 5) {
+ error("Invalid point count %d for Polygon %d", wp->numVerts, id);
+ }
+
+ drawWizPolygonTransform(resNum, state, wp->vert, flags, shadow, dstResNum, palette);
+}
+
+void Wiz::drawWizPolygonTransform(int resNum, int state, Common::Point *wp, int flags, int shadow, int dstResNum, int palette) {
+ debug(2, "drawWizPolygonTransform(resNum %d, flags 0x%X, shadow %d dstResNum %d palette %d)", resNum, flags, shadow, dstResNum, palette);
+ int i;
+
+ if (flags & 0x800000) {
+ warning("0x800000 flags not supported");
+ return;
+ }
+
+ const Common::Rect *r = NULL;
+ uint8 *srcWizBuf = drawWizImage(resNum, state, 0, 0, 0, shadow, 0, r, kWIFBlitToMemBuffer, 0, palette);
+ if (srcWizBuf) {
+ uint8 *dst;
+ int32 dstw, dsth, dstpitch, wizW, wizH;
+ VirtScreen *pvs = &_vm->virtscr[kMainVirtScreen];
+ int transColor = (_vm->VAR_WIZ_TCOLOR != 0xFF) ? _vm->VAR(_vm->VAR_WIZ_TCOLOR) : 5;
+
+ if (dstResNum) {
+ uint8 *dstPtr = _vm->getResourceAddress(rtImage, dstResNum);
+ assert(dstPtr);
+ dst = _vm->findWrappedBlock(MKID('WIZD'), dstPtr, 0, 0);
+ assert(dst);
+ getWizImageDim(dstResNum, 0, dstw, dsth);
+ dstpitch = dstw;
+ } else {
+ if (flags & kWIFMarkBufferDirty) {
+ dst = pvs->getPixels(0, 0);
+ } else {
+ dst = pvs->getBackPixels(0, 0);
+ }
+ dstw = pvs->w;
+ dsth = pvs->h;
+ dstpitch = pvs->pitch;
+ }
+
+ getWizImageDim(resNum, state, wizW, wizH);
+
+ Common::Point bbox[4];
+ bbox[0].x = 0;
+ bbox[0].y = 0;
+ bbox[1].x = wizW - 1;
+ bbox[1].y = 0;
+ bbox[2].x = wizW - 1;
+ bbox[2].y = wizH - 1;
+ bbox[3].x = 0;
+ bbox[3].y = wizH - 1;
+
+ int16 xmin_p, xmax_p, ymin_p, ymax_p;
+ xmin_p = ymin_p = (int16)0x7FFF;
+ xmax_p = ymax_p = (int16)0x8000;
+
+ for (i = 0; i < 4; ++i) {
+ xmin_p = MIN(wp[i].x, xmin_p);
+ xmax_p = MAX(wp[i].x, xmax_p);
+ ymin_p = MIN(wp[i].y, ymin_p);
+ ymax_p = MAX(wp[i].y, ymax_p);
+ }
+
+ int16 xmin_b, xmax_b, ymin_b, ymax_b;
+ xmin_b = ymin_b = (int16)0x7FFF;
+ xmax_b = ymax_b = (int16)0x8000;
+
+ for (i = 0; i < 4; ++i) {
+ xmin_b = MIN(bbox[i].x, xmin_b);
+ xmax_b = MAX(bbox[i].x, xmax_b);
+ ymin_b = MIN(bbox[i].y, ymin_b);
+ ymax_b = MAX(bbox[i].y, ymax_b);
+ }
+
+ PolygonDrawData pdd(ymax_p - ymin_p + 1);
+ pdd.mat[0].x = xmin_p;
+ pdd.mat[0].y = ymin_p;
+ pdd.mat[1].x = xmax_p;
+ pdd.mat[1].y = ymax_p;
+ pdd.mat[2].x = xmin_b;
+ pdd.mat[2].y = ymin_b;
+ pdd.mat[3].x = xmax_b;
+ pdd.mat[3].y = ymax_b;
+
+ // precompute the transformation which remaps 'bbox' pixels to 'wp'
+ for (i = 0; i < 3; ++i) {
+ pdd.transform(&wp[i], &wp[i + 1], &bbox[i], &bbox[i + 1]);
+ }
+ pdd.transform(&wp[3], &wp[0], &bbox[3], &bbox[0]);
+
+ pdd.rAreasNum = 0;
+ PolygonDrawData::ResultArea *pra = &pdd.ra[0];
+ int32 yoff = pdd.mat[0].y * dstpitch;
+ int16 y_start = pdd.mat[0].y;
+ for (i = 0; i < pdd.pAreasNum; ++i) {
+ PolygonDrawData::PolygonArea *ppa = &pdd.pa[i];
+ if (y_start >= 0 && y_start < dsth) {
+ int16 x1 = ppa->xmin;
+ if (x1 < 0) {
+ x1 = 0;
+ }
+ int16 x2 = ppa->xmax;
+ if (x2 >= dstw) {
+ x2 = dstw - 1;
+ }
+ int16 w = x2 - x1 + 1;
+ if (w > 0) {
+ int16 width = ppa->xmax - ppa->xmin + 1;
+ pra->x_step = ((ppa->x2 - ppa->x1) << 16) / width;
+ pra->y_step = ((ppa->y2 - ppa->y1) << 16) / width;
+ pra->dst_offs = yoff + x1;
+ pra->w = w;
+ pra->x_s = ppa->x1 << 16;
+ pra->y_s = ppa->y1 << 16;
+ int16 tmp = x1 - ppa->xmin;
+ if (tmp != 0) {
+ pra->x_s += pra->x_step * tmp;
+ pra->y_s += pra->y_step * tmp;
+ }
+ ++pra;
+ ++pdd.rAreasNum;
+ }
+ }
+ ++ppa;
+ yoff += dstpitch;
+ ++y_start;
+ }
+
+ pra = &pdd.ra[0];
+ for (i = 0; i < pdd.rAreasNum; ++i, ++pra) {
+ uint8 *dstPtr = dst + pra->dst_offs;
+ int32 w = pra->w;
+ int32 x_acc = pra->x_s;
+ int32 y_acc = pra->y_s;
+ while (--w) {
+ int32 src_offs = (y_acc >> 16) * wizW + (x_acc >> 16);
+ assert(src_offs < wizW * wizH);
+ x_acc += pra->x_step;
+ y_acc += pra->y_step;
+ if (transColor == -1 || transColor != srcWizBuf[src_offs]) {
+ *dstPtr = srcWizBuf[src_offs];
+ }
+ dstPtr++;
+ }
+ }
+
+ Common::Rect bound(xmin_p, ymin_p, xmax_p + 1, ymax_p + 1);
+ if (flags & kWIFMarkBufferDirty) {
+ _vm->markRectAsDirty(kMainVirtScreen, bound);
+ } else {
+ _vm->gdi.copyVirtScreenBuffers(bound);
+ }
+
+ free(srcWizBuf);
+ }
+}
+
+void Wiz::flushWizBuffer() {
+ for (int i = 0; i < _imagesNum; ++i) {
+ WizImage *pwi = &_images[i];
+ if (pwi->flags & kWIFIsPolygon) {
+ drawWizPolygon(pwi->resNum, pwi->state, pwi->x1, pwi->flags, pwi->shadow, 0, pwi->palette);
+ } else {
+ const Common::Rect *r = NULL;
+ drawWizImage(pwi->resNum, pwi->state, pwi->x1, pwi->y1, pwi->zorder, pwi->shadow, pwi->field_390, r, pwi->flags, 0, pwi->palette);
+ }
+ }
+ _imagesNum = 0;
+}
+
+void Wiz::loadWizCursor(int resId) {
+ int32 x, y;
+ getWizImageSpot(resId, 0, x, y);
+ if (x < 0) {
+ x = 0;
+ } else if (x > 32) {
+ x = 32;
+ }
+ if (y < 0) {
+ y = 0;
+ } else if (y > 32) {
+ y = 32;
+ }
+
+ const Common::Rect *r = NULL;
+ uint8 *cursor = drawWizImage(resId, 0, 0, 0, 0, 0, 0, r, kWIFBlitToMemBuffer, 0, 0);
+ int32 cw, ch;
+ getWizImageDim(resId, 0, cw, ch);
+ _vm->setCursorFromBuffer(cursor, cw, ch, cw);
+ _vm->setCursorHotspot(x, y);
+ free(cursor);
+}
+
+void Wiz::displayWizComplexImage(const WizParameters *params) {
+ int sourceImage = 0;
+ if (params->processFlags & kWPFMaskImg) {
+ sourceImage = params->sourceImage;
+ debug(0, "displayWizComplexImage() unhandled flag 0x80000");
+ }
+ int palette = 0;
+ if (params->processFlags & kWPFPaletteNum) {
+ palette = params->img.palette;
+ }
+ int scale = 256;
+ if (params->processFlags & kWPFScaled) {
+ scale = params->scale;
+ }
+ int rotationAngle = 0;
+ if (params->processFlags & kWPFRotate) {
+ rotationAngle = params->angle;
+ }
+ int state = 0;
+ if (params->processFlags & kWPFNewState) {
+ state = params->img.state;
+ }
+ int flags = 0;
+ if (params->processFlags & kWPFNewFlags) {
+ flags = params->img.flags;
+ }
+ int po_x = 0;
+ int po_y = 0;
+ if (params->processFlags & kWPFSetPos) {
+ po_x = params->img.x1;
+ po_y = params->img.y1;
+ }
+ int shadow = 0;
+ if (params->processFlags & kWPFShadow) {
+ shadow = params->img.shadow;
+ }
+ int field_390 = 0;
+ if (params->processFlags & 0x200000) {
+ field_390 = params->img.field_390;
+ debug(0, "displayWizComplexImage() unhandled flag 0x200000");
+ }
+ const Common::Rect *r = NULL;
+ if (params->processFlags & kWPFClipBox) {
+ r = &params->box;
+ }
+ int dstResNum = 0;
+ if (params->processFlags & kWPFDstResNum) {
+ dstResNum = params->dstResNum;
+ }
+ if (params->processFlags & kWPFRemapPalette) {
+ remapWizImagePal(params);
+ flags |= kWIFRemapPalette;
+ }
+
+ if (_vm->_fullRedraw && dstResNum == 0) {
+ if (sourceImage != 0 || (params->processFlags & (kWPFScaled | kWPFRotate)))
+ error("Can't do this command in the enter script.");
+
+ assert(_imagesNum < ARRAYSIZE(_images));
+ WizImage *pwi = &_images[_imagesNum];
+ pwi->resNum = params->img.resNum;
+ pwi->x1 = po_x;
+ pwi->y1 = po_y;
+ pwi->zorder = params->img.zorder;
+ pwi->state = state;
+ pwi->flags = flags;
+ pwi->shadow = shadow;
+ pwi->field_390 = field_390;
+ pwi->palette = palette;
+ ++_imagesNum;
+ } else {
+ if (sourceImage != 0) {
+ // TODO
+ } else if (params->processFlags & (kWPFScaled | kWPFRotate)) {
+ drawWizComplexPolygon(params->img.resNum, state, po_x, po_y, shadow, rotationAngle, scale, r, flags, dstResNum, palette);
+ } else {
+ if (flags & kWIFIsPolygon) {
+ drawWizPolygon(params->img.resNum, state, po_x, flags, shadow, dstResNum, palette); // XXX , VAR(VAR_WIZ_TCOLOR));
+ } else {
+ drawWizImage(params->img.resNum, state, po_x, po_y, params->img.zorder, shadow, field_390, r, flags, dstResNum, palette);
+ }
+ }
+ }
+}
+
+void Wiz::createWizEmptyImage(const WizParameters *params) {
+ int img_w = 640;
+ if (params->processFlags & kWPFUseDefImgWidth) {
+ img_w = params->resDefImgW;
+ }
+ int img_h = 480;
+ if (params->processFlags & kWPFUseDefImgHeight) {
+ img_h = params->resDefImgH;
+ }
+ int img_x = 0;
+ int img_y = 0;
+ if (params->processFlags & 1) {
+ img_x = params->img.x1;
+ img_y = params->img.y1;
+ }
+ const uint16 flags = 0xB;
+ int res_size = 0x1C;
+ if (flags & 1) {
+ res_size += 0x308;
+ }
+ if (flags & 2) {
+ res_size += 0x10;
+ }
+ if (flags & 8) {
+ res_size += 0x10C;
+ }
+ res_size += 8 + img_w * img_h;
+
+ const uint8 *palPtr;
+ if (_vm->_heversion >= 99) {
+ palPtr = _vm->_hePalettes + 1024;
+ } else {
+ palPtr = _vm->_currentPalette;
+ }
+ uint8 *res_data = _vm->res.createResource(rtImage, params->img.resNum, res_size);
+ if (!res_data) {
+ _vm->VAR(119) = -1;
+ } else {
+ _vm->VAR(119) = 0;
+ WRITE_BE_UINT32(res_data, 'AWIZ'); res_data += 4;
+ WRITE_BE_UINT32(res_data, res_size); res_data += 4;
+ WRITE_BE_UINT32(res_data, 'WIZH'); res_data += 4;
+ WRITE_BE_UINT32(res_data, 0x14); res_data += 4;
+ WRITE_LE_UINT32(res_data, 0); res_data += 4;
+ WRITE_LE_UINT32(res_data, img_w); res_data += 4;
+ WRITE_LE_UINT32(res_data, img_h); res_data += 4;
+ if (flags & 1) {
+ WRITE_BE_UINT32(res_data, 'RGBS'); res_data += 4;
+ WRITE_BE_UINT32(res_data, 0x308); res_data += 4;
+ memcpy(res_data, palPtr, 0x300); res_data += 0x300;
+ }
+ if (flags & 2) {
+ WRITE_BE_UINT32(res_data, 'SPOT'); res_data += 4;
+ WRITE_BE_UINT32(res_data, 0x10); res_data += 4;
+ WRITE_BE_UINT32(res_data, img_x); res_data += 4;
+ WRITE_BE_UINT32(res_data, img_y); res_data += 4;
+ }
+ if (flags & 8) {
+ WRITE_BE_UINT32(res_data, 'RMAP'); res_data += 4;
+ WRITE_BE_UINT32(res_data, 0x10C); res_data += 4;
+ WRITE_BE_UINT32(res_data, 0); res_data += 4;
+ for (int i = 0; i < 256; ++i) {
+ *res_data++ = i;
+ }
+ }
+ WRITE_BE_UINT32(res_data, 'WIZD'); res_data += 4;
+ WRITE_BE_UINT32(res_data, 8 + img_w * img_h); res_data += 4;
+ }
+ _vm->res.setModified(rtImage, params->img.resNum);
+}
+
+void Wiz::fillWizRect(const WizParameters *params) {
+ int state = 0;
+ if (params->processFlags & kWPFNewState) {
+ state = params->img.state;
+ }
+ uint8 *dataPtr = _vm->getResourceAddress(rtImage, params->img.resNum);
+ if (dataPtr) {
+ uint8 *wizh = _vm->findWrappedBlock(MKID('WIZH'), dataPtr, state, 0);
+ assert(wizh);
+ int c = READ_LE_UINT32(wizh + 0x0);
+ int w = READ_LE_UINT32(wizh + 0x4);
+ int h = READ_LE_UINT32(wizh + 0x8);
+ assert(c == 0);
+ Common::Rect areaRect, imageRect(w, h);
+ if (params->processFlags & kWPFClipBox) {
+ if (!imageRect.intersects(params->box)) {
+ return;
+ }
+ imageRect.clip(params->box);
+ }
+ if (params->processFlags & kWPFClipBox2) {
+ areaRect = params->box2;
+ } else {
+ areaRect = imageRect;
+ }
+ uint8 color = _vm->VAR(93);
+ if (params->processFlags & kWPFFillColor) {
+ color = params->fillColor;
+ }
+ if (areaRect.intersects(imageRect)) {
+ areaRect.clip(imageRect);
+ uint8 *wizd = _vm->findWrappedBlock(MKID('WIZD'), dataPtr, state, 0);
+ assert(wizd);
+ int dx = areaRect.width();
+ int dy = areaRect.height();
+ wizd += areaRect.top * w + areaRect.left;
+ while (dy--) {
+ memset(wizd, color, dx);
+ wizd += w;
+ }
+ }
+ }
+ _vm->res.setModified(rtImage, params->img.resNum);
+}
+
+void Wiz::fillWizLine(const WizParameters *params) {
+ if (params->processFlags & kWPFClipBox2) {
+ int state = 0;
+ if (params->processFlags & kWPFNewState) {
+ state = params->img.state;
+ }
+ uint8 *dataPtr = _vm->getResourceAddress(rtImage, params->img.resNum);
+ if (dataPtr) {
+ uint8 *wizh = _vm->findWrappedBlock(MKID('WIZH'), dataPtr, state, 0);
+ assert(wizh);
+ int c = READ_LE_UINT32(wizh + 0x0);
+ int w = READ_LE_UINT32(wizh + 0x4);
+ int h = READ_LE_UINT32(wizh + 0x8);
+ assert(c == 0);
+ Common::Rect imageRect(w, h);
+ if (params->processFlags & kWPFClipBox) {
+ if (!imageRect.intersects(params->box)) {
+ return;
+ }
+ imageRect.clip(params->box);
+ }
+ uint8 color = _vm->VAR(93);
+ if (params->processFlags & kWPFFillColor) {
+ color = params->fillColor;
+ }
+ uint8 *wizd = _vm->findWrappedBlock(MKID('WIZD'), dataPtr, state, 0);
+ assert(wizd);
+ int x1 = params->box2.left;
+ int y1 = params->box2.top;
+ int x2 = params->box2.right;
+ int y2 = params->box2.bottom;
+
+ int dx = x2 - x1;
+ int incx = 0;
+ if (dx > 0) {
+ incx = 1;
+ } else if (dx < 0) {
+ incx = -1;
+ }
+ int dy = y2 - y1;
+ int incy = 0;
+ if (dy > 0) {
+ incy = 1;
+ } else if (dy < 0) {
+ incy = -1;
+ }
+
+ dx = ABS(x2 - x1);
+ dy = ABS(y2 - y1);
+
+ if (imageRect.contains(x1, y1)) {
+ *(wizd + y1 * w + x1) = color;
+ }
+
+ if (dx >= dy) {
+ int step1_y = (dy - dx) * 2;
+ int step2_y = dy * 2;
+ int accum_y = dy * 2 - dx;
+ while (x1 != x2) {
+ if (accum_y <= 0) {
+ accum_y += step2_y;
+ } else {
+ accum_y += step1_y;
+ y1 += incy;
+ }
+ x1 += incx;
+ if (imageRect.contains(x1, y1)) {
+ *(wizd + y1 * w + x1) = color;
+ }
+ }
+ } else {
+ int step1_x = (dx - dy) * 2;
+ int step2_x = dx * 2;
+ int accum_x = dx * 2 - dy;
+ while (y1 != y2) {
+ if (accum_x <= 0) {
+ accum_x += step2_x;
+ } else {
+ accum_x += step1_x;
+ x1 += incx;
+ }
+ y1 += incy;
+ if (imageRect.contains(x1, y1)) {
+ *(wizd + y1 * w + x1) = color;
+ }
+ }
+ }
+ }
+ }
+ _vm->res.setModified(rtImage, params->img.resNum);
+}
+
+void Wiz::fillWizPixel(const WizParameters *params) {
+ if (params->processFlags & kWPFClipBox2) {
+ int px = params->box2.left;
+ int py = params->box2.top;
+ uint8 *dataPtr = _vm->getResourceAddress(rtImage, params->img.resNum);
+ if (dataPtr) {
+ int state = 0;
+ if (params->processFlags & kWPFNewState) {
+ state = params->img.state;
+ }
+ uint8 *wizh = _vm->findWrappedBlock(MKID('WIZH'), dataPtr, state, 0);
+ assert(wizh);
+ int c = READ_LE_UINT32(wizh + 0x0);
+ int w = READ_LE_UINT32(wizh + 0x4);
+ int h = READ_LE_UINT32(wizh + 0x8);
+ assert(c == 0);
+ Common::Rect imageRect(w, h);
+ if (params->processFlags & kWPFClipBox) {
+ if (!imageRect.intersects(params->box)) {
+ return;
+ }
+ imageRect.clip(params->box);
+ }
+ uint8 color = _vm->VAR(93);
+ if (params->processFlags & kWPFFillColor) {
+ color = params->fillColor;
+ }
+ if (imageRect.contains(px, py)) {
+ uint8 *wizd = _vm->findWrappedBlock(MKID('WIZD'), dataPtr, state, 0);
+ assert(wizd);
+ *(wizd + py * w + px) = color;
+ }
+ }
+ }
+ _vm->res.setModified(rtImage, params->img.resNum);
+}
+
+void Wiz::remapWizImagePal(const WizParameters *params) {
+ int st = (params->processFlags & kWPFNewState) ? params->img.state : 0;
+ int num = params->remapNum;
+ const uint8 *index = params->remapIndex;
+ uint8 *iwiz = _vm->getResourceAddress(rtImage, params->img.resNum);
+ assert(iwiz);
+ uint8 *rmap = _vm->findWrappedBlock(MKID('RMAP'), iwiz, st, 0) ;
+ assert(rmap);
+ WRITE_BE_UINT32(rmap, 0x01234567);
+ while (num--) {
+ uint8 idx = *index++;
+ rmap[4 + idx] = params->remapColor[idx];
+ }
+ _vm->res.setModified(rtImage, params->img.resNum);
+}
+
+void Wiz::processWizImage(const WizParameters *params) {
+ char buf[512];
+ unsigned int i;
+
+ debug(2, "processWizImage: processMode %d", params->processMode);
+ switch (params->processMode) {
+ case 0:
+ // Used in racedemo
+ break;
+ case 1:
+ displayWizComplexImage(params);
+ break;
+ case 2:
+ captureWizImage(params->img.resNum, params->box, (params->img.flags & kWIFBlitToFrontVideoBuffer) != 0, params->compType);
+ break;
+ case 3:
+ if (params->processFlags & kWPFUseFile) {
+ Common::File f;
+
+ // Convert Windows path separators to something more portable
+ strncpy(buf, (const char *)params->filename, 512);
+ for (i = 0; i < strlen(buf); i++) {
+ if (buf[i] == '\\')
+ buf[i] = '/';
+ }
+
+ if (f.open((const char *)buf, Common::File::kFileReadMode)) {
+ uint32 id = f.readUint32LE();
+ if (id == TO_LE_32(MKID('AWIZ')) || id == TO_LE_32(MKID('MULT'))) {
+ uint32 size = f.readUint32BE();
+ f.seek(0, SEEK_SET);
+ byte *p = _vm->res.createResource(rtImage, params->img.resNum, size);
+ if (f.read(p, size) != size) {
+ _vm->res.nukeResource(rtImage, params->img.resNum);
+ error("i/o error when reading '%s'", buf);
+ _vm->VAR(_vm->VAR_GAME_LOADED) = -2;
+ _vm->VAR(119) = -2;
+ } else {
+ _vm->res.setModified(rtImage, params->img.resNum);
+ _vm->VAR(_vm->VAR_GAME_LOADED) = 0;
+ _vm->VAR(119) = 0;
+ }
+ } else {
+ _vm->VAR(_vm->VAR_GAME_LOADED) = -1;
+ _vm->VAR(119) = -1;
+ }
+ f.close();
+ } else {
+ _vm->VAR(_vm->VAR_GAME_LOADED) = -3;
+ _vm->VAR(119) = -3;
+ debug(0, "Unable to open for read '%s'", buf);
+ }
+ }
+ break;
+ case 4:
+ if (params->processFlags & kWPFUseFile) {
+ Common::File f;
+
+ switch(params->fileWriteMode) {
+ case 2:
+ _vm->VAR(119) = -1;
+ break;
+ case 1:
+ // TODO Write image to file
+ break;
+ case 0:
+ // Convert Windows path separators to something more portable
+ strncpy(buf, (const char *)params->filename, 512);
+ for (i = 0; i < strlen(buf); i++) {
+ if (buf[i] == '\\')
+ buf[i] = '/';
+ }
+
+ if (!f.open((const char *)buf, Common::File::kFileWriteMode)) {
+ debug(0, "Unable to open for write '%s'", buf);
+ _vm->VAR(119) = -3;
+ } else {
+ byte *p = _vm->getResourceAddress(rtImage, params->img.resNum);
+ uint32 size = READ_BE_UINT32(p + 4);
+ if (f.write(p, size) != size) {
+ error("i/o error when writing '%s'", params->filename);
+ _vm->VAR(119) = -2;
+ } else {
+ _vm->VAR(119) = 0;
+ }
+ f.close();
+ }
+ break;
+ default:
+ error("processWizImage: processMode 4 unhandled fileWriteMode %d", params->fileWriteMode);
+ }
+ }
+ break;
+ case 6:
+ if (params->processFlags & kWPFRemapPalette) {
+ remapWizImagePal(params);
+ }
+ break;
+ // HE 99+
+ case 7:
+ // Used in PuttsFunShop/SamsFunShop/soccer2004
+ // TODO: Capture polygon
+ _vm->res.setModified(rtImage, params->img.resNum);
+ break;
+ case 8:
+ createWizEmptyImage(params);
+ break;
+ case 9:
+ fillWizRect(params);
+ break;
+ case 10:
+ fillWizLine(params);
+ break;
+ case 11:
+ fillWizPixel(params);
+ break;
+ case 12:
+ fillWizFlood(params);
+ break;
+ case 13:
+ // Used for text in FreddisFunShop/PuttsFunShop/SamsFunShop
+ // TODO: Start Font
+ break;
+ case 14:
+ // Used for text in FreddisFunShop/PuttsFunShop/SamsFunShop
+ // TODO: End Font
+ break;
+ case 15:
+ // Used for text in FreddisFunShop/PuttsFunShop/SamsFunShop
+ // TODO: Create Font
+ break;
+ case 16:
+ // TODO: Render Font String
+ error("Render Font String");
+ break;
+ case 17:
+ // Used in to draw circles in FreddisFunShop/PuttsFunShop/SamsFunShop
+ // TODO: Ellipse
+ _vm->res.setModified(rtImage, params->img.resNum);
+ break;
+ default:
+ error("Unhandled processWizImage mode %d", params->processMode);
+ }
+}
+
+void Wiz::getWizImageDim(int resNum, int state, int32 &w, int32 &h) {
+ uint8 *dataPtr = _vm->getResourceAddress(rtImage, resNum);
+ assert(dataPtr);
+ uint8 *wizh = _vm->findWrappedBlock(MKID('WIZH'), dataPtr, state, 0);
+ assert(wizh);
+ w = READ_LE_UINT32(wizh + 0x4);
+ h = READ_LE_UINT32(wizh + 0x8);
+}
+
+void Wiz::getWizImageSpot(int resId, int state, int32 &x, int32 &y) {
+ uint8 *dataPtr = _vm->getResourceAddress(rtImage, resId);
+ assert(dataPtr);
+ uint8 *spotPtr = _vm->findWrappedBlock(MKID('SPOT'), dataPtr, state, 0);
+ if (spotPtr) {
+ x = READ_LE_UINT32(spotPtr + 0);
+ y = READ_LE_UINT32(spotPtr + 4);
+ } else {
+ x = 0;
+ y = 0;
+ }
+}
+
+int Wiz::getWizImageData(int resNum, int state, int type) {
+ uint8 *dataPtr, *wizh;
+
+ dataPtr = _vm->getResourceAddress(rtImage, resNum);
+ assert(dataPtr);
+
+ switch (type) {
+ case 0:
+ wizh = _vm->findWrappedBlock(MKID('WIZH'), dataPtr, state, 0);
+ assert(wizh);
+ return READ_LE_UINT32(wizh + 0x0);
+ case 1:
+ return (_vm->findWrappedBlock(MKID('RGBS'), dataPtr, state, 0) != NULL) ? 1 : 0;
+ case 2:
+ return (_vm->findWrappedBlock(MKID('RMAP'), dataPtr, state, 0) != NULL) ? 1 : 0;
+ case 3:
+ return (_vm->findWrappedBlock(MKID('TRNS'), dataPtr, state, 0) != NULL) ? 1 : 0;
+ case 4:
+ return (_vm->findWrappedBlock(MKID('XMAP'), dataPtr, state, 0) != NULL) ? 1 : 0;
+ default:
+ error("getWizImageData: Unknown type %d", type);
+ }
+}
+
+int Wiz::getWizImageStates(int resNum) {
+ const uint8 *dataPtr = _vm->getResourceAddress(rtImage, resNum);
+ assert(dataPtr);
+ if (READ_UINT32(dataPtr) == MKID('MULT')) {
+ const byte *offs, *wrap;
+
+ wrap = _vm->findResource(MKID('WRAP'), dataPtr);
+ if (wrap == NULL)
+ return 1;
+
+ offs = _vm->findResourceData(MKID('OFFS'), wrap);
+ if (offs == NULL)
+ return 1;
+
+ return _vm->getResourceDataSize(offs) / 4;
+ } else {
+ return 1;
+ }
+}
+
+int Wiz::isWizPixelNonTransparent(int resNum, int state, int x, int y, int flags) {
+ int ret = 0;
+ uint8 *data = _vm->getResourceAddress(rtImage, resNum);
+ assert(data);
+ uint8 *wizh = _vm->findWrappedBlock(MKID('WIZH'), data, state, 0);
+ assert(wizh);
+ int c = READ_LE_UINT32(wizh + 0x0);
+ int w = READ_LE_UINT32(wizh + 0x4);
+ int h = READ_LE_UINT32(wizh + 0x8);
+ uint8 *wizd = _vm->findWrappedBlock(MKID('WIZD'), data, state, 0);
+ assert(wizd);
+ if (x >= 0 && x < w && y >= 0 && y < h) {
+ if (flags & kWIFFlipX) {
+ x = w - x - 1;
+ }
+ if (flags & kWIFFlipY) {
+ y = h - y - 1;
+ }
+ switch (c) {
+ case 0:
+ if (_vm->_heversion >= 99) {
+ ret = getRawWizPixelColor(wizd, x, y, w, h, _vm->VAR(_vm->VAR_WIZ_TCOLOR)) != _vm->VAR(_vm->VAR_WIZ_TCOLOR) ? 1 : 0;
+ } else {
+ ret = 0;
+ }
+ break;
+ case 1:
+ ret = isWizPixelNonTransparent(wizd, x, y, w, h);
+ break;
+ case 2:
+ // Used baseball2003
+ debug(0, "isWizPixelNonTransparent: Unhandled wiz compression type %d", c);
+ break;
+ default:
+ error("isWizPixelNonTransparent: Unhandled wiz compression type %d", c);
+ break;
+ }
+ }
+ return ret;
+}
+
+uint8 Wiz::getWizPixelColor(int resNum, int state, int x, int y, int flags) {
+ uint8 color;
+ uint8 *data = _vm->getResourceAddress(rtImage, resNum);
+ assert(data);
+ uint8 *wizh = _vm->findWrappedBlock(MKID('WIZH'), data, state, 0);
+ assert(wizh);
+ int c = READ_LE_UINT32(wizh + 0x0);
+ int w = READ_LE_UINT32(wizh + 0x4);
+ int h = READ_LE_UINT32(wizh + 0x8);
+ uint8 *wizd = _vm->findWrappedBlock(MKID('WIZD'), data, state, 0);
+ assert(wizd);
+ switch (c) {
+ case 0:
+ if (_vm->_heversion >= 99) {
+ color = getRawWizPixelColor(wizd, x, y, w, h, _vm->VAR(_vm->VAR_WIZ_TCOLOR));
+ } else {
+ color = _vm->VAR(_vm->VAR_WIZ_TCOLOR);
+ }
+ break;
+ case 1:
+ color = getWizPixelColor(wizd, x, y, w, h, _vm->VAR(_vm->VAR_WIZ_TCOLOR));
+ break;
+ default:
+ error("getWizPixelColor: Unhandled wiz compression type %d", c);
+ break;
+ }
+ return color;
+}
+
+int ScummEngine_v90he::computeWizHistogram(int resNum, int state, int x, int y, int w, int h) {
+ writeVar(0, 0);
+ defineArray(0, kDwordArray, 0, 0, 0, 255);
+ if (readVar(0) != 0) {
+ Common::Rect rCapt(x, y, w + 1, h + 1);
+ uint8 *data = getResourceAddress(rtImage, resNum);
+ assert(data);
+ uint8 *wizh = findWrappedBlock(MKID('WIZH'), data, state, 0);
+ assert(wizh);
+ int c = READ_LE_UINT32(wizh + 0x0);
+ w = READ_LE_UINT32(wizh + 0x4);
+ h = READ_LE_UINT32(wizh + 0x8);
+ Common::Rect rWiz(w, h);
+ uint8 *wizd = findWrappedBlock(MKID('WIZD'), data, state, 0);
+ assert(wizd);
+ if (rCapt.intersects(rWiz)) {
+ rCapt.clip(rWiz);
+ uint32 histogram[256];
+ memset(histogram, 0, sizeof(histogram));
+ switch (c) {
+ case 0:
+ _wiz->computeRawWizHistogram(histogram, wizd, w, rCapt);
+ break;
+ case 1:
+ _wiz->computeWizHistogram(histogram, wizd, rCapt);
+ break;
+ default:
+ error("computeWizHistogram: Unhandled wiz compression type %d", c);
+ break;
+ }
+ for (int i = 0; i < 256; ++i) {
+ writeArray(0, 0, i, histogram[i]);
+ }
+ }
+ }
+ return readVar(0);
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/he/wiz_he.h b/engines/scumm/he/wiz_he.h
new file mode 100644
index 0000000000..86d3e97721
--- /dev/null
+++ b/engines/scumm/he/wiz_he.h
@@ -0,0 +1,211 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#if !defined(WIZ_HE_H) && !defined(DISABLE_HE)
+#define WIZ_HE_H
+
+#include "common/rect.h"
+
+namespace Scumm {
+
+struct WizPolygon {
+ Common::Point vert[5];
+ Common::Rect bound;
+ int id;
+ int numVerts;
+ bool flag;
+};
+
+struct WizImage {
+ int resNum;
+ int x1;
+ int y1;
+ int zorder;
+ int state;
+ int flags;
+ int shadow;
+ int field_390;
+ int palette;
+};
+
+struct WizParameters {
+ int field_0;
+ byte filename[260];
+ Common::Rect box;
+ int processFlags;
+ int processMode;
+ int field_11C;
+ int field_120;
+ int field_124;
+ int field_128;
+ int field_12C;
+ int field_130;
+ int field_134;
+ int field_138;
+ int compType;
+ int fileWriteMode;
+ int angle;
+ int scale;
+ int field_164;
+ int field_168;
+ int resDefImgW;
+ int resDefImgH;
+ int sourceImage;
+ int field_180;
+ int field_184;
+ uint8 remapColor[256];
+ uint8 remapIndex[256];
+ int remapNum;
+ int dstResNum;
+ byte fillColor;
+ byte string1[4096];
+ byte string2[4096];
+ int field_2399;
+ int field_239D;
+ int field_23A1;
+ int field_23A5;
+ int field_23A9;
+ int field_23AD;
+ int field_23B1;
+ int field_23B5;
+ int field_23B9;
+ int field_23BD;
+ int field_23C1;
+ int field_23C5;
+ int field_23C9;
+ int field_23CD;
+ Common::Rect box2;
+ int field_23DE;
+ int spriteId;
+ int spriteGroup;
+ int field_23EA;
+ WizImage img;
+};
+
+enum WizImageFlags {
+ kWIFHasPalette = 0x1,
+ kWIFRemapPalette = 0x2,
+ kWIFPrint = 0x4,
+ kWIFBlitToFrontVideoBuffer = 0x8,
+ kWIFMarkBufferDirty = 0x10,
+ kWIFBlitToMemBuffer = 0x20,
+ kWIFIsPolygon = 0x40,
+ kWIFFlipX = 0x400,
+ kWIFFlipY = 0x800
+};
+
+enum WizProcessFlags {
+ kWPFSetPos = 0x1,
+ kWPFShadow = 0x4,
+ kWPFScaled = 0x8,
+ kWPFRotate = 0x10,
+ kWPFNewFlags = 0x20,
+ kWPFRemapPalette = 0x40,
+ kWPFClipBox = 0x200,
+ kWPFNewState = 0x400,
+ kWPFUseFile = 0x800,
+ kWPFUseDefImgWidth = 0x2000,
+ kWPFUseDefImgHeight = 0x4000,
+ kWPFPaletteNum = 0x8000,
+ kWPFDstResNum = 0x10000,
+ kWPFFillColor = 0x20000,
+ kWPFClipBox2 = 0x40000,
+ kWPFMaskImg = 0x80000
+};
+
+class ScummEngine_v70he;
+
+class Wiz {
+public:
+ enum {
+ NUM_POLYGONS = 200,
+ NUM_IMAGES = 255
+ };
+
+ WizImage _images[NUM_IMAGES];
+ uint16 _imagesNum;
+ WizPolygon _polygons[NUM_POLYGONS];
+
+ Wiz(ScummEngine_v70he *vm);
+
+ void clearWizBuffer();
+ Common::Rect _rectOverride;
+ bool _rectOverrideEnabled;
+
+ void polygonClear();
+ void polygonLoad(const uint8 *polData);
+ void polygonStore(int id, bool flag, int vert1x, int vert1y, int vert2x, int vert2y, int vert3x, int vert3y, int vert4x, int vert4y);
+ void polygonCalcBoundBox(Common::Point *vert, int numVerts, Common::Rect & bound);
+ void polygonErase(int fromId, int toId);
+ int polygonHit(int id, int x, int y);
+ bool polygonDefined(int id);
+ bool polygonContains(const WizPolygon &pol, int x, int y);
+ void polygonRotatePoints(Common::Point *pts, int num, int alpha);
+ void polygonTransform(int resNum, int state, int po_x, int po_y, int angle, int zoom, Common::Point *vert);
+
+ void createWizEmptyImage(const WizParameters *params);
+ void fillWizRect(const WizParameters *params);
+ void fillWizLine(const WizParameters *params);
+ void fillWizPixel(const WizParameters *params);
+ void fillWizFlood(const WizParameters *params);
+ void remapWizImagePal(const WizParameters *params);
+
+ void getWizImageDim(int resNum, int state, int32 &w, int32 &h);
+ int getWizImageStates(int resnum);
+ int isWizPixelNonTransparent(int resnum, int state, int x, int y, int flags);
+ uint8 getWizPixelColor(int resnum, int state, int x, int y, int flags);
+ int getWizImageData(int resNum, int state, int type);
+
+ void flushWizBuffer();
+
+ void getWizImageSpot(int resId, int state, int32 &x, int32 &y);
+ void loadWizCursor(int resId);
+
+ void captureWizImage(int resNum, const Common::Rect& r, bool frontBuffer, int compType);
+ void displayWizComplexImage(const WizParameters *params);
+ void displayWizImage(WizImage *pwi);
+ void processWizImage(const WizParameters *params);
+
+ uint8 *drawWizImage(int resNum, int state, int x1, int y1, int zorder, int shadow, int field_390, const Common::Rect *clipBox, int flags, int dstResNum, int palette);
+ void drawWizPolygon(int resNum, int state, int id, int flags, int shadow, int dstResNum, int palette);
+ void drawWizComplexPolygon(int resNum, int state, int po_x, int po_y, int shadow, int angle, int zoom, const Common::Rect *r, int flags, int dstResNum, int palette);
+ void drawWizPolygonTransform(int resNum, int state, Common::Point *wp, int flags, int shadow, int dstResNum, int palette);
+
+ static void copyAuxImage(uint8 *dst1, uint8 *dst2, const uint8 *src, int dstw, int dsth, int srcx, int srcy, int srcw, int srch);
+ static void copyWizImage(uint8 *dst, const uint8 *src, int dstw, int dsth, int srcx, int srcy, int srcw, int srch, const Common::Rect *rect, int flags = 0, const uint8 *palPtr = NULL, const uint8 *xmapPtr = NULL);
+ static void copyRawWizImage(uint8 *dst, const uint8 *src, int dstw, int dsth, int srcx, int srcy, int srcw, int srch, const Common::Rect *rect, int flags, const uint8 *palPtr, int transColor);
+ static void copyRaw16BitWizImage(uint8 *dst, const uint8 *src, int dstw, int dsth, int srcx, int srcy, int srcw, int srch, const Common::Rect *rect, int flags, const uint8 *palPtr, int transColor);
+ static void decompressWizImage(uint8 *dst, int dstPitch, const Common::Rect &dstRect, const uint8 *src, const Common::Rect &srcRect, int flags, const uint8 *palPtr = NULL, const uint8 *xmapPtr = NULL);
+ int isWizPixelNonTransparent(const uint8 *data, int x, int y, int w, int h);
+ uint8 getWizPixelColor(const uint8 *data, int x, int y, int w, int h, uint8 color);
+ uint8 getRawWizPixelColor(const uint8 *data, int x, int y, int w, int h, uint8 color);
+ void computeWizHistogram(uint32 *histogram, const uint8 *data, const Common::Rect& rCapt);
+ void computeRawWizHistogram(uint32 *histogram, const uint8 *data, int srcPitch, const Common::Rect& rCapt);
+
+private:
+ ScummEngine_v70he *_vm;
+};
+
+} // End of namespace Scumm
+
+#endif