/* ScummVM - Scumm Interpreter * Copyright (C) 2001 Ludvig Strigeus * Copyright (C) 2001-2005 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * $Header$ * */ #include "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/intern.h" #include "scumm/object.h" #include "scumm/resource.h" #include "scumm/resource_v7he.h" #include "scumm/scumm.h" #include "scumm/sound.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_addMessageToStack), 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_getSoundElapsedTimeOfSound), 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(o70_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(o60_wait), OPCODE(o6_getActorScaleX), OPCODE(o6_getActorAnimCounter1), /* AC */ OPCODE(o6_invalid), OPCODE(o6_isAnyOf), OPCODE(o70_quitPauseRestart), OPCODE(o6_isActorInBox), /* B0 */ OPCODE(o6_delay), OPCODE(o6_delaySeconds), OPCODE(o6_delayMinutes), OPCODE(o6_stopSentence), /* B4 */ OPCODE(o6_printLine), OPCODE(o6_printCursor), 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_unknownCF), /* 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(o60_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(o72_checkGlobQueue), 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_setWindowCaption), 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(5,"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]; writeVar(array, id); size *= dim2end - dim2start + 1; size *= dim1end - dim1start + 1; size >>= 3; ah = (ArrayHeader *)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(5, "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(5, "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); } } void ScummEngine_v72he::copyScriptString(byte *dst) { int i = 0; byte b; int array = pop(); if (array == -1) { int len = resStrLen(_stringBuffer) + 1; while (len--) *dst++ = _stringBuffer[i++]; } else { writeVar(0, array); while ((b = readArray(0, 0, i)) != 0) { *dst++ = b; i++; } } *dst = 0; } void ScummEngine_v72he::decodeScriptString(byte *dst, bool scriptString) { int args[31]; int num = 0, len, val; byte chr, string[256]; memset(string, 0, sizeof(string)); val = getStackList(args, ARRAYSIZE(args)); args[val] = pop(); if (scriptString) { addMessageToStack(_scriptPointer, string, sizeof(string)); len = resStrLen(_scriptPointer); _scriptPointer += len + 1; } else { copyScriptString(string); len = resStrLen(string) + 1; } while (len--) { chr = string[num++]; if (chr == '%') { chr = string[num++]; switch(chr) { case 'b': // byte break; case 'c': *dst++ = args[val--]; break; case 'd': dst += snprintf((char *)dst, 10, "%d", args[val--]); break; case 's': dst += addStringToStack(dst, 512, args[val--]); break; case 'x': // hexadecimal break; default: error("decodeScriptString: Unknown type %d", chr); } continue; } *dst++ = chr; } *dst = 0; } byte *ScummEngine_v72he::heFindResourceData(uint32 tag, byte *ptr) { ptr = heFindResource(tag, ptr); if (ptr == NULL) return NULL; return ptr + _resourceHeaderSize; } byte *ScummEngine_v72he::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_v72he::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); } } void ScummEngine_v72he::o72_pushDWord() { int a; if (*_lastCodePtr + sizeof(MemBlkHeader) != _scriptOrgPointer) { uint32 oldoffs = _scriptPointer - _scriptOrgPointer; getScriptBaseAddress(); _scriptPointer = _scriptOrgPointer + oldoffs; } a = READ_LE_UINT32(_scriptPointer); _scriptPointer += 4; push(a); } void ScummEngine_v72he::o72_addMessageToStack() { _stringLength = resStrLen(_scriptPointer) + 1; addMessageToStack(_scriptPointer, _stringBuffer, _stringLength); // Filter out pointless trace messages, which often flood if (strcmp((char *)_stringBuffer, "no trace") && strcmp((char *)_stringBuffer, "trace on")) debug(1,"o72_addMessageToStack(\"%s\")", _scriptPointer); _scriptPointer += _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); } void ScummEngine_v72he::o72_getObjectImageY() { int object = pop(); int objnum = getObjectIndex(object); if (objnum == -1) { push(0); return; } push(_objs[objnum].y_pos); } void ScummEngine_v72he::o72_captureWizImage() { Common::Rect grab; grab.bottom = pop() + 1; grab.right = pop() + 1; grab.top = pop(); grab.left = pop(); captureWizImage(rtImage, pop(), grab, false, true); } void ScummEngine_v72he::o72_getTimer() { int timer = pop(); int cmd = fetchScriptByte(); if (cmd == 10 || cmd == 50) { checkRange(3, 1, timer, "o72_getTimer: Timer %d out of range(%d)"); int diff = _system->getMillis() - _timers[timer]; push(diff); } else { push(0); } } void ScummEngine_v72he::o72_setTimer() { int timer = pop(); int cmd = fetchScriptByte(); if (cmd == 158 || cmd == 61) { checkRange(3, 1, timer, "o72_setTimer: Timer %d out of range(%d)"); _timers[timer] = _system->getMillis(); } else { error("TIMER command %d?", cmd); } } void ScummEngine_v72he::o72_getSoundElapsedTimeOfSound() { int snd = pop(); push(_mixer->getSoundElapsedTimeOfSoundID(snd) * 10); debug(1,"o72_getSoundElapsedTimeOfSound (%d)", 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; drawWizImage(rtImage, &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 op; byte filename[100]; op = fetchScriptByte(); switch (op) { 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, _roomResource); break; case 220: a = pop(); b = pop(); copyPalColor(a, b); break; case 221: copyScriptString(filename); _saveLoadFlag = pop(); _saveLoadSlot = 1; _saveTemporaryState = true; break; case 234: b = pop(); a = pop(); swapObjects(a, b); break; case 236: b = pop(); a = pop(); setPalette(a, b); break; default: error("o72_roomOps: default case %d", op); } } void ScummEngine_v72he::o72_actorOps() { Actor *a; int i, j, k; int args[32]; byte subOp; byte string[256]; 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); } debug(1,"o72_actorOps: case 21 (%d)", k); break; case 24: // HE 80+ k = pop(); a->_heNoTalkAnimation = 1; a->setTalkCondition(k); debug(1,"o72_actorOps: case 24 (%d)", k); break; case 43: // HE 90+ // Uses 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+ i = pop(); j = pop(); a->putActor(i, j, a->_room); break; case 68: // HE 90+ k = pop(); debug(1,"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); 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->_shadowMode = pop(); a->_needRedraw = true; debug(0, "Set actor XMAP idx to %d", a->_shadowMode); 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: { int top_actor = a->_top; int bottom_actor = a->_bottom; a->_drawToBackBuf = true; a->_needRedraw = true; a->drawActorCostume(); a->_drawToBackBuf = false; a->_needRedraw = true; a->drawActorCostume(); a->_needRedraw = false; if (a->_top > top_actor) a->_top = top_actor; if (a->_bottom < bottom_actor) a->_bottom = bottom_actor; } break; case 219: a->_drawToBackBuf = false; a->_needRedraw = true; a->_needBgReset = true; break; case 225: { copyScriptString(string); int slot = pop(); int len = resStrLen(string) + 1; addMessageToStack(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("o72_actorOps: default case %d", subOp); } } void ScummEngine_v72he::o72_verbOps() { int slot, a, b; VerbSlot *vs; byte op; byte name[200]; op = fetchScriptByte(); if (op == 196) { _curVerb = pop(); _curVerbSlot = getVerbSlot(_curVerb, 0); checkRange(_numVerbs - 1, 0, _curVerbSlot, "Illegal new verb slot %d"); return; } vs = &_verbs[_curVerbSlot]; slot = _curVerbSlot; switch (op) { 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); 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", op); } } void ScummEngine_v72he::o72_arrayOps() { byte subOp = fetchScriptByte(); int array = fetchScriptWord(); int offs, tmp, tmp2, tmp3; int dim1end, dim1start, dim2end, dim2start; int id, len, b, c; ArrayHeader *ah; int list[128]; byte string[2048]; debug(1,"o72_arrayOps: case %d", subOp); switch (subOp) { case 7: // SO_ASSIGN_STRING copyScriptString(string); len = resStrLen(string) + 1; 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: { // TODO //Array1 dim1end = pop(); dim1start = pop(); dim2end = pop(); dim2start = pop(); //Array2 array = fetchScriptWord(); dim1end = pop(); dim1start = pop(); dim2end = pop(); dim2start = pop(); } 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); } len = c - b; len |= dim2end; len = len - dim2end + 1; offs = (b >= c) ? 1 : -1; tmp2 = c; tmp3 = len; while (dim2start <= dim2end) { tmp = dim1start; while (tmp <= dim1end) { writeArray(array, dim2start, tmp, tmp2); if (--tmp3 == 0) { tmp2 = c; tmp3 = len; } else { tmp2 += offs; } tmp++; } dim2start++; } break; case 194: // SO_ASSIGN_STRING decodeScriptString(string); len = resStrLen(string) + 1; 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); } 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_talkActor() { Actor *a; _actorToPrintStrFor = pop(); a = derefActorSafe(_actorToPrintStrFor, "o72_talkActor"); _string[0].loadDefault(); _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; int type = fetchScriptByte(); switch (type) { 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", type); } defineArray(fetchScriptWord(), data, 0, 0, 0, pop()); } void ScummEngine_v72he::o72_dim2dimArray() { int a, b, data; int type = fetchScriptByte(); switch (type) { 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", type); } b = pop(); a = pop(); defineArray(fetchScriptWord(), data, 0, a, 0, b); } void ScummEngine_v72he::o72_traceStatus() { byte string[80]; copyScriptString(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; displayWizImage(&wi); } void ScummEngine_v72he::o72_unknownCF() { byte string[255]; copyScriptString(string); writeVar(0, 0); defineArray(0, kStringArray, 0, 0, 0, 0); writeArray(0, 0, 0, 0); push(readVar(0)); debug(1,"o72_unknownCF: 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, len, i, j; byte filename[256]; mode = pop(); copyScriptString(filename); debug(0,"Original filename %s", filename); // HACK: Convert paths in lost/smaller if (filename[0] == ':') { len = resStrLen(filename) + 1; j = 0; for (i = 1; i < len; i++) { if (filename[i] == ':') filename[j++] = '/'; else filename[j++] = filename[i]; } filename[j] = 0; debug(0,"Converted filename to %s", filename); } // Original games read path & filenames from INI file // We only need to add the required game name if (!strcmp((char *)filename,".he3")) { // For freddicove (Unencrypted and Updated Ru) memset(filename, 0, sizeof(filename)); sprintf((char *)filename, "%s.he3", _gameName.c_str()); debug(0,"New filename %s", filename); } else if (!strcmp((char *)filename,".he7")) { // For mustard memset(filename, 0, sizeof(filename)); sprintf((char *)filename, "%s.he7", _gameName.c_str()); debug(0,"New filename %s", filename); } else if (!strcmp((char *)filename,".HE9")) { // For bb2demo memset(filename, 0, sizeof(filename)); sprintf((char *)filename, "%s.he9", _gameName.c_str()); debug(0,"New filename %s", filename); } if (_substResFileNameIndex > 0) { char buf1[128]; generateSubstResFileName((char *)filename, buf1, 256, 0, _substResFileNameIndex); strcpy((char *)filename, buf1); } int r = 0; if (filename[0] == 'c' && filename[1] == ':') { // Strip path for (r = strlen((char*)filename); r != 0; r--) { if (filename[r - 1] == '\\') break; } } else { // Switch all \ to / for portablity len = resStrLen(_scriptPointer) + 1; for (i = 0; i < len; i++) { if (filename[i] == '\\') filename[i] = '/'; } } debug(0,"Final filename to %s", filename + r); slot = -1; for (i = 0; i < 17; i++) { if (_hFileTable[i].isOpen() == false) { slot = i; break; } } if (slot != -1) { switch(mode) { case 1: _hFileTable[slot].open((char*)filename + r, File::kFileReadMode, _saveFileMan->getSavePath()); if (_hFileTable[slot].isOpen() == false) _hFileTable[slot].open((char*)filename + r, File::kFileReadMode); break; case 2: _hFileTable[slot].open((char*)filename + r, 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); _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); } debug(1, "o72_readFile: slot %d, subOp %d val %d", slot, subOp, val); } void ScummEngine_v72he::writeFileFromArray(int slot, int 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() { int16 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); } debug(1, "o72_writeFile: slot %d, subOp %d", slot, subOp); } void ScummEngine_v72he::o72_findAllObjects() { int room = pop(); int i = 1; if (room != _currentRoom) warning("o72_findAllObjects: current room is not %d", room); writeVar(0, 0); defineArray(0, kDwordArray, 0, 0, 0, _numLocalObjects + 1); writeArray(0, 0, 0, _numLocalObjects); while (i < _numLocalObjects) { writeArray(0, 0, i, _objs[i].obj_nr); i++; } push(readVar(0)); } void ScummEngine_v72he::o72_deleteFile() { byte filename[100]; copyScriptString(filename); debug(1, "stub o72_deleteFile(%s)", filename); } void ScummEngine_v72he::o72_rename() { byte oldFilename[256],newFilename[256]; copyScriptString(newFilename); copyScriptString(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) { int16 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::o72_checkGlobQueue() { byte subOp = fetchScriptByte(); int idx = pop(); debug(1,"o72_checkGlobQueue stub (%d, %d)", subOp, idx); push(100); } void ScummEngine_v72he::o72_readINI() { byte option[100]; int type; // we pretend that we don't have .ini file copyScriptString(option); type = fetchScriptByte(); switch (type) { case 43: // HE 100 case 6: // number if (!strcmp((char *)option, "NoPrinting")) push(1); else push(0); break; case 77: // HE 100 case 7: // string writeVar(0, 0); defineArray(0, kStringArray, 0, 0, 0, 0); writeArray(0, 0, 0, 0); push(readVar(0)); // var ID string break; default: error("o72_readINI: default type %d", type); } debug(1, "o72_readINI (%d) %s", type, option); } void ScummEngine_v72he::o72_writeINI() { int type, value; byte option[256], string[1024]; type = fetchScriptByte(); switch (type) { case 43: // HE 100 case 6: // number value = pop(); copyScriptString(option); debug(1,"o72_writeINI: %s set to %d", option, value); break; case 77: // HE 100 case 7: // string copyScriptString(string); copyScriptString(option); debug(1,"o72_writeINI: %s set to %s", option, string); break; default: error("o72_writeINI: default type %d", type); } } void ScummEngine_v72he::o72_getResourceSize() { int size = 0, type; int idx = pop(); byte subOp = fetchScriptByte(); switch (subOp) { case 13: if (idx > _numSounds) { // TODO Music resource size push(100); return; } type = rtSound; break; case 14: type = rtRoomImage; break; case 15: type = rtImage; break; case 16: type = rtCostume; break; case 17: type = rtScript; break; default: warning("o72_getResourceSize: default type %d", subOp); push(0); return; } const byte *ptr = getResourceAddress(type, idx); if (ptr) size = READ_BE_UINT32(ptr + 4) - 8; push(size); } void ScummEngine_v72he::o72_setFilePath() { // File related byte filename[100]; copyScriptString(filename); debug(1,"o72_setFilePath: %s", filename); } void ScummEngine_v72he::o72_setWindowCaption() { byte name[100]; copyScriptString(name); int id = fetchScriptByte(); debug(1,"o72_setWindowCaption: (%d) %s", id, name); } 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(); a = derefActorSafe(_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