/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "cruise/cruise.h" #include "cruise/cruise_main.h" #include "common/endian.h" #include "common/textconsole.h" namespace Cruise { scriptInstanceStruct relHead; scriptInstanceStruct procHead; scriptInstanceStruct *currentScriptPtr; int8 getByteFromScript() { int8 var = *(int8 *)(currentData3DataPtr + currentScriptPtr->scriptOffset); ++currentScriptPtr->scriptOffset; return (var); } short int getShortFromScript() { short int var = (int16)READ_BE_UINT16(currentData3DataPtr + currentScriptPtr->scriptOffset); currentScriptPtr->scriptOffset += 2; return (var); } // load opcode int32 opcodeType0() { int index = 0; switch (currentScriptOpcodeType) { case 0: pushVar(getShortFromScript()); return (0); case 5: index = saveOpcodeVar; // fall through case 1: { uint8 *address = 0; int type = getByteFromScript(); int ovl = getByteFromScript(); short int offset = getShortFromScript(); offset += index; int typ7 = type & 7; if (!typ7) { return (-10); // unresloved link } if (!ovl) { address = scriptDataPtrTable[typ7]; } else { // TODO: if (!overlayTable[ovl].alreadyLoaded) { return (-7); } if (!overlayTable[ovl].ovlData) { return (-4); } if (typ7 == 5) { address = overlayTable[ovl].ovlData->data4Ptr; } else { assert(0); } } address += offset; int size = (type >> 3) & 3; if (size == 1) { address += index; pushVar((int16)READ_BE_UINT16(address)); return 0; } else if (size == 2) { pushVar(*address); return 0; } else { error("Unsupported code in opcodeType0 case 1"); } } case 2: { int16 var_16; int di = getByteFromScript(); int si = getByteFromScript(); int var_2 = getShortFromScript(); if (!si) { si = currentScriptPtr->overlayNumber; } if (getSingleObjectParam(si, var_2, di, &var_16)) { return -10; } pushVar(var_16); return 0; } default: error("Unsupported type %d in opcodeType0", currentScriptOpcodeType); } } // save opcode int32 opcodeType1() { int var = popVar(); int offset = 0; switch (currentScriptOpcodeType) { case 0: return (0); // strange, but happens also in original interpreter case 5: offset = saveOpcodeVar; // fall through case 1: { int var_A = 0; int byte1 = getByteFromScript(); int byte2 = getByteFromScript(); int short1 = getShortFromScript(); int var_6 = byte1 & 7; int var_C = short1; uint8 *ptr = 0; int type2; if (!var_6) return (-10); var_C = short1; if (byte2) { if (!overlayTable[byte2].alreadyLoaded) { return (-7); } if (!overlayTable[byte2].ovlData) { return (-4); } if (var_6 == 5) { ptr = overlayTable[byte2].ovlData->data4Ptr + var_C; } else { assert(0); } } else { ptr = scriptDataPtrTable[var_6] + var_C; } type2 = ((byte1 & 0x18) >> 3); switch (type2) { case 1: { WRITE_BE_UINT16(ptr + var_A + offset * 2, var); return 0; } case 2: { assert (ptr); *(ptr + var_A + offset) = var; return 0; } default: error("Unsupported code in opcodeType1 case 1"); } break; } case 2: { int mode = getByteFromScript(); int di = getByteFromScript(); int var_4 = getShortFromScript(); if (!di) { di = currentScriptPtr->overlayNumber; } if ((var == 0x85) && !strcmp((char *)currentCtpName, "S26.CTP") && !di && mode == 1) { // patch in bar var = 0x87; } setObjectPosition(di, var_4, mode, var); break; } case 4: { saveOpcodeVar = var; break; } default: error("Unsupported type %d in opcodeType1", currentScriptOpcodeType); } return (0); } int32 opcodeType2() { int index = 0; switch (currentScriptOpcodeType) { case 5: index = saveOpcodeVar; // fall through case 1: { uint8* adresse = NULL; int type = getByteFromScript(); int overlay = getByteFromScript(); int offset = getShortFromScript(); offset += index; int typ7 = type & 7; if (!typ7) { return (-10); } if (!overlay) { adresse = scriptDataPtrTable[typ7]; } else { if (!overlayTable[overlay].alreadyLoaded) { return (-7); } if (!overlayTable[overlay].ovlData) { return (-4); } assert(0); } adresse += offset; int size = (type >> 3) & 3; if (size == 1) { adresse += index; pushPtr(adresse); } else if (size == 2) { pushPtr(adresse); } } break; } return 0; } int32 opcodeType10() { // break return (0); } int32 opcodeType11() { // break return (1); } int32 opcodeType4() { // test int boolVar = 0; int var1 = popVar(); int var2 = popVar(); switch (currentScriptOpcodeType) { case 0: if (var2 != var1) boolVar = 1; break; case 1: if (var2 == var1) boolVar = 1; break; case 2: if (var2 < var1) boolVar = 1; break; case 3: if (var2 <= var1) boolVar = 1; break; case 4: if (var2 > var1) boolVar = 1; break; case 5: if (var2 >= var1) boolVar = 1; break; } pushVar(boolVar); return (0); } int32 opcodeType6() { int si = 0; int pop = popVar(); if (!pop) si = 1; if (pop < 0) si |= 4; if (pop > 0) si |= 2; currentScriptPtr->ccr = si; return (0); } int32 opcodeType7() { int var1 = popVar(); int var2 = popVar(); pushVar(var1); pushVar(var2); return (0); } int32 opcodeType5() { int offset = currentScriptPtr->scriptOffset; int short1 = getShortFromScript(); int newSi = short1 + offset; int bitMask = currentScriptPtr->ccr; switch (currentScriptOpcodeType) { case 0: if (!(bitMask & 1)) currentScriptPtr->scriptOffset = newSi; break; case 1: if (bitMask & 1) currentScriptPtr->scriptOffset = newSi; break; case 2: if (bitMask & 2) currentScriptPtr->scriptOffset = newSi; break; case 3: if (bitMask & 3) currentScriptPtr->scriptOffset = newSi; break; case 4: if (bitMask & 4) currentScriptPtr->scriptOffset = newSi; break; case 5: if (bitMask & 5) currentScriptPtr->scriptOffset = newSi; break; case 6: break; // never case 7: currentScriptPtr->scriptOffset = newSi; //always break; } return (0); } int32 opcodeType3() { // math int pop1 = popVar(); int pop2 = popVar(); switch (currentScriptOpcodeType) { case 0: pushVar(pop1 + pop2); return (0); case 1: if (pop2 == 0) error("opcodeType3 - Invalid value for pop2"); pushVar(pop1 / pop2); return (0); case 2: pushVar(pop1 - pop2); return (0); case 3: pushVar(pop1 * pop2); return (0); case 4: if (pop2 == 0) error("opcodeType3 - Invalid value for pop2"); pushVar(pop1 % pop2); return (0); case 7: case 5: pushVar(pop2 | pop1); return (0); case 6: pushVar(pop2 & pop1); return (0); } return 0; } int32 opcodeType9() { // stop script //debug("Stop a script of overlay %s", overlayTable[currentScriptPtr->overlayNumber].overlayName); currentScriptPtr->scriptNumber = -1; return (1); } void setupFuncArray() { for (int i = 0; i < 64; i++) opcodeTypeTable[i] = NULL; opcodeTypeTable[1] = opcodeType0; opcodeTypeTable[2] = opcodeType1; opcodeTypeTable[3] = opcodeType2; opcodeTypeTable[4] = opcodeType3; opcodeTypeTable[5] = opcodeType4; opcodeTypeTable[6] = opcodeType5; opcodeTypeTable[7] = opcodeType6; opcodeTypeTable[8] = opcodeType7; opcodeTypeTable[9] = opcodeType8; opcodeTypeTable[10] = opcodeType9; opcodeTypeTable[11] = opcodeType10; opcodeTypeTable[12] = opcodeType11; } int removeScript(int overlay, int idx, scriptInstanceStruct *headPtr) { scriptInstanceStruct *scriptPtr = headPtr->nextScriptPtr; while (scriptPtr) { if (scriptPtr->overlayNumber == overlay && (scriptPtr->scriptNumber == idx || idx == -1)) scriptPtr->scriptNumber = -1; scriptPtr = scriptPtr->nextScriptPtr; } return (0); } uint8 *attacheNewScriptToTail(scriptInstanceStruct *scriptHandlePtr, int16 overlayNumber, int16 param, int16 arg0, int16 arg1, int16 arg2, scriptTypeEnum scriptType) { int useArg3Neg = 0; ovlData3Struct *data3Ptr; int var_C; scriptInstanceStruct *oldTail; if (scriptType < 0) { useArg3Neg = 1; scriptType = (scriptTypeEnum) -scriptType; } if (scriptType == 20) data3Ptr = getOvlData3Entry(overlayNumber, param); else if (scriptType == 30) data3Ptr = scriptFunc1Sub2(overlayNumber, param); else return (NULL); if (!data3Ptr) return (NULL); if (!data3Ptr->dataPtr) return (NULL); var_C = data3Ptr->sysKey; oldTail = scriptHandlePtr; while (oldTail->nextScriptPtr) // go to the end of the list oldTail = oldTail->nextScriptPtr; scriptInstanceStruct *tempPtr = (scriptInstanceStruct *)mallocAndZero(sizeof(scriptInstanceStruct)); if (!tempPtr) return (NULL); tempPtr->data = NULL; if (var_C) tempPtr->data = (uint8 *) mallocAndZero(var_C); tempPtr->dataSize = var_C; tempPtr->nextScriptPtr = NULL; tempPtr->scriptOffset = 0; tempPtr->scriptNumber = param; tempPtr->overlayNumber = overlayNumber; if (scriptType == 20) // Obj or not ? tempPtr->sysKey = useArg3Neg; else tempPtr->sysKey = 1; tempPtr->freeze = 0; tempPtr->type = scriptType; tempPtr->var18 = arg2; tempPtr->var16 = arg1; tempPtr->var1A = arg0; tempPtr->nextScriptPtr = oldTail->nextScriptPtr; // should always be NULL as it's the tail oldTail->nextScriptPtr = tempPtr; // attach the new node to the list return (tempPtr->data); } int executeScripts(scriptInstanceStruct *ptr) { int numScript2; ovlData3Struct *ptr2; ovlDataStruct *ovlData; uint8 opcodeType; numScript2 = ptr->scriptNumber; if (ptr->type == 20) ptr2 = getOvlData3Entry(ptr->overlayNumber, numScript2); else if (ptr->type == 30) ptr2 = scriptFunc1Sub2(ptr->overlayNumber, numScript2); else return (-6); if (!ptr2) return (-4); if (!overlayTable[ptr->overlayNumber].alreadyLoaded) return (-7); ovlData = overlayTable[ptr->overlayNumber].ovlData; if (!ovlData) return (-4); currentData3DataPtr = ptr2->dataPtr; scriptDataPtrTable[1] = (uint8 *) ptr->data; scriptDataPtrTable[2] = getDataFromData3(ptr2, 1); scriptDataPtrTable[5] = ovlData->data4Ptr; // free strings scriptDataPtrTable[6] = ovlData->ptr8; currentScriptPtr = ptr; positionInStack = 0; do { #ifdef SKIP_INTRO if (currentScriptPtr->scriptOffset == 290 && currentScriptPtr->overlayNumber == 4 && currentScriptPtr->scriptNumber == 0) { currentScriptPtr->scriptOffset = 923; } #endif opcodeType = getByteFromScript(); debugC(5, kCruiseDebugScript, "Script %s/%d ip=%d opcode=%d", overlayTable[currentScriptPtr->overlayNumber].overlayName, currentScriptPtr->scriptNumber, currentScriptPtr->scriptOffset, (opcodeType & 0xFB) >> 3); currentScriptOpcodeType = opcodeType & 7; if (!opcodeTypeTable[(opcodeType & 0xFB) >> 3]) { error("Unsupported opcode type %d", (opcodeType & 0xFB) >> 3); } } while (!opcodeTypeTable[(opcodeType & 0xFB) >> 3]()); currentScriptPtr = NULL; return (0); } void manageScripts(scriptInstanceStruct *scriptHandle) { scriptInstanceStruct *ptr = scriptHandle; while (ptr) { if (!overlayTable[ptr->overlayNumber].executeScripts) { if ((ptr->scriptNumber != -1) && (ptr->freeze == 0) && (ptr->sysKey != 0)) executeScripts(ptr); if (ptr->sysKey == 0) ptr->sysKey = 1; } ptr = ptr->nextScriptPtr; } } } // End of namespace Cruise