diff options
Diffstat (limited to 'engines/scumm/he')
-rw-r--r-- | engines/scumm/he/floodfill_he.cpp | 293 | ||||
-rw-r--r-- | engines/scumm/he/floodfill_he.h | 67 | ||||
-rw-r--r-- | engines/scumm/he/intern_he.h | 606 | ||||
-rw-r--r-- | engines/scumm/he/logic_he.cpp | 836 | ||||
-rw-r--r-- | engines/scumm/he/logic_he.h | 124 | ||||
-rw-r--r-- | engines/scumm/he/palette_he.cpp | 317 | ||||
-rw-r--r-- | engines/scumm/he/resource_v7he.cpp | 1912 | ||||
-rw-r--r-- | engines/scumm/he/resource_v7he.h | 556 | ||||
-rw-r--r-- | engines/scumm/he/script_v100he.cpp | 2978 | ||||
-rw-r--r-- | engines/scumm/he/script_v6he.cpp | 1276 | ||||
-rw-r--r-- | engines/scumm/he/script_v72he.cpp | 2368 | ||||
-rw-r--r-- | engines/scumm/he/script_v7he.cpp | 1153 | ||||
-rw-r--r-- | engines/scumm/he/script_v80he.cpp | 811 | ||||
-rw-r--r-- | engines/scumm/he/script_v90he.cpp | 2636 | ||||
-rw-r--r-- | engines/scumm/he/sound_he.cpp | 516 | ||||
-rw-r--r-- | engines/scumm/he/sprite_he.cpp | 1440 | ||||
-rw-r--r-- | engines/scumm/he/sprite_he.h | 222 | ||||
-rw-r--r-- | engines/scumm/he/wiz_he.cpp | 2088 | ||||
-rw-r--r-- | engines/scumm/he/wiz_he.h | 211 |
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 = ¶ms->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 |