/** * Given the scene background film, extracts the palette handle for * everything else's use, then starts a display process for each reel * in the film. * @param hFilm Scene background film */ void StartupBackground(CORO_PARAM, SCNHANDLE hFilm) { CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); CORO_BEGIN_CODE(_ctx); const FILM *pfilm; IMAGE *pim; hBackground = hFilm; // Save handle in case of Save_Scene() pim = GetImageFromFilm(hFilm, 0, NULL, NULL, &pfilm); SetBackPal(FROM_LE_32(pim->hImgPal)); // Extract the film speed BGspeed = ONE_SECOND / FROM_LE_32(pfilm->frate); // Start display process for each reel in the film g_scheduler->createProcess(PID_REEL, BGmainProcess, &pfilm->reels[0], sizeof(FREEL)); if (TinselV0) { for (uint i = 1; i < FROM_LE_32(pfilm->numreels); ++i) g_scheduler->createProcess(PID_REEL, BGotherProcess, &pfilm->reels[i], sizeof(FREEL)); } if (pBG[0] == NULL) ControlStartOff(); if (TinselV2 && (coroParam != nullContext)) CORO_GIVE_WAY; CORO_END_CODE; }
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 = FROM_LE_16(pe_res->number_of_named_entries) + FROM_LE_16(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 = ((FROM_LE_32(dirent[c].offset_to_data) & IMAGE_RESOURCE_DATA_IS_DIRECTORY) != 0); wr[c].children = fi->first_resource + (FROM_LE_32(dirent[c].offset_to_data) & ~IMAGE_RESOURCE_DATA_IS_DIRECTORY); /* fill in wr->id, wr->numeric_id */ if (!decode_pe_resource_id(fi, wr + c, FROM_LE_32(dirent[c].name))) { free(wr); return NULL; } } return wr; }
/** * Called at the start of each scene for each actor with a code block. * @param as Actor structure * @param bRunScript Flag for whether to run actor's script for the scene */ void StartActor(const T1_ACTOR_STRUC *as, bool bRunScript) { SCNHANDLE hActorId = FROM_LE_32(as->hActorId); // Zero-out many things actorInfo[hActorId - 1].bHidden = false; actorInfo[hActorId - 1].completed = false; actorInfo[hActorId - 1].x = 0; actorInfo[hActorId - 1].y = 0; actorInfo[hActorId - 1].presReel = NULL; actorInfo[hActorId - 1].presFilm = 0; actorInfo[hActorId - 1].presObj = NULL; // Store current scene's parameters for this actor actorInfo[hActorId - 1].mtype = FROM_LE_32(as->masking); actorInfo[hActorId - 1].actorCode = FROM_LE_32(as->hActorCode); // Run actor's script for this scene if (bRunScript) { if (bActorsOn) actorInfo[hActorId - 1].bAlive = true; if (actorInfo[hActorId - 1].bAlive && FROM_LE_32(as->hActorCode)) ActorEvent(hActorId, STARTUP, PLR_NOEVENT); } }
/** * Return the position of an object. * Returns X, Y and direction in angles */ void ScummEngine::getObjectXYPos(int object, int &x, int &y, int &dir) { int idx = getObjectIndex(object); assert(idx >= 0); ObjectData &od = _objs[idx]; int state; const byte *ptr; const ImageHeader *imhd; if (_game.version >= 6) { state = getState(object) - 1; if (state < 0) state = 0; ptr = getOBIMFromObjectData(od); if (!ptr) { // FIXME: We used to assert here, but it seems that in the nexus // in The Dig, this can happen, at least with old savegames, and // it's safe to continue... debug(0, "getObjectXYPos: Can't find object %d", object); return; } imhd = (const ImageHeader *)findResourceData(MKTAG('I','M','H','D'), ptr); assert(imhd); if (_game.version == 8) { switch (FROM_LE_32(imhd->v8.version)) { case 800: x = od.x_pos + (int32)READ_LE_UINT32((const byte *)imhd + 8 * state + 0x44); y = od.y_pos + (int32)READ_LE_UINT32((const byte *)imhd + 8 * state + 0x48); break; case 801: x = od.x_pos + (int32)READ_LE_UINT32(&imhd->v8.hotspot[state].x); y = od.y_pos + (int32)READ_LE_UINT32(&imhd->v8.hotspot[state].y); break; default: error("Unsupported image header version %d", FROM_LE_32(imhd->v8.version)); } } else if (_game.version == 7) { x = od.x_pos + (int16)READ_LE_UINT16(&imhd->v7.hotspot[state].x); y = od.y_pos + (int16)READ_LE_UINT16(&imhd->v7.hotspot[state].y); } else { x = od.x_pos + (int16)READ_LE_UINT16(&imhd->old.hotspot[state].x); y = od.y_pos + (int16)READ_LE_UINT16(&imhd->old.hotspot[state].y); } } else if (_game.version <= 2) { x = od.walk_x; y = od.walk_y; // Adjust x, y when no actor direction is set, but only perform this // adjustment for V0 games (e.g. MM C64), otherwise certain scenes in // newer games are affected as well (e.g. the interior of the Shuttle // Bus scene in Zak V2, where no actor is present). Refer to bug #3526089. if (!od.actordir && _game.version == 0) { x = od.x_pos + od.width / 2; y = od.y_pos + od.height / 2; } x = x >> V12_X_SHIFT; y = y >> V12_Y_SHIFT; } else {
/** * LoadBasicChunks */ void LoadBasicChunks(void) { byte *cptr; int numObjects; // Allocate RAM for savescene data InitialiseSaveScenes(); // CHUNK_TOTAL_ACTORS seems to be missing in the released version, hard coding a value // TODO: Would be nice to just change 511 to MAX_SAVED_ALIVES cptr = FindChunk(MASTER_SCNHANDLE, CHUNK_TOTAL_ACTORS); RegisterActors((cptr != NULL) ? READ_LE_UINT32(cptr) : 511); // CHUNK_TOTAL_GLOBALS seems to be missing in some versions. // So if it is missing, set a reasonably high value for the number of globals. cptr = FindChunk(MASTER_SCNHANDLE, CHUNK_TOTAL_GLOBALS); RegisterGlobals((cptr != NULL) ? READ_LE_UINT32(cptr) : 512); cptr = FindChunk(INV_OBJ_SCNHANDLE, CHUNK_TOTAL_OBJECTS); numObjects = (cptr != NULL) ? READ_LE_UINT32(cptr) : 0; cptr = FindChunk(INV_OBJ_SCNHANDLE, CHUNK_OBJECTS); #ifdef SCUMM_BIG_ENDIAN //convert to native endianness INV_OBJECT *io = (INV_OBJECT *)cptr; for (int i = 0; i < numObjects; i++, io++) { io->id = FROM_LE_32(io->id); io->hIconFilm = FROM_LE_32(io->hIconFilm); io->hScript = FROM_LE_32(io->hScript); io->attribute = FROM_LE_32(io->attribute); } #endif RegisterIcons(cptr, numObjects); cptr = FindChunk(MASTER_SCNHANDLE, CHUNK_TOTAL_POLY); if (cptr != NULL) MaxPolygons(*cptr); if (TinselV2) { // Global processes cptr = FindChunk(MASTER_SCNHANDLE, CHUNK_NUM_PROCESSES); assert(cptr && (*cptr < 100)); int num = *cptr; cptr = FindChunk(MASTER_SCNHANDLE, CHUNK_PROCESSES); assert(!num || cptr); GlobalProcesses(num, cptr); // CdPlay() stuff cptr = FindChunk(MASTER_SCNHANDLE, CHUNK_CDPLAY_HANDLE); assert(cptr); uint32 playHandle = READ_LE_UINT32(cptr); assert(playHandle < 512); SetCdPlayHandle(playHandle); } }
static void MoverProcessHelper(int X, int Y, int id, PMOVER pMover) { const FILM *pfilm; const MULTI_INIT *pmi; const FRAME *pFrame; IMAGE *pim; assert(BgPal()); // Can't start actor without a background palette assert(pMover->walkReels[0][FORWARD]); // Starting actor process without walk reels InitMover(pMover); InitialPathChecks(pMover, X, Y); pfilm = (const FILM *)LockMem(pMover->walkReels[0][FORWARD]); pmi = (const MULTI_INIT *)LockMem(FROM_LE_32(pfilm->reels[0].mobj)); //--- pFrame = (const FRAME *)LockMem(FROM_LE_32(pmi->hMulFrame)); // get pointer to image pim = (IMAGE *)LockMem(READ_LE_UINT32(pFrame)); // handle to image pim->hImgPal = TO_LE_32(BgPal()); //--- pMover->actorObj = MultiInitObject(pmi); /**/ assert(pMover->actorID == id); pMover->actorID = id; // add it to display list MultiInsertObject(GetPlayfieldList(FIELD_WORLD), pMover->actorObj); storeActorReel(id, NULL, 0, pMover->actorObj, 0, 0, 0); InitStepAnimScript(&pMover->actorAnim, pMover->actorObj, FROM_LE_32(pfilm->reels[0].script), ONE_SECOND / FROM_LE_32(pfilm->frate)); pMover->stepCount = 0; MultiSetAniXY(pMover->actorObj, pMover->objX, pMover->objY); // If no path, just use first path in the scene if (pMover->hCpath != NOPOLY) SetMoverZ(pMover, pMover->objY, GetPolyZfactor(pMover->hCpath)); else SetMoverZ(pMover, pMover->objY, GetPolyZfactor(FirstPathPoly())); // Make him the right size SetMoverStanding(pMover); //**** if added 18/11/94, am if (X != MAGICX && Y != MAGICY) { HideMover(pMover, 0); // Allows a play to come in before this appears pMover->bHidden = false; // ...but don't stay hidden } pMover->bActive = true; }
byte *Win32ResExtractor::get_resource_entry(WinLibrary *fi, WinResource *wr, int *size) { byte *result; Win32ImageResourceDataEntry *dataent; dataent = (Win32ImageResourceDataEntry *) wr->children; RETURN_IF_BAD_POINTER(NULL, *dataent); *size = FROM_LE_32(dataent->size); result = fi->memory + FROM_LE_32(dataent->offset_to_data); RETURN_IF_BAD_OFFSET(NULL, result, *size); return result; }
void ResourceManager::readCluIndex(uint16 fileNum, Common::File *file) { // we didn't read from this file before, get its index table assert(_resFiles[fileNum].entryTab == NULL); assert(file); // 1st DWORD of a cluster is an offset to the look-up table uint32 table_offset = file->readUint32LE(); debug(6, "table offset = %d", table_offset); uint32 tableSize = file->size() - table_offset; // the table is stored at the end of the file file->seek(table_offset); assert((tableSize % 8) == 0); _resFiles[fileNum].entryTab = (uint32 *)malloc(tableSize); _resFiles[fileNum].numEntries = tableSize / 8; assert(_resFiles[fileNum].entryTab); file->read(_resFiles[fileNum].entryTab, tableSize); if (file->eos() || file->err()) error("unable to read index table from file %s", _resFiles[fileNum].fileName); #ifdef SCUMM_BIG_ENDIAN for (int tabCnt = 0; tabCnt < _resFiles[fileNum].numEntries * 2; tabCnt++) _resFiles[fileNum].entryTab[tabCnt] = FROM_LE_32(_resFiles[fileNum].entryTab[tabCnt]); #endif }
/** * Return the position of an object. * Returns X, Y and direction in angles */ void ScummEngine::getObjectXYPos(int object, int &x, int &y, int &dir) { int idx = (_v0ObjectIndex) ? object : getObjectIndex(object); assert(idx >= 0); ObjectData &od = _objs[idx]; int state; const byte *ptr; const ImageHeader *imhd; if (_game.version >= 6) { state = getState(object) - 1; if (state < 0) state = 0; ptr = getOBIMFromObjectData(od); if (!ptr) { // FIXME: We used to assert here, but it seems that in the nexus // in The Dig, this can happen, at least with old savegames, and // it's safe to continue... debug(0, "getObjectXYPos: Can't find object %d", object); return; } imhd = (const ImageHeader *)findResourceData(MKTAG('I','M','H','D'), ptr); assert(imhd); if (_game.version == 8) { switch (FROM_LE_32(imhd->v8.version)) { case 800: x = od.x_pos + (int32)READ_LE_UINT32((const byte *)imhd + 8 * state + 0x44); y = od.y_pos + (int32)READ_LE_UINT32((const byte *)imhd + 8 * state + 0x48); break; case 801: x = od.x_pos + (int32)READ_LE_UINT32(&imhd->v8.hotspot[state].x); y = od.y_pos + (int32)READ_LE_UINT32(&imhd->v8.hotspot[state].y); break; default: error("Unsupported image header version %d", FROM_LE_32(imhd->v8.version)); } } else if (_game.version == 7) { x = od.x_pos + (int16)READ_LE_UINT16(&imhd->v7.hotspot[state].x); y = od.y_pos + (int16)READ_LE_UINT16(&imhd->v7.hotspot[state].y); } else { x = od.x_pos + (int16)READ_LE_UINT16(&imhd->old.hotspot[state].x); y = od.y_pos + (int16)READ_LE_UINT16(&imhd->old.hotspot[state].y); } } else if (_game.version <= 2) { x = od.walk_x >> V12_X_SHIFT; y = od.walk_y >> V12_Y_SHIFT; } else {
/** * Run a scene process with the given event. */ void SceneProcessEvent(CORO_PARAM, uint32 procID, TINSEL_EVENT event, bool bWait, int myEscape, bool *result) { uint32 i; // Loop counter if (result) *result = false; CORO_BEGIN_CONTEXT; PROCESS_STRUC *pStruc; PPROCESS pProc; PINT_CONTEXT pic; CORO_END_CONTEXT(_ctx); CORO_BEGIN_CODE(_ctx); _ctx->pStruc = (PROCESS_STRUC *)LockMem(hSceneProcess); for (i = 0; i < numSceneProcess; i++) { if (FROM_LE_32(_ctx->pStruc[i].processId) == procID) { assert(_ctx->pStruc[i].hProcessCode); // Must have some code to run _ctx->pic = InitInterpretContext(GS_PROCESS, FROM_LE_32(_ctx->pStruc[i].hProcessCode), event, NOPOLY, // No polygon 0, // No actor NULL, // No object myEscape); if (_ctx->pic == NULL) return; _ctx->pProc = g_scheduler->createProcess(PID_PROCESS + i, ProcessTinselProcess, &_ctx->pic, sizeof(_ctx->pic)); AttachInterpret(_ctx->pic, _ctx->pProc); break; } } if (i == numSceneProcess) return; if (bWait) { CORO_INVOKE_2(WaitInterpret, _ctx->pProc, result); } CORO_END_CODE; }
/** * Change which reel is playing for a moving actor. */ void AlterMover(PMOVER pMover, SCNHANDLE film, AR_FUNCTION fn) { const FILM *pfilm; assert(pMover->actorObj); // Altering null moving actor's animation script if (fn == AR_POPREEL) { // Use the saved film film = pMover->hPushedFilm; } if (fn == AR_PUSHREEL) { // Save the one we're replacing pMover->hPushedFilm = (pMover->bSpecReel) ? pMover->hLastFilm : 0; } if (film == 0) { if (pMover->bSpecReel) { // Revert to 'normal' actor SetMoverWalkReel(pMover, pMover->direction, pMover->scale, true); pMover->bSpecReel = false; } } else { // Remember this one in case the actor talks pMover->hLastFilm = film; pfilm = (const FILM *)LockMem(film); assert(pfilm != NULL); InitStepAnimScript(&pMover->actorAnim, pMover->actorObj, FROM_LE_32(pfilm->reels[0].script), ONE_SECOND / FROM_LE_32(pfilm->frate)); if (!TinselV2) pMover->stepCount = 0; // If no path, just use first path in the scene if (pMover->hCpath != NOPOLY) SetMoverZ(pMover, pMover->objY, GetPolyZfactor(pMover->hCpath)); else SetMoverZ(pMover, pMover->objY, GetPolyZfactor(FirstPathPoly())); if (fn == AR_WALKREEL) { pMover->bSpecReel = false; pMover->bWalkReel = true; } else { pMover->bSpecReel = true; pMover->bWalkReel = false; #ifdef DEBUG assert(StepAnimScript(&pMover->actorAnim) != ScriptFinished); // Actor reel has finished! #else StepAnimScript(&pMover->actorAnim); // 04/01/95 #endif } // Hang on, we may not want him yet! 04/01/95 if (pMover->bHidden) MultiSetZPosition(pMover->actorObj, -1); } }
/** * Called at the start of each scene. Start each actor with a code block. * @param ah Scene handle * @param numActors Number of actors * @param bRunScript Flag for whether to run actor scene scripts */ void StartTaggedActors(SCNHANDLE ah, int numActors, bool bRunScript) { int i; if (TinselV2) { // Clear it all out for a fresh start memset(taggedActors, 0, sizeof(taggedActors)); numTaggedActors = numActors; } else { // Only actors with code blocks got (x, y) re-initialized, so... for (i = 0; i < NumActors; i++) { actorInfo[i].x = actorInfo[i].y = 0; actorInfo[i].mtype = 0; } } if (!TinselV2) { // Tinsel 1 load variation const T1_ACTOR_STRUC *as = (const T1_ACTOR_STRUC *)LockMem(ah); for (i = 0; i < numActors; i++, as++) { StartActor(as, bRunScript); } } else if (numActors > 0) { // Tinsel 2 load variation const T2_ACTOR_STRUC *as = (T2_ACTOR_STRUC *)LockMem(ah); for (i = 0; i < numActors; i++, as++) { assert(as->hActorCode); // Store current scene's parameters for this tagged actor taggedActors[i].id = FROM_LE_32(as->hActorId); taggedActors[i].hTagText = FROM_LE_32(as->hTagText); taggedActors[i].tagPortionV = FROM_LE_32(as->tagPortionV); taggedActors[i].tagPortionH = FROM_LE_32(as->tagPortionH); taggedActors[i].hActorCode = FROM_LE_32(as->hActorCode); // Run actor's script for this scene if (bRunScript) { // Send in reverse order - they get swapped round in the scheduler ActorEvent(nullContext, taggedActors[i].id, SHOWEVENT, false, 0); ActorEvent(nullContext, taggedActors[i].id, STARTUP, false, 0); } } } }
/** * Kill all instances of a scene process. */ void KillSceneProcess(uint32 procID) { uint32 i; // Loop counter PROCESS_STRUC *pStruc; pStruc = (PROCESS_STRUC *) LockMem(hSceneProcess); for (i = 0; i < numSceneProcess; i++) { if (FROM_LE_32(pStruc[i].processId) == procID) { g_scheduler->killMatchingProcess(PID_PROCESS + i, -1); break; } } }
/** * Runs secondary reels for a scene background */ static void BGotherProcess(CORO_PARAM, const void *param) { // COROUTINE CORO_BEGIN_CONTEXT; OBJECT *pObj; ANIM anim; CORO_END_CONTEXT(_ctx); const FREEL *pReel = (const FREEL *)param; const MULTI_INIT *pmi = (const MULTI_INIT *)LockMem(FROM_LE_32(pReel->mobj)); CORO_BEGIN_CODE(_ctx); // Initialise and insert the object, and initialise its script. _ctx->pObj = MultiInitObject(pmi); MultiInsertObject(GetPlayfieldList(FIELD_WORLD), _ctx->pObj); InitStepAnimScript(&_ctx->anim, pBG[0], FROM_LE_32(pReel->script), BGspeed); while (StepAnimScript(&_ctx->anim) != ScriptFinished) CORO_SLEEP(1); CORO_END_CODE; }
/** * Called to restore a scene process. */ void RestoreSceneProcess(INT_CONTEXT *pic) { uint32 i; PROCESS_STRUC *pStruc; pStruc = (PROCESS_STRUC *)LockMem(hSceneProcess); for (i = 0; i < numSceneProcess; i++) { if (FROM_LE_32(pStruc[i].hProcessCode) == pic->hCode) { g_scheduler->createProcess(PID_PROCESS + i, RestoredProcessProcess, &pic, sizeof(pic)); break; } } assert(i < numSceneProcess); }
/** * Give a object a new image and new orientation flags. * @param pAniObj Object to be updated * @param newflags Objects new flags * @param hNewImg Objects new image */ void AnimateObjectFlags(OBJECT *pAniObj, int newflags, SCNHANDLE hNewImg) { // validate object pointer assert(isValidObject(pAniObj)); if (pAniObj->hImg != hNewImg || (pAniObj->flags & DMA_HARDFLAGS) != (newflags & DMA_HARDFLAGS)) { // something has changed int oldAniX, oldAniY; // objects old animation offsets int newAniX, newAniY; // objects new animation offsets // get objects old animation offsets GetAniOffset(pAniObj->hImg, pAniObj->flags, &oldAniX, &oldAniY); // get objects new animation offsets GetAniOffset(hNewImg, newflags, &newAniX, &newAniY); if (hNewImg) { // get pointer to image const IMAGE *pNewImg = (IMAGE *)LockMem(hNewImg); // setup new shape pAniObj->width = FROM_LE_16(pNewImg->imgWidth); pAniObj->height = FROM_LE_16(pNewImg->imgHeight) & ~C16_FLAG_MASK; newflags &= ~C16_FLAG_MASK; newflags |= FROM_LE_16(pNewImg->imgHeight) & C16_FLAG_MASK; // set objects bitmap definition pAniObj->hBits = FROM_LE_32(pNewImg->hImgBits); } else { // null image pAniObj->width = 0; pAniObj->height = 0; pAniObj->hBits = 0; } // set objects flags and signal a change pAniObj->flags = newflags | DMA_CHANGED; // set objects image pAniObj->hImg = hNewImg; // adjust objects position - subtract new from old for difference pAniObj->xPos += intToFrac(oldAniX - newAniX); pAniObj->yPos += intToFrac(oldAniY - newAniY); } }
/** * Initialise a multi-part object using a list of images to init * each object piece. One object is created for each image in the list. * All objects are given the same palette as the first image. A pointer * to the first (master) object created is returned. * @param pInitTbl Pointer to multi-object initialisation table */ OBJECT *MultiInitObject(const MULTI_INIT *pInitTbl) { OBJ_INIT obj_init; // object init table OBJECT *pFirst, *pObj; // object pointers FRAME *pFrame; // list of images for the multi-part object if (FROM_LE_32(pInitTbl->hMulFrame)) { // we have a frame handle pFrame = (FRAME *)LockMem(FROM_LE_32(pInitTbl->hMulFrame)); obj_init.hObjImg = READ_LE_UINT32(pFrame); // first objects shape } else { // this must be a animation list for a NULL object pFrame = NULL; obj_init.hObjImg = 0; // first objects shape } // init the object init table obj_init.objFlags = (int)FROM_LE_32(pInitTbl->mulFlags); // all objects have same flags obj_init.objID = (int)FROM_LE_32(pInitTbl->mulID); // all objects have same ID obj_init.objX = (int)FROM_LE_32(pInitTbl->mulX); // all objects have same X ani pos obj_init.objY = (int)FROM_LE_32(pInitTbl->mulY); // all objects have same Y ani pos obj_init.objZ = (int)FROM_LE_32(pInitTbl->mulZ); // all objects have same Z pos // create and init the first object pObj = pFirst = InitObject(&obj_init); if (pFrame) { // if we have any animation frames pFrame++; while (READ_LE_UINT32(pFrame) != 0) { // set next objects shape obj_init.hObjImg = READ_LE_UINT32(pFrame); // create next object and link to previous pObj = pObj->pSlave = InitObject(&obj_init); pFrame++; } } // null end of list for final object pObj->pSlave = NULL; // return master object return pFirst; }
void ScummEngine_v70he::readGlobalObjects() { int num = _fileHandle->readUint16LE(); assert(num == _numGlobalObjects); assert(_objectStateTable); assert(_objectOwnerTable); _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 }
/** * Get actor to adopt its appropriate walking reel. */ void SetMoverWalkReel(PMOVER pMover, DIRECTION reel, int scale, bool force) { SCNHANDLE whichReel; const FILM *pfilm; // Kill off any play that may be going on for this actor // and restore the real actor storeActorReel(pMover->actorID, NULL, 0, NULL, 0, 0, 0); UnHideMover(pMover); // Don't do it if using a special walk reel if (pMover->bWalkReel) return; if (force || pMover->scale != scale || pMover->direction != reel) { assert(reel >= 0 && reel <= 3 && scale > 0 && scale <= TOTAL_SCALES); // out of range scale or reel // If scale change and both are regular scales // and there's a scaling reel in the right direction if (pMover->scale != scale && scale <= NUM_MAINSCALES && pMover->scale <= NUM_MAINSCALES && (whichReel = ScalingReel(pMover->actorID, pMover->scale, scale, reel)) != 0) { // error("Cripes"); ; // Use what is now in 'whichReel' } else { whichReel = pMover->walkReels[scale-1][reel]; assert(whichReel); // no reel } pfilm = (const FILM *)LockMem(whichReel); assert(pfilm != NULL); // no film InitStepAnimScript(&pMover->actorAnim, pMover->actorObj, FROM_LE_32(pfilm->reels[0].script), 1); // Synchronised walking reels assert(pMover->stepCount >= 0); SkipFrames(&pMover->actorAnim, pMover->stepCount); pMover->scale = scale; pMover->direction = reel; } }
BaseSound::BaseSound(Audio::Mixer *mixer, File *file, uint32 base, bool bigendian) { _mixer = mixer; _file = file; uint res = 0; uint32 size; _file->seek(base + sizeof(uint32), SEEK_SET); if (bigendian) size = _file->readUint32BE(); else size = _file->readUint32LE(); // The Feeble Files uses set amount of voice offsets if (size == 0) size = 40000; res = size / sizeof(uint32); _offsets = (uint32 *)malloc(size + sizeof(uint32)); _freeOffsets = true; _file->seek(base, SEEK_SET); if (_file->read(_offsets, size) != size) error("Can't read offsets"); for (uint i = 0; i < res; i++) { #if defined(SCUMM_BIG_ENDIAN) if (!(bigendian)) _offsets[i] = FROM_LE_32(_offsets[i]); #endif if (bigendian) _offsets[i] = TO_BE_32(_offsets[i]); _offsets[i] += base; } // only needed for mp3 _offsets[res] = _file->size(); }
/** * About to jump or end * @param pAnim Animation data structure */ bool AboutToJumpOrEnd(PANIM pAnim) { if (pAnim->aniDelta == 1) { // get a pointer to the script ANI_SCRIPT *pAni = (ANI_SCRIPT *)LockMem(pAnim->hScript); int zzz = pAnim->scriptIndex; for (;;) { // repeat until a real image switch (FROM_LE_32(pAni[zzz].op)) { case ANI_END: // end of animation script case ANI_JUMP: // do animation jump return true; case ANI_HFLIP: // flip animated object horizontally case ANI_VFLIP: // flip animated object vertically case ANI_HVFLIP: // flip animated object in both directions zzz++; break; case ANI_ADJUSTX: // adjust animated object x animation point case ANI_ADJUSTY: // adjust animated object y animation point zzz += 2; break; case ANI_ADJUSTXY: // adjust animated object x & y animation points zzz += 3; break; case ANI_HIDE: // hide animated object default: // must be an actual animation frame handle return false; } } } return false; }
/** * Run main animation that comprises the scene background. */ static void BGmainProcess(CORO_PARAM, const void *param) { // COROUTINE CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); CORO_BEGIN_CODE(_ctx); const FILM *pFilm; const FREEL *pReel; const MULTI_INIT *pmi; // get the stuff copied to process when it was created if (pBG[0] == NULL) { /*** At start of scene ***/ if (!TinselV2) { pReel = (const FREEL *)param; // Get the MULTI_INIT structure pmi = (const MULTI_INIT *)LockMem(FROM_LE_32(pReel->mobj)); // Initialise and insert the object, and initialise its script. pBG[0] = MultiInitObject(pmi); MultiInsertObject(GetPlayfieldList(FIELD_WORLD), pBG[0]); InitStepAnimScript(&thisAnim[0], pBG[0], FROM_LE_32(pReel->script), BGspeed); bgReels = 1; } else { /*** At start of scene ***/ pFilm = (const FILM *)LockMem(hBackground); bgReels = FROM_LE_32(pFilm->numreels); int i; for (i = 0; i < bgReels; i++) { // Get the MULTI_INIT structure pmi = (PMULTI_INIT) LockMem(FROM_LE_32(pFilm->reels[i].mobj)); // Initialise and insert the object, and initialise its script. pBG[i] = MultiInitObject(pmi); MultiInsertObject(GetPlayfieldList(FIELD_WORLD), pBG[i]); MultiSetZPosition(pBG[i], 0); InitStepAnimScript(&thisAnim[i], pBG[i], FROM_LE_32(pFilm->reels[i].script), BGspeed); if (i > 0) pBG[i-1]->pSlave = pBG[i]; } } if (bDoFadeIn) { FadeInFast(NULL); bDoFadeIn = false; } else if (TinselV2) PokeInTagColor(); for (;;) { for (int i = 0; i < bgReels; i++) { if (StepAnimScript(&thisAnim[i]) == ScriptFinished) error("Background animation has finished"); } CORO_SLEEP(1); } } else { // New background during scene if (!TinselV2) { pReel = (const FREEL *)param; InitStepAnimScript(&thisAnim[0], pBG[0], FROM_LE_32(pReel->script), BGspeed); StepAnimScript(&thisAnim[0]); } else { pFilm = (const FILM *)LockMem(hBackground); assert(bgReels == (int32)FROM_LE_32(pFilm->numreels)); // Just re-initialise the scripts. for (int i = 0; i < bgReels; i++) { InitStepAnimScript(&thisAnim[i], pBG[i], pFilm->reels[i].script, BGspeed); StepAnimScript(&thisAnim[i]); } } } CORO_END_CODE; }
/** * Advance to next frame routine. * @param pAnim Animation data structure */ SCRIPTSTATE DoNextFrame(ANIM *pAnim) { // get a pointer to the script const ANI_SCRIPT *pAni = (const ANI_SCRIPT *)LockMem(pAnim->hScript); while (1) { // repeat until a real image debugC(DEBUG_DETAILED, kTinselDebugAnimations, "DoNextFrame %ph index=%d, op=%xh", (byte *)pAnim, pAnim->scriptIndex, FROM_LE_32(pAni[pAnim->scriptIndex].op)); switch ((int32)FROM_LE_32(pAni[pAnim->scriptIndex].op)) { case ANI_END: // end of animation script // move to next opcode pAnim->scriptIndex++; // indicate script has finished return ScriptFinished; case ANI_JUMP: // do animation jump // move to jump address pAnim->scriptIndex++; // jump to new frame position pAnim->scriptIndex += (int32)FROM_LE_32(pAni[pAnim->scriptIndex].op); // go fetch a real image break; case ANI_HFLIP: // flip animated object horizontally // next opcode pAnim->scriptIndex++; MultiHorizontalFlip(pAnim->pObject); // go fetch a real image break; case ANI_VFLIP: // flip animated object vertically // next opcode pAnim->scriptIndex++; MultiVerticalFlip(pAnim->pObject); // go fetch a real image break; case ANI_HVFLIP: // flip animated object in both directions // next opcode pAnim->scriptIndex++; MultiHorizontalFlip(pAnim->pObject); MultiVerticalFlip(pAnim->pObject); // go fetch a real image break; case ANI_ADJUSTX: // adjust animated object x animation point // move to x adjustment operand pAnim->scriptIndex++; MultiAdjustXY(pAnim->pObject, (int32)FROM_LE_32(pAni[pAnim->scriptIndex].op), 0); // next opcode pAnim->scriptIndex++; // go fetch a real image break; case ANI_ADJUSTY: // adjust animated object y animation point // move to y adjustment operand pAnim->scriptIndex++; MultiAdjustXY(pAnim->pObject, 0, (int32)FROM_LE_32(pAni[pAnim->scriptIndex].op)); // next opcode pAnim->scriptIndex++; // go fetch a real image break; case ANI_ADJUSTXY: // adjust animated object x & y animation points { int x, y; // move to x adjustment operand pAnim->scriptIndex++; x = (int32)FROM_LE_32(pAni[pAnim->scriptIndex].op); // move to y adjustment operand pAnim->scriptIndex++; y = (int32)FROM_LE_32(pAni[pAnim->scriptIndex].op); MultiAdjustXY(pAnim->pObject, x, y); // next opcode pAnim->scriptIndex++; // go fetch a real image break; } case ANI_NOSLEEP: // do not sleep for this frame // next opcode pAnim->scriptIndex++; // indicate not to sleep return ScriptNoSleep; case ANI_CALL: // call routine // move to function address pAnim->scriptIndex++; // make function call // REMOVED BUGGY CODE // pFunc is a function pointer that's part of a union and is assumed to be 32-bits. // There is no known place where a function pointer is stored inside the animation // scripts, something which wouldn't have worked anyway. Having played through the // entire game, there hasn't been any occurence of this case, so just error out here // in case we missed something (highly unlikely though) error("ANI_CALL opcode encountered! Please report this error to the ScummVM team"); //(*pAni[pAnim->scriptIndex].pFunc)(pAnim); return ScriptSleep; // for compilers that don't support NORETURN #if 0 // next opcode pAnim->scriptIndex++; // go fetch a real image break; #endif case ANI_HIDE: // hide animated object MultiHideObject(pAnim->pObject); // next opcode pAnim->scriptIndex++; // dont skip a sleep return ScriptSleep; default: // must be an actual animation frame handle // set objects new animation frame pAnim->pObject->hShape = FROM_LE_32(pAni[pAnim->scriptIndex].hFrame); // re-shape the object MultiReshape(pAnim->pObject); // next opcode pAnim->scriptIndex++; // dont skip a sleep return ScriptSleep; } } }
static void LoadScene(SCNHANDLE scene, int entry) { uint i; TP_INIT init; const SCENE_STRUC *ss; const ENTRANCE_STRUC *es; // Scene handle SceneHandle = scene; // Save scene handle in case of Save_Scene() LockMem(SceneHandle); // Make sure scene is loaded LockScene(SceneHandle); // Prevent current scene from being discarded if (TinselV2) { // CdPlay() stuff byte *cptr = FindChunk(scene, CHUNK_CDPLAY_FILENUM); assert(cptr); i = READ_LE_UINT32(cptr); assert(i < 512); cptr = FindChunk(scene, CHUNK_CDPLAY_FILENAME); assert(cptr); SetCdPlaySceneDetails(i, (const char *)cptr); } // Find scene structure ss = GetSceneStruc(FindChunk(scene, CHUNK_SCENE)); assert(ss != NULL); if (TinselV2) { // Handle to scene description newestScene = FROM_LE_32(ss->hSceneDesc); // Music stuff char *cptr = (char *)FindChunk(scene, CHUNK_MUSIC_FILENAME); assert(cptr); _vm->_pcmMusic->setMusicSceneDetails(FROM_LE_32(ss->hMusicScript), FROM_LE_32(ss->hMusicSegment), cptr); } if (entry == NO_ENTRY_NUM) { // Restoring scene // Initialise all the polygons for this scene InitPolygons(FROM_LE_32(ss->hPoly), FROM_LE_32(ss->numPoly), true); // Initialise the actors for this scene StartTaggedActors(FROM_LE_32(ss->hTaggedActor), FROM_LE_32(ss->numTaggedActor), false); if (TinselV2) // Returning from cutscene SendSceneTinselProcess(RESTORE); } else { // Genuine new scene // Initialise all the polygons for this scene InitPolygons(FROM_LE_32(ss->hPoly), FROM_LE_32(ss->numPoly), false); // Initialise the actors for this scene StartTaggedActors(FROM_LE_32(ss->hTaggedActor), FROM_LE_32(ss->numTaggedActor), true); // Run the appropriate entrance code (if any) es = (const ENTRANCE_STRUC *)LockMem(FROM_LE_32(ss->hEntrance)); for (i = 0; i < FROM_LE_32(ss->numEntrance); i++) { if (FROM_LE_32(es->eNumber) == (uint)entry) { if (es->hScript) { init.event = STARTUP; init.hTinselCode = es->hScript; g_scheduler->createProcess(PID_TCODE, SceneTinselProcess, &init, sizeof(init)); } break; } // Move to next entrance if (TinselV2) ++es; else es = (const ENTRANCE_STRUC *)((const byte *)es + 8); } if (i == FROM_LE_32(ss->numEntrance)) error("Non-existant scene entry number"); if (ss->hSceneScript) { init.event = STARTUP; init.hTinselCode = ss->hSceneScript; g_scheduler->createProcess(PID_TCODE, SceneTinselProcess, &init, sizeof(init)); } } // Default refer type SetDefaultRefer(FROM_LE_32(ss->defRefer)); // Scene's processes SceneProcesses(FROM_LE_32(ss->numProcess), FROM_LE_32(ss->hProcess)); }
/** * Initialise a object using a OBJ_INIT structure to supply parameters. * @param pInitTbl Pointer to object initialisation table */ OBJECT *InitObject(const OBJ_INIT *pInitTbl) { // allocate a new object OBJECT *pObj = AllocObject(); // make sure object created assert(pObj != NULL); // set objects shape pObj->hImg = pInitTbl->hObjImg; // set objects ID pObj->oid = pInitTbl->objID; // set objects flags pObj->flags = DMA_CHANGED | pInitTbl->objFlags; // set objects Z position pObj->zPos = pInitTbl->objZ; // get pointer to image if (pInitTbl->hObjImg) { int aniX, aniY; // objects animation offsets PALQ *pPalQ = NULL; // palette queue pointer const IMAGE *pImg = (const IMAGE *)LockMem(pInitTbl->hObjImg); // handle to image if (pImg->hImgPal) { // allocate a palette for this object pPalQ = AllocPalette(FROM_LE_32(pImg->hImgPal)); // make sure palette allocated assert(pPalQ != NULL); } // assign palette to object pObj->pPal = pPalQ; // set objects size pObj->width = FROM_LE_16(pImg->imgWidth); pObj->height = FROM_LE_16(pImg->imgHeight) & ~C16_FLAG_MASK; pObj->flags &= ~C16_FLAG_MASK; pObj->flags |= FROM_LE_16(pImg->imgHeight) & C16_FLAG_MASK; // set objects bitmap definition pObj->hBits = FROM_LE_32(pImg->hImgBits); // get animation offset of object GetAniOffset(pObj->hImg, pInitTbl->objFlags, &aniX, &aniY); // set objects X position - subtract ani offset pObj->xPos = intToFrac(pInitTbl->objX - aniX); // set objects Y position - subtract ani offset pObj->yPos = intToFrac(pInitTbl->objY - aniY); } else { // no image handle - null image // set objects X position pObj->xPos = intToFrac(pInitTbl->objX); // set objects Y position pObj->yPos = intToFrac(pInitTbl->objY); } // return new object return pObj; }
byte *ScEngine::getCompiledScript(const char *filename, uint32 *outSize, bool ignoreCache) { // is script in cache? if (!ignoreCache) { for (int i = 0; i < MAX_CACHED_SCRIPTS; i++) { if (_cachedScripts[i] && scumm_stricmp(_cachedScripts[i]->_filename.c_str(), filename) == 0) { _cachedScripts[i]->_timestamp = g_system->getMillis(); *outSize = _cachedScripts[i]->_size; return _cachedScripts[i]->_buffer; } } } // nope, load it byte *compBuffer; uint32 compSize; uint32 size; byte *buffer = BaseEngine::instance().getFileManager()->readWholeFile(filename, &size); if (!buffer) { _gameRef->LOG(0, "ScEngine::GetCompiledScript - error opening script '%s'", filename); return nullptr; } // needs to be compiled? if (FROM_LE_32(*(uint32 *)buffer) == SCRIPT_MAGIC) { compBuffer = buffer; compSize = size; } else { if (!_compilerAvailable) { _gameRef->LOG(0, "ScEngine::GetCompiledScript - script '%s' needs to be compiled but compiler is not available", filename); delete[] buffer; return nullptr; } // This code will never be called, since _compilerAvailable is const false. // It's only here in the event someone would want to reinclude the compiler. error("Script needs compilation, ScummVM does not contain a WME compiler"); } byte *ret = nullptr; // add script to cache CScCachedScript *cachedScript = new CScCachedScript(filename, compBuffer, compSize); if (cachedScript) { int index = 0; uint32 minTime = g_system->getMillis(); for (int i = 0; i < MAX_CACHED_SCRIPTS; i++) { if (_cachedScripts[i] == nullptr) { index = i; break; } else if (_cachedScripts[i]->_timestamp <= minTime) { minTime = _cachedScripts[i]->_timestamp; index = i; } } if (_cachedScripts[index] != nullptr) { delete _cachedScripts[index]; } _cachedScripts[index] = cachedScript; ret = cachedScript->_buffer; *outSize = cachedScript->_size; } // cleanup delete[] buffer; return ret; }
int Logic::runScript2(byte *scriptData, byte *objectData, byte *offsetPtr) { // Interestingly, unlike our BASS engine the stack is a local variable. // I don't know whether or not this is relevant to the working of the // BS2 engine. int32 stack[STACK_SIZE]; int32 stackPtr = 0; uint32 offset = READ_LE_UINT32(offsetPtr); ResHeader header; header.read(scriptData); scriptData += ResHeader::size() + ObjectHub::size(); // The script data format: // int32_TYPE 1 Size of variable space in bytes // ... The variable space // int32_TYPE 1 numberOfScripts // int32_TYPE numberOfScripts The offsets for each script // Initialise some stuff uint32 ip = 0; // Code pointer int scriptNumber; // Get the start of variables and start of code byte *localVars = scriptData + 4; byte *code = scriptData + READ_LE_UINT32(scriptData) + 4; uint32 noScripts = READ_LE_UINT32(code); code += 4; byte *offsetTable = code; if (offset < noScripts) { ip = READ_LE_UINT32(offsetTable + offset * 4); scriptNumber = offset; debug(8, "Starting script %d from %d", scriptNumber, ip); } else { uint i; ip = offset; for (i = 1; i < noScripts; i++) { if (READ_LE_UINT32(offsetTable + 4 * i) >= ip) break; } scriptNumber = i - 1; debug(8, "Resuming script %d from %d", scriptNumber, ip); } // There are a couple of known script bugs related to interacting with // certain objects. We try to work around a few of them. bool checkMopBug = false; bool checkPyramidBug = false; bool checkElevatorBug = false; if (scriptNumber == 2) { if (strcmp((char *)header.name, "mop_73") == 0) checkMopBug = true; else if (strcmp((char *)header.name, "titipoco_81") == 0) checkPyramidBug = true; else if (strcmp((char *)header.name, "lift_82") == 0) checkElevatorBug = true; } code += noScripts * 4; // Code should now be pointing at an identifier and a checksum byte *checksumBlock = code; code += 4 * 3; if (READ_LE_UINT32(checksumBlock) != 12345678) { error("Invalid script in object %s", header.name); return 0; } int32 codeLen = READ_LE_UINT32(checksumBlock + 4); int32 checksum = 0; for (int i = 0; i < codeLen; i++) checksum += (unsigned char) code[i]; if (checksum != (int32)READ_LE_UINT32(checksumBlock + 8)) { debug(1, "Checksum error in object %s", header.name); // This could be bad, but there has been a report about someone // who had problems running the German version because of // checksum errors. Could there be a version where checksums // weren't properly calculated? } bool runningScript = true; int parameterReturnedFromMcodeFunction = 0; // Allow scripts to return things int savedStartOfMcode = 0; // For saving start of mcode commands while (runningScript) { int i; int32 a, b; int curCommand, parameter, value; // Command and parameter variables int retVal; int caseCount; bool foundCase; byte *ptr; curCommand = code[ip++]; switch (curCommand) { // Script-related opcodes case CP_END_SCRIPT: // End the script runningScript = false; // WORKAROUND: The dreaded pyramid bug makes the torch // untakeable when you speak to Titipoco. This is // because one of the conditions for the torch to be // takeable is that Titipoco isn't doing anything out // of the ordinary. Global variable 913 has to be 0 to // signify that he is in his "idle" state. // // Unfortunately, simply the act of speaking to him // sets variable 913 to 1 (probably to stop him from // turning around every now and then). The script may // then go on to set the variable to different values // to trigger various behaviours in him, but if you // have run out of these cases the script won't ever // set it back to 0 again. // // So if his click hander finishes, and variable 913 is // 1, we set it back to 0 manually. if (checkPyramidBug && readVar(913) == 1) { warning("Working around pyramid bug: Resetting Titipoco's state"); writeVar(913, 0); } // WORKAROUND: The not-so-known-but-should-be-dreaded // elevator bug. // // The click handler for the top of the elevator only // handles using the elevator, not examining it. When // examining it, the mouse cursor is removed but never // restored. if (checkElevatorBug && readVar(RIGHT_BUTTON)) { warning("Working around elevator bug: Restoring mouse pointer"); fnAddHuman(NULL); } debug(9, "CP_END_SCRIPT"); break; case CP_QUIT: // Quit out for a cycle WRITE_LE_UINT32(offsetPtr, ip); debug(9, "CP_QUIT"); return 0; case CP_TERMINATE: // Quit out immediately without affecting the offset // pointer debug(9, "CP_TERMINATE"); return 3; case CP_RESTART_SCRIPT: // Start the script again ip = FROM_LE_32(offsetTable[scriptNumber]); debug(9, "CP_RESTART_SCRIPT"); break; // Stack-related opcodes case CP_PUSH_INT32: // Push a long word value on to the stack Read32ip(parameter); push(parameter); debug(9, "CP_PUSH_INT32: %d", parameter); break; case CP_PUSH_LOCAL_VAR32: // Push the contents of a local variable Read16ip(parameter); push(READ_LE_UINT32(localVars + parameter)); debug(9, "CP_PUSH_LOCAL_VAR32: localVars[%d] => %d", parameter / 4, READ_LE_UINT32(localVars + parameter)); break; case CP_PUSH_GLOBAL_VAR32: // Push a global variable Read16ip(parameter); push(readVar(parameter)); debug(9, "CP_PUSH_GLOBAL_VAR32: scriptVars[%d] => %d", parameter, readVar(parameter)); break; case CP_PUSH_LOCAL_ADDR: // Push the address of a local variable // From what I understand, some scripts store data // (e.g. mouse pointers) in their local variable space // from the very beginning, and use this mechanism to // pass that data to the opcode function. I don't yet // know the conceptual difference between this and the // CP_PUSH_DEREFERENCED_STRUCTURE opcode. Read16ip(parameter); push_ptr(localVars + parameter); debug(9, "CP_PUSH_LOCAL_ADDR: &localVars[%d] => %p", parameter / 4, localVars + parameter); break; case CP_PUSH_STRING: // Push the address of a string on to the stack // Get the string size Read8ip(parameter); // ip now points to the string ptr = code + ip; push_ptr(ptr); debug(9, "CP_PUSH_STRING: \"%s\"", ptr); ip += (parameter + 1); break; case CP_PUSH_DEREFERENCED_STRUCTURE: // Push the address of a dereferenced structure Read32ip(parameter); ptr = objectData + 4 + ResHeader::size() + ObjectHub::size() + parameter; push_ptr(ptr); debug(9, "CP_PUSH_DEREFERENCED_STRUCTURE: %d => %p", parameter, ptr); break; case CP_POP_LOCAL_VAR32: // Pop a value into a local word variable Read16ip(parameter); value = pop(); WRITE_LE_UINT32(localVars + parameter, value); debug(9, "CP_POP_LOCAL_VAR32: localVars[%d] = %d", parameter / 4, value); break; case CP_POP_GLOBAL_VAR32: // Pop a global variable Read16ip(parameter); value = pop(); // WORKAROUND for bug #1214168: The not-at-all dreaded // mop bug. // // At the London Docks, global variable 1003 keeps // track of Nico: // // 0: Hiding behind the first crate. // 1: Hiding behind the second crate. // 2: Standing in plain view on the deck. // 3: Hiding on the roof. // // The bug happens when trying to pick up the mop while // hiding on the roof. Nico climbs down, the mop is // picked up, but the variable remains set to 3. // Visually, everything looks ok. But as far as the // scripts are concerned, she's still hiding up on the // roof. This is not fatal, but leads to a number of // glitches until the state is corrected. E.g. trying // to climb back up the ladder will cause Nico to climb // down again. // // Global variable 1017 keeps track of the mop. Setting // it to 2 means that the mop has been picked up. We // use that as the signal that Nico's state needs to be // updated as well. if (checkMopBug && parameter == 1017 && readVar(1003) != 2) { warning("Working around mop bug: Setting Nico's state"); writeVar(1003, 2); } writeVar(parameter, value); debug(9, "CP_POP_GLOBAL_VAR32: scriptsVars[%d] = %d", parameter, value); break; case CP_ADDNPOP_LOCAL_VAR32: Read16ip(parameter); value = READ_LE_UINT32(localVars + parameter) + pop(); WRITE_LE_UINT32(localVars + parameter, value); debug(9, "CP_ADDNPOP_LOCAL_VAR32: localVars[%d] => %d", parameter / 4, value); break; case CP_SUBNPOP_LOCAL_VAR32: Read16ip(parameter); value = READ_LE_UINT32(localVars + parameter) - pop(); WRITE_LE_UINT32(localVars + parameter, value); debug(9, "CP_SUBNPOP_LOCAL_VAR32: localVars[%d] => %d", parameter / 4, value); break; case CP_ADDNPOP_GLOBAL_VAR32: // Add and pop a global variable Read16ip(parameter); value = readVar(parameter) + pop(); writeVar(parameter, value); debug(9, "CP_ADDNPOP_GLOBAL_VAR32: scriptVars[%d] => %d", parameter, value); break; case CP_SUBNPOP_GLOBAL_VAR32: // Sub and pop a global variable Read16ip(parameter); value = readVar(parameter) - pop(); writeVar(parameter, value); debug(9, "CP_SUBNPOP_GLOBAL_VAR32: scriptVars[%d] => %d", parameter, value); break; // Jump opcodes case CP_SKIPONTRUE: // Skip if the value on the stack is true Read32ipLeaveip(parameter); value = pop(); if (!value) { ip += 4; debug(9, "CP_SKIPONTRUE: %d (IS FALSE (NOT SKIPPED))", parameter); } else { ip += parameter; debug(9, "CP_SKIPONTRUE: %d (IS TRUE (SKIPPED))", parameter); } break; case CP_SKIPONFALSE: // Skip if the value on the stack is false Read32ipLeaveip(parameter); value = pop(); if (value) { ip += 4; debug(9, "CP_SKIPONFALSE: %d (IS TRUE (NOT SKIPPED))", parameter); } else { ip += parameter; debug(9, "CP_SKIPONFALSE: %d (IS FALSE (SKIPPED))", parameter); } break; case CP_SKIPALWAYS: // skip a block Read32ipLeaveip(parameter); ip += parameter; debug(9, "CP_SKIPALWAYS: %d", parameter); break; case CP_SWITCH: // switch value = pop(); Read32ip(caseCount); // Search the cases foundCase = false; for (i = 0; i < caseCount && !foundCase; i++) { if (value == (int32)READ_LE_UINT32(code + ip)) { // We have found the case, so lets // jump to it foundCase = true; ip += READ_LE_UINT32(code + ip + 4); } else ip += 4 * 2; } // If we found no matching case then use the default if (!foundCase) ip += READ_LE_UINT32(code + ip); debug(9, "CP_SWITCH: [SORRY, NO DEBUG INFO]"); break; case CP_SAVE_MCODE_START: // Save the start position on an mcode instruction in // case we need to restart it again savedStartOfMcode = ip - 1; debug(9, "CP_SAVE_MCODE_START"); break; case CP_CALL_MCODE: // Call an mcode routine Read16ip(parameter); assert(parameter < ARRAYSIZE(opcodes)); // amount to adjust stack by (no of parameters) Read8ip(value); debug(9, "CP_CALL_MCODE: '%s', %d", opcodes[parameter].desc, value); stackPtr -= value; assert(stackPtr >= 0); retVal = (this->*opcodes[parameter].proc)(&stack[stackPtr]); switch (retVal & 7) { case IR_STOP: // Quit out for a cycle WRITE_LE_UINT32(offsetPtr, ip); return 0; case IR_CONT: // Continue as normal break; case IR_TERMINATE: // Return without updating the offset return 2; case IR_REPEAT: // Return setting offset to start of this // function call WRITE_LE_UINT32(offsetPtr, savedStartOfMcode); return 0; case IR_GOSUB: // that's really neat WRITE_LE_UINT32(offsetPtr, ip); return 2; default: error("Bad return code (%d) from '%s'", retVal & 7, opcodes[parameter].desc); } parameterReturnedFromMcodeFunction = retVal >> 3; break; case CP_JUMP_ON_RETURNED: // Jump to a part of the script depending on // the return value from an mcode routine // Get the maximum value Read8ip(parameter); debug(9, "CP_JUMP_ON_RETURNED: %d => %d", parameterReturnedFromMcodeFunction, READ_LE_UINT32(code + ip + parameterReturnedFromMcodeFunction * 4)); ip += READ_LE_UINT32(code + ip + parameterReturnedFromMcodeFunction * 4); break; // Operators case OP_ISEQUAL: b = pop(); a = pop(); push(a == b); debug(9, "OP_ISEQUAL: RESULT = %d", a == b); break; case OP_NOTEQUAL: b = pop(); a = pop(); push(a != b); debug(9, "OP_NOTEQUAL: RESULT = %d", a != b); break; case OP_GTTHAN: b = pop(); a = pop(); push(a > b); debug(9, "OP_GTTHAN: RESULT = %d", a > b); break; case OP_LSTHAN: b = pop(); a = pop(); push(a < b); debug(9, "OP_LSTHAN: RESULT = %d", a < b); break; case OP_GTTHANE: b = pop(); a = pop(); push(a >= b); debug(9, "OP_GTTHANE: RESULT = %d", a >= b); break; case OP_LSTHANE: b = pop(); a = pop(); push(a <= b); debug(9, "OP_LSTHANE: RESULT = %d", a <= b); break; case OP_PLUS: b = pop(); a = pop(); push(a + b); debug(9, "OP_PLUS: RESULT = %d", a + b); break; case OP_MINUS: b = pop(); a = pop(); push(a - b); debug(9, "OP_MINUS: RESULT = %d", a - b); break; case OP_TIMES: b = pop(); a = pop(); push(a * b); debug(9, "OP_TIMES: RESULT = %d", a * b); break; case OP_DIVIDE: b = pop(); a = pop(); push(a / b); debug(9, "OP_DIVIDE: RESULT = %d", a / b); break; case OP_ANDAND: b = pop(); a = pop(); push(a && b); debug(9, "OP_ANDAND: RESULT = %d", a && b); break; case OP_OROR: b = pop(); a = pop(); push(a || b); debug(9, "OP_OROR: RESULT = %d", a || b); break; // Debugging opcodes, I think case CP_DEBUGON: debug(9, "CP_DEBUGON"); break; case CP_DEBUGOFF: debug(9, "CP_DEBUGOFF"); break; case CP_TEMP_TEXT_PROCESS: Read32ip(parameter); debug(9, "CP_TEMP_TEXT_PROCESS: %d", parameter); break; default: error("Invalid script command %d", curCommand); return 3; } } return 1; }
uint32 File::readLong() { uint32 v; read(&v, sizeof(uint32)); return FROM_LE_32(v); }
/** * Skip the specified number of frames. * @param pAnim Animation data structure * @param numFrames Number of frames to skip */ void SkipFrames(ANIM *pAnim, int numFrames) { // get a pointer to the script const ANI_SCRIPT *pAni = (const ANI_SCRIPT *)LockMem(pAnim->hScript); if (!TinselV2 && (numFrames <= 0)) // do nothing return; while (1) { // repeat until a real image switch ((int32)FROM_LE_32(pAni[pAnim->scriptIndex].op)) { case ANI_END: // end of animation script // going off the end is probably a error, but only in Tinsel 1 if (!TinselV2) error("SkipFrames(): formally 'assert(0)!'"); return; case ANI_JUMP: // do animation jump // move to jump address pAnim->scriptIndex++; // jump to new frame position pAnim->scriptIndex += (int32)FROM_LE_32(pAni[pAnim->scriptIndex].op); if (TinselV2) // Done if skip to jump return; break; case ANI_HFLIP: // flip animated object horizontally // next opcode pAnim->scriptIndex++; MultiHorizontalFlip(pAnim->pObject); break; case ANI_VFLIP: // flip animated object vertically // next opcode pAnim->scriptIndex++; MultiVerticalFlip(pAnim->pObject); break; case ANI_HVFLIP: // flip animated object in both directions // next opcode pAnim->scriptIndex++; MultiHorizontalFlip(pAnim->pObject); MultiVerticalFlip(pAnim->pObject); break; case ANI_ADJUSTX: // adjust animated object x animation point // move to x adjustment operand pAnim->scriptIndex++; MultiAdjustXY(pAnim->pObject, (int32)FROM_LE_32(pAni[pAnim->scriptIndex].op), 0); // next opcode pAnim->scriptIndex++; break; case ANI_ADJUSTY: // adjust animated object y animation point // move to y adjustment operand pAnim->scriptIndex++; MultiAdjustXY(pAnim->pObject, 0, (int32)FROM_LE_32(pAni[pAnim->scriptIndex].op)); // next opcode pAnim->scriptIndex++; break; case ANI_ADJUSTXY: // adjust animated object x & y animation points { int x, y; // move to x adjustment operand pAnim->scriptIndex++; x = (int32)FROM_LE_32(pAni[pAnim->scriptIndex].op); // move to y adjustment operand pAnim->scriptIndex++; y = (int32)FROM_LE_32(pAni[pAnim->scriptIndex].op); MultiAdjustXY(pAnim->pObject, x, y); // next opcode pAnim->scriptIndex++; break; } case ANI_NOSLEEP: // do not sleep for this frame // next opcode pAnim->scriptIndex++; break; case ANI_CALL: // call routine // skip function address pAnim->scriptIndex += 2; break; case ANI_HIDE: // hide animated object // next opcode pAnim->scriptIndex++; break; default: // must be an actual animation frame handle // one less frame if (numFrames == 0) return; if (numFrames == -1 || numFrames-- > 0) { // next opcode pAnim->scriptIndex++; } else { // set objects new animation frame pAnim->pObject->hShape = FROM_LE_32(pAni[pAnim->scriptIndex].hFrame); // re-shape the object MultiReshape(pAnim->pObject); // we have skipped to the correct place return; } break; } } }
/** * Tinsel 2 Moving actor process * - 1 per moving actor in current scene. */ void T2MoverProcess(CORO_PARAM, const void *param) { CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); // Get the co-ordinates - copied to process when it was created const MAINIT *rpos = (const MAINIT *)param; PMOVER pMover = rpos->pMover; int i; FILM *pFilm; PMULTI_INIT pmi; CORO_BEGIN_CODE(_ctx); for (i = 0; i < TOTAL_SCALES; i++) { if (pMover->walkReels[i][FORWARD]) break; } assert(i < TOTAL_SCALES); InitMover(pMover); InitialPathChecks(pMover, rpos->X, rpos->Y); pFilm = (FILM *)LockMem(pMover->walkReels[i][FORWARD]); // Any old reel pmi = (PMULTI_INIT)LockMem(FROM_LE_32(pFilm->reels[0].mobj)); // Poke in the background palette PokeInPalette(pmi); pMover->actorObj = MultiInitObject(pmi); pMover->actorID = pMover->actorID; pMover->bActive = true; // add it to display list MultiInsertObject( GetPlayfieldList(FIELD_WORLD), pMover->actorObj ); InitStepAnimScript(&pMover->actorAnim, pMover->actorObj, pFilm->reels[0].script, ONE_SECOND/pFilm->frate); pMover->stepCount = 0; MultiSetAniXY(pMover->actorObj, pMover->objX, pMover->objY); // If no path, just use first path in the scene if (pMover->hCpath != NOPOLY) SetMoverZ(pMover, pMover->objY, GetPolyZfactor(pMover->hCpath)); else SetMoverZ(pMover, pMover->objY, GetPolyZfactor(FirstPathPoly())); // Make him the right size SetMoverStanding(pMover); HideMover(pMover); // Allows a play to come in before this appears pMover->bHidden = false; // ...but don't stay hidden for (;;) { if (pMover->bSpecReel) { if (!pMover->bHidden) StepAnimScript(&pMover->actorAnim); } else DoMoveActor(pMover); CheckBrightness(pMover); CORO_SLEEP(1); } CORO_END_CODE; }