Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

4
engines/tinsel/POTFILES Normal file
View File

@@ -0,0 +1,4 @@
engines/tinsel/detection_tables.h
engines/tinsel/metaengine.cpp
engines/tinsel/saveload.cpp
engines/tinsel/tinsel.cpp

1694
engines/tinsel/actors.cpp Normal file

File diff suppressed because it is too large Load Diff

228
engines/tinsel/actors.h Normal file
View File

@@ -0,0 +1,228 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* Prototypes of actor functions
*/
#ifndef TINSEL_ACTOR_H // prevent multiple includes
#define TINSEL_ACTOR_H
#include "tinsel/dw.h" // for SCNHANDLE
#include "tinsel/events.h" // for TINSEL_EVENT
#include "tinsel/palette.h" // for COLORREF
#include "tinsel/movers.h" // for MOVER *
namespace Common {
class Serializer;
}
namespace Tinsel {
struct FREEL;
struct INT_CONTEXT;
struct MOVER;
struct OBJECT;
struct ACTORINFO;
struct Z_POSITIONS;
#define ACTORTAG_KEY 0x1000000
#define OTH_RELATEDACTOR 0x00000fff
#define OTH_RELATIVE 0x00001000
#define OTH_ABSOLUTE 0x00002000
#define MAX_TAGACTORS 10
#define NUM_ZPOSITIONS 200 // Reasonable-sounding number
#define MAX_REELS 6
struct TAGACTORDATA {
// Copies of compiled data
int id;
SCNHANDLE hTagText; // handle to tag text
int32 tagPortionV; // which portion is active
int32 tagPortionH; // which portion is active
SCNHANDLE hActorCode; // The actor's script
int tagFlags;
SCNHANDLE hOverrideTag; // Override tag.
};
struct SAVED_ACTOR {
short actorID;
short zFactor;
bool bAlive;
bool bHidden;
SCNHANDLE presFilm; ///< the film that reel belongs to
short presRnum; ///< the present reel number
short presPlayX, presPlayY;
};
struct Z_POSITIONS {
short actor;
short column;
int z;
};
struct ACTORDATA {
int32 masking; ///< type of actor masking (Tinsel V1)
SCNHANDLE hActorId; ///< handle actor ID string index
SCNHANDLE hActorCode; ///< handle to actor script
SCNHANDLE hTagText; // tag (Tinsel V2)
int32 tagPortionV; // defines tag area (Tinsel V2)
int32 tagPortionH; // defines tag area (Tinsel V2)
};
/*----------------------------------------------------------------------*/
class Actor {
public:
Actor();
virtual ~Actor();
int GetLeadId();
SCNHANDLE GetActorTagHandle(int ano);
void ToggleActor(int ano, bool show);
SCNHANDLE GetActorCode(int ano);
SCNHANDLE GetTaggedActorCode(int ano);
void RunCodeToCompletion(int ano);
int GetCount() { return _numActors; }
void RegisterActors(int num);
void SetLeadId(int rid);
bool ActorIsGhost(int actor);
void StartTaggedActors(SCNHANDLE ah, int numActors, bool bRunScript);
void DropActors(); // No actor reels running
void DisableActor(int actor);
void EnableActor(int actor);
void Tag_Actor(int ano, SCNHANDLE tagtext, int tp);
void UnTagActor(int ano);
void ReTagActor(int ano);
int TagType(int ano);
bool actorAlive(int ano);
int32 actorMaskType(int ano);
void GetActorPos(int ano, int *x, int *y);
void GetActorMidTop(int ano, int *x, int *y);
int GetActorLeft(int ano);
int GetActorRight(int ano);
int GetActorTop(int ano);
int GetActorBottom(int ano);
bool ActorHidden(int ano);
bool HideMovingActor(int id, int sf);
void unHideMovingActor(int id);
void restoreMovement(int id);
void storeActorReel(int ano, const FREEL *reel, SCNHANDLE hFilm, OBJECT *pobj, int reelnum, int x, int y);
const FREEL *actorReel(int ano);
void SetActorPlayFilm(int ano, SCNHANDLE hFilm);
SCNHANDLE GetActorPlayFilm(int ano);
void SetActorTalkFilm(int ano, SCNHANDLE hFilm);
SCNHANDLE GetActorTalkFilm(int ano);
void SetActorTalking(int ano, bool tf);
bool ActorIsTalking(int ano);
void SetActorLatestFilm(int ano, SCNHANDLE hFilm);
SCNHANDLE GetActorLatestFilm(int ano);
void UpdateActorEsc(int ano, bool escOn, int escEvent);
void UpdateActorEsc(int ano, int escEvent);
bool ActorEsc(int ano);
int ActorEev(int ano);
void StoreActorPos(int ano, int x, int y);
void StoreActorSteps(int ano, int steps);
int GetActorSteps(int ano);
void StoreActorZpos(int ano, int z, int column = -1);
int GetActorZpos(int ano, int column);
void IncLoopCount(int ano);
int GetLoopCount(int ano);
SCNHANDLE GetActorTag(int ano);
void FirstTaggedActor();
int NextTaggedActor();
int NextTaggedActor(int previous);
int AsetZPos(OBJECT *pObj, int y, int32 zFactor);
void storeActorAttr(int ano, int r1, int g1, int b1);
COLORREF GetActorRGB(int ano);
void SetActorRGB(int ano, COLORREF color);
void SetActorZfactor(int ano, uint32 zFactor);
uint32 GetActorZfactor(int ano);
void SetActorsOn();
void ActorsLife(int id, bool bAlive);
void dwEndActor(int ano);
void SetActorPointedTo(int actor, bool bPointedTo);
bool ActorIsPointedTo(int actor);
void SetActorTagWanted(int actor, bool bTagWanted, bool bCursor, SCNHANDLE hOverrideTag);
bool ActorTagIsWanted(int actor);
bool InHotSpot(int ano, int curX, int curY);
int FrontTaggedActor();
void GetActorTagPos(int actor, int *pTagX, int *pTagY, bool bAbsolute);
bool IsTaggedActor(int actor);
void StoreActorPresFilm(int ano, SCNHANDLE hFilm, int x, int y);
SCNHANDLE GetActorPresFilm(int ano);
int GetActorFilmNumber(int ano);
void StoreActorReel(int actor, int column, OBJECT *pObj);
void NotPlayingReel(int actor, int filmNumber, int column);
bool ActorReelPlaying(int actor, int column);
int SaveActors(SAVED_ACTOR *sActorInfo);
void RestoreActors(int numActors, SAVED_ACTOR *sActorInfo);
void SaveZpositions(void *zpp);
void RestoreZpositions(void *zpp);
void SaveActorZ(byte *saveActorZ);
void RestoreActorZ(byte *saveActorZ);
int TaggedActorIndex(int actor);
void syncAllActorsAlive(Common::Serializer &s);
private:
void StartActor(const ACTORDATA *ad, bool bRunScript);
void GetActorTagPortion(int ano, unsigned *top, unsigned *bottom, unsigned *left, unsigned *right);
ACTORINFO *_actorInfo;
COLORREF _defaultColor; // Text color
bool _actorsOn;
int ti;
TAGACTORDATA _taggedActors[MAX_TAGACTORS];
int _numTaggedActors;
uint8 *_zFactors;
Z_POSITIONS _zPositions[NUM_ZPOSITIONS];
int _leadActorId; // The lead actor
int _numActors; // The total number of actors in the game
};
/*----------------------------------------------------------------------*/
void ActorEvent(int ano, TINSEL_EVENT event, PLR_EVENT be);
void ActorEvent(CORO_PARAM, int ano, TINSEL_EVENT tEvent, bool bWait, int myEscape, bool *result = NULL);
void ActorEvent(int ano, TINSEL_EVENT tEvent, bool bWait, int myEscape, bool *result = NULL);
void ShowActor(CORO_PARAM, int ano);
void HideActor(CORO_PARAM, int ano);
void RestoreActorProcess(int id, INT_CONTEXT *pic, bool savegameFlag);
} // End of namespace Tinsel
#endif /* TINSEL_ACTOR_H */

169
engines/tinsel/adpcm.cpp Normal file
View File

@@ -0,0 +1,169 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/stream.h"
#include "common/util.h"
#include "tinsel/adpcm.h"
namespace Tinsel {
static const double TinselFilterTable[4][2] = {
{0, 0 },
{0.9375, 0},
{1.796875, -0.8125},
{1.53125, -0.859375}
};
void Tinsel_ADPCMStream::readBufferTinselHeader() {
uint8 start = _stream->readByte();
uint8 filterVal = (start & 0xC0) >> 6;
if ((start & 0x20) != 0) {
//Lower 6 bit are negative
// Negate
start = ~(start | 0xC0) + 1;
_status.predictor = (uint64)1 << start;
} else {
// Lower 6 bit are positive
// Truncate
start &= 0x1F;
_status.predictor = ((double) 1.0) / ((uint64)1 << start);
}
_status.K0 = TinselFilterTable[filterVal][0];
_status.K1 = TinselFilterTable[filterVal][1];
}
int16 Tinsel_ADPCMStream::decodeTinsel(int16 code, double eVal) {
double sample;
sample = (double)code;
sample *= eVal * _status.predictor;
sample += (_status.d0 * _status.K0) + (_status.d1 * _status.K1);
_status.d1 = _status.d0;
_status.d0 = sample;
return (int16) CLIP<double>(sample, -32768.0, 32767.0);
}
int Tinsel4_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) {
int samples;
uint16 data;
const double eVal = 1.142822265;
samples = 0;
assert(numSamples % 2 == 0);
while (samples < numSamples && !_stream->eos() && _stream->pos() < _endpos) {
if (_blockPos[0] == _blockAlign) {
readBufferTinselHeader();
_blockPos[0] = 0;
}
for (; samples < numSamples && _blockPos[0] < _blockAlign && !_stream->eos() && _stream->pos() < _endpos; samples += 2, _blockPos[0]++) {
// Read 1 byte = 8 bits = two 4 bit blocks
data = _stream->readByte();
buffer[samples] = decodeTinsel((data << 8) & 0xF000, eVal);
buffer[samples+1] = decodeTinsel((data << 12) & 0xF000, eVal);
}
}
return samples;
}
int Tinsel6_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) {
int samples;
const double eVal = 1.032226562;
samples = 0;
while (samples < numSamples && !_stream->eos() && _stream->pos() < _endpos) {
if (_blockPos[0] == _blockAlign) {
readBufferTinselHeader();
_blockPos[0] = 0;
_chunkPos = 0;
}
for (; samples < numSamples && _blockPos[0] < _blockAlign && !_stream->eos() && _stream->pos() < _endpos; samples++, _chunkPos = (_chunkPos + 1) % 4) {
switch (_chunkPos) {
case 0:
_chunkData = _stream->readByte();
buffer[samples] = decodeTinsel((_chunkData << 8) & 0xFC00, eVal);
break;
case 1:
_chunkData = (_chunkData << 8) | (_stream->readByte());
buffer[samples] = decodeTinsel((_chunkData << 6) & 0xFC00, eVal);
_blockPos[0]++;
break;
case 2:
_chunkData = (_chunkData << 8) | (_stream->readByte());
buffer[samples] = decodeTinsel((_chunkData << 4) & 0xFC00, eVal);
_blockPos[0]++;
break;
case 3:
_chunkData = (_chunkData << 8);
buffer[samples] = decodeTinsel((_chunkData << 2) & 0xFC00, eVal);
_blockPos[0]++;
break;
default:
break;
}
}
}
return samples;
}
int Tinsel8_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) {
int samples;
byte data;
const double eVal = 1.007843258;
samples = 0;
while (samples < numSamples && !_stream->eos() && _stream->pos() < _endpos) {
if (_blockPos[0] == _blockAlign) {
readBufferTinselHeader();
_blockPos[0] = 0;
}
for (; samples < numSamples && _blockPos[0] < _blockAlign && !_stream->eos() && _stream->pos() < _endpos; samples++, _blockPos[0]++) {
// Read 1 byte = 8 bits = one 8 bit block
data = _stream->readByte();
buffer[samples] = decodeTinsel(data << 8, eVal);
}
}
return samples;
}
} // End of namespace Tinsel

101
engines/tinsel/adpcm.h Normal file
View File

@@ -0,0 +1,101 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef TINSEL_ADPCM_H
#define TINSEL_ADPCM_H
#include "audio/decoders/adpcm_intern.h"
namespace Tinsel {
class Tinsel_ADPCMStream : public Audio::ADPCMStream {
protected:
struct {
// Tinsel
double predictor;
double K0, K1;
double d0, d1;
} _status;
void reset() override {
ADPCMStream::reset();
memset(&_status, 0, sizeof(_status));
}
int16 decodeTinsel(int16, double);
void readBufferTinselHeader();
public:
Tinsel_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign)
: ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) {
if (blockAlign == 0)
error("Tinsel_ADPCMStream(): blockAlign isn't specified");
if (channels != 1)
error("Tinsel_ADPCMStream(): Tinsel ADPCM only supports mono");
memset(&_status, 0, sizeof(_status));
}
};
class Tinsel4_ADPCMStream : public Tinsel_ADPCMStream {
public:
Tinsel4_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign)
: Tinsel_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) {}
int readBuffer(int16 *buffer, const int numSamples) override;
};
class Tinsel6_ADPCMStream : public Tinsel_ADPCMStream {
protected:
uint8 _chunkPos;
uint16 _chunkData;
void reset() override {
ADPCMStream::reset();
_chunkPos = 0;
_chunkData = 0;
}
public:
Tinsel6_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign)
: Tinsel_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) {
_chunkPos = 0;
_chunkData = 0;
}
int readBuffer(int16 *buffer, const int numSamples) override;
};
class Tinsel8_ADPCMStream : public Tinsel_ADPCMStream {
public:
Tinsel8_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign)
: Tinsel_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) {}
int readBuffer(int16 *buffer, const int numSamples) override;
};
} // End of namespace Tinsel
#endif

448
engines/tinsel/anim.cpp Normal file
View File

@@ -0,0 +1,448 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* This file contains utilities to handle object animation.
*/
#include "tinsel/anim.h"
#include "tinsel/handle.h"
#include "tinsel/multiobj.h" // multi-part object defintions etc.
#include "tinsel/object.h"
#include "tinsel/sched.h"
#include "tinsel/tinsel.h"
#include "common/textconsole.h"
#include "common/util.h"
namespace Tinsel {
/**
* 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 *)_vm->_handle->LockMem(pAnim->hScript);
while (1) { // repeat until a real image
debugC(DEBUG_DETAILED, kTinselDebugAnimations,
"DoNextFrame %ph index=%d, op=%xh", (const void *)pAnim, pAnim->scriptIndex,
FROM_32(pAni[pAnim->scriptIndex].op));
switch ((int32)FROM_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_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_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_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_32(pAni[pAnim->scriptIndex].op);
// move to y adjustment operand
pAnim->scriptIndex++;
y = (int32)FROM_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 occurrence 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_32(pAni[pAnim->scriptIndex].hFrame);
// re-shape the object
MultiReshape(pAnim->pObject);
// next opcode
pAnim->scriptIndex++;
// dont skip a sleep
return ScriptSleep;
}
}
}
/**
* Init a ANIM structure for single stepping through a animation script.
* @param pAnim Animation data structure
* @param pAniObj Object to animate
* @param hNewScript Script of multipart frames
* @param aniSpeed Sets speed of animation in frames
*/
void InitStepAnimScript(ANIM *pAnim, OBJECT *pAniObj, SCNHANDLE hNewScript, int aniSpeed) {
OBJECT *pObj; // multi-object list iterator
debugC(DEBUG_DETAILED, kTinselDebugAnimations,
"InitStepAnimScript Object=(%d,%d,%xh) script=%xh aniSpeed=%d rec=%ph",
!pAniObj ? 0 : fracToInt(pAniObj->xPos),
!pAniObj ? 0 : fracToInt(pAniObj->yPos),
!pAniObj ? 0 : pAniObj->hImg, hNewScript, aniSpeed, (void *)pAnim);
pAnim->aniDelta = 1; // will animate on next call to NextAnimRate
pAnim->pObject = pAniObj; // set object to animate
pAnim->hScript = hNewScript; // set animation script
pAnim->scriptIndex = 0; // start of script
pAnim->aniRate = aniSpeed; // set speed of animation
// reset flip flags for the object - let the script do the flipping
for (pObj = pAniObj; pObj != NULL; pObj = pObj->pSlave) {
AnimateObjectFlags(pObj, pObj->flags & ~(DMA_FLIPH | DMA_FLIPV),
pObj->hImg);
}
}
/**
* Execute the next command in a animation script.
* @param pAnim Animation data structure
*/
SCRIPTSTATE StepAnimScript(ANIM *pAnim) {
SCRIPTSTATE state;
if (--pAnim->aniDelta == 0) {
// re-init animation delta counter
pAnim->aniDelta = pAnim->aniRate;
if (TinselVersion >= 2)
state = DoNextFrame(pAnim);
else {
// move to next frame
while ((state = DoNextFrame(pAnim)) == ScriptNoSleep)
;
}
return state;
}
// indicate calling task should sleep
return ScriptSleep;
}
/**
* 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 *)_vm->_handle->LockMem(pAnim->hScript);
if ((TinselVersion <= 1) && (numFrames <= 0))
// do nothing
return;
while (1) { // repeat until a real image
switch ((int32)FROM_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 (TinselVersion <= 1)
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_32(pAni[pAnim->scriptIndex].op);
if (TinselVersion >= 2)
// 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_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_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_32(pAni[pAnim->scriptIndex].op);
// move to y adjustment operand
pAnim->scriptIndex++;
y = (int32)FROM_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_32(pAni[pAnim->scriptIndex].hFrame);
// re-shape the object
MultiReshape(pAnim->pObject);
// we have skipped to the correct place
return;
}
break;
}
}
}
/**
* About to jump or end
* @param pAnim Animation data structure
*/
bool AboutToJumpOrEnd(ANIM *pAnim) {
if (pAnim->aniDelta == 1) {
// get a pointer to the script
ANI_SCRIPT *pAni = (ANI_SCRIPT *)_vm->_handle->LockMem(pAnim->hScript);
int zzz = pAnim->scriptIndex;
for (;;) {
// repeat until a real image
switch (FROM_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;
}
} // End of namespace Tinsel

91
engines/tinsel/anim.h Normal file
View File

@@ -0,0 +1,91 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* Object animation definitions
*/
#ifndef TINSEL_ANIM_H // prevent multiple includes
#define TINSEL_ANIM_H
#include "tinsel/dw.h" // for SCNHANDLE
namespace Tinsel {
struct OBJECT;
/** animation structure */
struct ANIM {
int aniRate; ///< animation speed
int aniDelta; ///< animation speed delta counter
OBJECT *pObject; ///< object to animate (assumed to be multi-part)
uint32 hScript; ///< animation script handle
int scriptIndex; ///< current position in animation script
};
/** Animation script commands */
enum {
ANI_END = 0, ///< end of animation script
ANI_JUMP = 1, ///< animation script jump
ANI_HFLIP = 2, ///< flip animated object horizontally
ANI_VFLIP = 3, ///< flip animated object vertically
ANI_HVFLIP = 4, ///< flip animated object in both directions
ANI_ADJUSTX = 5, ///< adjust animated object x animation point
ANI_ADJUSTY = 6, ///< adjust animated object y animation point
ANI_ADJUSTXY = 7, ///< adjust animated object x & y animation points
ANI_NOSLEEP = 8, ///< do not sleep for this frame
ANI_CALL = 9, ///< call routine
ANI_HIDE = 10, ///< hide animated object
ANI_STOP = 11 ///< stop sound
};
/** animation script command possibilities */
union ANI_SCRIPT {
int32 op; ///< treat as an opcode or operand
uint32 hFrame; ///< treat as a animation frame handle
};
/*----------------------------------------------------------------------*\
|* Anim Function Prototypes *|
\*----------------------------------------------------------------------*/
/** states for DoNextFrame */
enum SCRIPTSTATE {ScriptFinished, ScriptNoSleep, ScriptSleep};
SCRIPTSTATE DoNextFrame( // Execute the next animation frame of a animation script
ANIM *pAnim); // animation data structure
void InitStepAnimScript( // Init a ANIM struct for single stepping through a animation script
ANIM *pAnim, // animation data structure
OBJECT *pAniObj, // object to animate
SCNHANDLE hNewScript, // handle to script of multipart frames
int aniSpeed); // sets speed of animation in frames
SCRIPTSTATE StepAnimScript( // Execute the next command in a animation script
ANIM *pAnim); // animation data structure
void SkipFrames( // Skip the specified number of frames
ANIM *pAnim, // animation data structure
int numFrames); // number of frames to skip
bool AboutToJumpOrEnd(ANIM *pAnim);
} // End of namespace Tinsel
#endif // TINSEL_ANIM_H

View File

@@ -0,0 +1,330 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* Background handling code.
*/
#include "tinsel/background.h"
#include "tinsel/cliprect.h" // object clip rect defs
#include "tinsel/font.h"
#include "tinsel/graphics.h"
#include "tinsel/multiobj.h"
#include "tinsel/sched.h" // process sheduler defs
#include "tinsel/object.h"
#include "tinsel/pid.h" // process identifiers
#include "tinsel/tinsel.h"
#include "tinsel/noir/spriter.h"
namespace Tinsel {
Background::Background(Font* font) : _font(font), _pCurBgnd(nullptr), _hBgPal(0), _BGspeed(0), _hBackground(0), _bDoFadeIn(false), _bgReels(0) {
for (int i = 0; i < MAX_BG; i++) {
_pBG[i] = nullptr;
_thisAnim[i].pObject = nullptr;
}
}
/**
* Called to initialize a background.
*/
void Background::InitBackground() {
// set current background
_pCurBgnd = new BACKGND();
_pCurBgnd->rgbSkyColor = BLACK;
_pCurBgnd->ptInitWorld = Common::Point(0, 0);
_pCurBgnd->rcScrollLimits = Common::Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
_pCurBgnd->refreshRate = 0; // no background update process
_pCurBgnd->pXscrollTable = nullptr;
_pCurBgnd->pYscrollTable = nullptr;
_pCurBgnd->bAutoErase = false;
int numPlayFields = 2;
if (TinselVersion == 3) {
numPlayFields = 9;
}
for (int i = 0; i < numPlayFields; ++i) {
PLAYFIELD playfield = {
NULL, // display list
0, // init field x
0, // init field y
0, // x vel
0, // y vel
Common::Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT), // clip rect
false // moved flag
};
_pCurBgnd->fieldArray.push_back(playfield);
}
// init background sky color
SetBgndColor(_pCurBgnd->rgbSkyColor);
}
/**
* Sets the xy position of the specified playfield in the current background.
* @param which Which playfield
* @param newXpos New x position
* @param newYpos New y position
*/
void Background::PlayfieldSetPos(unsigned int which, int newXpos, int newYpos) {
PLAYFIELD *pPlayfield; // pointer to relavent playfield
// make sure there is a background
assert(_pCurBgnd != NULL);
// make sure the playfield number is in range
assert(which < _pCurBgnd->fieldArray.size());
// get playfield pointer
pPlayfield = &_pCurBgnd->fieldArray[which];
// set new integer position
pPlayfield->fieldX = intToFrac(newXpos);
pPlayfield->fieldY = intToFrac(newYpos);
// set moved flag
pPlayfield->bMoved = true;
}
/**
* Returns the xy position of the specified playfield in the current background.
* @param which Which playfield
* @param pXpos Returns current x position
* @param pYpos Returns current y position
*/
void Background::PlayfieldGetPos(unsigned int which, int *pXpos, int *pYpos) {
PLAYFIELD *pPlayfield; // pointer to relavent playfield
// make sure there is a background
assert(_pCurBgnd != NULL);
// make sure the playfield number is in range
assert(which < _pCurBgnd->fieldArray.size());
// get playfield pointer
pPlayfield = &_pCurBgnd->fieldArray[which];
// get current integer position
*pXpos = fracToInt(pPlayfield->fieldX);
*pYpos = fracToInt(pPlayfield->fieldY);
}
/**
* Returns the x position of the center of the specified playfield
* @param which Which playfield
*/
int Background::PlayfieldGetCenterX(unsigned int which) {
PLAYFIELD *pPlayfield; // pointer to relavent playfield
// make sure there is a background
assert(_pCurBgnd != NULL);
// make sure the playfield number is in range
assert(which < _pCurBgnd->fieldArray.size());
// get playfield pointer
pPlayfield = &_pCurBgnd->fieldArray[which];
// get current integer position
return fracToInt(pPlayfield->fieldX) + SCREEN_WIDTH/2;
}
/**
* Returns the display list for the specified playfield.
* @param which Which playfield
*/
OBJECT **Background::GetPlayfieldList(unsigned int which) {
PLAYFIELD *pPlayfield; // pointer to relavent playfield
// make sure there is a background
assert(_pCurBgnd != NULL);
// make sure the playfield number is in range
assert(which < _pCurBgnd->fieldArray.size());
// get playfield pointer
pPlayfield = &_pCurBgnd->fieldArray[which];
// return the display list pointer for this playfield
return &pPlayfield->pDispList;
}
/**
* Draws all the playfield object lists for the current background.
* The playfield velocity is added to the playfield position in order
* to scroll each playfield before it is drawn.
*/
void Background::DrawBackgnd() {
PLAYFIELD *pPlay; // playfield pointer
int prevX, prevY; // save interger part of position
Common::Point ptWin; // window top left
if (_pCurBgnd == NULL)
return; // no current background
// scroll each background playfield
for (unsigned int i = 0; i < _pCurBgnd->fieldArray.size(); i++) {
// get pointer to correct playfield
pPlay = &_pCurBgnd->fieldArray[i];
// save integer part of position
prevX = fracToInt(pPlay->fieldX);
prevY = fracToInt(pPlay->fieldY);
// update scrolling
pPlay->fieldX += pPlay->fieldXvel;
pPlay->fieldY += pPlay->fieldYvel;
// convert fixed point window pos to a int
ptWin.x = fracToInt(pPlay->fieldX);
ptWin.y = fracToInt(pPlay->fieldY);
// set the moved flag if the playfield has moved
if (prevX != ptWin.x || prevY != ptWin.y)
pPlay->bMoved = true;
// sort the display list for this background - just in case somebody has changed object Z positions
SortObjectList(&pPlay->pDispList);
// generate clipping rects for all objects that have moved etc.
FindMovingObjects(&pPlay->pDispList, &ptWin,
&pPlay->rcClip, false, pPlay->bMoved);
// clear playfield moved flag
pPlay->bMoved = false;
}
// merge the clipping rectangles
MergeClipRect();
// redraw all playfields within the clipping rectangles
const RectList &clipRects = GetClipRects();
// Noir 3D model's cliprect is updated for rendering.
if (TinselVersion == 3) {
for (unsigned int i = 0; i < _pCurBgnd->fieldArray.size(); i++) {
// get pointer to correct playfield
pPlay = &_pCurBgnd->fieldArray[i];
// convert fixed point window pos to a int
ptWin.x = fracToInt(pPlay->fieldX);
ptWin.y = fracToInt(pPlay->fieldY);
for (OBJECT* pObj = pPlay->pDispList; pObj != NULL; pObj = pObj->pNext) {
if (pObj->flags & DMA_3D) {
_vm->_spriter->Draw(0, 0, 0, 0, 0);
// TODO: fix clip rect after the Spriter updates them
//AddClipRect(SpriterGetModelRect());
//AddClipRect(SpriterGetShadowRect());
//MergeClipRect();
} else {
for (RectList::const_iterator r = clipRects.begin(); r != clipRects.end(); ++r) {
Common::Rect rcPlayClip; // clip rect for this playfield
if (IntersectRectangle(rcPlayClip, pPlay->rcClip, *r)) {
// redraw all objects within this clipping rect
UpdateClipRectSingle(pObj, &ptWin, &rcPlayClip);
}
}
}
}
}
} else {
for (RectList::const_iterator r = clipRects.begin(); r != clipRects.end(); ++r) {
// clear the clip rectangle on the virtual screen
// for each background playfield
for (unsigned int i = 0; i < _pCurBgnd->fieldArray.size(); i++) {
Common::Rect rcPlayClip; // clip rect for this playfield
// get pointer to correct playfield
pPlay = &_pCurBgnd->fieldArray[i];
// convert fixed point window pos to a int
ptWin.x = fracToInt(pPlay->fieldX);
ptWin.y = fracToInt(pPlay->fieldY);
if (IntersectRectangle(rcPlayClip, pPlay->rcClip, *r))
// redraw all objects within this clipping rect
UpdateClipRect(&pPlay->pDispList, &ptWin, &rcPlayClip);
}
}
}
if (TinselVersion != 3) {
// transfer any new palettes to the video DAC
PalettesToVideoDAC();
}
// update the screen within the clipping rectangles
for (RectList::const_iterator r = clipRects.begin(); r != clipRects.end(); ++r) {
UpdateScreenRect(*r);
}
g_system->updateScreen();
// delete all the clipping rectangles
ResetClipRect();
}
int Background::BgWidth() {
assert(_pBG[0]);
return MultiRightmost(_pBG[0]) + 1;
}
int Background::BgHeight() {
assert(_pBG[0]);
return MultiLowest(_pBG[0]) + 1;
}
void Background::SetBackPal(SCNHANDLE hPal) {
_hBgPal = hPal;
_font->FettleFontPal(_hBgPal);
CreateTranslucentPalette(_hBgPal);
}
void Background::DropBackground() {
_pBG[0] = nullptr; // No background
if (TinselVersion <= 1)
_hBgPal = 0; // No background palette
}
void Background::ChangePalette(SCNHANDLE hPal) {
SwapPalette(FindPalette(_hBgPal), hPal);
SetBackPal(hPal);
}
void Background::WaitForBG(CORO_PARAM) {
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
while (_pBG[0] == nullptr) {
CORO_SLEEP(1);
}
CORO_END_CODE;
}
} // End of namespace Tinsel

168
engines/tinsel/background.h Normal file
View File

@@ -0,0 +1,168 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* Data structures used for handling backgrounds
*/
#ifndef TINSEL_BACKGND_H // prevent multiple includes
#define TINSEL_BACKGND_H
#include "common/array.h"
#include "common/coroutines.h"
#include "common/frac.h"
#include "common/rect.h"
#include "tinsel/anim.h" // for ANIM
#include "tinsel/dw.h" // for SCNHANDLE
#include "tinsel/object.h" // for OBJECT *
#include "tinsel/palette.h" // palette definitions
namespace Tinsel {
struct OBJECT;
/** Scrolling padding. Needed because scroll process does not normally run on every frame */
enum {
SCROLLX_PAD = 64,
SCROLLY_PAD = 64
};
/** When module BLK_INFO list is this long, switch from a binary to linear search */
#define LINEAR_SEARCH 5
/** background playfield structure - a playfield is a container for modules */
struct PLAYFIELD {
OBJECT *pDispList; ///< object display list for this playfield
frac_t fieldX; ///< current world x position of playfield
frac_t fieldY; ///< current world y position of playfield
frac_t fieldXvel; ///< current x velocity of playfield
frac_t fieldYvel; ///< current y velocity of playfield
Common::Rect rcClip; ///< clip rectangle for this playfield
bool bMoved; ///< set when playfield has moved
};
/** multi-playfield background structure - a backgnd is a container of playfields */
struct BACKGND {
COLORREF rgbSkyColor; ///< background sky color
Common::Point ptInitWorld; ///< initial world position
Common::Rect rcScrollLimits; ///< scroll limits
int refreshRate; ///< background update process refresh rate
frac_t *pXscrollTable; ///< pointer to x direction scroll table for this background
frac_t *pYscrollTable; ///< pointer to y direction scroll table for this background
Common::Array<PLAYFIELD> fieldArray; ///< list of all playfields for this background
bool bAutoErase; ///< when set - screen is cleared before anything is plotted (unused)
};
/*----------------------------------------------------------------------*\
|* Background Function Prototypes *|
\*----------------------------------------------------------------------*/
#define MAX_BG 10
class Font;
class Background {
public:
Background(Font* font);
void InitBackground();
void DrawBackgnd(); // Draws all playfields for the current background
/**
* Called before scene change.
*/
void DropBackground();
void ResetBackground() {
_pCurBgnd->fieldArray.clear();
delete _pCurBgnd;
_pCurBgnd = nullptr;
}
void StartupBackground(CORO_PARAM, SCNHANDLE hFilm);
void PlayfieldSetPos( // Sets the xy position of the specified playfield in the current background
unsigned int which, // which playfield
int newXpos, // new x position
int newYpos); // new y position
void PlayfieldGetPos( // Returns the xy position of the specified playfield in the current background
unsigned int which, // which playfield
int* pXpos, // returns current x position
int* pYpos); // returns current y position
int PlayfieldGetCenterX( // Returns the xy position of the specified playfield in the current background
unsigned int which); // which playfield
OBJECT** GetPlayfieldList( // Returns the display list for the specified playfield
unsigned int which); // which playfield
OBJECT* GetBgObject() { return _pBG[0]; }
void ChangePalette(SCNHANDLE hPal);
SCNHANDLE BgPal() { return _hBgPal; }
void SetDoFadeIn(bool tf) { _bDoFadeIn = tf; }
bool GetDoFadeIn() { return _bDoFadeIn; }
/**
* Return the current scene handle.
*/
SCNHANDLE GetBgroundHandle() { return _hBackground; }
/**
* Return the width of the current background.
*/
int BgWidth();
/**
* Return the height of the current background.
*/
int BgHeight();
void SetBackPal(SCNHANDLE hPal);
int getBgSpeed() { return _BGspeed; }
void WaitForBG(CORO_PARAM);
private:
Font *_font;
// current background
BACKGND *_pCurBgnd;
SCNHANDLE _hBgPal; // Background's palette
int _BGspeed;
SCNHANDLE _hBackground; // Current scene handle - stored in case of Save_Scene()
bool _bDoFadeIn;
public:
int _bgReels;
OBJECT *_pBG[MAX_BG];
ANIM _thisAnim[MAX_BG]; // used by BGmainProcess()
};
} // End of namespace Tinsel
#endif // TINSEL_BACKGND_H

202
engines/tinsel/bg.cpp Normal file
View File

@@ -0,0 +1,202 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* Plays the background film of a scene.
*/
#include "tinsel/anim.h"
#include "tinsel/background.h"
#include "tinsel/cursor.h"
#include "tinsel/dw.h"
#include "tinsel/faders.h"
#include "tinsel/film.h"
#include "tinsel/font.h"
#include "tinsel/handle.h"
#include "tinsel/multiobj.h"
#include "tinsel/object.h"
#include "tinsel/pcode.h" // CONTROL_STARTOFF
#include "tinsel/pid.h"
#include "tinsel/sched.h"
#include "tinsel/timers.h" // For ONE_SECOND constant
#include "tinsel/tinlib.h" // For Control()
#include "tinsel/tinsel.h"
#include "common/textconsole.h"
#include "common/util.h"
namespace Tinsel {
/**
* Run main animation that comprises the scene background.
*/
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 (_vm->_bg->_pBG[0] == NULL) {
/*** At start of scene ***/
if (TinselVersion <= 1) {
pReel = (const FREEL *)param;
// Get the MULTI_INIT structure
pmi = pReel->GetMultiInit();
// Initialize and insert the object, and initialize its script.
_vm->_bg->_pBG[0] = MultiInitObject(pmi);
MultiInsertObject(_vm->_bg->GetPlayfieldList(FIELD_WORLD), _vm->_bg->_pBG[0]);
InitStepAnimScript(&_vm->_bg->_thisAnim[0], _vm->_bg->_pBG[0], FROM_32(pReel->script), _vm->_bg->getBgSpeed());
_vm->_bg->_bgReels = 1;
} else {
/*** At start of scene ***/
pFilm = (const FILM *)_vm->_handle->LockMem(_vm->_bg->GetBgroundHandle());
_vm->_bg->_bgReels = FROM_32(pFilm->numreels);
int i;
for (i = 0; i < _vm->_bg->_bgReels; i++) {
// Get the MULTI_INIT structure
pmi = pFilm->reels[i].GetMultiInit();
// Initialize and insert the object, and initialize its script.
_vm->_bg->_pBG[i] = MultiInitObject(pmi);
MultiInsertObject(_vm->_bg->GetPlayfieldList(FIELD_WORLD), _vm->_bg->_pBG[i]);
MultiSetZPosition(_vm->_bg->_pBG[i], 0);
InitStepAnimScript(&_vm->_bg->_thisAnim[i], _vm->_bg->_pBG[i], FROM_32(pFilm->reels[i].script), _vm->_bg->getBgSpeed());
if (i > 0)
_vm->_bg->_pBG[i-1]->pSlave = _vm->_bg->_pBG[i];
}
}
if (_vm->_bg->GetDoFadeIn()) {
FadeInFast();
_vm->_bg->SetDoFadeIn(false);
} else if (TinselVersion >= 2)
PokeInTagColor();
for (;;) {
for (int i = 0; i < _vm->_bg->_bgReels; i++) {
if (StepAnimScript(&_vm->_bg->_thisAnim[i]) == ScriptFinished)
error("Background animation has finished");
}
CORO_SLEEP(1);
}
} else {
// New background during scene
if (TinselVersion <= 1) {
pReel = (const FREEL *)param;
InitStepAnimScript(&_vm->_bg->_thisAnim[0], _vm->_bg->_pBG[0], FROM_32(pReel->script), _vm->_bg->getBgSpeed());
StepAnimScript(&_vm->_bg->_thisAnim[0]);
} else {
pFilm = (const FILM *)_vm->_handle->LockMem(_vm->_bg->GetBgroundHandle());
assert(_vm->_bg->_bgReels == (int32)FROM_32(pFilm->numreels));
// Just re-initialize the scripts.
for (int i = 0; i < _vm->_bg->_bgReels; i++) {
InitStepAnimScript(&_vm->_bg->_thisAnim[i], _vm->_bg->_pBG[i], pFilm->reels[i].script, _vm->_bg->getBgSpeed());
StepAnimScript(&_vm->_bg->_thisAnim[i]);
}
}
}
CORO_END_CODE;
}
/**
* Runs secondary reels for a scene background
*/
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 = pReel->GetMultiInit();
CORO_BEGIN_CODE(_ctx);
// Initialize and insert the object, and initialize its script.
_ctx->pObj = MultiInitObject(pmi);
MultiInsertObject(_vm->_bg->GetPlayfieldList(FIELD_WORLD), _ctx->pObj);
InitStepAnimScript(&_ctx->anim, _vm->_bg->_pBG[0], FROM_32(pReel->script), _vm->_bg->getBgSpeed());
while (StepAnimScript(&_ctx->anim) != ScriptFinished)
CORO_SLEEP(1);
CORO_END_CODE;
}
/**
* 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 Background::StartupBackground(CORO_PARAM, SCNHANDLE hFilm) {
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
const FILM *pfilm = (const FILM *)_vm->_handle->LockMem(hFilm);
const FREEL *pfr = &pfilm->reels[0];
if (TinselVersion != 3) {
const MULTI_INIT *pmi = pfr->GetMultiInit();
const FRAME *pFrame = pmi->GetFrame();
const IMAGE *pim = _vm->_handle->GetImage(READ_32(pFrame));
SetBackPal(pim->hImgPal);
delete pim;
}
CORO_BEGIN_CODE(_ctx);
_hBackground = hFilm; // Save handle in case of Save_Scene()
// Extract the film speed
_BGspeed = ONE_SECOND / FROM_32(pfilm->frate);
// Start display process for each reel in the film
CoroScheduler.createProcess(PID_REEL, BGmainProcess, &pfilm->reels[0], sizeof(FREEL));
if (TinselVersion == 0) {
for (uint i = 1; i < FROM_32(pfilm->numreels); ++i)
CoroScheduler.createProcess(PID_REEL, BGotherProcess, &pfilm->reels[i], sizeof(FREEL));
}
if (_pBG[0] == NULL)
ControlStartOff();
if ((TinselVersion >= 2) && (coroParam != Common::nullContext))
CORO_GIVE_WAY;
CORO_END_CODE;
}
} // End of namespace Tinsel

1475
engines/tinsel/bmv.cpp Normal file

File diff suppressed because it is too large Load Diff

185
engines/tinsel/bmv.h Normal file
View File

@@ -0,0 +1,185 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* Plays films within a scene, takes into account the actor in each 'column'.
*/
#ifndef TINSEL_BMV_H
#define TINSEL_BMV_H
#include "common/coroutines.h"
#include "common/file.h"
#include "audio/mixer.h"
#include "tinsel/object.h"
#include "tinsel/palette.h"
namespace Audio {
class QueuingAudioStream;
}
namespace Tinsel {
typedef enum
{
BMV_OP_DELTA = 0,
BMV_OP_RAW = 1,
BMV_OP_RUN = 2,
BMV_OP_COUNT
} BMV_OP;
class BMVPlayer {
bool bOldAudio;
/// Set when a movie is on
bool bMovieOn;
/// Set to kill one off
bool bAbort;
/// For escaping out of movies
int bmvEscape;
/// Movie file pointer
Common::File stream;
/// Movie file name
char szMovieFile[14];
/// Pointers to buffers
byte *bigBuffer;
/// Next data to use to extract a frame
int nextUseOffset;
/// Next data to use to extract sound data
int nextSoundOffset;
/// When above offset gets to what this is set at, rewind
int wrapUseOffset;
/// The offset of the most future packet
int mostFutureOffset;
/// The current frame
int currentFrame;
int currentSoundFrame;
/// Number of packets currently in RAM
int numAdvancePackets;
/// Next slot that will be read from disc
int nextReadSlot;
/// Set when the whole file has been read
bool bFileEnd;
/// Palette
COLORREF moviePal[256 * 8]; // TinselV1 & V2 need 256, TinselVersion == 3 needs 2048
int blobsInBuffer;
struct {
OBJECT *pText;
int dieFrame;
} texts[2];
COLORREF talkColor;
/// TinselV3 header fields
int slotSize;
int frames;
int prefetchSlots;
int numSlots;
int frameRate;
int audioMaxSize;
int audioBlobSize;
int width;
int height;
/// TinselV3
int frameTime;
int bpp;
int bigProblemCount;
bool bIsText;
int movieTick;
int startTick;
uint32 nextMovieTime;
uint16 Au_Prev1;
uint16 Au_Prev2;
byte *ScreenBeg;
byte *screenBuffer;
bool audioStarted;
Audio::QueuingAudioStream *_audioStream;
Audio::SoundHandle _audioHandle;
int nextMaintain;
public:
BMVPlayer();
void PlayBMV(CORO_PARAM, SCNHANDLE hFileStem, int myEscape);
void FinishBMV();
void CopyMovieToScreen();
void FettleBMV();
bool MoviePlaying();
int32 MovieAudioLag();
uint32 NextMovieTime();
void AbortMovie();
private:
void ReadHeader();
void InitBMV(byte *memoryBuffer);
void PrepAudio(const byte *sourceData, int blobCount, byte *destPtr);
void PrepBMV(const byte *sourceData, int length, short deltaFetchDisp);
void t3DoOperation(BMV_OP op, uint32 len, const byte **src, byte **dst, int32 deltaOffset);
void t3PrepBMV(const byte *src, uint32 len, int32 deltaOffset);
void MoviePalette(int paletteOffset);
void InitializeMovieSound();
void StartMovieSound();
void FinishMovieSound();
void MovieAudio(int audioOffset, int blobs);
void FettleMovieText();
void BmvDrawText(bool bDraw);
void MovieText(CORO_PARAM, int stringId, int x, int y, int fontId, COLORREF *pTalkColor, int duration);
int MovieCommand(char cmd, int commandOffset);
int FollowingPacket(int thisPacket, bool bReallyImportant);
void LoadSlots(int number);
void InitializeBMV();
bool MaintainBuffer();
bool DoBMVFrame();
bool DoSoundFrame();
};
} // End of namespace Tinsel
#endif

324
engines/tinsel/cliprect.cpp Normal file
View File

@@ -0,0 +1,324 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* This file contains the clipping rectangle code.
*/
#include "tinsel/cliprect.h" // object clip rect defs
#include "tinsel/graphics.h" // normal object drawing
#include "tinsel/object.h"
#include "tinsel/palette.h"
#include "tinsel/tinsel.h" // for _vm
namespace Tinsel {
/**
* Resets the clipping rectangle allocator.
*/
void ResetClipRect() {
_vm->_clipRects.clear();
}
/**
* Allocate a clipping rectangle from the free list.
* @param pClip clip rectangle dimensions to allocate
*/
void AddClipRect(const Common::Rect &pClip) {
_vm->_clipRects.push_back(pClip);
}
const RectList &GetClipRects() {
return _vm->_clipRects;
}
/**
* Creates the intersection of two rectangles.
* Returns True if there is a intersection.
* @param pDest Pointer to destination rectangle that is to receive the intersection
* @param pSrc1 Pointer to a source rectangle
* @param pSrc2 Pointer to a source rectangle
*/
bool IntersectRectangle(Common::Rect &pDest, const Common::Rect &pSrc1, const Common::Rect &pSrc2) {
pDest.left = MAX(pSrc1.left, pSrc2.left);
pDest.top = MAX(pSrc1.top, pSrc2.top);
pDest.right = MIN(pSrc1.right, pSrc2.right);
pDest.bottom = MIN(pSrc1.bottom, pSrc2.bottom);
return !pDest.isEmpty();
}
/**
* Creates the union of two rectangles.
* Returns True if there is a union.
* @param pDest destination rectangle that is to receive the new union
* @param pSrc1 a source rectangle
* @param pSrc2 a source rectangle
*/
bool UnionRectangle(Common::Rect &pDest, const Common::Rect &pSrc1, const Common::Rect &pSrc2) {
pDest.left = MIN(pSrc1.left, pSrc2.left);
pDest.top = MIN(pSrc1.top, pSrc2.top);
pDest.right = MAX(pSrc1.right, pSrc2.right);
pDest.bottom = MAX(pSrc1.bottom, pSrc2.bottom);
return !pDest.isEmpty();
}
/**
* Check if the two rectangles are next to each other.
* @param pSrc1 a source rectangle
* @param pSrc2 a source rectangle
*/
static bool LooseIntersectRectangle(const Common::Rect &pSrc1, const Common::Rect &pSrc2) {
Common::Rect pDest;
pDest.left = MAX(pSrc1.left, pSrc2.left);
pDest.top = MAX(pSrc1.top, pSrc2.top);
pDest.right = MIN(pSrc1.right, pSrc2.right);
pDest.bottom = MIN(pSrc1.bottom, pSrc2.bottom);
return pDest.isValidRect();
}
/**
* Adds velocities and creates clipping rectangles for all the
* objects that have moved on the specified object list.
* @param pObjList Playfield display list to draw
* @param pWin Playfield window top left position
* @param pClip Playfield clipping rectangle
* @param bNoVelocity When reset, objects pos is updated with velocity
* @param bScrolled) When set, playfield has scrolled
*/
void FindMovingObjects(OBJECT **pObjList, Common::Point *pWin, Common::Rect *pClip, bool bNoVelocity, bool bScrolled) {
OBJECT *pObj; // object list traversal pointer
for (pObj = *pObjList; pObj != NULL; pObj = pObj->pNext) {
if (!bNoVelocity) {
// we want to add velocities to objects position
if (bScrolled) {
// this playfield has scrolled
// indicate change
pObj->flags |= DMA_CHANGED;
}
}
if ((pObj->flags & DMA_CHANGED) || // object changed
HasPalMoved(pObj->pPal)) { // or palette moved
// object has changed in some way
Common::Rect rcClip; // objects clipped bounding rectangle
Common::Rect rcObj; // objects bounding rectangle
// calc intersection of objects previous bounding rectangle
// NOTE: previous position is in screen co-ords
if (IntersectRectangle(rcClip, pObj->rcPrev, *pClip)) {
// previous position is within clipping rect
AddClipRect(rcClip);
}
// calc objects current bounding rectangle
if (pObj->flags & DMA_ABS) {
// object position is absolute
rcObj.left = fracToInt(pObj->xPos);
rcObj.top = fracToInt(pObj->yPos);
} else {
// object position is relative to window
rcObj.left = fracToInt(pObj->xPos) - pWin->x;
rcObj.top = fracToInt(pObj->yPos) - pWin->y;
}
rcObj.right = rcObj.left + pObj->width;
rcObj.bottom = rcObj.top + pObj->height;
// calc intersection of object with clipping rect
if (IntersectRectangle(rcClip, rcObj, *pClip)) {
// current position is within clipping rect
AddClipRect(rcClip);
// update previous position
pObj->rcPrev = rcClip;
} else {
// clear previous position
pObj->rcPrev = Common::Rect();
}
// clear changed flag
pObj->flags &= ~DMA_CHANGED;
}
}
}
/**
* Merges any clipping rectangles that overlap to try and reduce
* the total number of clip rectangles.
*/
void MergeClipRect() {
RectList &s_rectList = _vm->_clipRects;
if (s_rectList.size() <= 1)
return;
RectList::iterator rOuter, rInner;
for (rOuter = s_rectList.begin(); rOuter != s_rectList.end(); ++rOuter) {
rInner = rOuter;
while (++rInner != s_rectList.end()) {
if (LooseIntersectRectangle(*rOuter, *rInner)) {
// these two rectangles overlap or
// are next to each other - merge them
UnionRectangle(*rOuter, *rOuter, *rInner);
// remove the inner rect from the list
s_rectList.erase(rInner);
// move back to beginning of list
rInner = rOuter;
}
}
}
}
/**
* Redraws single objects within this clipping rectangle.
* @param pObjList Object list to draw
* @param pWin Window top left position
* @param pClip Pointer to clip rectangle
*/
void UpdateClipRectSingle(OBJECT *pObj, Common::Point *pWin, Common::Rect *pClip) {
int x, y, right, bottom; // object corners
int hclip, vclip; // total size of object clipping
DRAWOBJECT currentObj; // filled in to draw the current object in list
// Initialize the fields of the drawing object to empty
memset(&currentObj, 0, sizeof(DRAWOBJECT));
if (pObj->flags & DMA_ABS) {
// object position is absolute
x = fracToInt(pObj->xPos);
y = fracToInt(pObj->yPos);
} else {
// object position is relative to window
x = fracToInt(pObj->xPos) - pWin->x;
y = fracToInt(pObj->yPos) - pWin->y;
}
// calc object right
right = x + pObj->width;
if (right < 0)
// totally clipped if negative
return;
// calc object bottom
bottom = y + pObj->height;
if (bottom < 0)
// totally clipped if negative
return;
// bottom clip = low right y - clip low right y
currentObj.botClip = bottom - pClip->bottom;
if (currentObj.botClip < 0) {
// negative - object is not clipped
currentObj.botClip = 0;
}
// right clip = low right x - clip low right x
currentObj.rightClip = right - pClip->right;
if (currentObj.rightClip < 0) {
// negative - object is not clipped
currentObj.rightClip = 0;
}
// top clip = clip top left y - top left y
currentObj.topClip = pClip->top - y;
if (currentObj.topClip < 0) {
// negative - object is not clipped
currentObj.topClip = 0;
} else { // clipped - adjust start position to top of clip rect
y = pClip->top;
}
// left clip = clip top left x - top left x
currentObj.leftClip = pClip->left - x;
if (currentObj.leftClip < 0) {
// negative - object is not clipped
currentObj.leftClip = 0;
} else {
// NOTE: This else statement is disabled in tinsel v1
// clipped - adjust start position to left of clip rect
x = pClip->left;
}
// calc object total horizontal clipping
hclip = currentObj.leftClip + currentObj.rightClip;
// calc object total vertical clipping
vclip = currentObj.topClip + currentObj.botClip;
if (hclip + vclip != 0) {
// object is clipped in some way
if (pObj->width <= hclip)
// object totally clipped horizontally - ignore
return;
if (pObj->height <= vclip)
// object totally clipped vertically - ignore
return;
// set clip bit in object flags
currentObj.flags = pObj->flags | DMA_CLIP;
} else { // object is not clipped - copy flags
currentObj.flags = pObj->flags;
}
// copy object properties to local object
currentObj.width = pObj->width;
currentObj.height = pObj->height;
currentObj.xPos = (short)x;
currentObj.yPos = (short)y;
if (TinselVersion != 3) {
currentObj.pPal = pObj->pPal;
} else {
currentObj.isRLE = pObj->isRLE;
currentObj.colorFlags = pObj->colorFlags;
}
currentObj.constant = pObj->constant;
currentObj.hBits = pObj->hBits;
// draw the object
DrawObject(&currentObj);
}
/**
* Redraws all objects within this clipping rectangle.
* @param pObjList Object list to draw
* @param pWin Window top left position
* @param pClip Pointer to clip rectangle
*/
void UpdateClipRect(OBJECT **pObjList, Common::Point *pWin, Common::Rect *pClip) {
OBJECT *pObj; // object list iterator
for (pObj = *pObjList; pObj != NULL; pObj = pObj->pNext) {
UpdateClipRectSingle(pObj, pWin, pClip);
}
}
} // End of namespace Tinsel

77
engines/tinsel/cliprect.h Normal file
View File

@@ -0,0 +1,77 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* Clipping rectangle defines
*/
#ifndef TINSEL_CLIPRECT_H // prevent multiple includes
#define TINSEL_CLIPRECT_H
#include "common/list.h"
#include "common/rect.h"
namespace Tinsel {
struct OBJECT;
typedef Common::List<Common::Rect> RectList;
/*----------------------------------------------------------------------*\
|* Clip Rect Function Prototypes *|
\*----------------------------------------------------------------------*/
void ResetClipRect(); // Resets the clipping rectangle allocator
void AddClipRect( // Allocate a clipping rectangle from the free list
const Common::Rect &pClip); // clip rectangle dimensions to allocate
const RectList &GetClipRects();
bool IntersectRectangle( // Creates the intersection of two rectangles
Common::Rect &pDest, // pointer to destination rectangle that is to receive the intersection
const Common::Rect &pSrc1, // pointer to a source rectangle
const Common::Rect &pSrc2); // pointer to a source rectangle
bool UnionRectangle( // Creates the union of two rectangles
Common::Rect &pDest, // destination rectangle that is to receive the new union
const Common::Rect &pSrc1, // a source rectangle
const Common::Rect &pSrc2); // a source rectangle
void FindMovingObjects( // Creates clipping rectangles for all the objects that have moved on the specified object list
OBJECT **pObjList, // playfield display list to draw
Common::Point *pWin, // playfield window top left position
Common::Rect *pClip, // playfield clipping rectangle
bool bVelocity, // when set, objects pos is updated with velocity
bool bScrolled); // when set, playfield has scrolled
void MergeClipRect(); // Merges any clipping rectangles that overlap
void UpdateClipRectSingle( // Redraws single object within this clipping rectangle
OBJECT *pObj, // object to draw
Common::Point *pWin, // window top left position
Common::Rect *pClip); // pointer to clip rectangle
void UpdateClipRect( // Redraws all objects within this clipping rectangle
OBJECT **pObjList, // object list to draw
Common::Point *pWin, // window top left position
Common::Rect *pClip); // pointer to clip rectangle
} // End of namespace Tinsel
#endif // TINSEL_CLIPRECT_H

181
engines/tinsel/config.cpp Normal file
View File

@@ -0,0 +1,181 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* This file contains configuration functionality
*/
//#define USE_3FLAGS 1
#include "tinsel/config.h"
#include "tinsel/dw.h"
#include "tinsel/sound.h"
#include "tinsel/music.h"
#include "common/file.h"
#include "common/config-manager.h"
#include "audio/mixer.h"
namespace Tinsel {
Config::Config(TinselEngine *vm) : _vm(vm) {
_dclickSpeed = DOUBLE_CLICK_TIME;
_musicVolume = Audio::Mixer::kMaxChannelVolume;
_soundVolume = Audio::Mixer::kMaxChannelVolume;
_voiceVolume = Audio::Mixer::kMaxChannelVolume;
_textSpeed = DEFTEXTSPEED;
_useSubtitles = false;
_swapButtons = false;
_language = TXT_ENGLISH;
_isAmericanEnglishVersion = false;
}
/**
* Write settings to config manager and flush the config file to disk.
*/
void Config::writeToDisk() {
ConfMan.setInt("dclick_speed", _dclickSpeed);
ConfMan.setInt("music_volume", _musicVolume);
ConfMan.setInt("sfx_volume", _soundVolume);
ConfMan.setInt("speech_volume", _voiceVolume);
ConfMan.setInt("talkspeed", (_textSpeed * 255) / 100);
ConfMan.setBool("subtitles", _useSubtitles);
//ConfMan.setBool("swap_buttons", _swapButtons ? 1 : 0);
// Store language for multilingual versions
if ((_vm->getFeatures() & GF_USE_3FLAGS) || (_vm->getFeatures() & GF_USE_4FLAGS) || (_vm->getFeatures() & GF_USE_5FLAGS)) {
Common::Language lang;
switch (_language) {
case TXT_FRENCH:
lang = Common::FR_FRA;
break;
case TXT_GERMAN:
lang = Common::DE_DEU;
break;
case TXT_SPANISH:
lang = Common::ES_ESP;
break;
case TXT_ITALIAN:
lang = Common::IT_ITA;
break;
case TXT_US:
lang = Common::EN_USA;
break;
case TXT_JAPANESE:
lang = Common::JA_JPN;
break;
default:
lang = Common::EN_ANY;
}
ConfMan.set("language", Common::getLanguageCode(lang));
}
// Write to disk
ConfMan.flushToDisk();
}
/**
* Read configuration settings from the config file into memory
*/
void Config::readFromDisk() {
if (ConfMan.hasKey("dclick_speed"))
_dclickSpeed = ConfMan.getInt("dclick_speed");
// HACK/FIXME:
// We need to clip the volumes from [0, 256] to [0, 255]
// here, since for example Tinsel's internal options dialog
// and also the midi playback code rely on the volumes to be
// in [0, 255]
_musicVolume = CLIP(ConfMan.getInt("music_volume"), 0, 255);
_soundVolume = CLIP(ConfMan.getInt("sfx_volume"), 0, 255);
_voiceVolume = CLIP(ConfMan.getInt("speech_volume"), 0, 255);
if (ConfMan.hasKey("talkspeed"))
_textSpeed = (ConfMan.getInt("talkspeed") * 100) / 255;
if (ConfMan.hasKey("subtitles"))
_useSubtitles = ConfMan.getBool("subtitles");
// FIXME: If JAPAN is set, subtitles are forced OFF in the original engine
//_swapButtons = ConfMan.getBool("swap_buttons") == 1 ? true : false;
//ConfigData.language = language; // not necessary, as language has been set in the launcher
//ConfigData._isAmericanEnglishVersion = _isAmericanEnglishVersion; // EN_USA / EN_GRB
// Set language - we'll be clever here and use the ScummVM language setting
_language = TXT_ENGLISH;
Common::Language lang = _vm->getLanguage();
if (lang == Common::UNK_LANG && ConfMan.hasKey("language"))
lang = Common::parseLanguage(ConfMan.get("language")); // For multi-lingual versions, fall back to user settings
switch (lang) {
case Common::FR_FRA:
_language = TXT_FRENCH;
break;
case Common::DE_DEU:
_language = TXT_GERMAN;
break;
case Common::ES_ESP:
_language = TXT_SPANISH;
break;
case Common::IT_ITA:
_language = TXT_ITALIAN;
break;
case Common::EN_USA:
_language = TXT_US;
break;
case Common::JA_JPN:
_language = TXT_JAPANESE;
break;
default:
_language = TXT_ENGLISH;
}
if (lang == Common::JA_JPN) {
// TODO: Add support for JAPAN version
} else if (lang == Common::HE_ISR) {
// TODO: Add support for HEBREW version
// The Hebrew version appears to the software as being English
// but it needs to have subtitles on...
_language = TXT_ENGLISH;
_useSubtitles = true;
} else if (_vm->getFeatures() & GF_USE_3FLAGS) {
// 3 FLAGS version supports French, German, Spanish
// Fall back to German if necessary
if (_language != TXT_FRENCH && _language != TXT_GERMAN && _language != TXT_SPANISH) {
_language = TXT_GERMAN;
_useSubtitles = true;
}
} else if (_vm->getFeatures() & GF_USE_4FLAGS) {
// 4 FLAGS version supports French, German, Spanish, Italian
// Fall back to German if necessary
if (_language != TXT_FRENCH && _language != TXT_GERMAN &&
_language != TXT_SPANISH && _language != TXT_ITALIAN) {
_language = TXT_GERMAN;
_useSubtitles = true;
}
}
}
bool Config::isJapanMode() {
return _language == TXT_JAPANESE;
}
} // End of namespace Tinsel

62
engines/tinsel/config.h Normal file
View File

@@ -0,0 +1,62 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef TINSEL_CONFIG_H
#define TINSEL_CONFIG_H
#include "tinsel/dw.h"
namespace Tinsel {
// double click timer initial value
enum {
DOUBLE_CLICK_TIME = 10, // 10 @ 18Hz = .55 sec
DEFTEXTSPEED = 0
};
class TinselEngine;
class Config {
private:
TinselEngine *_vm;
public:
int _dclickSpeed;
int _musicVolume;
int _soundVolume;
int _voiceVolume;
int _textSpeed;
int _useSubtitles;
int _swapButtons;
LANGUAGE _language;
int _isAmericanEnglishVersion;
public:
Config(TinselEngine *vm);
void writeToDisk();
void readFromDisk();
bool isJapanMode();
};
} // End of namespace Tinsel
#endif

View File

@@ -0,0 +1,3 @@
# This file is included from the main "configure" script
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] [components]
add_engine tinsel "Tinsel" yes "" "" "" "midi tinygl"

View File

@@ -0,0 +1,9 @@
begin_section("Tinsel");
add_person("Torbj&ouml;rn Andersson", "eriktorbjorn", "");
add_person("Fabio Battaglia", "Hkz", "PSX version support");
add_person("Paul Gilbert", "dreammaster", "");
add_person("Sven Hesse", "DrMcCoy", "");
add_person("Max Horn", "Fingolfin", "(retired)");
add_person("Filippos Karapetis", "bluegr", "");
add_person("Joost Peters", "joostp", "");
end_section();

630
engines/tinsel/cursor.cpp Normal file
View File

@@ -0,0 +1,630 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* Cursor and cursor trails.
*/
#include "tinsel/cursor.h"
#include "tinsel/anim.h"
#include "tinsel/background.h"
#include "tinsel/cursor.h"
#include "tinsel/dw.h"
#include "tinsel/events.h" // For EventsManager class
#include "tinsel/film.h"
#include "tinsel/graphics.h"
#include "tinsel/handle.h"
#include "tinsel/dialogs.h"
#include "tinsel/multiobj.h" // multi-part object defintions etc.
#include "tinsel/object.h"
#include "tinsel/pid.h"
#include "tinsel/play.h"
#include "tinsel/sched.h"
#include "tinsel/sysvar.h"
#include "tinsel/text.h"
#include "tinsel/timers.h" // For ONE_SECOND constant
#include "tinsel/tinlib.h" // resetidletime()
#include "tinsel/tinsel.h" // For engine access
namespace Tinsel {
//----------------- LOCAL DEFINES --------------------
#define ITERATION_BASE FRAC_ONE
#define ITER_ACCELERATION (10L << (FRAC_BITS - 4))
Cursor::Cursor() {
_mainCursor = nullptr;
_auxCursor = nullptr;
_mainCursorAnim = {0, 0, 0, 0, 0};
_auxCursorAnim = {0, 0, 0, 0, 0};
_hiddenCursor = false;
_hiddenTrails = false;
_tempHiddenCursor = false;
_frozenCursor = false;
_iterationSize = 0;
_cursorFilm = 0;
_numTrails = 0;
_nextTrail = 0;
_cursorProcessesStopped = false;
_cursorProcessesRestarted = false;
_auxCursorOffsetX = 0;
_auxCursorOffsetY = 0;
_lastCursorX = 0;
_lastCursorY = 0;
}
/**
* Initialize and insert a cursor trail object, set its Z-pos, and hide
* it. Also initialize its animation script.
*/
void Cursor::InitCurTrailObj(int i, int x, int y) {
if (!_numTrails)
return;
const FILM *pFilm = (const FILM *)_vm->_handle->LockMem(_cursorFilm);
const FREEL *pfr = (const FREEL *)&pFilm->reels[i + 1];
const MULTI_INIT *pmi = pfr->GetMultiInit();
PokeInPalette(pmi);
// Get rid of old object
MultiDeleteObjectIfExists(FIELD_STATUS, &_trailData[i].trailObj);
// Initialize and insert the object, set its Z-pos, and hide it
_trailData[i].trailObj = MultiInitObject(pmi);
MultiInsertObject(_vm->_bg->GetPlayfieldList(FIELD_STATUS), _trailData[i].trailObj);
MultiSetAniXYZ(_trailData[i].trailObj, x, y, Z_CURSORTRAIL);
// Initialize the animation script
InitStepAnimScript(&_trailData[i].trailAnim, _trailData[i].trailObj, FROM_32(pfr->script), ONE_SECOND / FROM_32(pFilm->frate));
StepAnimScript(&_trailData[i].trailAnim);
}
/**
* Get the cursor position from the mouse driver.
*/
bool Cursor::GetDriverPosition(int *x, int *y) {
Common::Point ptMouse = _vm->getMousePosition();
*x = ptMouse.x;
*y = ptMouse.y;
return(*x >= 0 && *x <= SCREEN_WIDTH - 1 &&
*y >= 0 && *y <= SCREEN_HEIGHT - 1);
}
/**
* Move the cursor relative to current position.
*/
void Cursor::AdjustCursorXY(int deltaX, int deltaY) {
int x, y;
if (deltaX || deltaY) {
if (GetDriverPosition(&x, &y))
_vm->setMousePosition(Common::Point(x + deltaX, y + deltaY));
}
DoCursorMove();
}
/**
* Move the cursor to an absolute position.
*/
void Cursor::SetCursorXY(int newx, int newy) {
int x, y;
int Loffset, Toffset; // Screen offset
_vm->_bg->PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
newx -= Loffset;
newy -= Toffset;
if (GetDriverPosition(&x, &y))
_vm->setMousePosition(Common::Point(newx, newy));
DoCursorMove();
}
/**
* Move the cursor to a screen position.
*/
void Cursor::SetCursorScreenXY(int newx, int newy) {
int x, y;
if (GetDriverPosition(&x, &y))
_vm->setMousePosition(Common::Point(newx, newy));
DoCursorMove();
}
/**
* Called by the world and his brother.
* Returns the cursor's animation position in (x,y).
* Returns false if there is no cursor object.
*/
bool Cursor::GetCursorXYNoWait(int *x, int *y, bool absolute) {
if (_mainCursor == NULL) {
*x = *y = 0;
return false;
}
GetAniPosition(_mainCursor, x, y);
if (absolute) {
int Loffset, Toffset; // Screen offset
_vm->_bg->PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
*x += Loffset;
*y += Toffset;
}
return true;
}
/**
* Called by the world and his brother.
* Returns the cursor's animation position.
* If called while there is no cursor object, the calling process ends
* up waiting until there is.
*/
void Cursor::GetCursorXY(int *x, int *y, bool absolute) {
//while (McurObj == NULL)
// ProcessSleepSelf();
assert(_mainCursor);
GetCursorXYNoWait(x, y, absolute);
}
/**
* Re-initialize the main cursor to use the main cursor reel.
* Called from TINLIB.C to restore cursor after hiding it.
* Called from INVENTRY.C to restore cursor after customising it.
*/
void Cursor::RestoreMainCursor() {
const FILM *pfilm;
if (_mainCursor != NULL) {
pfilm = (const FILM *)_vm->_handle->LockMem(_cursorFilm);
InitStepAnimScript(&_mainCursorAnim, _mainCursor, FROM_32(pfilm->reels->script), ONE_SECOND / FROM_32(pfilm->frate));
StepAnimScript(&_mainCursorAnim);
}
_hiddenCursor = false;
_frozenCursor = false;
}
/**
* Called from INVENTRY.C to customise the main cursor.
*/
void Cursor::SetTempCursor(SCNHANDLE pScript) {
if (_mainCursor != NULL)
InitStepAnimScript(&_mainCursorAnim, _mainCursor, pScript, 2);
}
/**
* Hide the cursor.
*/
void Cursor::DwHideCursor() {
_hiddenCursor = true;
if (_mainCursor)
MultiHideObject(_mainCursor);
if (_auxCursor)
MultiHideObject(_auxCursor);
for (int i = 0; i < _numTrails; i++) {
MultiDeleteObjectIfExists(FIELD_STATUS, &_trailData[i].trailObj);
}
}
void Cursor::HideCursorProcess() {
if (_mainCursor)
MultiHideObject(_mainCursor);
if (_auxCursor)
MultiHideObject(_auxCursor);
for (int i = 0; i < _numTrails; i++) {
if (_trailData[i].trailObj != NULL)
MultiHideObject(_trailData[i].trailObj);
}
}
/**
* Unhide the cursor.
*/
void Cursor::UnHideCursor() {
_hiddenCursor = false;
}
/**
* Freeze the cursor.
*/
void Cursor::FreezeCursor() {
_frozenCursor = true;
}
/**
* Freeze the cursor, or not.
*/
void Cursor::DoFreezeCursor(bool bFreeze) {
_frozenCursor = bFreeze;
}
/**
* HideCursorTrails
*/
void Cursor::HideCursorTrails() {
int i;
_hiddenTrails = true;
for (i = 0; i < _numTrails; i++) {
MultiDeleteObjectIfExists(FIELD_STATUS, &_trailData[i].trailObj);
}
}
/**
* UnHideCursorTrails
*/
void Cursor::UnHideCursorTrails() {
_hiddenTrails = false;
}
/**
* Delete auxiliary cursor. Restore animation offsets in the image.
*/
void Cursor::DelAuxCursor() {
MultiDeleteObjectIfExists(FIELD_STATUS, &_auxCursor);
}
/**
* Set auxiliary cursor.
* Save animation offsets from the image if required.
*/
void Cursor::SetAuxCursor(SCNHANDLE hFilm) {
const FILM *pfilm = (const FILM *)_vm->_handle->LockMem(hFilm);
const FREEL *pfr = &pfilm->reels[0];
const MULTI_INIT *pmi = pfr->GetMultiInit();
const FRAME *pFrame = pmi->GetFrame();
const IMAGE *pim;
int x, y; // Cursor position
DelAuxCursor(); // Get rid of previous
// Noir does not use palettes
if (TinselVersion < 3) {
// WORKAROUND: There's no palette when loading a DW1 savegame with a held item, so exit if so
if (!_vm->_bg->BgPal())
return;
assert(_vm->_bg->BgPal()); // no background palette
PokeInPalette(pmi);
}
GetCursorXY(&x, &y, false); // Note: also waits for cursor to appear
pim = _vm->_handle->GetImage(READ_32(pFrame)); // Get pointer to auxiliary cursor's image
_auxCursorOffsetX = (short)(pim->imgWidth / 2 - ((int16) pim->anioffX));
_auxCursorOffsetY = (short)((pim->imgHeight & ~C16_FLAG_MASK) / 2 -
((int16) pim->anioffY));
delete pim;
// Initialize and insert the auxiliary cursor object
_auxCursor = MultiInitObject(pmi);
MultiInsertObject(_vm->_bg->GetPlayfieldList(FIELD_STATUS), _auxCursor);
// Initialize the animation and set its position
InitStepAnimScript(&_auxCursorAnim, _auxCursor, FROM_32(pfr->script), ONE_SECOND / FROM_32(pfilm->frate));
MultiSetAniXYZ(_auxCursor, x - _auxCursorOffsetX, y - _auxCursorOffsetY, Z_ACURSOR);
if (_hiddenCursor)
MultiHideObject(_auxCursor);
}
/**
* MoveCursor
*/
void Cursor::DoCursorMove() {
int startX, startY;
Common::Point ptMouse;
frac_t newX, newY;
unsigned dir;
// get cursors start animation position
GetCursorXYNoWait(&startX, &startY, false);
// get mouse drivers current position
ptMouse = _vm->getMousePosition();
// convert to fixed point
newX = intToFrac(ptMouse.x);
newY = intToFrac(ptMouse.y);
// modify mouse driver position depending on cursor keys
dir = _vm->getKeyDirection();
if (dir != 0) {
if (dir & MSK_LEFT)
newX -= _iterationSize;
if (dir & MSK_RIGHT)
newX += _iterationSize;
if (dir & MSK_UP)
newY -= _iterationSize;
if (dir & MSK_DOWN)
newY += _iterationSize;
_iterationSize += ITER_ACCELERATION;
// set new mouse driver position
_vm->setMousePosition(Common::Point(fracToInt(newX), fracToInt(newY)));
} else
_iterationSize = ITERATION_BASE;
// get new mouse driver position - could have been modified
ptMouse = _vm->getMousePosition();
if (_lastCursorX != ptMouse.x || _lastCursorY != ptMouse.y) {
resetUserEventTime();
if (!_hiddenTrails && !_hiddenCursor) {
InitCurTrailObj(_nextTrail++, _lastCursorX, _lastCursorY);
if (_nextTrail == _numTrails)
_nextTrail = 0;
}
}
// adjust cursor to new mouse position
if (_mainCursor)
MultiSetAniXY(_mainCursor, ptMouse.x, ptMouse.y);
if (_auxCursor != NULL)
MultiSetAniXY(_auxCursor, ptMouse.x - _auxCursorOffsetX, ptMouse.y - _auxCursorOffsetY);
if (_vm->_dialogs->inventoryActive() && _mainCursor) {
// Notify the inventory
_vm->_dialogs->xMovement(ptMouse.x - startX);
_vm->_dialogs->yMovement(ptMouse.y - startY);
}
_lastCursorX = ptMouse.x;
_lastCursorY = ptMouse.y;
}
/**
* Initialize cursor object.
*/
void Cursor::InitCurObj() {
const FILM *pFilm = (const FILM *)_vm->_handle->LockMem(_cursorFilm);
const FREEL *pfr = (const FREEL *)&pFilm->reels[0];
const MULTI_INIT *pmi = pfr->GetMultiInit();
if (TinselVersion != 3) {
PokeInPalette(pmi);
}
if (TinselVersion <= 1)
_auxCursor = nullptr; // No auxiliary cursor
_mainCursor = MultiInitObject(pmi);
MultiInsertObject(_vm->_bg->GetPlayfieldList(FIELD_STATUS), _mainCursor);
InitStepAnimScript(&_mainCursorAnim, _mainCursor, FROM_32(pfr->script), ONE_SECOND / FROM_32(pFilm->frate));
}
/**
* Initialize the cursor position.
*/
void Cursor::InitCurPos() {
Common::Point ptMouse = _vm->getMousePosition();
_lastCursorX = ptMouse.x;
_lastCursorY = ptMouse.y;
MultiSetZPosition(_mainCursor, Z_CURSOR);
DoCursorMove();
MultiHideObject(_mainCursor);
_iterationSize = ITERATION_BASE;
}
/**
* Called from dec_cursor() Glitter function.
* Register the handle to cursor reel data.
*/
void Cursor::DwInitCursor(SCNHANDLE bfilm) {
const FILM *pfilm;
_cursorFilm = bfilm;
pfilm = (const FILM *)_vm->_handle->LockMem(_cursorFilm);
_numTrails = FROM_32(pfilm->numreels) - 1;
assert(_numTrails <= MAX_TRAILERS);
}
/**
* DropCursor is called when a scene is closing down.
*/
void Cursor::DropCursor() {
if (TinselVersion >= 2) {
if (_auxCursor)
MultiDeleteObjectIfExists(FIELD_STATUS, &_auxCursor);
if (_mainCursor)
MultiDeleteObjectIfExists(FIELD_STATUS, &_mainCursor);
_cursorProcessesRestarted = false;
}
_auxCursor = nullptr; // No auxiliary cursor
_mainCursor = nullptr; // No cursor object (imminently deleted elsewhere)
_hiddenCursor = false; // Not hidden in next scene
_hiddenTrails = false; // Trailers not hidden in next scene
_cursorProcessesStopped = true; // Suspend cursor processes
for (int i = 0; i < _numTrails; i++) {
MultiDeleteObjectIfExists(FIELD_STATUS, &_trailData[i].trailObj);
}
}
/**
* RestartCursor is called when a new scene is starting up.
*/
void Cursor::RestartCursor() {
_cursorProcessesRestarted = true; // Get the main cursor to re-initialize
}
/**
* Called when restarting the game, ensures correct re-start with NULL
* pointers etc.
*/
void Cursor::RebootCursor() {
_mainCursor = _auxCursor = nullptr;
for (int i = 0; i < MAX_TRAILERS; i++)
_trailData[i].trailObj = nullptr;
_hiddenCursor = _hiddenTrails = _frozenCursor = false;
_cursorFilm = 0;
_cursorProcessesStopped = false;
_cursorProcessesRestarted = false;
}
void Cursor::StartCursorFollowed() {
DelAuxCursor();
if (!SysVar(SV_ENABLEPRINTCURSOR))
_tempHiddenCursor = true;
}
void Cursor::EndCursorFollowed() {
_vm->_dialogs->inventoryIconCursor(false); // May be holding something
_tempHiddenCursor = false;
}
bool Cursor::isCursorShown() {
return !(_tempHiddenCursor || _hiddenCursor);
}
void Cursor::AnimateProcess() {
// Step the animation script(s)
StepAnimScript(&_mainCursorAnim);
if (_auxCursor != NULL)
StepAnimScript(&_auxCursorAnim);
for (int i = 0; i < _vm->_cursor->NumTrails(); i++) {
if (_trailData[i].trailObj != NULL) {
if (StepAnimScript(&_trailData[i].trailAnim) == ScriptFinished) {
MultiDeleteObjectIfExists(FIELD_STATUS, &_trailData[i].trailObj);
}
}
}
// Move the cursor as appropriate
if (!_vm->_cursor->CursorIsFrozen())
_vm->_cursor->DoCursorMove();
}
/**
* CursorStoppedCheck
*/
void CursorStoppedCheck(CORO_PARAM) {
// COROUTINE
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
// If scene is closing down
if (_vm->_cursor->_cursorProcessesStopped) {
// ...wait for next scene start-up
while (!_vm->_cursor->_cursorProcessesRestarted)
CORO_SLEEP(1);
// Re-initialize
_vm->_cursor->InitCurObj();
_vm->_cursor->InitCurPos();
_vm->_dialogs->inventoryIconCursor(false); // May be holding something
// Re-start the cursor trails
_vm->_cursor->_cursorProcessesRestarted = true;
_vm->_cursor->_cursorProcessesStopped = false;
}
CORO_END_CODE;
}
bool CanInitializeCursor() {
if (!_vm->_cursor->HasReelData()) {
return false;
} else if (TinselVersion != 3) {
return (_vm->_bg->BgPal() != 0);
}
return true;
}
/**
* The main cursor process.
*/
void CursorProcess(CORO_PARAM, const void *) {
// COROUTINE
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
while (!CanInitializeCursor())
CORO_SLEEP(1);
_vm->_cursor->InitCurObj();
_vm->_cursor->InitCurPos();
_vm->_dialogs->inventoryIconCursor(false); // May be holding something
_vm->_cursor->_cursorProcessesStopped = false;
_vm->_cursor->_cursorProcessesRestarted = false;
while (1) {
// allow rescheduling
CORO_SLEEP(1);
// Stop/start between scenes
CORO_INVOKE_0(CursorStoppedCheck);
_vm->_cursor->AnimateProcess();
// If the cursor should be hidden...
if (_vm->_cursor->ShouldBeHidden()) {
_vm->_cursor->HideCursorProcess();
// Wait 'til cursor is again required.
while (_vm->_cursor->IsHidden()) {
CORO_SLEEP(1);
// Stop/start between scenes
CORO_INVOKE_0(CursorStoppedCheck);
}
}
}
CORO_END_CODE;
}
} // End of namespace Tinsel

124
engines/tinsel/cursor.h Normal file
View File

@@ -0,0 +1,124 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* Clipping rectangle defines
*/
#ifndef TINSEL_CURSOR_H // prevent multiple includes
#define TINSEL_CURSOR_H
#include "common/frac.h"
#include "tinsel/anim.h"
#include "tinsel/dw.h" // for SCNHANDLE
namespace Tinsel {
struct IMAGE;
struct FREEL;
struct MULTI_INIT;
struct FILM;
struct OBJECT;
#define MAX_TRAILERS 10
class Cursor {
public:
Cursor();
virtual ~Cursor() {}
void AdjustCursorXY(int deltaX, int deltaY);
void SetCursorXY(int x, int y);
void SetCursorScreenXY(int newx, int newy);
void GetCursorXY(int *x, int *y, bool absolute);
bool GetCursorXYNoWait(int *x, int *y, bool absolute);
bool isCursorShown();
void RestoreMainCursor();
void SetTempCursor(SCNHANDLE pScript);
void DwHideCursor();
void UnHideCursor();
void HideCursorProcess();
void AnimateProcess();
void FreezeCursor();
void DoFreezeCursor(bool bFreeze);
void HideCursorTrails();
void UnHideCursorTrails();
void DelAuxCursor();
void SetAuxCursor(SCNHANDLE hFilm);
void DwInitCursor(SCNHANDLE bfilm);
void DropCursor();
void RestartCursor();
void RebootCursor();
void StartCursorFollowed();
void EndCursorFollowed();
void InitCurObj();
void InitCurPos();
void DoCursorMove();
bool CursorIsFrozen() { return _frozenCursor; }
int NumTrails() { return _numTrails; }
bool IsHidden() { return _hiddenCursor; }
bool ShouldBeHidden() { return _hiddenCursor || _tempHiddenCursor; }
bool HasReelData() { return _cursorFilm != 0; }
bool _cursorProcessesStopped; // Set by DropCursor() at the end of a scene
// - causes cursor processes to do nothing
// Reset when main cursor has re-initialized
bool _cursorProcessesRestarted; // When main cursor has been bWhoa-ed, it waits
// for this to be true.
// Main cursor sets this to true after a re-start
private:
void InitCurTrailObj(int i, int x, int y);
bool GetDriverPosition(int *x, int *y);
OBJECT *_mainCursor; // Main cursor object
OBJECT *_auxCursor; // Auxiliary cursor object
ANIM _mainCursorAnim; // Main cursor animation structure
ANIM _auxCursorAnim; // Auxiliary cursor animation structure
bool _hiddenCursor; // Set when cursor is hidden
bool _hiddenTrails; // Set when cursor trails are hidden
bool _tempHiddenCursor; // Set when cursor is hidden
bool _frozenCursor; // Set when cursor position is frozen
frac_t _iterationSize;
SCNHANDLE _cursorFilm; // Handle to cursor reel data
int _numTrails;
int _nextTrail;
// Auxillary cursor image's animation offsets
short _auxCursorOffsetX;
short _auxCursorOffsetY;
struct {
ANIM trailAnim; // Animation structure
OBJECT *trailObj; // This trailer's object
} _trailData[MAX_TRAILERS];
int _lastCursorX, _lastCursorY;
};
} // End of namespace Tinsel
#endif // TINSEL_CURSOR_H

209
engines/tinsel/debugger.cpp Normal file
View File

@@ -0,0 +1,209 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "tinsel/tinsel.h"
#include "tinsel/debugger.h"
#include "tinsel/dialogs.h"
#include "tinsel/pcode.h"
#include "tinsel/scene.h"
#include "tinsel/sound.h"
#include "tinsel/music.h"
#include "tinsel/font.h"
#include "tinsel/strres.h"
#include "tinsel/noir/notebook.h"
namespace Tinsel {
//----------------- EXTERNAL FUNCTIONS ---------------------
// In PDISPLAY.CPP
extern void TogglePathDisplay();
// In tinsel.cpp
extern void SetNewScene(SCNHANDLE scene, int entrance, int transition);
// In scene.cpp
extern SCNHANDLE GetSceneHandle();
//----------------- SUPPORT FUNCTIONS ---------------------
//static
int strToInt(const char *s) {
if (!*s)
// No string at all
return 0;
else if (toupper(s[strlen(s) - 1]) != 'H')
// Standard decimal string
return atoi(s);
// Hexadecimal string
uint tmp;
if (!sscanf(s, "%xh", &tmp))
tmp = 0;
return (int)tmp;
}
//----------------- CONSOLE CLASS ---------------------
Console::Console() : GUI::Debugger() {
if (TinselVersion == 3) {
registerCmd("add_clue", WRAP_METHOD(Console, cmd_add_clue));
registerCmd("add_all_clues", WRAP_METHOD(Console, cmd_add_all_clues));
registerCmd("cross_clue", WRAP_METHOD(Console, cmd_cross_clue));
registerCmd("list_clues", WRAP_METHOD(Console, cmd_list_clues));
}
registerCmd("item", WRAP_METHOD(Console, cmd_item));
registerCmd("scene", WRAP_METHOD(Console, cmd_scene));
registerCmd("music", WRAP_METHOD(Console, cmd_music));
registerCmd("sound", WRAP_METHOD(Console, cmd_sound));
registerCmd("string", WRAP_METHOD(Console, cmd_string));
}
Console::~Console() {
}
bool Console::cmd_item(int argc, const char **argv) {
if (argc < 2) {
debugPrintf("%s item_number\n", argv[0]);
debugPrintf("Sets the currently active 'held' item\n");
return true;
}
_vm->_dialogs->holdItem(INV_NOICON);
_vm->_dialogs->holdItem(strToInt(argv[1]));
return false;
}
bool Console::cmd_scene(int argc, const char **argv) {
if (argc < 1 || argc > 3) {
debugPrintf("%s [scene_number [entry number]]\n", argv[0]);
debugPrintf("If no parameters are given, prints the current scene.\n");
debugPrintf("Otherwise changes to the specified scene number. Entry number defaults to 1 if none provided\n");
return true;
}
if (argc == 1) {
debugPrintf("Current scene is %d\n", GetSceneHandle() >> SCNHANDLE_SHIFT);
return true;
}
uint32 sceneNumber = (uint32)strToInt(argv[1]) << SCNHANDLE_SHIFT;
int entryNumber = (argc >= 3) ? strToInt(argv[2]) : 1;
SetNewScene(sceneNumber, entryNumber, TRANS_CUT);
return false;
}
bool Console::cmd_music(int argc, const char **argv) {
if (argc < 2) {
debugPrintf("%s track_number or %s -offset\n", argv[0], argv[0]);
debugPrintf("Plays the MIDI track number provided, or the offset inside midi.dat\n");
debugPrintf("A positive number signifies a track number, whereas a negative signifies an offset\n");
return true;
}
int param = strToInt(argv[1]);
if (param == 0) {
debugPrintf("Track number/offset can't be 0!\n");
} else if (param > 0) {
// Track provided
_vm->_music->PlayMidiSequence(_vm->_music->GetTrackOffset(param - 1), false);
} else if (param < 0) {
// Offset provided
param = param * -1;
_vm->_music->PlayMidiSequence(param, false);
}
return true;
}
bool Console::cmd_sound(int argc, const char **argv) {
if (argc < 2) {
debugPrintf("%s id\n", argv[0]);
debugPrintf("Plays the sound with the given ID\n");
return true;
}
int id = strToInt(argv[1]);
if (_vm->_sound->sampleExists(id)) {
if (TinselVersion <= 1)
_vm->_sound->playSample(id, Audio::Mixer::kSpeechSoundType);
else
_vm->_sound->playSample(id, 0, false, 0, 0, PRIORITY_TALK, Audio::Mixer::kSpeechSoundType);
} else {
debugPrintf("Sample %d does not exist!\n", id);
}
return true;
}
bool Console::cmd_string(int argc, const char **argv) {
if (argc < 2) {
debugPrintf("%s id\n", argv[0]);
debugPrintf("Prints the string with the given ID\n");
return true;
}
char tmp[TBUFSZ];
int id = strToInt(argv[1]);
LoadStringRes(id, tmp, TBUFSZ);
debugPrintf("%s\n", tmp);
return true;
}
// Noir:
bool Console::cmd_add_clue(int argc, const char **argv) {
if (argc < 2) {
debugPrintf("%s clue_id\n", argv[0]);
debugPrintf("Adds a clue to the notebook\n");
return true;
}
_vm->_notebook->addClue(strToInt(argv[1]));
return false;
}
bool Console::cmd_add_all_clues(int argc, const char **argv) {
auto clues = _vm->_dialogs->getAllNotebookClues();
for (auto clue : clues) {
_vm->_notebook->addClue(clue);
}
return false;
}
bool Console::cmd_cross_clue(int argc, const char **argv) {
if (argc < 2) {
debugPrintf("%s clue_id\n", argv[0]);
debugPrintf("Crosses out a clue in the notebook\n");
return true;
}
_vm->_notebook->crossClue(strToInt(argv[1]));
return false;
}
bool Console::cmd_list_clues(int argc, const char **argv) {
auto clues = _vm->_dialogs->getAllNotebookClues();
for (auto clue : clues) {
debugPrintf("%d\n", clue);
}
return true;
}
} // End of namespace Tinsel

50
engines/tinsel/debugger.h Normal file
View File

@@ -0,0 +1,50 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef TINSEL_DEBUGGER_H
#define TINSEL_DEBUGGER_H
#include "gui/debugger.h"
namespace Tinsel {
class TinselEngine;
class Console: public GUI::Debugger {
public:
Console();
~Console() override;
private:
bool cmd_add_clue(int argc, const char **argv);
bool cmd_add_all_clues(int argc, const char **argv);
bool cmd_cross_clue(int argc, const char **argv);
bool cmd_list_clues(int argc, const char **argv);
bool cmd_item(int argc, const char **argv);
bool cmd_scene(int argc, const char **argv);
bool cmd_music(int argc, const char **argv);
bool cmd_sound(int argc, const char **argv);
bool cmd_string(int argc, const char **argv);
};
} // End of namespace Tinsel
#endif

View File

@@ -0,0 +1,220 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "base/plugins.h"
#include "engines/advancedDetector.h"
#include "common/file.h"
#include "common/md5.h"
#include "tinsel/detection.h"
#include "tinsel/tinsel.h"
static const PlainGameDescriptor tinselGames[] = {
{"dw", "Discworld"},
{"dw2", "Discworld II: Missing Presumed ...!?"},
{"noir", "Discworld Noir"},
{0, 0}
};
static const DebugChannelDef debugFlagList[] = {
{Tinsel::kTinselDebugAnimations, "animations", "Animations debugging"},
{Tinsel::kTinselDebugActions, "actions", "Actions debugging"},
{Tinsel::kTinselDebugSound, "sound", "Sound debugging"},
{Tinsel::kTinselDebugMusic, "music", "Music debugging"},
DEBUG_CHANNEL_END
};
#include "tinsel/detection_tables.h"
class TinselMetaEngineDetection : public AdvancedMetaEngineDetection<Tinsel::TinselGameDescription> {
public:
TinselMetaEngineDetection() : AdvancedMetaEngineDetection(Tinsel::gameDescriptions, tinselGames) {
}
const char *getName() const override{
return "tinsel";
}
const char *getEngineName() const override {
return "Tinsel";
}
const char *getOriginalCopyright() const override {
return "Tinsel (C) Psygnosis";
}
const DebugChannelDef *getDebugChannels() const override {
return debugFlagList;
}
ADDetectedGame fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist, ADDetectedGameExtraInfo **extraInfo) const override;
};
struct SizeMD5 {
unsigned int size;
Common::String md5;
};
typedef Common::HashMap<Common::Path, SizeMD5, Common::Path::IgnoreCase_Hash, Common::Path::IgnoreCase_EqualTo> SizeMD5Map;
typedef Common::HashMap<Common::Path, Common::FSNode, Common::Path::IgnoreCase_Hash, Common::Path::IgnoreCase_EqualTo> FileMap;
typedef Common::Array<const ADGameDescription *> ADGameDescList;
/**
* Fallback detection scans the list of Discworld 2 targets to see if it can detect an installation
* where the files haven't been renamed (i.e. don't have the '1' just before the extension)
*/
ADDetectedGame TinselMetaEngineDetection::fallbackDetect(const FileMap &allFilesXXX, const Common::FSList &fslist, ADDetectedGameExtraInfo **extraInfo) const {
Common::String extra;
FileMap allFiles;
SizeMD5Map filesSizeMD5;
const ADGameFileDescription *fileDesc;
const Tinsel::TinselGameDescription *g;
if (fslist.empty())
return ADDetectedGame();
// TODO: The following code is essentially a slightly modified copy of the
// complete code of function detectGame() in engines/advancedDetector.cpp.
// That quite some hefty and undesirable code duplication. Its only purpose
// seems to be to treat filenames of the form "foo1.ext" as "foo.ext".
// It would be nice to avoid this code duplication.
// First we compose a hashmap of all files in fslist.
for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
if (file->isDirectory()) {
if (!scumm_stricmp(file->getName().c_str(), "dw2")) {
// Probably Discworld 2 subfolder on CD, so add it's contents as well
Common::FSList files;
if (file->getChildren(files, Common::FSNode::kListAll)) {
Common::FSList::const_iterator file2;
for (file2 = files.begin(); file2 != files.end(); ++file2) {
if (file2->isDirectory())
continue;
Common::Path fname = file2->getPathInArchive();
allFiles[fname] = *file2;
}
}
}
continue;
}
Common::Path tstr = file->getPathInArchive();
allFiles[tstr] = *file; // Record the presence of this file
}
// Check which files are included in some dw2 ADGameDescription *and* present
// in fslist without a '1' suffix character. Compute MD5s and file sizes for these files.
for (g = &Tinsel::gameDescriptions[0]; g->desc.gameId != 0; ++g) {
if (strcmp(g->desc.gameId, "dw2") != 0)
continue;
for (fileDesc = g->desc.filesDescriptions; fileDesc->fileName; fileDesc++) {
// Get the next filename, stripping off any '1' suffix character
char tempFilename[50];
Common::strlcpy(tempFilename, fileDesc->fileName, 50);
char *pOne = strchr(tempFilename, '1');
if (pOne) {
do {
*pOne = *(pOne + 1);
pOne++;
} while (*pOne);
}
Common::Path fname(tempFilename);
if (allFiles.contains(fname) && !filesSizeMD5.contains(fname)) {
SizeMD5 tmp;
Common::File testFile;
if (testFile.open(allFiles[fname])) {
tmp.size = (int32)testFile.size();
tmp.md5 = computeStreamMD5AsString(testFile, _md5Bytes);
} else {
tmp.size = AD_NO_SIZE;
}
filesSizeMD5[fname] = tmp;
}
}
}
ADDetectedGame matched;
int maxFilesMatched = 0;
// MD5 based matching
for (g = &Tinsel::gameDescriptions[0]; g->desc.gameId != 0; ++g) {
if (strcmp(g->desc.gameId, "dw2") != 0)
continue;
bool fileMissing = false;
// Try to match all files for this game
for (fileDesc = g->desc.filesDescriptions; fileDesc->fileName; fileDesc++) {
// Get the next filename, stripping off any '1' suffix character
char tempFilename[50];
Common::strlcpy(tempFilename, fileDesc->fileName, 50);
char *pOne = strchr(tempFilename, '1');
if (pOne) {
do {
*pOne = *(pOne + 1);
pOne++;
} while (*pOne);
}
Common::Path tstr(tempFilename);
if (!filesSizeMD5.contains(tstr)) {
fileMissing = true;
break;
}
if (fileDesc->md5 != NULL && fileDesc->md5 != filesSizeMD5[tstr].md5) {
fileMissing = true;
break;
}
if (fileDesc->fileSize != AD_NO_SIZE && fileDesc->fileSize != filesSizeMD5[tstr].size) {
fileMissing = true;
break;
}
}
if (!fileMissing) {
// Count the number of matching files. Then, only keep those
// entries which match a maximal amount of files.
int curFilesMatched = 0;
for (fileDesc = g->desc.filesDescriptions; fileDesc->fileName; fileDesc++)
curFilesMatched++;
if (curFilesMatched >= maxFilesMatched) {
maxFilesMatched = curFilesMatched;
matched = ADDetectedGame(&g->desc);
}
}
}
return matched;
}
REGISTER_PLUGIN_STATIC(TINSEL_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, TinselMetaEngineDetection);

View File

@@ -0,0 +1,82 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef TINSEL_DETECTION_H
#define TINSEL_DETECTION_H
#include "engines/advancedDetector.h"
namespace Tinsel {
// (Optionally) remove the "baked-in" letterboxing in Discworld 2/Noir.
#define GAMEOPTION_CROP_HEIGHT_480_TO_432 GUIO_GAMEOPTIONS1
enum TinselGameID {
GID_DW1 = 0,
GID_DW2 = 1,
GID_NOIR = 2
};
enum TinselGameFeatures {
GF_SCNFILES = 1 << 0,
GF_ENHANCED_AUDIO_SUPPORT = 1 << 1,
GF_ALT_MIDI = 1 << 2, // Alternate sequence in midi.dat file
// The GF_USE_?FLAGS values specify how many country flags are displayed
// in the subtitles options dialog.
// None of these defined -> 1 language, in ENGLISH.TXT
GF_USE_3FLAGS = 1 << 3, // French, German, Spanish
GF_USE_4FLAGS = 1 << 4, // French, German, Spanish, Italian
GF_USE_5FLAGS = 1 << 5 // All 5 flags
};
/**
* The following is the ScummVM definitions of the various Tinsel versions:
* TINSEL_V0 - This was an early engine version that was only used in the Discworld 1
* demo.
* TINSEL_V1 - This was the engine version used by Discworld 1. Note that there were two
* major releases: an earlier version that used *.gra files, and a later one that
* used *.scn files, and contained certain script and engine bugfixes. In ScummVM,
* we treat both releases as 'Tinsel 1', since the engine fixes from the later
* version work equally well the earlier version data.
* TINSEL_V2 - This is the engine used for the Discworld 2 game.
*/
enum TinselEngineVersion {
TINSEL_V0 = 0,
TINSEL_V1 = 1,
TINSEL_V2 = 2,
TINSEL_V3 = 3
};
struct TinselGameDescription {
AD_GAME_DESCRIPTION_HELPERS(desc);
ADGameDescription desc;
int gameID;
int gameType;
uint32 features;
uint16 version;
};
} // End of namespace Tinsel
#endif // TINSEL_DETECTION_H

View File

@@ -0,0 +1,872 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/translation.h"
namespace Tinsel {
static const TinselGameDescription gameDescriptions[] = {
// Note: The following is the (hopefully) definitive list of version details:
// TINSEL_V0: Used only by the Discworld 1 demo - this used a more primitive version
// of the Tinsel engine and graphics compression
// TINSEL_V1: There were two versions of the Discworld 1 game - the first used .GRA
// files, and the second used .SCN files. The second also provided some fixes to
// various script bugs and coding errors, but is still considered TINSEL_V1,
// as both game versions work equally well with the newer code.
// TINSEL_V2: The Discworld 2 game used this updated version of the Tinsel 1 engine,
// and as far as we know there aren't any variations of this engine.
// ==== Discworld 1 early (TinselV0) entries ==============================
{ // Floppy Demo V0 from https://web.archive.org/web/20100415160943/http://www.adventure-treff.de/specials/dl_demos.php
{
"dw",
"Floppy Demo",
AD_ENTRY1s("dw.gra", "ce1b57761ba705221bcf70955b827b97", 441192),
//AD_ENTRY1s("dw.scn", "ccd72f02183d0e96b6e7d8df9492cda8", 23308),
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_DEMO,
GUIO3(GUIO_NOSPEECH, GUIO_NOSFX, GUIO_NOMUSIC)
},
GID_DW1,
0,
0,
TINSEL_V0,
},
// ==== Discworld 1 entries ===============================================
{ // CD Demo V1 version, with *.gra files
{
"dw",
"CD Demo",
AD_ENTRY2s("dw.gra", "ef5a2518c9e205f786f5a4526396e661", 781676,
"english.smp", NULL, AD_NO_SIZE),
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_DEMO | ADGF_CD,
GUIO0()
},
GID_DW1,
0,
0,
TINSEL_V1,
},
{ // Macintosh CD Demo V1 version, with *.scn files, see tracker #5517
{
"dw",
"CD Demo",
{
{"dw.scn", 0, "cfc40a8d5d476a1c9d3abf826fa46f8c", 1272686},
{"english.txt", 0, "c69b5d2067e9114a63569a61e9a82faa", 228878},
{"english.smp", 0, NULL, AD_NO_SIZE},
AD_LISTEND
},
Common::EN_ANY,
Common::kPlatformMacintosh,
ADGF_DEMO | ADGF_CD,
GUIO0()
},
GID_DW1,
0,
GF_SCNFILES,
TINSEL_V1,
},
{ // Multilingual Floppy V1 with *.gra files.
// Note: It contains no english subtitles.
{
"dw",
"Floppy",
{
{"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
{"french.txt", 0, NULL, AD_NO_SIZE},
{"german.txt", 0, NULL, AD_NO_SIZE},
{"italian.txt", 0, NULL, AD_NO_SIZE},
{"spanish.txt", 0, NULL, AD_NO_SIZE},
AD_LISTEND
},
Common::FR_FRA,
Common::kPlatformDOS,
ADGF_DROPLANGUAGE,
GUIO1(GUIO_NOSPEECH)
},
GID_DW1,
0,
GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
TINSEL_V1,
},
{
{
"dw",
"Floppy",
{
{"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
{"french.txt", 0, NULL, AD_NO_SIZE},
{"german.txt", 0, NULL, AD_NO_SIZE},
{"italian.txt", 0, NULL, AD_NO_SIZE},
{"spanish.txt", 0, NULL, AD_NO_SIZE},
AD_LISTEND
},
Common::DE_DEU,
Common::kPlatformDOS,
ADGF_DROPLANGUAGE,
GUIO1(GUIO_NOSPEECH)
},
GID_DW1,
0,
GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
TINSEL_V1,
},
{
{
"dw",
"Floppy",
{
{"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
{"french.txt", 0, NULL, AD_NO_SIZE},
{"german.txt", 0, NULL, AD_NO_SIZE},
{"italian.txt", 0, NULL, AD_NO_SIZE},
{"spanish.txt", 0, NULL, AD_NO_SIZE},
AD_LISTEND
},
Common::IT_ITA,
Common::kPlatformDOS,
ADGF_DROPLANGUAGE,
GUIO1(GUIO_NOSPEECH)
},
GID_DW1,
0,
GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
TINSEL_V1,
},
{
{
"dw",
"Floppy",
{
{"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
{"french.txt", 0, NULL, AD_NO_SIZE},
{"german.txt", 0, NULL, AD_NO_SIZE},
{"italian.txt", 0, NULL, AD_NO_SIZE},
{"spanish.txt", 0, NULL, AD_NO_SIZE},
AD_LISTEND
},
Common::ES_ESP,
Common::kPlatformDOS,
ADGF_DROPLANGUAGE,
GUIO1(GUIO_NOSPEECH)
},
GID_DW1,
0,
GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
TINSEL_V1,
},
{ // Floppy V1 version, with *.gra files
{
"dw",
"Floppy",
AD_ENTRY1s("dw.gra", "c8808ccd988d603dd35dff42013ae7fd", 781656),
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GUIO_NOSPEECH)
},
GID_DW1,
0,
GF_ENHANCED_AUDIO_SUPPORT,
TINSEL_V1,
},
{ // CD V1 version, with *.gra files (same as the floppy one, with english.smp)
{
"dw",
"CD",
AD_ENTRY2s("dw.gra", "c8808ccd988d603dd35dff42013ae7fd", 781656,
"english.smp", NULL, AD_NO_SIZE),
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_CD,
GUIO0()
},
GID_DW1,
0,
GF_ENHANCED_AUDIO_SUPPORT,
TINSEL_V1,
},
{ // Polish fan translation CD V1 version, with *.gra files (same as the floppy one, with english.smp)
{
"dw",
"CD",
AD_ENTRY2s("dw.gra", "ef05bbd2a754bd11a2e87bcd84ab5ccf", 781864,
"english.smp", NULL, AD_NO_SIZE),
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_CD,
GUIO_NONE
},
GID_DW1,
0,
GF_ENHANCED_AUDIO_SUPPORT,
TINSEL_V1,
},
{ // Polish fan translaction floppy V1 version, with *.gra files
{
"dw",
"Floppy",
AD_ENTRY1s("dw.gra", "ef05bbd2a754bd11a2e87bcd84ab5ccf", 781864),
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO_NOSPEECH
},
GID_DW1,
0,
GF_ENHANCED_AUDIO_SUPPORT,
TINSEL_V1,
},
{ // Italian CD with english speech and *.gra files.
// Note: It contains only italian subtitles, but inside english.txt
{
"dw",
"CD",
{
{"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
{"english.txt", 0, "15f0703f85477d7fab4280bf938b61c1", 237774},
{"english.smp", 0, NULL, AD_NO_SIZE},
AD_LISTEND
},
Common::IT_ITA,
Common::kPlatformDOS,
ADGF_DROPLANGUAGE | ADGF_CD,
GUIO0()
},
GID_DW1,
0,
GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
TINSEL_V1,
},
{ // Multilingual CD with english speech and *.gra files.
// Note: It contains no english subtitles.
{
"dw",
"CD",
{
{"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
{"english.smp", 0, NULL, AD_NO_SIZE},
{"french.txt", 0, NULL, AD_NO_SIZE},
{"german.txt", 0, NULL, AD_NO_SIZE},
{"italian.txt", 0, NULL, AD_NO_SIZE},
{"spanish.txt", 0, NULL, AD_NO_SIZE},
AD_LISTEND
},
Common::FR_FRA,
Common::kPlatformDOS,
ADGF_DROPLANGUAGE | ADGF_CD,
GUIO0()
},
GID_DW1,
0,
GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
TINSEL_V1,
},
{
{
"dw",
"CD",
{
{"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
{"english.smp", 0, NULL, AD_NO_SIZE},
{"french.txt", 0, NULL, AD_NO_SIZE},
{"german.txt", 0, NULL, AD_NO_SIZE},
{"italian.txt", 0, NULL, AD_NO_SIZE},
{"spanish.txt", 0, NULL, AD_NO_SIZE},
AD_LISTEND
},
Common::DE_DEU,
Common::kPlatformDOS,
ADGF_DROPLANGUAGE | ADGF_CD,
GUIO0()
},
GID_DW1,
0,
GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
TINSEL_V1,
},
{
{
"dw",
"CD",
{
{"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
{"english.smp", 0, NULL, AD_NO_SIZE},
{"french.txt", 0, NULL, AD_NO_SIZE},
{"german.txt", 0, NULL, AD_NO_SIZE},
{"italian.txt", 0, NULL, AD_NO_SIZE},
{"spanish.txt", 0, NULL, AD_NO_SIZE},
AD_LISTEND
},
Common::IT_ITA,
Common::kPlatformDOS,
ADGF_DROPLANGUAGE | ADGF_CD,
GUIO0()
},
GID_DW1,
0,
GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
TINSEL_V1,
},
{
{
"dw",
"CD",
{
{"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
{"english.smp", 0, NULL, AD_NO_SIZE},
{"french.txt", 0, NULL, AD_NO_SIZE},
{"german.txt", 0, NULL, AD_NO_SIZE},
{"italian.txt", 0, NULL, AD_NO_SIZE},
{"spanish.txt", 0, NULL, AD_NO_SIZE},
AD_LISTEND
},
Common::ES_ESP,
Common::kPlatformDOS,
ADGF_DROPLANGUAGE | ADGF_CD,
GUIO0()
},
GID_DW1,
0,
GF_USE_4FLAGS | GF_ENHANCED_AUDIO_SUPPORT,
TINSEL_V1,
},
{ // English CD v2
{
"dw",
"CD",
AD_ENTRY2s("dw.scn", "70955425870c7720d6eebed903b2ef41", 776188,
"english.smp", NULL, AD_NO_SIZE),
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_CD,
GUIO0()
},
GID_DW1,
0,
GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
TINSEL_V1,
},
{ // Hebrew CD v2
{
"dw",
"CD",
AD_ENTRY2s("dw.scn", "759d1374b4f02af6d52fc07c96679936", 770780,
"english.smp", NULL, AD_NO_SIZE),
Common::HE_ISR,
Common::kPlatformDOS,
ADGF_CD,
GUIO0()
},
GID_DW1,
0,
GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
TINSEL_V1,
},
{ // Discworld PSX CD
{
"dw",
"CD",
AD_ENTRY1s("english.txt", "7526cfc3a64e00f223795de476b4e2c9", 230326),
Common::EN_ANY,
Common::kPlatformPSX,
ADGF_CD,
GUIO0()
},
GID_DW1,
0,
GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
TINSEL_V1,
},
{ // Discworld PSX German CD
{
"dw",
"CD",
AD_ENTRY1s("dw.scn", "0b34bb57cd3961e4528e4bce48cc0ab9", 339764),
Common::DE_DEU,
Common::kPlatformPSX,
ADGF_CD,
GUIO0()
},
GID_DW1,
0,
GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
TINSEL_V1,
},
{ // Discworld PSX CD Japanese
{
"dw",
"CD",
AD_ENTRY1s("dw.scn", "bd2e47010565998641ec45a9c9285be0", 328048),
Common::JA_JPN,
Common::kPlatformPSX,
ADGF_CD | ADGF_UNSTABLE,
GUIO0()
},
GID_DW1,
0,
GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
TINSEL_V1,
},
{ // multilanguage PSX demo
{
"dw",
"CD Demo",
{
{"french.txt", 0, "e7020d35f58d0d187052ac406d86cc87", 273914},
{"german.txt", 0, "52f0a01e0ff0d340b02a36fd5109d705", 263942},
{"italian.txt", 0, "15f0703f85477d7fab4280bf938b61c1", 239834},
{"spanish.txt", 0, "c324170c3f1922c605c5cc09ba265aa5", 236702},
{"english.txt", 0, "7526cfc3a64e00f223795de476b4e2c9", 230326},
AD_LISTEND
},
Common::EN_ANY,
Common::kPlatformPSX,
ADGF_CD | ADGF_DEMO,
GUIO0()
},
GID_DW1,
0,
GF_SCNFILES,
TINSEL_V1,
},
{ // English Saturn CD
{
"dw",
_s("Saturn CD version is not yet supported"),
AD_ENTRY2s("dw.scn", "6803f293c88758057cc685b9437f7637", 382248,
"english.smp", NULL, AD_NO_SIZE),
Common::EN_ANY,
Common::kPlatformSaturn,
ADGF_CD,
GUIO0()
},
GID_DW1,
0,
GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT | ADGF_UNSUPPORTED,
TINSEL_V1,
},
{ // Mac English CD, see tracker #6384
{
"dw",
"CD",
AD_ENTRY2s("dw.scn", "114643df0d1f1530a0a9c5d4e38917bc", 1268553,
"english.smp", NULL, AD_NO_SIZE),
Common::EN_ANY,
Common::kPlatformMacintosh,
ADGF_CD,
GUIO0()
},
GID_DW1,
0,
GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
TINSEL_V1,
},
{ // Mac multilanguage CD
{
"dw",
"CD",
AD_ENTRY2s("dw.scn", "cfc40a8d5d476a1c9d3abf826fa46f8c", 1265532,
"english.smp", NULL, AD_NO_SIZE),
Common::EN_ANY,
Common::kPlatformMacintosh,
ADGF_CD,
GUIO0()
},
GID_DW1,
0,
GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
TINSEL_V1,
},
{ // Mac Japanese CD
{
"dw",
"CD",
AD_ENTRY2s("dw.scn", "55a11596b16130027fb28d8c203655a8", 1430841,
"english.smp", NULL, AD_NO_SIZE),
Common::JA_JPN,
Common::kPlatformMacintosh,
ADGF_CD | ADGF_UNSTABLE,
GUIO0()
},
GID_DW1,
0,
GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
TINSEL_V1,
},
{ // German CD re-release "Neon Edition"
// Note: This release has ENGLISH.TXT (with german content) instead of GERMAN.TXT
{
"dw",
"CD",
AD_ENTRY1s("dw.scn", "6182c7986eaec893c62fb6ea13a9f225", 774556),
Common::DE_DEU,
Common::kPlatformDOS,
ADGF_CD,
GUIO0()
},
GID_DW1,
0,
GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT | GF_ALT_MIDI,
TINSEL_V1,
},
{ // Russian Discworld 1
{
"dw",
"CD",
{
{"dw.scn", 0, "133041bde59d05c1bf084fd6f1bdce4b", 776524},
{"english.txt", 0, "f73dcbd7b136b37c2adf7c9448ea336d", 231821},
{"english.smp", 0, NULL, AD_NO_SIZE},
AD_LISTEND
},
Common::RU_RUS,
Common::kPlatformDOS,
ADGF_CD,
GUIO1(GUIO_NOASPECT)
},
GID_DW1,
0,
GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
TINSEL_V1,
},
{ // English CD "Argentum Collection"
{
"dw",
"CD",
AD_ENTRY2s("dw.scn", "36795d539e290838fa1d4c11789cb142", 776188,
"english.smp", NULL, AD_NO_SIZE),
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_CD,
GUIO0()
},
GID_DW1,
0,
GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
TINSEL_V1,
},
{ // Russian Discworld 1. Fan translation v1.1
{
"dw",
"CD v1.1",
{
{"dw.scn", 0, "133041bde59d05c1bf084fd6f1bdce4b", 776524},
{"english.txt", 0, "317542cf2e50106d9c9421ddcf821e22", 221656},
{"english.smp", 0, NULL, AD_NO_SIZE},
AD_LISTEND
},
Common::RU_RUS,
Common::kPlatformDOS,
ADGF_CD,
GUIO1(GUIO_NOASPECT)
},
GID_DW1,
0,
GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
TINSEL_V1,
},
{ // Polish fan translaction Discworld 1
{
"dw",
"CD",
{
{"dw.scn", 0, "fa169d2c98660215ebd84b49c1899eef", 776396},
{"english.txt", 0, "c1a53eb7ec812689dab70e2bb22cf2ab", 224151},
{"english.smp", 0, NULL, AD_NO_SIZE},
AD_LISTEND
},
Common::PL_POL,
Common::kPlatformDOS,
ADGF_CD,
GUIO_NONE
},
GID_DW1,
0,
GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
TINSEL_V1,
},
// ==== Discworld 2 entries ===============================================
// Note: All Discworld 2 versions are CD only, therefore we don't add the ADGF_CD flag
#define DISCWORLD2_GUIOPTIONS GUIO2(GUIO_NOASPECT, GAMEOPTION_CROP_HEIGHT_480_TO_432)
{ // English Discworld 2 demo (dw2-win-demo-en)
{
"dw2",
"Demo",
AD_ENTRY2s("dw2.scn", "853ab998f5136b69bc586991175d6eeb", 4231121,
"english.smp", "b5660a0e031cb4710bcb0ef5629ea61d", 28562357),
Common::EN_ANY,
Common::kPlatformWindows,
ADGF_DEMO,
DISCWORLD2_GUIOPTIONS
},
GID_DW2,
0,
GF_SCNFILES,
TINSEL_V2,
},
{ // English Discworld 2 demo (second Windows demo: dw2-win-demo-2-en)
{
"dw2",
"",
AD_ENTRY3s("dw2.scn", "3f24abb61a058f8faeac7c0768cf21fc", 4224921,
"english.smp", "b5660a0e031cb4710bcb0ef5629ea61d", 31360342,
"english.txt", "f17e10eccac0fb2d1fea489a951da266", 283144),
Common::EN_ANY,
Common::kPlatformWindows,
ADGF_UNSUPPORTED | ADGF_DEMO,
DISCWORLD2_GUIOPTIONS
},
GID_DW2,
0,
GF_SCNFILES,
TINSEL_V2,
},
{ // English (US) Discworld 2 demo (DOS demo: dw2-dos-demo-en)
{
"dw2",
"",
AD_ENTRY2s("dw2.scn", "05beafadd26562d708f68194d337b2cb", 103221,
"us.smp", "b5660a0e031cb4710bcb0ef5629ea61d", 28320582),
Common::EN_USA,
Common::kPlatformDOS,
ADGF_UNSUPPORTED | ADGF_DEMO,
DISCWORLD2_GUIOPTIONS
},
GID_DW2,
0,
GF_SCNFILES,
TINSEL_V2,
},
{ // European/Australian Discworld 2 release
{
"dw2",
"CD",
AD_ENTRY2s("dw2.scn", "c6d15ce9720a9d8fef06e6582dcf3f34", 103593,
"english1.smp", NULL, AD_NO_SIZE),
Common::EN_GRB,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
DISCWORLD2_GUIOPTIONS
},
GID_DW2,
0,
GF_SCNFILES,
TINSEL_V2,
},
{ // US Discworld 2 release
{
"dw2",
"CD",
AD_ENTRY2s("dw2.scn", "c6d15ce9720a9d8fef06e6582dcf3f34", 103593,
"us1.smp", NULL, AD_NO_SIZE),
Common::EN_USA,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
DISCWORLD2_GUIOPTIONS
},
GID_DW2,
0,
GF_SCNFILES,
TINSEL_V2,
},
{ // French version of Discworld 2
{
"dw2",
"CD",
AD_ENTRY2s("dw2.scn", "c6d15ce9720a9d8fef06e6582dcf3f34", 103593,
"french1.smp", NULL, AD_NO_SIZE),
Common::FR_FRA,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
DISCWORLD2_GUIOPTIONS
},
GID_DW2,
0,
GF_SCNFILES,
TINSEL_V2,
},
{ // German Discworld 2 re-release "Neon Edition"
{
"dw2",
"CD",
AD_ENTRY2s("dw2.scn", "c6d15ce9720a9d8fef06e6582dcf3f34", 103593,
"german1.smp", NULL, AD_NO_SIZE),
Common::DE_DEU,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
DISCWORLD2_GUIOPTIONS
},
GID_DW2,
0,
GF_SCNFILES,
TINSEL_V2,
},
{ // Italian/Spanish Discworld 2
{
"dw2",
"CD",
{
{"dw2.scn", 0, "c6d15ce9720a9d8fef06e6582dcf3f34", 103593},
{"english1.smp", 0, NULL, AD_NO_SIZE},
{"italian1.txt", 0, "d443249f8b55489b5888c227b9096f4e", 246495},
AD_LISTEND
},
Common::IT_ITA,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
DISCWORLD2_GUIOPTIONS
},
GID_DW2,
0,
GF_SCNFILES,
TINSEL_V2,
},
{
{
"dw2",
"CD",
{
{"dw2.scn", 0, "c6d15ce9720a9d8fef06e6582dcf3f34", 103593},
{"english1.smp", 0, NULL, AD_NO_SIZE},
{"spanish1.txt", 0, "bc6e147c5f542db228ac577357e4d897", 230323},
AD_LISTEND
},
Common::ES_ESP,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
DISCWORLD2_GUIOPTIONS
},
GID_DW2,
0,
GF_SCNFILES,
TINSEL_V2,
},
{ // Russian Discworld 2 release by Fargus
{
"dw2",
"CD",
{
{"dw2.scn", 0, "c6d15ce9720a9d8fef06e6582dcf3f34", 103593},
{"english1.smp", 0, NULL, AD_NO_SIZE},
{"english1.txt", 0, "b522e19d7b2cd7b85e50e36fe48e36a9", 274444},
AD_LISTEND
},
Common::RU_RUS,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
DISCWORLD2_GUIOPTIONS
},
GID_DW2,
0,
GF_SCNFILES,
TINSEL_V2,
},
// ==== Discworld Noir entries ===============================================
// Note: All Discworld Noir versions are CD only, therefore we don't add the ADGF_CD flag
#define NOIR_GUIOPTIONS GUIO1(GAMEOPTION_CROP_HEIGHT_480_TO_432)
{ // Discworld Noir, Windows 3CD version
{
"noir",
"CD",
AD_ENTRY2s("dw3.scn", "16104acdc66cda903f860acac02a96bd", AD_NO_SIZE,
"english.smp", "94e510fd33c5c4a67b274bf5c068a87a", AD_NO_SIZE),
Common::EN_ANY,
Common::kPlatformWindows,
ADGF_UNSTABLE,
NOIR_GUIOPTIONS
},
GID_NOIR,
0,
GF_SCNFILES,
TINSEL_V3,
},
{ // Discworld Noir, Interactive Windows Demo
{
"noir",
"Demo",
AD_ENTRY2s("english.smp", "ecca3ec84ad6460bf289e6171ac32048", 73494004,
"dw3.scn", "f8e50c19b44a2c6eaee5f78654647878", 803781),
Common::EN_ANY,
Common::kPlatformWindows,
ADGF_UNSTABLE | ADGF_DEMO,
NOIR_GUIOPTIONS
},
GID_NOIR,
0,
GF_SCNFILES,
TINSEL_V3,
},
{ AD_TABLE_END_MARKER, 0, 0, 0, 0 }
};
} // End of namespace Tinsel

5914
engines/tinsel/dialogs.cpp Normal file

File diff suppressed because it is too large Load Diff

577
engines/tinsel/dialogs.h Normal file
View File

@@ -0,0 +1,577 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* Inventory related functions
*/
#ifndef TINSEL_INVENTORY_H // prevent multiple includes
#define TINSEL_INVENTORY_H
#include "tinsel/dw.h"
#include "tinsel/events.h" // for PLR_EVENT, PLR_EVENT
#include "tinsel/inv_objects.h"
#include "tinsel/object.h"
#include "tinsel/movers.h"
namespace Common {
class Serializer;
struct KeyState;
}
namespace Tinsel {
struct OBJECT;
struct FREEL;
struct FILM;
struct CONFINIT;
enum {
INV_OPEN = -1, // DW1 only
INV_CONV = 0,
INV_1 = 1,
INV_2 = 2,
DW0_INV_CONF = 3,
INV_MENU = 3, // DW2 constant
NUM_INV_V0 = 4,
// Discworld 2 constants
DW2_INV_OPEN = 5,
INV_DEFAULT = 6,
// Noir constants
INV_3 = 3,
INV_4 = 4,
NOIR_INV_CONF = 5,
NUM_INV_V3 = 6,
INV_7NOINV = 7,
INV_8NOINV = 8,
INV_NOTEBOOK = 9,
MAX_NUM_INV = NUM_INV_V3 // For determination of _invD array size
};
#define INV_CONF ((TinselVersion == 3) ? NOIR_INV_CONF : DW0_INV_CONF)
#define NUM_INV ((TinselVersion == 3) ? NUM_INV_V3 : NUM_INV_V0)
enum {
NOOBJECT = -1,
INV_NOICON_V0 = -1,
INV_CLOSEICON = -2,
INV_OPENICON = -3,
INV_HELDNOTIN_V0 = -4,
// Noir discerns between NOOBJECT and INV_NOICON
INV_NOICON_V3 = 0,
INV_HELDNOTIN_V3 = 1,
INV_HELDIN = 2,
};
#define INV_NOICON ((TinselVersion == 3) ? INV_NOICON_V3 : INV_NOICON_V0)
#define INV_HELDNOTIN ((TinselVersion == 3) ? INV_HELDNOTIN_V3 : INV_HELDNOTIN_V0)
enum CONV_PARAM {
CONV_DEF,
CONV_BOTTOM,
CONV_END,
CONV_TOP
};
enum InventoryType { EMPTY,
FULL,
CONF };
enum InvCursorFN { IC_AREA,
IC_DROP };
#define sliderRange (_sliderYmax - _sliderYmin)
#define MAXSLIDES 4
#define MAX_PERMICONS 10 // Max permanent conversation icons
#define MAXHICONS 10 // Max dimensions of
#define MAXVICONS 6 // an inventory window
#define SG_DESC_LEN 40 // Max. saved game description length
// Number of objects that makes up an empty window
#define MAX_WCOMP_T0 21 // 4 corners + (3+3) sides + (2+2) extra sides
// + Bground + title + slider
// + more Needed for save game window
#define MAX_WCOMP_T3 84
#define MAX_WCOMP (TinselVersion == 3 ? MAX_WCOMP_T3 : MAX_WCOMP_T0)
#define MAX_ICONS MAXHICONS *MAXVICONS
#define MAX_ININV_TOT 160
enum CONFTYPE {
MAIN_MENU,
SAVE_MENU,
LOAD_MENU,
QUIT_MENU,
RESTART_MENU,
SOUND_MENU,
CONTROLS_MENU,
SUBTITLES_MENU,
HOPPER_MENU1,
HOPPER_MENU2,
TOP_WINDOW
};
struct INV_DEF {
int MinHicons; // }
int MinVicons; // } Dimension limits
int MaxHicons; // }
int MaxVicons; // }
int NoofHicons; // }
int NoofVicons; // } Current dimentsions
int contents[160]; // Contained items
int NoofItems; // Current number of held items
int FirstDisp; // Index to first item currently displayed
int inventoryX; // } Display position
int inventoryY; // }
int otherX; // } Display position
int otherY; // }
int MaxInvObj; // Max. allowed contents
SCNHANDLE hInvTitle; // Window heading
bool resizable; // Re-sizable window?
bool bMoveable; // Moveable window?
int sNoofHicons; // }
int sNoofVicons; // } Current dimensions
bool bMax; // Maximised last time open?
};
//----- Data pertinant to scene hoppers ------------------------
struct HOPPER {
uint32 hScene;
SCNHANDLE hSceneDesc;
uint32 numEntries;
uint32 entryIndex;
};
struct HOPENTRY {
uint32 eNumber; // entrance number
SCNHANDLE hDesc; // handle to entrance description
uint32 flags;
};
enum BTYPE {
RGROUP, ///< Radio button group - 1 is selectable at a time. Action on double click
ARSBUT, ///< Action if a radio button is selected
AABUT, ///< Action always
AATBUT, ///< Action always, text box
ARSGBUT,
AAGBUT, ///< Action always, graphic button
SLIDER, ///< Not a button at all
TOGGLE, ///< Discworld 1 toggle
TOGGLE1, ///< Discworld 2 toggle type 1
TOGGLE2, ///< Discworld 2 toggle type 2
DCTEST,
FLIP,
FRGROUP,
ROTATE,
NOTHING
};
enum BFUNC {
NOFUNC,
SAVEGAME,
LOADGAME,
IQUITGAME,
CLOSEWIN,
OPENLOAD,
OPENSAVE,
OPENREST,
OPENSOUND,
OPENCONT,
#ifndef JAPAN
OPENSUBT,
#endif
OPENQUIT,
INITGAME,
MUSICVOL,
HOPPER2, // Call up Scene Hopper 2
BF_CHANGESCENE,
CLANG,
RLANG
#ifdef MAC_OPTIONS
,
MASTERVOL,
SAMPVOL
#endif
};
enum TM { TM_POINTER,
TM_INDEX,
TM_STRINGNUM,
TM_UNK4,
TM_NONE };
// For slideSlider() and similar
enum SSFN {
S_START,
S_SLIDE,
S_END,
S_TIMEUP,
S_TIMEDN
};
struct CONFBOX {
BTYPE boxType;
BFUNC boxFunc;
TM textMethod;
char *boxText;
int ixText;
int xpos;
int ypos;
int w; // Doubles as max value for SLIDERs
int h; // Doubles as iteration size for SLIDERs
int *ival;
int bi; // Base index for AAGBUTs
};
// Data for button press/toggle effects
struct BUTTONEFFECT {
bool bButAnim;
CONFBOX *box;
bool press; // true = button press; false = button toggle
};
enum class SysReel;
class Dialogs {
public:
Dialogs();
virtual ~Dialogs();
void popUpInventory(int invno, int menuId = -1);
void openMenu(CONFTYPE menuType);
void xMovement(int x);
void yMovement(int y);
void eventToInventory(PLR_EVENT pEvent, const Common::Point &coOrds);
int whichItemHeld();
void holdItem(int item, bool bKeepFilm = false);
void dropItem(int item);
void clearInventory(int invno);
void addToInventory(int invno, int icon, bool hold = false);
bool remFromInventory(int invno, int icon);
void registerIcons(void *cptr, int num);
void idec_convw(SCNHANDLE text, int MaxContents, int MinWidth, int MinHeight,
int StartWidth, int StartHeight, int MaxWidth, int MaxHeight);
void idec_inv1(SCNHANDLE text, int MaxContents, int MinWidth, int MinHeight,
int StartWidth, int StartHeight, int MaxWidth, int MaxHeight);
void idec_inv2(SCNHANDLE text, int MaxContents, int MinWidth, int MinHeight,
int StartWidth, int StartHeight, int MaxWidth, int MaxHeight);
// Noir
Common::Array<int> getAllNotebookClues() const;
void idec_invMain(SCNHANDLE text, int MaxContents);
bool inventoryActive();
bool inventoryOrNotebookActive();
void permaConvIcon(int icon, bool bEnd = false);
void convPos(int bpos);
void convPoly(HPOLYGON hPoly);
int getIcon();
void closeDownConv();
void hideConversation(bool hide);
bool convIsHidden();
void convAction(int index);
void setConvDetails(CONV_PARAM fn, HPOLYGON hPoly, int ano);
void inventoryIconCursor(bool bNewItem);
void setInvWinParts(SCNHANDLE hf);
void setFlagFilms(SCNHANDLE hf);
void setConfigStrings(SCNHANDLE *tp);
int invItem(Common::Point &coOrds, bool update);
int invItem(int *x, int *y, bool update);
int invItemId(int x, int y);
int inventoryPos(int num);
bool isInInventory(int object, int invnum);
void killInventory();
void syncInvInfo(Common::Serializer &s);
int invGetLimit(int invno);
void invSetLimit(int invno, int MaxContents);
void invSetSize(int invno, int MinWidth, int MinHeight,
int StartWidth, int StartHeight, int MaxWidth, int MaxHeight);
bool getIsInvObject(int id);
int whichInventoryOpen();
bool isTopWindow();
bool menuActive();
bool isConvWindow();
const FILM *getObjectFilm(int object) const;
void setObjectFilm(int object, SCNHANDLE hFilm);
void callFunction(BFUNC boxFunc);
OBJECT *addObject(const FREEL *pfreel, int num);
void invPutDown(int index);
void slideMSlider(int x, SSFN fn);
void addBoxes(bool posnSlide);
void select(int i, bool force);
void fillInInventory();
void invCursor(InvCursorFN fn, int CurX, int CurY);
const InventoryObject *getInvObject(int id);
const InventoryObjectT3 *getInvObjectT3(int id);
void invPointEvent(const InventoryObject *invObj, int index);
bool updateString(const Common::KeyState &kbd);
bool inventoryIsActive() { return _inventoryState == ACTIVE_INV; }
bool isMixingDeskControl() { return _invDragging == ID_MDCONT; }
int currentInventoryX() { return _invD[_activeInv].inventoryX; }
int currentInventoryY() { return _invD[_activeInv].inventoryY; }
bool configurationIsActive() { return _activeInv == INV_CONF; }
bool displayObjectsActive() { return _objArray[0] != NULL; }
bool inventoryIsHidden() { return _InventoryHidden; }
const FILM *getWindowData();
void redraw();
// Noir
bool isConvAndNotMove();
bool _noLanguage;
int _glitterIndex;
volatile int _pointedWaitCount; // used by ObjectProcess - fix the 'repeated pressing bug'
// For editing save game names
char _saveGameDesc[SG_DESC_LEN + 2];
OBJECT *_iconArray[MAX_ICONS]; // Current display objects (icons)
BUTTONEFFECT _buttonEffect;
private:
int whichMenuBox(int curX, int curY, bool bSlides);
void confActionSpecial(int i);
bool rePosition();
bool languageChange();
void primeSceneHopper();
void freeSceneHopper();
void rememberChosenScene();
void setChosenScene();
void firstEntry(int first);
void hopAction();
void dumpIconArray();
void dumpDobjArray();
void dumpObjArray();
void firstScene(int first);
void firstFile(int first);
int getObjectIndex(int id) const;
void invSaveGame();
void invLoadGame();
int invArea(int x, int y);
void invBoxes(bool InBody, int curX, int curY);
void invLabels(bool InBody, int aniX, int aniY);
void adjustTop();
OBJECT *addInvObject(int num, SCNHANDLE *hNewScript, int *aniSpeed);
void addBackground(OBJECT **rect, const Common::Rect &bounds, OBJECT **title = nullptr, int textFrom = 0);
void addTitle(OBJECT **title, const Common::Rect &rect);
void addSlider(OBJECT **slide, const FILM *pfilm);
void addBox(int *pi, const int i);
void addEWSlider(OBJECT **slide, const FILM *pfilm);
void positionInventory(OBJECT *pMultiObj, int xOffset, int yOffset, int zPosition);
int addExtraWindow(int x, int y, OBJECT **retObj);
void constructInventoryCommon(SysReel reel, bool hasTitle);
void constructConversationInventory();
void constructInventory(InventoryType filling);
void constructOtherInventory(int menuId);
void constructMainInventory();
void alterCursor(int num);
void setMenuGlobals(CONFINIT *ci);
void closeInventory();
int nearestSlideY(int fity);
void slideSlider(int y, SSFN fn);
void slideCSlider(int y, SSFN fn);
void gettingTaller();
void gettingShorter();
void gettingWider();
void gettingNarrower();
void changeingSize();
void invDragStart();
void invDragEnd();
bool menuDown(int lines);
bool menuUp(int lines);
void menuRollDown();
void menuRollUp();
void menuPageDown();
void menuPageUp();
void inventoryDown();
void inventoryUp();
void menuAction(int i, bool dbl);
void invPickup(int index);
void invWalkTo(const Common::Point &coOrds);
void invAction();
void invLook(const Common::Point &coOrds);
void idec_inv(int num, SCNHANDLE text, int MaxContents,
int MinWidth, int MinHeight,
int StartWidth, int StartHeight,
int MaxWidth, int MaxHeight,
int startx, int starty, bool moveable);
//----- Permanent data (set once) -----
SCNHANDLE _flagFilm; // Window members and cursors' graphic data
SCNHANDLE _configStrings[20];
INV_DEF _invD[MAX_NUM_INV]; // Conversation + 2 inventories + ...
int _activeInv; // Which inventory is currently active
InventoryObjects *_invObjects; // Inventory objects' data
SCNHANDLE *_invFilms;
DIRECTION _initialDirection;
//----- Permanent data (updated, valid while inventory closed) -----
int _heldItem; // Current held item
SCNHANDLE _heldFilm;
SCNHANDLE _hWinParts; // Window members and cursors' graphic data
// Permanent contents of conversation inventory
int _permIcons[MAX_PERMICONS]; // Basic items i.e. permanent contents
int _numPermIcons; // - copy to conv. inventory at pop-up time
int _numEndIcons;
//----- Data pertinant to current active inventory -----
bool _InventoryHidden;
bool _InventoryMaximised;
bool _ItemsChanged; // When set, causes items to be re-drawn
int _SuppH; // 'Linear' element of
int _SuppV; // dimensions during re-sizing
int _yChange; //
int _yCompensate; // All to do with re-sizing.
int _xChange; //
int _xCompensate; //
bool _reOpenMenu;
int _TL, _TR, _BL, _BR; // Used during window construction
int _TLwidth, _TLheight; //
int _TRwidth; //
int _BLheight; //
LANGUAGE _displayedLanguage;
OBJECT *_objArray[MAX_WCOMP_T3]; // Current display objects (window)
OBJECT *_dispObjArray[MAX_WCOMP_T3]; // Current display objects (re-sizing window)
ANIM _iconAnims[MAX_ICONS];
OBJECT *_rectObject, *_slideObject; // Current display objects, for reference
// objects are in objArray.
int _sliderYpos; // For positioning the slider
int _sliderYmax, _sliderYmin; //
// Also to do with the slider
struct {
int n;
int y;
} _slideStuff[MAX_ININV_TOT + 1];
struct MDSLIDES {
int num;
OBJECT *obj;
int min, max;
};
MDSLIDES _mdSlides[MAXSLIDES];
int _numMdSlides;
// Icon clicked on to cause an event
// - Passed to conversation polygon or actor code via Topic()
// - (sometimes) Passed to inventory icon code via OtherObject()
int _thisIcon;
CONV_PARAM _thisConvFn; // Top, 'Middle' or Bottom
HPOLYGON _thisConvPoly; // Conversation code is in a polygon code block
int _thisConvActor; // ...or an actor's code block.
int _pointedIcon; // used by invLabels - icon pointed to on last call
int _sX; // used by slideMSlider() - current x-coordinate
int _lX; // used by slideMSlider() - last x-coordinate
bool _bMoveOnUnHide; // Set before start of conversation
// - causes conversation to be started in a sensible place
HOPPER *_pHopper;
HOPENTRY *_pEntries;
int _numScenes;
int _numEntries;
HOPPER *_pChosenScene;
int _lastChosenScene;
bool _bRemember;
enum { IC_NORMAL,
IC_DR,
IC_UR,
IC_TB,
IC_LR,
IC_INV,
IC_UP,
IC_DN } _invCursor;
enum { NO_INV,
IDLE_INV,
ACTIVE_INV,
BOGUS_INV } _inventoryState;
enum { ID_NONE,
ID_MOVE,
ID_SLIDE,
ID_BOTTOM,
ID_TOP,
ID_LEFT,
ID_RIGHT,
ID_TLEFT,
ID_TRIGHT,
ID_BLEFT,
ID_BRIGHT,
ID_CSLIDE,
ID_MDCONT } _invDragging;
};
void ObjectEvent(CORO_PARAM, int objId, TINSEL_EVENT event, bool bWait, int myEscape, bool *result = NULL);
} // End of namespace Tinsel
#endif /* TINSEL_INVENTRY_H */

244
engines/tinsel/drives.cpp Normal file
View File

@@ -0,0 +1,244 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* CD/drive handling functions
*/
#include "common/textconsole.h"
#include "tinsel/drives.h"
#include "tinsel/scene.h"
#include "tinsel/tinsel.h"
#include "tinsel/sched.h"
#include "tinsel/strres.h"
namespace Tinsel {
// These vars are reset upon engine destruction
char g_currentCD = '1';
static bool g_bChangingCD = false;
static char g_nextCD = '\0';
static uint32 g_lastTime = 0;
extern LANGUAGE g_sampleLanguage;
void ResetVarsDrives() {
g_currentCD = '1';
g_bChangingCD = false;
g_nextCD = '\0';
g_lastTime = 0;
}
void CdCD(CORO_PARAM) {
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
while (g_bChangingCD) {
if (CoroScheduler.getCurrentProcess()) {
// FIXME: CdCD gets passed a Common::nullContext in RegisterGlobals() and
// primeSceneHopper(), because I didn't know how to get a proper
// context without converting the whole calling stack to CORO'd
// functions. If these functions really get called while a CD
// change is requested, this needs to be resolved.
if (coroParam == Common::nullContext)
error("CdCD needs context");
CORO_SLEEP(1);
} else
error("No current process in CdCD()");
}
CORO_END_CODE;
}
int GetCurrentCD() {
// count from 1
return (g_currentCD - '1' + 1);
}
static const uint32 cdFlags[] = { fCd1, fCd2, fCd3, fCd4, fCd5, fCd6, fCd7, fCd8 };
void SetCD(int flags) {
if (flags & cdFlags[g_currentCD - '1'])
return;
error("SetCD() problem");
}
int GetCD(int flags) {
int i;
char cd = '\0';
if (flags & cdFlags[g_currentCD - '1'])
return GetCurrentCD();
for (i = 0; i < 8; i++) {
if (flags & cdFlags[i]) {
cd = (char)(i + '1');
break;
}
}
assert(i != 8);
g_nextCD = cd;
return cd;
}
void DoCdChange() {
if (g_bChangingCD && (g_system->getMillis() > (g_lastTime + 1000))) {
g_lastTime = g_system->getMillis();
_vm->_sound->closeSampleStream();
// Use the filesize of the sample file to determine, for Discworld 2, which CD it is
if (TinselVersion >= 2) {
TinselFile f;
if (!f.open(_vm->getSampleFile(g_sampleLanguage)))
// No CD present
return;
char sampleCdNumber = (f.size() >= (200 * 1024 * 1024)) ? '1' : '2';
f.close();
if (g_currentCD != sampleCdNumber)
return;
}
_vm->_sound->openSampleFiles();
ChangeLanguage(TextLanguage());
g_bChangingCD = false;
}
}
void SetNextCD(int cdNumber) {
assert(cdNumber == 1 || cdNumber == 2);
g_nextCD = (char)(cdNumber + '1' - 1);
}
bool GotoCD() {
// WORKAROUND: Somehow, CdDoChange() is called twice... Hopefully, this guard helps
if (g_currentCD == g_nextCD)
return false;
g_currentCD = g_nextCD;
/* if (bNoCD) {
strcpy(cdDirectory, hdDirectory);
cdLastBit[3] = currentCD;
strcat(cdDirectory, cdLastBit);
}
*/
g_bChangingCD = true;
return true;
}
bool TinselFile::_warningShown = false;
TinselFile::TinselFile() : ReadStreamEndian(TinselV1Saturn) {
_stream = nullptr;
}
TinselFile::TinselFile(bool bigEndian) : ReadStreamEndian(bigEndian) {
_stream = nullptr;
}
TinselFile::~TinselFile() {
delete _stream;
}
bool TinselFile::openInternal(const Common::Path &filename) {
_stream = SearchMan.createReadStreamForMember(filename);
if (!_stream)
_stream = SearchMan.createReadStreamForMember(filename.append("."));
return _stream != 0;
}
bool TinselFile::open(const Common::String &filename) {
if (openInternal(Common::Path(filename)))
return true;
if (TinselVersion <= 1)
return false;
// Check if the file being requested is the *1.* or *2.* files
const char *fname = filename.c_str();
const char *p = strchr(fname, '1');
if (!p)
p = strchr(fname, '2');
if (!p || (*(p + 1) != '.'))
return false;
// Form a filename without the CD number character
char newFilename[50];
strncpy(newFilename, fname, p - fname);
Common::strcpy_s(newFilename + (p - fname), sizeof(newFilename) - (p - fname), p + 1);
return openInternal(newFilename);
}
void TinselFile::close() {
delete _stream;
_stream = nullptr;
}
int64 TinselFile::pos() const {
assert(_stream);
return _stream->pos();
}
int64 TinselFile::size() const {
assert(_stream);
return _stream->size();
}
bool TinselFile::seek(int64 offset, int whence) {
assert(_stream);
return _stream->seek(offset, whence);
}
bool TinselFile::eos() const {
assert(_stream);
return _stream->eos();
}
bool TinselFile::err() const {
assert(_stream);
return _stream->err();
}
void TinselFile::clearErr() {
assert(_stream);
_stream->clearErr();
}
uint32 TinselFile::read(void *dataPtr, uint32 dataSize) {
assert(_stream);
return _stream->read(dataPtr, dataSize);
}
} // End of namespace Tinsel

90
engines/tinsel/drives.h Normal file
View File

@@ -0,0 +1,90 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* CD/drive handling functions
*/
#ifndef TINSEL_DRIVES_H
#define TINSEL_DRIVES_H
#include "common/coroutines.h"
#include "common/stream.h"
#include "tinsel/dw.h"
namespace Common {
class Path;
}
namespace Tinsel {
// flags2
#define fCd1 0x00000001L
#define fCd2 0x00000002L
#define fCd3 0x00000004L
#define fCd4 0x00000008L
#define fCd5 0x00000010L
#define fCd6 0x00000020L
#define fCd7 0x00000040L
#define fCd8 0x00000080L
#define fAllCds (fCd1|fCd2|fCd3|fCd4|fCd5|fCd6|fCd7|fCd8)
void DoCdChange();
void CdCD(CORO_PARAM);
int GetCurrentCD();
void SetCD(int flags);
int GetCD(int flags);
void SetNextCD(int cdNumber);
bool GotoCD();
class TinselFile : public Common::SeekableReadStream, public Common::ReadStreamEndian {
private:
static bool _warningShown;
Common::SeekableReadStream *_stream;
bool openInternal(const Common::Path &filename);
public:
// This constructor is only used for _sampleStream inside sound.h
TinselFile();
TinselFile(bool bigEndian);
~TinselFile() override;
bool open(const Common::String &filename);
void close();
char getCdNumber();
bool err() const override;
void clearErr() override;
bool eos() const override;
uint32 read(void *dataPtr, uint32 dataSize) override;
int64 pos() const override;
int64 size() const override;
bool seek(int64 offset, int whence = SEEK_SET) override;
};
} // End of namespace Tinsel
#endif /* TINSEL_DRIVES_H */

117
engines/tinsel/dw.h Normal file
View File

@@ -0,0 +1,117 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef TINSEL_DW_H
#define TINSEL_DW_H
#include "common/scummsys.h"
#include "common/endian.h"
namespace Tinsel {
/** scene handle data type */
typedef uint32 SCNHANDLE;
/** polygon handle */
typedef int HPOLYGON;
#define EOS_CHAR '\0' // string terminator
#define LF_CHAR '\x0a' // line feed
// file names
#define MIDI_FILE "midi.dat" // all MIDI sequences
#define INDEX_FILENAME "index" // name of index file
#define PSX_INDEX_FILENAME "index.dat" // name of index file in psx version
#define NO_SCNHANDLES 300 // number of memory handles for scenes
#define MASTER_SCNHANDLE 0 // master scene memory handle
// the minimum value a integer number can have
#define MIN_INT (1 << (8*sizeof(int) - 1))
#define MIN_INT16 (-32767)
// the maximum value a integer number can have
#define MAX_INT (~MIN_INT)
// inventory object handle (if there are inventory objects)
#define INV_OBJ_SCNHANDLE ((TinselVersion == 0) ? (2 << SCNHANDLE_SHIFT) : (1 << SCNHANDLE_SHIFT))
#define FIELD_WORLD ((TinselVersion == 3) ? 2 : 0)
#define FIELD_STATUS ((TinselVersion == 3) ? 8 : 1)
#define ZSHIFT 10
// We don't set the Z position for print and talk text
// i.e. it gets a Z position of 0
#define Z_INV_BRECT 10 // Inventory background rectangle
#define Z_INV_MFRAME ((TinselVersion == 3) ? 16 : 15) // Inventory window frame
#define Z_INV_HTEXT 15 // Inventory heading text
#define Z_INV_ICONS 16 // Icons in inventory
#define Z_INV_ITEXT 995 // Icon text
#define Z_INV_RFRAME 22 // Re-sizing frame
#define Z_CURSOR 1000 // Cursor
#define Z_CURSORTRAIL 999 // Cursor trails
#define Z_ACURSOR 990 // Auxillary cursor
#define Z_TAG_TEXT 995 // In front of auxiliary cursor
#define Z_MDGROOVE 20
#define Z_MDSLIDER 21
#define Z_TOPPLAY 100
#define Z_TOPW_TEXT Z_TAG_TEXT
// Started a collection of assorted maximum numbers here:
// TODO: Noir only has two movers - deal with that
#define MAX_MOVERS 6 // Moving actors using path system
#define MAX_SAVED_ACTORS 32 // Saved 'Normal' actors
#define MAX_SAVED_ALIVES 512 // Saves actors'lives
#define MAX_SAVED_ACTOR_Z 512 // Saves actors' Z-ness
// Legal non-existent entrance number for LoadScene()
#define NO_ENTRY_NUM (-3458) // Magic unlikely number
#define SAMPLETIMEOUT (20*ONE_SECOND)
// Language for the resource strings
enum LANGUAGE {
TXT_ENGLISH, TXT_FRENCH, TXT_GERMAN, TXT_ITALIAN, TXT_SPANISH,
TXT_HEBREW, TXT_HUNGARIAN, TXT_JAPANESE, TXT_US,
NUM_LANGUAGES
};
#define MAX_READ_RETRIES 5
// Definitions used for error messages
#define FILE_IS_CORRUPT "File %s is corrupt"
#define FILE_READ_ERROR "Error reading file %s"
#define CANNOT_FIND_FILE "Cannot find file %s"
#define NO_MEM "Cannot allocate memory for %s!"
} // End of namespace Tinsel
#endif // TINSEL_DW_H

139
engines/tinsel/effect.cpp Normal file
View File

@@ -0,0 +1,139 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
// Handles effect polygons.
//
// EffectPolyProcess() monitors triggering of effect code (i.e. a moving
// actor entering an effect polygon).
// EffectProcess() runs the appropriate effect code.
//
// NOTE: Currently will only run one effect process at a time, i.e.
// effect polygons will not currently nest. It won't be very difficult
// to fix this if required.
#include "tinsel/actors.h"
#include "tinsel/dw.h"
#include "tinsel/events.h"
#include "tinsel/pid.h"
#include "tinsel/pcode.h" // LEAD_ACTOR
#include "tinsel/polygons.h"
#include "tinsel/movers.h"
#include "tinsel/sched.h"
#include "tinsel/tinsel.h"
namespace Tinsel {
struct EP_INIT {
HPOLYGON hEpoly;
MOVER *pMover;
int index;
};
/**
* Runs an effect polygon's Glitter code with ENTER event, waits for the
* actor to leave that polygon. Then runs the polygon's Glitter code
* with LEAVE event.
*/
static void EffectProcess(CORO_PARAM, const void *param) {
// COROUTINE
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
const EP_INIT *to = (const EP_INIT *)param; // get the stuff copied to process when it was created
CORO_BEGIN_CODE(_ctx);
int x, y; // Lead actor position
// Run effect poly enter script
if (TinselVersion >= 2)
CORO_INVOKE_ARGS(PolygonEvent, (CORO_SUBCTX, to->hEpoly, WALKIN,
GetMoverId(to->pMover), false, 0));
else
effRunPolyTinselCode(to->hEpoly, WALKIN, to->pMover->actorID);
do {
CORO_SLEEP(1);
GetMoverPosition(to->pMover, &x, &y);
} while (InPolygon(x, y, EFFECT) == to->hEpoly);
// Run effect poly leave script
if (TinselVersion >= 2)
CORO_INVOKE_ARGS(PolygonEvent, (CORO_SUBCTX, to->hEpoly, WALKOUT,
GetMoverId(to->pMover), false, 0));
else
effRunPolyTinselCode(to->hEpoly, WALKOUT, to->pMover->actorID);
SetMoverInEffect(to->index, false);
CORO_END_CODE;
}
/**
* If the actor was not already in an effect polygon, checks to see if
* it has just entered one. If it has, a process is started up to run
* the polygon's Glitter code.
*/
static void FettleEffectPolys(int x, int y, int index, MOVER *pActor) {
HPOLYGON hPoly;
EP_INIT epi;
// If just entered an effect polygon, the effect should be triggered.
if (!IsMAinEffectPoly(index)) {
hPoly = InPolygon(x, y, EFFECT);
if (hPoly != NOPOLY) {
//Just entered effect polygon
SetMoverInEffect(index, true);
epi.hEpoly = hPoly;
epi.pMover = pActor;
epi.index = index;
CoroScheduler.createProcess(PID_TCODE, EffectProcess, &epi, sizeof(epi));
}
}
}
/**
* Just calls FettleEffectPolys() every clock tick.
*/
void EffectPolyProcess(CORO_PARAM, const void *param) {
// COROUTINE
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
while (1) {
for (int i = 0; i < MAX_MOVERS; i++) {
MOVER *pActor = GetLiveMover(i);
if (pActor != NULL) {
int x, y;
GetMoverPosition(pActor, &x, &y);
FettleEffectPolys(x, y, i, pActor);
}
}
CORO_SLEEP(1); // allow re-scheduling
}
CORO_END_CODE;
}
} // End of namespace Tinsel

726
engines/tinsel/events.cpp Normal file
View File

@@ -0,0 +1,726 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* Main purpose is to process user events.
* Also provides a couple of utility functions.
*/
#include "common/coroutines.h"
#include "tinsel/actors.h"
#include "tinsel/background.h"
#include "tinsel/config.h"
#include "tinsel/cursor.h"
#include "tinsel/dw.h"
#include "tinsel/events.h"
#include "tinsel/handle.h" // For LockMem()
#include "tinsel/dialogs.h"
#include "tinsel/move.h" // For walking lead actor
#include "tinsel/pcode.h" // For Interpret()
#include "tinsel/pdisplay.h"
#include "tinsel/pid.h"
#include "tinsel/polygons.h"
#include "tinsel/movers.h" // For walking lead actor
#include "tinsel/sched.h"
#include "tinsel/scroll.h" // For DontScrollCursor()
#include "tinsel/timers.h" // DwGetCurrentTime()
#include "tinsel/tinlib.h" // For control()
#include "tinsel/tinsel.h"
#include "tinsel/token.h"
#include "tinsel/noir/notebook.h"
#include "backends/keymapper/keymapper.h"
namespace Tinsel {
//----------------- EXTERNAL FUNCTIONS ---------------------
// in PDISPLAY.C
extern int GetTaggedActor();
extern HPOLYGON GetTaggedPoly();
//----------------- EXTERNAL GLOBAL DATA ---------------------
extern bool g_bEnableMenu;
//----------------- LOCAL GLOBAL DATA --------------------
// These vars are reset upon engine destruction
static uint32 g_lastUserEvent = 0; // Time it happened
static int g_leftEvents = 0; // Single or double, left or right. Or escape key.
static int g_escEvents = 1; // Escape key
static int g_userEvents = 0; // Whenever a button or a key comes in
static int g_eCount = 0;
static int g_controlState;
static bool g_bStartOff;
static int g_controlX, g_controlY;
static bool g_bProvNotProcessed = false;
static uint32 lastRealAction = 0;
void ResetVarsEvents() {
g_lastUserEvent = 0;
g_leftEvents = 0; // Single or double, left or right. Or escape key.
g_escEvents = 1; // Escape key
g_userEvents = 0; // Whenever a button or a key comes in
g_eCount = 0;
g_controlState = 0;
g_bStartOff = false;
g_controlX = 0;
g_controlY = 0;
g_bProvNotProcessed = false;
lastRealAction = 0;
}
/**
* Gets called before each schedule, only 1 user action per schedule
* is allowed.
*/
void ResetEcount() {
g_eCount = 0;
}
void IncUserEvents() {
g_userEvents++;
g_lastUserEvent = DwGetCurrentTime();
}
/**
* If this is a single click, wait to check it's not the first half of a
* double click.
* If this is a double click, the process from the waiting single click
* gets killed.
*/
void AllowDclick(CORO_PARAM, PLR_EVENT be) {
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
if (be == PLR_SLEFT) {
GetToken(TOKEN_LEFT_BUT);
CORO_SLEEP(_vm->_config->_dclickSpeed+1);
FreeToken(TOKEN_LEFT_BUT);
// Prevent activation of 2 events on the same tick
if (++g_eCount != 1)
CORO_KILL_SELF();
break;
} else if (be == PLR_DLEFT) {
GetToken(TOKEN_LEFT_BUT);
FreeToken(TOKEN_LEFT_BUT);
}
CORO_END_CODE;
}
/**
* Re-enables user control
*/
void ControlOn() {
if (TinselVersion <= 1) {
Control(CONTROL_ON);
return;
}
g_bEnableMenu = false;
if (g_controlState == CONTROL_OFF) {
// Control is on
g_controlState = CONTROL_ON;
// Restore cursor to where it was
if (g_bStartOff == true)
g_bStartOff = false;
else
_vm->_cursor->SetCursorXY(g_controlX, g_controlY);
// Re-instate cursor
_vm->_cursor->UnHideCursor();
// Turn tags back on
if (!_vm->_dialogs->inventoryOrNotebookActive())
EnableTags();
}
}
/**
* Takes control from the user
*/
void ControlOff() {
if (TinselVersion <= 1) {
Control(CONTROL_OFF);
return;
}
g_bEnableMenu = false;
if (g_controlState == CONTROL_ON) {
// Control is off
g_controlState = CONTROL_OFF;
// Store cursor position
_vm->_cursor->GetCursorXY(&g_controlX, &g_controlY, true);
// Blank out cursor
_vm->_cursor->DwHideCursor();
// Switch off tags
DisableTags();
}
}
/**
* Prevent tags and cursor re-appearing
*/
void ControlStartOff() {
if (TinselVersion <= 1) {
Control(CONTROL_STARTOFF);
return;
}
g_bEnableMenu = false;
// Control is off
g_controlState = CONTROL_OFF;
// Blank out cursor
_vm->_cursor->DwHideCursor();
// Switch off tags
DisableTags();
g_bStartOff = true;
}
/**
* Take control from player, if the player has it.
* Return TRUE if control taken, FALSE if not.
*/
bool GetControl(int param) {
if (TinselVersion >= 2)
return GetControl();
else if (TestToken(TOKEN_CONTROL)) {
Control(param);
return true;
} else
return false;
}
bool GetControl() {
if (g_controlState == CONTROL_ON) {
ControlOff();
return true;
} else
return false;
}
bool ControlIsOn() {
if (TinselVersion >= 2)
return (g_controlState == CONTROL_ON);
return TestToken(TOKEN_CONTROL);
}
//-----------------------------------------------------------------------
struct WP_INIT {
int x; // } Where to walk to
int y; // }
};
/**
* Perform a walk directly initiated by a click.
*/
static void WalkProcess(CORO_PARAM, const void *param) {
// COROUTINE
CORO_BEGIN_CONTEXT;
MOVER *pMover;
int thisWalk;
CORO_END_CONTEXT(_ctx);
const WP_INIT *to = (const WP_INIT *)param; // get the co-ordinates - copied to process when it was created
CORO_BEGIN_CODE(_ctx);
_ctx->pMover = GetMover(LEAD_ACTOR);
if ((TinselVersion >= 2) && MoverIs(_ctx->pMover) && !MoverIsSWalking(_ctx->pMover)) {
assert(_ctx->pMover->hCpath != NOPOLY); // Lead actor is not in a path
_ctx->thisWalk = SetActorDest(_ctx->pMover, to->x, to->y, false, 0);
_vm->_scroll->DontScrollCursor();
while (MoverMoving(_ctx->pMover) && (_ctx->thisWalk == GetWalkNumber(_ctx->pMover)))
CORO_SLEEP(1);
} else if ((TinselVersion <= 1) && _ctx->pMover->bActive) {
assert(_ctx->pMover->hCpath != NOPOLY); // Lead actor is not in a path
GetToken(TOKEN_LEAD);
SetActorDest(_ctx->pMover, to->x, to->y, false, 0);
_vm->_scroll->DontScrollCursor();
while (MoverMoving(_ctx->pMover))
CORO_SLEEP(1);
FreeToken(TOKEN_LEAD);
}
CORO_END_CODE;
}
void WalkTo(int x, int y) {
WP_INIT to = { x, y };
CoroScheduler.createProcess(PID_TCODE, WalkProcess, &to, sizeof(to));
}
/**
* Run appropriate actor or polygon glitter code.
* If none, and it's a WALKTO event, do a walk.
*/
static void ProcessUserEvent(TINSEL_EVENT uEvent, const Common::Point &coOrds, PLR_EVENT be = PLR_NOEVENT) {
int actor;
int aniX, aniY;
HPOLYGON hPoly;
// Prevent activation of 2 events on the same tick
if (++g_eCount != 1)
return;
if ((actor = GetTaggedActor()) != 0) {
// Event for a tagged actor
if (TinselVersion >= 2)
ActorEvent(Common::nullContext, actor, uEvent, false, 0);
else
ActorEvent(actor, uEvent, be);
} else if ((hPoly = GetTaggedPoly()) != NOPOLY) {
// Event for active tagged polygon
if (TinselVersion <= 1)
RunPolyTinselCode(hPoly, uEvent, be, false);
else if (uEvent != PROV_WALKTO)
PolygonEvent(Common::nullContext, hPoly, uEvent, 0, false, 0);
} else {
_vm->_cursor->GetCursorXY(&aniX, &aniY, true);
// There could be a poly involved which has no tag.
if ((hPoly = InPolygon(aniX, aniY, TAG)) != NOPOLY ||
((TinselVersion <= 1) && ((hPoly = InPolygon(aniX, aniY, EXIT)) != NOPOLY))) {
if ((TinselVersion >= 2) && (uEvent != PROV_WALKTO))
PolygonEvent(Common::nullContext, hPoly, uEvent, 0, false, 0);
else if (TinselVersion <= 1)
RunPolyTinselCode(hPoly, uEvent, be, false);
} else if ((uEvent == PROV_WALKTO) || (uEvent == WALKTO)) {
if (TinselVersion >= 2)
ProcessedProvisional();
WalkTo(aniX, aniY);
}
}
}
/**
* ProcessButEvent
*/
void ProcessButEvent(PLR_EVENT be) {
if (_vm->_config->_swapButtons) {
switch (be) {
case PLR_SLEFT:
be = PLR_SRIGHT;
break;
case PLR_DLEFT:
be = PLR_DRIGHT;
break;
case PLR_SRIGHT:
be = PLR_SLEFT;
break;
case PLR_DRIGHT:
be = PLR_DLEFT;
break;
case PLR_DRAG1_START:
be = PLR_DRAG2_START;
break;
case PLR_DRAG1_END:
be = PLR_DRAG2_END;
break;
case PLR_DRAG2_START:
be = PLR_DRAG1_START;
break;
case PLR_DRAG2_END:
be = PLR_DRAG1_END;
break;
default:
break;
}
}
PlayerEvent(be, _vm->getMousePosition());
}
/**
* ProcessKeyEvent
*/
void ProcessKeyEvent(PLR_EVENT ke) {
// Pass the keyboard event to the player event handler
int xp, yp;
_vm->_cursor->GetCursorXYNoWait(&xp, &yp, true);
const Common::Point mousePos(xp, yp);
PlayerEvent(ke, mousePos);
}
#define REAL_ACTION_CHECK if (TinselVersion >= 2) { \
if (DwGetCurrentTime() - lastRealAction < 4) return; \
lastRealAction = DwGetCurrentTime(); \
}
void CloseOpenInventories() {
if (_vm->_notebook->isOpen()) {
_vm->_notebook->close();
} else {
if (_vm->_dialogs->inventoryActive()) {
if (_vm->_dialogs->whichInventoryOpen() != INV_3) {
_vm->_dialogs->killInventory();
}
}
}
}
/**
* Main interface point for specifying player atcions
*/
void PlayerEvent(PLR_EVENT pEvent, const Common::Point &coOrds) {
// Logging of player actions
const char *actionList[] = {
"PLR_PROV_WALKTO", "PLR_WALKTO", "PLR_LOOK", "PLR_ACTION", "PLR_ESCAPE",
"PLR_MENU", "PLR_QUIT", "PLR_PGUP", "PLR_PGDN", "PLR_HOME", "PLR_END",
"PLR_DRAG1_START", "PLR_DRAG1_END", "PLR_DRAG2_START", "PLR_DRAG2_END",
"PLR_JUMP", "PLR_NOEVENT", "PLR_SAVE", "PLR_LOAD", "PLR_WHEEL_UP",
"PLR_WHEEL_DOWN", "PLR_INVENTORY", "PLR_NOTEBOOK" };
debugC(DEBUG_BASIC, kTinselDebugActions, "%s - (%d,%d)",
actionList[pEvent], coOrds.x, coOrds.y);
// This stuff to allow F1 key during startup.
if (g_bEnableMenu && pEvent == PLR_MENU)
Control(CONTROL_ON);
else
IncUserEvents();
if (pEvent == PLR_ESCAPE) {
++g_escEvents;
++g_leftEvents; // Yes, I do mean this
} else if ((pEvent == PLR_PROV_WALKTO)
|| (pEvent == PLR_WALKTO)
|| (pEvent == PLR_LOOK)
|| (pEvent == PLR_ACTION)) {
++g_leftEvents;
}
// Only allow events if player control is on
if (!ControlIsOn() && (pEvent != PLR_DRAG1_END))
return;
if ((TinselVersion >= 2) && _vm->_dialogs->inventoryOrNotebookActive()) {
int x, y;
_vm->_bg->PlayfieldGetPos(FIELD_WORLD, &x, &y);
_vm->_dialogs->eventToInventory(pEvent, Common::Point(coOrds.x - x, coOrds.y - y));
return;
}
switch (pEvent) {
case PLR_QUIT:
_vm->_dialogs->openMenu(QUIT_MENU);
break;
case PLR_MENU:
if (TinselVersion == 3) {
CloseOpenInventories();
}
_vm->_dialogs->openMenu(MAIN_MENU);
break;
case PLR_INVENTORY:
if (TinselVersion == 3) {
CloseOpenInventories();
_vm->_dialogs->popUpInventory(INV_1);
}
break;
case PLR_NOTEBOOK:
if (TinselVersion == 3) {
CloseOpenInventories();
_vm->_notebook->show(false);
}
break;
case PLR_JUMP:
_vm->_dialogs->openMenu(HOPPER_MENU1);
break;
case PLR_SAVE:
if (TinselVersion == 3) {
CloseOpenInventories();
}
_vm->_dialogs->openMenu(SAVE_MENU);
break;
case PLR_LOAD:
if (TinselVersion == 3) {
CloseOpenInventories();
}
_vm->_dialogs->openMenu(LOAD_MENU);
break;
case PLR_PROV_WALKTO: // Provisional WALKTO !
ProcessUserEvent(PROV_WALKTO, coOrds);
break;
case PLR_WALKTO:
REAL_ACTION_CHECK;
if ((TinselVersion >= 2) || !_vm->_dialogs->inventoryActive())
ProcessUserEvent(WALKTO, coOrds, PLR_SLEFT);
else
_vm->_dialogs->eventToInventory(PLR_SLEFT, coOrds);
break;
case PLR_ACTION:
REAL_ACTION_CHECK;
if ((TinselVersion >= 2) || !_vm->_dialogs->inventoryActive())
ProcessUserEvent(ACTION, coOrds, PLR_DLEFT);
else
_vm->_dialogs->eventToInventory(PLR_DLEFT, coOrds);
break;
case PLR_LOOK:
REAL_ACTION_CHECK;
if ((TinselVersion >= 2) || !_vm->_dialogs->inventoryActive())
ProcessUserEvent(LOOK, coOrds, PLR_SRIGHT);
else
_vm->_dialogs->eventToInventory(PLR_SRIGHT, coOrds);
break;
default:
if (_vm->_dialogs->inventoryActive())
_vm->_dialogs->eventToInventory(pEvent, coOrds);
break;
}
}
/**
* For ESCapable Glitter sequences
*/
int GetEscEvents() {
return g_escEvents;
}
/**
* For cutting short talk()s etc.
*/
int GetLeftEvents() {
return g_leftEvents;
}
bool LeftEventChange(int myleftEvent) {
if (g_leftEvents != myleftEvent) {
ProcessedProvisional();
return true;
} else
return false;
}
/**
* For waitkey() Glitter function
*/
int getUserEvents() {
return g_userEvents;
}
uint32 getUserEventTime() {
return DwGetCurrentTime() - g_lastUserEvent;
}
void resetUserEventTime() {
g_lastUserEvent = DwGetCurrentTime();
}
struct PTP_INIT {
HPOLYGON hPoly; // Polygon
TINSEL_EVENT event; // Trigerring event
PLR_EVENT bev; // To allow for double clicks
bool take_control; // Set if control should be taken while code is running.
int actor;
INT_CONTEXT *pic;
};
/**
* Runs glitter code associated with a polygon.
*/
void PolyTinselProcess(CORO_PARAM, const void *param) {
// COROUTINE
CORO_BEGIN_CONTEXT;
INT_CONTEXT *pic;
bool bTookControl; // Set if this function takes control
CORO_END_CONTEXT(_ctx);
const PTP_INIT *to = (const PTP_INIT *)param; // get the stuff copied to process when it was created
CORO_BEGIN_CODE(_ctx);
if (TinselVersion >= 2) {
// Take control for CONVERSE events
if (to->event == CONVERSE) {
_ctx->bTookControl = GetControl();
_vm->_dialogs->hideConversation(true);
} else
_ctx->bTookControl = false;
CORO_INVOKE_1(Interpret, to->pic);
// Restore conv window if applicable
if (to->event == CONVERSE) {
// Free control if we took it
if (_ctx->bTookControl)
ControlOn();
_vm->_dialogs->hideConversation(false);
}
} else {
CORO_INVOKE_1(AllowDclick, to->bev); // May kill us if single click
// Control may have gone off during AllowDclick()
if (!TestToken(TOKEN_CONTROL)
&& (to->event == WALKTO || to->event == ACTION || to->event == LOOK))
CORO_KILL_SELF();
// Take control, if requested
if (to->take_control)
_ctx->bTookControl = GetControl(CONTROL_OFF);
else
_ctx->bTookControl = false;
// Hide conversation if appropriate
if (to->event == CONVERSE)
_vm->_dialogs->hideConversation(true);
// Run the code
_ctx->pic = InitInterpretContext(GS_POLYGON, GetPolyScript(to->hPoly), to->event, to->hPoly, to->actor, NULL);
CORO_INVOKE_1(Interpret, _ctx->pic);
// Free control if we took it
if (_ctx->bTookControl)
Control(CONTROL_ON);
// Restore conv window if applicable
if (to->event == CONVERSE)
_vm->_dialogs->hideConversation(false);
}
CORO_END_CODE;
}
/**
* Run the Polygon process with the given event
*/
void PolygonEvent(CORO_PARAM, HPOLYGON hPoly, TINSEL_EVENT tEvent, int actor, bool bWait,
int myEscape, bool *result) {
CORO_BEGIN_CONTEXT;
Common::PPROCESS pProc;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
PTP_INIT to;
if (result)
*result = false;
to.hPoly = -1;
to.event = tEvent;
to.pic = InitInterpretContext(GS_POLYGON,
GetPolyScript(hPoly),
tEvent,
hPoly, // Polygon
actor, // Actor
NULL, // No Object
myEscape);
if (to.pic != NULL) {
_ctx->pProc = CoroScheduler.createProcess(PID_TCODE, PolyTinselProcess, &to, sizeof(to));
AttachInterpret(to.pic, _ctx->pProc);
if (bWait)
CORO_INVOKE_2(WaitInterpret, _ctx->pProc, result);
}
CORO_END_CODE;
}
/**
* Runs glitter code associated with a polygon.
*/
void RunPolyTinselCode(HPOLYGON hPoly, TINSEL_EVENT event, PLR_EVENT be, bool tc) {
PTP_INIT to = { hPoly, event, be, tc, 0, NULL };
assert(TinselVersion <= 1);
CoroScheduler.createProcess(PID_TCODE, PolyTinselProcess, &to, sizeof(to));
}
void effRunPolyTinselCode(HPOLYGON hPoly, TINSEL_EVENT event, int actor) {
PTP_INIT to = { hPoly, event, PLR_NOEVENT, false, actor, NULL };
assert(TinselVersion <= 1);
CoroScheduler.createProcess(PID_TCODE, PolyTinselProcess, &to, sizeof(to));
}
/**
* If provisional event was processed, calling this prevents the
* subsequent 'real' event.
*/
void ProcessedProvisional() {
g_bProvNotProcessed = false;
}
/**
* Resets the bProvNotProcessed flag
*/
void ProvNotProcessed() {
g_bProvNotProcessed = true;
}
bool GetProvNotProcessed() {
return g_bProvNotProcessed;
}
} // End of namespace Tinsel

146
engines/tinsel/events.h Normal file
View File

@@ -0,0 +1,146 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* User events processing and utility functions
*/
#ifndef TINSEL_EVENTS_H
#define TINSEL_EVENTS_H
#include "common/coroutines.h"
#include "common/rect.h"
#include "tinsel/dw.h"
namespace Tinsel {
/*
enum BUTEVENT {
PLR_NOEVENT, PLR_SLEFT, PLR_DLEFT, PLR_SRIGHT, PLR_DRIGHT,
PLR_DRAG1_START, PLR_DRAG1_END, PLR_DRAG2_START, PLR_DRAG2_END,
PLR_UNKNOWN
};
enum KEYEVENT {
PLR_ESCAPE, PLR_QUIT, PLR_SAVE, PLR_LOAD, PLR_MENU,
PLR_PGUP, PLR_PGDN, PLR_HOME, PLR_END,
PLR_WALKTO, PLR_ACTION, PLR_LOOK,
NOEVENT_KEY
};*/
enum PLR_EVENT {
// action list
PLR_PROV_WALKTO = 0, // Provisional WALKTO !
PLR_WALKTO = 1,
PLR_LOOK = 2,
PLR_ACTION = 3,
PLR_ESCAPE = 4,
PLR_MENU = 5,
PLR_QUIT = 6,
PLR_PGUP = 7,
PLR_PGDN = 8,
PLR_HOME = 9,
PLR_END = 10,
PLR_DRAG1_START = 11,
PLR_DRAG1_END = 12,
PLR_DRAG2_START = 13,
PLR_DRAG2_END = 14,
PLR_JUMP = 15, // Call up scene hopper
PLR_NOEVENT = 16,
PLR_SAVE = 17,
PLR_LOAD = 18,
PLR_WHEEL_UP = 19,
PLR_WHEEL_DOWN = 20,
PLR_INVENTORY = 21,
PLR_NOTEBOOK = 22,
// Aliases used for DW1 actions
PLR_SLEFT = PLR_WALKTO,
PLR_DLEFT = PLR_ACTION,
PLR_SRIGHT = PLR_LOOK,
PLR_DRIGHT = PLR_NOEVENT,
PLR_UNKNOWN = PLR_NOEVENT
};
/**
* Reasons for running Glitter code.
* Do not re-order these as equivalent CONSTs are defined in the master
* scene Glitter source file for testing against the event() library function.
*
* Note: DW2 renames ENTER & LEAVE to WALKIN & WALKOUT, and has a new LEAVE event
*/
// ': int' because out-of-range values happen in DW2 and we do enum casts (PVS-Studio V1016)
enum TINSEL_EVENT : int {
NOEVENT, STARTUP, CLOSEDOWN, POINTED, UNPOINT, WALKIN, WALKOUT,
PICKUP, PUTDOWN, WALKTO, LOOK, ACTION, CONVERSE, SHOWEVENT,
HIDEEVENT, TALKING, ENDTALK, LEAVE_T2, RESTORE, PROV_WALKTO
};
enum TINSEL1_EVENT {
T1_POINTED, T1_WALKTO, T1_ACTION, T1_LOOK, T1_ENTER, T1_LEAVE, T1_STARTUP, T1_CONVERSE,
T1_UNPOINT, T1_PUTDOWN, T1_NOEVENT
};
const TINSEL1_EVENT TINSEL1_EVENT_MAP[] = {
T1_NOEVENT, T1_STARTUP, T1_NOEVENT, T1_POINTED, T1_UNPOINT, T1_ENTER, T1_LEAVE,
T1_NOEVENT, T1_PUTDOWN, T1_WALKTO, T1_LOOK, T1_ACTION, T1_CONVERSE, T1_NOEVENT,
T1_NOEVENT, T1_NOEVENT, T1_NOEVENT, T1_NOEVENT, T1_NOEVENT, T1_NOEVENT
};
void AllowDclick(CORO_PARAM, PLR_EVENT be);
bool GetControl(int param);
bool GetControl();
bool ControlIsOn();
void ControlOn();
void ControlOff();
void ControlStartOff();
void RunPolyTinselCode(HPOLYGON hPoly, TINSEL_EVENT event, PLR_EVENT be, bool tc);
void effRunPolyTinselCode(HPOLYGON hPoly, TINSEL_EVENT event, int actor);
void ProcessButEvent(PLR_EVENT be);
void ProcessKeyEvent(PLR_EVENT ke);
int GetEscEvents();
int GetLeftEvents();
bool LeftEventChange(int myleftEvent);
int getUserEvents();
uint32 getUserEventTime();
void resetUserEventTime();
void ResetEcount();
void PolygonEvent(CORO_PARAM, HPOLYGON hPoly, TINSEL_EVENT tEvent, int actor, bool bWait,
int myEscape, bool *result = NULL);
void PlayerEvent(PLR_EVENT pEvent, const Common::Point &coOrds);
void ProcessedProvisional();
void ProvNotProcessed();
bool GetProvNotProcessed();
} // End of namespace Tinsel
#endif /* TINSEL_EVENTS_H */

221
engines/tinsel/faders.cpp Normal file
View File

@@ -0,0 +1,221 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* Palette Fader and Flasher processes.
*/
#include "tinsel/actors.h"
#include "tinsel/faders.h" // fader defs
#include "tinsel/handle.h"
#include "tinsel/palette.h" // Palette Manager defs
#include "tinsel/pid.h" // list of all process IDs
#include "tinsel/sched.h" // scheduler defs
#include "tinsel/sysvar.h"
#include "tinsel/tinsel.h"
namespace Tinsel {
/** structure used by the "FadeProcess" process */
struct FADE {
const long *pColorMultTable; // list of fixed point color multipliers - terminated with negative entry
PALQ *pPalQ; // palette queue entry to fade
};
// fixed point fade multiplier tables
//const long fadeout[] = {0xf000, 0xd000, 0xb000, 0x9000, 0x7000, 0x5000, 0x3000, 0x1000, 0, -1};
//const long fadein[] = {0, 0x1000, 0x3000, 0x5000, 0x7000, 0x9000, 0xb000, 0xd000, 0x10000L, -1};
/**
* Scale 'color' by the fixed point color multiplier 'colorMult'
* @param color Color to scale
* @param colorMult Fixed point multiplier
*/
static COLORREF ScaleColor(COLORREF color, uint32 colorMult) {
// apply multiplier to RGB components
byte r = (byte)(color & 0xFF);
byte g = (byte)((color >> 8) & 0xFF);
byte b = (byte)((color >> 16) & 0xFF);
uint32 red = ((r * colorMult) << 8) >> 24;
uint32 green = ((g * colorMult) << 8) >> 24;
uint32 blue = ((b * colorMult) << 8) >> 24;
// return new color
return TINSEL_RGB(red, green, blue);
}
/**
* Applies the fixed point multiplier 'mult' to all colors in
* 'pOrig' to produce 'pNew'. Each color in the palette will be
* multiplied by 'mult'.
* @param pNew Pointer to new palette
* @param pOrig Pointer to original palette
* @param numColors Number of colors in the above palettes
* @param mult Fixed point multiplier
*/
static void FadePalette(COLORREF *pNew, COLORREF *pOrig, int numColors, uint32 mult) {
for (int i = 0; i < numColors; i++, pNew++, pOrig++) {
if (TinselVersion <= 1)
// apply multiplier to RGB components
*pNew = ScaleColor(*pOrig, mult);
else if (i == (TalkColor() - 1)) {
*pNew = GetTalkColorRef();
*pNew = ScaleColor(*pNew, mult);
} else if (SysVar(SV_TAGCOLOR) && i == (SysVar(SV_TAGCOLOR) - 1)) {
*pNew = GetTagColorRef();
*pNew = ScaleColor(*pNew, mult);
} else {
*pNew = ScaleColor(*pOrig, mult);
}
}
}
/**
* Process to fade one palette.
* A pointer to a 'FADE' structure must be passed to this process when
* it is created.
*/
static void FadeProcess(CORO_PARAM, const void *param) {
// COROUTINE
CORO_BEGIN_CONTEXT;
COLORREF fadeRGB[MAX_COLORS]; // local copy of palette
const long *pColMult; // pointer to color multiplier table
PALETTE *pPalette; // pointer to palette
CORO_END_CONTEXT(_ctx);
// get the fade data structure - copied to process when it was created
const FADE *pFade = (const FADE *)param;
CORO_BEGIN_CODE(_ctx);
if (TinselVersion >= 2)
// Note that this palette is being faded
FadingPalette(pFade->pPalQ, true);
// get pointer to palette - reduce pointer indirection a bit
_ctx->pPalette = _vm->_handle->GetPalette(pFade->pPalQ->hPal);
for (_ctx->pColMult = pFade->pColorMultTable; *_ctx->pColMult >= 0; _ctx->pColMult++) {
// go through all multipliers in table - until a negative entry
// fade palette using next multiplier
if (TinselVersion >= 2)
FadePalette(_ctx->fadeRGB, pFade->pPalQ->palRGB,
pFade->pPalQ->numColors, (uint32) *_ctx->pColMult);
else
FadePalette(_ctx->fadeRGB, _ctx->pPalette->palRGB,
_ctx->pPalette->numColors, (uint32) *_ctx->pColMult);
// send new palette to video DAC
UpdateDACqueue(pFade->pPalQ->posInDAC, _ctx->pPalette->numColors, _ctx->fadeRGB);
// allow time for video DAC to be updated
CORO_SLEEP(1);
}
if (TinselVersion >= 2)
// Note that this palette is being faded
FadingPalette(pFade->pPalQ, false);
delete _ctx->pPalette;
CORO_END_CODE;
}
/**
* Generic palette fader/unfader. Creates a 'FadeProcess' process
* for each palette that is to fade.
* @param multTable Fixed point color multiplier table
*/
static void Fader(const long multTable[]) {
PALQ *pPal; // palette manager iterator
if (TinselVersion >= 2) {
// The is only ever one cuncurrent fade
// But this could be a fade out and the fade in is still going!
CoroScheduler.killMatchingProcess(PID_FADER);
NoFadingPalettes();
}
// create a process for each palette in the palette queue
for (pPal = GetNextPalette(NULL); pPal != NULL; pPal = GetNextPalette(pPal)) {
FADE fade;
// fill in FADE struct
fade.pColorMultTable = multTable;
fade.pPalQ = pPal;
// create a fader process for this palette
CoroScheduler.createProcess(PID_FADER, FadeProcess, (void *)&fade, sizeof(FADE));
}
}
/**
* Fades a list of palettes down to black.
*/
void FadeOutMedium() {
// Fixed point fade multiplier table
static const long fadeout[] = {0xea00, 0xd000, 0xb600, 0x9c00,
0x8200, 0x6800, 0x4e00, 0x3400, 0x1a00, 0, -1};
// call generic fader
Fader(fadeout);
}
/**
* Fades a list of palettes down to black.
*/
void FadeOutFast() {
// Fixed point fade multiplier table
static const long fadeout[] = {0xd000, 0xa000, 0x7000, 0x4000, 0x1000, 0, -1};
// call generic fader
Fader(fadeout);
}
/**
* Fades a list of palettes from black to their current colors.
*/
void FadeInMedium() {
// Fade multiplier table
static const long fadein[] = {0, 0x1a00, 0x3400, 0x4e00, 0x6800,
0x8200, 0x9c00, 0xb600, 0xd000, 0xea00, 0x10000L, -1};
// call generic fader
Fader(fadein);
}
/**
* Fades a list of palettes from black to their current colors.
*/
void FadeInFast() {
// Fade multiplier table
static const long fadein[] = {0, 0x1000, 0x4000, 0x7000, 0xa000, 0xd000, 0x10000L, -1};
// call generic fader
Fader(fadein);
}
void PokeInTagColor() {
if (SysVar(SV_TAGCOLOR)) {
const COLORREF c = _vm->_actor->GetActorRGB(-1);
UpdateDACqueue(SysVar(SV_TAGCOLOR), c);
}
}
} // End of namespace Tinsel

49
engines/tinsel/faders.h Normal file
View File

@@ -0,0 +1,49 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* Data structures used by the fader and flasher processes
*/
#ifndef TINSEL_FADERS_H // prevent multiple includes
#define TINSEL_FADERS_H
#include "tinsel/dw.h" // for SCNHANDLE
#include "tinsel/tinsel.h"
namespace Tinsel {
/**
* Number of iterations in a fade out.
*/
// FIXME: There seems to be some confusion in Tinsel 2 whether this should be 9 or 6
#define COUNTOUT_COUNT 6
/*----------------------------------------------------------------------*\
|* Fader Function Prototypes *|
\*----------------------------------------------------------------------*/
void FadeOutMedium();
void FadeOutFast();
void FadeInMedium();
void FadeInFast();
void PokeInTagColor();
} // End of namespace Tinsel
#endif // TINSEL_FADERS_H

36
engines/tinsel/film.cpp Normal file
View File

@@ -0,0 +1,36 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "tinsel/film.h"
#include "tinsel/handle.h"
#include "tinsel/tinsel.h"
namespace Tinsel {
MULTI_INIT *FREEL::GetMultiInit() {
return (MULTI_INIT *)_vm->_handle->LockMem(FROM_32(mobj));
}
const MULTI_INIT *FREEL::GetMultiInit() const {
return (const MULTI_INIT *)_vm->_handle->LockMem(FROM_32(mobj));
}
} // End of namespace Tinsel

50
engines/tinsel/film.h Normal file
View File

@@ -0,0 +1,50 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef TINSEL_FILM_H // prevent multiple includes
#define TINSEL_FILM_H
#include "tinsel/dw.h"
namespace Tinsel {
#include "common/pack-start.h" // START STRUCT PACKING
struct MULTI_INIT;
struct FREEL {
SCNHANDLE mobj;
SCNHANDLE script;
MULTI_INIT *GetMultiInit();
const MULTI_INIT *GetMultiInit() const;
} PACKED_STRUCT;
struct FILM {
int32 frate;
int32 numreels;
FREEL reels[1];
} PACKED_STRUCT;
#include "common/pack-end.h" // END STRUCT PACKING
} // End of namespace Tinsel
#endif

61
engines/tinsel/font.cpp Normal file
View File

@@ -0,0 +1,61 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include "tinsel/actors.h"
#include "tinsel/dw.h"
#include "tinsel/font.h"
#include "tinsel/handle.h"
#include "tinsel/object.h"
#include "tinsel/sysvar.h"
#include "tinsel/text.h"
#include "tinsel/tinsel.h"
namespace Tinsel {
void Font::SetTagFontHandle(SCNHANDLE hFont) {
_hTagFont = _hRegularTagFont = hFont;
if (TinselVersion == 0)
SetTalkFontHandle(hFont); // Also re-use for talk text
}
SCNHANDLE Font::GetTalkFontHandle() {
// FIXME: Mac Discworld 1 was rendering the talk font as black.
// Currently hacking around it by simply using the tooltip font
return TinselV1Mac ? _hTagFont : _hTalkFont;
}
void Font::FettleFontPal(SCNHANDLE fontPal) {
Handle *h = _vm->_handle;
assert(fontPal);
assert(_hTagFont); // Tag font not declared
assert(_hTalkFont); // Talk font not declared
h->SetImagePalette(h->GetFontImageHandle(_hTagFont), (TinselVersion <= 1) ? fontPal : 0); // get image for char 0
h->SetImagePalette(h->GetFontImageHandle(_hTalkFont), (TinselVersion <= 1) ? fontPal : 0); // get image for char 0
if ((TinselVersion >= 2) && SysVar(SV_TAGCOLOR)) {
const COLORREF c = _vm->_actor->GetActorRGB(-1);
SetTagColorRef(c);
UpdateDACqueue(SysVar(SV_TAGCOLOR), c);
}
}
} // End of namespace Tinsel

99
engines/tinsel/font.h Normal file
View File

@@ -0,0 +1,99 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef TINSEL_FONT_H // prevent multiple includes
#define TINSEL_FONT_H
#include "tinsel/dw.h"
namespace Tinsel {
// A temporary buffer for extracting text into is defined in font.c
// Accessed using TextBufferAddr(), this is how big it is:
#define TBUFSZ 512
class Font {
public:
Font() : _hTagFont(0), _hTalkFont(0), _hRegularTalkFont(0), _hRegularTagFont(0) {
}
/**
* Return address of tBuffer
*/
char* TextBufferAddr() { return _tBuffer; }
/**
* Return hTagFont handle.
*/
SCNHANDLE GetTagFontHandle() { return _hTagFont; }
/**
* Return hTalkFont handle.
*/
SCNHANDLE GetTalkFontHandle();
/**
* Called from dec_tagfont() Glitter function. Store the tag font handle.
*/
void SetTagFontHandle(SCNHANDLE hFont);
/**
* Called from dec_talkfont() Glitter function.
* Store the talk font handle.
*/
void SetTalkFontHandle(SCNHANDLE hFont) {
_hTalkFont = _hRegularTalkFont = hFont;
}
/**
* Declare a temporary text font (DW2 only).
*/
void SetTempTagFontHandle(SCNHANDLE hFont) {
_hTagFont = hFont;
}
/**
* Declare a temporary text font (DW2 only).
*/
void SetTempTalkFontHandle(SCNHANDLE hFont) {
_hTalkFont = hFont;
}
void ResetFontHandles() {
_hTagFont = _hRegularTagFont;
_hTalkFont = _hRegularTalkFont;
}
/**
* Poke the background palette into character 0's images.
*/
void FettleFontPal(SCNHANDLE fontPal);
private:
char _tBuffer[TBUFSZ];
SCNHANDLE _hTagFont, _hTalkFont;
SCNHANDLE _hRegularTalkFont, _hRegularTagFont;
};
} // End of namespace Tinsel
#endif // TINSEL_FONT_H

1272
engines/tinsel/graphics.cpp Normal file

File diff suppressed because it is too large Load Diff

72
engines/tinsel/graphics.h Normal file
View File

@@ -0,0 +1,72 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* Low level graphics interface.
*/
#ifndef TINSEL_GRAPHICS_H // prevent multiple includes
#define TINSEL_GRAPHICS_H
#include "tinsel/dw.h"
#include "tinsel/text.h"
#include "common/rect.h"
#include "common/system.h"
#include "graphics/surface.h"
namespace Tinsel {
struct PALQ;
/** draw object structure - only used when drawing objects */
struct DRAWOBJECT {
char *charBase; // character set base address
int transOffset; // transparent character offset
int flags; // object flags - see above for list
PALQ *pPal; // objects palette Q position
short isRLE; // TinselVersion == 3, if image is using run-length encoding
short colorFlags; // TinselV3, type of color blending
int constant; // which color in palette for monochrome objects
int width; // width of object
int height; // height of object
SCNHANDLE hBits; // image bitmap handle
int lineoffset; // offset to next line
int leftClip; // amount to clip off object left
int rightClip; // amount to clip off object right
int topClip; // amount to clip off object top
int botClip; // amount to clip off object bottom
short xPos; // x position of object
short yPos; // y position of object
uint32 baseCol; // For 4-bit stuff
};
/*----------------------------------------------------------------------*\
|* Function Prototypes *|
\*----------------------------------------------------------------------*/
void ClearScreen();
void DrawObject(DRAWOBJECT *pObj);
// called to update a rectangle on the video screen from a video page
void UpdateScreenRect(const Common::Rect &pClip);
} // End of namespace Tinsel
#endif

659
engines/tinsel/handle.cpp Normal file
View File

@@ -0,0 +1,659 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* This file contains the handle based Memory Manager code
*/
#define BODGE
#include "common/file.h"
#include "common/memstream.h"
#include "common/textconsole.h"
#include "common/str.h"
#include "tinsel/actors.h"
#include "tinsel/background.h"
#include "tinsel/drives.h"
#include "tinsel/dw.h"
#include "tinsel/handle.h"
#include "tinsel/heapmem.h" // heap memory manager
#include "tinsel/palette.h"
#include "tinsel/sched.h"
#include "tinsel/timers.h" // for DwGetCurrentTime()
#include "tinsel/tinsel.h"
#include "tinsel/scene.h"
#include "tinsel/noir/lzss.h"
namespace Tinsel {
//----------------- LOCAL DEFINES --------------------
struct MEMHANDLE {
char szName[12]; ///< file name of graphics file
int32 filesize; ///< file size and flags
MEM_NODE *_node; ///< memory node for the graphics
uint32 flags2;
};
/** memory allocation flags - stored in the top bits of the filesize field */
enum {
fPreload = 0x01000000L, ///< preload memory
fDiscard = 0x02000000L, ///< discard memory
fSound = 0x04000000L, ///< sound data
fGraphic = 0x08000000L, ///< graphic data
fCompressed = 0x10000000L, ///< compressed data
fLoaded = 0x20000000L, ///< set when file data has been loaded
fUnknown = 0x40000000L ///< v3 specific
};
#define FSIZE_MASK ((TinselVersion == 3) ? 0xFFFFFFFFL : 0x00FFFFFFL) //!< mask to isolate the filesize
#define MEMFLAGS(x) ((TinselVersion == 3) ? x->flags2 : x->filesize)
#define MEMFLAGSET(x, mask) ((TinselVersion == 3) ? x->flags2 |= mask : x->filesize |= mask)
Handle::Handle() : _handleTable(0), _numHandles(0), _cdPlayHandle((uint32)-1), _cdBaseHandle(0), _cdTopHandle(0), _cdGraphStream(nullptr) {
}
Handle::~Handle() {
free(_handleTable);
_handleTable = nullptr;
delete _cdGraphStream;
_cdGraphStream = nullptr;
}
/**
* Loads the graphics handle table index file and preloads all the
* permanent graphics etc.
*/
void Handle::SetupHandleTable() {
bool t2Flag = TinselVersion >= 2;
int RECORD_SIZE = t2Flag ? 24 : 20;
int len;
uint i;
MEMHANDLE *pH;
TinselFile f(TinselV1Mac || TinselV1Saturn);
Common::String indexFileName = TinselV1PSX ? PSX_INDEX_FILENAME : INDEX_FILENAME;
if (Common::File::exists("index_"))
indexFileName = "index_";
if (f.open(indexFileName)) {
// get size of index file
len = f.size();
if (len > 0) {
if ((len % RECORD_SIZE) != 0) {
// index file is corrupt
error(FILE_IS_CORRUPT, indexFileName.c_str());
}
// calc number of handles
_numHandles = len / RECORD_SIZE;
// allocate memory for the index file
_handleTable = (MEMHANDLE *)calloc(_numHandles, sizeof(struct MEMHANDLE));
// make sure memory allocated
assert(_handleTable);
// load data
for (i = 0; i < _numHandles; i++) {
f.read(_handleTable[i].szName, 12);
_handleTable[i].filesize = f.readUint32();
// The pointer should always be NULL. We don't
// need to read that from the file.
_handleTable[i]._node= nullptr;
f.seek(4, SEEK_CUR);
// For Discworld 2, read in the flags2 field
_handleTable[i].flags2 = t2Flag ? f.readUint32() : 0;
}
if (f.eos() || f.err()) {
// index file is corrupt
error(FILE_IS_CORRUPT, indexFileName.c_str());
}
// close the file
f.close();
} else { // index file is corrupt
error(FILE_IS_CORRUPT, indexFileName.c_str());
}
} else { // cannot find the index file
error(CANNOT_FIND_FILE, indexFileName.c_str());
}
// allocate memory nodes and load all permanent graphics
for (i = 0, pH = _handleTable; i < _numHandles; i++, pH++) {
if (MEMFLAGS(pH) & fPreload) {
// allocate a fixed memory node for permanent files
pH->_node = MemoryAllocFixed((pH->filesize & FSIZE_MASK));
// make sure memory allocated
assert(pH->_node);
// load the data
LoadFile(pH);
}
#ifdef BODGE
else if ((pH->filesize & FSIZE_MASK) == 8) {
pH->_node= nullptr;
}
#endif
else {
// allocate a discarded memory node for other files
pH->_node = MemoryNoAlloc();
// make sure memory allocated
assert(pH->_node);
}
}
}
/**
* Loads a memory block as a file.
*/
void Handle::OpenCDGraphFile() {
delete _cdGraphStream;
// As the theory goes, the right CD will be in there!
_cdGraphStream = new Common::File;
if (!_cdGraphStream->open(_szCdPlayFile))
error(CANNOT_FIND_FILE, _szCdPlayFile.toString().c_str());
}
void Handle::LoadCDGraphData(MEMHANDLE *pH) {
// read the data
uint bytes;
byte *addr;
int retries = 0;
assert(!(pH->filesize & fCompressed));
// Can't be preloaded
assert(!(MEMFLAGS(pH) & fPreload));
// discardable - lock the memory
addr = (byte *)MemoryLock(pH->_node);
// make sure address is valid
assert(addr);
// Move to correct place in file and load the required data
assert(_cdGraphStream);
_cdGraphStream->seek(_cdBaseHandle & OFFSETMASK, SEEK_SET);
bytes = _cdGraphStream->read(addr, (_cdTopHandle - _cdBaseHandle) & OFFSETMASK);
// New code to try and handle CD read failures 24/2/97
while (bytes != ((_cdTopHandle - _cdBaseHandle) & OFFSETMASK) && retries++ < MAX_READ_RETRIES) {
// Try again
_cdGraphStream->seek(_cdBaseHandle & OFFSETMASK, SEEK_SET);
bytes = _cdGraphStream->read(addr, (_cdTopHandle - _cdBaseHandle) & OFFSETMASK);
}
// discardable - unlock the memory
MemoryUnlock(pH->_node);
// set the loaded flag
MEMFLAGSET(pH, fLoaded);
// clear the loading flag
// pH->filesize &= ~fLoading;
if (bytes != ((_cdTopHandle - _cdBaseHandle) & OFFSETMASK))
// file is corrupt
error(FILE_READ_ERROR, "CD play file");
}
/**
* Called immediately preceding a CDplay().
* Prepares the ground so that when LockMem() is called, the
* appropriate section of the extra scene file is loaded.
* @param start Handle of start of range
* @param next Handle of end of range + 1
*/
void Handle::LoadExtraGraphData(SCNHANDLE start, SCNHANDLE next) {
OpenCDGraphFile();
MemoryDiscard((_handleTable + _cdPlayHandle)->_node); // Free it
// It must always be the same
assert(_cdPlayHandle == (start >> SCNHANDLE_SHIFT));
assert(_cdPlayHandle == (next >> SCNHANDLE_SHIFT));
_cdBaseHandle = start;
_cdTopHandle = next;
}
void Handle::SetCdPlaySceneDetails(const char *fileName) {
_szCdPlayFile = fileName;
}
void Handle::SetCdPlayHandle(int fileNum) {
_cdPlayHandle = fileNum;
}
/**
* Loads a memory block as a file.
* @param pH Memory block pointer
*/
void Handle::LoadFile(MEMHANDLE *pH) {
Common::File f;
char szFilename[sizeof(pH->szName) + 1];
// extract and zero terminate the filename
memcpy(szFilename, pH->szName, sizeof(pH->szName));
szFilename[sizeof(pH->szName)] = 0;
if ((TinselVersion != 3) && MEMFLAGS(pH) & fCompressed) {
error("Compression handling has been removed - %s", szFilename);
}
if (f.open(szFilename)) {
// read the data
int bytes;
uint8 *addr;
// lock the memory
addr = (uint8 *)MemoryLock(pH->_node);
// make sure address is valid
assert(addr);
if ((TinselVersion == 3) && MEMFLAGS(pH) & fCompressed) {
bytes = decompressLZSS(f, addr);
} else {
bytes = f.read(addr, pH->filesize & FSIZE_MASK);
}
// close the file
f.close();
// discardable - unlock the memory
MemoryUnlock(pH->_node);
// set the loaded flag
MEMFLAGSET(pH, fLoaded);
if (bytes == int(pH->filesize & FSIZE_MASK)) {
return;
}
// file is corrupt
error(FILE_IS_CORRUPT, szFilename);
}
// cannot find file
error(CANNOT_FIND_FILE, szFilename);
}
/**
* Return a font specified by a SCNHANDLE
* Handles endianess internally
* @param offset Handle and offset to data
* @return FONT structure
*/
FONT *Handle::GetFont(SCNHANDLE offset) {
byte *data = LockMem(offset);
const bool isBE = TinselV1Mac || TinselV1Saturn;
const uint32 size = ((TinselVersion == 3) ? 12 * 4 : 11 * 4) + 300 * 4; // FONT struct size
Common::MemoryReadStreamEndian *stream = new Common::MemoryReadStreamEndian(data, size, isBE);
FONT *font = new FONT();
font->xSpacing = stream->readSint32();
font->ySpacing = stream->readSint32();
font->xShadow = stream->readSint32();
font->yShadow = stream->readSint32();
font->spaceSize = stream->readSint32();
font->baseColor = (TinselVersion == 3) ? stream->readSint32() : 0;
font->fontInit.hObjImg = stream->readUint32();
font->fontInit.objFlags = stream->readSint32();
font->fontInit.objID = stream->readSint32();
font->fontInit.objX = stream->readSint32();
font->fontInit.objY = stream->readSint32();
font->fontInit.objZ = stream->readSint32();
for (int i = 0; i < 300; i++)
font->fontDef[i] = stream->readUint32();
delete stream;
return font;
}
/**
* Return a palette specified by a SCNHANDLE
* Handles endianess internally
* @param offset Handle and offset to data
* @return PALETTE structure
*/
PALETTE *Handle::GetPalette(SCNHANDLE offset) {
byte *data = LockMem(offset);
const bool isBE = TinselV1Mac || TinselV1Saturn;
const uint32 size = 4 + 256 * 4; // numColors + 256 COLORREF (max)
Common::MemoryReadStreamEndian *stream = new Common::MemoryReadStreamEndian(data, size, isBE);
PALETTE *pal = new PALETTE();
pal->numColors = stream->readSint32();
for (int32 i = 0; i < pal->numColors; i++) {
pal->palRGB[i] = stream->readUint32();
// get the RGB color model values
pal->palette[i * 3] = (byte)(pal->palRGB[i] & 0xFF);
pal->palette[i * 3 + 1] = (byte)((pal->palRGB[i] >> 8) & 0xFF);
pal->palette[i * 3 + 2] = (byte)((pal->palRGB[i] >> 16) & 0xFF);
}
delete stream;
return pal;
}
/**
* Return an image specified by a SCNHANDLE
* Handles endianess internally
* @param offset Handle and offset to data
* @return IMAGE structure
*/
const IMAGE *Handle::GetImage(SCNHANDLE offset) {
byte *data = LockMem(offset);
const bool isBE = TinselV1Mac || TinselV1Saturn;
const uint32 size = 16; // IMAGE struct size
Common::MemoryReadStreamEndian *stream = new Common::MemoryReadStreamEndian(data, size, isBE);
IMAGE *img = new IMAGE();
img->imgWidth = stream->readSint16();
img->imgHeight = stream->readUint16();
img->anioffX = stream->readSint16();
img->anioffY = stream->readSint16();
img->hImgBits = stream->readUint32();
if (TinselVersion != 3) {
img->hImgPal = stream->readUint32();
} else {
img->isRLE = stream->readSint16();
img->colorFlags = stream->readSint16();
}
delete stream;
return img;
}
void Handle::SetImagePalette(SCNHANDLE offset, SCNHANDLE palHandle) {
byte *img = LockMem(offset);
WRITE_32(img + 12, palHandle); // hImgPal
}
SCNHANDLE Handle::GetFontImageHandle(SCNHANDLE offset) {
FONT *font = GetFont(offset);
SCNHANDLE handle = font->fontInit.hObjImg;
delete font;
return handle;
}
/**
* Return an actor's data specified by a SCNHANDLE
* Handles endianess internally
* @param offset Handle and offset to data
* @param count Data count
* @return IMAGE structure
*/
const ACTORDATA *Handle::GetActorData(SCNHANDLE offset, uint32 count) {
byte *data = LockMem(offset);
const bool isBE = TinselV1Mac || TinselV1Saturn;
const uint32 size = (TinselVersion >= 2) ? 20 : 12; // ACTORDATA struct size
Common::MemoryReadStreamEndian *stream = new Common::MemoryReadStreamEndian(data, size * count, isBE);
ACTORDATA *actorData = new ACTORDATA[count];
for (uint32 i = 0; i < count; i++) {
if (TinselVersion <= 1) {
actorData[i].masking = stream->readSint32();
actorData[i].hActorId = stream->readUint32();
actorData[i].hActorCode = stream->readUint32();
} else {
actorData[i].hActorId = stream->readUint32();
actorData[i].hTagText = stream->readUint32();
actorData[i].tagPortionV = stream->readSint32();
actorData[i].tagPortionH = stream->readSint32();
actorData[i].hActorCode = stream->readUint32();
}
}
delete stream;
return actorData;
}
/**
* Return a process specified by a SCNHANDLE
* Handles endianess internally
* @param offset Handle and offset to data
* @param count Data count
* @return PROCESS_STRUC structure
*/
const PROCESS_STRUC *Handle::GetProcessData(SCNHANDLE offset, uint32 count) {
byte *data = LockMem(offset);
const bool isBE = TinselV1Mac || TinselV1Saturn;
const uint32 size = 8; // PROCESS_STRUC struct size
Common::MemoryReadStreamEndian *stream = new Common::MemoryReadStreamEndian(data, size * count, isBE);
PROCESS_STRUC *processData = new PROCESS_STRUC[count];
for (uint32 i = 0; i < count; i++) {
processData[i].processId = stream->readUint32();
processData[i].hProcessCode = stream->readUint32();
}
delete stream;
return processData;
}
/**
* Compute and return the address specified by a SCNHANDLE.
* @param offset Handle and offset to data
*/
byte *Handle::LockMem(SCNHANDLE offset) {
uint32 handle = offset >> SCNHANDLE_SHIFT; // calc memory handle to use
debug(8, "Locking offset of type %ld (%lx), offset %ld, handle %d", (offset & HANDLEMASK) >> SCNHANDLE_SHIFT, (offset & HANDLEMASK) >> SCNHANDLE_SHIFT, offset & OFFSETMASK, handle);
MEMHANDLE *pH; // points to table entry
// range check the memory handle
assert(handle < _numHandles);
pH = _handleTable + handle;
if (MEMFLAGS(pH) & fPreload) {
// permanent files are already loaded, nothing to be done
} else if (handle == _cdPlayHandle) {
// Must be in currently loaded/loadable range
if (offset < _cdBaseHandle || offset >= _cdTopHandle)
error("Overlapping (in time) CD-plays");
// May have been discarded, if so, we have to reload
if (!MemoryDeref(pH->_node)) {
// Data was discarded, we have to reload
MemoryReAlloc(pH->_node, _cdTopHandle - _cdBaseHandle);
LoadCDGraphData(pH);
// update the LRU time (new in this file)
MemoryTouch(pH->_node);
}
// make sure address is valid
assert(MEMFLAGS(pH) & fLoaded);
offset -= _cdBaseHandle;
} else {
if (!MemoryDeref(pH->_node)) {
// Data was discarded, we have to reload
MemoryReAlloc(pH->_node, pH->filesize & FSIZE_MASK);
if (TinselVersion >= 2) {
SetCD(pH->flags2 & fAllCds);
CdCD(Common::nullContext);
}
LoadFile(pH);
}
// make sure address is valid
assert(MEMFLAGS(pH) & fLoaded);
}
return MemoryDeref(pH->_node) + (offset & OFFSETMASK);
}
/**
* Called to lock the current scene and make it non-discardable.
* @param offset Handle and offset to data
*/
void Handle::LockScene(SCNHANDLE offset) {
uint32 handle = offset >> SCNHANDLE_SHIFT; // calc memory handle to use
MEMHANDLE *pH; // points to table entry
// range check the memory handle
assert(handle < _numHandles);
pH = _handleTable + handle;
if ((MEMFLAGS(pH) & fPreload) == 0) {
// Ensure the scene handle is allocated.
MemoryReAlloc(pH->_node, pH->filesize & FSIZE_MASK);
// Now lock it to make sure it stays allocated and in a fixed position.
MemoryLock(pH->_node);
}
}
/**
* Called to make the current scene discardable again.
* @param offset Handle and offset to data
*/
void Handle::UnlockScene(SCNHANDLE offset) {
uint32 handle = offset >> SCNHANDLE_SHIFT; // calc memory handle to use
MEMHANDLE *pH; // points to table entry
// range check the memory handle
assert(handle < _numHandles);
pH = _handleTable + handle;
if ((MEMFLAGS(pH) & fPreload) == 0) {
// unlock the scene data
MemoryUnlock(pH->_node);
}
}
/*----------------------------------------------------------------------*/
#ifdef BODGE
/**
* Validates that a specified handle pointer is valid
* @param offset Handle and offset to data
*/
bool Handle::ValidHandle(SCNHANDLE offset) {
uint32 handle = offset >> SCNHANDLE_SHIFT; // calc memory handle to use
MEMHANDLE *pH; // points to table entry
// range check the memory handle
assert(handle < _numHandles);
pH = _handleTable + handle;
return (pH->filesize & FSIZE_MASK) != 8;
}
#endif
/**
* TouchMem
* @param offset Handle and offset to data
*/
void Handle::TouchMem(SCNHANDLE offset) {
MEMHANDLE *pH; // points to table entry
uint32 handle = offset >> SCNHANDLE_SHIFT; // calc memory handle to use
if (offset != 0) {
pH = _handleTable + handle;
// update the LRU time whether its loaded or not!
if (pH->_node)
MemoryTouch(pH->_node);
}
}
/**
* Returns true if the given handle is into the cd graph data
* @param offset Handle and offset to data
*/
bool Handle::IsCdPlayHandle(SCNHANDLE offset) {
uint32 handle = offset >> SCNHANDLE_SHIFT; // calc memory handle to use
// range check the memory handle
assert(handle < _numHandles);
return (handle == _cdPlayHandle);
}
/**
* Returns the CD for a given scene handle
*/
int Handle::CdNumber(SCNHANDLE offset) {
uint handle = offset >> SCNHANDLE_SHIFT; // calc memory handle to use
// range check the memory handle
assert(handle < _numHandles);
MEMHANDLE *pH = _handleTable + handle;
if (TinselVersion <= 1)
return 1;
return GetCD(pH->flags2 & fAllCds);
}
/**
* Searches for a resource by name and returns the handle to it.
*
* @param fileName Name of the resource to search for
*/
SCNHANDLE Handle::FindLanguageSceneHandle(const char *fileName) {
Common::String nameString{fileName};
for (uint i = 0; i < _numHandles; ++i) {
if (nameString == Common::String{_handleTable[i].szName}) {
return i << SCNHANDLE_SHIFT;
}
}
error("Can't find handle for language scene\n");
}
} // End of namespace Tinsel

108
engines/tinsel/handle.h Normal file
View File

@@ -0,0 +1,108 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* Graphics Memory Manager data structures
*/
#ifndef TINSEL_HANDLE_H
#define TINSEL_HANDLE_H
#include "common/path.h"
#include "tinsel/dw.h" // new data types
namespace Common {
class File;
}
namespace Tinsel {
struct FONT;
struct MEMHANDLE;
struct PALETTE;
struct IMAGE;
struct ACTORDATA;
struct PROCESS_STRUC;
class Handle {
public:
Handle();
virtual ~Handle();
/**
* Loads the graphics handle table index file and preloads all the permanent graphics etc.
*/
void SetupHandleTable();
FONT *GetFont(SCNHANDLE offset);
PALETTE *GetPalette(SCNHANDLE offset);
const IMAGE *GetImage(SCNHANDLE offset);
void SetImagePalette(SCNHANDLE offset, SCNHANDLE palHandle);
SCNHANDLE GetFontImageHandle(SCNHANDLE offset);
const ACTORDATA *GetActorData(SCNHANDLE offset, uint32 count);
const PROCESS_STRUC *GetProcessData(SCNHANDLE offset, uint32 count);
byte *LockMem(SCNHANDLE offset);
void LockScene(SCNHANDLE offset);
void UnlockScene(SCNHANDLE offset);
bool IsCdPlayHandle(SCNHANDLE offset);
void TouchMem(SCNHANDLE offset);
// Called at scene startup
void SetCdPlaySceneDetails(const char *fileName);
// Called at game startup
void SetCdPlayHandle(int fileNum);
void LoadExtraGraphData(
SCNHANDLE start, // Handle of start of range
SCNHANDLE next); // Handle of end of range + 1
int CdNumber(SCNHANDLE offset);
// Noir
SCNHANDLE FindLanguageSceneHandle(const char *fileName);
#ifdef BODGE
bool ValidHandle(SCNHANDLE offset);
#endif
private:
void LoadFile(MEMHANDLE *pH); // load a memory block as a file
void OpenCDGraphFile();
void LoadCDGraphData(MEMHANDLE *pH);
// handle table gets loaded from index file at runtime
MEMHANDLE *_handleTable;
// number of handles in the handle table
uint _numHandles;
uint32 _cdPlayHandle;
SCNHANDLE _cdBaseHandle, _cdTopHandle;
Common::File *_cdGraphStream;
Common::Path _szCdPlayFile;
};
} // End of namespace Tinsel
#endif // TINSEL_HANDLE_H

478
engines/tinsel/heapmem.cpp Normal file
View File

@@ -0,0 +1,478 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* This file contains the handle based Memory Manager code.
*/
#include "tinsel/heapmem.h"
#include "tinsel/timers.h" // For DwGetCurrentTime
#include "tinsel/tinsel.h"
namespace Tinsel {
#define NUM_MNODES 192 // the number of memory management nodes (was 128, then 192)
// internal allocation flags
#define DWM_USED 0x0001 ///< the objects memory block is in use
#define DWM_DISCARDED 0x0002 ///< the objects memory block has been discarded
#define DWM_LOCKED ((TinselVersion == 3) ? 0x0200 : 0x0004) ///< the objects memory block is locked
#define DWM_SENTINEL 0x0008 ///< the objects memory block is a sentinel
// Noir
#define DWM_V3X20 0x0020 ///< unknown
#define DWM_V3X4 0x0004 ///< unknown
struct MEM_NODE {
MEM_NODE *pNext; // link to the next node in the list
MEM_NODE *pPrev; // link to the previous node in the list
uint8 *pBaseAddr; // base address of the memory object
long size; // size of the memory object
uint32 lruTime; // time when memory object was last accessed
int flags; // allocation attributes
};
// Specifies the total amount of memory required for DW1 demo, DW1, or DW2 respectively.
// Currently this is set at 5MB for the DW1 demo and DW1 and 10MB for DW2
// This could probably be reduced somewhat
// If the memory is not enough, the engine throws an "Out of memory" error in handle.cpp inside LockMem()
static const uint32 MemoryPoolSize[4] = {5 * 1024 * 1024, 5 * 1024 * 1024, 10 * 1024 * 1024, 512 * 1024 * 1024};
// These vars are reset upon engine destruction
// list of all memory nodes
MEM_NODE g_mnodeList[NUM_MNODES];
// pointer to the linked list of free mnodes
static MEM_NODE *g_pFreeMemNodes;
// list of all fixed memory nodes
MEM_NODE g_s_fixedMnodesList[5];
// the mnode heap sentinel
static MEM_NODE g_heapSentinel;
//
static MEM_NODE *AllocMemNode();
#ifdef DEBUG
static void MemoryStats() {
int usedNodes = 0;
int allocedNodes = 0;
int lockedNodes = 0;
int lockedSize = 0;
int totalSize = 0;
const MEM_NODE *pHeap = &g_heapSentinel;
MEM_NODE *pCur;
for (pCur = pHeap->pNext; pCur != pHeap; pCur = pCur->pNext) {
usedNodes++;
totalSize += pCur->size;
if (pCur->flags)
allocedNodes++;
if (pCur->flags & DWM_LOCKED) {
lockedNodes++;
lockedSize += pCur->size;
}
}
debug("%d nodes used, %d alloced, %d locked; %d bytes locked, %d used",
usedNodes, allocedNodes, lockedNodes, lockedSize, totalSize);
}
#endif
/**
* Initializes the memory manager.
*/
void MemoryInit() {
// place first node on free list
g_pFreeMemNodes = g_mnodeList;
// link all other objects after first
memset(g_mnodeList, 0, sizeof(g_mnodeList));
for (int i = 1; i < NUM_MNODES; i++) {
g_mnodeList[i - 1].pNext = g_mnodeList + i;
}
// null the last mnode
g_mnodeList[NUM_MNODES - 1].pNext = nullptr;
// clear list of fixed memory nodes
memset(g_s_fixedMnodesList, 0, sizeof(g_s_fixedMnodesList));
// set cyclic links to the sentinel
g_heapSentinel.pPrev = &g_heapSentinel;
g_heapSentinel.pNext = &g_heapSentinel;
// flag sentinel as locked
g_heapSentinel.flags = DWM_LOCKED | DWM_SENTINEL;
// store the current heap size in the sentinel
uint32 size = MemoryPoolSize[0];
if (TinselVersion == 1) size = MemoryPoolSize[1];
else if (TinselVersion == 2) size = MemoryPoolSize[2];
else if (TinselVersion == 3) {
warning("TODO: Find the correct memory pool size for Noir, using 512 MiB for now");
size = MemoryPoolSize[3];
}
g_heapSentinel.size = size;
}
/**
* Deinitializes the memory manager.
*/
void MemoryDeinit() {
const MEM_NODE *pHeap = &g_heapSentinel;
MEM_NODE *pCur;
pCur = g_s_fixedMnodesList;
for (int i = 0; i < ARRAYSIZE(g_s_fixedMnodesList); ++i, ++pCur) {
free(pCur->pBaseAddr);
pCur->pBaseAddr = 0;
}
for (pCur = pHeap->pNext; pCur != pHeap; pCur = pCur->pNext) {
free(pCur->pBaseAddr);
pCur->pBaseAddr = 0;
}
memset(g_mnodeList, 0, sizeof(g_mnodeList));
memset(g_s_fixedMnodesList, 0, sizeof(g_s_fixedMnodesList));
g_pFreeMemNodes = nullptr;
}
/**
* Allocate a mnode from the free list.
*/
static MEM_NODE *AllocMemNode() {
// get the first free mnode
MEM_NODE *pMemNode = g_pFreeMemNodes;
// make sure a mnode is available
assert(pMemNode); // Out of memory nodes
// the next free mnode
g_pFreeMemNodes = pMemNode->pNext;
// wipe out the mnode
memset(pMemNode, 0, sizeof(MEM_NODE));
// return new mnode
return pMemNode;
}
/**
* Return a mnode back to the free list.
* @param pMemNode Node of the memory object
*/
void FreeMemNode(MEM_NODE *pMemNode) {
// validate mnode pointer
assert(pMemNode >= g_mnodeList && pMemNode <= g_mnodeList + NUM_MNODES - 1);
// place free list in mnode next
pMemNode->pNext = g_pFreeMemNodes;
// add mnode to top of free list
g_pFreeMemNodes = pMemNode;
}
/**
* Tries to make space for the specified number of bytes on the specified heap.
* @param size Number of bytes to free up
* @return true if any blocks were discarded, false otherwise
*/
static bool HeapCompact(long size) {
const MEM_NODE *pHeap = &g_heapSentinel;
MEM_NODE *pCur, *pOldest;
uint32 oldest; // time of the oldest discardable block
while (g_heapSentinel.size < size) {
// find the oldest discardable block
oldest = DwGetCurrentTime();
pOldest = nullptr;
for (pCur = pHeap->pNext; pCur != pHeap; pCur = pCur->pNext) {
if (pCur->flags == DWM_USED) {
// found a non-discarded discardable block
if (pCur->lruTime < oldest) {
oldest = pCur->lruTime;
pOldest = pCur;
}
}
}
if (pOldest)
// discard the oldest block
MemoryDiscard(pOldest);
else
// cannot discard any blocks
return false;
}
// we have freed enough memory
return true;
}
/**
* Allocates the specified number of bytes from the heap.
* @param flags Allocation attributes
* @param size Number of bytes to allocate
*/
static MEM_NODE *MemoryAlloc(long size) {
MEM_NODE *pHeap = &g_heapSentinel;
#ifdef SCUMM_NEED_ALIGNMENT
const int alignPadding = sizeof(void *) - 1;
size = (size + alignPadding) & ~alignPadding; //round up to nearest multiple of sizeof(void *), this ensures the addresses that are returned are alignment-safe.
#endif
// compact the heap to make up room for 'size' bytes, if necessary
if (!HeapCompact(size))
return 0;
// success! we may allocate a new node of the right size
// Allocate a node.
MEM_NODE *pNode = AllocMemNode();
// Allocate memory for the node.
pNode->pBaseAddr = (byte *)malloc(size);
// Verify that we got the memory.
// TODO: If this fails, we should first try to compact the heap some further.
assert(pNode->pBaseAddr);
// Subtract size of new block from total
g_heapSentinel.size -= size;
#ifdef DEBUG
MemoryStats();
#endif
// Set flags, LRU time and size
pNode->flags = DWM_USED;
pNode->lruTime = DwGetCurrentTime() + 1;
pNode->size = size;
// set mnode at the end of the list
pNode->pPrev = pHeap->pPrev;
pNode->pNext = pHeap;
// fix links to this mnode
pHeap->pPrev->pNext = pNode;
pHeap->pPrev = pNode;
return pNode;
}
/**
* Allocate a discarded MEM_NODE. Actual memory can be assigned to it
* by using MemoryReAlloc().
*/
MEM_NODE *MemoryNoAlloc() {
MEM_NODE *pHeap = &g_heapSentinel;
// chain a discarded node onto the end of the heap
MEM_NODE *pNode = AllocMemNode();
pNode->flags = DWM_USED | DWM_DISCARDED;
pNode->lruTime = DwGetCurrentTime();
pNode->size = 0;
// set mnode at the end of the list
pNode->pPrev = pHeap->pPrev;
pNode->pNext = pHeap;
// fix links to this mnode
pHeap->pPrev->pNext = pNode;
pHeap->pPrev = pNode;
// return the discarded node
return pNode;
}
/**
* Allocate a fixed block of data.
* @todo We really should keep track of the allocated pointers,
* so that we can discard them later on, when the engine quits.
*/
MEM_NODE *MemoryAllocFixed(long size) {
#ifdef SCUMM_NEED_ALIGNMENT
const int alignPadding = sizeof(void *) - 1;
size = (size + alignPadding) & ~alignPadding; //round up to nearest multiple of sizeof(void *), this ensures the addresses that are returned are alignment-safe.
#endif
// Search for a free entry in s_fixedMnodesList
MEM_NODE *pNode = g_s_fixedMnodesList;
for (int i = 0; i < ARRAYSIZE(g_s_fixedMnodesList); ++i, ++pNode) {
if (!pNode->pBaseAddr) {
pNode->pNext = 0;
pNode->pPrev = 0;
pNode->pBaseAddr = (byte *)malloc(size);
pNode->size = size;
pNode->lruTime = DwGetCurrentTime() + 1;
pNode->flags = DWM_USED;
// Subtract size of new block from total
g_heapSentinel.size -= size;
return pNode;
}
}
return 0;
}
/**
* Discards the specified memory object.
* @param pMemNode Node of the memory object
*/
void MemoryDiscard(MEM_NODE *pMemNode) {
// validate mnode pointer
assert(pMemNode >= g_mnodeList && pMemNode <= g_mnodeList + NUM_MNODES - 1);
// object must be in use and locked
assert((pMemNode->flags & (DWM_USED | DWM_LOCKED)) == DWM_USED);
// discard it if it isn't already
if ((pMemNode->flags & DWM_DISCARDED) == 0) {
// free memory
free(pMemNode->pBaseAddr);
g_heapSentinel.size += pMemNode->size;
#ifdef DEBUG
MemoryStats();
#endif
// mark the node as discarded
pMemNode->flags |= DWM_DISCARDED;
pMemNode->pBaseAddr = nullptr;
pMemNode->size = 0;
}
}
/**
* Locks a memory object and returns a pointer to the first byte
* of the objects memory block.
* @param pMemNode Node of the memory object
*/
void *MemoryLock(MEM_NODE *pMemNode) {
// make sure memory object is not already locked
assert((pMemNode->flags & DWM_LOCKED) == 0);
// check for a discarded or null memory object
if ((pMemNode->flags & DWM_DISCARDED) || pMemNode->size == 0)
return NULL;
// set the lock flag
pMemNode->flags |= DWM_LOCKED;
#ifdef DEBUG
MemoryStats();
#endif
// return memory objects base address
return pMemNode->pBaseAddr;
}
/**
* Unlocks a memory object.
* @param pMemNode Node of the memory object
*/
void MemoryUnlock(MEM_NODE *pMemNode) {
// make sure memory object is already locked
assert(pMemNode->flags & DWM_LOCKED);
// clear the lock flag
pMemNode->flags &= ~DWM_LOCKED;
#ifdef DEBUG
MemoryStats();
#endif
// update the LRU time
pMemNode->lruTime = DwGetCurrentTime();
}
/**
* Changes the size of a specified memory object and re-allocate it if necessary.
* @param pMemNode Node of the memory object
* @param size New size of block
*/
void MemoryReAlloc(MEM_NODE *pMemNode, long size) {
MEM_NODE *pNew;
// validate mnode pointer
assert(pMemNode >= g_mnodeList && pMemNode <= g_mnodeList + NUM_MNODES - 1);
// align the size to machine boundary requirements
size = (size + sizeof(void *) - 1) & ~(sizeof(void *) - 1);
// validate the size
assert(size);
if (size != pMemNode->size) {
// make sure memory object is discarded and not locked
assert(pMemNode->flags == (DWM_USED | DWM_DISCARDED));
assert(pMemNode->size == 0);
// unlink the mnode from the current heap
pMemNode->pNext->pPrev = pMemNode->pPrev;
pMemNode->pPrev->pNext = pMemNode->pNext;
// allocate a new node
pNew = MemoryAlloc(size);
// make sure memory allocated
assert(pNew != NULL);
// copy the node to the current node
memcpy(pMemNode, pNew, sizeof(MEM_NODE));
// relink the mnode into the list
pMemNode->pPrev->pNext = pMemNode;
pMemNode->pNext->pPrev = pMemNode;
// free the new node
FreeMemNode(pNew);
}
assert(pMemNode->pBaseAddr);
}
/**
* Touch a memory object by updating its LRU time.
* @param pMemNode Node of the memory object
*/
void MemoryTouch(MEM_NODE *pMemNode) {
// update the LRU time
pMemNode->lruTime = DwGetCurrentTime();
}
uint8 *MemoryDeref(MEM_NODE *pMemNode) {
return pMemNode->pBaseAddr;
}
} // End of namespace Tinsel

67
engines/tinsel/heapmem.h Normal file
View File

@@ -0,0 +1,67 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* This file contains the handle based Memory Manager defines
*/
#ifndef TINSEL_HEAPMEM_H
#define TINSEL_HEAPMEM_H
#include "tinsel/dw.h" // new data types
namespace Tinsel {
struct MEM_NODE;
/*----------------------------------------------------------------------*\
|* Memory Function Prototypes *|
\*----------------------------------------------------------------------*/
void MemoryInit(); // initializes the memory manager
void MemoryDeinit(); // deinitializes the memory manager
// reserves a memory node for a movable & discardable block
MEM_NODE *MemoryNoAlloc();
// allocates a fixed block with the specified number of bytes
MEM_NODE *MemoryAllocFixed(long size);
void MemoryDiscard( // discards the specified memory object
MEM_NODE *pMemNode); // node of the memory object
void *MemoryLock( // locks a memory object and returns a pointer to the first byte of the objects memory block
MEM_NODE *pMemNode); // node of the memory object
void MemoryReAlloc( // changes the size or attributes of a specified memory object
MEM_NODE *pMemNode, // node of the memory object
long size); // new size of block
void MemoryUnlock( // unlocks a memory object
MEM_NODE *pMemNode); // node of the memory object
// 'touch' the memory object, i.e., update its "least recently used" counter.
void MemoryTouch(MEM_NODE *pMemNode);
// Dereference a given memory node
uint8 *MemoryDeref(MEM_NODE *pMemNode);
} // End of namespace Tinsel
#endif

View File

@@ -0,0 +1,107 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "tinsel/inv_objects.h"
#include "common/memstream.h"
#include "tinsel/tinsel.h"
namespace Tinsel {
InventoryObject::InventoryObject(Common::MemoryReadStreamEndian &stream) {
_id = stream.readUint32();
_hIconFilm = stream.readUint32();
_hScript = stream.readUint32();
if (TinselVersion == 0) {
_attribute = 0;
} else {
_attribute = stream.readUint32();
}
}
template<typename T>
class InventoryObjectsImpl : public InventoryObjects {
public:
InventoryObjectsImpl(const byte *objects, int numObjects) {
bool bigEndian = (TinselV1Mac || TinselV1Saturn);
auto stream = new Common::MemoryReadStreamEndian(objects, T::SIZE() * numObjects, bigEndian, DisposeAfterUse::NO);
for (int i = 0; i < numObjects; i++) {
_objects.push_back(T(*stream));
}
assert((!stream->eos()) && stream->pos() == stream->size());
delete stream;
}
~InventoryObjectsImpl(){};
const InventoryObject *GetInvObject(int id) override {
auto index = GetObjectIndexIfExists(id);
if (index != -1) {
return _objects.data() + index;
}
return nullptr;
}
const InventoryObjectT3 *GetInvObjectT3(int id) override;
const InventoryObject *GetObjectByIndex(int index) const override {
assert(index >= 0 && index < numObjects());
return _objects.data() + index;
}
void SetObjectFilm(int id, SCNHANDLE hFilm) override {
int index = GetObjectIndexIfExists(id);
_objects[index].setIconFilm(hFilm);
}
int GetObjectIndexIfExists(int id) const override {
for (uint i = 0; i < _objects.size(); i++) {
if (_objects[i].getId() == id) {
return i;
}
}
return -1;
};
int numObjects() const override {
return _objects.size();
}
private:
Common::Array<T> _objects;
};
template<>
const InventoryObjectT3 *InventoryObjectsImpl<InventoryObjectT3>::GetInvObjectT3(int id) {
auto index = GetObjectIndexIfExists(id);
if (index != -1) {
return _objects.data() + index;
}
return nullptr;
}
template<>
const InventoryObjectT3 *InventoryObjectsImpl<InventoryObject>::GetInvObjectT3(int id) {
error("Can't query Noir inventory objects from non Noir-game");
}
InventoryObjects *InstantiateInventoryObjects(const byte *invObjects, int numObjects) {
switch (TinselVersion) {
case 3:
return new InventoryObjectsImpl<InventoryObjectT3>(invObjects, numObjects);
default:
return new InventoryObjectsImpl<InventoryObject>(invObjects, numObjects);
}
}
} // End of namespace Tinsel

View File

@@ -0,0 +1,120 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef TINSEL_INV_OBJECT_H
#define TINSEL_INV_OBJECT_H
#include "common/memstream.h"
#include "tinsel/tinsel.h"
#include "tinsel/dw.h"
namespace Tinsel {
// attribute values - not a bit bit field to prevent portability problems
enum class InvObjAttr {
IO_ONLYINV1 = 0x01,
IO_ONLYINV2 = 0x02,
IO_DROPCODE = 0x04,
DEFINV1 = 0x08,
DEFINV2 = 0x10,
PERMACONV = 0x20,
CONVENDITEM = 0x40,
// Noir only
V3ATTR_X80 = 0x80,
NOTEBOOK_CLUE = 0x200,
V3ATTR_X400 = 0x400,
NOTEBOOK_TITLE = 0x800, // is a notebook title
V3ATTR_X1000 = 0x1000,
V3ATTR_X2000 = 0x2000,
};
class InventoryObject {
public:
InventoryObject(Common::MemoryReadStreamEndian &stream);
int32 getId() const { return _id; }
SCNHANDLE getIconFilm() const { return _hIconFilm; };
void setIconFilm(SCNHANDLE hIconFilm) { _hIconFilm = hIconFilm; }
SCNHANDLE getScript() const { return _hScript; }
// Tinsel1+
bool hasAttribute(InvObjAttr attribute) const {
return getAttribute() & (int32)attribute;
}
// Data size consumed by constructor
static int SIZE() {
return (TinselVersion == 0 ? T0_SIZE : T1_SIZE);
}
protected:
static const int T0_SIZE = 3 * 4;
static const int T1_SIZE = T0_SIZE + 4; // Versions above 0 have attributes
// Tinsel 1+
int32 getAttribute() const {
return _attribute;
};
private:
int32 _id; // inventory objects id
SCNHANDLE _hIconFilm; // inventory objects animation film
SCNHANDLE _hScript; // inventory objects event handling script
int32 _attribute = 0;
};
class InventoryObjectT3 : public InventoryObject {
public:
InventoryObjectT3(Common::MemoryReadStreamEndian &stream) : InventoryObject(stream) {
_unknown = stream.readUint32();
_title = stream.readUint32();
}
// Noir:
bool isNotebookTitle() const {
return (getAttribute() & (int)InvObjAttr::NOTEBOOK_TITLE) != 0;
}
int32 getUnknown() const {
return _unknown;
}
int32 getTitle() const {
return _title;
}
// Data size consumed by constructor
static int SIZE() {
return InventoryObject::SIZE() + 8;
}
private:
int32 _unknown;
int32 _title;
};
class InventoryObjects {
public:
virtual ~InventoryObjects() {};
virtual const InventoryObject *GetInvObject(int id) = 0;
virtual const InventoryObjectT3 *GetInvObjectT3(int id) = 0;
virtual const InventoryObject *GetObjectByIndex(int index) const = 0;
virtual void SetObjectFilm(int id, SCNHANDLE hFilm) = 0;
virtual int GetObjectIndexIfExists(int id) const = 0;
virtual int numObjects() const = 0;
};
InventoryObjects *InstantiateInventoryObjects(const byte *invObjects, int numObjects);
} // End of namespace Tinsel
#endif // TINSEL_INV_OBJECT_H

188
engines/tinsel/mareels.cpp Normal file
View File

@@ -0,0 +1,188 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* Functions to set up moving actors' reels.
*/
#include "tinsel/handle.h"
#include "tinsel/pcode.h" // For D_UP, D_DOWN
#include "tinsel/movers.h"
#include "common/textconsole.h"
#include "common/util.h"
namespace Tinsel {
//----------------- LOCAL GLOBAL DATA --------------------
enum {
NUM_INTERVALS = REQ_MAIN_SCALES - 1,
// 2 for up and down, 3 allow enough entries for 3 fully subscribed moving actors' worth
MAX_SCRENTRIES = NUM_INTERVALS*2*3
};
struct SCIdataStruct {
int actor;
int scale;
int direction;
SCNHANDLE reels[4];
};
// These vars are reset upon engine destruction
static SCIdataStruct g_SCIdata[MAX_SCRENTRIES];
static int g_scrEntries = 0;
/**
* Sets an actor's walk reels
*/
void SetWalkReels(MOVER *pMover, int scale,
SCNHANDLE al, SCNHANDLE ar, SCNHANDLE af, SCNHANDLE aa) {
assert(scale > 0 && scale <= TOTAL_SCALES);
pMover->walkReels[scale-1][LEFTREEL] = al;
pMover->walkReels[scale-1][RIGHTREEL] = ar;
pMover->walkReels[scale-1][FORWARD] = af;
pMover->walkReels[scale-1][AWAY] = aa;
}
/**
* Sets an actor's stand reels
*/
void SetStandReels(MOVER *pMover, int scale,
SCNHANDLE al, SCNHANDLE ar, SCNHANDLE af, SCNHANDLE aa) {
assert(scale > 0 && scale <= TOTAL_SCALES);
pMover->standReels[scale-1][LEFTREEL] = al;
pMover->standReels[scale-1][RIGHTREEL] = ar;
pMover->standReels[scale-1][FORWARD] = af;
pMover->standReels[scale-1][AWAY] = aa;
}
/**
* Sets an actor's talk reels
*/
void SetTalkReels(MOVER *pMover, int scale,
SCNHANDLE al, SCNHANDLE ar, SCNHANDLE af, SCNHANDLE aa) {
assert(scale > 0 && scale <= TOTAL_SCALES);
pMover->talkReels[scale-1][LEFTREEL] = al;
pMover->talkReels[scale-1][RIGHTREEL] = ar;
pMover->talkReels[scale-1][FORWARD] = af;
pMover->talkReels[scale-1][AWAY] = aa;
}
/**
* Return handle to actor's talk reel at present scale and direction.
*/
SCNHANDLE GetMoverTalkReel(MOVER *pActor, TFTYPE dirn) {
assert(1 <= pActor->scale && pActor->scale <= TOTAL_SCALES);
switch (dirn) {
case TF_NONE:
return pActor->talkReels[pActor->scale-1][pActor->direction];
case TF_UP:
return pActor->talkReels[pActor->scale-1][AWAY];
case TF_DOWN:
return pActor->talkReels[pActor->scale-1][FORWARD];
case TF_LEFT:
return pActor->talkReels[pActor->scale-1][LEFTREEL];
case TF_RIGHT:
return pActor->talkReels[pActor->scale-1][RIGHTREEL];
default:
error("GetMoverTalkReel() - illegal direction");
}
}
/**
* scalingreels
*/
void SetScalingReels(int actor, int scale, int direction,
SCNHANDLE left, SCNHANDLE right, SCNHANDLE forward, SCNHANDLE away) {
assert(scale >= 1 && scale <= NUM_MAINSCALES); // invalid scale
assert(!(scale == 1 && direction == D_UP) &&
!(scale == NUM_MAINSCALES && direction == D_DOWN)); // illegal direction from scale
assert(g_scrEntries < MAX_SCRENTRIES); // Scaling reels limit reached!
g_SCIdata[g_scrEntries].actor = actor;
g_SCIdata[g_scrEntries].scale = scale;
g_SCIdata[g_scrEntries].direction = direction;
g_SCIdata[g_scrEntries].reels[LEFTREEL] = left;
g_SCIdata[g_scrEntries].reels[RIGHTREEL] = right;
g_SCIdata[g_scrEntries].reels[FORWARD] = forward;
g_SCIdata[g_scrEntries].reels[AWAY] = away;
g_scrEntries++;
}
/**
* ScalingReel
*/
SCNHANDLE ScalingReel(int ano, int scale1, int scale2, DIRECTION reel) {
int d; // Direction
// The smaller the number, the bigger the scale
if (scale1 < scale2)
d = D_DOWN;
else
d = D_UP;
for (int i = 0; i < g_scrEntries; i++) {
if (g_SCIdata[i].actor == ano && g_SCIdata[i].scale == scale1 && g_SCIdata[i].direction == d) {
if (g_SCIdata[i].reels[reel] == TF_NONE)
return 0;
else
return g_SCIdata[i].reels[reel];
}
}
return 0;
}
/**
* RebootScalingReels
*/
void RebootScalingReels() {
g_scrEntries = 0;
memset(g_SCIdata, 0, sizeof(g_SCIdata));
}
/**
* Discourage them from being ditched.
*/
void TouchMoverReels() {
MOVER *pMover = NextMover(NULL);
int scale;
do {
for (scale = 0; scale < TOTAL_SCALES; scale++) {
_vm->_handle->TouchMem(pMover->walkReels[scale][LEFTREEL]);
}
} while ((pMover = NextMover(pMover)) != NULL);
}
} // End of namespace Tinsel

52
engines/tinsel/mareels.h Normal file
View File

@@ -0,0 +1,52 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef TINSEL_MAREELS_H // prevent multiple includes
#define TINSEL_MAREELS_H
#include "tinsel/dw.h" // for SCNHANDLE
#include "tinsel/movers.h"
namespace Tinsel {
void SetWalkReels(MOVER *pMover, int scale,
SCNHANDLE al, SCNHANDLE ar, SCNHANDLE af, SCNHANDLE aa);
void SetStandReels(MOVER *pMover, int scale,
SCNHANDLE al, SCNHANDLE ar, SCNHANDLE af, SCNHANDLE aa);
void SetTalkReels(MOVER *pMover, int scale,
SCNHANDLE al, SCNHANDLE ar, SCNHANDLE af, SCNHANDLE aa);
SCNHANDLE GetMoverTalkReel(MOVER *pActor, TFTYPE dirn);
void SetScalingReels(int actor, int scale, int direction,
SCNHANDLE left, SCNHANDLE right, SCNHANDLE forward, SCNHANDLE away);
SCNHANDLE ScalingReel(int ano, int scale1, int scale2, DIRECTION reel);
void RebootScalingReels();
void TouchMoverReels();
} // End of namespace Tinsel
#endif

View File

@@ -0,0 +1,440 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/savefile.h"
#include "common/config-manager.h"
#include "common/translation.h"
#include "engines/advancedDetector.h"
#include "tinsel/bmv.h"
#include "tinsel/cursor.h"
#include "tinsel/tinsel.h"
#include "tinsel/savescn.h" // needed by TinselMetaEngine::
#include "tinsel/detection.h"
#include "backends/keymapper/action.h"
#include "backends/keymapper/keymapper.h"
#include "backends/keymapper/standard-actions.h"
static const ADExtraGuiOptionsMap optionsList[] = {
{
GAMEOPTION_CROP_HEIGHT_480_TO_432,
{
_s("Remove Black Bars"),
_s("The game originally renders at 640x432 which is then presented letterboxed at 640x480. Enabling this option removes the forced letterbox effect, so that the game fits better on screens with an aspect ratio wider than 4:3."),
"crop_black_bars",
false,
0,
0
}
},
AD_EXTRA_GUI_OPTIONS_TERMINATOR
};
namespace Tinsel {
uint32 TinselEngine::getGameID() const {
return _gameDescription->gameID;
}
uint32 TinselEngine::getFeatures() const {
return _gameDescription->features;
}
Common::Language TinselEngine::getLanguage() const {
return _gameDescription->desc.language;
}
Common::Platform TinselEngine::getPlatform() const {
return _gameDescription->desc.platform;
}
uint16 TinselEngine::getVersion() const {
return _gameDescription->version;
}
bool TinselEngine::getIsADGFDemo() const {
return (bool)(_gameDescription->desc.flags & ADGF_DEMO);
}
bool TinselEngine::isV1CD() const {
return (bool)(_gameDescription->desc.flags & ADGF_CD);
}
} // End of namespace Tinsel
class TinselMetaEngine : public AdvancedMetaEngine<Tinsel::TinselGameDescription> {
public:
const char *getName() const override{
return "tinsel";
}
Common::Error createInstance(OSystem *syst, Engine **engine, const Tinsel::TinselGameDescription *desc) const override;
bool hasFeature(MetaEngineFeature f) const override;
SaveStateList listSaves(const char *target) const override;
int getMaximumSaveSlot() const override;
SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const override;
bool removeSaveState(const char *target, int slot) const override;
// TODO: Add getSavegameFile(). See comments in loadGameState and removeSaveState
void registerDefaultSettings(const Common::String &target) const override;
const ADExtraGuiOptionsMap *getAdvancedExtraGuiOptions() const override;
Common::KeymapArray initKeymaps(const char *target) const override;
};
bool TinselMetaEngine::hasFeature(MetaEngineFeature f) const {
return
(f == kSupportsListSaves) ||
(f == kSupportsLoadingDuringStartup) ||
(f == kSupportsDeleteSave) ||
(f == kSimpleSavesNames) ||
(f == kSavesSupportMetaInfo) ||
(f == kSavesSupportPlayTime) ||
(f == kSavesSupportCreationDate);
}
bool Tinsel::TinselEngine::hasFeature(EngineFeature f) const {
return
(f == kSupportsReturnToLauncher) ||
(f == kSupportsLoadingDuringRuntime);
}
SaveStateDescriptor TinselMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
Common::String fileName;
fileName = Common::String::format("%s.%03u", target, slot);
Common::InSaveFile *file = g_system->getSavefileManager()->openForLoading(fileName);
if (!file) {
return SaveStateDescriptor();
}
file->readUint32LE(); // skip id
file->readUint32LE(); // skip size
uint32 ver = file->readUint32LE();
char saveDesc[SG_DESC_LEN];
file->read(saveDesc, sizeof(saveDesc));
saveDesc[SG_DESC_LEN - 1] = 0;
SaveStateDescriptor desc(this, slot, saveDesc);
int8 tm_year = file->readUint16LE();
int8 tm_mon = file->readSByte();
int8 tm_mday = file->readSByte();
int8 tm_hour = file->readSByte();
int8 tm_min = file->readSByte();
file->readSByte(); // skip secs
desc.setSaveDate(1900 + tm_year, 1 + tm_mon, tm_mday);
desc.setSaveTime(tm_hour, tm_min);
if (ver >= 3) {
uint32 playTime = file->readUint32LE(); // playTime in seconds
desc.setPlayTime(playTime);
}
delete file;
return desc;
}
namespace Tinsel {
extern int getList(Common::SaveFileManager *saveFileMan, const Common::String &target);
}
SaveStateList TinselMetaEngine::listSaves(const char *target) const {
Common::String pattern = target;
pattern = pattern + ".###";
Common::StringArray files = g_system->getSavefileManager()->listSavefiles(pattern);
SaveStateList saveList;
int slotNum = 0;
for (Common::StringArray::const_iterator file = files.begin(); file != files.end(); ++file) {
// Obtain the last 3 digits of the filename, since they correspond to the save slot
slotNum = atoi(file->c_str() + file->size() - 3);
const Common::String &fname = *file;
Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(fname);
if (in) {
in->readUint32LE(); // skip id
in->readUint32LE(); // skip size
in->readUint32LE(); // skip version
char saveDesc[SG_DESC_LEN];
in->read(saveDesc, sizeof(saveDesc));
saveDesc[SG_DESC_LEN - 1] = 0;
saveList.push_back(SaveStateDescriptor(this, slotNum, saveDesc));
delete in;
}
}
// Sort saves based on slot number.
Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
Common::Error TinselMetaEngine::createInstance(OSystem *syst, Engine **engine, const Tinsel::TinselGameDescription *desc) const {
*engine = new Tinsel::TinselEngine(syst,desc);
return Common::kNoError;
}
int TinselMetaEngine::getMaximumSaveSlot() const { return 99; }
bool TinselMetaEngine::removeSaveState(const char *target, int slot) const {
Tinsel::setNeedLoad();
// Same issue here as with loadGameState(): we need the physical savegame
// slot. Refer to bug #5819.
int listSlot = -1;
const int numStates = Tinsel::getList(g_system->getSavefileManager(), target);
for (int i = 0; i < numStates; ++i) {
const char *fileName = Tinsel::ListEntry(i, Tinsel::LE_NAME);
const int saveSlot = atoi(fileName + strlen(fileName) - 3);
if (saveSlot == slot) {
listSlot = i;
break;
}
}
bool success = g_system->getSavefileManager()->removeSavefile(Tinsel::ListEntry(listSlot, Tinsel::LE_NAME));
Tinsel::setNeedLoad();
Tinsel::getList(g_system->getSavefileManager(), target);
return success;
}
void TinselMetaEngine::registerDefaultSettings(const Common::String &target) const {
ConfMan.registerDefault("crop_black_bars", false); // show the black bars by default (original behaviour)
}
const ADExtraGuiOptionsMap *TinselMetaEngine::getAdvancedExtraGuiOptions() const {
return optionsList;
}
Common::KeymapArray TinselMetaEngine::initKeymaps(const char *target) const {
using namespace Common;
using namespace Tinsel;
Keymap *engineKeyMap = new Keymap(Keymap::kKeymapTypeGame, "tinsel-default", _("Default keymappings"));
Keymap *gameKeyMap = new Keymap(Keymap::kKeymapTypeGame, "game-shortcuts", _("Game keymappings"));
Keymap *saveloadKeyMap = new Keymap(Keymap::kKeymapTypeGame, "saveload-shortcuts", _("Save/Load keymappings"));
Action *act;
act = new Action(kStandardActionLeftClick, _("Left click"));
act->setLeftClickEvent();
act->addDefaultInputMapping("MOUSE_LEFT");
act->addDefaultInputMapping("JOY_A");
engineKeyMap->addAction(act);
act = new Action(kStandardActionRightClick, _("Right click"));
act->setRightClickEvent();
act->addDefaultInputMapping("MOUSE_RIGHT");
act->addDefaultInputMapping("JOY_B");
engineKeyMap->addAction(act);
act = new Action("ESCAPE", _("Escape"));
act->setCustomEngineActionEvent(kActionEscape);
act->addDefaultInputMapping("ESCAPE");
engineKeyMap->addAction(act);
act = new Action("MOVEUP", _("Move up"));
act->setCustomEngineActionEvent(kActionMoveUp);
act->addDefaultInputMapping("UP");
act->addDefaultInputMapping("KP8");
gameKeyMap->addAction(act);
act = new Action("MOVEDOWN", _("Move down"));
act->setCustomEngineActionEvent(kActionMoveDown);
act->addDefaultInputMapping("DOWN");
act->addDefaultInputMapping("KP2");
gameKeyMap->addAction(act);
act = new Action("MOVELEFT", _("Move left"));
act->setCustomEngineActionEvent(kActionMoveLeft);
act->addDefaultInputMapping("LEFT");
act->addDefaultInputMapping("KP4");
gameKeyMap->addAction(act);
act = new Action("MOVERIGHT", _("Move right"));
act->setCustomEngineActionEvent(kActionMoveRight);
act->addDefaultInputMapping("RIGHT");
act->addDefaultInputMapping("KP6");
gameKeyMap->addAction(act);
// I18N: Walk to where cursor points
act = new Action("WALK", _("Walk to"));
act->setCustomEngineActionEvent(kActionWalkTo);
act->addDefaultInputMapping("SPACE");
gameKeyMap->addAction(act);
// I18N: Perform action on object where cursor points
act = new Action("ACTION", _("Action"));
act->setCustomEngineActionEvent(kActionAction);
act->addDefaultInputMapping("RETURN");
act->addDefaultInputMapping("KP_ENTER");
gameKeyMap->addAction(act);
act = new Action("LOOK", _("Look"));
act->setCustomEngineActionEvent(kActionLook);
act->addDefaultInputMapping("l");
act->addDefaultInputMapping("LCTRL");
act->addDefaultInputMapping("RCTRL");
gameKeyMap->addAction(act);
act = new Action("OPTIONSDIALOG", _("Options menu"));
act->setCustomEngineActionEvent(kActionOptionsDialog);
act->addDefaultInputMapping("F1");
act->addDefaultInputMapping("1");
gameKeyMap->addAction(act);
act = new Action("INVENTORY", _("Inventory"));
act->setCustomEngineActionEvent(kActionInventory);
act->addDefaultInputMapping("F2");
gameKeyMap->addAction(act);
act = new Action("NOTEBOOK", _("Notebook"));
act->setCustomEngineActionEvent(kActionNotebook);
act->addDefaultInputMapping("F3");
gameKeyMap->addAction(act);
act = new Action("SAVE", _("Save"));
act->setCustomEngineActionEvent(kActionSave);
act->addDefaultInputMapping("F5");
act->addDefaultInputMapping("5");
gameKeyMap->addAction(act);
act = new Action("LOAD", _("Load"));
act->setCustomEngineActionEvent(kActionLoad);
act->addDefaultInputMapping("7");
act->addDefaultInputMapping("F7");
gameKeyMap->addAction(act);
act = new Action("QUIT", _("Quit"));
act->setCustomEngineActionEvent(kActionQuit);
act->addDefaultInputMapping("C+q");
act->addDefaultInputMapping("A+q");
gameKeyMap->addAction(act);
act = new Action("PGUPMENU", _("Page up in save / load menu"));
act->setCustomEngineActionEvent(kActionPageUp);
act->addDefaultInputMapping("PAGEUP");
act->addDefaultInputMapping("KP9");
saveloadKeyMap->addAction(act);
act = new Action("PGDOWNMENU", _("Page down in save / load menu"));
act->setCustomEngineActionEvent(kActionPageDown);
act->addDefaultInputMapping("PAGEDOWN");
act->addDefaultInputMapping("KP3");
saveloadKeyMap->addAction(act);
act = new Action("STARTOFMENU", _("Go to start of save / load menu"));
act->setCustomEngineActionEvent(kActionHome);
act->addDefaultInputMapping("HOME");
act->addDefaultInputMapping("KP7");
saveloadKeyMap->addAction(act);
act = new Action("ENDOFMENU", _("Go to end of save / load menu"));
act->setCustomEngineActionEvent(kActionEnd);
act->addDefaultInputMapping("END");
act->addDefaultInputMapping("KP1");
saveloadKeyMap->addAction(act);
KeymapArray keymaps(3);
keymaps[0] = engineKeyMap;
keymaps[1] = gameKeyMap;
keymaps[2] = saveloadKeyMap;
return keymaps;
}
#if PLUGIN_ENABLED_DYNAMIC(TINSEL)
REGISTER_PLUGIN_DYNAMIC(TINSEL, PLUGIN_TYPE_ENGINE, TinselMetaEngine);
#else
REGISTER_PLUGIN_STATIC(TINSEL, PLUGIN_TYPE_ENGINE, TinselMetaEngine);
#endif
namespace Tinsel {
Common::Error TinselEngine::loadGameState(int slot) {
// FIXME: Hopefully this is only used when loading games via
// the launcher, since we do a hacky savegame slot to savelist
// entry mapping here.
//
// You might wonder why is needed and here is the answer:
// The save/load dialog of the GMM operates with the physical
// savegame slots, while Tinsel internally uses entry numbers in
// a savelist (which is sorted latest to first). Now to allow
// proper loading of (especially Discworld2) saves we need to
// get a savelist entry number instead of the physical slot.
//
// There are different possible solutions:
//
// One way to fix this would be to pass the filename instead of
// the savelist entry number to RestoreGame, though it could make
// problems how DW2 handles CD switches. Normally DW2 would pass
// '-2' as slot when it changes CDs.
//
// Another way would be to convert all of Tinsel to use physical
// slot numbers instead of savelist entry numbers for loading.
// This would also allow '-2' as slot for CD changes without
// any major hackery.
int listSlot = -1;
const int numStates = Tinsel::getList();
for (int i = 0; i < numStates; ++i) {
const char *fileName = Tinsel::ListEntry(i, Tinsel::LE_NAME);
const int saveSlot = atoi(fileName + strlen(fileName) - 3);
if (saveSlot == slot) {
listSlot = i;
break;
}
}
if (listSlot == -1)
return Common::kUnknownError; // TODO: proper error code
RestoreGame(listSlot);
return Common::kNoError; // TODO: return success/failure
}
#if 0
Common::Error TinselEngine::saveGameState(int slot, const Common::String &desc, bool isAutosave) {
Common::String saveName = _vm->getSavegameFilename((int16)(slot + 1));
char saveDesc[SG_DESC_LEN];
Common::strlcpy(saveDesc, desc, SG_DESC_LEN);
SaveGame((char *)saveName.c_str(), saveDesc);
ProcessSRQueue(); // This shouldn't be needed, but for some reason it is...
return Common::kNoError; // TODO: return success/failure
}
#endif
bool TinselEngine::canLoadGameStateCurrently(Common::U32String *msg) { return !_bmv->MoviePlaying(); }
#if 0
bool TinselEngine::canSaveGameStateCurrently(Common::U32String *msg) { return isCursorShown(); }
#endif
} // End of namespace Tinsel

66
engines/tinsel/module.mk Normal file
View File

@@ -0,0 +1,66 @@
MODULE := engines/tinsel
MODULE_OBJS := \
noir/lzss.o \
actors.o \
adpcm.o \
anim.o \
background.o \
bg.o \
bmv.o \
cliprect.o \
config.o \
cursor.o \
debugger.o \
dialogs.o \
drives.o \
effect.o \
events.o \
faders.o \
film.o \
font.o \
graphics.o \
handle.o \
heapmem.o \
inv_objects.o \
mareels.o \
metaengine.o \
move.o \
movers.o \
multiobj.o \
music.o \
object.o \
palette.o \
pcode.o \
pdisplay.o \
play.o \
polygons.o \
saveload.o \
savescn.o \
scene.o \
sched.o \
scn.o \
scroll.o \
sound.o \
strres.o \
sysvar.o \
text.o \
timers.o \
tinlib.o \
tinsel.o \
token.o \
noir/notebook.o \
noir/notebook_page.o \
noir/sysreel.o \
noir/spriter.o \
# This module can be built as a plugin
ifeq ($(ENABLE_TINSEL), DYNAMIC_PLUGIN)
PLUGIN := 1
endif
# Include common rules
include $(srcdir)/rules.mk
# Detection objects
DETECT_OBJS += $(MODULE)/detection.o

1787
engines/tinsel/move.cpp Normal file

File diff suppressed because it is too large Load Diff

46
engines/tinsel/move.h Normal file
View File

@@ -0,0 +1,46 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef TINSEL_MOVE_H // prevent multiple includes
#define TINSEL_MOVE_H
#include "tinsel/dw.h" // for SCNHANDLE
#include "tinsel/movers.h"
namespace Tinsel {
struct MOVER;
typedef enum { YB_X2, YB_X1_5 } YBIAS;
int SetActorDest(MOVER *pMover, int x, int y, bool igPath, SCNHANDLE film);
void SSetActorDest(MOVER *pActor);
void DoMoveActor(MOVER *pMover);
void SetDefaultRefer(int32 defRefer);
int GetLastLeadXdest();
int GetLastLeadYdest();
DIRECTION GetDirection(int fromx, int fromy, int tox, int toy, DIRECTION lastreel,
HPOLYGON hPath, YBIAS yBias = YB_X2);
} // End of namespace Tinsel
#endif /* TINSEL_MOVE_H */

1089
engines/tinsel/movers.cpp Normal file

File diff suppressed because it is too large Load Diff

230
engines/tinsel/movers.h Normal file
View File

@@ -0,0 +1,230 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* Should really be called "moving actors.h"
*/
#ifndef TINSEL_MOVERS_H // prevent multiple includes
#define TINSEL_MOVERS_H
#include "tinsel/anim.h" // for ANIM
#include "tinsel/scene.h" // for TFTYPE
#include "tinsel/tinsel.h"
namespace Tinsel {
struct OBJECT;
enum NPS {NOT_IN, GOING_UP, GOING_DOWN, LEAVING, ENTERING};
enum IND {NO_PROB, TRY_CENTER, TRY_CORNER, TRY_NEXTCORNER};
enum DIRECTION { LEFTREEL, RIGHTREEL, FORWARD, AWAY };
#define NUM_MAINSCALES ((TinselVersion >= 2) ? 10 : 5)
#define NUM_AUXSCALES 5
#define TOTAL_SCALES (NUM_MAINSCALES + NUM_AUXSCALES)
#define REQ_MAIN_SCALES 10
#define REQ_TOTAL_SCALES 15
#define BOGUS_BRIGHTNESS -1
enum MOVER_TYPE { MOVER_2D, MOVER_3D };
struct MOVER {
int objX, objY; /* Co-ordinates object */
int targetX, targetY;
int ItargetX, ItargetY; /* Intermediate destination */
int UtargetX, UtargetY; /* Ultimate destination */
HPOLYGON hIpath; /* Intermediate path */
HPOLYGON hUpath; /* Ultimate path */
HPOLYGON hCpath; /* Current path */
bool over;
int walkNumber;
IND InDifficulty;
/* For use in 'follow nodes' polygons */
HPOLYGON hFnpath;
NPS npstatus;
int line;
int Tline; // NEW
// TODO: TagReelRunning may be the same as bSpecReel
bool bSpecReel;
/* Used internally */
DIRECTION direction; // Current reel
int scale; // Current scale
int stepCount; // Step count for walking reel synchronisation
int walkedFromX, walkedFromY;
bool bMoving; // Set this to TRUE during a walk
bool bNoPath;
bool bIgPath;
bool bWalkReel;
OBJECT *actorObj; // Actor's object
ANIM actorAnim; // Actor's animation script
SCNHANDLE hLastFilm; // } Used by AlterMover()
SCNHANDLE hPushedFilm; // }
int actorID;
int actorToken;
SCNHANDLE walkReels[REQ_TOTAL_SCALES][4];
SCNHANDLE standReels[REQ_TOTAL_SCALES][4];
SCNHANDLE talkReels[REQ_TOTAL_SCALES][4];
bool bActive;
int SlowFactor; // Slow down movement while hidden
bool bStop;
/* NOTE: If effect polys can overlap, this needs improving */
bool bInEffect;
Common::PROCESS *pProc;
// Discworld 2 specific fields
int32 zOverride;
bool bHidden;
int brightness; // Current brightness
int startColor;
int paletteLength;
HPOLYGON hRpath; // Recent path
// Noir specific fields
MOVER_TYPE type;
SCNHANDLE hModelName;
SCNHANDLE hTextureName;
int posX, posY, posZ;
int animSpeed;
uint nextIdleAnim;
};
struct MAINIT {
int X;
int Y;
MOVER *pMover;
};
//---------------------------------------------------------------------------
void MoverProcessCreate(int X, int Y, int id, MOVER *pMover);
enum AR_FUNCTION { AR_NORMAL, AR_PUSHREEL, AR_POPREEL, AR_WALKREEL };
void StoreMoverPalette(MOVER *pMover, int startColor, int length);
void MoverBrightness(MOVER *pMover, int brightness);
MOVER *GetMover(int ano);
MOVER *RegisterMover(int ano);
void KillMover(MOVER *pMover);
MOVER *GetLiveMover(int index);
bool getMActorState(MOVER *psActor);
int GetMoverId(MOVER *pMover);
void SetMoverZ(MOVER *pMover, int y, uint32 zFactor);
void SetMoverZoverride(MOVER *pMover, uint32 zFactor);
void HideMover(MOVER *pMover, int sf = 0);
bool MoverHidden(MOVER *pMover);
bool MoverIs(MOVER *pMover);
bool MoverIsSWalking(MOVER *pMover);
bool MoverMoving(MOVER *pMover);
int GetWalkNumber(MOVER *pMover);
void UnHideMover(MOVER *pMover);
void DropMovers();
void PositionMover(MOVER *pMover, int x, int y);
void GetMoverPosition(MOVER *pMover, int *aniX, int *aniY);
void GetMoverMidTop(MOVER *pMover, int *aniX, int *aniY);
int GetMoverLeft(MOVER *pMover);
int GetMoverRight(MOVER *pMover);
int GetMoverTop(MOVER *pMover);
int GetMoverBottom(MOVER *pMover);
bool MoverIsInPolygon(MOVER *pMover, HPOLYGON hPoly);
void AlterMover(MOVER *pMover, SCNHANDLE film, AR_FUNCTION fn);
DIRECTION GetMoverDirection(MOVER *pMover);
int GetMoverScale(MOVER *pMover);
void SetMoverDirection(MOVER *pMover, DIRECTION dirn);
void SetMoverStanding(MOVER *pMover);
void SetMoverWalkReel(MOVER *pMover, DIRECTION reel, int scale, bool force);
MOVER *InMoverBlock(MOVER *pMover, int x, int y);
void RebootMovers();
bool IsMAinEffectPoly(int index);
void SetMoverInEffect(int index, bool tf);
void StopMover(MOVER *pMover);
/*----------------------------------------------------------------------*/
struct SAVED_MOVER {
int actorID;
int objX;
int objY;
SCNHANDLE hLastfilm;
SCNHANDLE walkReels[REQ_TOTAL_SCALES][4];
SCNHANDLE standReels[REQ_TOTAL_SCALES][4];
SCNHANDLE talkReels[REQ_TOTAL_SCALES][4];
bool bActive;
bool bHidden;
int brightness;
int startColor;
int paletteLength;
};
void SaveMovers(SAVED_MOVER *sMoverInfo);
void RestoreAuxScales(SAVED_MOVER *sMoverInfo);
MOVER *NextMover(MOVER *pMover);
/*----------------------------------------------------------------------*/
enum {
MAGICX = -101,
MAGICY = -102
};
/*----------------------------------------------------------------------*/
} // End of namespace Tinsel
#endif /* TINSEL_MOVERS_H */

608
engines/tinsel/multiobj.cpp Normal file
View File

@@ -0,0 +1,608 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* This file contains utilities to handle multi-part objects.
*/
#include "tinsel/background.h"
#include "tinsel/film.h"
#include "tinsel/multiobj.h"
#include "tinsel/handle.h"
#include "tinsel/object.h"
#include "tinsel/tinsel.h"
#include "tinsel/noir/sysreel.h"
namespace Tinsel {
const FRAME *MULTI_INIT::GetFrame() const {
return (const FRAME *)_vm->_handle->LockMem(FROM_32(hMulFrame));
}
/**
* Initialize 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
const FRAME *pFrame; // list of images for the multi-part object
if (FROM_32(pInitTbl->hMulFrame)) {
// we have a frame handle
pFrame = pInitTbl->GetFrame();
obj_init.hObjImg = READ_32(pFrame); // first objects shape
} else { // this must be a animation list for a NULL object
pFrame = nullptr;
obj_init.hObjImg = 0; // first objects shape
}
// init the object init table
obj_init.objFlags = (int)FROM_32(pInitTbl->mulFlags); // all objects have same flags
obj_init.objID = (int)FROM_32(pInitTbl->mulID); // all objects have same ID
obj_init.objX = (int)FROM_32(pInitTbl->mulX); // all objects have same X ani pos
obj_init.objY = (int)FROM_32(pInitTbl->mulY); // all objects have same Y ani pos
obj_init.objZ = (int)FROM_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_32(pFrame) != 0) {
// set next objects shape
obj_init.hObjImg = READ_32(pFrame);
// create next object and link to previous
pObj = pObj->pSlave = InitObject(&obj_init);
pFrame++;
}
}
// null end of list for final object
pObj->pSlave = nullptr;
// return master object
return pFirst;
}
OBJECT *InsertReelObj(const FREEL *reels) {
const MULTI_INIT *pmi = reels->GetMultiInit();
// Verify that there is an image defined
const FRAME *frame = pmi->GetFrame();
const IMAGE *image = (const IMAGE*)_vm->_handle->LockMem(*frame);
assert(image);
auto pInsObj = MultiInitObject(pmi);
MultiInsertObject(_vm->_bg->GetPlayfieldList(FIELD_STATUS), pInsObj);
return pInsObj; // Result
}
const FILM *GetSystemReelFilm(SysReel reelIndex) {
SCNHANDLE hFilm = _vm->_systemReel->get(reelIndex);
const FILM *pfilm = (const FILM *)_vm->_handle->LockMem(hFilm);
return pfilm;
}
OBJECT *InsertSystemReelObj(SysReel reelIndex) {
return InsertReelObj(GetSystemReelFilm(reelIndex)->reels);
}
/**
* Inserts the multi-part object onto the specified object list.
* @param pObjList List to insert multi-part object onto
* @param pInsObj Head of multi-part object to insert
*/
void MultiInsertObject(OBJECT **pObjList, OBJECT *pInsObj) {
// validate object pointer
assert(isValidObject(pInsObj));
// for all the objects that make up this multi-part
do {
// add next part to the specified list
InsertObject(pObjList, pInsObj);
// next obj in list
pInsObj = pInsObj->pSlave;
} while (pInsObj != NULL);
}
/**
* Deletes all the pieces of a multi-part object from the
* specified object list.
* @param pObjList List to delete multi-part object from
* @param pMultiObj Multi-part object to be deleted
*/
void MultiDeleteObject(OBJECT **pObjList, OBJECT *pMultiObj) {
// validate object pointer
assert(isValidObject(pMultiObj));
// for all the objects that make up this multi-part
do {
// delete object
DelObject(pObjList, pMultiObj);
// next obj in list
pMultiObj = pMultiObj->pSlave;
} while (pMultiObj != NULL);
}
/**
* Deletes all the pieces of a multi-part object from the
* specified playfield's object list, then sets the pointer to nullptr.
* @param which The playfield whos object list we delete from.
* @param pMultiObj Multi-part object to be deleted
*/
void MultiDeleteObjectIfExists(unsigned int playfield, OBJECT **pMultiObj) {
if (*pMultiObj) {
MultiDeleteObject(_vm->_bg->GetPlayfieldList(playfield), *pMultiObj);
*pMultiObj = nullptr;
}
}
/**
* Hides a multi-part object by giving each object a "NullImage"
* image pointer.
* @param pMultiObj Multi-part object to be hidden
*/
void MultiHideObject(OBJECT *pMultiObj) {
// validate object pointer
assert(isValidObject(pMultiObj));
// set master shape to null animation frame
pMultiObj->hShape = 0;
// change all objects
MultiReshape(pMultiObj);
}
/**
* Horizontally flip a multi-part object.
* @param pFlipObj Head of multi-part object to flip
*/
void MultiHorizontalFlip(OBJECT *pFlipObj) {
// validate object pointer
assert(isValidObject(pFlipObj));
// for all the objects that make up this multi-part
do {
// horizontally flip the next part
AnimateObjectFlags(pFlipObj, pFlipObj->flags ^ DMA_FLIPH,
pFlipObj->hImg);
// next obj in list
pFlipObj = pFlipObj->pSlave;
} while (pFlipObj != NULL);
}
/**
* Vertically flip a multi-part object.
* @param pFlipObj Head of multi-part object to flip
*/
void MultiVerticalFlip(OBJECT *pFlipObj) {
// validate object pointer
assert(isValidObject(pFlipObj));
// for all the objects that make up this multi-part
do {
// vertically flip the next part
AnimateObjectFlags(pFlipObj, pFlipObj->flags ^ DMA_FLIPV,
pFlipObj->hImg);
// next obj in list
pFlipObj = pFlipObj->pSlave;
} while (pFlipObj != NULL);
}
/**
* Adjusts the coordinates of a multi-part object. The adjustments
* take into account the orientation of the object.
* @param pMultiObj Multi-part object to be adjusted
* @param deltaX X adjustment
* @param deltaY Y adjustment
*/
void MultiAdjustXY(OBJECT *pMultiObj, int deltaX, int deltaY) {
// validate object pointer
assert(isValidObject(pMultiObj));
if (deltaX == 0 && deltaY == 0)
return; // ignore no change
if (TinselVersion <= 1) {
// *** This may be wrong!!!
if (pMultiObj->flags & DMA_FLIPH) {
// image is flipped horizontally - flip the x direction
deltaX = -deltaX;
}
if (pMultiObj->flags & DMA_FLIPV) {
// image is flipped vertically - flip the y direction
deltaY = -deltaY;
}
}
// for all the objects that make up this multi-part
do {
// signal a change in the object
pMultiObj->flags |= DMA_CHANGED;
// adjust the x position
pMultiObj->xPos += intToFrac(deltaX);
// adjust the y position
pMultiObj->yPos += intToFrac(deltaY);
// next obj in list
pMultiObj = pMultiObj->pSlave;
} while (pMultiObj != NULL);
}
/**
* Moves all the pieces of a multi-part object by the specified
* amount. Does not take into account the objects orientation.
* @param pMultiObj Multi-part object to be adjusted
* @param deltaX X movement
* @param deltaY Y movement
*/
void MultiMoveRelXY(OBJECT *pMultiObj, int deltaX, int deltaY) {
// validate object pointer
assert(isValidObject(pMultiObj));
if (deltaX == 0 && deltaY == 0)
return; // ignore no change
// for all the objects that make up this multi-part
do {
// signal a change in the object
pMultiObj->flags |= DMA_CHANGED;
// adjust the x position
pMultiObj->xPos += intToFrac(deltaX);
// adjust the y position
pMultiObj->yPos += intToFrac(deltaY);
// next obj in list
pMultiObj = pMultiObj->pSlave;
} while (pMultiObj != NULL);
}
/**
* Sets the x & y anim position of all pieces of a multi-part object.
* @param pMultiObj Multi-part object whose position is to be changed
* @param newAniX New x animation position
* @param newAniY New y animation position
*/
void MultiSetAniXY(OBJECT *pMultiObj, int newAniX, int newAniY) {
int curAniX, curAniY; // objects current animation position
// validate object pointer
assert(isValidObject(pMultiObj));
// get master objects current animation position
GetAniPosition(pMultiObj, &curAniX, &curAniY);
// calc difference between current and new positions
newAniX -= curAniX;
newAniY -= curAniY;
// move all pieces by the difference
MultiMoveRelXY(pMultiObj, newAniX, newAniY);
}
/**
* Sets the x & y anim position of all pieces of a multi-part object, as well as the Z Position.
* @param pMultiObj Multi-part object whose position is to be changed
* @param newAniX New x animation position
* @param newAniY New y animation position
*/
void MultiSetAniXYZ(OBJECT *pMultiObj, int newAniX, int newAniY, int zPosition) {
MultiSetAniXY(pMultiObj, newAniX, newAniY);
MultiSetZPosition(pMultiObj, zPosition);
}
/**
* Sets the x anim position of all pieces of a multi-part object.
* @param pMultiObj Multi-part object whose x position is to be changed
* @param newAniX New x animation position
*/
void MultiSetAniX(OBJECT *pMultiObj, int newAniX) {
int curAniX, curAniY; // objects current animation position
// validate object pointer
assert(isValidObject(pMultiObj));
// get master objects current animation position
GetAniPosition(pMultiObj, &curAniX, &curAniY);
// calc x difference between current and new positions
newAniX -= curAniX;
curAniY = 0;
// move all pieces by the difference
MultiMoveRelXY(pMultiObj, newAniX, curAniY);
}
/**
* Sets the y anim position of all pieces of a multi-part object.
* @param pMultiObj Multi-part object whose x position is to be changed
* @param newAniX New y animation position
*/
void MultiSetAniY(OBJECT *pMultiObj, int newAniY) {
int curAniX, curAniY; // objects current animation position
// validate object pointer
assert(isValidObject(pMultiObj));
// get master objects current animation position
GetAniPosition(pMultiObj, &curAniX, &curAniY);
// calc y difference between current and new positions
curAniX = 0;
newAniY -= curAniY;
// move all pieces by the difference
MultiMoveRelXY(pMultiObj, curAniX, newAniY);
}
/**
* Sets the Z position of all pieces of a multi-part object.
* @param pMultiObj Multi-part object to be adjusted
* @param newZ New Z order
*/
void MultiSetZPosition(OBJECT *pMultiObj, int newZ) {
// validate object pointer
assert(isValidObject(pMultiObj));
// for all the objects that make up this multi-part
do {
// signal a change in the object
pMultiObj->flags |= DMA_CHANGED;
// set the new z position
pMultiObj->zPos = newZ;
// next obj in list
pMultiObj = pMultiObj->pSlave;
} while (pMultiObj != NULL);
}
/**
* Reshape a multi-part object.
* @param pMultiObj Multi-part object to re-shape
*/
void MultiReshape(OBJECT *pMultiObj) {
SCNHANDLE hFrame;
// validate object pointer
assert(isValidObject(pMultiObj));
// get objects current anim frame
hFrame = pMultiObj->hShape;
if (hFrame != 0 && hFrame != pMultiObj->hMirror) {
// a valid shape frame which is different from previous
// get pointer to frame
const FRAME *pFrame = (const FRAME *)_vm->_handle->LockMem(hFrame);
// update previous
pMultiObj->hMirror = hFrame;
while (READ_32(pFrame) != 0 && pMultiObj != NULL) {
// a normal image - update the current object with this image
AnimateObject(pMultiObj, READ_32(pFrame));
// move to next image for this frame
pFrame++;
// move to next part of object
pMultiObj = pMultiObj->pSlave;
}
// null the remaining object parts
while (pMultiObj != NULL) {
// set a null image for this object part
AnimateObject(pMultiObj, 0);
// move to next part of object
pMultiObj = pMultiObj->pSlave;
}
} else if (hFrame == 0) {
// update previous
pMultiObj->hMirror = hFrame;
// null all the object parts
while (pMultiObj != NULL) {
// set a null image for this object part
AnimateObject(pMultiObj, 0);
// move to next part of object
pMultiObj = pMultiObj->pSlave;
}
}
}
/**
* Returns the left-most point of a multi-part object.
* @param pMulti Multi-part object
*/
int MultiLeftmost(OBJECT *pMulti) {
int left;
// validate object pointer
assert(isValidObject(pMulti));
// init leftmost point to first object
left = fracToInt(pMulti->xPos);
// for all the objects in this multi
while ((pMulti = pMulti->pSlave) != NULL) {
if (pMulti->hImg != 0) {
// non null object part
if (fracToInt(pMulti->xPos) < left)
// this object is further left
left = fracToInt(pMulti->xPos);
}
}
// return left-most point
return left;
}
/**
* Returns the right-most point of a multi-part object.
* @param pMulti Multi-part object
*/
int MultiRightmost(OBJECT *pMulti) {
int right;
// validate object pointer
assert(isValidObject(pMulti));
// init right-most point to first object
right = fracToInt(pMulti->xPos) + pMulti->width;
// for all the objects in this multi
while ((pMulti = pMulti->pSlave) != NULL) {
if (pMulti->hImg != 0) {
// non null object part
if (fracToInt(pMulti->xPos) + pMulti->width > right)
// this object is further right
right = fracToInt(pMulti->xPos) + pMulti->width;
}
}
// return right-most point
return right - 1;
}
/**
* Returns the highest point of a multi-part object.
* @param pMulti Multi-part object
*/
int MultiHighest(OBJECT *pMulti) {
int highest;
// validate object pointer
assert(isValidObject(pMulti));
// init highest point to first object
highest = fracToInt(pMulti->yPos);
// for all the objects in this multi
while ((pMulti = pMulti->pSlave) != NULL) {
if (pMulti->hImg != 0) {
// non null object part
if (fracToInt(pMulti->yPos) < highest)
// this object is higher
highest = fracToInt(pMulti->yPos);
}
}
// return highest point
return highest;
}
/**
* Returns the lowest point of a multi-part object.
* @param pMulti Multi-part object
*/
int MultiLowest(OBJECT *pMulti) {
int lowest;
// validate object pointer
assert(isValidObject(pMulti));
// init lowest point to first object
lowest = fracToInt(pMulti->yPos) + pMulti->height;
// for all the objects in this multi
while ((pMulti = pMulti->pSlave) != NULL) {
if (pMulti->hImg != 0) {
// non null object part
if (fracToInt(pMulti->yPos) + pMulti->height > lowest)
// this object is lower
lowest = fracToInt(pMulti->yPos) + pMulti->height;
}
}
// return lowest point
return lowest - 1;
}
/**
* Returns TRUE if the object currently has an image.
* @param pMulti Multi-part object
*/
bool MultiHasShape(OBJECT *pMulti) {
return (pMulti->hShape != 0);
}
/**
* Bodge for text on movies. Makes sure it appears for it's lifetime.
* @param pMultiObj Multi-part object to be adjusted
*/
void MultiForceRedraw(OBJECT *pMultiObj) {
// validate object pointer
assert(isValidObject(pMultiObj));
// for all the objects that make up this multi-part
do {
// signal a change in the object
pMultiObj->flags |= DMA_CHANGED;
// next obj in list
pMultiObj = pMultiObj->pSlave;
} while (pMultiObj != NULL);
}
} // End of namespace Tinsel

146
engines/tinsel/multiobj.h Normal file
View File

@@ -0,0 +1,146 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* Multi-part object definitions
*/
#ifndef TINSEL_MULTIOBJ_H // prevent multiple includes
#define TINSEL_MULTIOBJ_H
#include "tinsel/dw.h"
#include "tinsel/object.h"
namespace Tinsel {
struct OBJECT;
#include "common/pack-start.h" // START STRUCT PACKING
/**
* multi-object initialisation structure (parallels OBJ_INIT struct)
*/
struct MULTI_INIT {
SCNHANDLE hMulFrame; ///< multi-objects shape - NULL terminated list of IMAGE structures
int32 mulFlags; ///< multi-objects flags
int32 mulID; ///< multi-objects id
int32 mulX; ///< multi-objects initial x ani position
int32 mulY; ///< multi-objects initial y ani position
int32 mulZ; ///< multi-objects initial z position
uint32 otherFlags; ///< multi-objects Tinsel 2 - other flags
const FRAME *GetFrame() const;
} PACKED_STRUCT;
#include "common/pack-end.h" // END STRUCT PACKING
/*----------------------------------------------------------------------*\
|* Multi Object Function Prototypes *|
\*----------------------------------------------------------------------*/
OBJECT *MultiInitObject( // Initialize a multi-part object
const MULTI_INIT *pInitTbl); // pointer to multi-object initialisation table
void MultiInsertObject( // Insert a multi-part object onto a object list
OBJECT **pObjList, // list to insert multi-part object onto
OBJECT *pInsObj); // head of multi-part object to insert
void MultiDeleteObject( // Delete all the pieces of a multi-part object
OBJECT **pObjList, // list to delete multi-part object from
OBJECT *pMultiObj); // multi-part object to be deleted
void MultiDeleteObjectIfExists( // Delete all the pieces of a multi-part object (if it exists)
unsigned int playfield, // playfield to delete the objects from
OBJECT **pMultiObj); // multi-part object to be deleted
void MultiHideObject( // Hide a multi-part object
OBJECT *pMultiObj); // multi-part object to be hidden
void MultiHorizontalFlip( // Hortizontally flip a multi-part object
OBJECT *pFlipObj); // head of multi-part object to flip
void MultiVerticalFlip( // Vertically flip a multi-part object
OBJECT *pFlipObj); // head of multi-part object to flip
void MultiAdjustXY( // Adjust coords of a multi-part object. Takes into account the orientation
OBJECT *pMultiObj, // multi-part object to be adjusted
int deltaX, // x adjustment
int deltaY); // y adjustment
void MultiMoveRelXY( // Move multi-part object relative. Does not take into account the orientation
OBJECT *pMultiObj, // multi-part object to be moved
int deltaX, // x movement
int deltaY); // y movement
void MultiSetAniXY( // Set the x & y anim position of a multi-part object
OBJECT *pMultiObj, // multi-part object whose position is to be changed
int newAniX, // new x animation position
int newAniY); // new y animation position
void MultiSetAniXYZ( // Set the x & y anim position of a multi-part object
OBJECT *pMultiObj, // multi-part object whose position is to be changed
int newAniX, // new x animation position
int newAniY, // new y animation position
int zPosition); // new Z order
void MultiSetAniX( // Set the x anim position of a multi-part object
OBJECT *pMultiObj, // multi-part object whose x position is to be changed
int newAniX); // new x animation position
void MultiSetAniY( // Set the y anim position of a multi-part object
OBJECT *pMultiObj, // multi-part object whose y position is to be adjusted
int newAniY); // new y animation position
void MultiSetZPosition( // Sets the z position of a multi-part object
OBJECT *pMultiObj, // multi-part object to be adjusted
int newZ); // new Z order
void MultiReshape( // Reshape a multi-part object
OBJECT *pMultiObj); // multi-part object to re-shape
Common::Rect MultiBounds( // Returns the bounds of a multi-part object
OBJECT *pMulti); // multi-part object
int MultiLeftmost( // Returns the left-most point of a multi-part object
OBJECT *pMulti); // multi-part object
int MultiRightmost( // Returns the right-most point of a multi-part object
OBJECT *pMulti); // multi-part object
int MultiHighest( // Returns the highest point of a multi-part object
OBJECT *pMulti); // multi-part object
int MultiLowest( // Returns the lowest point of a multi-part object
OBJECT *pMulti); // multi-part object
bool MultiHasShape( // Returns TRUE if the object currently has an image
OBJECT *pMulti); // multi-part object
void MultiForceRedraw(
OBJECT *pMultiObj); // multi-part object to be forced
struct FREEL;
OBJECT *InsertReelObj(const FREEL *reels);
struct FILM;
enum class SysReel;
const FILM *GetSystemReelFilm(SysReel reelIndex);
OBJECT *InsertSystemReelObj(SysReel reelIndex);
} // End of namespace Tinsel
#endif // TINSEL_MULTIOBJ_H

1147
engines/tinsel/music.cpp Normal file

File diff suppressed because it is too large Load Diff

207
engines/tinsel/music.h Normal file
View File

@@ -0,0 +1,207 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
// Music class
#ifndef TINSEL_MUSIC_H
#define TINSEL_MUSIC_H
#include "audio/midiplayer.h"
#include "audio/audiostream.h"
#include "audio/mixer.h"
#include "common/mutex.h"
class MidiParser;
namespace Tinsel {
class Music {
public:
Music() : _currentMidi(0), _currentLoop(false) {
_midiBuffer.pDat = nullptr;
_midiBuffer.size = 0;
}
bool PlayMidiSequence( // Plays the specified MIDI sequence through the sound driver
uint32 dwFileOffset, // handle of MIDI sequence data
bool bLoop); // Whether to loop the sequence
bool MidiPlaying(); // Returns TRUE if a Midi tune is currently playing
bool StopMidi(); // Stops any currently playing midi
void SetMidiVolume( // Sets the volume of the MIDI music. Returns the old volume
int vol); // new volume - 0..MAXMIDIVOL
int GetMidiVolume();
void OpenMidiFiles();
void DeleteMidiBuffer();
void CurrentMidiFacts(SCNHANDLE *pMidi, bool *pLoop);
void RestoreMidiFacts(SCNHANDLE Midi, bool Loop);
int GetTrackNumber(SCNHANDLE hMidi);
SCNHANDLE GetTrackOffset(int trackNumber);
uint8 *GetMidiBuffer() { return _midiBuffer.pDat; }
uint8* ResizeMidiBuffer(uint32 newSize) {
if (_midiBuffer.size < newSize) {
_midiBuffer.pDat = (byte*)realloc(_midiBuffer.pDat, newSize);
assert(_midiBuffer.pDat);
}
return _midiBuffer.pDat;
}
void dumpMusic();
private:
// sound buffer structure used for MIDI data and samples
struct SOUND_BUFFER {
uint8 *pDat; // pointer to actual buffer
uint32 size; // size of the buffer
};
// MIDI buffer
SOUND_BUFFER _midiBuffer;
SCNHANDLE _currentMidi;
bool _currentLoop;
// We allocate 155 entries because that's the maximum, used in the SCN version
SCNHANDLE _midiOffsets[155];
};
class MidiMusicPlayer : public Audio::MidiPlayer {
public:
MidiMusicPlayer(TinselEngine *vm);
void setVolume(int volume) override;
void playMIDI(uint32 size, bool loop);
// void stop();
void pause() override;
void resume() override;
// MidiDriver_BASE interface implementation
void send(uint32 b) override;
// The original sets the "sequence timing" to 109 Hz, whatever that
// means. The default is 120.
uint32 getBaseTempo() { return _driver ? (109 * _driver->getBaseTempo()) / 120 : 0; }
bool _milesAudioMode;
private:
void playXMIDI(uint32 size, bool loop);
void playSEQ(uint32 size, bool loop);
};
class PCMMusicPlayer : public Audio::AudioStream {
public:
PCMMusicPlayer();
~PCMMusicPlayer() override;
bool isPlaying() const;
bool isDimmed() const;
void getTunePlaying(void *voidPtr, int length);
void restoreThatTune(void *voidPtr);
void setMusicSceneDetails(SCNHANDLE hScript, SCNHANDLE hSegment, const char *fileName);
void setVolume(int volume);
void startPlay(int id);
void stopPlay();
bool getMusicTinselDimmed() const;
void dim(bool bTinselDim);
void unDim(bool bTinselUnDim);
void dimIteration();
void startFadeOut(int ticks);
void fadeOutIteration();
int readBuffer(int16 *buffer, const int numSamples) override;
bool isStereo() const override;
bool endOfData() const override { return _end; }
bool endOfStream() const override { return false; }
int getRate() const override;
protected:
enum State {
S_IDLE,
S_NEW,
S_MID,
S_END1,
S_END2,
S_END3,
S_NEXT,
S_STOP
};
Audio::SoundHandle _handle;
Audio::AudioStream *_curChunk;
Common::Mutex _mutex;
bool _end;
int _silenceSamples;
State _state, _mState;
bool _forcePlay;
int32 _scriptNum;
int32 _scriptIndex;
SCNHANDLE _hScript;
SCNHANDLE _hSegment;
Common::Path _filename;
Common::File _file;
uint8 _volume;
bool _dimmed;
bool _dimmedTinsel;
uint8 _dimmedVolume;
int _dimIteration;
int _dimPosition;
uint8 _fadeOutVolume;
int _fadeOutIteration;
void play();
void stop();
void setVol(uint8 volume);
bool getNextChunk();
void loadMusicFromSegment(int segmentNum);
void loadADPCMMusicFromSegment(int segmentNum);
void loadMP3MusicFromSegment(int segmentNum);
};
} // End of namespace Tinsel
#endif

View File

@@ -0,0 +1,125 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* Prototypes of actor functions
*/
#include "common/textconsole.h"
#include "tinsel/noir/lzss.h"
namespace Tinsel {
static byte HIGH_BITS(byte byteValue, int numBits) {
unsigned int mask = ((1 << numBits) - 1) << (8 - numBits);
return (byteValue & mask) >> (8 - numBits);
}
static byte LOW_BITS(byte byteValue, int numBits) {
unsigned int mask = ((1 << numBits) - 1);
return byteValue & mask;
}
int decompressLZSS(Common::SeekableReadStream &input, byte *output) {
static const int kDictionarySize = 4096;
byte dictionary[kDictionarySize] = {};
int dictionaryOffset = 1;
int outputOffset = 0;
byte *data = new byte[input.size()];
input.read(data, input.size());
unsigned int offset = 0;
int bitShift = 0;
int bytesWritten = 0;
while (true) {
byte value = data[offset];
byte bitMask = 0x80 >> bitShift++;
// First bit:
// 0 -> Copy data from dictionary
// 1 -> Copy raw byte from input
bool useRawByte = value & bitMask;
if (bitShift == 8) {
bitShift = 0;
offset++;
}
if (!useRawByte) {
unsigned int bitsFromFirst = 8 - bitShift;
unsigned int bitsFromLast = 16 - 8 - bitsFromFirst;
// The dictionary lookup is 16 bit:
// 12 bits for the offset
// 4 bits for the run-length
// Combined with the first bit this makes for 17 bits,
// So we will be reading from three bytes, except when
// the first bit was read from the end of a byte, then
// bitShift will be 0, and bitsFromLast will be 8.
// We make the assumption that we can dereference the third byte
// even if we aren't using it. We will check "offset" after decompression
// to verify this assumption.
unsigned int byte1 = LOW_BITS(data[offset], bitsFromFirst);
unsigned int byte2 = data[offset + 1];
unsigned int byte3 = HIGH_BITS(data[offset + 2], bitsFromLast);
unsigned int lookup = (byte1 << (8 + bitsFromLast)) | (byte2 << bitsFromLast) | byte3;
int lookupOffset = (lookup >> 4) & 0xFFF;
if (lookupOffset == 0) {
break;
}
int lookupRunLength = (lookup & 0xF) + 2;
for (int j = 0; j < lookupRunLength; j++) {
output[outputOffset++] = dictionary[(lookupOffset + j) % kDictionarySize];
dictionary[dictionaryOffset++] = dictionary[(lookupOffset + j) % kDictionarySize];
dictionaryOffset %= kDictionarySize;
}
offset += 2;
bytesWritten += lookupRunLength;
} else {
// Raw byte, but since we spent a bit first,
// we must reassemble it from potentially two bytes.
unsigned int bitsFromFirst = 8 - bitShift;
unsigned int bitsFromLast = 8 - bitsFromFirst;
byte byteValue = LOW_BITS(data[offset], bitsFromFirst) << bitsFromLast;
byteValue |= HIGH_BITS(data[offset + 1], bitsFromLast);
offset++;
output[outputOffset++] = byteValue;
dictionary[dictionaryOffset++] = byteValue;
dictionaryOffset %= kDictionarySize;
bytesWritten++;
}
}
delete[] data;
if ((int32)offset > input.size()) {
error("Read too far during decompression");
}
return bytesWritten;
}
}

View File

@@ -0,0 +1,34 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* Prototypes of actor functions
*/
#ifndef TINSEL_NOIR_LZSS_H
#define TINSEL_NOIR_LZSS_H
#include "common/stream.h"
namespace Tinsel {
int decompressLZSS(Common::SeekableReadStream &input, byte *output);
}
#endif

View File

@@ -0,0 +1,322 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "tinsel/noir/notebook.h"
#include "tinsel/background.h"
#include "tinsel/dialogs.h"
#include "tinsel/film.h"
#include "tinsel/handle.h"
#include "tinsel/multiobj.h"
#include "tinsel/noir/sysreel.h"
#include "tinsel/pdisplay.h"
#include "tinsel/polygons.h"
#include "tinsel/timers.h"
namespace Tinsel {
Notebook::Notebook() {
_polygons = instantiateNoteBookPolygons();
}
Notebook::~Notebook() {
delete _polygons;
}
void Notebook::addHyperlink(int32 id1, int32 id2) {
auto *invObject = _vm->_dialogs->getInvObjectT3(id1);
if (invObject->getTitle() != 0) {
error("A clue can only be hyperlinked if it only has one title!");
return;
}
invObject = _vm->_dialogs->getInvObjectT3(id2);
if (invObject->getTitle() != 0) {
error("A clue can only be hyperlinked if it only has one title!");
return;
}
uint32 i;
for (i = 0; i < MAX_HYPERS; ++i) {
int32 curr_id1 = _hyperlinks[i].id1;
if (curr_id1 == 0) {
_hyperlinks[i].id1 = id1;
_hyperlinks[i].id2 = id2;
return;
}
if ((curr_id1 == id1) || (id1 == _hyperlinks[i].id2)) {
if ((curr_id1 != id2) && (id2 != _hyperlinks[i].id2)) {
error("A clue/title can only be hyperlinked to one other clue/title!");
}
return;
}
}
// No free hyperlink entry was found
error("Too many hyperlinks");
}
void Notebook::clearNotebookPage() {
if (_prevPage != -1) {
_pages[_prevPage].clear();
}
_prevPage = -1;
_pages[_currentPage].clear();
}
void Notebook::refresh() {
auto reel = (_currentPage == 0 ? SysReel::NOTEPAD_CLOSED : SysReel::NOTEPAD_OPEN);
auto film = GetSystemReelFilm(reel);
InitStepAnimScript(&_anim, _object, film->reels->script, ONE_SECOND / film->frate);
clearNotebookPage();
if (_currentPage != 0) {
_pages[_currentPage].fillIn();
}
}
int Notebook::addTitle(const InventoryObjectT3 &invObject) {
int id = invObject.getId();
assert(invObject.isNotebookTitle());
for (uint32 i = 0; i < _numPages; i++) {
if (_pages[i].getTitle() == id) {
return i;
}
}
int linkedFromPage = invObject.getUnknown();
// 0 page is the closed notebook, has no entries.
if (linkedFromPage != 0) {
// Allocate a line on the linked from page.
assert(_pages[linkedFromPage].getTitle() != 0);
_pages[linkedFromPage].addLine(id);
}
int pageIndex = _numPages++;
_pages[pageIndex].setTitle(id);
return pageIndex;
}
void Notebook::addClue(const InventoryObjectT3 &invObject) {
if (invObject.getUnknown() == 0) {
// This affects two clues, that should get special treatment.
warning("TODO: Handle clues with no parent page");
return;
}
// Add title if missing, otherwise just get the page it's on.
auto titleObject = _vm->_dialogs->getInvObjectT3(invObject.getUnknown());
int pageIndex = addTitle(*titleObject);
_pages[pageIndex].addLine(invObject.getId());
if (invObject.getTitle() != 0) {
auto secondTitleObject = _vm->_dialogs->getInvObjectT3(invObject.getTitle());
pageIndex = addTitle(*secondTitleObject);
_pages[pageIndex].addLine(invObject.getId());
}
}
void Notebook::addClue(int id) {
auto invObject = _vm->_dialogs->getInvObjectT3(id);
if (invObject->isNotebookTitle()) {
addTitle(*invObject);
} else {
addClue(*invObject);
}
}
int Notebook::getPageWithTitle(int id) {
for (uint32 i = 0; i < _numPages; i++) {
if (_pages[i].getTitle() == id) {
return i;
}
}
return -1;
}
void Notebook::crossClue(int id) {
auto invObject = _vm->_dialogs->getInvObjectT3(id);
if (invObject->isNotebookTitle()) {
return;
}
int titles[2] = {
invObject->getUnknown(),
invObject->getTitle()
};
for (int i = 0; i < 2; i++) {
if (titles[i] == 0) {
continue;
}
int page = getPageWithTitle(titles[i]);
if (page != -1) {
_pages[page].crossClue(id);
}
}
}
void InitNotebookAnim(OBJECT **obj, ANIM &anim, SysReel reel, int zPosition) {
auto film = GetSystemReelFilm(reel);
MultiDeleteObjectIfExists(FIELD_STATUS, obj);
*obj = InsertReelObj(film->reels);
MultiSetZPosition(*obj, zPosition);
InitStepAnimScript(&anim, *obj, film->reels->script, ONE_SECOND / film->frate);
}
void Notebook::setNextPage(int pageIndex) {
assert(_prevPage == -1 || _prevPage == (int32)_currentPage); // Check that we've cleaned any outstanding page.
_prevPage = _currentPage;
_currentPage = pageIndex;
}
void Notebook::pageFlip(bool up) {
int nextPage = _currentPage + (up ? -1 : 1);
if (nextPage <= 0) {
setNextPage(0);
refresh();
return;
} else if (nextPage == 1) {
// TODO: Should possibly just call whatever function we use to open.
InitNotebookAnim(&_object, _anim, SysReel::NOTEPAD_OPENING, Z_INV_RFRAME);
_state = BOOKSTATE::OPEN_ANIMATING;
setNextPage(nextPage);
return;
}
setNextPage(nextPage);
SysReel reel = (up ? SysReel::NOTEPAD_FLIPUP : SysReel::NOTEPAD_FLIPDOWN);
InitNotebookAnim(&_pageObject, _pageAnim, reel, 19);
_state = BOOKSTATE::PAGEFLIP;
}
void Notebook::show(bool isOpen) {
auto reel = (isOpen ? SysReel::NOTEPAD_OPEN : SysReel::NOTEPAD_OPENING);
InitNotebookAnim(&_object, _anim, reel, Z_INV_MFRAME);
_state = (isOpen ? BOOKSTATE::OPENED : BOOKSTATE::OPEN_ANIMATING);
setNextPage(1);
refresh();
DisableTags(); // Tags disabled in Notebook
DisablePointing(); // Pointing disabled in Notebook
}
bool Notebook::isOpen() const {
return _state != BOOKSTATE::CLOSED;
}
void Notebook::close() {
clearNotebookPage();
MultiDeleteObjectIfExists(FIELD_STATUS, &_object);
MultiDeleteObjectIfExists(FIELD_STATUS, &_pageObject);
_state = BOOKSTATE::CLOSED;
if (_vm->_dialogs->inventoryOrNotebookActive()) {
EnablePointing();
EnableTags();
}
}
void Notebook::stepAnimScripts() {
if (_state == BOOKSTATE::OPEN_ANIMATING) {
auto state = StepAnimScript(&_anim);
if (state == ScriptFinished) {
_state = BOOKSTATE::OPENED;
refresh();
}
}
if (_state == BOOKSTATE::PAGEFLIP) {
auto state = StepAnimScript(&_pageAnim);
if (state == ScriptFinished) {
MultiDeleteObjectIfExists(FIELD_STATUS, &_pageObject);
_state = BOOKSTATE::OPENED;
refresh();
}
}
}
int32 Notebook::GetPointedClue(const Common::Point &point) const {
if (_currentPage == 0 || _currentPage > _numPages) {
return 0;
}
return _pages[_currentPage].getClueForLine(_polygons->lineHit(point));
}
bool Notebook::handlePointer(const Common::Point &point) {
if (!isOpen()) {
return 0;
}
auto inside = _polygons->isInsideNotebook(point);
if (inside) {
auto hit = _polygons->lineHit(point);
_pages[_currentPage].handlePointAtLine(hit);
return true; // We handled the pointer
}
return false;
}
bool Notebook::handleEvent(PLR_EVENT pEvent, const Common::Point &coOrds) {
if (!isOpen()) { // TODO: Clicking outside should close the notebook
return false;
}
auto inside = _polygons->isInsideNotebook(coOrds);
switch(pEvent) {
case PLR_ACTION:
if (inside) {
return true;
}
return false;
case PLR_LOOK:
if (inside) {
return true;
}
return false;
case PLR_WALKTO: {
// Handle clue-clicks
auto poly = _polygons->mostSpecificHit(coOrds);
switch (poly) {
case NoteBookPoly::NEXT:
handleEvent(PLR_PGUP, coOrds);
return true;
case NoteBookPoly::PREV:
handleEvent(PLR_PGDN, coOrds);
return true;
case NoteBookPoly::NONE:
handleEvent(PLR_ESCAPE, coOrds);
return true;
default:
return true;
}
}
case PLR_ESCAPE:
close();
return true;
case PLR_PGUP:
pageFlip(true);
return true;
case PLR_PGDN:
pageFlip(false);
return true;
case PLR_HOME:
case PLR_END:
default:
return false;
}
return false;
}
} // End of namespace Tinsel

View File

@@ -0,0 +1,118 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef TINSEL_NOTEBOOK_H // prevent multiple includes
#define TINSEL_NOTEBOOK_H
#include "common/scummsys.h"
#include "notebook_page.h"
#include "tinsel/anim.h"
#include "tinsel/events.h"
#include "tinsel/object.h"
namespace Tinsel {
// links two clue/title objects together
struct HYPERLINK {
int32 id1 = 0;
int32 id2 = 0;
};
// 6 bytes large
struct ENTRY {
int32 id;
bool active;
int32 page1;
int32 indexOnPage1;
int32 page2;
int32 indexOnPage2;
};
enum class BOOKSTATE {
CLOSED = 0,
OPEN_UNKNOWN = 1,
OPEN_ANIMATING = 2,
OPENED = 3,
PAGEFLIP = 4
};
class NoteBookPolygons;
class InventoryObjectT3;
class Notebook {
public:
Notebook();
~Notebook();
// Adds a connection between a clue/title
void addHyperlink(int32 id1, int32 id2);
void addClue(int id);
void crossClue(int id);
// Called within InventoryProcess loop
void redraw();
// Called from OPENNOTEBOOK
void show(bool isOpen);
bool isOpen() const;
void close();
bool handlePointer(const Common::Point &point);
bool handleEvent(PLR_EVENT pEvent, const Common::Point &coOrds);
void stepAnimScripts();
void refresh();
NoteBookPolygons *_polygons = nullptr;
private:
int addTitle(const InventoryObjectT3 &invObject);
void addClue(const InventoryObjectT3 &invObject);
int getPageWithTitle(int id);
void pageFlip(bool up);
int32 GetPointedClue(const Common::Point &point) const;
void clearNotebookPage();
void setNextPage(int pageIndex);
const static uint32 MAX_PAGES = 0x15;
const static uint32 MAX_HYPERS = 0xf;
HYPERLINK _hyperlinks[MAX_HYPERS];
uint32 _numPages = 1;
int32 _prevPage = -1;
uint32 _currentPage = 1;
NotebookPage _pages[MAX_PAGES] = {};
ANIM _anim = {};
OBJECT *_object = nullptr;
ANIM _pageAnim = {};
OBJECT *_pageObject = nullptr;
BOOKSTATE _state = BOOKSTATE::CLOSED;
};
} // End of namespace Tinsel
#endif

View File

@@ -0,0 +1,162 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "tinsel/noir/notebook_page.h"
#include "tinsel/dialogs.h"
#include "tinsel/dw.h"
#include "tinsel/film.h"
#include "tinsel/handle.h"
#include "tinsel/multiobj.h"
#include "tinsel/polygons.h"
#include "tinsel/timers.h"
#include "tinsel/tinsel.h"
#include "tinsel/noir/sysreel.h"
namespace Tinsel {
void NotebookLine::clear() {
MultiDeleteObjectIfExists(FIELD_STATUS, &_obj);
}
bool HasReelFrame(SCNHANDLE pReel) {
if (pReel) {
const FREEL* reel = (const FREEL*)_vm->_handle->LockMem(pReel);
if (reel && reel->mobj) {
const MULTI_INIT* pmi = reel->GetMultiInit();
if (pmi) {
return pmi->GetFrame() != nullptr;
}
}
}
return false;
}
int FindReelIndexForEntry(const FILM *pFilm, int pageLine) {
if (HasReelFrame(pFilm->reels[pageLine].mobj)) {
return pageLine;
}
for (int i = pageLine; i < pFilm->numreels; i++) {
if (HasReelFrame(pFilm->reels[i].mobj)) {
return i;
}
}
for (int i = pageLine; i >= 0; i--) {
if (HasReelFrame(pFilm->reels[i].mobj)) {
return i;
}
}
return -1;
}
void NotebookLine::fillIn(int pageLine) {
const FILM *pFilm = _vm->_dialogs->getObjectFilm(_id);
if (!pFilm)
return;
int reelIndex = FindReelIndexForEntry(pFilm, pageLine);
assert(reelIndex >= 0);
const FREEL *reel = &pFilm->reels[reelIndex];
MultiDeleteObjectIfExists(FIELD_STATUS, &_obj);
_obj = InsertReelObj(reel);
MultiSetZPosition(_obj, 17);
InitStepAnimScript(&_anim, _obj, pFilm->reels[reelIndex].script, ONE_SECOND / pFilm->frate);
if (_crossedOut) {
auto scribbleFilm = GetSystemReelFilm(SysReel::SCRIBBLES);
_scribbles = InsertReelObj(&scribbleFilm->reels[reelIndex]);
MultiSetZPosition(_scribbles, 18);
InitStepAnimScript(&_scribbleAnim, _scribbles, scribbleFilm->reels[reelIndex].script, ONE_SECOND / pFilm->frate);
}
}
void NotebookLine::crossOut() {
_crossedOut = true;
}
void NotebookPage::handlePointAtLine(int line) {
auto objId = getClueForLine(line);
if (objId != 0 && objId != _pointedClue) {
auto obj = _vm->_dialogs->getInvObject(objId);
_vm->_dialogs->invPointEvent(obj, -1);
_pointedClue = objId;
}
}
int NotebookPage::indexOfClue(int id) const {
for (uint32 i = 0; i < _numLines; i++) {
if (_lines[i]._id == id) {
return i;
}
}
return -1;
}
bool NotebookPage::containsClue(int id) {
return indexOfClue(id) != -1;
}
void NotebookPage::crossClue(int id) {
int index = indexOfClue(id);
assert(index != -1);
_lines[index].crossOut();
}
void NotebookPage::addLine(int id) {
if (containsClue(id)) {
return;
}
assert(_numLines < MAX_ENTRIES_PER_PAGE);
_lines[_numLines++]._id = id;
}
void NotebookPage::setTitle(int id) {
_lines[0]._id = id;
if (_numLines == 0) {
_numLines++;
}
}
int32 NotebookPage::getTitle() const {
return _lines[0]._id;
}
void NotebookPage::fillIn() {
for (uint32 i = 0; i < _numLines; i++) {
_lines[i].fillIn(i);
}
}
void NotebookPage::clear() {
for (uint32 i = 0; i < _numLines; i++) {
_lines[i].clear();
}
_pointedClue = -1;
}
int NotebookPage::getClueForLine(int line) const {
if (line >= (int)_numLines) {
return 0;
}
return _lines[line]._id;
}
} // End of namespace Tinsel

View File

@@ -0,0 +1,69 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef TINSEL_NOTEBOOK_PAGE_H // prevent multiple includes
#define TINSEL_NOTEBOOK_PAGE_H
#include "common/scummsys.h"
#include "tinsel/anim.h"
#include "tinsel/tinsel.h"
#include "tinsel/object.h"
namespace Tinsel {
class NotebookLine {
public:
int _id = 0;
void crossOut();
void clear();
void fillIn(int pageLine);
private:
bool _crossedOut = false;
ANIM _anim = {};
OBJECT *_obj = nullptr;
OBJECT *_scribbles = nullptr;
ANIM _scribbleAnim = {};
};
class NotebookPage {
public:
bool containsClue(int id);
void crossClue(int id);
void addLine(int id);
void setTitle(int id);
int32 getTitle() const;
void fillIn();
void clear();
int getPointedClue(const Common::Point &point) const;
int getClueForLine(int line) const;
void handlePointAtLine(int line);
private:
int indexOfClue(int id) const;
int _pointedClue = -1;
const static uint32 MAX_ENTRIES_PER_PAGE = 8;
NotebookLine _lines[MAX_ENTRIES_PER_PAGE] = {};
uint32 _numLines = 0;
};
} // End of namespace Tinsel
#endif // SCUMMVM_NOTEBOOK_PAGE_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,300 @@
/* 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.
*
* Spriter - 3D Renderer
*/
#ifndef TINSEL_SPRITER_H
#define TINSEL_SPRITER_H
#include "tinsel/dw.h"
#include "common/rect.h"
#include "common/stack.h"
#include "common/str.h"
#include "math/vector3d.h"
#include "math/vector2d.h"
#include "math/matrix4.h"
#if defined(USE_TINYGL)
#include "graphics/tinygl/tinygl.h"
#endif
namespace Tinsel {
typedef Common::FixedStack<Math::Matrix4, 30> MatrixStack;
enum RenderProgramOp : uint16 {
MATRIX_DUPLICATE = 1,
MATRIX_REMOVE = 2,
UNUSED = 3,
TRANSFORM = 4,
TRANSLATE_X = 5,
TRANSLATE_Y = 6,
TRANSLATE_Z = 7,
TRANSLATE_XYZ = 8,
ROTATE_X = 9,
ROTATE_Y = 10,
ROTATE_Z = 11,
ROTATE_XYZ = 17,
STOP = 16,
};
struct AnimationInfo {
Common::String name;
uint meshNum;
uint translateTablesHunk;
uint translateTables;
uint translateNum;
uint rotateTablesHunk;
uint rotateTables;
uint rotateNum;
uint scaleTablesHunk;
uint scaleTables;
uint scaleNum;
uint maxFrame;
};
struct MeshInfo {
uint meshTablesHunk;
uint meshTables;
uint programHunk;
uint program;
};
struct Hunk {
Common::Array<uint8> data;
Common::Array<uint> mappingIdx;
uint size;
uint flags;
};
typedef Common::Array<Hunk> Hunks;
typedef Common::Array<Math::Vector3d> Vectors;
struct Primitive {
uint indices[8];
uint color;
Math::Vector2d uv[4];
uint texture;
};
enum MeshPartType {
MESH_PART_TYPE_COLOR,
MESH_PART_TYPE_SOLID,
MESH_PART_TYPE_TEXTURE,
};
struct MeshPart {
MeshPartType type;
uint cull;
uint numVertices;
Common::Array<Primitive> primitives;
};
struct Mesh {
Common::Array<Math::Vector3d> vertices;
Common::Array<Math::Vector3d> normals;
Common::Array<MeshPart> parts;
Common::Array<MeshPart> parts2;
};
struct Meshes {
uint vertexCount;
uint normalCount;
Common::Array<Mesh> meshes;
};
typedef Common::Array<Vectors> AnimationData;
struct ModelTables {
Vectors translations;
Vectors rotations;
Vectors scales;
Meshes meshes;
};
enum ModelFlags {
MODEL_HAS_TRANSLATION_TABLE = 1,
MODEL_HAS_SCALE_TABLE = 2,
MODEL_HAS_ROTATION_TABLE = 4
};
struct Model {
Hunks hunks;
Hunks hunksOverlay; // The game script can load additional data to supplement the main model. E.g. a special animation.
uint animationCount;
uint field_0xe;
uint field_0xf;
uint8* program;
// animation tables
AnimationData startTranslateTables;
AnimationData startRotateTables;
AnimationData startScaleTables;
AnimationData endTranslateTables;
AnimationData endRotateTables;
AnimationData endScaleTables;
int startFrame;
int endFrame;
uint flags;
uint field_0x32;
uint field_0x33;
ModelTables tables;
Math::Vector3d position;
Math::Vector3d rotation;
Math::Vector3d scale;
uint time; // interpolant
};
struct Viewport {
int ap;
float width;
float height;
Common::Rect rect;
};
struct View {
int centerX;
int centerY;
Common::Rect viewRect;
Common::Rect screenRect;
Viewport viewport;
Math::Vector3d position;
Math::Vector3d rotation;
};
class Spriter {
private:
MatrixStack _modelMatrix;
MatrixStack* _currentMatrix;
Common::Array<AnimationInfo> _animMain;
AnimationInfo _animShadow;
MeshInfo _meshMain;
Common::Array<MeshInfo> _meshShadow;
View _view;
Common::Array<uint8> _palette;
Common::Array<uint8> _textureData;
bool _textureGenerated;
#if defined(USE_TINYGL)
uint _texture[4];
#endif
bool _modelIdle;
uint _animId;
uint _animSpeed;
uint _animDelay;
uint _animDelayMax;
uint _sequencesCount;
uint _direction;
public:
Model _modelMain;
Model _modelShadow;
public:
Spriter();
virtual ~Spriter();
void Init(int width, int height);
void SetCamera(int rotX, int rotY, int rotZ, int posX, int posY, int posZ, int cameraAp);
void TransformSceneXYZ(int x, int y, int z, int& xOut, int& yOut);
void Load(const Common::String& modelName, const Common::String& textureName);
void SetPalette(SCNHANDLE hPalette);
void SetSequence(uint animId, uint delay);
Common::Rect Draw(int direction, int x, int y, int z, int tDelta);
private:
const Math::Matrix4& MatrixCurrent() const;
void MatrixReset();
void MatrixPop();
void MatrixPush();
void MatrixTranslate(float x, float y, float z);
void MatrixScale(float x, float y, float z);
void MatrixRotateX(float angle);
void MatrixRotateY(float angle);
void MatrixRotateZ(float angle);
void SetViewport(int ap);
// Loading of the model
void LoadH(const Common::String& modelName);
void LoadGBL(const Common::String& modelName);
void LoadRBH(const Common::String& modelName, Hunks& hunks);
void LoadVMC(const Common::String& textureName);
void UpdateTextures();
Meshes LoadMeshes(const Hunks &hunks, uint hunk, uint offset, int frame);
template<bool convert>
AnimationData LoadAnimationData(const Hunks &hunks, uint hunk, uint offset);
void InitModel(Model& model, MeshInfo& meshInfo, Common::Array<AnimationInfo>& animInfo, uint flags);
// Processing of the model
void RunRenderProgram(Model &model, bool preprocess);
void FindSimilarVertices(Mesh& mesh, Vectors& vertices, Common::Array<uint16>& sameVertices) const;
void MergeVertices(Mesh& mesh, Common::Array<uint16>& sameVertices);
void TransformMesh(Mesh& mesh, Vectors& vertices);
void CalculateNormals(Mesh& mesh, Vectors& vertices, Vectors &normals);
// Rendering
void RenderModel(Model& model);
void RenderMesh(Mesh& mesh, Vectors& vertices, Vectors &normals);
void RenderMeshPartColor(MeshPart& part, Vectors& vertices, Vectors &normals);
void RenderMeshPartTexture(MeshPart& part, Vectors& vertices, Vectors &normals);
// Animation
bool SetStartFrame(Model &model, const AnimationInfo &anim, int frame);
bool SetEndFrame(Model &model, const AnimationInfo &anim, int frame);
};
} // End of namespace Tinsel
#endif // TINSEL_SPRITER_H

View File

@@ -0,0 +1,67 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "tinsel/noir/sysreel.h"
#include "common/scummsys.h"
#include "tinsel/cursor.h"
#include "tinsel/pid.h"
#include "tinsel/tinsel.h"
namespace Tinsel {
/**
* Returns the handle to the sysreel at the given index.
*
* @param index reel to get the handle to
*/
SCNHANDLE SystemReel::get(SysReel index) {
assert((int)index >= 0 && (int)index < MAX_SYSREELS);
return _reels[(int)index];
}
/**
* Stores a reel at an index and if the index is a cursor
*
* @param index where to store the reel
* @param reel handle to the reel
*/
void SystemReel::set(int32 index, SCNHANDLE reel) {
assert(index >= 0 && index < MAX_SYSREELS);
if (index == (int)SysReel::LOADSCREEN) {
if (CoroScheduler.getCurrentPID() != PID_SCENE) {
return;
}
}
_reels[index] = reel;
// Noir actually calls a function specifically for doing DwInitCursor on
// system reel 11.
if (index == (int)SysReel::CURSOR && reel != 0) {
_vm->_cursor->DwInitCursor(reel);
}
}
} // End of namespace Tinsel

View File

@@ -0,0 +1,66 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef TINSEL_SYSREEL_H // prevent multiple includes
#define TINSEL_SYSREEL_H
#include "tinsel/dw.h"
namespace Tinsel {
enum class SysReel {
NOTEPAD_OPENING = 4,
NOTEPAD_OPEN = 5,
NOTEPAD_CLOSED = 6,
NOTEPAD_FLIPDOWN = 7,
NOTEPAD_FLIPUP = 8,
SCRIBBLES = 9,
CURSOR = 11,
INVMAIN = 15,
SLIDER = 16,
CONVERSATION_FRAME = 19,
OPTIONS_MENU = 21,
LOADSAVE_MENU = 22,
QUIT_MENU = 23,
SUBTITLES_MENU = 24,
SLIDER_BG = 25,
SLIDER_HI = 26,
LEFT = 29,
RIGHT = 30,
LOADSCREEN = 31
};
class SystemReel {
public:
SystemReel() = default;
SCNHANDLE get(SysReel index);
void set(int32 index, SCNHANDLE reel);
private:
const static int32 MAX_SYSREELS = 0x28;
SCNHANDLE _reels[MAX_SYSREELS];
};
} // End of namespace Tinsel
#endif

557
engines/tinsel/object.cpp Normal file
View File

@@ -0,0 +1,557 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* This file contains the Object Manager code.
*/
#include "tinsel/object.h"
#include "tinsel/background.h"
#include "tinsel/cliprect.h" // object clip rect defs
#include "tinsel/graphics.h" // low level interface
#include "tinsel/handle.h"
#include "tinsel/text.h"
#include "tinsel/tinsel.h"
#include "common/textconsole.h"
#define OID_EFFECTS 0x2000 // generic special effects object id
namespace Tinsel {
// These vars are reset upon engine destruction
// list of all objects
static OBJECT *objectList = nullptr;
// pointer to free object list
static OBJECT *pFreeObjects = nullptr;
#ifdef DEBUG
// diagnostic object counters
static int numObj = 0;
static int maxObj = 0;
#endif
void FreeObjectList() {
free(objectList);
objectList= nullptr;
}
/**
* Kills all objects and places them on the free list.
*/
void KillAllObjects() {
int i;
#ifdef DEBUG
// clear number of objects in use
numObj = 0;
#endif
if (objectList == NULL) {
// first time - allocate memory for object list
objectList = (OBJECT *)calloc(NUM_OBJECTS, sizeof(OBJECT));
// make sure memory allocated
if (objectList == NULL) {
error("Cannot allocate memory for object data");
}
}
// place first object on free list
pFreeObjects = objectList;
// link all other objects after first
for (i = 1; i < NUM_OBJECTS; i++) {
objectList[i - 1].pNext = objectList + i;
}
// null the last object
objectList[NUM_OBJECTS - 1].pNext= nullptr;
}
#ifdef DEBUG
/**
* Shows the maximum number of objects used at once.
*/
void ObjectStats() {
debug("%i objects of %i used", maxObj, NUM_OBJECTS);
}
#endif
/**
* Allocate a object from the free list.
*/
OBJECT *AllocObject() {
OBJECT *pObj = pFreeObjects; // get a free object
// check for no free objects
assert(pObj != NULL);
// a free object exists
// get link to next free object
pFreeObjects = pObj->pNext;
// clear out object
pObj->reset();
// set default drawing mode and set changed bit
pObj->flags = DMA_WNZ | DMA_CHANGED;
#ifdef DEBUG
// one more object in use
if (++numObj > maxObj)
maxObj = numObj;
#endif
// return new object
return pObj;
}
bool isValidObject(OBJECT *obj) {
return (obj >= objectList && obj <= objectList + NUM_OBJECTS - 1);
}
/**
* Copy one object to another.
* @param pDest Destination object
* @param pSrc Source object
*/
void CopyObject(OBJECT *pDest, OBJECT *pSrc) {
// save previous dimensions etc.
Common::Rect rcSave = pDest->rcPrev;
// make a copy
memcpy(pDest, pSrc, sizeof(OBJECT));
// restore previous dimensions etc.
pDest->rcPrev = rcSave;
// set changed flag in destination
pDest->flags |= DMA_CHANGED;
// null the links
pDest->pNext = pDest->pSlave= nullptr;
}
/**
* Inserts an object onto the specified object list. The object
* lists are sorted in Z Y order.
* @param pObjList List to insert object onto
* @param pInsObj Object to insert
*/
void InsertObject(OBJECT **pObjList, OBJECT *pInsObj) {
OBJECT **pAnchor, *pObj; // object list traversal pointers
// validate object pointer
assert(isValidObject(pInsObj));
for (pAnchor = pObjList, pObj = *pAnchor; pObj != NULL; pAnchor = &pObj->pNext, pObj = *pAnchor) {
// check Z order
if (pInsObj->zPos < pObj->zPos) {
// object Z is lower than list Z - insert here
break;
} else if (pInsObj->zPos == pObj->zPos) {
// Z values are the same - sort on Y
if (fracToDouble(pInsObj->yPos) <= fracToDouble(pObj->yPos)) {
// object Y is lower than or same as list Y - insert here
break;
}
}
}
// insert obj between pAnchor and pObj
pInsObj->pNext = pObj;
*pAnchor = pInsObj;
}
/**
* Deletes an object from the specified object list and places it
* on the free list.
* @param pObjList List to delete object from
* @param pDelObj Object to delete
*/
void DelObject(OBJECT **pObjList, OBJECT *pDelObj) {
OBJECT **pAnchor, *pObj; // object list traversal pointers
const Common::Rect rcScreen(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
// validate object pointer
assert(isValidObject(pDelObj));
#ifdef DEBUG
// one less object in use
--numObj;
assert(numObj >= 0);
#endif
for (pAnchor = pObjList, pObj = *pAnchor; pObj != NULL; pAnchor = &pObj->pNext, pObj = *pAnchor) {
if (pObj == pDelObj) {
// found object to delete
if (IntersectRectangle(pDelObj->rcPrev, pDelObj->rcPrev, rcScreen)) {
// allocate a clipping rect for objects previous pos
AddClipRect(pDelObj->rcPrev);
}
// make PREV next = OBJ next - removes OBJ from list
*pAnchor = pObj->pNext;
// place free list in OBJ next
pObj->pNext = pFreeObjects;
// add OBJ to top of free list
pFreeObjects = pObj;
// delete objects palette
if (pObj->pPal)
FreePalette(pObj->pPal);
// quit
return;
}
}
// if we get to here - object has not been found on the list
// This can be triggered in Act 3 in DW1 while talking to the guard,
// so this has been turned to a warning instead of an error
warning("DelObject(): formally 'assert(0)!'");
}
/**
* Sort the specified object list in Z Y order.
* @param pObjList List to sort
*/
void SortObjectList(OBJECT **pObjList) {
OBJECT *pPrev, *pObj; // object list traversal pointers
OBJECT head; // temporary head of list - because pObjList is not usually a OBJECT
// put at head of list
head.pNext = *pObjList;
// set head of list dummy OBJ Z Y values to lowest possible
head.yPos = intToFrac(MIN_INT16);
head.zPos = MIN_INT;
for (pPrev = &head, pObj = head.pNext; pObj != NULL; pPrev = pObj, pObj = pObj->pNext) {
// check Z order
if (pObj->zPos < pPrev->zPos) {
// object Z is lower than previous Z
// remove object from list
pPrev->pNext = pObj->pNext;
// re-insert object on list
InsertObject(pObjList, pObj);
// back to beginning of list
pPrev = &head;
pObj = head.pNext;
} else if (pObj->zPos == pPrev->zPos) {
// Z values are the same - sort on Y
if (fracToDouble(pObj->yPos) < fracToDouble(pPrev->yPos)) {
// object Y is lower than previous Y
// remove object from list
pPrev->pNext = pObj->pNext;
// re-insert object on list
InsertObject(pObjList, pObj);
// back to beginning of list
pPrev = &head;
pObj = head.pNext;
}
}
}
}
/**
* Returns the animation offsets of a image, dependent on the
* images orientation flags.
* @param hImg Iimage to get animation offset of
* @param flags Images current flags
* @param pAniX Gets set to new X animation offset
* @param pAniY Gets set to new Y animation offset
*/
void GetAniOffset(SCNHANDLE hImg, int flags, int *pAniX, int *pAniY) {
if (hImg) {
const IMAGE *pImg = _vm->_handle->GetImage(hImg);
// set ani X
*pAniX = (int16) pImg->anioffX;
// set ani Y
*pAniY = (int16) pImg->anioffY;
if (flags & DMA_FLIPH) {
// we are flipped horizontally
// set ani X = -ani X + width - 1
*pAniX = -*pAniX + pImg->imgWidth - 1;
}
if (flags & DMA_FLIPV) {
// we are flipped vertically
// set ani Y = -ani Y + height - 1
*pAniY = -*pAniY + (pImg->imgHeight & ~C16_FLAG_MASK) - 1;
}
delete pImg;
} else
// null image
*pAniX = *pAniY = 0;
}
/**
* Returns the x,y position of an objects animation point.
* @param pObj Pointer to object
* @param pPosX Gets set to objects X animation position
* @param pPosY Gets set to objects Y animation position
*/
void GetAniPosition(OBJECT *pObj, int *pPosX, int *pPosY) {
// validate object pointer
assert(isValidObject(pObj));
// get the animation offset of the object
GetAniOffset(pObj->hImg, pObj->flags, pPosX, pPosY);
// from animation offset and objects position - determine objects animation point
*pPosX += fracToInt(pObj->xPos);
*pPosY += fracToInt(pObj->yPos);
}
/**
* Initialize 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= nullptr; // palette queue pointer
const IMAGE *pImg = _vm->_handle->GetImage(pInitTbl->hObjImg); // handle to image
if (TinselVersion != 3) {
if (pImg->hImgPal) {
// allocate a palette for this object
pPalQ = AllocPalette(pImg->hImgPal);
// make sure palette allocated
assert(pPalQ != NULL);
}
// assign palette to object
pObj->pPal = pPalQ;
} else {
if ((pImg->colorFlags & 0x0C) == 0) { // bits 0b1100 are used to select blending mode
pObj->flags = pObj->flags & ~DMA_GHOST;
} else {
assert((pObj->flags & DMA_WNZ) != 0);
pObj->flags |= DMA_GHOST;
}
pObj->isRLE = pImg->isRLE;
pObj->colorFlags = pImg->colorFlags;
}
// set objects size
pObj->width = pImg->imgWidth;
pObj->height = pImg->imgHeight & ~C16_FLAG_MASK;
pObj->flags &= ~C16_FLAG_MASK;
pObj->flags |= pImg->imgHeight & C16_FLAG_MASK;
// set objects bitmap definition
pObj->hBits = pImg->hImgBits;
delete pImg;
// 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;
}
/**
* 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 = _vm->_handle->GetImage(hNewImg);
// setup new shape
pAniObj->width = pNewImg->imgWidth;
pAniObj->height = pNewImg->imgHeight & ~C16_FLAG_MASK;
newflags &= ~C16_FLAG_MASK;
newflags |= pNewImg->imgHeight & C16_FLAG_MASK;
// set objects bitmap definition
pAniObj->hBits = pNewImg->hImgBits;
delete pNewImg;
} 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);
}
}
/**
* Give an object a new image.
* @param pAniObj Object to animate
* @param hNewImg Objects new image
*/
void AnimateObject(OBJECT *pAniObj, SCNHANDLE hNewImg) {
// dont change the objects flags
AnimateObjectFlags(pAniObj, pAniObj->flags, hNewImg);
}
/**
* Creates a rectangle object of the given dimensions and returns
* a pointer to the object.
* @param hPal Palette for the rectangle object
* @param color Which color offset from the above palette
* @param width Width of rectangle
* @param height Height of rectangle
*/
OBJECT *RectangleObject(SCNHANDLE hPal, int color, int width, int height) {
// template for initializing the rectangle object
static const OBJ_INIT rectObj = {0, DMA_CONST, OID_EFFECTS, 0, 0, 0};
PALQ *pPalQ; // palette queue pointer
// allocate and init a new object
OBJECT *pRect = InitObject(&rectObj);
// allocate a palette for this object
pPalQ = AllocPalette(hPal);
// make sure palette allocated
assert(pPalQ != NULL);
// assign palette to object
pRect->pPal = pPalQ;
// set color in the palette
pRect->constant = color;
// set rectangle width
pRect->width = width;
// set rectangle height
pRect->height = height;
// return pointer to rectangle object
return pRect;
}
/**
* Creates a translucent rectangle object of the given dimensions
* and returns a pointer to the object.
* @param width Width of rectangle
* @param height Height of rectangle
*/
OBJECT *TranslucentObject(int width, int height) {
// template for initializing the rectangle object
static const OBJ_INIT rectObj = {0, DMA_TRANS, OID_EFFECTS, 0, 0, 0};
// allocate and init a new object
OBJECT *pRect = InitObject(&rectObj);
// set rectangle width
pRect->width = width;
// set rectangle height
pRect->height = height;
// return pointer to rectangle object
return pRect;
}
} // End of namespace Tinsel

207
engines/tinsel/object.h Normal file
View File

@@ -0,0 +1,207 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* Object Manager data structures
*/
#ifndef TINSEL_OBJECT_H // prevent multiple includes
#define TINSEL_OBJECT_H
#include "tinsel/dw.h"
#include "common/frac.h"
#include "common/rect.h"
namespace Tinsel {
struct PALQ;
enum {
/** the maximum number of objects */
NUM_OBJECTS = 512,
// object flags
DMA_WNZ = 0x0001, ///< write non-zero data
DMA_CNZ = 0x0002, ///< TinselV1 write constant on non-zero data
DMA_RLWA = 0x0002, ///< TenselV2+ run-length write all
DMA_CONST = 0x0004, ///< write constant on both zero & non-zero data
DMA_WA = 0x0008, ///< write all data
DMA_FLIPH = 0x0010, ///< flip object horizontally
DMA_FLIPV = 0x0020, ///< flip object vertically
DMA_CLIP = 0x0040, ///< clip object
DMA_TRANS = 0x0084, ///< translucent rectangle object
DMA_ABS = 0x0100, ///< position of object is absolute
DMA_CHANGED = 0x0200, ///< object has changed in some way since the last frame
DMA_USERDEF = 0x0400, ///< user defined flags start here
DMA_GHOST = 0x0080,
DMA_3D = 0x0400, ///< 3D objects for TinselV3
/** flags that effect an objects appearance */
DMA_HARDFLAGS = (DMA_WNZ | DMA_CNZ | DMA_CONST | DMA_WA | DMA_FLIPH | DMA_FLIPV | DMA_TRANS)
};
/** structure for image */
struct IMAGE {
short imgWidth; ///< image width
unsigned short imgHeight; ///< image height
short anioffX; ///< image x animation offset
short anioffY; ///< image y animation offset
SCNHANDLE hImgBits; ///< image bitmap handle
SCNHANDLE hImgPal; ///< image palette handle (Tinsel V1/V2)
short isRLE; ///< if image is using run-length encoding (Tinsel V3)
short colorFlags; ///< type of blending (Tinsel V3)
};
/** a multi-object animation frame is a list of multi-image handles */
typedef uint32 FRAME;
// object structure
struct OBJECT {
OBJECT *pNext; ///< pointer to next object in list
OBJECT *pSlave; ///< pointer to slave object (multi-part objects)
// char *pOnDispList; ///< pointer to display list byte for background objects
// frac_t xVel; ///< x velocity of object
// frac_t yVel; ///< y velocity of object
frac_t xPos; ///< x position of object
frac_t yPos; ///< y position of object
int zPos; ///< z position of object
Common::Rect rcPrev; ///< previous screen coordinates of object bounding rectangle
int flags; ///< object flags - see above for list
PALQ *pPal; ///< objects palette Q position
short isRLE; ///< TinselVersion == 3, if image is using run-length encoding
short colorFlags; /// TinselV3, type of color blending
int constant; ///< which color in palette for monochrome objects
int width; ///< width of object
int height; ///< height of object
SCNHANDLE hBits; ///< image bitmap handle
SCNHANDLE hImg; ///< handle to object image definition
SCNHANDLE hShape; ///< objects current animation frame
SCNHANDLE hMirror; ///< objects previous animation frame
int oid; ///< object identifier
void reset() {
pNext = nullptr;
pSlave = nullptr;
//pOnDispList = nullptr;
//xVel = 0;
//yVel = 0;
xPos = 0;
yPos = 0;
zPos = 0;
rcPrev.top = 0;
rcPrev.left = 0;
rcPrev.bottom = 0;
rcPrev.right = 0;
flags = 0;
pPal = nullptr;
constant = 0;
width = 0;
height = 0;
hBits = 0;
hImg = 0;
hShape = 0;
hMirror = 0;
oid = 0;
}
OBJECT() { reset(); }
};
// object initialisation structure
struct OBJ_INIT {
SCNHANDLE hObjImg; // objects shape - handle to IMAGE structure
int32 objFlags; // objects flags
int32 objID; // objects id
int32 objX; // objects initial x position
int32 objY; // objects initial y position
int32 objZ; // objects initial z position
};
/*----------------------------------------------------------------------*\
|* Object Function Prototypes *|
\*----------------------------------------------------------------------*/
void KillAllObjects(); // kill all objects and place them on free list
void FreeObjectList(); // free the object list
#ifdef DEBUG
void ObjectStats(); // Shows the maximum number of objects used at once
#endif
OBJECT *AllocObject(); // allocate a object from the free list
void FreeObject( // place a object back on the free list
OBJECT *pFreeObj); // object to free
bool isValidObject(OBJECT *obj);
void CopyObject( // copy one object to another
OBJECT *pDest, // destination object
OBJECT *pSrc); // source object
void InsertObject( // insert a object onto a sorted object list
OBJECT **pObjList, // list to insert object onto
OBJECT *pInsObj); // object to insert
void DelObject( // delete a object from a object list and add to free list
OBJECT **pObjList, // list to delete object from
OBJECT *pDelObj); // object to delete
void SortObjectList( // re-sort an object list
OBJECT **pObjList); // list to sort
void GetAniOffset( // returns the anim offsets of a image, takes into account orientation
SCNHANDLE hImg, // image to get animation offset of
int flags, // images current flags
int *pAniX, // gets set to new X animation offset
int *pAniY); // gets set to new Y animation offset
void GetAniPosition( // Returns a objects x,y animation point
OBJECT *pObj, // pointer to object
int *pPosX, // gets set to objects X animation position
int *pPosY); // gets set to objects Y animation position
OBJECT *InitObject( // Init a object using a OBJ_INIT struct
const OBJ_INIT *pInitTbl); // pointer to object initialisation table
void AnimateObjectFlags( // Give a object a new image and new orientation flags
OBJECT *pAniObj, // object to be updated
int newflags, // objects new flags
SCNHANDLE hNewImg); // objects new image
void AnimateObject( // give a object a new image
OBJECT *pAniObj, // object to animate
SCNHANDLE hNewImg); // objects new image
void HideObject( // Hides a object by giving it a "NullImage" image pointer
OBJECT *pObj); // object to be hidden
OBJECT *RectangleObject( // create a rectangle object of the given dimensions
SCNHANDLE hPal, // palette for the rectangle object
int color, // which color offset from the above palette
int width, // width of rectangle
int height); // height of rectangle
OBJECT *TranslucentObject( // create a translucent rectangle object of the given dimensions
int width, // width of rectangle
int height); // height of rectangle
} // End of namespace Tinsel
#endif // TINSEL_OBJECT_H

685
engines/tinsel/palette.cpp Normal file
View File

@@ -0,0 +1,685 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* Palette Allocator for IBM PC.
*/
#include "tinsel/dw.h" // TBLUE1 definition
#include "tinsel/graphics.h"
#include "tinsel/handle.h" // LockMem definition
#include "tinsel/palette.h" // palette allocator structures etc.
#include "tinsel/sysvar.h"
#include "tinsel/tinsel.h"
#include "common/system.h"
#include "common/textconsole.h"
#include "graphics/paletteman.h"
namespace Tinsel {
//----------------- LOCAL DEFINES --------------------
/** video DAC transfer Q structure */
struct VIDEO_DAC_Q {
union {
SCNHANDLE hRGBarray; ///< handle of palette or
COLORREF *pRGBarray; ///< list of palette colors
COLORREF singleRGB;
} pal;
bool bHandle; ///< when set - use handle of palette
int destDACindex; ///< start index of palette in video DAC
int numColors; ///< number of colors in "hRGBarray"
};
/** video DAC transfer Q length */
#define VDACQLENGTH (NUM_PALETTES + 2)
/** color index of the 4 colors used for the translucent palette */
#define COL_HILIGHT TBLUE1
//----------------- LOCAL GLOBAL DATA --------------------
// These vars are reset upon engine destruction
/** palette allocator data */
static PALQ g_palAllocData[NUM_PALETTES];
/** video DAC transfer Q */
static VIDEO_DAC_Q g_vidDACdata[VDACQLENGTH];
/** video DAC transfer Q head pointer */
static VIDEO_DAC_Q *g_pDAChead;
/** the translucent palette lookup table */
uint8 g_transPalette[MAX_COLORS]; // used in graphics.cpp
static int g_translucentIndex = 228;
static int g_talkIndex = 233;
static COLORREF g_talkColRef = 0;
static COLORREF g_tagColRef = 0;
#ifdef DEBUG
// diagnostic palette counters
static int numPals = 0;
static int maxPals = 0;
static int maxDACQ = 0;
#endif
void ResetVarsPalette() {
memset(g_palAllocData, 0, sizeof(g_palAllocData));
g_pDAChead = g_vidDACdata;
memset(g_transPalette, 0, sizeof(g_transPalette));
g_translucentIndex = 228;
g_talkIndex = 233;
g_talkColRef = 0;
g_tagColRef = 0;
}
/**
* Map PSX palettes to original palette from resource file
*/
void psxPaletteMapper(PALQ *originalPal, uint8 *psxClut, byte *mapperTable) {
PALETTE *pal = _vm->_handle->GetPalette(originalPal->hPal);
bool colorFound = false;
uint16 clutEntry = 0;
// Empty the table with color correspondences
memset(mapperTable, 0, 16);
for (int j = 1; j < 16; j++) {
clutEntry = READ_16(psxClut + (sizeof(uint16) * j));
if (clutEntry) {
if (clutEntry == 0x7EC0) { // This is an already known value, used by the in-game text
mapperTable[j] = 232;
continue;
}
// Check for correspondent color
for (int32 i = 0; (i < pal->numColors) && !colorFound; i++) {
// get R G B values in the same way as psx format converters
uint16 psxEquivalent = TINSEL_PSX_RGB(
pal->palette[i * 3] >> 3,
pal->palette[i * 3 + 1] >> 3,
pal->palette[i * 3 + 2] >> 3
);
if (psxEquivalent == clutEntry) {
mapperTable[j] = i + 1; // Add entry in the table for the found color
colorFound = true;
}
}
colorFound = false;
} else { // The rest of the entries are zeroes
delete pal;
return;
}
}
delete pal;
}
/**
* Transfer palettes in the palette Q to Video DAC.
*/
void PalettesToVideoDAC() {
PALQ *pPalQ; // palette Q iterator
VIDEO_DAC_Q *pDACtail = g_vidDACdata; // set tail pointer
byte pal[768];
memset(pal, 0, sizeof(pal));
// while Q is not empty
while (g_pDAChead != pDACtail) {
#ifdef DEBUG
// make sure palette does not overlap
assert(pDACtail->destDACindex + pDACtail->numColors <= MAX_COLORS);
#else
// make sure palette does not overlap
if (pDACtail->destDACindex + pDACtail->numColors > MAX_COLORS)
pDACtail->numColors = MAX_COLORS - pDACtail->destDACindex;
#endif
if (pDACtail->bHandle) {
// we are using a palette handle
PALETTE *newPal = _vm->_handle->GetPalette(pDACtail->pal.hRGBarray);
memcpy(pal, newPal->palette, pDACtail->numColors * 3);
delete newPal;
} else if (pDACtail->numColors == 1) {
// we are using a single color palette
pal[0] = (byte)(pDACtail->pal.singleRGB & 0xFF);
pal[1] = (byte)((pDACtail->pal.singleRGB >> 8) & 0xFF);
pal[2] = (byte)((pDACtail->pal.singleRGB >> 16) & 0xFF);
} else {
// we are using a palette pointer
for (int i = 0; i < pDACtail->numColors; ++i) {
pal[i * 3 + 0] = (byte)(pDACtail->pal.pRGBarray[i] & 0xFF);
pal[i * 3 + 1] = (byte)((pDACtail->pal.pRGBarray[i] >> 8) & 0xFF);
pal[i * 3 + 2] = (byte)((pDACtail->pal.pRGBarray[i] >> 16) & 0xFF);
}
}
// Swap black/white colors in the Mac version.
// We need to swap the current black/white values so that screen fade
// in/out is done correctly.
if (TinselV1Mac) {
byte macWhite = pal[ 0 * 3 + 0];
byte macBlack = pal[254 * 3 + 0];
pal[254 * 3 + 0] = pal[254 * 3 + 1] = pal[254 * 3 + 2] = macWhite;
pal[ 0 * 3 + 0] = pal[ 0 * 3 + 1] = pal[ 0 * 3 + 2] = macBlack;
}
// update the system palette
g_system->getPaletteManager()->setPalette(pal, pDACtail->destDACindex, pDACtail->numColors);
// update tail pointer
pDACtail++;
}
// reset video DAC transfer Q head pointer
g_pDAChead = g_vidDACdata;
// clear all palette moved bits
for (pPalQ = g_palAllocData; pPalQ < g_palAllocData + NUM_PALETTES; pPalQ++)
pPalQ->posInDAC &= ~PALETTE_MOVED;
}
/**
* Commpletely reset the palette allocator.
*/
void ResetPalAllocator() {
#ifdef DEBUG
// clear number of palettes in use
numPals = 0;
#endif
// wipe out the palette allocator data
memset(g_palAllocData, 0, sizeof(g_palAllocData));
// reset video DAC transfer Q head pointer
g_pDAChead = g_vidDACdata;
}
#ifdef DEBUG
/**
* Shows the maximum number of palettes used at once.
*/
void PaletteStats() {
debug("%i palettes of %i used", maxPals, NUM_PALETTES);
debug("%i DAC queue entries of %i used", maxDACQ, VDACQLENGTH);
}
#endif
/**
* Places a palette in the video DAC queue.
* @param posInDAC Position in video DAC
* @param numColors Number of colors in palette
* @param hPalette Handle to palette
*/
void UpdateDACqueueHandle(int posInDAC, int numColors, SCNHANDLE hPalette) {
// check Q overflow
assert(g_pDAChead < g_vidDACdata + VDACQLENGTH);
g_pDAChead->destDACindex = posInDAC & ~PALETTE_MOVED; // set index in video DAC
g_pDAChead->numColors = numColors; // set number of colors
g_pDAChead->pal.hRGBarray = hPalette; // set handle of palette
g_pDAChead->bHandle = true; // we are using a palette handle
// update head pointer
++g_pDAChead;
#ifdef DEBUG
if ((g_pDAChead-g_vidDACdata) > maxDACQ)
maxDACQ = g_pDAChead-g_vidDACdata;
#endif
}
/**
* Places a palette in the video DAC queue.
* @param posInDAC Position in video DAC
* @param numColors Number of colors in palette
* @param pColors List of RGB triples
*/
void UpdateDACqueue(int posInDAC, int numColors, COLORREF *pColors) {
// check Q overflow
assert(g_pDAChead < g_vidDACdata + NUM_PALETTES);
g_pDAChead->destDACindex = posInDAC & ~PALETTE_MOVED; // set index in video DAC
g_pDAChead->numColors = numColors; // set number of colors
if (numColors == 1)
g_pDAChead->pal.singleRGB = *pColors; // set single color of which the "palette" consists
else
g_pDAChead->pal.pRGBarray = pColors; // set addr of palette
g_pDAChead->bHandle = false; // we are not using a palette handle
// update head pointer
++g_pDAChead;
#ifdef DEBUG
if ((g_pDAChead-g_vidDACdata) > maxDACQ)
maxDACQ = g_pDAChead-g_vidDACdata;
#endif
}
/**
* Places a "palette" consisting of a single color in the video DAC queue.
* @param posInDAC Position in video DAC
* @param color Single RGB triple
*/
void UpdateDACqueue(int posInDAC, COLORREF color) {
// check Q overflow
assert(g_pDAChead < g_vidDACdata + NUM_PALETTES);
g_pDAChead->destDACindex = posInDAC & ~PALETTE_MOVED; // set index in video DAC
g_pDAChead->numColors = 1; // set number of colors
g_pDAChead->pal.singleRGB = color; // set single color of which the "palette" consists
g_pDAChead->bHandle = false; // we are not using a palette handle
// update head pointer
++g_pDAChead;
#ifdef DEBUG
if ((g_pDAChead-g_vidDACdata) > maxDACQ)
maxDACQ = g_pDAChead-g_vidDACdata;
#endif
}
/**
* Allocate a palette.
* @param hNewPal Palette to allocate
*/
PALQ *AllocPalette(SCNHANDLE hNewPal) {
PALQ *pPrev, *p; // walks palAllocData
int iDAC; // color index in video DAC
PALQ *pNxtPal; // next PALQ struct in palette allocator
// search all structs in palette allocator - see if palette already allocated
for (p = g_palAllocData; p < g_palAllocData + NUM_PALETTES; p++) {
if (p->hPal == hNewPal) {
// found the desired palette in palette allocator
p->objCount++; // update number of objects using palette
return p; // return palette queue position
}
}
PALETTE *pal = _vm->_handle->GetPalette(hNewPal);
// search all structs in palette allocator - find a free slot
iDAC = FGND_DAC_INDEX; // init DAC index to first available foreground color
for (p = g_palAllocData; p < g_palAllocData + NUM_PALETTES; p++) {
if (p->hPal == 0) {
// found a free slot in palette allocator
p->objCount = 1; // init number of objects using palette
p->posInDAC = iDAC; // set palettes start pos in video DAC
p->hPal = hNewPal; // set hardware palette data
p->numColors = pal->numColors; // set number of colors in palette
if (TinselVersion >= 2)
// Copy all the colors
memcpy(p->palRGB, pal->palRGB, p->numColors * sizeof(COLORREF));
#ifdef DEBUG
// one more palette in use
if (++numPals > maxPals)
maxPals = numPals;
#endif
// Q the change to the video DAC
if (TinselVersion >= 2)
UpdateDACqueue(p->posInDAC, p->numColors, p->palRGB);
else
UpdateDACqueueHandle(p->posInDAC, p->numColors, p->hPal);
// move all palettes after this one down (if necessary)
for (pPrev = p, pNxtPal = pPrev + 1; pNxtPal < g_palAllocData + NUM_PALETTES; pNxtPal++) {
if (pNxtPal->hPal != 0) {
// palette slot is in use
if (pNxtPal->posInDAC >= pPrev->posInDAC + pPrev->numColors)
// no need to move palettes down
break;
// move palette down - indicate change
pNxtPal->posInDAC = (pPrev->posInDAC + pPrev->numColors) | PALETTE_MOVED;
// Q the palette change in position to the video DAC
if (TinselVersion <= 1)
UpdateDACqueueHandle(pNxtPal->posInDAC, pNxtPal->numColors, pNxtPal->hPal);
else if (!pNxtPal->bFading)
UpdateDACqueue(pNxtPal->posInDAC, pNxtPal->numColors, pNxtPal->palRGB);
// update previous palette to current palette
pPrev = pNxtPal;
}
}
delete pal;
// return palette pointer
return p;
}
// set new DAC index
iDAC = p->posInDAC + p->numColors;
}
// no free palettes
error("AllocPalette(): formally 'assert(0)!'");
}
/**
* Free a palette allocated with "AllocPalette".
* @param pFreePal Palette queue entry to free
*/
void FreePalette(PALQ *pFreePal) {
// validate palette Q pointer
assert(pFreePal >= g_palAllocData && pFreePal <= g_palAllocData + NUM_PALETTES - 1);
// reduce the palettes object reference count
pFreePal->objCount--;
// make sure palette has not been deallocated too many times
assert(pFreePal->objCount >= 0);
if (pFreePal->objCount == 0) {
pFreePal->hPal = 0; // palette is no longer in use
#ifdef DEBUG
// one less palette in use
--numPals;
assert(numPals >= 0);
#endif
}
}
/**
* Find the specified palette.
* @param hSrchPal Hardware palette to search for
*/
PALQ *FindPalette(SCNHANDLE hSrchPal) {
PALQ *pPal; // palette allocator iterator
// search all structs in palette allocator
for (pPal = g_palAllocData; pPal < g_palAllocData + NUM_PALETTES; pPal++) {
if (pPal->hPal == hSrchPal)
// found palette in palette allocator
return pPal;
}
// palette not found
return NULL;
}
/**
* Swaps the palettes at the specified palette queue position.
* @param pPalQ Palette queue position
* @param hNewPal New palette
*/
void SwapPalette(PALQ *pPalQ, SCNHANDLE hNewPal) {
PALETTE *pal = _vm->_handle->GetPalette(hNewPal);
// validate palette Q pointer
assert(pPalQ >= g_palAllocData && pPalQ <= g_palAllocData + NUM_PALETTES - 1);
if (pPalQ->numColors >= pal->numColors) {
// new palette will fit the slot
// install new palette
pPalQ->hPal = hNewPal;
if (TinselVersion >= 2) {
pPalQ->numColors = pal->numColors;
// Copy all the colors
memcpy(pPalQ->palRGB, pal->palRGB, pal->numColors * sizeof(COLORREF));
if (!pPalQ->bFading)
// Q the change to the video DAC
UpdateDACqueue(pPalQ->posInDAC, pal->numColors, pPalQ->palRGB);
} else {
// Q the change to the video DAC
UpdateDACqueueHandle(pPalQ->posInDAC, pal->numColors, hNewPal);
}
} else {
// # colors are different - will have to update all following palette entries
assert(TinselVersion <= 1); // Fatal error for Tinsel 2
PALQ *pNxtPalQ; // next palette queue position
for (pNxtPalQ = pPalQ + 1; pNxtPalQ < g_palAllocData + NUM_PALETTES; pNxtPalQ++) {
if (pNxtPalQ->posInDAC >= pPalQ->posInDAC + pPalQ->numColors)
// no need to move palettes down
break;
// move palette down
pNxtPalQ->posInDAC = (pPalQ->posInDAC + pPalQ->numColors) | PALETTE_MOVED;
// Q the palette change in position to the video DAC
UpdateDACqueueHandle(pNxtPalQ->posInDAC,
pNxtPalQ->numColors,
pNxtPalQ->hPal);
// update previous palette to current palette
pPalQ = pNxtPalQ;
}
}
delete pal;
}
/**
* Statless palette iterator. Returns the next palette in the list
* @param pStrtPal Palette to start from - when NULL will start from beginning of list
*/
PALQ *GetNextPalette(PALQ *pStrtPal) {
if (pStrtPal == NULL) {
// start of palette iteration - return 1st palette
return (g_palAllocData[0].objCount) ? g_palAllocData : NULL;
}
// validate palette Q pointer
assert(pStrtPal >= g_palAllocData && pStrtPal <= g_palAllocData + NUM_PALETTES - 1);
// return next active palette in list
while (++pStrtPal < g_palAllocData + NUM_PALETTES) {
if (pStrtPal->objCount)
// active palette found
return pStrtPal;
}
// non found
return NULL;
}
/**
* Sets the current background color.
* @param color Color to set the background to
*/
void SetBgndColor(COLORREF color) {
// update background color struct by queuing the change to the video DAC
UpdateDACqueue(BGND_DAC_INDEX, color);
}
/**
* Note whether a palette is being faded.
* @param pPalQ Palette queue position
* @param bFading Whether it is fading
*/
void FadingPalette(PALQ *pPalQ, bool bFading) {
// validate palette Q pointer
assert(pPalQ >= g_palAllocData && pPalQ <= g_palAllocData + NUM_PALETTES - 1);
// validate that this is a change
assert(pPalQ->bFading != bFading);
pPalQ->bFading = bFading;
}
/**
* All fading processes have just been killed, so none of the
* palettes are fading.
*/
void NoFadingPalettes() {
PALQ *pPalQ;
for (pPalQ = g_palAllocData; pPalQ <= g_palAllocData + NUM_PALETTES - 1; pPalQ++) {
pPalQ->bFading = false;
}
}
/**
* Builds the translucent palette from the current backgrounds palette.
* @param hPalette Handle to current background palette
*/
void CreateTranslucentPalette(SCNHANDLE hPalette) {
PALETTE *pal = _vm->_handle->GetPalette(hPalette);
// leave background color alone
g_transPalette[0] = 0;
for (int32 i = 0; i < pal->numColors; i++) {
byte red = pal->palette[i * 3];
byte green = pal->palette[i * 3 + 1];
byte blue = pal->palette[i * 3 + 2];
// calculate the Value field of the HSV color model
unsigned val = (red > green) ? red : green;
val = (val > blue) ? val : blue;
// map the Value field to one of the 4 colors reserved for the translucent palette
val /= 63;
byte blackColorIndex = (!TinselV1Mac) ? 0 : 255;
g_transPalette[i + 1] = (uint8)((val == 0) ? blackColorIndex : val +
((TinselVersion >= 2) ? TranslucentColor() : COL_HILIGHT) - 1);
}
delete pal;
}
/**
* Returns an adjusted color RGB
* @param color Color to scale
*/
static COLORREF DimColor(byte r, byte g, byte b, int factor) {
if (factor == 10) {
// No change
return TINSEL_RGB(r, g, b);
} else if (factor == 0) {
// No brightness
return 0;
} else {
// apply multiplier to RGB components
uint32 red = r * factor / 10;
uint32 green = g * factor / 10;
uint32 blue = b * factor / 10;
// return new color
return TINSEL_RGB(red, green, blue);
}
}
/**
* DimPartPalette
*/
void DimPartPalette(SCNHANDLE hDimPal, int startColor, int length, int brightness) {
PALQ *pPalQ = FindPalette(hDimPal);
assert(pPalQ);
// Adjust for the fact that palettes don't contain color 0
startColor -= 1;
// Check some other things
if (startColor + length > pPalQ->numColors)
error("DimPartPalette(): color overrun");
// Check if the palette actually contains entries
if (length == 0)
return;
PALETTE *pal = _vm->_handle->GetPalette(hDimPal);
for (int iColor = startColor; iColor < startColor + length; iColor++) {
byte r = pal->palette[iColor * 3];
byte g = pal->palette[iColor * 3 + 1];
byte b = pal->palette[iColor * 3 + 2];
pPalQ->palRGB[iColor] = DimColor(r, g, b, brightness);
}
delete pal;
if (!pPalQ->bFading) {
// Q the change to the video DAC
UpdateDACqueue(pPalQ->posInDAC + startColor, length, &pPalQ->palRGB[startColor]);
}
}
int DarkGreen() {
return _vm->screen().format.RGBToColor(0x00, 0x40, 0x00);
}
int TranslucentColor() {
return g_translucentIndex;
}
int HighlightColor() {
if (TinselVersion == 3) {
return _vm->screen().format.RGBToColor(0x00, 0x80, 0x00);
}
UpdateDACqueue(g_talkIndex, (COLORREF)SysVar(SYS_HighlightRGB));
return g_talkIndex;
}
int TalkColor() {
return (TinselVersion >= 2) ? g_talkIndex : TALKFONT_COL;
}
void SetTalkColorRef(COLORREF colRef) {
g_talkColRef = colRef;
}
COLORREF GetTalkColorRef() {
return g_talkColRef;
}
void SetTagColorRef(COLORREF colRef) {
g_tagColRef = colRef;
}
COLORREF GetTagColorRef() {
return g_tagColRef;
}
void SetTranslucencyOffset(int offset) {
g_translucentIndex = offset;
}
void SetTalkTextOffset(int offset) {
g_talkIndex = offset;
}
} // End of namespace Tinsel

169
engines/tinsel/palette.h Normal file
View File

@@ -0,0 +1,169 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* Palette Allocator Definitions
*/
#ifndef TINSEL_PALETTE_H // prevent multiple includes
#define TINSEL_PALETTE_H
#include "tinsel/dw.h"
namespace Tinsel {
typedef uint32 COLORREF;
#define TINSEL_RGB(r,g,b) ((COLORREF)((uint8)(r)|((uint16)(g)<<8))|(((uint32)(uint8)(b))<<16))
#define TINSEL_PSX_RGB(r,g,b) ((uint16)(((uint8)(r))|((uint16)(g)<<5)|(((uint16)(b))<<10)))
enum {
MAX_COLORS = 256, ///< maximum number of colors - for VGA 256
BITS_PER_PIXEL = 8, ///< number of bits per pixel for VGA 256
MAX_INTENSITY = 255, ///< the biggest value R, G or B can have
NUM_PALETTES = 32, ///< number of palettes
// Discworld has some fixed apportioned bits in the palette.
BGND_DAC_INDEX = 0, ///< index of background color in Video DAC
FGND_DAC_INDEX = 1, ///< index of first foreground color in Video DAC
TBLUE1 = 228, ///< Blue used in translucent rectangles
TBLUE2 = 229, ///< Blue used in translucent rectangles
TBLUE3 = 230, ///< Blue used in translucent rectangles
TBLUE4 = 231, ///< Blue used in translucent rectangles
TALKFONT_COL = 233
};
// some common colors
#define BLACK (TINSEL_RGB(0, 0, 0))
#define WHITE (TINSEL_RGB(MAX_INTENSITY, MAX_INTENSITY, MAX_INTENSITY))
#define RED (TINSEL_RGB(MAX_INTENSITY, 0, 0))
#define GREEN (TINSEL_RGB(0, MAX_INTENSITY, 0))
#define BLUE (TINSEL_RGB(0, 0, MAX_INTENSITY))
#define YELLOW (TINSEL_RGB(MAX_INTENSITY, MAX_INTENSITY, 0))
#define MAGENTA (TINSEL_RGB(MAX_INTENSITY, 0, MAX_INTENSITY))
#define CYAN (TINSEL_RGB(0, MAX_INTENSITY, MAX_INTENSITY))
struct PALETTE {
int32 numColors; ///< number of colors in the palette
COLORREF palRGB[MAX_COLORS]; ///< actual palette colors
byte palette[MAX_COLORS * 3]; ///< actual palette colors (RGB values)
};
/** palette queue structure */
struct PALQ {
SCNHANDLE hPal; ///< handle to palette data struct
int objCount; ///< number of objects using this palette
int posInDAC; ///< palette position in the video DAC
int32 numColors; ///< number of colors in the palette
// Discworld 2 fields
bool bFading; // Whether or not fading
COLORREF palRGB[MAX_COLORS]; // actual palette colors
};
#define PALETTE_MOVED 0x8000 // when this bit is set in the "posInDAC"
// field - the palette entry has moved
// Translucent objects have NULL pPal
#define HasPalMoved(pPal) (((pPal) != NULL) && ((pPal)->posInDAC & PALETTE_MOVED))
/*----------------------------------------------------------------------*\
|* Palette Manager Function Prototypes *|
\*----------------------------------------------------------------------*/
void ResetPalAllocator(); // wipe out all palettes
#ifdef DEBUG
void PaletteStats(); // Shows the maximum number of palettes used at once
#endif
void psxPaletteMapper(PALQ *originalPal, uint8 *psxClut, byte *mapperTable); // Maps PSX CLUTs to original palette in resource file
void PalettesToVideoDAC(); // Update the video DAC with palettes currently in the DAC queue
void UpdateDACqueueHandle(
int posInDAC, // position in video DAC
int numColors, // number of colors in palette
SCNHANDLE hPalette); // handle to palette
void UpdateDACqueue( // places a palette in the video DAC queue
int posInDAC, // position in video DAC
int numColors, // number of colors in palette
COLORREF *pColors); // list of RGB tripples
void UpdateDACqueue(int posInDAC, COLORREF color);
PALQ *AllocPalette( // allocate a new palette
SCNHANDLE hNewPal); // palette to allocate
void FreePalette( // free a palette allocated with "AllocPalette"
PALQ *pFreePal); // palette queue entry to free
PALQ *FindPalette( // find a palette in the palette queue
SCNHANDLE hSrchPal); // palette to search for
void SwapPalette( // swaps palettes at the specified palette queue position
PALQ *pPalQ, // palette queue position
SCNHANDLE hNewPal); // new palette
PALQ *GetNextPalette( // returns the next palette in the queue
PALQ *pStrtPal); // queue position to start from - when NULL will start from beginning of queue
COLORREF GetBgndColor(); // returns current background color
void SetBgndColor( // sets current background color
COLORREF color); // color to set the background to
void FadingPalette(PALQ *pPalQ, bool bFading);
void CreateTranslucentPalette(SCNHANDLE BackPal);
void NoFadingPalettes(); // All fading processes have just been killed
void DimPartPalette(
SCNHANDLE hPal,
int startColor,
int length,
int brightness); // 0 = black, 10 == 100%
int TranslucentColor();
int DarkGreen();
#define BoxColor (TinselVersion == 3 ? DarkGreen : TranslucentColor)
int HighlightColor();
int TalkColor();
void SetTalkColorRef(COLORREF colRef);
COLORREF GetTalkColorRef();
void SetTagColorRef(COLORREF colRef);
COLORREF GetTagColorRef();
void SetTalkTextOffset(int offset);
void SetTranslucencyOffset(int offset);
} // End of namespace Tinsel
#endif // TINSEL_PALETTE_H

1027
engines/tinsel/pcode.cpp Normal file

File diff suppressed because it is too large Load Diff

165
engines/tinsel/pcode.h Normal file
View File

@@ -0,0 +1,165 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* Virtual processor definitions
*/
#ifndef TINSEL_PCODE_H // prevent multiple includes
#define TINSEL_PCODE_H
#include "tinsel/events.h" // for TINSEL_EVENT
#include "tinsel/sched.h" // for Common::PROCESS
namespace Common {
class Serializer;
}
namespace Tinsel {
class InventoryObject;
enum RESUME_STATE {
RES_NOT, RES_1, RES_2, RES_SAVEGAME
};
enum {
PCODE_STACK_SIZE = 128 ///< interpeters stack size
};
enum GSORT {
GS_NONE, GS_ACTOR, GS_MASTER, GS_POLYGON, GS_INVENTORY, GS_SCENE,
GS_PROCESS, GS_GPROCESS
};
enum RESCODE {RES_WAITING, RES_FINISHED, RES_CUTSHORT};
struct WorkaroundEntry;
struct INT_CONTEXT {
// Elements for interpret context management
Common::PROCESS *pProc; ///< processes owning this context
GSORT GSort; ///< sort of this context
// Previously parameters to Interpret()
SCNHANDLE hCode; ///< scene handle of the code to execute
byte *code; ///< pointer to the code to execute
TINSEL_EVENT event; ///< causal event
HPOLYGON hPoly; ///< associated polygon (if any)
int idActor; ///< associated actor (if any)
const InventoryObject *pinvo; ///< associated inventory object
// Previously local variables in Interpret()
int32 stack[PCODE_STACK_SIZE]; ///< interpeters run time stack
int sp; ///< stack pointer
int bp; ///< base pointer
int ip; ///< instruction pointer
bool bHalt; ///< set to exit interpeter
bool escOn;
int myEscape; ///< only initialized to prevent compiler warning!
uint32 waitNumber1; // The waiting numbert
uint32 waitNumber2; // The wait for number
RESCODE resumeCode;
RESUME_STATE resumeState;
// Used to store execution state within a script workaround fragment
const WorkaroundEntry *fragmentPtr;
void syncWithSerializer(Common::Serializer &s);
};
/*----------------------------------------------------------------------*\
|* Interpreter Function Prototypes *|
\*----------------------------------------------------------------------*/
// Interprets the PCODE instructions in the code array
void Interpret(CORO_PARAM, INT_CONTEXT *ic);
INT_CONTEXT *InitInterpretContext(
GSORT gsort,
SCNHANDLE hCode, // code to execute
TINSEL_EVENT event, // causal event
HPOLYGON hpoly, // associated polygon (if any)
int actorid, // associated actor (if any)
const InventoryObject *pinvo,
int myEscape = -1); // associated inventory object
INT_CONTEXT *RestoreInterpretContext(INT_CONTEXT *ric);
void FreeMostInterpretContexts();
void FreeMasterInterpretContext();
void SaveInterpretContexts(INT_CONTEXT *sICInfo);
void RegisterGlobals(int num);
void FreeGlobals();
void AttachInterpret(INT_CONTEXT *pic, Common::PROCESS *pProc);
void WaitInterpret(CORO_PARAM, Common::PPROCESS pWaitProc, bool *result);
#define NUM_INTERPRET (CORO_NUM_PROCESS - 20)
#define MAX_INTERPRET (CORO_MAX_PROCESSES - 20)
/*----------------------------------------------------------------------*\
|* Library Procedure and Function codes parameter enums *|
\*----------------------------------------------------------------------*/
#define TAG_DEF 0 // For tagactor()
#define TAG_Q1TO3 1 // tag types
#define TAG_Q1TO4 2 // tag types
#define CONV_DEF 0 //
#define CONV_BOTTOM 1 // conversation() parameter
#define CONV_END 2 //
#define CONTROL_OFF 0 // control()
#define CONTROL_ON 1 // parameter
#define CONTROL_OFFV 2 //
#define CONTROL_OFFV2 3 //
#define CONTROL_STARTOFF 4 //
#define NULL_ACTOR (-1) // For actor parameters
#define LEAD_ACTOR (-2) //
#define RAND_NORM 0 // For random() frills
#define RAND_NORPT 1 //
#define D_UP 1
#define D_DOWN 0
#define TW_START 1 // topwindow() parameter
#define TW_END 2 //
#define MIDI_DEF 0
#define MIDI_LOOP 1
#define FM_IN 0 //
#define FM_OUT 1 // fademidi()
#define FG_ON 0 //
#define FG_OFF 1 // FrameGrab()
#define ST_ON 0 //
#define ST_OFF 1 // SubTitles()
} // End of namespace Tinsel
#endif // TINSEL_PCODE_H

846
engines/tinsel/pdisplay.cpp Normal file
View File

@@ -0,0 +1,846 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* CursorPositionProcess()
* TagProcess()
* PointProcess()
*/
#include "common/coroutines.h"
#include "tinsel/actors.h"
#include "tinsel/background.h"
#include "tinsel/cursor.h"
#include "tinsel/dw.h"
#include "tinsel/events.h"
#include "tinsel/font.h"
#include "tinsel/graphics.h"
#include "tinsel/multiobj.h"
#include "tinsel/object.h"
#include "tinsel/pcode.h"
#include "tinsel/polygons.h"
#include "tinsel/movers.h"
#include "tinsel/sched.h"
#include "tinsel/strres.h"
#include "tinsel/text.h"
#include "tinsel/tinsel.h"
#include "common/textconsole.h"
namespace Tinsel {
//----------------- EXTERNAL GLOBAL DATA --------------------
#ifdef DEBUG
//extern int Overrun; // The overrun counter, in DOS_DW.C
extern int g_newestString; // The overrun counter, in STRRES.C
#endif
//----------------- LOCAL DEFINES --------------------
enum HotSpotTag {
NO_HOTSPOT_TAG,
POLY_HOTSPOT_TAG,
ACTOR_HOTSPOT_TAG
};
//----------------- LOCAL GLOBAL DATA --------------------
// These vars are reset upon engine destruction
static bool g_DispPath = false;
static bool g_bShowString = false;
static int g_TaggedActor = 0;
static HPOLYGON g_hTaggedPolygon = NOPOLY;
static bool g_bTagsActive = true;
static bool g_bPointingActive = true;
static int tagX = 0, tagY = 0; // Values when tag was displayed
static int Loffset = 0, Toffset = 0; // Values when tag was displayed
static int curX = 0, curY = 0;
void ResetVarsPDisplay() {
g_DispPath = false;
g_bShowString = false;
g_TaggedActor = 0;
g_hTaggedPolygon = NOPOLY;
g_bTagsActive = true;
g_bPointingActive = true;
tagX = tagY = 0;
Loffset = Toffset = 0;
curX = curY = 0;
}
#ifdef DEBUG
/**
* Displays the cursor and lead actor's co-ordinates and the overrun
* counter. Also which path polygon the cursor is in, if required.
*
* This process is only started up if a Glitter showpos() call is made.
* Obviously, this is for testing purposes only...
*/
void CursorPositionProcess(CORO_PARAM, const void *) {
// COROUTINE
CORO_BEGIN_CONTEXT;
int prevsX, prevsY; // Last screen top left
int prevcX, prevcY; // Last displayed cursor position
int prevlX, prevlY; // Last displayed lead actor position
// int prevOver; // Last displayed overrun
int prevString; // Last displayed string number
OBJECT *cpText; // cursor position text object pointer
OBJECT *cpathText; // cursor path text object pointer
OBJECT *rpText; // text object pointer
// OBJECT *opText; // text object pointer
OBJECT *spText; // string number text object pointer
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
_ctx->prevsX = -1;
_ctx->prevsY = -1;
_ctx->prevcX = -1;
_ctx->prevcY = -1;
_ctx->prevlX = -1;
_ctx->prevlY = -1;
// _ctx->prevOver = -1;
_ctx->prevString = -1;
_ctx->cpText = nullptr;
_ctx->cpathText = nullptr;
_ctx->rpText = nullptr;
// _ctx->opText = nullptr;
_ctx->spText = nullptr;
int aniX, aniY; // cursor/lead actor position
int Loffset, Toffset; // Screen top left
char PositionString[64]; // Common::sprintf_s() things into here
MOVER *pActor; // Lead actor
while (1) {
_vm->_bg->PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
/*-----------------------------------*\
| Cursor's position and path display. |
\*-----------------------------------*/
_vm->_cursor->GetCursorXY(&aniX, &aniY, false);
// Change in cursor position?
if (aniX != _ctx->prevcX || aniY != _ctx->prevcY ||
Loffset != _ctx->prevsX || Toffset != _ctx->prevsY) {
// kill current text objects
if (_ctx->cpText) {
MultiDeleteObject(_vm->_bg->GetPlayfieldList(FIELD_STATUS), _ctx->cpText);
}
if (_ctx->cpathText) {
MultiDeleteObject(_vm->_bg->GetPlayfieldList(FIELD_STATUS), _ctx->cpathText);
_ctx->cpathText = nullptr;
}
// New text objects
Common::sprintf_s(PositionString, "%d %d", aniX + Loffset, aniY + Toffset);
_ctx->cpText = ObjectTextOut(_vm->_bg->GetPlayfieldList(FIELD_STATUS), PositionString,
0, CPOSX, POSY, _vm->_font->GetTagFontHandle(), TXT_CENTER);
if (g_DispPath) {
HPOLYGON hp = InPolygon(aniX + Loffset, aniY + Toffset, PATH);
if (hp == NOPOLY)
Common::sprintf_s(PositionString, "No path");
else
Common::sprintf_s(PositionString, "%d,%d %d,%d %d,%d %d,%d",
PolyCornerX(hp, 0), PolyCornerY(hp, 0),
PolyCornerX(hp, 1), PolyCornerY(hp, 1),
PolyCornerX(hp, 2), PolyCornerY(hp, 2),
PolyCornerX(hp, 3), PolyCornerY(hp, 3));
_ctx->cpathText = ObjectTextOut(_vm->_bg->GetPlayfieldList(FIELD_STATUS), PositionString,
0, 4, POSY+ 10, _vm->_font->GetTagFontHandle(), 0);
}
// update previous position
_ctx->prevcX = aniX;
_ctx->prevcY = aniY;
}
#if 0
/*------------------------*\
| Overrun counter display. |
\*------------------------*/
if (Overrun != _ctx->prevOver) {
// kill current text objects
if (_ctx->opText) {
MultiDeleteObject(_vm->_bg->GetPlayfieldList(FIELD_STATUS), _ctx->opText);
}
Common::sprintf_s(PositionString, "%d", Overrun);
_ctx->opText = ObjectTextOut(_vm->_bg->GetPlayfieldList(FIELD_STATUS), PositionString,
0, OPOSX, POSY, GetTagFontHandle(), TXT_CENTER);
// update previous value
_ctx->prevOver = Overrun;
}
#endif
/*----------------------*\
| Lead actor's position. |
\*----------------------*/
pActor = GetMover(LEAD_ACTOR);
if (pActor && getMActorState(pActor)) {
// get lead's animation position
_vm->_actor->GetActorPos(LEAD_ACTOR, &aniX, &aniY);
// Change in position?
if (aniX != _ctx->prevlX || aniY != _ctx->prevlY ||
Loffset != _ctx->prevsX || Toffset != _ctx->prevsY) {
// Kill current text objects
if (_ctx->rpText) {
MultiDeleteObject(_vm->_bg->GetPlayfieldList(FIELD_STATUS), _ctx->rpText);
}
// create new text object list
Common::sprintf_s(PositionString, "%d %d", aniX, aniY);
_ctx->rpText = ObjectTextOut(_vm->_bg->GetPlayfieldList(FIELD_STATUS), PositionString,
0, LPOSX, POSY, _vm->_font->GetTagFontHandle(), TXT_CENTER);
// update previous position
_ctx->prevlX = aniX;
_ctx->prevlY = aniY;
}
}
/*-------------*\
| String number |
\*-------------*/
if (g_bShowString && g_newestString != _ctx->prevString) {
// kill current text objects
if (_ctx->spText) {
MultiDeleteObject(_vm->_bg->GetPlayfieldList(FIELD_STATUS), _ctx->spText);
}
Common::sprintf_s(PositionString, "String: %d", g_newestString);
_ctx->spText = ObjectTextOut(_vm->_bg->GetPlayfieldList(FIELD_STATUS), PositionString,
0, SPOSX, POSY+10, _vm->_font->GetTalkFontHandle(), TXT_CENTER);
// update previous value
_ctx->prevString = g_newestString;
}
// update previous playfield position
_ctx->prevsX = Loffset;
_ctx->prevsY = Toffset;
CORO_SLEEP(1); // allow re-scheduling
}
CORO_END_CODE;
}
#endif
/**
* While inventory/menu is open.
*/
void DisablePointing() {
int i;
HPOLYGON hPoly; // Polygon handle
g_bPointingActive = false;
for (i = 0; i < MAX_POLY; i++) {
hPoly = GetPolyHandle(i);
if (hPoly != NOPOLY && PolyType(hPoly) == TAG && PolyIsPointedTo(hPoly)) {
SetPolyPointedTo(hPoly, false);
SetPolyTagWanted(hPoly, false, false, 0);
PolygonEvent(Common::nullContext, hPoly, UNPOINT, 0, false, 0);
}
}
// For each tagged actor
for (i = 0; (i = _vm->_actor->NextTaggedActor(i)) != 0;) {
if (_vm->_actor->ActorIsPointedTo(i)) {
_vm->_actor->SetActorPointedTo(i, false);
_vm->_actor->SetActorTagWanted(i, false, false, 0);
ActorEvent(Common::nullContext, i, UNPOINT, false, 0);
}
}
}
/**
* EnablePointing()
*/
void EnablePointing() {
g_bPointingActive = true;
}
/**
* Tag process keeps us updated as to which tagged actor is currently tagged
* (if one is). Tag process asks us for this information, as does ProcessUserEvent().
*/
static void SaveTaggedActor(int ano) {
g_TaggedActor = ano;
}
/**
* Tag process keeps us updated as to which tagged actor is currently tagged
* (if one is). Tag process asks us for this information, as does ProcessUserEvent().
*/
int GetTaggedActor() {
return g_TaggedActor;
}
/**
* Tag process keeps us updated as to which polygon is currently tagged
* (if one is). Tag process asks us for this information, as does ProcessUserEvent().
*/
static void SaveTaggedPoly(HPOLYGON hp) {
g_hTaggedPolygon = hp;
}
HPOLYGON GetTaggedPoly() {
return g_hTaggedPolygon;
}
/**
* Given cursor position and an actor number, ascertains whether the
* cursor is within the actor's tag area.
* Returns TRUE for a positive result, FALSE for negative.
* If TRUE, the mid-top co-ordinates of the actor's tag area are also
* returned.
*/
static bool InHotSpot(int ano, int aniX, int aniY, int *pxtext, int *pytext) {
int Top, Bot; // Top and bottom limits of active area
int left, right; // left and right of active area
int qrt = 0; // 1/4 of height (sometimes 1/2)
// First check if within x-range
if (aniX > (left = _vm->_actor->GetActorLeft(ano)) && aniX < (right = _vm->_actor->GetActorRight(ano))) {
Top = _vm->_actor->GetActorTop(ano);
Bot = _vm->_actor->GetActorBottom(ano);
// y-range varies according to tag-type
switch (_vm->_actor->TagType(ano)) {
case TAG_DEF:
// Next to bottom 1/4 of the actor's area
qrt = (Bot - Top) >> 1; // Half actor's height
Top += qrt; // Top = mid-height
qrt = qrt >> 1; // Quarter height
Bot -= qrt; // Bot = 1/4 way up
break;
case TAG_Q1TO3:
// Top 3/4 of the actor's area
qrt = (Bot - Top) >> 2; // 1/4 actor's height
Bot -= qrt; // Bot = 1/4 way up
break;
case TAG_Q1TO4:
// All the actor's area
break;
default:
error("illegal tag area type");
}
// Now check if within y-range
if (aniY >= Top && aniY <= Bot) {
if (_vm->_actor->TagType(ano) == TAG_Q1TO3)
*pytext = Top + qrt;
else
*pytext = Top;
*pxtext = (left + right) / 2;
return true;
}
}
return false;
}
/**
* See if the cursor is over a tagged actor's hot-spot. If so, display
* the tag or, if tag already displayed, maintain the tag's position on
* the screen.
*/
static bool ActorTag(int curX_, int curY_, HotSpotTag *pTag, OBJECT **ppText) {
int newX, newY; // new values, to keep tag in place
int ano;
int xtext, ytext;
bool newActor;
char tagBuffer[64];
if (TinselVersion >= 2) {
// Tinsel 2 version
// Get the foremost pointed to actor
int actor = _vm->_actor->FrontTaggedActor();
if (actor == 0) {
SaveTaggedActor(0);
return false;
}
// If new actor
// or actor has suddenly decided it wants tagging...
if (actor != GetTaggedActor() || (_vm->_actor->ActorTagIsWanted(actor) && !*ppText)) {
// Put up actor tag
SaveTaggedActor(actor); // This actor tagged
SaveTaggedPoly(NOPOLY); // No tagged polygon
MultiDeleteObjectIfExists(FIELD_STATUS, ppText);
if (_vm->_actor->ActorTagIsWanted(actor)) {
_vm->_actor->GetActorTagPos(actor, &tagX, &tagY, false);
LoadStringRes(_vm->_actor->GetActorTagHandle(actor), tagBuffer, sizeof(tagBuffer));
// May have buggered cursor
_vm->_cursor->EndCursorFollowed();
*ppText = ObjectTextOut(_vm->_bg->GetPlayfieldList(FIELD_STATUS), tagBuffer,
0, tagX, tagY, _vm->_font->GetTagFontHandle(), TXT_CENTER, 0);
assert(*ppText);
MultiSetZPosition(*ppText, Z_TAG_TEXT);
} else
*ppText = nullptr;
} else if (*ppText) {
// Same actor, maintain tag position
_vm->_actor->GetActorTagPos(actor, &newX, &newY, false);
if (newX != tagX || newY != tagY) {
MultiMoveRelXY(*ppText, newX - tagX, newY - tagY);
tagX = newX;
tagY = newY;
}
}
return true;
}
// Tinsel 1 version
// For each actor with a tag....
_vm->_actor->FirstTaggedActor();
while ((ano = _vm->_actor->NextTaggedActor()) != 0) {
if (InHotSpot(ano, curX_, curY_, &xtext, &ytext)) {
// Put up or maintain actor tag
if (*pTag != ACTOR_HOTSPOT_TAG)
newActor = true;
else if (ano != GetTaggedActor())
newActor = true; // Different actor
else
newActor = false; // Same actor
if (newActor) {
// Display actor's tag
MultiDeleteObjectIfExists(FIELD_STATUS, ppText);
*pTag = ACTOR_HOTSPOT_TAG;
SaveTaggedActor(ano); // This actor tagged
SaveTaggedPoly(NOPOLY); // No tagged polygon
_vm->_bg->PlayfieldGetPos(FIELD_WORLD, &tagX, &tagY);
LoadStringRes(_vm->_actor->GetActorTag(ano), _vm->_font->TextBufferAddr(), TBUFSZ);
*ppText = ObjectTextOut(_vm->_bg->GetPlayfieldList(FIELD_STATUS), _vm->_font->TextBufferAddr(),
0, xtext - tagX, ytext - tagY, _vm->_font->GetTagFontHandle(), TXT_CENTER);
assert(*ppText); // Actor tag string produced NULL text
MultiSetZPosition(*ppText, Z_TAG_TEXT);
} else {
// Maintain actor tag's position
_vm->_bg->PlayfieldGetPos(FIELD_WORLD, &newX, &newY);
if (newX != tagX || newY != tagY) {
MultiMoveRelXY(*ppText, tagX - newX, tagY - newY);
tagX = newX;
tagY = newY;
}
}
return true;
}
}
// No tagged actor
if (*pTag == ACTOR_HOTSPOT_TAG) {
*pTag = NO_HOTSPOT_TAG;
SaveTaggedActor(0);
}
return false;
}
/**
* Perhaps some comment in due course.
*
* Under control of PointProcess(), when the cursor is over a TAG or
* EXIT polygon, its pointState flag is set to POINTING. If its Glitter
* code contains a printtag() call, its tagState flag gets set to TAG_ON.
*/
static bool PolyTag(HotSpotTag *pTag, OBJECT **ppText) {
int nLoff, nToff; // new values, to keep tag in place
HPOLYGON hp;
bool newPoly;
int shift;
int tagx, tagy; // Tag display co-ordinates
SCNHANDLE hTagtext; // Tag text
// For each polgon with a tag....
for (int i = 0; i < MAX_POLY; i++) {
hp = GetPolyHandle(i);
if ((TinselVersion >= 2) && (hp == NOPOLY))
continue;
// Added code for un-tagged tags
if ((hp != NOPOLY) && (PolyPointState(hp) == PS_POINTING) && (PolyTagState(hp) != TAG_ON)) {
// This poly is entitled to be tagged
if (hp != GetTaggedPoly()) {
MultiDeleteObjectIfExists(FIELD_STATUS, ppText);
*pTag = POLY_HOTSPOT_TAG;
SaveTaggedActor(0); // No tagged actor
SaveTaggedPoly(hp); // This polygon tagged
}
return true;
} else if (((TinselVersion >= 2) && PolyTagIsWanted(hp)) ||
((TinselVersion <= 1) && hp != NOPOLY && PolyTagState(hp) == TAG_ON)) {
// Put up or maintain polygon tag
newPoly = false;
if (TinselVersion >= 2) {
if (hp != GetTaggedPoly())
newPoly = true; // Different polygon
} else {
if (*pTag != POLY_HOTSPOT_TAG)
newPoly = true; // A new polygon (no current)
else if (hp != GetTaggedPoly())
newPoly = true; // Different polygon
}
if (newPoly) {
if (*ppText)
MultiDeleteObject(_vm->_bg->GetPlayfieldList(FIELD_STATUS), *ppText);
if (TinselVersion <= 1)
*pTag = POLY_HOTSPOT_TAG;
SaveTaggedActor(0); // No tagged actor
SaveTaggedPoly(hp); // This polygon tagged
_vm->_bg->PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
GetTagTag(hp, &hTagtext, &tagx, &tagy);
int strLen;
if (GetPolyTagHandle(hp) != 0)
strLen = LoadStringRes(GetPolyTagHandle(hp), _vm->_font->TextBufferAddr(), TBUFSZ);
else
strLen = LoadStringRes(hTagtext, _vm->_font->TextBufferAddr(), TBUFSZ);
if (strLen == 0)
// No valid string returned, so leave ppText as NULL
ppText = nullptr;
else if ((TinselVersion >= 2) && !PolyTagFollowsCursor(hp)) {
// May have buggered cursor
_vm->_cursor->EndCursorFollowed();
*ppText = ObjectTextOut(_vm->_bg->GetPlayfieldList(FIELD_STATUS),
_vm->_font->TextBufferAddr(), 0, tagx - Loffset, tagy - Toffset,
_vm->_font->GetTagFontHandle(), TXT_CENTER, 0);
} else if (TinselVersion >= 2) {
// Bugger cursor
const char *tagPtr = _vm->_font->TextBufferAddr();
if (tagPtr[0] < ' ' && tagPtr[1] == EOS_CHAR)
_vm->_cursor->StartCursorFollowed();
_vm->_cursor->GetCursorXYNoWait(&curX, &curY, false);
*ppText = ObjectTextOut(_vm->_bg->GetPlayfieldList(FIELD_STATUS), _vm->_font->TextBufferAddr(),
0, curX, curY, _vm->_font->GetTagFontHandle(), TXT_CENTER, 0);
} else {
// Handle displaying the tag text on-screen
*ppText = ObjectTextOut(_vm->_bg->GetPlayfieldList(FIELD_STATUS), _vm->_font->TextBufferAddr(),
0, tagx - Loffset, tagy - Toffset,
_vm->_font->GetTagFontHandle(), TXT_CENTER);
assert(*ppText); // Polygon tag string produced NULL text
}
// DW1 has some tags without text, e.g. the "equals" button when talking to the guard in act 3
if (ppText) {
MultiSetZPosition(*ppText, Z_TAG_TEXT);
/*
* New feature: Don't go off the side of the background
*/
shift = MultiRightmost(*ppText) + Loffset + 2;
if (shift >= _vm->_bg->BgWidth()) // Not off right
MultiMoveRelXY(*ppText, _vm->_bg->BgWidth() - shift, 0);
shift = MultiLeftmost(*ppText) + Loffset - 1;
if (shift <= 0) // Not off left
MultiMoveRelXY(*ppText, -shift, 0);
shift = MultiLowest(*ppText) + Toffset;
if (shift > _vm->_bg->BgHeight()) // Not off bottom
MultiMoveRelXY(*ppText, 0, _vm->_bg->BgHeight() - shift);
}
} else if ((TinselVersion >= 2) && (*ppText)) {
if (!PolyTagFollowsCursor(hp)) {
_vm->_bg->PlayfieldGetPos(FIELD_WORLD, &nLoff, &nToff);
if (nLoff != Loffset || nToff != Toffset) {
MultiMoveRelXY(*ppText, Loffset - nLoff, Toffset - nToff);
Loffset = nLoff;
Toffset = nToff;
}
} else {
_vm->_cursor->GetCursorXY(&tagx, &tagy, false);
if (tagx != curX || tagy != curY) {
MultiMoveRelXY(*ppText, tagx - curX, tagy - curY);
curX = tagx;
curY = tagy;
}
}
} else if (TinselVersion <= 1) {
_vm->_bg->PlayfieldGetPos(FIELD_WORLD, &nLoff, &nToff);
if (nLoff != Loffset || nToff != Toffset) {
MultiMoveRelXY(*ppText, Loffset - nLoff, Toffset - nToff);
Loffset = nLoff;
Toffset = nToff;
}
}
return true;
}
}
// No tagged polygon
if (TinselVersion >= 2)
SaveTaggedPoly(NOPOLY);
else if (*pTag == POLY_HOTSPOT_TAG) {
*pTag = NO_HOTSPOT_TAG;
SaveTaggedPoly(NOPOLY);
}
return false;
}
/**
* Handle display of tagged actor and polygon tags.
* Tagged actor's get priority over polygons.
*/
void TagProcess(CORO_PARAM, const void *) {
// COROUTINE
CORO_BEGIN_CONTEXT;
OBJECT *pText; // text object pointer
HotSpotTag Tag;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
_ctx->pText = nullptr;
_ctx->Tag = NO_HOTSPOT_TAG;
SaveTaggedActor(0); // No tagged actor yet
SaveTaggedPoly(NOPOLY); // No tagged polygon yet
while (1) {
if (g_bTagsActive) {
int curX_, curY_; // cursor position
while (!_vm->_cursor->GetCursorXYNoWait(&curX_, &curY_, true))
CORO_SLEEP(1);
if (!ActorTag(curX_, curY_, &_ctx->Tag, &_ctx->pText)
&& !PolyTag(&_ctx->Tag, &_ctx->pText)) {
// Nothing tagged. Remove tag, if there is one
if (_ctx->pText) {
MultiDeleteObject(_vm->_bg->GetPlayfieldList(FIELD_STATUS), _ctx->pText);
_ctx->pText = nullptr;
if (TinselVersion >= 2)
// May have buggered cursor
_vm->_cursor->EndCursorFollowed();
}
}
} else {
SaveTaggedActor(0);
SaveTaggedPoly(NOPOLY);
// Remove tag, if there is one
if (_ctx->pText) {
// kill current text objects
MultiDeleteObjectIfExists(FIELD_STATUS, &_ctx->pText);
_ctx->Tag = NO_HOTSPOT_TAG;
}
}
CORO_SLEEP(1); // allow re-scheduling
}
CORO_END_CODE;
}
/**
* Called from PointProcess() as appropriate.
*/
static void enteringpoly(CORO_PARAM, HPOLYGON hp) {
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
SetPolyPointState(hp, PS_POINTING);
if (TinselVersion >= 2)
CORO_INVOKE_ARGS(PolygonEvent, (CORO_SUBCTX, hp, POINTED, 0, false, 0));
else
RunPolyTinselCode(hp, POINTED, PLR_NOEVENT, false);
CORO_END_CODE;
}
/**
* Called from PointProcess() as appropriate.
*/
static void leavingpoly(CORO_PARAM, HPOLYGON hp) {
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
SetPolyPointState(hp, PS_NOT_POINTING);
if (TinselVersion >= 2) {
CORO_INVOKE_ARGS(PolygonEvent, (CORO_SUBCTX, hp, UNPOINT, 0, false, 0));
SetPolyTagWanted(hp, false, false, 0);
} else if (PolyTagState(hp) == TAG_ON) {
// Delete this tag entry
SetPolyTagState(hp, TAG_OFF);
}
CORO_END_CODE;
}
/**
* For TAG and EXIT polygons, monitor cursor entering and leaving.
* Maintain the polygons' pointState and tagState flags accordingly.
* Also run the polygon's Glitter code when the cursor enters.
*/
void PointProcess(CORO_PARAM, const void *) {
// COROUTINE
CORO_BEGIN_CONTEXT;
HPOLYGON hPoly;
int i;
int curX, curY; // cursor/tagged actor position
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
if (TinselVersion >= 2)
EnablePointing();
while (1) {
while (!_vm->_cursor->GetCursorXYNoWait(&_ctx->curX, &_ctx->curY, true))
CORO_SLEEP(1);
/*----------------------------------*\
| For polygons of type TAG and EXIT. |
\*----------------------------------*/
for (_ctx->i = 0; _ctx->i < MAX_POLY; _ctx->i++) {
_ctx->hPoly = GetPolyHandle(_ctx->i);
if ((_ctx->hPoly == NOPOLY) || ((PolyType(_ctx->hPoly) != TAG) &&
(PolyType(_ctx->hPoly) != EXIT)))
continue;
if (!PolyIsPointedTo(_ctx->hPoly)) {
if (IsInPolygon(_ctx->curX, _ctx->curY, _ctx->hPoly)) {
if (TinselVersion >= 2) {
SetPolyPointedTo(_ctx->hPoly, true);
CORO_INVOKE_ARGS(PolygonEvent, (CORO_SUBCTX, _ctx->hPoly, POINTED, 0, false, 0));
} else {
CORO_INVOKE_1(enteringpoly, _ctx->hPoly);
}
}
} else {
if (!IsInPolygon(_ctx->curX, _ctx->curY, _ctx->hPoly)) {
if (TinselVersion >= 2) {
SetPolyPointedTo(_ctx->hPoly, false);
SetPolyTagWanted(_ctx->hPoly, false, false, 0);
CORO_INVOKE_ARGS(PolygonEvent, (CORO_SUBCTX, _ctx->hPoly, UNPOINT, 0, false, 0));
} else {
CORO_INVOKE_1(leavingpoly, _ctx->hPoly);
}
}
}
}
if (TinselVersion >= 2) {
// For each tagged actor
for (_ctx->i = 0; (_ctx->i = _vm->_actor->NextTaggedActor(_ctx->i)) != 0;) {
if (!_vm->_actor->ActorIsPointedTo(_ctx->i)) {
if (_vm->_actor->InHotSpot(_ctx->i, _ctx->curX, _ctx->curY)) {
_vm->_actor->SetActorPointedTo(_ctx->i, true);
CORO_INVOKE_ARGS(ActorEvent, (CORO_SUBCTX, _ctx->i, POINTED, false, 0));
}
} else {
if (!_vm->_actor->InHotSpot(_ctx->i, _ctx->curX, _ctx->curY)) {
_vm->_actor->SetActorPointedTo(_ctx->i, false);
_vm->_actor->SetActorTagWanted(_ctx->i, false, false, 0);
CORO_INVOKE_ARGS(ActorEvent, (CORO_SUBCTX, _ctx->i, UNPOINT, false, 0));
}
}
}
// allow re-scheduling
do {
CORO_SLEEP(1);
} while (!g_bPointingActive);
} else {
// allow re-scheduling
CORO_SLEEP(1);
}
}
CORO_END_CODE;
}
void DisableTags() {
g_bTagsActive = false;
}
void EnableTags() {
g_bTagsActive = true;
}
bool DisableTagsIfEnabled() {
if (g_bTagsActive) {
DisableTags();
return true;
} else
return false;
}
/**
* For testing purposes only.
* Causes CursorPositionProcess() to display, or not, the path that the
* cursor is in.
*/
void TogglePathDisplay() {
g_DispPath ^= 1; // Toggle path display (XOR with true)
}
void setshowstring() {
g_bShowString = true;
}
} // End of namespace Tinsel

36
engines/tinsel/pdisplay.h Normal file
View File

@@ -0,0 +1,36 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* Tag related methods
*/
#ifndef TINSEL_PDISPLAY_H // prevent multiple includes
#define TINSEL_PDISPLAY_H
namespace Tinsel {
void EnableTags();
void DisableTags();
void DisablePointing();
void EnablePointing();
} // End of namespace Tinsel
#endif /* TINSEL_PDISPLAY_H */

65
engines/tinsel/pid.h Normal file
View File

@@ -0,0 +1,65 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* List of all process identifiers
*/
#ifndef TINSEL_PID_H // prevent multiple includes
#define TINSEL_PID_H
namespace Tinsel {
#define PID_DESTROY 0x8000 // process id of any process that is to be destroyed between scenes
#define PID_EFFECTS (0x0010 | PID_DESTROY) // generic special effects process id
#define PID_FADER (PID_EFFECTS + 4) // fader process
#define PID_MOUSE 0x0030 // mouse button checking process id
#define PID_KEYBOARD 0x0050 // keyboard scanning process
#define PID_CURSOR 0x0060 // cursor process
#define PID_SCROLL (0x0070 | PID_DESTROY) // scroll process
#define PID_INVENTORY 0x0080 // inventory process
#define PID_POSITION (0x0090 | PID_DESTROY) // cursor position process
#define PID_TAG (0x00A0 | PID_DESTROY) // tag process
#define PID_TCODE (0x00B0 | PID_DESTROY) // tinsel code process
#define PID_MASTER_SCR 0x00C0 // tinsel master script process
#define PID_MOVER (0x00D0 | PID_DESTROY) // moving actor process
#define PID_REEL (0x00E0 | PID_DESTROY) // process for each film reel
#define PID_BTN_CLICK 0x110 // process to handle mouse button clicks
#define PID_PROCESS (((TinselVersion == 3) ? 0x0100 : 0x0110) | PID_DESTROY) // Scene process base
#define PID_GPROCESS ((TinselVersion == 3) ? ( 0x110 | PID_DESTROY) : 0x0120) // Global process base
// distinction introduced by noir
#define PID_SCENE ((TinselVersion == 3) ? (0x0001 | PID_TCODE) : PID_TCODE) // Root scene process
} // End of namespace Tinsel
#endif // TINSEL_PID_H

1186
engines/tinsel/play.cpp Normal file

File diff suppressed because it is too large Load Diff

59
engines/tinsel/play.h Normal file
View File

@@ -0,0 +1,59 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* Plays films within a scene, takes into account the actor in each 'column'.
*/
#ifndef TINSEL_PLAY_H // prevent multiple includes
#define TINSEL_PLAY_H
#include "common/coroutines.h"
#include "tinsel/dw.h"
#include "tinsel/multiobj.h"
namespace Tinsel {
#define MAX_SOUNDREELS 10
struct SOUNDREELS {
SCNHANDLE hFilm; // The 'film'
int column; // Column number
int actorCol;
};
void PlayFilm(CORO_PARAM, SCNHANDLE film, int x, int y, int actorid, bool splay, bool sfact, bool escOn, int myescEvent, bool bTop, OBJECT** playfield);
void PlayFilm(CORO_PARAM, SCNHANDLE hFilm, int x, int y, int myescEvent, bool bTop, OBJECT** playfield);
void PlayFilmc(CORO_PARAM, SCNHANDLE hFilm, int x, int y, int actorid, bool splay, bool sfact, bool escOn, int myescEvent, bool bTop, OBJECT** playfield);
void RestoreActorReels(SCNHANDLE hFilm, short reelnum, short z, int x, int y);
void RestoreActorReels(SCNHANDLE hFilm, int actor, int x, int y);
void PokeInPalette(const MULTI_INIT *pmi);
void NoSoundReels();
void SaveSoundReels(SOUNDREELS *psr);
void RestoreSoundReels(SOUNDREELS *psr);
int ExtractActor(SCNHANDLE hFilm);
} // End of namespace Tinsel
#endif

2587
engines/tinsel/polygons.cpp Normal file

File diff suppressed because it is too large Load Diff

193
engines/tinsel/polygons.h Normal file
View File

@@ -0,0 +1,193 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* Definition of POLYGON structure and functions in POLYGONS.C
*/
#ifndef TINSEL_POLYGONS_H // prevent multiple includes
#define TINSEL_POLYGONS_H
#include "tinsel/dw.h" // for SCNHANDLE
#include "tinsel/scene.h" // for PPOLY and REEL
namespace Tinsel {
// Polygon Types
enum PTYPE {
// Tinsel 2 Polygon type list
TEST,
BLOCK, EFFECT, PATH, REFER, TAG,
EX_BLOCK, EX_EFFECT, EX_PATH, EX_REFER, EX_TAG,
// Extra polygon types from Tinsel v1
EXIT, EX_EXIT,
// Extra polygon types from Tinsel v3
SCALE, EX_SCALE, SHAPE
};
// subtype
enum {
NORMAL = 0,
NODE = 1 // For paths
};
// tagFlags
enum {
POINTING = 0x01,
TAGWANTED = 0x02,
FOLLOWCURSOR = 0x04
};
// tagState
enum TSTATE {
TAG_OFF, TAG_ON
};
// pointState
enum PSTATE {
PS_NO_POINT, PS_NOT_POINTING, PS_POINTING
};
enum {
NOPOLY = -1
};
struct POLY_VOLATILE {
bool bDead;
short xoff, yoff; // Polygon offset
};
/*-------------------------------------------------------------------------*/
bool IsInPolygon(int xt, int yt, HPOLYGON p);
HPOLYGON InPolygon(int xt, int yt, PTYPE type);
void BlockingCorner(HPOLYGON poly, int *x, int *y, int tarx, int tary);
void FindBestPoint(HPOLYGON path, int *x, int *y, int *line);
bool IsAdjacentPath(HPOLYGON path1, HPOLYGON path2);
HPOLYGON GetPathOnTheWay(HPOLYGON from, HPOLYGON to);
int NearestEndNode(HPOLYGON path, int x, int y);
int NearEndNode(HPOLYGON spath, HPOLYGON dpath);
int NearestNodeWithin(HPOLYGON npath, int x, int y);
void NearestCorner(int *x, int *y, HPOLYGON spath, HPOLYGON dpath);
bool IsPolyCorner(HPOLYGON hPath, int x, int y);
int GetScale(HPOLYGON path, int y);
int GetBrightness(HPOLYGON hPath, int y);
void getNpathNode(HPOLYGON npath, int node, int *px, int *py);
SCNHANDLE GetPolyFilm(HPOLYGON p);
void GetPolyNode(HPOLYGON hp, int *pNodeX, int *pNodeY);
SCNHANDLE GetPolyScript(HPOLYGON p);
REEL GetPolyReelType(HPOLYGON p);
int32 GetPolyZfactor(HPOLYGON p);
int numNodes(HPOLYGON pp);
void RebootDeadTags();
void DisableBlock(int block);
void EnableBlock(int block);
void DisableEffect(int effect);
void EnableEffect(int effect);
void DisablePath(int path);
void EnablePath(int path);
void DisableRefer(int refer);
void EnableRefer(int refer);
HPOLYGON GetTagHandle(int tagno);
void DisableTag(CORO_PARAM, int tag);
void EnableTag(CORO_PARAM, int tag);
void DisableExit(int exitno);
void EnableExit(int exitno);
HPOLYGON FirstPathPoly();
HPOLYGON GetPolyHandle(int i);
void InitPolygons(SCNHANDLE ph, int numPoly, bool bRestart);
void DropPolygons();
void SaveDeadPolys(bool *sdp);
void RestoreDeadPolys(bool *sdp);
void SavePolygonStuff(POLY_VOLATILE *sps);
void RestorePolygonStuff(POLY_VOLATILE *sps);
/*-------------------------------------------------------------------------*/
PTYPE PolyType(HPOLYGON hp); // ->type
int PolySubtype(HPOLYGON hp); // ->subtype
int PolyCenterX(HPOLYGON hp); // ->pcenterx
int PolyCenterY(HPOLYGON hp); // ->pcentery
int PolyCornerX(HPOLYGON hp, int n); // ->cx[n]
int PolyCornerY(HPOLYGON hp, int n); // ->cy[n]
PSTATE PolyPointState(HPOLYGON hp); // ->pointState
TSTATE PolyTagState(HPOLYGON hp); // ->tagState
void SetPolyPointState(HPOLYGON hp, PSTATE ps); // ->pointState
void SetPolyTagState(HPOLYGON hp, TSTATE ts); // ->tagState
void SetPolyTagHandle(HPOLYGON hp, SCNHANDLE th);// ->oTagHandle
void MaxPolygons(int maxPolys);
int GetTagPolyId(HPOLYGON hp);
void GetTagTag(HPOLYGON hp, SCNHANDLE *hTagText, int *tagX, int *tagY);
void SetPolyPointedTo(HPOLYGON hp, bool bPointedTo);
bool PolyIsPointedTo(HPOLYGON hp);
void SetPolyTagWanted(HPOLYGON hp, bool bTagWanted, bool bCursor, SCNHANDLE hOverrideTag);
bool PolyTagIsWanted(HPOLYGON hp);
bool PolyTagFollowsCursor(HPOLYGON hp);
SCNHANDLE GetPolyTagHandle(HPOLYGON hp);
bool IsTagPolygon(int tagno);
void GetPolyMidBottom(HPOLYGON hp, int *pX, int *pY);
int PathCount();
void MovePolygon(PTYPE ptype, int id, int x, int y);
void MovePolygonTo(PTYPE ptype, int id, int x, int y);
void NotebookPolyEntry(Common::Point c0, Common::Point c1, Common::Point c2, Common::Point c3);
void NotebookPolyNextPage(Common::Point c0, Common::Point c1, Common::Point c2, Common::Point c3);
void NotebookPolyPrevPage(Common::Point c0, Common::Point c1, Common::Point c2, Common::Point c3);
/*-------------------------------------------------------------------------*/
void UpdateGroundPlane();
enum class NoteBookPoly {
CLUE_0 = 0,
CLUE_1 = 1,
CLUE_2 = 2,
CLUE_3 = 3,
CLUE_4 = 4,
CLUE_5 = 5,
CLUE_6 = 6,
CLUE_7 = 7,
MAIN,
NEXT,
PREV,
NONE
};
class NoteBookPolygons {
public:
virtual ~NoteBookPolygons() {}
virtual void setPolygon(NoteBookPoly polyKind, const Common::Point &c0, const Common::Point &c1, const Common::Point &c2, const Common::Point &c3) = 0;
virtual void pushPolygon(const Common::Point &c0, const Common::Point &c1, const Common::Point &c2, const Common::Point &c3) = 0;
virtual NoteBookPoly mostSpecificHit(const Common::Point &point) const = 0;
virtual int lineHit(const Common::Point &point) const = 0;
virtual bool isInsideNotebook(const Common::Point &point) const = 0;
};
NoteBookPolygons *instantiateNoteBookPolygons();
} // End of namespace Tinsel
#endif /* TINSEL_POLYGONS_H */

749
engines/tinsel/saveload.cpp Normal file
View File

@@ -0,0 +1,749 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* Save and restore scene and game.
*/
#include "tinsel/actors.h"
#include "tinsel/config.h"
#include "tinsel/dialogs.h"
#include "tinsel/drives.h"
#include "tinsel/dw.h"
#include "tinsel/movers.h"
#include "tinsel/savescn.h"
#include "tinsel/timers.h"
#include "tinsel/tinlib.h"
#include "tinsel/tinsel.h"
#include "common/serializer.h"
#include "common/savefile.h"
#include "common/textconsole.h"
#include "common/translation.h"
#include "gui/message.h"
namespace Tinsel {
/**
* The current savegame format version.
* Our save/load system uses an elaborate scheme to allow us to modify the
* savegame while keeping full backward compatibility, in the sense that newer
* ScummVM versions always are able to load old savegames.
* In order to achieve that, we store a version in the savegame files, and whenever
* the savegame layout is modified, the version is incremented.
*
* This roughly works by marking each savegame entry with a range of versions
* for which it is valid; the save/load code iterates over all entries, but
* only saves/loads those which are valid for the version of the savegame
* which is being loaded/saved currently.
*/
#define CURRENT_VER 4
//----------------- EXTERN FUNCTIONS --------------------
// in DOS_DW.C
extern void syncSCdata(Common::Serializer &s);
// in PCODE.C
extern void syncGlobInfo(Common::Serializer &s);
// in POLYGONS.C
extern void syncPolyInfo(Common::Serializer &s);
extern int g_sceneCtr;
extern bool g_ASceneIsSaved;
//----------------- LOCAL DEFINES --------------------
struct SaveGameHeader {
uint32 id;
uint32 size;
uint32 ver;
char desc[SG_DESC_LEN];
TimeDate dateTime;
uint32 playTime;
bool scnFlag;
byte language;
uint16 numInterpreters; // Savegame version 2 or later only
};
enum {
DW1_SAVEGAME_ID = 0x44575399, // = 'DWSc' = "DiscWorld 1 ScummVM"
DW2_SAVEGAME_ID = 0x44573253, // = 'DW2S' = "DiscWorld 2 ScummVM"
SAVEGAME_HEADER_SIZE = 4 + 4 + 4 + SG_DESC_LEN + 7 + 4 + 1 + 1 + 2
};
#define SAVEGAME_ID ((TinselVersion >= 2) ? (uint32)DW2_SAVEGAME_ID : (uint32)DW1_SAVEGAME_ID)
enum {
// FIXME: Save file names in ScummVM can be longer than 8.3, overflowing the
// name field in savedFiles. Raising it to 256 as a preliminary fix.
FNAMELEN = 256 // 8.3
};
struct SFILES {
char name[FNAMELEN];
char desc[SG_DESC_LEN + 2];
TimeDate dateTime;
};
//----------------- GLOBAL GLOBAL DATA --------------------
int g_thingHeld = 0;
int g_restoreCD = 0;
SRSTATE g_SRstate = SR_IDLE;
//----------------- LOCAL GLOBAL DATA --------------------
// These vars are reset upon engine destruction
static int g_numSfiles = 0;
static SFILES g_savedFiles[MAX_SAVED_FILES];
static bool g_NeedLoad = true;
static SAVED_DATA *g_srsd = nullptr;
static int g_RestoreGameNumber = 0;
static char *g_SaveSceneName = nullptr;
static const char *g_SaveSceneDesc = nullptr;
static int *g_SaveSceneSsCount = 0;
static SAVED_DATA *g_SaveSceneSsData = nullptr; // points to 'SAVED_DATA ssdata[MAX_NEST]'
//------------- SAVE/LOAD SUPPORT METHODS ----------------
void ResetVarsSaveLoad() {
g_thingHeld = 0;
g_restoreCD = 0;
g_SRstate = SR_IDLE;
g_numSfiles = 0;
memset(g_savedFiles, 0, sizeof(g_savedFiles));
g_NeedLoad = true;
g_srsd = nullptr;
g_RestoreGameNumber = 0;
g_SaveSceneName = nullptr;
g_SaveSceneDesc = nullptr;
g_SaveSceneSsCount = 0;
g_SaveSceneSsData = nullptr;
}
void setNeedLoad() {
g_NeedLoad = true;
}
static void syncTime(Common::Serializer &s, TimeDate &t) {
s.syncAsUint16LE(t.tm_year);
s.syncAsByte(t.tm_mon);
s.syncAsByte(t.tm_mday);
s.syncAsByte(t.tm_hour);
s.syncAsByte(t.tm_min);
s.syncAsByte(t.tm_sec);
}
static bool syncSaveGameHeader(Common::Serializer &s, SaveGameHeader &hdr) {
s.syncAsUint32LE(hdr.id);
s.syncAsUint32LE(hdr.size);
s.syncAsUint32LE(hdr.ver);
s.syncBytes((byte *)hdr.desc, SG_DESC_LEN);
hdr.desc[SG_DESC_LEN - 1] = 0;
syncTime(s, hdr.dateTime);
if (hdr.ver >= 3)
s.syncAsUint32LE(hdr.playTime);
else
hdr.playTime = 0;
int tmp = hdr.size - s.bytesSynced();
// NOTE: We can't use SAVEGAME_ID here when attempting to remove a saved game from the launcher,
// as there is no TinselEngine initialized then. This means that we can't check if this is a DW1
// or DW2 savegame in this case, but it doesn't really matter, as the saved game is about to be
// deleted anyway. Refer to bug #5819.
bool correctID = _vm ? (hdr.id == SAVEGAME_ID) : (hdr.id == DW1_SAVEGAME_ID || hdr.id == DW2_SAVEGAME_ID);
// Perform sanity check
if (tmp < 0 || !correctID || hdr.ver > CURRENT_VER || hdr.size > 1024)
return false;
if (tmp > 0) {
// If there's header space left, handling syncing the Scn flag and game language
s.syncAsByte(hdr.scnFlag);
s.syncAsByte(hdr.language);
tmp -= 2;
if (_vm && s.isLoading()) {
// If the engine is loaded, ensure the Scn/Gra usage is correct, and it's the correct language
if ((hdr.scnFlag != ((_vm->getFeatures() & GF_SCNFILES) != 0)) ||
(hdr.language != _vm->_config->_language))
return false;
}
}
// Handle the number of interpreter contexts that will be saved in the savegame
if (tmp >= 2) {
tmp -= 2;
hdr.numInterpreters = NUM_INTERPRET;
s.syncAsUint16LE(hdr.numInterpreters);
} else {
if(_vm) // See comment above about bug #5819
hdr.numInterpreters = ((TinselVersion >= 2) ? 70 : 64) - 20;
else
hdr.numInterpreters = 50; // This value doesn't matter since the saved game is being deleted.
}
// Skip over any extra bytes
s.skip(tmp);
return true;
}
static void syncSavedMover(Common::Serializer &s, SAVED_MOVER &sm) {
int i, j;
s.syncAsUint32LE(sm.bActive);
s.syncAsSint32LE(sm.actorID);
s.syncAsSint32LE(sm.objX);
s.syncAsSint32LE(sm.objY);
s.syncAsUint32LE(sm.hLastfilm);
// Sync walk reels
for (i = 0; i < TOTAL_SCALES; ++i)
for (j = 0; j < 4; ++j)
s.syncAsUint32LE(sm.walkReels[i][j]);
// Sync stand reels
for (i = 0; i < TOTAL_SCALES; ++i)
for (j = 0; j < 4; ++j)
s.syncAsUint32LE(sm.standReels[i][j]);
// Sync talk reels
for (i = 0; i < TOTAL_SCALES; ++i)
for (j = 0; j < 4; ++j)
s.syncAsUint32LE(sm.talkReels[i][j]);
if (TinselVersion >= 2) {
s.syncAsByte(sm.bHidden);
s.syncAsSint32LE(sm.brightness);
s.syncAsSint32LE(sm.startColor);
s.syncAsSint32LE(sm.paletteLength);
}
}
static void syncSavedActor(Common::Serializer &s, SAVED_ACTOR &sa) {
s.syncAsUint16LE(sa.actorID);
s.syncAsUint16LE(sa.zFactor);
s.syncAsUint16LE(sa.bAlive);
s.syncAsUint16LE(sa.bHidden);
s.syncAsUint32LE(sa.presFilm);
s.syncAsUint16LE(sa.presRnum);
s.syncAsUint16LE(sa.presPlayX);
s.syncAsUint16LE(sa.presPlayY);
}
static void syncNoScrollB(Common::Serializer &s, NOSCROLLB &ns) {
s.syncAsSint32LE(ns.ln);
s.syncAsSint32LE(ns.c1);
s.syncAsSint32LE(ns.c2);
}
static void syncZPosition(Common::Serializer &s, Z_POSITIONS &zp) {
s.syncAsSint16LE(zp.actor);
s.syncAsSint16LE(zp.column);
s.syncAsSint32LE(zp.z);
}
static void syncPolyVolatile(Common::Serializer &s, POLY_VOLATILE &p) {
s.syncAsByte(p.bDead);
s.syncAsSint16LE(p.xoff);
s.syncAsSint16LE(p.yoff);
}
static void syncSoundReel(Common::Serializer &s, SOUNDREELS &sr) {
s.syncAsUint32LE(sr.hFilm);
s.syncAsSint32LE(sr.column);
s.syncAsSint32LE(sr.actorCol);
}
static void syncSavedData(Common::Serializer &s, SAVED_DATA &sd, int numInterp, int numSystemVars) {
s.syncAsUint32LE(sd.SavedSceneHandle);
s.syncAsUint32LE(sd.SavedBgroundHandle);
for (int i = 0; i < MAX_MOVERS; ++i)
syncSavedMover(s, sd.SavedMoverInfo[i]);
for (int i = 0; i < MAX_SAVED_ACTORS; ++i)
syncSavedActor(s, sd.SavedActorInfo[i]);
s.syncAsSint32LE(sd.NumSavedActors);
s.syncAsSint32LE(sd.SavedLoffset);
s.syncAsSint32LE(sd.SavedToffset);
for (int i = 0; i < numInterp; ++i)
sd.SavedICInfo[i].syncWithSerializer(s);
for (int i = 0; i < MAX_POLY; ++i)
s.syncAsUint32LE(sd.SavedDeadPolys[i]);
s.syncAsUint32LE(sd.SavedControl);
s.syncAsUint32LE(sd.SavedMidi);
s.syncAsUint32LE(sd.SavedLoop);
s.syncAsUint32LE(sd.SavedNoBlocking);
// SavedNoScrollData
for (int i = 0; i < MAX_VNOSCROLL; ++i)
syncNoScrollB(s, sd.SavedNoScrollData.NoVScroll[i]);
for (int i = 0; i < MAX_HNOSCROLL; ++i)
syncNoScrollB(s, sd.SavedNoScrollData.NoHScroll[i]);
s.syncAsUint32LE(sd.SavedNoScrollData.NumNoV);
s.syncAsUint32LE(sd.SavedNoScrollData.NumNoH);
// Tinsel 2 fields
if (TinselVersion >= 2) {
// SavedNoScrollData
s.syncAsUint32LE(sd.SavedNoScrollData.xTrigger);
s.syncAsUint32LE(sd.SavedNoScrollData.xDistance);
s.syncAsUint32LE(sd.SavedNoScrollData.xSpeed);
s.syncAsUint32LE(sd.SavedNoScrollData.yTriggerTop);
s.syncAsUint32LE(sd.SavedNoScrollData.yTriggerBottom);
s.syncAsUint32LE(sd.SavedNoScrollData.yDistance);
s.syncAsUint32LE(sd.SavedNoScrollData.ySpeed);
for (int i = 0; i < NUM_ZPOSITIONS; ++i)
syncZPosition(s, sd.zPositions[i]);
s.syncBytes(sd.savedActorZ, MAX_SAVED_ACTOR_Z);
for (int i = 0; i < MAX_POLY; ++i)
syncPolyVolatile(s, sd.SavedPolygonStuff[i]);
for (int i = 0; i < 3; ++i)
s.syncAsUint32LE(sd.SavedTune[i]);
s.syncAsByte(sd.bTinselDim);
s.syncAsSint32LE(sd.SavedScrollFocus);
for (int i = 0; i < numSystemVars; ++i)
s.syncAsSint32LE(sd.SavedSystemVars[i]);
int numReels = s.getVersion() >= 4 ? MAX_SOUNDREELS : 5;
for (int i = 0; i < numReels; ++i)
syncSoundReel(s, sd.SavedSoundReels[i]);
// For old saves we zero the remaining sound reels
if (s.isLoading() && numReels < MAX_SOUNDREELS) {
const auto reelSize = sizeof sd.SavedSoundReels[0];
memset(&sd.SavedSoundReels[numReels], 0, reelSize * (MAX_SOUNDREELS - numReels));
}
}
}
/**
* Compare two TimeDate structs to see which one was earlier.
* Returns 0 if they are equal, a negative value if a is lower / first, and
* a positive value if b is lower / first.
*/
static int cmpTimeDate(const TimeDate &a, const TimeDate &b) {
int tmp;
#define CMP_ENTRY(x) tmp = a.x - b.x; if (tmp != 0) return tmp
CMP_ENTRY(tm_year);
CMP_ENTRY(tm_mon);
CMP_ENTRY(tm_mday);
CMP_ENTRY(tm_hour);
CMP_ENTRY(tm_min);
CMP_ENTRY(tm_sec);
#undef CMP_ENTRY
return 0;
}
/**
* Compute a list of all available saved game files.
* Store the file details, ordered by time, in savedFiles[] and return
* the number of files found.
*/
int getList(Common::SaveFileManager *saveFileMan, const Common::String &target) {
// No change since last call?
// TODO/FIXME: Just always reload this data? Be careful about slow downs!!!
if (!g_NeedLoad)
return g_numSfiles;
int i;
const Common::String pattern = target + ".???";
Common::StringArray files = saveFileMan->listSavefiles(pattern);
g_numSfiles = 0;
for (Common::StringArray::const_iterator file = files.begin(); file != files.end(); ++file) {
if (g_numSfiles >= MAX_SAVED_FILES)
break;
const Common::String &fname = *file;
Common::InSaveFile *f = saveFileMan->openForLoading(fname);
if (f == NULL) {
continue;
}
// Try to load save game header
Common::Serializer s(f, 0);
SaveGameHeader hdr;
bool validHeader = syncSaveGameHeader(s, hdr);
delete f;
if (!validHeader) {
continue; // Invalid header, or savegame too new -> skip it
// TODO: In SCUMM, we still show an entry for the save, but with description
// "incompatible version".
}
i = g_numSfiles;
#ifndef DISABLE_SAVEGAME_SORTING
for (i = 0; i < g_numSfiles; i++) {
if (cmpTimeDate(hdr.dateTime, g_savedFiles[i].dateTime) > 0) {
Common::copy_backward(&g_savedFiles[i], &g_savedFiles[g_numSfiles], &g_savedFiles[g_numSfiles + 1]);
break;
}
}
#endif
Common::strlcpy(g_savedFiles[i].name, fname.c_str(), FNAMELEN);
Common::strlcpy(g_savedFiles[i].desc, hdr.desc, SG_DESC_LEN);
g_savedFiles[i].dateTime = hdr.dateTime;
++g_numSfiles;
}
// Next getList() needn't do its stuff again
g_NeedLoad = false;
return g_numSfiles;
}
int getList() {
// No change since last call?
// TODO/FIXME: Just always reload this data? Be careful about slow downs!!!
if (!g_NeedLoad)
return g_numSfiles;
return getList(_vm->getSaveFileMan(), _vm->getTargetName());
}
char *ListEntry(int i, letype which) {
if (i == -1)
i = g_numSfiles;
assert(i >= 0);
if (i < g_numSfiles)
return which == LE_NAME ? g_savedFiles[i].name : g_savedFiles[i].desc;
else
return NULL;
}
static bool DoSync(Common::Serializer &s, int numInterp, int numSystemVars) {
int sg = 0;
if (TinselVersion >= 2) {
if (s.isSaving())
g_restoreCD = GetCurrentCD();
s.syncAsSint16LE(g_restoreCD);
if (s.isLoading())
_vm->_dialogs->holdItem(INV_NOICON);
}
syncSavedData(s, *g_srsd, numInterp, numSystemVars);
syncGlobInfo(s); // Glitter globals
_vm->_dialogs->syncInvInfo(s); // Inventory data
// Held object
if (s.isSaving())
sg = _vm->_dialogs->whichItemHeld();
s.syncAsSint32LE(sg);
if (s.isLoading()) {
if (sg != -1 && !_vm->_dialogs->getIsInvObject(sg))
// Not a valid inventory object, so return false
return false;
if (TinselVersion >= 2)
g_thingHeld = sg;
else
_vm->_dialogs->holdItem(sg);
}
syncTimerInfo(s); // Timer data
if (TinselVersion <= 1)
syncPolyInfo(s); // Dead polygon data
syncSCdata(s); // Hook Scene and delayed scene
s.syncAsSint32LE(*g_SaveSceneSsCount);
if (*g_SaveSceneSsCount != 0) {
SAVED_DATA *sdPtr = g_SaveSceneSsData;
for (int i = 0; i < *g_SaveSceneSsCount; ++i, ++sdPtr)
syncSavedData(s, *sdPtr, numInterp, numSystemVars);
// Flag that there is a saved scene to return to. Note that in this context 'saved scene'
// is a stored scene to return to from another scene, such as from the Summoning Book close-up
// in Discworld 1 to whatever scene Rincewind was in prior to that
g_ASceneIsSaved = true;
}
if (TinselVersion <= 1)
_vm->_actor->syncAllActorsAlive(s);
return true;
}
/**
* DoRestore
*/
static bool DoRestore() {
Common::InSaveFile *f = _vm->getSaveFileMan()->openForLoading(g_savedFiles[g_RestoreGameNumber].name);
if (f == NULL) {
return false;
}
Common::Serializer s(f, 0);
SaveGameHeader hdr;
if (!syncSaveGameHeader(s, hdr)) {
delete f; // Invalid header, or savegame too new -> skip it
return false;
}
s.setVersion(hdr.ver);
if (hdr.ver >= 3)
_vm->setTotalPlayTime(hdr.playTime);
else
_vm->setTotalPlayTime(0);
// Load in the data. For older savegame versions, we potentially need to load the data twice, once
// for pre 1.5 savegames, and if that fails, a second time for 1.5 savegames
int numInterpreters = hdr.numInterpreters;
int32 currentPos = f->pos();
int numberOfTries = ((hdr.ver >= 2) ? 1 : 2);
int numSystemVars = SV_TOPVALID; // Appropriate for both Noir and DW2
for (int tryNumber = 0; tryNumber < numberOfTries; ++tryNumber) {
if (tryNumber == 1) {
f->seek(currentPos);
// If it's the second loop iteration, try with the 1.5 savegame number of interpreter contexts
if (hdr.ver < 2) {
numInterpreters = 80;
}
}
// Load the savegame data
bool successfullSync = DoSync(s, numInterpreters, numSystemVars);
uint32 id = f->readSint32LE();
int remaining = f->size() - f->pos();
// BUG #13897: Older savegames won't run on ScummVM 2.6.x
// The reason being that the system vars for Noir were added increasing the SV_TOPVALID value,
// which also affected DW2 unintentionally, creating some v3 savegames that had additional data
// stored there. To properly load these savegames, we'll have to try again, with the knowledge
// that we have to skip this data.
if (hdr.id == DW2_SAVEGAME_ID && hdr.ver == 3 && remaining != 0) {
if (tryNumber == 0) {
numberOfTries++;
// Make the second attempt read the Noir amount of sys-vars, so that the problematic
// savegames can be loaded.
numSystemVars = SV_TOPVALID_T3;
}
continue;
}
if (successfullSync) {
if (id != (uint32)0xFEEDFACE) {
error("Incompatible saved game");
}
// Data load was successful (or likely), so break out of loop
break;
}
}
int remainingBytes = f->size() - f->pos();
if (remainingBytes != 0) {
error("%d bytes of savegame not read", remainingBytes);
}
bool failed = (f->eos() || f->err());
delete f;
if (failed) {
GUI::MessageDialog dialog(_("Failed to load saved game from file."));
dialog.runModal();
}
return !failed;
}
static void SaveFailure(Common::OutSaveFile *f) {
if (f) {
delete f;
_vm->getSaveFileMan()->removeSavefile(g_SaveSceneName);
}
g_SaveSceneName= nullptr; // Invalidate save name
GUI::MessageDialog dialog(_("Failed to save game to file."));
dialog.runModal();
}
/**
* DoSave
*/
static void DoSave() {
Common::OutSaveFile *f;
char tmpName[FNAMELEN];
// Next getList() must do its stuff again
g_NeedLoad = true;
if (g_SaveSceneName == NULL) {
// Generate a new unique save name
int i;
int ano = 1; // Allocated number
while (1) {
Common::String fname = _vm->getSavegameFilename(ano);
Common::strlcpy(tmpName, fname.c_str(), FNAMELEN);
for (i = 0; i < g_numSfiles; i++)
if (!strcmp(g_savedFiles[i].name, tmpName))
break;
if (i == g_numSfiles)
break;
ano++;
}
g_SaveSceneName = tmpName;
}
if (g_SaveSceneDesc[0] == 0)
g_SaveSceneDesc = "unnamed";
f = _vm->getSaveFileMan()->openForSaving(g_SaveSceneName);
Common::Serializer s(0, f);
if (f == NULL) {
SaveFailure(f);
return;
}
// Write out a savegame header
SaveGameHeader hdr;
hdr.id = SAVEGAME_ID;
hdr.size = SAVEGAME_HEADER_SIZE;
hdr.ver = CURRENT_VER;
memset(hdr.desc, 0, SG_DESC_LEN);
Common::strlcpy(hdr.desc, g_SaveSceneDesc, SG_DESC_LEN);
g_system->getTimeAndDate(hdr.dateTime);
hdr.playTime = _vm->getTotalPlayTime();
hdr.scnFlag = _vm->getFeatures() & GF_SCNFILES;
hdr.language = _vm->_config->_language;
if (!syncSaveGameHeader(s, hdr) || f->err()) {
SaveFailure(f);
return;
}
s.setVersion(hdr.ver);
DoSync(s, hdr.numInterpreters, SV_TOPVALID);
// Write out the special Id for Discworld savegames
f->writeUint32LE(0xFEEDFACE);
if (f->err()) {
SaveFailure(f);
return;
}
f->finalize();
delete f;
g_SaveSceneName= nullptr; // Invalidate save name
}
/**
* ProcessSRQueue
*/
void ProcessSRQueue() {
switch (g_SRstate) {
case SR_DORESTORE:
// If a load has been done directly from title screens, set a larger value for scene ctr so the
// code used to skip the title screens in Discworld 1 gets properly disabled
if (g_sceneCtr < 10)
g_sceneCtr = 10;
if (DoRestore()) {
DoRestoreScene(g_srsd, false);
}
g_SRstate = SR_IDLE;
break;
case SR_DOSAVE:
DoSave();
g_SRstate = SR_IDLE;
break;
default:
break;
}
}
void RequestSaveGame(char *name, char *desc, SAVED_DATA *sd, int *pSsCount, SAVED_DATA *pSsData) {
assert(g_SRstate == SR_IDLE);
g_SaveSceneName = name;
g_SaveSceneDesc = desc;
g_SaveSceneSsCount = pSsCount;
g_SaveSceneSsData = pSsData;
g_srsd = sd;
g_SRstate = SR_DOSAVE;
}
void RequestRestoreGame(int num, SAVED_DATA *sd, int *pSsCount, SAVED_DATA *pSsData) {
if (TinselVersion >= 2) {
if (num == -1)
return;
else if (num == -2) {
// From CD change for restore
num = g_RestoreGameNumber;
}
}
assert(num >= 0);
g_RestoreGameNumber = num;
g_SaveSceneSsCount = pSsCount;
g_SaveSceneSsData = pSsData;
g_srsd = sd;
g_SRstate = SR_DORESTORE;
}
/**
* Returns the index of the most recently saved savegame. This will always be
* the file at the first index, since the list is sorted by date/time
*/
int NewestSavedGame() {
int numFiles = getList();
return (numFiles == 0) ? -1 : 0;
}
} // End of namespace Tinsel

485
engines/tinsel/savescn.cpp Normal file
View File

@@ -0,0 +1,485 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* Save and restore scene and game.
*/
#include "tinsel/actors.h"
#include "tinsel/background.h"
#include "tinsel/config.h"
#include "tinsel/drives.h"
#include "tinsel/dw.h"
#include "tinsel/faders.h" // FadeOutFast()
#include "tinsel/graphics.h" // ClearScreen()
#include "tinsel/handle.h"
#include "tinsel/dialogs.h"
#include "tinsel/music.h"
#include "tinsel/pid.h"
#include "tinsel/play.h"
#include "tinsel/polygons.h"
#include "tinsel/movers.h"
#include "tinsel/savescn.h"
#include "tinsel/scene.h"
#include "tinsel/sched.h"
#include "tinsel/scroll.h"
#include "tinsel/sound.h"
#include "tinsel/sysvar.h"
#include "tinsel/tinlib.h"
#include "tinsel/token.h"
#include "common/textconsole.h"
namespace Tinsel {
//----------------- EXTERN FUNCTIONS --------------------
// In DOS_DW.C
void RestoreMasterProcess(INT_CONTEXT *pic);
// in EVENTS.C (declared here and not in events.h because of strange goings-on)
void RestoreProcess(INT_CONTEXT *pic);
// in SCENE.C
extern SCNHANDLE GetSceneHandle();
//----------------- LOCAL DEFINES --------------------
enum {
RS_COUNT = 5, // Restore scene count
MAX_NEST = 4
};
//----------------- EXTERNAL GLOBAL DATA --------------------
extern int g_thingHeld;
extern int g_restoreCD;
extern SRSTATE g_SRstate;
//----------------- LOCAL GLOBAL DATA --------------------
// These vars are reset upon engine destruction
bool g_ASceneIsSaved = false;
static int g_savedSceneCount = 0;
static bool g_bNotDoneYet = false;
static SAVED_DATA *g_ssData = nullptr;
static SAVED_DATA g_sgData;
static SAVED_DATA *g_rsd = nullptr;
static int g_RestoreSceneCount = 0;
static bool g_bNoFade = false;
void ResetVarsSaveScn() {
g_ASceneIsSaved = false;
g_savedSceneCount = 0;
g_bNotDoneYet = false;
free(g_ssData);
g_ssData = nullptr;
memset(&g_sgData, 0, sizeof(g_sgData));
g_rsd = nullptr;
g_RestoreSceneCount = 0;
g_bNoFade = false;
}
/**
* Save current scene.
* @param sd Pointer to the scene data
*/
void DoSaveScene(SAVED_DATA *sd) {
sd->SavedSceneHandle = GetSceneHandle();
sd->SavedBgroundHandle = _vm->_bg->GetBgroundHandle();
SaveMovers(sd->SavedMoverInfo);
sd->NumSavedActors = _vm->_actor->SaveActors(sd->SavedActorInfo);
_vm->_bg->PlayfieldGetPos(FIELD_WORLD, &sd->SavedLoffset, &sd->SavedToffset);
SaveInterpretContexts(sd->SavedICInfo);
sd->SavedControl = ControlIsOn();
sd->SavedNoBlocking = GetNoBlocking();
_vm->_scroll->GetNoScrollData(&sd->SavedNoScrollData);
if (TinselVersion >= 2) {
// Tinsel 2 specific data save
_vm->_actor->SaveActorZ(sd->savedActorZ);
_vm->_actor->SaveZpositions(sd->zPositions);
SavePolygonStuff(sd->SavedPolygonStuff);
_vm->_pcmMusic->getTunePlaying(sd->SavedTune, sizeof(sd->SavedTune));
sd->bTinselDim = _vm->_pcmMusic->getMusicTinselDimmed();
sd->SavedScrollFocus = _vm->_scroll->GetScrollFocus();
SaveSysVars(sd->SavedSystemVars);
SaveSoundReels(sd->SavedSoundReels);
} else {
// Tinsel 1 specific data save
SaveDeadPolys(sd->SavedDeadPolys);
_vm->_music->CurrentMidiFacts(&sd->SavedMidi, &sd->SavedLoop);
}
g_ASceneIsSaved = true;
}
/**
* Initiate restoration of the saved scene.
* @param sd Pointer to the scene data
* @param bFadeOut Flag to perform a fade out
*/
void DoRestoreScene(SAVED_DATA *sd, bool bFadeOut) {
g_rsd = sd;
if (bFadeOut)
g_RestoreSceneCount = RS_COUNT + COUNTOUT_COUNT; // Set restore scene count
else
g_RestoreSceneCount = RS_COUNT; // Set restore scene count
}
void InitializeSaveScenes() {
if (g_ssData == NULL) {
g_ssData = (SAVED_DATA *)calloc(MAX_NEST, sizeof(SAVED_DATA));
if (g_ssData == NULL) {
error("Cannot allocate memory for scene changes");
}
} else {
// Re-initialize - no scenes saved
g_savedSceneCount = 0;
}
}
void FreeSaveScenes() {
free(g_ssData);
g_ssData = nullptr;
}
/**
* Checks that all non-moving actors are playing the same reel as when
* the scene was saved.
* Also 'stand' all the moving actors at their saved positions.
*/
void sortActors(SAVED_DATA *sd) {
assert(TinselVersion <= 1);
for (int i = 0; i < sd->NumSavedActors; i++) {
_vm->_actor->ActorsLife(sd->SavedActorInfo[i].actorID, sd->SavedActorInfo[i].bAlive);
// Should be playing the same reel.
if (sd->SavedActorInfo[i].presFilm != 0) {
if (!_vm->_actor->actorAlive(sd->SavedActorInfo[i].actorID))
continue;
RestoreActorReels(sd->SavedActorInfo[i].presFilm, sd->SavedActorInfo[i].presRnum, sd->SavedActorInfo[i].zFactor,
sd->SavedActorInfo[i].presPlayX, sd->SavedActorInfo[i].presPlayY);
}
}
RestoreAuxScales(sd->SavedMoverInfo);
for (int i = 0; i < MAX_MOVERS; i++) {
if (sd->SavedMoverInfo[i].bActive)
Stand(Common::nullContext, sd->SavedMoverInfo[i].actorID, sd->SavedMoverInfo[i].objX,
sd->SavedMoverInfo[i].objY, sd->SavedMoverInfo[i].hLastfilm);
}
}
/**
* Stand all the moving actors at their saved positions.
* Not called from the foreground.
*/
static void SortMAProcess(CORO_PARAM, const void *) {
CORO_BEGIN_CONTEXT;
int i;
int viaActor;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
// Disable via actor for the stands
_ctx->viaActor = SysVar(ISV_DIVERT_ACTOR);
SetSysVar(ISV_DIVERT_ACTOR, 0);
RestoreAuxScales(g_rsd->SavedMoverInfo);
for (_ctx->i = 0; _ctx->i < MAX_MOVERS; _ctx->i++) {
if (g_rsd->SavedMoverInfo[_ctx->i].bActive) {
CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, g_rsd->SavedMoverInfo[_ctx->i].actorID,
g_rsd->SavedMoverInfo[_ctx->i].objX, g_rsd->SavedMoverInfo[_ctx->i].objY,
g_rsd->SavedMoverInfo[_ctx->i].hLastfilm));
if (g_rsd->SavedMoverInfo[_ctx->i].bHidden)
HideMover(GetMover(g_rsd->SavedMoverInfo[_ctx->i].actorID));
}
ActorPalette(g_rsd->SavedMoverInfo[_ctx->i].actorID,
g_rsd->SavedMoverInfo[_ctx->i].startColor, g_rsd->SavedMoverInfo[_ctx->i].paletteLength);
if (g_rsd->SavedMoverInfo[_ctx->i].brightness != BOGUS_BRIGHTNESS)
ActorBrightness(g_rsd->SavedMoverInfo[_ctx->i].actorID, g_rsd->SavedMoverInfo[_ctx->i].brightness);
}
// Restore via actor
SetSysVar(ISV_DIVERT_ACTOR, _ctx->viaActor);
g_bNotDoneYet = false;
CORO_END_CODE;
}
//---------------------------------------------------------------------------
void ResumeInterprets() {
// Master script only affected on restore game, not restore scene
if (TinselVersion <= 1) {
if (g_rsd == &g_sgData) {
CoroScheduler.killMatchingProcess(PID_MASTER_SCR, -1);
FreeMasterInterpretContext();
}
}
for (int i = 0; i < NUM_INTERPRET; i++) {
switch (g_rsd->SavedICInfo[i].GSort) {
case GS_NONE:
break;
case GS_INVENTORY:
if (g_rsd->SavedICInfo[i].event != POINTED) {
RestoreProcess(&g_rsd->SavedICInfo[i]);
}
break;
case GS_MASTER:
// Master script only affected on restore game, not restore scene
if (g_rsd == &g_sgData)
RestoreMasterProcess(&g_rsd->SavedICInfo[i]);
break;
case GS_PROCESS:
// Tinsel 2 process
RestoreSceneProcess(&g_rsd->SavedICInfo[i]);
break;
case GS_GPROCESS:
// Tinsel 2 Global processes only affected on restore game, not restore scene
if (g_rsd == &g_sgData)
RestoreGlobalProcess(&g_rsd->SavedICInfo[i]);
break;
case GS_ACTOR:
if (TinselVersion >= 2)
RestoreProcess(&g_rsd->SavedICInfo[i]);
else
RestoreActorProcess(g_rsd->SavedICInfo[i].idActor, &g_rsd->SavedICInfo[i], g_rsd == &g_sgData);
break;
case GS_POLYGON:
case GS_SCENE:
RestoreProcess(&g_rsd->SavedICInfo[i]);
break;
default:
warning("Unhandled GSort in ResumeInterprets");
}
}
}
/**
* Do restore scene
* @param n Id
*/
static int DoRestoreSceneFrame(SAVED_DATA *sd, int n) {
switch (n) {
case RS_COUNT + COUNTOUT_COUNT:
// Trigger pre-load and fade and start countdown
FadeOutFast();
break;
case RS_COUNT:
_vm->_sound->stopAllSamples();
ClearScreen();
if (TinselVersion >= 2) {
// Master script only affected on restore game, not restore scene
if (sd == &g_sgData) {
CoroScheduler.killMatchingProcess(PID_MASTER_SCR);
KillGlobalProcesses();
FreeMasterInterpretContext();
}
RestorePolygonStuff(sd->SavedPolygonStuff);
// Abandon temporarily if different CD
if (sd == &g_sgData && g_restoreCD != GetCurrentCD()) {
g_SRstate = SR_IDLE;
EndScene();
SetNextCD(g_restoreCD);
CDChangeForRestore(g_restoreCD);
return 0;
}
} else {
RestoreDeadPolys(sd->SavedDeadPolys);
}
// Start up the scene
StartNewScene(sd->SavedSceneHandle, NO_ENTRY_NUM);
_vm->_bg->SetDoFadeIn(!g_bNoFade);
g_bNoFade = false;
_vm->_bg->StartupBackground(Common::nullContext, sd->SavedBgroundHandle);
if (TinselVersion >= 2) {
Offset(EX_USEXY, sd->SavedLoffset, sd->SavedToffset);
} else {
_vm->_scroll->KillScroll();
_vm->_bg->PlayfieldSetPos(FIELD_WORLD, sd->SavedLoffset, sd->SavedToffset);
SetNoBlocking(sd->SavedNoBlocking);
}
_vm->_scroll->RestoreNoScrollData(&sd->SavedNoScrollData);
if (TinselVersion >= 2) {
// create process to sort out the moving actors
CoroScheduler.createProcess(PID_MOVER, SortMAProcess, NULL, 0);
g_bNotDoneYet = true;
_vm->_actor->RestoreActorZ(sd->savedActorZ);
_vm->_actor->RestoreZpositions(sd->zPositions);
RestoreSysVars(sd->SavedSystemVars);
_vm->_actor->RestoreActors(sd->NumSavedActors, sd->SavedActorInfo);
RestoreSoundReels(sd->SavedSoundReels);
return 1;
}
sortActors(sd);
break;
case 2:
break;
case 1:
if (TinselVersion >= 2) {
if (g_bNotDoneYet)
return n;
if (sd == &g_sgData)
_vm->_dialogs->holdItem(g_thingHeld, true);
if (sd->bTinselDim)
_vm->_pcmMusic->dim(true);
_vm->_pcmMusic->restoreThatTune(sd->SavedTune);
_vm->_scroll->ScrollFocus(sd->SavedScrollFocus);
} else {
_vm->_music->RestoreMidiFacts(sd->SavedMidi, sd->SavedLoop);
}
if (sd->SavedControl)
ControlOn(); // Control was on
ResumeInterprets();
break;
default:
break;
}
return n - 1;
}
/**
* Restore game
* @param num num
*/
void RestoreGame(int num) {
_vm->_dialogs->killInventory();
RequestRestoreGame(num, &g_sgData, &g_savedSceneCount, g_ssData);
// Actual restoring is performed by ProcessSRQueue
}
/**
* Save game
* @param name Name of savegame
* @param desc Description of savegame
*/
void SaveGame(char *name, char *desc) {
// Get current scene data
DoSaveScene(&g_sgData);
RequestSaveGame(name, desc, &g_sgData, &g_savedSceneCount, g_ssData);
// Actual saving is performed by ProcessSRQueue
}
//---------------------------------------------------------------------------------
bool IsRestoringScene() {
if (g_RestoreSceneCount) {
g_RestoreSceneCount = DoRestoreSceneFrame(g_rsd, g_RestoreSceneCount);
}
return g_RestoreSceneCount ? true : false;
}
/**
* Restores Scene
*/
void TinselRestoreScene(bool bFade) {
// only called by restore_scene PCODE
if (g_RestoreSceneCount == 0) {
assert(g_savedSceneCount >= 1); // No saved scene to restore
if (g_ASceneIsSaved)
DoRestoreScene(&g_ssData[--g_savedSceneCount], bFade);
if (!bFade)
g_bNoFade = true;
}
}
/**
* Please Save Scene
*/
void TinselSaveScene(CORO_PARAM) {
// only called by save_scene PCODE
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
assert(g_savedSceneCount < MAX_NEST); // nesting limit reached
// Don't save the same thing multiple times!
// FIXME/TODO: Maybe this can be changed to an assert?
if (g_savedSceneCount && g_ssData[g_savedSceneCount-1].SavedSceneHandle == GetSceneHandle())
CORO_KILL_SELF();
DoSaveScene(&g_ssData[g_savedSceneCount++]);
CORO_END_CODE;
}
} // End of namespace Tinsel

101
engines/tinsel/savescn.h Normal file
View File

@@ -0,0 +1,101 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* Should really be called "moving actors.h"
*/
#ifndef TINSEL_SAVESCN_H
#define TINSEL_SAVESCN_H
#include "tinsel/actors.h" // SAVED_ACTOR
#include "tinsel/dw.h" // SCNHANDLE
#include "tinsel/movers.h" // SAVED_MOVER
#include "tinsel/pcode.h" // INT_CONTEXT
#include "tinsel/play.h"
#include "tinsel/polygons.h"
#include "tinsel/scroll.h" // SCROLLDATA
#include "tinsel/sysvar.h"
namespace Tinsel {
#define SG_DESC_LEN 40 // Max. saved game description length
#define MAX_SAVED_FILES 100
struct SAVED_DATA {
SCNHANDLE SavedSceneHandle; // Scene handle
SCNHANDLE SavedBgroundHandle; // Background handle
SAVED_MOVER SavedMoverInfo[MAX_MOVERS]; // Moving actors
SAVED_ACTOR SavedActorInfo[MAX_SAVED_ACTORS]; // } Actors
int NumSavedActors; // }
int SavedLoffset, SavedToffset; // Screen offsets
INT_CONTEXT SavedICInfo[MAX_INTERPRET]; // Interpret contexts
bool SavedDeadPolys[MAX_POLY];
bool SavedControl;
SCNHANDLE SavedMidi; // }
bool SavedLoop; // } Midi
bool SavedNoBlocking;
SCROLLDATA SavedNoScrollData;
// Tinsel 2 fields
Z_POSITIONS zPositions[NUM_ZPOSITIONS];
byte savedActorZ[MAX_SAVED_ACTOR_Z];
POLY_VOLATILE SavedPolygonStuff[MAX_POLY];
uint32 SavedTune[3]; // Music
bool bTinselDim;
int SavedScrollFocus;
int SavedSystemVars[SV_TOPVALID_T3];
SOUNDREELS SavedSoundReels[MAX_SOUNDREELS];
};
enum SRSTATE {
SR_IDLE, SR_DORESTORE, SR_DONERESTORE,
SR_DOSAVE, SR_DONESAVE, SR_ABORTED
};
void TinselRestoreScene(bool bFade);
void TinselSaveScene(CORO_PARAM);
void DoRestoreScene(SAVED_DATA *sd, bool bFadeOut);
void DoSaveScene(SAVED_DATA *sd);
bool IsRestoringScene();
enum letype{
LE_NAME, LE_DESC
};
char *ListEntry(int i, letype which);
int getList();
void setNeedLoad();
void RestoreGame(int num);
void SaveGame(char *name, char *desc);
void ProcessSRQueue();
void RequestSaveGame(char *name, char *desc, SAVED_DATA *sd, int *ssCount, SAVED_DATA *ssData);
void RequestRestoreGame(int num, SAVED_DATA *sd, int *ssCount, SAVED_DATA *ssData);
void InitializeSaveScenes();
void FreeSaveScenes();
} // End of namespace Tinsel
#endif /* TINSEL_SAVESCN_H */

581
engines/tinsel/scene.cpp Normal file
View File

@@ -0,0 +1,581 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* Starts up new scenes.
*/
#include "tinsel/actors.h"
#include "tinsel/anim.h"
#include "tinsel/background.h"
#include "tinsel/config.h"
#include "tinsel/cursor.h"
#include "tinsel/dialogs.h"
#include "tinsel/dw.h"
#include "tinsel/graphics.h"
#include "tinsel/handle.h"
#include "tinsel/film.h"
#include "tinsel/font.h"
#include "tinsel/mareels.h"
#include "tinsel/move.h"
#include "tinsel/music.h"
#include "tinsel/object.h"
#include "tinsel/pcode.h"
#include "tinsel/pid.h" // process IDs
#include "tinsel/play.h"
#include "tinsel/polygons.h"
#include "tinsel/movers.h"
#include "tinsel/sched.h"
#include "tinsel/scn.h"
#include "tinsel/scroll.h"
#include "tinsel/sound.h" // stopAllSamples()
#include "tinsel/sysvar.h"
#include "tinsel/token.h"
#include "tinsel/noir/spriter.h"
#include "common/memstream.h"
#include "common/textconsole.h"
namespace Tinsel {
//----------------- EXTERNAL FUNCTIONS ---------------------
// in EFFECT.C
extern void EffectPolyProcess(CORO_PARAM, const void *);
// in PDISPLAY.C
#ifdef DEBUG
extern void CursorPositionProcess(CORO_PARAM, const void *);
#endif
extern void TagProcess(CORO_PARAM, const void *);
extern void PointProcess(CORO_PARAM, const void *);
extern void EnableTags();
//----------------- LOCAL DEFINES --------------------
#include "common/pack-start.h" // START STRUCT PACKING
/** scene structure - one per scene */
struct SCENE_STRUC {
int32 defRefer; // Default refer direction (REFTYPE)
SCNHANDLE hSceneScript; // handle to scene script
SCNHANDLE hSceneDesc; // handle to scene description
int32 numEntrance; // number of entrances in this scene
SCNHANDLE hEntrance; // handle to table of entrances
int32 numPoly; // number of various polygons in this scene
SCNHANDLE hPoly; // handle to table of polygons
int32 numTaggedActor; // number of tagged actors in this scene
SCNHANDLE hTaggedActor; // handle to table of tagged actors
int32 numProcess; // number of processes in this scene
SCNHANDLE hProcess; // handle to table of processes
SCNHANDLE hMusicScript; // handle to music script data - Tinsel 2 only
SCNHANDLE hMusicSegment; // handle to music segments - Tinsel 2 only
int32 numCameras;
SCNHANDLE hCamera;
int32 numLights;
SCNHANDLE hLight;
} PACKED_STRUCT;
/** entrance structure - one per entrance */
struct ENTRANCE_STRUC {
int32 eNumber; ///< entrance number
SCNHANDLE hScript; ///< handle to entrance script
// Tinsel 2 fields
SCNHANDLE hEntDesc; // handle to entrance description
uint32 flags;
} PACKED_STRUCT;
/** camera strucutre, Noir only */
struct CAMERA_STRUC {
int32 sceneId;
int32 rotX;
int32 rotY;
int32 rotZ;
int32 posX;
int32 posY;
int32 posZ;
int32 aperture;
} PACKED_STRUCT;
struct LIGHT_STRUC {
int32 sceneId;
int32 posX;
int32 posY;
int32 posZ;
} PACKED_STRUCT;
#include "common/pack-end.h" // END STRUCT PACKING
//----------------- LOCAL GLOBAL DATA --------------------
// These vars are reset upon engine destruction
#ifdef DEBUG
static bool g_ShowPosition = false; // Set when showpos() has been called
#endif
int g_sceneCtr = 0;
static int g_initialMyEscape = 0;
static SCNHANDLE g_SceneHandle = 0; // Current scene handle - stored in case of Save_Scene()
SCENE_STRUC g_tempStruc;
static bool g_isViewSet = false;
struct TP_INIT {
SCNHANDLE hTinselCode; // Code
TINSEL_EVENT event; // Triggering event
};
void ResetVarsScene() {
g_sceneCtr = 0;
g_initialMyEscape = 0;
g_SceneHandle = 0;
g_isViewSet = false;
memset(&g_tempStruc, 0, sizeof(SCENE_STRUC));
}
SCENE_STRUC* parseSceneV1(const byte *p) {
g_tempStruc.numEntrance = READ_UINT32(p); p += sizeof(uint32);
g_tempStruc.numPoly = READ_UINT32(p); p += sizeof(uint32);
g_tempStruc.numTaggedActor = READ_UINT32(p); p += sizeof(uint32);
g_tempStruc.defRefer = READ_UINT32(p); p += sizeof(uint32);
g_tempStruc.hSceneScript = READ_UINT32(p); p += sizeof(uint32);
g_tempStruc.hEntrance = READ_UINT32(p); p += sizeof(uint32);
g_tempStruc.hPoly = READ_UINT32(p); p += sizeof(uint32);
g_tempStruc.hTaggedActor = READ_UINT32(p); p += sizeof(uint32);
return &g_tempStruc;
}
SCENE_STRUC* parseSceneV2(const byte *p) {
g_tempStruc.defRefer = READ_UINT32(p); p += sizeof(uint32);
g_tempStruc.hSceneScript = READ_UINT32(p); p += sizeof(uint32);
g_tempStruc.hSceneDesc = READ_UINT32(p); p += sizeof(uint32);
g_tempStruc.numEntrance = READ_UINT32(p); p += sizeof(uint32);
g_tempStruc.hEntrance = READ_UINT32(p); p += sizeof(uint32);
g_tempStruc.numPoly = READ_UINT32(p); p += sizeof(uint32);
g_tempStruc.hPoly = READ_UINT32(p); p += sizeof(uint32);
g_tempStruc.numTaggedActor = READ_UINT32(p); p += sizeof(uint32);
g_tempStruc.hTaggedActor = READ_UINT32(p); p += sizeof(uint32);
g_tempStruc.numProcess = READ_UINT32(p); p += sizeof(uint32);
g_tempStruc.hProcess = READ_UINT32(p); p += sizeof(uint32);
g_tempStruc.hMusicScript = READ_UINT32(p); p += sizeof(uint32);
g_tempStruc.hMusicSegment = READ_UINT32(p); p += sizeof(uint32);
return &g_tempStruc;
}
SCENE_STRUC* parseSceneV3(const byte *p) {
g_tempStruc.defRefer = READ_UINT32(p); p += sizeof(uint32);
g_tempStruc.hSceneScript = READ_UINT32(p); p += sizeof(uint32);
g_tempStruc.hSceneDesc = READ_UINT32(p); p += sizeof(uint32);
g_tempStruc.numEntrance = READ_UINT32(p); p += sizeof(uint32);
g_tempStruc.hEntrance = READ_UINT32(p); p += sizeof(uint32);
g_tempStruc.numCameras = READ_UINT32(p); p += sizeof(uint32);
g_tempStruc.hCamera = READ_UINT32(p); p += sizeof(uint32);
g_tempStruc.numLights = READ_UINT32(p); p += sizeof(uint32);
g_tempStruc.hLight = READ_UINT32(p); p += sizeof(uint32);
g_tempStruc.numPoly = READ_UINT32(p); p += sizeof(uint32);
g_tempStruc.hPoly = READ_UINT32(p); p += sizeof(uint32);
g_tempStruc.numTaggedActor = READ_UINT32(p); p += sizeof(uint32);
g_tempStruc.hTaggedActor = READ_UINT32(p); p += sizeof(uint32);
g_tempStruc.numProcess = READ_UINT32(p); p += sizeof(uint32);
g_tempStruc.hProcess = READ_UINT32(p); p += sizeof(uint32);
g_tempStruc.hMusicScript = READ_UINT32(p); p += sizeof(uint32);
g_tempStruc.hMusicSegment = READ_UINT32(p); p += sizeof(uint32);
// scollfields scaling/speed - 4 ints
warning("TODO: Complete scene loading logic for Noir");
return &g_tempStruc;
}
const SCENE_STRUC *GetSceneStruc(const byte *pStruc) {
memset(&g_tempStruc, 0, sizeof(SCENE_STRUC));
if (TinselVersion == 3) {
return parseSceneV3(pStruc);
} else if (TinselVersion == 2) {
return parseSceneV2(pStruc);
} else {
return parseSceneV1(pStruc);
}
}
/**
* Started up for scene script and entrance script.
*/
static void SceneTinselProcess(CORO_PARAM, const void *param) {
// COROUTINE
CORO_BEGIN_CONTEXT;
INT_CONTEXT *pic;
const TP_INIT *pInit;
int myEscape;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
// The following myEscape value setting is used for enabling title screen skipping in DW1
if ((TinselVersion == 1) && (g_sceneCtr == 1)) g_initialMyEscape = GetEscEvents();
// DW1 PSX, Saturn and Mac has its own scene skipping script code for scenes 2 and 3 (bug #6094).
_ctx->myEscape = ((TinselVersion == 1) && (g_sceneCtr < ((TinselV1PSX || TinselV1Saturn || TinselV1Mac) ? 2 : 4))) ? g_initialMyEscape : 0;
// get the stuff copied to process when it was created
_ctx->pInit = (const TP_INIT *)param;
assert(_ctx->pInit);
assert(_ctx->pInit->hTinselCode); // Must have some code to run
_ctx->pic = InitInterpretContext(GS_SCENE,
FROM_32(_ctx->pInit->hTinselCode),
(TinselVersion >= 2) ? _ctx->pInit->event : NOEVENT,
NOPOLY, // No polygon
0, // No actor
NULL, // No object
_ctx->myEscape);
CORO_INVOKE_1(Interpret, _ctx->pic);
CORO_END_CODE;
}
/**
* Start up a SceneTinselProcess() running the scene
* script for various events.
*/
void SendSceneTinselProcess(TINSEL_EVENT event) {
SCENE_STRUC *ss;
if (g_SceneHandle != 0) {
ss = (SCENE_STRUC *) FindChunk(g_SceneHandle, CHUNK_SCENE);
if (ss->hSceneScript) {
TP_INIT init;
init.event = event;
init.hTinselCode = ss->hSceneScript;
CoroScheduler.createProcess(PID_TCODE, SceneTinselProcess, &init, sizeof(init));
}
}
}
/**
* Get the SCENE_STRUC
* Initialize polygons for the scene
* Initialize the actors for this scene
* Run the appropriate entrance code (if any)
* Get the default refer type
*/
static void LoadScene(SCNHANDLE scene, int entry) {
uint32 i;
TP_INIT init;
const SCENE_STRUC *ss;
const ENTRANCE_STRUC *es;
// Scene handle
g_SceneHandle = scene; // Save scene handle in case of Save_Scene()
_vm->_handle->LockMem(g_SceneHandle); // Make sure scene is loaded
_vm->_handle->LockScene(g_SceneHandle); // Prevent current scene from being discarded
if (TinselVersion >= 2) {
// CdPlay() stuff
byte *cptr = FindChunk(scene, CHUNK_CDPLAY_FILENUM);
assert(cptr);
i = READ_32(cptr);
assert(i < 512);
cptr = FindChunk(scene, CHUNK_CDPLAY_FILENAME);
assert(cptr);
_vm->_handle->SetCdPlaySceneDetails((const char *)cptr);
}
// Find scene structure
ss = GetSceneStruc(FindChunk(scene, CHUNK_SCENE));
assert(ss != NULL);
if (TinselVersion >= 2) {
// Music stuff
char *cptr = (char *)FindChunk(scene, CHUNK_MUSIC_FILENAME);
assert(cptr);
_vm->_pcmMusic->setMusicSceneDetails(FROM_32(ss->hMusicScript), FROM_32(ss->hMusicSegment), cptr);
}
if (entry == NO_ENTRY_NUM) {
// Restoring scene
// Initialize all the polygons for this scene
InitPolygons(FROM_32(ss->hPoly), FROM_32(ss->numPoly), true);
// Initialize the actors for this scene
_vm->_actor->StartTaggedActors(FROM_32(ss->hTaggedActor), FROM_32(ss->numTaggedActor), false);
if (TinselVersion >= 2)
// Returning from cutscene
SendSceneTinselProcess(RESTORE);
} else {
// Genuine new scene
// Initialize all the polygons for this scene
InitPolygons(FROM_32(ss->hPoly), FROM_32(ss->numPoly), false);
// Initialize the actors for this scene
_vm->_actor->StartTaggedActors(FROM_32(ss->hTaggedActor), FROM_32(ss->numTaggedActor), true);
// Run the appropriate entrance code (if any)
es = (const ENTRANCE_STRUC *)_vm->_handle->LockMem(FROM_32(ss->hEntrance));
for (i = 0; i < FROM_32(ss->numEntrance); i++) {
if (FROM_32(es->eNumber) == (uint)entry) {
if (es->hScript) {
init.event = STARTUP;
init.hTinselCode = es->hScript;
CoroScheduler.createProcess(PID_TCODE, SceneTinselProcess, &init, sizeof(init));
}
break;
}
// Move to next entrance
if (TinselVersion >= 2)
++es;
else
es = (const ENTRANCE_STRUC *)((const byte *)es + 8);
}
if (i == FROM_32(ss->numEntrance))
error("Non-existent scene entry number");
if (ss->hSceneScript) {
init.event = STARTUP;
init.hTinselCode = ss->hSceneScript;
CoroScheduler.createProcess(PID_TCODE, SceneTinselProcess, &init, sizeof(init));
}
}
// Default refer type
SetDefaultRefer(FROM_32(ss->defRefer));
// Scene's processes
SceneProcesses(FROM_32(ss->numProcess), FROM_32(ss->hProcess));
}
/**
* Wrap up the last scene.
*/
void EndScene() {
if (g_SceneHandle != 0) {
_vm->_handle->UnlockScene(g_SceneHandle);
g_SceneHandle = 0;
}
_vm->_dialogs->killInventory(); // Close down any open inventory
DropPolygons(); // No polygons
_vm->_scroll->DropScroll(); // No no-scrolls
_vm->_bg->DropBackground(); // No background
DropMovers(); // No moving actors
_vm->_cursor->DropCursor(); // No cursor
_vm->_actor->DropActors(); // No actor reels running
FreeAllTokens(); // No-one has tokens
FreeMostInterpretContexts(); // Only master script still interpreting
if (TinselVersion >= 2) {
SetSysVar(ISV_DIVERT_ACTOR, 0);
SetSysVar(ISV_GHOST_ACTOR, 0);
SetSysVar(SV_MinimumXoffset, 0);
SetSysVar(SV_MaximumXoffset, 0);
SetSysVar(SV_MinimumYoffset, 0);
SetSysVar(SV_MaximumYoffset, 0);
_vm->_font->ResetFontHandles();
NoSoundReels();
}
_vm->_sound->stopAllSamples(); // Kill off any still-running sample
//_vm->_mixer->stopAll();
// init the palette manager
ResetPalAllocator();
// init the object manager
KillAllObjects();
// kill all destructable process
CoroScheduler.killMatchingProcess(PID_DESTROY, PID_DESTROY);
}
/**
* Start up the standard stuff for the next scene.
*/
void PrimeScene() {
SetNoBlocking(false);
SetSysVar(SYS_SceneFxDimFactor, SysVar(SYS_DefaultFxDimFactor));
_vm->_cursor->RestartCursor(); // Restart the cursor
if (TinselVersion <= 1)
EnableTags(); // Next scene with tags enabled
CoroScheduler.createProcess(PID_SCROLL, ScrollProcess, NULL, 0);
CoroScheduler.createProcess(PID_SCROLL, EffectPolyProcess, NULL, 0);
#ifdef DEBUG
if (g_ShowPosition)
CoroScheduler.createProcess(PID_POSITION, CursorPositionProcess, NULL, 0);
#endif
CoroScheduler.createProcess(PID_TAG, TagProcess, NULL, 0);
CoroScheduler.createProcess(PID_TAG, PointProcess, NULL, 0);
// init the current background
_vm->_bg->InitBackground();
}
/**
* Wrap up the last scene and start up the next scene.
*/
void StartNewScene(SCNHANDLE scene, int entry) {
EndScene(); // Wrap up the last scene.
if (TinselVersion >= 2) {
TouchMoverReels();
_vm->_handle->LockMem(scene); // Do CD change before PrimeScene
}
PrimeScene(); // Start up the standard stuff for the next scene.
LoadScene(scene, entry);
g_isViewSet = false;
}
#ifdef DEBUG
/**
* Sets the ShowPosition flag, causing the cursor position process to be
* created in each scene.
*/
void setshowpos() {
g_ShowPosition = true;
}
#endif
/**
* Return the current scene handle.
*/
SCNHANDLE GetSceneHandle() {
return g_SceneHandle;
}
/**
* DoHailScene
*/
void DoHailScene(SCNHANDLE scene) {
// Find scene structure
const SCENE_STRUC *ss = GetSceneStruc(FindChunk(scene, CHUNK_SCENE));
if (ss != NULL && ss->hSceneScript) {
TP_INIT init;
init.event = NOEVENT;
init.hTinselCode = ss->hSceneScript;
CoroScheduler.createProcess(PID_SCENE, SceneTinselProcess, &init, sizeof(init));
}
}
/**
* WrapScene
*/
void WrapScene() {
SendSceneTinselProcess(CLOSEDOWN);
}
/**
* Set parameters for 3D rendering for Noir.
*/
void SetView(int sceneId, int scale) {
if (sceneId == SysVar(SV_SPRITER_SCENE_ID)) {
if (scale == SysVar(SV_SPRITER_SCALE)) {
debug("Ignoring SetView()");
return;
}
}
debug("SetView(%d, %d)", sceneId, scale);
SetSysVar(SV_SPRITER_SCALE, scale);
SetSysVar(SV_USER3 ,0x28);
if (g_isViewSet) {
//EndActors();
} else {
g_isViewSet = true;
}
int i = 0;
CAMERA_STRUC *pCamera = (CAMERA_STRUC *)_vm->_handle->LockMem(g_tempStruc.hCamera);
for (i = 0; i < g_tempStruc.numCameras; ++i, ++pCamera) {
if (sceneId == (int)FROM_32(pCamera->sceneId)) {
_vm->_spriter->SetCamera(
FROM_32(pCamera->rotX),
FROM_32(pCamera->rotY),
FROM_32(pCamera->rotZ),
FROM_32(pCamera->posX * SysVar(SV_SPRITER_SCALE)),
FROM_32(-pCamera->posY * SysVar(SV_SPRITER_SCALE)),
FROM_32(-pCamera->posZ * SysVar(SV_SPRITER_SCALE)),
FROM_32(pCamera->aperture)
);
SetSysVar(SV_SPRITER_SCENE_ID, sceneId);
break;
}
}
if (i == g_tempStruc.numCameras) {
// no suitable camera found
return;
}
LIGHT_STRUC *pLight = (LIGHT_STRUC *)_vm->_handle->LockMem(g_tempStruc.hLight);
for (i = 0; i < g_tempStruc.numLights; ++i, ++pLight) {
if (sceneId == (int)FROM_32(pLight->sceneId)) {
// TODO: Load lights
break;
}
}
if (i == g_tempStruc.numLights) {
// if no lights are present use the default light
}
// TODO: Update the ground plane
}
} // End of namespace Tinsel

95
engines/tinsel/scene.h Normal file
View File

@@ -0,0 +1,95 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* Scene parsing defines
*/
#ifndef TINSEL_SCENE_H
#define TINSEL_SCENE_H
#include "tinsel/dw.h"
#include "tinsel/events.h"
namespace Tinsel {
enum {
MAX_NODES = 32, ///< maximum nodes in a Node Path
MAX_NOSCROLL = 16, ///< maximum number of NoScroll commands in a scene
MAX_ENTRANCE = 25, ///< maximum number of entrances in a scene
MAX_POLY = 256, ///< maximum number of polygons in a scene
MAX_ACTOR = 32 ///< maximum number of actors in a scene
};
// ENTRANCE_STRUC bitflags
enum ENTRANCE_FLAGS {
fCall = 0x00000001L,
fHook = 0x00000002L
};
/** reference direction */
enum REFTYPE {
REF_DEFAULT, REF_UP, REF_DOWN, REF_LEFT, REF_RIGHT, REF_POINT
};
enum TFTYPE {
TF_NONE, TF_UP, TF_DOWN, TF_LEFT, TF_RIGHT, TF_FILM
};
/** different actor masks */
enum MASK_TYPE{
ACT_DEFAULT,
ACT_MASK = -1,
ACT_ALWAYS = -2
};
/** different scales */
enum SCALE {
SCALE_DEFAULT, SCALE_LARGE, SCALE_MEDIUM, SCALE_SMALL,
SCALE_COMPACT, SCALE_TINY,
SCALE_AUX1, SCALE_AUX2, SCALE_AUX3,
SCALE_AUX4, SCALE_AUX5
};
/** different reels */
enum REEL {
REEL_DEFAULT, REEL_ALL, REEL_HORIZ, REEL_VERT
};
typedef enum { TRANS_DEF, TRANS_CUT, TRANS_FADE } TRANSITS;
// amount to shift scene handles by
#define SCNHANDLE_SHIFT (((TinselVersion >= 2) && !TinselV2Demo) ? 25 : 23)
#define OFFSETMASK (((TinselVersion >= 2) && !TinselV2Demo) ? 0x01ffffffL : 0x007fffffL)
#define HANDLEMASK (((TinselVersion >= 2) && !TinselV2Demo) ? 0xFE000000L : 0xFF800000L)
void DoHailScene(SCNHANDLE scene);
void WrapScene();
void StartNewScene(SCNHANDLE scene, int entry);
void EndScene();
void SendSceneTinselProcess(TINSEL_EVENT event);
void SetView(int id, int scale);
} // End of namespace Tinsel
#endif // TINSEL_SCENE_H

325
engines/tinsel/sched.cpp Normal file
View File

@@ -0,0 +1,325 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* Process scheduler.
*/
#include "tinsel/handle.h"
#include "tinsel/pcode.h"
#include "tinsel/pid.h"
#include "tinsel/polygons.h"
#include "tinsel/sched.h"
#include "common/textconsole.h"
#include "common/util.h"
namespace Tinsel {
//----------------- LOCAL GLOBAL DATA --------------------
// These vars are reset upon engine destruction
static uint32 g_numSceneProcess = 0;
static SCNHANDLE g_hSceneProcess = 0;
static uint32 g_numGlobalProcess = 0;
static PROCESS_STRUC *g_pGlobalProcess = nullptr;
/**************************************************************************\
|*********** Stuff to do with scene and global processes ************|
\**************************************************************************/
void ResetVarsSched() {
g_numSceneProcess = 0;
g_hSceneProcess = 0;
g_numGlobalProcess = 0;
g_pGlobalProcess = nullptr;
}
/**
* The code for for restored scene processes.
*/
static void RestoredProcessProcess(CORO_PARAM, const void *param) {
CORO_BEGIN_CONTEXT;
INT_CONTEXT *pic;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
// get the stuff copied to process when it was created
// FIXME: Code without typedef emits -Wcast-qual GCC warning.
// However, adding const casts break compilation with -fpermissive.
// Reverted to local typedef for now until this can be avoided.
#if 0
_ctx->pic = *(INT_CONTEXT **)param;
//_ctx->pic = *(const INT_CONTEXT **)param;
#else
typedef INT_CONTEXT *PINT_CONTEXT;
_ctx->pic = *(const PINT_CONTEXT *)param;
#endif
_ctx->pic = RestoreInterpretContext(_ctx->pic);
AttachInterpret(_ctx->pic, CoroScheduler.getCurrentProcess());
CORO_INVOKE_1(Interpret, _ctx->pic);
CORO_END_CODE;
}
/**
* Process Tinsel Process
*/
static void ProcessTinselProcess(CORO_PARAM, const void *param) {
// FIXME: Code without typedef emits -Wcast-qual GCC warning.
// However, adding const casts break compilation with -fpermissive.
// Reverted to local typedef for now until this can be avoided.
#if 0
//INT_CONTEXT **pPic = (INT_CONTEXT **)param;
const INT_CONTEXT **pPic = (const INT_CONTEXT **)param;
#else
typedef INT_CONTEXT *PINT_CONTEXT;
const PINT_CONTEXT *pPic = (const PINT_CONTEXT *)param;
#endif
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
// get the stuff copied to process when it was created
CORO_INVOKE_1(Interpret, *pPic);
CORO_KILL_SELF();
CORO_END_CODE;
}
/**************************************************************************\
|***************** Stuff to do with scene processes *****************|
\**************************************************************************/
/**
* Called to restore a scene process.
*/
void RestoreSceneProcess(INT_CONTEXT *pic) {
const PROCESS_STRUC *processes = _vm->_handle->GetProcessData(g_hSceneProcess, g_numSceneProcess);
for (uint32 i = 0; i < g_numSceneProcess; i++) {
if (processes[i].hProcessCode == pic->hCode) {
CoroScheduler.createProcess(PID_PROCESS + i, RestoredProcessProcess,
&pic, sizeof(pic));
break;
}
}
delete[] processes;
}
/**
* Run a scene process with the given event.
*/
void SceneProcessEvent(CORO_PARAM, uint32 procID, TINSEL_EVENT event, bool bWait, int myEscape,
bool *result) {
if (result) *result = false;
CORO_BEGIN_CONTEXT;
const PROCESS_STRUC *processes;
Common::PPROCESS pProc;
INT_CONTEXT *pic;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
_ctx->processes = _vm->_handle->GetProcessData(g_hSceneProcess, g_numSceneProcess);
for (uint32 i = 0; i < g_numSceneProcess; i++) {
if (_ctx->processes[i].processId == procID) {
assert(_ctx->processes[i].hProcessCode); // Must have some code to run
_ctx->pic = InitInterpretContext(GS_PROCESS,
_ctx->processes[i].hProcessCode,
event,
NOPOLY, // No polygon
0, // No actor
nullptr, // No object
myEscape
);
if (_ctx->pic) {
_ctx->pProc = CoroScheduler.createProcess(
PID_PROCESS + i,
ProcessTinselProcess,
&_ctx->pic,
sizeof(_ctx->pic));
AttachInterpret(_ctx->pic, _ctx->pProc);
}
break;
}
}
if (bWait && _ctx->pProc)
CORO_INVOKE_2(WaitInterpret, _ctx->pProc, result);
delete[] _ctx->processes;
CORO_END_CODE;
}
/**
* Kill all instances of a scene process.
*/
void KillSceneProcess(uint32 procID) {
const PROCESS_STRUC *processes = _vm->_handle->GetProcessData(g_hSceneProcess, g_numSceneProcess);
for (uint32 i = 0; i < g_numSceneProcess; i++) {
if (processes[i].processId == procID) {
CoroScheduler.killMatchingProcess(PID_PROCESS + i, -1);
break;
}
}
delete[] processes;
}
/**
* Register the scene processes in a scene.
*/
void SceneProcesses(uint32 numProcess, SCNHANDLE hProcess) {
g_numSceneProcess = numProcess;
g_hSceneProcess = hProcess;
}
/**************************************************************************\
|***************** Stuff to do with global processes ****************|
\**************************************************************************/
/**
* Called to restore a global process.
*/
void RestoreGlobalProcess(INT_CONTEXT *pic) {
uint32 i; // Loop counter
for (i = 0; i < g_numGlobalProcess; i++) {
if (g_pGlobalProcess[i].hProcessCode == pic->hCode) {
CoroScheduler.createProcess(PID_GPROCESS + i, RestoredProcessProcess,
&pic, sizeof(pic));
break;
}
}
assert(i < g_numGlobalProcess);
}
/**
* Kill them all (restore game).
*/
void KillGlobalProcesses() {
for (uint32 i = 0; i < g_numGlobalProcess; ++i) {
CoroScheduler.killMatchingProcess(PID_GPROCESS + i, -1);
}
}
/**
* Run a global process with the given event.
*/
bool GlobalProcessEvent(CORO_PARAM, uint32 procID, TINSEL_EVENT event, bool bWait, int myEscape) {
CORO_BEGIN_CONTEXT;
INT_CONTEXT *pic;
Common::PPROCESS pProc;
CORO_END_CONTEXT(_ctx);
bool result = false;
CORO_BEGIN_CODE(_ctx);
uint32 i; // Loop counter
_ctx->pProc= nullptr;
for (i = 0; i < g_numGlobalProcess; ++i) {
if (g_pGlobalProcess[i].processId == procID) {
assert(g_pGlobalProcess[i].hProcessCode); // Must have some code to run
_ctx->pic = InitInterpretContext(GS_GPROCESS,
g_pGlobalProcess[i].hProcessCode,
event,
NOPOLY, // No polygon
0, // No actor
NULL, // No object
myEscape);
if (_ctx->pic != NULL) {
_ctx->pProc = CoroScheduler.createProcess(PID_GPROCESS + i, ProcessTinselProcess,
&_ctx->pic, sizeof(_ctx->pic));
AttachInterpret(_ctx->pic, _ctx->pProc);
}
break;
}
}
if ((i == g_numGlobalProcess) || (_ctx->pic == NULL))
result = false;
else if (bWait)
CORO_INVOKE_ARGS_V(WaitInterpret, false, (CORO_SUBCTX, _ctx->pProc, &result));
CORO_END_CODE;
return result;
}
/**
* Kill all instances of a global process.
*/
void xKillGlobalProcess(uint32 procID) {
uint32 i; // Loop counter
for (i = 0; i < g_numGlobalProcess; ++i) {
if (g_pGlobalProcess[i].processId == procID) {
CoroScheduler.killMatchingProcess(PID_GPROCESS + i, -1);
break;
}
}
}
/**
* Register the global processes list
*/
void GlobalProcesses(uint32 numProcess, byte *pProcess) {
g_pGlobalProcess = new PROCESS_STRUC[numProcess];
g_numGlobalProcess = numProcess;
byte *p = pProcess;
for (uint i = 0; i < numProcess; ++i, p += 8) {
g_pGlobalProcess[i].processId = READ_32(p);
g_pGlobalProcess[i].hProcessCode = READ_32(p + 4);
}
}
/**
* Frees the global processes list
*/
void FreeGlobalProcesses() {
delete[] g_pGlobalProcess;
g_pGlobalProcess = 0;
g_numGlobalProcess = 0;
}
} // End of namespace Tinsel

57
engines/tinsel/sched.h Normal file
View File

@@ -0,0 +1,57 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* Data structures used by the process scheduler
*/
#ifndef TINSEL_SCHED_H // prevent multiple includes
#define TINSEL_SCHED_H
#include "common/coroutines.h"
#include "tinsel/dw.h" // new data types
#include "tinsel/events.h"
#include "tinsel/tinsel.h"
namespace Tinsel {
struct INT_CONTEXT;
struct PROCESS_STRUC {
uint32 processId; // ID of process
SCNHANDLE hProcessCode; // handle to actor script
};
//----------------- FUNCTION PROTOTYPES --------------------
void SceneProcesses(uint32 numProcess, SCNHANDLE hProcess);
void KillSceneProcess(uint32 procID);
void SceneProcessEvent(CORO_PARAM, uint32 procID, TINSEL_EVENT event, bool bWait,
int myEscape, bool *result = NULL);
void RestoreSceneProcess(INT_CONTEXT *pic);
void GlobalProcesses(uint32 numProcess, byte *pProcess);
void xKillGlobalProcess(uint32 procID);
bool GlobalProcessEvent(CORO_PARAM, uint32 procID, TINSEL_EVENT event, bool bWait, int myEscape);
void RestoreGlobalProcess(INT_CONTEXT *pic);
void KillGlobalProcesses();
void FreeGlobalProcesses();
} // End of namespace Tinsel
#endif // TINSEL_SCHED_H

71
engines/tinsel/scn.cpp Normal file
View File

@@ -0,0 +1,71 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* A (some would say very) small collection of utility functions.
*/
#include "tinsel/dw.h"
#include "tinsel/film.h"
#include "tinsel/handle.h"
#include "tinsel/multiobj.h"
#include "tinsel/scn.h"
#include "tinsel/tinsel.h" // for _vm
namespace Tinsel {
/**
* Given a scene handle and a chunk id, gets the scene in RAM and
* locates the requested chunk.
* @param handle Scene handle
* @param chunk Chunk Id
*/
byte *FindChunk(SCNHANDLE handle, uint32 chunk) {
byte *bptr = _vm->_handle->LockMem(handle);
uint32 *lptr = (uint32 *)bptr;
uint32 add;
// Initial adjustment for Tinsel 1 chunk types
if ((TinselVersion <= 1) && (chunk >= CHUNK_SCENE) &&
(chunk != CHUNK_MBSTRING))
--chunk;
// V0 chunk types can be found by subtracting 2 from the
// chunk type. Note that CHUNK_STRING and CHUNK_BITMAP are
// the same in V0 and V1
if (TinselVersion == 0 &&
chunk != CHUNK_STRING && chunk != CHUNK_BITMAP)
chunk -= 0x2L;
while (1) {
if (READ_32(lptr) == chunk)
return (byte *)(lptr + 2);
++lptr;
add = READ_32(lptr);
if (!add)
// End of file reached
return NULL;
// Move to next chunk
lptr = (uint32 *)(bptr + add);
}
}
} // End of namespace Tinsel

85
engines/tinsel/scn.h Normal file
View File

@@ -0,0 +1,85 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef TINSEL_SCN_H // prevent multiple includes
#define TINSEL_SCN_H
#include "tinsel/dw.h"
namespace Tinsel {
// chunk identifier numbers
// V2 chunks
#define CHUNK_STRING 0x33340001L // same in V1 and V2
#define CHUNK_BITMAP 0x33340002L // same in V1 and V2
#define CHUNK_CHARPTR 0x33340003L // not used!
#define CHUNK_CHARMATRIX 0x33340004L // not used!
#define CHUNK_PALETTE 0x33340005L // not used!
#define CHUNK_IMAGE 0x33340006L // not used!
#define CHUNK_ANI_FRAME 0x33340007L // not used!
#define CHUNK_FILM 0x33340008L // not used!
#define CHUNK_FONT 0x33340009L // not used!
#define CHUNK_PCODE 0x3334000AL
#define CHUNK_ENTRANCE 0x3334000BL // not used!
#define CHUNK_POLYGONS 0x3334000CL // not used!
#define CHUNK_ACTORS 0x3334000DL // not used!
#define CHUNK_PROCESSES 0x3334000EL // Tinsel 2 only
// Following chunk Ids should be decremented by 1 for Tinsel 1
#define CHUNK_SCENE 0x3334000FL
#define CHUNK_TOTAL_ACTORS 0x33340010L
#define CHUNK_TOTAL_GLOBALS 0x33340011L
#define CHUNK_TOTAL_OBJECTS 0x33340012L
#define CHUNK_OBJECTS 0x33340013L
#define CHUNK_MIDI 0x33340014L // not used!
#define CHUNK_SAMPLE 0x33340015L // not used!
#define CHUNK_TOTAL_POLY 0x33340016L
// Following chunks are Tinsel 2 only
#define CHUNK_NUM_PROCESSES 0x33340017L // Master scene only
#define CHUNK_MASTER_SCRIPT 0x33340018L
#define CHUNK_CDPLAY_FILENUM 0x33340019L
#define CHUNK_CDPLAY_HANDLE 0x3334001AL
#define CHUNK_CDPLAY_FILENAME 0x3334001BL
#define CHUNK_MUSIC_FILENAME 0x3334001CL
#define CHUNK_MUSIC_SCRIPT 0x3334001DL
#define CHUNK_MUSIC_SEGMENT 0x3334001EL
#define CHUNK_SCENE_HOPPER 0x3334001FL // Hopper file only
#define CHUNK_SCENE_HOPPER2 0x33340030L // Hopper file only
#define CHUNK_TIME_STAMPS 0x33340020L
// This single chunk is common to all Tinsel versions
#define CHUNK_MBSTRING 0x33340022L
// Used in Discworld Noir
#define CHUNK_GAME 0x33340031L
// This is a base, subsequent numbers may also get used
#define CHUNK_GRAB_NAME 0x33340100L
byte *FindChunk(SCNHANDLE handle, uint32 chunk);
} // End of namespace Tinsel
#endif /* TINSEL_SCN_H */

542
engines/tinsel/scroll.cpp Normal file
View File

@@ -0,0 +1,542 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* Handles scrolling
*/
#include "tinsel/actors.h"
#include "tinsel/background.h"
#include "tinsel/cursor.h"
#include "tinsel/dw.h"
#include "tinsel/graphics.h"
#include "tinsel/polygons.h"
#include "tinsel/movers.h"
#include "tinsel/scroll.h"
#include "tinsel/sched.h"
#include "tinsel/sysvar.h"
#include "tinsel/tinsel.h"
namespace Tinsel {
//----------------- LOCAL DEFINES --------------------
#define LEFT 'L'
#define RIGHT 'R'
#define UP 'U'
#define DOWN 'D'
#define SCROLLPIXELS 8 // Number of pixels to scroll per iteration
// Distance from edge that triggers a scroll
#define RLDISTANCE ((TinselVersion >= 2) ? _scrollData.xTrigger : 50)
#define UDISTANCE ((TinselVersion >= 2) ? _scrollData.yTriggerTop : 20)
#define DDISTANCE ((TinselVersion >= 2) ? _scrollData.yTriggerBottom : 20)
// Number of iterations to make
#define RLSCROLL 160 // 20*8 = 160 = half a screen
#define UDSCROLL 100 // 12.5*8 = 100 = half a screen
Scroll::Scroll() {
_leftScroll = 0;
_downScroll = 0;
_scrollActor = 0;
_pScrollMover = nullptr;
_oldx = 0;
_oldy = 0;
_scrollData = {
{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}},
{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}},
0,
0,
// DW2 fields
0,
0,
0,
0,
0,
0,
0};
_imageH = 0;
_imageW = 0;
_scrollCursor = 0;
_scrollPixelsX = SCROLLPIXELS;
_scrollPixelsY = SCROLLPIXELS;
}
/**
* Reset the ScrollCursor flag
*/
void Scroll::DontScrollCursor() {
_scrollCursor = false;
}
/**
* Set the ScrollCursor flag
*/
void Scroll::DoScrollCursor() {
_scrollCursor = true;
}
/**
* Configure a no-scroll boundary for a scene.
*/
void Scroll::SetNoScroll(int x1, int y1, int x2, int y2) {
if (x1 == x2) {
/* Vertical line */
assert(_scrollData.NumNoH < MAX_HNOSCROLL);
_scrollData.NoHScroll[_scrollData.NumNoH].ln = x1; // X pos of vertical line
_scrollData.NoHScroll[_scrollData.NumNoH].c1 = y1;
_scrollData.NoHScroll[_scrollData.NumNoH].c2 = y2;
_scrollData.NumNoH++;
} else if (y1 == y2) {
/* Horizontal line */
assert(_scrollData.NumNoV < MAX_VNOSCROLL);
_scrollData.NoVScroll[_scrollData.NumNoV].ln = y1; // Y pos of horizontal line
_scrollData.NoVScroll[_scrollData.NumNoV].c1 = x1;
_scrollData.NoVScroll[_scrollData.NumNoV].c2 = x2;
_scrollData.NumNoV++;
} else {
/* No-scroll lines must be horizontal or vertical */
}
}
/**
* Called from scroll process when it thinks that a scroll is in order.
* Checks for no-scroll boundaries and sets off a scroll if allowed.
*/
void Scroll::NeedScroll(int direction) {
uint i;
int BottomLine, RightCol;
int Loffset, Toffset;
// get background offsets
_vm->_bg->PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
switch (direction) {
case LEFT: /* Picture will go left, 'camera' right */
BottomLine = Toffset + (SCREEN_HEIGHT - 1);
RightCol = Loffset + (SCREEN_WIDTH - 1);
for (i = 0; i < _scrollData.NumNoH; i++) {
if (RightCol >= _scrollData.NoHScroll[i].ln - 1 && RightCol <= _scrollData.NoHScroll[i].ln + 1 &&
((_scrollData.NoHScroll[i].c1 >= Toffset && _scrollData.NoHScroll[i].c1 <= BottomLine) ||
(_scrollData.NoHScroll[i].c2 >= Toffset && _scrollData.NoHScroll[i].c2 <= BottomLine) ||
(_scrollData.NoHScroll[i].c1 < Toffset && _scrollData.NoHScroll[i].c2 > BottomLine)))
return;
}
if (_leftScroll <= 0) {
if (TinselVersion >= 2) {
_scrollPixelsX = _scrollData.xSpeed;
_leftScroll += _scrollData.xDistance;
} else {
_scrollPixelsX = SCROLLPIXELS;
_leftScroll = RLSCROLL;
}
}
break;
case RIGHT: /* Picture will go right, 'camera' left */
BottomLine = Toffset + (SCREEN_HEIGHT - 1);
for (i = 0; i < _scrollData.NumNoH; i++) {
if (Loffset >= _scrollData.NoHScroll[i].ln - 1 && Loffset <= _scrollData.NoHScroll[i].ln + 1 &&
((_scrollData.NoHScroll[i].c1 >= Toffset && _scrollData.NoHScroll[i].c1 <= BottomLine) ||
(_scrollData.NoHScroll[i].c2 >= Toffset && _scrollData.NoHScroll[i].c2 <= BottomLine) ||
(_scrollData.NoHScroll[i].c1 < Toffset && _scrollData.NoHScroll[i].c2 > BottomLine)))
return;
}
if (_leftScroll >= 0) {
if (TinselVersion >= 2) {
_scrollPixelsX = _scrollData.xSpeed;
_leftScroll -= _scrollData.xDistance;
} else {
_scrollPixelsX = SCROLLPIXELS;
_leftScroll = -RLSCROLL;
}
}
break;
case UP: /* Picture will go upwards, 'camera' downwards */
BottomLine = Toffset + (SCREEN_HEIGHT - 1);
RightCol = Loffset + (SCREEN_WIDTH - 1);
for (i = 0; i < _scrollData.NumNoV; i++) {
if ((BottomLine >= _scrollData.NoVScroll[i].ln - 1 && BottomLine <= _scrollData.NoVScroll[i].ln + 1) &&
((_scrollData.NoVScroll[i].c1 >= Loffset && _scrollData.NoVScroll[i].c1 <= RightCol) ||
(_scrollData.NoVScroll[i].c2 >= Loffset && _scrollData.NoVScroll[i].c2 <= RightCol) ||
(_scrollData.NoVScroll[i].c1 < Loffset && _scrollData.NoVScroll[i].c2 > RightCol)))
return;
}
if (_downScroll <= 0) {
if (TinselVersion >= 2) {
_scrollPixelsY = _scrollData.ySpeed;
_downScroll += _scrollData.yDistance;
} else {
_scrollPixelsY = SCROLLPIXELS;
_downScroll = UDSCROLL;
}
}
break;
case DOWN: /* Picture will go downwards, 'camera' upwards */
RightCol = Loffset + (SCREEN_WIDTH - 1);
for (i = 0; i < _scrollData.NumNoV; i++) {
if (Toffset >= _scrollData.NoVScroll[i].ln - 1 && Toffset <= _scrollData.NoVScroll[i].ln + 1 &&
((_scrollData.NoVScroll[i].c1 >= Loffset && _scrollData.NoVScroll[i].c1 <= RightCol) ||
(_scrollData.NoVScroll[i].c2 >= Loffset && _scrollData.NoVScroll[i].c2 <= RightCol) ||
(_scrollData.NoVScroll[i].c1 < Loffset && _scrollData.NoVScroll[i].c2 > RightCol)))
return;
}
if (_downScroll >= 0) {
if (TinselVersion >= 2) {
_scrollPixelsY = _scrollData.ySpeed;
_downScroll -= _scrollData.yDistance;
} else {
_scrollPixelsY = SCROLLPIXELS;
_downScroll = -UDSCROLL;
}
}
break;
default:
break;
}
}
/**
* Called from scroll process - Scrolls the image as appropriate.
*/
void Scroll::ScrollImage() {
int OldLoffset = 0, OldToffset = 0; // Used when keeping cursor on a tag
int Loffset, Toffset;
int curX, curY;
if (!_leftScroll && !_downScroll) // Only scroll if required
return;
// get background offsets
_vm->_bg->PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
/*
* Keeping cursor on a tag?
*/
if (_scrollCursor) {
_vm->_cursor->GetCursorXYNoWait(&curX, &curY, true);
if (InPolygon(curX, curY, TAG) != NOPOLY || InPolygon(curX, curY, EXIT) != NOPOLY) {
OldLoffset = Loffset;
OldToffset = Toffset;
} else
_scrollCursor = false;
}
/*
* Horizontal scrolling
*/
if (_leftScroll > 0) {
_leftScroll -= _scrollPixelsX;
if (_leftScroll < 0) {
Loffset += _leftScroll;
_leftScroll = 0;
}
Loffset += _scrollPixelsX; // Move right
if (Loffset > _imageW - SCREEN_WIDTH)
Loffset = _imageW - SCREEN_WIDTH;// Now at extreme right
/*** New feature to prop up rickety scroll boundaries ***/
if ((TinselVersion >= 2) && SysVar(SV_MaximumXoffset) && (Loffset > SysVar(SV_MaximumXoffset)))
Loffset = SysVar(SV_MaximumXoffset);
} else if (_leftScroll < 0) {
_leftScroll += _scrollPixelsX;
if (_leftScroll > 0) {
Loffset += _leftScroll;
_leftScroll = 0;
}
Loffset -= _scrollPixelsX; // Move left
if (Loffset < 0)
Loffset = 0; // Now at extreme left
/*** New feature to prop up rickety scroll boundaries ***/
if ((TinselVersion >= 2) && SysVar(SV_MinimumXoffset) && (Loffset < SysVar(SV_MinimumXoffset)))
Loffset = SysVar(SV_MinimumXoffset);
}
/*
* Vertical scrolling
*/
if (_downScroll > 0) {
_downScroll -= _scrollPixelsY;
if (_downScroll < 0) {
Toffset += _downScroll;
_downScroll = 0;
}
Toffset += _scrollPixelsY; // Move down
if (Toffset > _imageH - SCREEN_HEIGHT)
Toffset = _imageH - SCREEN_HEIGHT;// Now at extreme bottom
/*** New feature to prop up rickety scroll boundaries ***/
if ((TinselVersion >= 2) && SysVar(SV_MaximumYoffset) && Toffset > SysVar(SV_MaximumYoffset))
Toffset = SysVar(SV_MaximumYoffset);
} else if (_downScroll < 0) {
_downScroll += _scrollPixelsY;
if (_downScroll > 0) {
Toffset += _downScroll;
_downScroll = 0;
}
Toffset -= _scrollPixelsY; // Move up
if (Toffset < 0)
Toffset = 0; // Now at extreme top
/*** New feature to prop up rickety scroll boundaries ***/
if ((TinselVersion >= 2) && SysVar(SV_MinimumYoffset) && Toffset < SysVar(SV_MinimumYoffset))
Toffset = SysVar(SV_MinimumYoffset);
}
/*
* Move cursor if keeping cursor on a tag.
*/
if (_scrollCursor)
_vm->_cursor->AdjustCursorXY(OldLoffset - Loffset, OldToffset - Toffset);
_vm->_bg->PlayfieldSetPos(FIELD_WORLD, Loffset, Toffset);
}
/**
* See if the actor on whom the camera is is approaching an edge.
* Request a scroll if he is.
*/
void Scroll::MonitorScroll() {
int newx, newy;
int Loffset, Toffset;
/*
* Only do it if the actor is there and is visible
*/
if (!_pScrollMover || MoverHidden(_pScrollMover) || !MoverIs(_pScrollMover))
return;
_vm->_actor->GetActorPos(_scrollActor, &newx, &newy);
if (_oldx == newx && _oldy == newy)
return;
_vm->_bg->PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
/*
* Approaching right side or left side of the screen?
*/
if (newx > Loffset+SCREEN_WIDTH - RLDISTANCE && Loffset < _imageW - SCREEN_WIDTH) {
if (newx > _oldx)
NeedScroll(LEFT);
} else if (newx < Loffset + RLDISTANCE && Loffset) {
if (newx < _oldx)
NeedScroll(RIGHT);
}
/*
* Approaching bottom or top of the screen?
*/
if (newy > Toffset+SCREEN_HEIGHT-DDISTANCE && Toffset < _imageH-SCREEN_HEIGHT) {
if (newy > _oldy)
NeedScroll(UP);
} else if (Toffset && newy < Toffset + UDISTANCE + _vm->_actor->GetActorBottom(_scrollActor) - _vm->_actor->GetActorTop(_scrollActor)) {
if (newy < _oldy)
NeedScroll(DOWN);
}
_oldx = newx;
_oldy = newy;
}
void Scroll::RestoreScrollDefaults() {
_scrollData.xTrigger = SysVar(SV_SCROLL_XTRIGGER);
_scrollData.xDistance = SysVar(SV_SCROLL_XDISTANCE);
_scrollData.xSpeed = SysVar(SV_SCROLL_XSPEED);
_scrollData.yTriggerTop = SysVar(SV_SCROLL_YTRIGGERTOP);
_scrollData.yTriggerBottom= SysVar(SV_SCROLL_YTRIGGERBOT);
_scrollData.yDistance = SysVar(SV_SCROLL_YDISTANCE);
_scrollData.ySpeed = SysVar(SV_SCROLL_YSPEED);
}
/**
* Does the obvious - called at the end of a scene.
*/
void Scroll::DropScroll() {
_scrollData.NumNoH = _scrollData.NumNoV = 0;
if (TinselVersion >= 2) {
_leftScroll = _downScroll = 0; // No iterations outstanding
_oldx = _oldy = 0;
_scrollPixelsX = _scrollData.xSpeed;
_scrollPixelsY = _scrollData.ySpeed;
RestoreScrollDefaults();
}
}
/**
* Change which actor the camera is following.
*/
void Scroll::ScrollFocus(int ano) {
if (_scrollActor != ano) {
_oldx = _oldy = 0;
_scrollActor = ano;
_pScrollMover = ano ? GetMover(_scrollActor) : NULL;
}
}
/**
* Returns the actor which the camera is following
*/
int Scroll::GetScrollFocus() {
return _scrollActor;
}
/**
* Scroll to abslote position.
*/
void Scroll::ScrollTo(int x, int y, int xIter, int yIter) {
int Loffset, Toffset; // for background offsets
_scrollPixelsX = xIter != 0 ? xIter : ((TinselVersion >= 2) ? _scrollData.xSpeed : SCROLLPIXELS);
_scrollPixelsY = yIter != 0 ? yIter : ((TinselVersion >= 2) ? _scrollData.ySpeed : SCROLLPIXELS);
_vm->_bg->PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset); // get background offsets
_leftScroll = x - Loffset;
_downScroll = y - Toffset;
}
/**
* Kill of any current scroll.
*/
void Scroll::KillScroll() {
_leftScroll = _downScroll = 0;
}
void Scroll::GetNoScrollData(SCROLLDATA *ssd) {
memcpy(ssd, &_scrollData, sizeof(SCROLLDATA));
}
void Scroll::RestoreNoScrollData(SCROLLDATA *ssd) {
memcpy(&_scrollData, ssd, sizeof(SCROLLDATA));
}
/**
* SetScrollParameters
*/
void Scroll::SetScrollParameters(int xTrigger, int xDistance, int xSpeed, int yTriggerTop,
int yTriggerBottom, int yDistance, int ySpeed) {
if (xTrigger == 0 && xDistance == 0 && xSpeed == 0
&& yTriggerTop == 0 && yTriggerBottom && yDistance == 0 && ySpeed == 0) {
// Restore defaults
RestoreScrollDefaults();
} else {
if (xTrigger)
_scrollData.xTrigger = xTrigger;
if (xDistance)
_scrollData.xDistance = xDistance;
if (xSpeed)
_scrollData.xSpeed = xSpeed;
if (yTriggerTop)
_scrollData.yTriggerTop = yTriggerTop;
if (yTriggerBottom)
_scrollData.yTriggerBottom = yTriggerBottom;
if (yDistance)
_scrollData.yDistance = yDistance;
if (ySpeed)
_scrollData.ySpeed = ySpeed;
}
}
bool Scroll::IsScrolling() {
return (_leftScroll || _downScroll);
}
void Scroll::InitScroll(int width, int height) {
_imageH = height; // Dimensions
_imageW = width; // of this scene.
if (TinselVersion <= 1) {
_leftScroll = _downScroll = 0; // No iterations outstanding
_oldx = _oldy = 0;
_scrollPixelsX = _scrollPixelsY = SCROLLPIXELS;
}
if (!_scrollActor)
_scrollActor = _vm->_actor->GetLeadId();
_pScrollMover = GetMover(_scrollActor);
}
/**
* Decide when to scroll and scroll when decided to.
*/
void ScrollProcess(CORO_PARAM, const void *) {
int width, height;
// COROUTINE
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
// In Tinsel v2, scenes may play movies, so the background may not always
// already be initialized like it is in v1
while (!_vm->_bg->GetBgObject())
CORO_SLEEP(1);
width = _vm->_bg->BgWidth(); // Dimensions
height = _vm->_bg->BgHeight(); // of this scene.
// Give up if there'll be no purpose in this process
if (width == SCREEN_WIDTH && height == SCREEN_HEIGHT)
CORO_KILL_SELF();
_vm->_scroll->InitScroll(width, height);
while (1) {
_vm->_scroll->MonitorScroll(); // Set scroll requirement
_vm->_scroll->ScrollImage();
CORO_SLEEP(1); // allow re-scheduling
}
CORO_END_CODE;
}
} // End of namespace Tinsel

106
engines/tinsel/scroll.h Normal file
View File

@@ -0,0 +1,106 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef TINSEL_SCROLL_H // prevent multiple includes
#define TINSEL_SCROLL_H
namespace Tinsel {
#define MAX_HNOSCROLL 10
#define MAX_VNOSCROLL 10
// These structures defined here so boundaries can be saved
struct NOSCROLLB {
int ln;
int c1;
int c2;
};
struct SCROLLDATA {
NOSCROLLB NoVScroll[MAX_VNOSCROLL]; // Vertical no-scroll boundaries
NOSCROLLB NoHScroll[MAX_HNOSCROLL]; // Horizontal no-scroll boundaries
unsigned NumNoV, NumNoH; // Counts of no-scroll boundaries
// DW2 fields
int xTrigger;
int xDistance;
int xSpeed;
int yTriggerTop;
int yTriggerBottom;
int yDistance;
int ySpeed;
};
class Scroll {
public:
Scroll();
void DontScrollCursor();
void DoScrollCursor();
void SetNoScroll(int x1, int y1, int x2, int y2);
void DropScroll();
void ScrollFocus(int actor);
int GetScrollFocus();
void ScrollTo(int x, int y, int xIter, int yIter);
void KillScroll();
void GetNoScrollData(SCROLLDATA *ssd);
void RestoreNoScrollData(SCROLLDATA *ssd);
void SetScrollParameters(int xTrigger, int xDistance, int xSpeed, int yTriggerTop,
int yTriggerBottom, int yDistance, int ySpeed);
bool IsScrolling();
void ScrollImage();
void MonitorScroll();
void InitScroll(int width, int height);
private:
void NeedScroll(int direction);
void RestoreScrollDefaults();
int _leftScroll, _downScroll; // Number of iterations outstanding
int _scrollActor;
MOVER *_pScrollMover;
int _oldx, _oldy;
/** Boundaries and numbers of boundaries */
SCROLLDATA _scrollData;
int _imageW, _imageH;
bool _scrollCursor; // If a TAG or EXIT polygon is clicked on,
// the cursor is kept over that polygon
// whilst scrolling
int _scrollPixelsX, _scrollPixelsY;
};
void ScrollProcess(CORO_PARAM, const void *);
} // End of namespace Tinsel
#endif /* TINSEL_SCROLL_H */

574
engines/tinsel/sound.cpp Normal file
View File

@@ -0,0 +1,574 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* sound functionality
*/
#include "tinsel/sound.h"
#include "tinsel/adpcm.h"
#include "tinsel/dw.h"
#include "tinsel/config.h"
#include "tinsel/music.h"
#include "tinsel/tinsel.h"
#include "tinsel/sysvar.h"
#include "tinsel/background.h"
#include "common/endian.h"
#include "common/memstream.h"
#include "common/system.h"
#include "audio/mixer.h"
#include "audio/decoders/flac.h"
#include "audio/decoders/mp3.h"
#include "audio/decoders/raw.h"
#include "audio/decoders/vorbis.h"
#include "audio/decoders/xa.h"
#include "gui/message.h"
namespace Tinsel {
extern LANGUAGE g_sampleLanguage;
//--------------------------- General data ----------------------------------
SoundManager::SoundManager(TinselEngine *vm) :
//_vm(vm), // TODO: Enable this once global _vm var is gone
_sampleIndex(0), _sampleIndexLen(0),
_soundMode(kVOCMode) {
for (int i = 0; i < kNumChannels; i++)
_channels[i].sampleNum = _channels[i].subSample = -1;
}
SoundManager::~SoundManager() {
free(_sampleIndex);
}
/**
* Plays the specified sample through the sound driver.
* @param id Identifier of sample to be played
* @param type type of sound (voice or sfx)
* @param handle sound handle
*/
// playSample for DiscWorld 1
bool SoundManager::playSample(int id, Audio::Mixer::SoundType type, Audio::SoundHandle *handle) {
// Floppy version has no sample file.
if (!_vm->isV1CD())
return false;
// no sample driver?
if (!_vm->_mixer->isReady())
return false;
Channel &curChan = _channels[kChannelTinsel1];
// stop any currently playing sample
_vm->_mixer->stopHandle(curChan.handle);
// make sure id is in range
assert(id > 0 && id < _sampleIndexLen);
curChan.sampleNum = id;
curChan.subSample = 0;
// get file offset for this sample
uint32 dwSampleIndex = _sampleIndex[id];
// move to correct position in the sample file
_sampleStream.seek(dwSampleIndex);
if (_sampleStream.eos() || _sampleStream.err() || (uint32)_sampleStream.pos() != dwSampleIndex)
error(FILE_IS_CORRUPT, _vm->getSampleFile(g_sampleLanguage));
// read the length of the sample
uint32 sampleLen = _sampleStream.readUint32();
if (_sampleStream.eos() || _sampleStream.err())
error(FILE_IS_CORRUPT, _vm->getSampleFile(g_sampleLanguage));
if (TinselV1PSX) {
// Read the stream and create a XA ADPCM audio stream
Audio::AudioStream *xaStream = Audio::makeXAStream(_sampleStream.readStream(sampleLen), 44100);
// FIXME: Should set this in a different place ;)
_vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, _vm->_config->_soundVolume);
//_vm->_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, soundVolumeMusic);
_vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, _vm->_config->_voiceVolume);
// Play the audio stream
_vm->_mixer->playStream(type, &curChan.handle, xaStream);
} else if (TinselV1Saturn) {
// TODO: Sound format for the Saturn version - looks to be raw, but isn't
} else {
// allocate a buffer
byte *sampleBuf = (byte *)malloc(sampleLen);
assert(sampleBuf);
// read all of the sample
if (_sampleStream.read(sampleBuf, sampleLen) != sampleLen)
error(FILE_IS_CORRUPT, _vm->getSampleFile(g_sampleLanguage));
// FIXME: Should set this in a different place ;)
_vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, _vm->_config->_soundVolume);
//_vm->_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, soundVolumeMusic);
_vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, _vm->_config->_voiceVolume);
Audio::AudioStream *sampleStream = 0;
// play it
switch (_soundMode) {
case kMP3Mode:
#ifdef USE_MAD
{
Common::MemoryReadStream *compressedStream =
new Common::MemoryReadStream(sampleBuf, sampleLen, DisposeAfterUse::YES);
sampleStream = Audio::makeMP3Stream(compressedStream, DisposeAfterUse::YES);
}
#endif
break;
case kVorbisMode:
#ifdef USE_VORBIS
{
Common::MemoryReadStream *compressedStream =
new Common::MemoryReadStream(sampleBuf, sampleLen, DisposeAfterUse::YES);
sampleStream = Audio::makeVorbisStream(compressedStream, DisposeAfterUse::YES);
}
#endif
break;
case kFLACMode:
#ifdef USE_FLAC
{
Common::MemoryReadStream *compressedStream =
new Common::MemoryReadStream(sampleBuf, sampleLen, DisposeAfterUse::YES);
sampleStream = Audio::makeFLACStream(compressedStream, DisposeAfterUse::YES);
}
#endif
break;
default:
sampleStream = Audio::makeRawStream(sampleBuf, sampleLen, 22050, Audio::FLAG_UNSIGNED);
break;
}
if (sampleStream) {
_vm->_mixer->playStream(type, &curChan.handle, sampleStream);
}
}
if (handle)
*handle = curChan.handle;
return true;
}
void SoundManager::playDW1MacMusic(Common::File &s, uint32 length) {
// TODO: It's a bad idea to load the music track in a buffer.
// We should use a SubReadStream instead, and keep midi.dat open.
// However, the track lengths aren't that big (about 1-4MB),
// so this shouldn't be a major issue.
byte *soundData = (byte *)malloc(length);
assert(soundData);
// read all of the sample
if (s.read(soundData, length) != length)
error(FILE_IS_CORRUPT, MIDI_FILE);
Common::SeekableReadStream *memStream = new Common::MemoryReadStream(soundData, length);
Audio::SoundHandle *handle = &_channels[kChannelDW1MacMusic].handle;
// Stop any previously playing music track
_vm->_mixer->stopHandle(*handle);
// TODO: Compression support (MP3/OGG/FLAC) for midi.dat in DW1 Mac
Audio::RewindableAudioStream *musicStream = Audio::makeRawStream(memStream, 22050, Audio::FLAG_UNSIGNED, DisposeAfterUse::YES);
if (musicStream)
_vm->_mixer->playStream(Audio::Mixer::kMusicSoundType, handle, Audio::makeLoopingAudioStream(musicStream, 0));
}
// playSample for DiscWorld 2
bool SoundManager::playSample(int id, int sub, bool bLooped, int x, int y, int priority,
Audio::Mixer::SoundType type, Audio::SoundHandle *handle) {
// no sample driver?
if (!_vm->_mixer->isReady())
return false;
Channel *curChan;
uint8 sndVol = 255;
// Sample on screen?
if (!offscreenChecks(x, y))
return false;
// If that sample is already playing, stop it
stopSpecSample(id, sub);
if (type == Audio::Mixer::kSpeechSoundType) {
curChan = &_channels[kChannelTalk];
} else if (type == Audio::Mixer::kSFXSoundType) {
uint32 oldestTime = g_system->getMillis();
int oldestChan = kChannelSFX;
int chan;
for (chan = kChannelSFX; chan < kNumChannels; chan++) {
if (!_vm->_mixer->isSoundHandleActive(_channels[chan].handle))
break;
if ((_channels[chan].lastStart < oldestTime) &&
(_channels[chan].priority <= priority)) {
oldestTime = _channels[chan].lastStart;
oldestChan = chan;
}
}
if (chan == kNumChannels) {
if (_channels[oldestChan].priority > priority) {
warning("playSample: No free channel");
return false;
}
chan = oldestChan;
}
if (_vm->_pcmMusic->isDimmed() && SysVar(SYS_SceneFxDimFactor))
sndVol = 255 - 255/SysVar(SYS_SceneFxDimFactor);
curChan = &_channels[chan];
} else {
warning("playSample: Unknown SoundType");
return false;
}
// stop any currently playing sample
_vm->_mixer->stopHandle(curChan->handle);
// make sure id is in range
assert(id > 0 && id < _sampleIndexLen);
// get file offset for this sample
uint32 dwSampleIndex = _sampleIndex[id];
if (dwSampleIndex == 0) {
warning("Tinsel2 playSample, non-existent sample %d", id);
return false;
}
// move to correct position in the sample file
_sampleStream.seek(dwSampleIndex);
if (_sampleStream.eos() || _sampleStream.err() || (uint32)_sampleStream.pos() != dwSampleIndex)
error(FILE_IS_CORRUPT, _vm->getSampleFile(g_sampleLanguage));
// read the length of the sample
uint32 sampleLen = _sampleStream.readUint32LE();
if (_sampleStream.eos() || _sampleStream.err())
error(FILE_IS_CORRUPT, _vm->getSampleFile(g_sampleLanguage));
if (sampleLen & 0x80000000) {
// Has sub samples
int32 numSubs = sampleLen & ~0x80000000;
assert(sub >= 0 && sub < numSubs);
// Skipping
for (int32 i = 0; i < sub; i++) {
sampleLen = _sampleStream.readUint32LE();
_sampleStream.skip(sampleLen);
if (_sampleStream.eos() || _sampleStream.err())
error(FILE_IS_CORRUPT, _vm->getSampleFile(g_sampleLanguage));
}
sampleLen = _sampleStream.readUint32LE();
if (_sampleStream.eos() || _sampleStream.err())
error(FILE_IS_CORRUPT, _vm->getSampleFile(g_sampleLanguage));
}
debugC(DEBUG_DETAILED, kTinselDebugSound, "Playing sound %d.%d, %d bytes at %d (pan %d)", id, sub, sampleLen,
(int)_sampleStream.pos(), getPan(x));
// allocate a buffer
byte *sampleBuf = (byte *) malloc(sampleLen);
assert(sampleBuf);
// read all of the sample
if (_sampleStream.read(sampleBuf, sampleLen) != sampleLen)
error(FILE_IS_CORRUPT, _vm->getSampleFile(g_sampleLanguage));
Common::MemoryReadStream *compressedStream =
new Common::MemoryReadStream(sampleBuf, sampleLen, DisposeAfterUse::YES);
Audio::AudioStream *sampleStream = 0;
switch (_soundMode) {
case kMP3Mode:
#ifdef USE_MAD
sampleStream = Audio::makeMP3Stream(compressedStream, DisposeAfterUse::YES);
#endif
break;
case kVorbisMode:
#ifdef USE_VORBIS
sampleStream = Audio::makeVorbisStream(compressedStream, DisposeAfterUse::YES);
#endif
break;
case kFLACMode:
#ifdef USE_FLAC
sampleStream = Audio::makeFLACStream(compressedStream, DisposeAfterUse::YES);
#endif
break;
default:
sampleStream = new Tinsel6_ADPCMStream(compressedStream, DisposeAfterUse::YES, sampleLen, 22050, 1, 24);
break;
}
// FIXME: Should set this in a different place ;)
_vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, _vm->_config->_soundVolume);
//_vm->_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, soundVolumeMusic);
_vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, _vm->_config->_voiceVolume);
curChan->sampleNum = id;
curChan->subSample = sub;
curChan->looped = bLooped;
curChan->x = x;
curChan->y = y;
curChan->priority = priority;
curChan->lastStart = g_system->getMillis();
// /---Compression----\ Milis BytesPerSecond
// not needed and won't work when using MP3/OGG/FLAC anyway
//curChan->timeDuration = (((sampleLen * 64) / 25) * 1000) / (22050 * 2);
// Play it
_vm->_mixer->playStream(type, &curChan->handle, sampleStream);
_vm->_mixer->setChannelVolume(curChan->handle, sndVol);
_vm->_mixer->setChannelBalance(curChan->handle, getPan(x));
if (handle)
*handle = curChan->handle;
return true;
}
/**
* Returns FALSE if sample doesn't need playing
*/
bool SoundManager::offscreenChecks(int x, int &y) {
// No action if no x specification
if (x == -1)
return true;
// convert x to offset from screen center
x -= _vm->_bg->PlayfieldGetCenterX(FIELD_WORLD);
if (x < -SCREEN_WIDTH || x > SCREEN_WIDTH) {
// A long way offscreen, ignore it
return false;
} else if (x < -SCREEN_WIDTH/2 || x > SCREEN_WIDTH/2) {
// Off-screen, attennuate it
y = (y > 0) ? (y / 2) : 50;
return true;
} else
return true;
}
int8 SoundManager::getPan(int x) {
if (x == -1)
return 0;
x -= _vm->_bg->PlayfieldGetCenterX(FIELD_WORLD);
if (x == 0)
return 0;
if (x < 0) {
if (x < (-SCREEN_WIDTH / 2))
return -127;
x = (-x * 127) / (SCREEN_WIDTH / 2);
return 0 - x;
}
if (x > (SCREEN_WIDTH / 2))
return 127;
x = (x * 127) / (SCREEN_WIDTH / 2);
return x;
}
/**
* Returns TRUE if there is a sample for the specified sample identifier.
* @param id Identifier of sample to be checked
*/
bool SoundManager::sampleExists(int id) {
if (_vm->_mixer->isReady()) {
// make sure id is in range
if (id > 0 && id < _sampleIndexLen) {
// check for a sample index
if (_sampleIndex[id])
return true;
}
}
// no sample driver or no sample
return false;
}
/**
* Returns true if a sample is currently playing.
*/
bool SoundManager::sampleIsPlaying() {
if (TinselVersion <= 1)
return _vm->_mixer->isSoundHandleActive(_channels[kChannelTinsel1].handle);
for (int i = 0; i < kNumChannels; i++)
if (_vm->_mixer->isSoundHandleActive(_channels[i].handle))
return true;
return false;
}
/**
* Stops any currently playing sample.
*/
void SoundManager::stopAllSamples() {
if (TinselVersion <= 1) {
_vm->_mixer->stopHandle(_channels[kChannelTinsel1].handle);
return;
}
for (int i = 0; i < kNumChannels; i++)
_vm->_mixer->stopHandle(_channels[i].handle);
}
void SoundManager::stopSpecSample(int id, int sub) {
debugC(DEBUG_DETAILED, kTinselDebugSound, "stopSpecSample(%d, %d)", id, sub);
if (TinselVersion <= 1) {
if (_channels[kChannelTinsel1].sampleNum == id)
_vm->_mixer->stopHandle(_channels[kChannelTinsel1].handle);
return;
}
for (int i = kChannelTalk; i < kNumChannels; i++) {
if ((_channels[i].sampleNum == id) && (_channels[i].subSample == sub))
_vm->_mixer->stopHandle(_channels[i].handle);
}
}
void SoundManager::setSFXVolumes(uint8 volume) {
if (TinselVersion <= 1)
return;
for (int i = kChannelSFX; i < kNumChannels; i++)
_vm->_mixer->setChannelVolume(_channels[i].handle, volume);
}
void SoundManager::showSoundError(const char *errorMsg, const char *soundFile) {
Common::String msg;
msg = Common::String::format(errorMsg, soundFile);
GUI::MessageDialog dialog(msg.c_str());
dialog.runModal();
error("%s", msg.c_str());
}
/**
* Opens and inits all sound sample files.
*/
void SoundManager::openSampleFiles() {
// V1 Floppy and V0 demo versions have no sample files
if ((TinselVersion == 0) || ((TinselVersion == 1) && !_vm->isV1CD()))
return;
TinselFile f(TinselV1Saturn);
if (_sampleIndex)
// already allocated
return;
// Open sample index (*.idx) in binary mode
if (f.open(_vm->getSampleIndex(g_sampleLanguage))) {
uint32 fileSize = f.size();
_sampleIndex = (uint32 *)malloc(fileSize);
if (_sampleIndex == NULL) {
showSoundError(NO_MEM, _vm->getSampleIndex(g_sampleLanguage));
return;
}
_sampleIndexLen = fileSize / 4; // total sample of indices (DWORDs)
// Load data
for (int i = 0; i < _sampleIndexLen; ++i) {
_sampleIndex[i] = f.readUint32();
if (f.err()) {
showSoundError(FILE_READ_ERROR, _vm->getSampleIndex(g_sampleLanguage));
}
}
f.close();
// Detect format of soundfile by looking at 1st sample-index
switch (TO_BE_32(_sampleIndex[0])) {
case MKTAG('M','P','3',' '):
debugC(DEBUG_DETAILED, kTinselDebugSound, "Detected MP3 sound-data");
_soundMode = kMP3Mode;
break;
case MKTAG('O','G','G',' '):
debugC(DEBUG_DETAILED, kTinselDebugSound, "Detected OGG sound-data");
_soundMode = kVorbisMode;
break;
case MKTAG('F','L','A','C'):
debugC(DEBUG_DETAILED, kTinselDebugSound, "Detected FLAC sound-data");
_soundMode = kFLACMode;
break;
default:
debugC(DEBUG_DETAILED, kTinselDebugSound, "Detected original sound-data");
if (TinselVersion == 3) {
// And in Noir, the data is MP3
_soundMode = kMP3Mode;
}
break;
}
// Normally the 1st sample index points to nothing at all. We use it to
// determine if the game's sample files have been compressed, thus restore
// it here
_sampleIndex[0] = 0;
} else {
showSoundError(FILE_READ_ERROR, _vm->getSampleIndex(g_sampleLanguage));
}
// Open sample file (*.smp) in binary mode
if (!_sampleStream.open(_vm->getSampleFile(g_sampleLanguage))) {
showSoundError(FILE_READ_ERROR, _vm->getSampleFile(g_sampleLanguage));
}
}
void SoundManager::closeSampleStream() {
_sampleStream.close();
free(_sampleIndex);
_sampleIndex = 0;
_sampleIndexLen = 0;
}
} // End of namespace Tinsel

130
engines/tinsel/sound.h Normal file
View File

@@ -0,0 +1,130 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* This file contains the Sound Driver data structures etc.
*/
#ifndef TINSEL_SOUND_H
#define TINSEL_SOUND_H
#include "common/file.h"
#include "common/file.h"
#include "audio/mixer.h"
#include "tinsel/dw.h"
#include "tinsel/tinsel.h"
#include "tinsel/drives.h"
namespace Tinsel {
enum STYPE {FX, VOICE};
enum PlayPriority { PRIORITY_SCRIPT, PRIORITY_SPLAY1, PRIORITY_SPLAY2, PRIORITY_TALK };
enum SampleFlags {PS_COMPLETE = 0x01, PS_SUSTAIN = 0x02};
/*----------------------------------------------------------------------*\
|* Function Prototypes *|
\*----------------------------------------------------------------------*/
class SoundManager {
protected:
static const int kNumSFX = 3; // Number of SFX channels
enum {
kChannelTalk = 0,
kChannelTinsel1 = 0, // Always using this channel for DW1
kChannelSFX = 1,
kChannelDW1MacMusic = 2
};
static const int kNumChannels = kChannelSFX + kNumSFX;
enum SoundMode {
kVOCMode,
kMP3Mode,
kVorbisMode,
kFLACMode
};
struct Channel {
// Sample handle
Audio::SoundHandle handle;
// Sample id
int sampleNum;
int subSample;
// Playing properties
bool looped;
int x, y;
int priority;
// Time properties
uint32 timeStarted;
uint32 timeDuration;
uint32 lastStart;
};
Channel _channels[kNumChannels];
//TinselEngine *_vm; // TODO: Enable this once global _vm var is gone
/** Sample index buffer and number of entries */
uint32 *_sampleIndex;
/** Number of entries in the sample index */
int _sampleIndexLen;
/** Specifies if the sample-data is compressed and if yes, how */
SoundMode _soundMode;
/** file stream for sample file */
TinselFile _sampleStream;
bool offscreenChecks(int x, int &y);
int8 getPan(int x);
public:
SoundManager(TinselEngine *vm);
~SoundManager();
bool playSample(int id, Audio::Mixer::SoundType type, Audio::SoundHandle *handle = 0);
bool playSample(int id, int sub, bool bLooped, int x, int y, int priority,
Audio::Mixer::SoundType type, Audio::SoundHandle *handle = 0);
void playDW1MacMusic(Common::File &s, uint32 length);
void stopAllSamples(); // Stops any currently playing sample
void stopSpecSample(int id, int sub = 0); // Stops a specific sample
void setSFXVolumes(uint8 volume);
bool sampleExists(int id);
bool sampleIsPlaying();
void openSampleFiles();
void closeSampleStream();
private:
void showSoundError(const char *errorMsg, const char *soundFile);
};
} // End of namespace Tinsel
#endif // TINSEL_SOUND_H

403
engines/tinsel/strres.cpp Normal file
View File

@@ -0,0 +1,403 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* String resource management routines
*/
#include "tinsel/dw.h"
#include "tinsel/drives.h"
#include "tinsel/sound.h"
#include "tinsel/strres.h"
#include "tinsel/scn.h"
#include "common/file.h"
#include "common/endian.h"
#include "common/textconsole.h"
#include "gui/message.h"
namespace Tinsel {
// These vars are reset upon engine destruction
#ifdef DEBUG
// Diagnostic number
int g_newestString;
#endif
// buffer for resource strings
static uint8 *g_textBuffer = nullptr;
static struct {
bool bPresent;
const char *szStem;
SCNHANDLE hDescription;
SCNHANDLE hFlagFilm;
} g_languages[NUM_LANGUAGES] = {
{ false, "English", 0, 0 },
{ false, "French", 0, 0 },
{ false, "German", 0, 0 },
{ false, "Italian", 0, 0 },
{ false, "Spanish", 0, 0 },
{ false, "Hebrew", 0, 0 },
{ false, "Magyar", 0, 0 },
{ false, "Japanese",0, 0 },
{ false, "US", 0, 0 }
};
// Set if we're handling 2-byte characters.
bool g_bMultiByte = false;
LANGUAGE g_textLanguage, g_sampleLanguage = TXT_ENGLISH;
//----------------- FUNCTIONS --------------------------------
void ResetVarsStrRes() {
g_textBuffer = nullptr;
g_bMultiByte = false;
g_textLanguage = g_sampleLanguage = TXT_ENGLISH;
}
/**
* Called to load a resource file for a different language
* @param newLang The new language
*/
void ChangeLanguage(LANGUAGE newLang) {
TinselFile f(TinselV1Mac || TinselV1Saturn);
uint32 textLen = 0; // length of buffer
g_textLanguage = newLang;
g_sampleLanguage = newLang;
// free the previous buffer
free(g_textBuffer);
g_textBuffer = nullptr;
// Try and open the specified language file. If it fails, and the language
// isn't English, try falling back on opening 'english.txt' - some foreign
// language versions reused it rather than their proper filename
if (!f.open(_vm->getTextFile(newLang))) {
if ((newLang == TXT_ENGLISH) || !f.open(_vm->getTextFile(TXT_ENGLISH))) {
char buf[50];
Common::sprintf_s(buf, CANNOT_FIND_FILE, _vm->getTextFile(newLang));
GUI::MessageDialog dialog(buf);
dialog.runModal();
error(CANNOT_FIND_FILE, _vm->getTextFile(newLang));
}
}
// Check whether the file is compressed or not - for compressed files the
// first long is the filelength and for uncompressed files it is the chunk
// identifier
textLen = f.readUint32();
if (f.eos() || f.err())
error(FILE_IS_CORRUPT, _vm->getTextFile(newLang));
if (textLen == CHUNK_STRING || textLen == CHUNK_MBSTRING) {
// the file is uncompressed
g_bMultiByte = (textLen == CHUNK_MBSTRING);
// get length of uncompressed file
textLen = f.size();
f.seek(0, SEEK_SET); // Set to beginning of file
if (g_textBuffer == NULL) {
// allocate a text buffer for the strings
g_textBuffer = (uint8 *)malloc(textLen);
// make sure memory allocated
assert(g_textBuffer);
}
// load data
if (f.read(g_textBuffer, textLen) != textLen)
// file must be corrupt if we get to here
error(FILE_IS_CORRUPT, _vm->getTextFile(newLang));
// close the file
f.close();
} else {
// the file must be compressed
error("Compression handling for text file has been removed");
}
}
/**
* FindStringBase
*/
static byte *FindStringBase(int id) {
// base of string resource table
byte *pText = g_textBuffer;
// For Tinsel 0, Ids are decremented by 1
if (TinselVersion == 0)
--id;
// index into text resource file
uint32 index = 0;
// number of chunks to skip
int chunkSkip = id / STRINGS_PER_CHUNK;
// number of strings to skip when in the correct chunk
int strSkip = id % STRINGS_PER_CHUNK;
// skip to the correct chunk
while (chunkSkip-- != 0) {
// make sure chunk id is correct
assert(READ_32(pText + index) == CHUNK_STRING || READ_32(pText + index) == CHUNK_MBSTRING);
if (READ_32(pText + index + sizeof(uint32)) == 0) {
// string does not exist
return NULL;
}
// get index to next chunk
index = READ_32(pText + index + sizeof(uint32));
}
// skip over chunk id and offset
index += (2 * sizeof(uint32));
// pointer to strings
pText = pText + index;
// skip to the correct string
while (strSkip-- != 0) {
// skip to next string
if ((TinselVersion <= 1) || ((*pText & 0x80) == 0)) {
// Tinsel 1, or string of length < 128
pText += *pText + 1;
} else if (*pText == 0x80) {
// string of length 128 - 255
pText++; // skip control byte
pText += *pText + 1;
} else if (*pText == 0x90) {
// string of length 256 - 511
pText++; // skip control byte
pText += *pText + 1 + 256;
} else { // multiple string
int subCount;
subCount = *pText & ~0x80;
pText++; // skip control byte
// skip prior sub-strings
while (subCount--) {
// skip control byte, if there is one
if (*pText == 0x80) {
pText++;
pText += *pText + 1;
} else if (*pText == 0x90) {
pText++;
pText += *pText + 1 + 256;
} else
pText += *pText + 1;
}
}
}
return pText;
}
/**
* Loads a string resource identified by id.
* @param id identifier of string to be loaded
* @param pBuffer points to buffer that receives the string
* @param bufferMax maximum number of chars to be copied to the buffer
*/
int LoadStringResource(int id, int sub, char *pBuffer, int bufferMax) {
int len; // length of string
byte *pText = FindStringBase(id);
if (pText == NULL) {
Common::strcpy_s(pBuffer, bufferMax, "!! HIGH STRING !!");
return 0;
}
if ((TinselVersion <= 1) || ((*pText & 0x80) == 0)) {
// get length of string
len = *pText;
} else if (*pText == 0x80) {
// string of length 128 - 255
pText++; // skip control byte
// get length of string
len = *pText;
} else if (*pText == 0x90) {
// string of length 128 - 255
pText++; // skip control byte
// get length of string
len = *pText + 256;
} else {
// multiple string
pText++; // skip control byte
// skip prior sub-strings
while (sub--) {
// skip control byte, if there is one
if (*pText == 0x80) {
pText++;
pText += *pText + 1;
} else if (*pText == 0x90) {
pText++;
pText += *pText + 1 + 256;
} else
pText += *pText + 1;
}
// skip control byte, if there is one
if (*pText == 0x80) {
pText++;
// get length of string
len = *pText;
} else if (*pText == 0x90) {
pText++;
// get length of string
len = *pText + 256;
} else {
// get length of string
len = *pText;
}
}
if (len) {
// the string exists
// copy the string to the buffer
if (len < bufferMax) {
memcpy(pBuffer, pText + 1, len);
// null terminate
pBuffer[len] = 0;
// number of chars copied
return len + 1;
} else {
memcpy(pBuffer, pText + 1, bufferMax - 1);
// null terminate
pBuffer[bufferMax - 1] = 0;
// number of chars copied
return bufferMax;
}
}
// TEMPORARY DIRTY BODGE
Common::strcpy_s(pBuffer, bufferMax, "!! NULL STRING !!");
// string does not exist
return 0;
}
int LoadStringRes(int id, char *pBuffer, int bufferMax) {
return LoadStringResource(id, 0, pBuffer, bufferMax);
}
/**
* Loads a string resource identified by id
* @param id identifier of string to be loaded
* @param sub sub-string number
* @param pBuffer points to buffer that receives the string
* @param bufferMax maximum number of chars to be copied to the buffer
*/
int LoadSubString(int id, int sub, char *pBuffer, int bufferMax) {
return LoadStringResource(id, sub, pBuffer, bufferMax);
}
/**
* SubStringCount
* @param id Identifier of string to be tested
*/
int SubStringCount(int id) {
byte *pText;
pText = FindStringBase(id);
if (pText == NULL)
return 0;
if ((*pText & 0x80) == 0 || *pText == 0x80 || *pText == 0x90) {
// string of length < 128 or string of length 128 - 255
// or of length 256 - 511
return 1;
} else
return (*pText & ~0x80);
}
void FreeTextBuffer() {
free(g_textBuffer);
g_textBuffer = nullptr;
}
/**
* Called from TINLIB.C from DeclareLanguage().
*/
void LanguageFacts(int language, SCNHANDLE hDescription, SCNHANDLE hFlagFilm) {
assert(language >= 0 && language < NUM_LANGUAGES);
g_languages[language].hDescription = hDescription;
g_languages[language].hFlagFilm = hFlagFilm;
}
/**
* Gets the current subtitles language
*/
LANGUAGE TextLanguage() {
return g_textLanguage;
}
/**
* Gets the current voice language
*/
LANGUAGE SampleLanguage() {
return g_sampleLanguage;
}
int NumberOfLanguages() {
int i, count;
for (i = 0, count = 0; i < NUM_LANGUAGES; i++) {
if (g_languages[i].bPresent)
count++;
}
return count;
}
SCNHANDLE LanguageDesc(LANGUAGE thisOne) {
return g_languages[thisOne].hDescription;
}
SCNHANDLE LanguageFlag(LANGUAGE thisOne) {
return g_languages[thisOne].hFlagFilm;
}
} // End of namespace Tinsel

95
engines/tinsel/strres.h Normal file
View File

@@ -0,0 +1,95 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* String resource management routines
*/
#ifndef TINSEL_STRRES_H
#define TINSEL_STRRES_H
#include "common/scummsys.h"
#include "tinsel/dw.h"
namespace Tinsel {
#define STRINGS_PER_CHUNK 64 // number of strings per chunk in the language text files
#define FIRST_STR_ID 1 // id number of first string in string table
#define MAX_STRING_SIZE 255 // maximum size of a string in the resource table
#define MAX_STRRES_SIZE 300000 // maximum size of string resource file
// Set if we're handling 2-byte characters.
extern bool g_bMultiByte;
/*----------------------------------------------------------------------*\
|* Function Prototypes *|
\*----------------------------------------------------------------------*/
/**
* Called to load a resource file for a different language
* @param newLang The new language
*/
void ChangeLanguage(LANGUAGE newLang);
/**
* Loads a string resource identified by id.
* @param id identifier of string to be loaded
* @param pBuffer points to buffer that receives the string
* @param bufferMax maximum number of chars to be copied to the buffer
*/
int LoadStringRes(int id, char *pBuffer, int bufferMax);
/**
* Loads a string resource identified by id
* @param id identifier of string to be loaded
* @param sub sub-string number
* @param pBuffer points to buffer that receives the string
* @param bufferMax maximum number of chars to be copied to the buffer
*/
int LoadSubString(int id, int sub, char *pBuffer, int bufferMax);
int SubStringCount(int id); // identifier of string to be tested
/**
* Frees the text buffer allocated from ChangeLanguage()
*/
void FreeTextBuffer();
/**
* Called from TINLIB.C from DeclareLanguage().
*/
void LanguageFacts(int language, SCNHANDLE hDescription, SCNHANDLE hFlagFilm);
/**
* Gets the current subtitles language
*/
LANGUAGE TextLanguage();
/**
* Gets the current voice language
*/
LANGUAGE SampleLanguage();
int NumberOfLanguages();
SCNHANDLE LanguageDesc(LANGUAGE thisOne);
SCNHANDLE LanguageFlag(LANGUAGE thisOne);
} // End of namespace Tinsel
#endif

214
engines/tinsel/sysvar.cpp Normal file
View File

@@ -0,0 +1,214 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* System variable handling.
*/
#include "tinsel/dw.h"
#include "tinsel/graphics.h"
#include "tinsel/dialogs.h"
#include "tinsel/strres.h"
#include "tinsel/sysvar.h"
#include "tinsel/tinsel.h"
#include "common/textconsole.h"
namespace Tinsel {
// Return for SYS_Platform
typedef enum { DOS_PC, WIN_PC, APPLE_MAC, SONY_PSX, SEGA_SATURN } platform;
//----------------- GLOBAL GLOBAL DATA --------------------
extern int NewestSavedGame();
//----------------- LOCAL GLOBAL DATA --------------------
// These vars are reset upon engine destruction
static int g_systemVars[SV_TOPVALID_T3];
static SCNHANDLE g_systemStrings[SS_MAX_VALID];
//----------------- FUNCTIONS --------------------------------
void ResetVarsSysVar() {
memset(g_systemVars, 0, sizeof(g_systemVars));
memset(g_systemStrings, 0, sizeof(g_systemStrings));
}
/**
* Initializes the system variable list
*/
void InitSysVars() {
int initialSystemVars[SV_TOPVALID_T3] = {
INV_1, // Default inventory
10, // Y-offset of Conversation(TOP)
320, // Y-offset of Conversation(BOT)
15, // Minimum distance from side
10, // Minimum distance from top
115, // Distance above actor
10, // Distance below actor
0, // Current language **READ ONLY**
0, // Sample language **READ ONLY**
0, // Current state **READ ONLY**
0, // Saved Game Exists **READ ONLY**
true, // Should Conversation() wait for scroll? [TRUE]
true, // Should Talk()/Say() wait for scroll? [TRUE]
true, // Enable PointTag()
true, // Enable cursor with PrintCursor()
100, // SV_SCROLL_XTRIGGER
0, // SV_SCROLL_XDISTANCE
16, // SV_SCROLL_XSPEED
40, // SV_SCROLL_YTRIGGERTOP
40, // SV_SCROLL_YTRIGGERBOT
0, // SV_SCROLL_YDISTANCE
16, // SV_SCROLL_YSPEED
2, // Speech Delay
2, // Music dim factor
0, // if set, default actor's text color gets poked in here
0, // user 1
0, // user 2
0, // user 3
0, // user 4
0, // user 5
0, // user 6
0, // SYS_MinimumXoffset
0, // SYS_MaximumXoffset
0, // SYS_MinimumYoffset
0, // SYS_MaximumYoffset
0, // SYS_DefaultFxDimFactor
0, // SYS_SceneFxDimFactor
0x606060, // SYS_HighlightRGB
WIN_PC, // SYS_Platform,
0, // SYS_Debug
0, // ISV_DIVERT_ACTOR
false, // ISV_NO_BLOCKING
0, // ISV_GHOST_ACTOR
0, // ISV_GHOST_BASE
0 // ISV_GHOST_COLOR
};
memcpy(g_systemVars, initialSystemVars, sizeof(g_systemVars));
g_systemVars[SV_SCROLL_XDISTANCE] = SCREEN_WIDTH / 2;
g_systemVars[SV_SCROLL_YDISTANCE] = SCREEN_BOX_HEIGHT1 / 2;
}
/**
* SetSysVar
*/
void SetSysVar(int varId, int newValue) {
if (varId < 0 || varId >= SV_TOPVALID)
error("SetSystemVar(): out of range identifier");
switch (varId) {
case SV_LANGUAGE:
case SV_SAMPLE_LANGUAGE:
case SV_SUBTITLES:
case SV_SAVED_GAME_EXISTS:
case SYS_Platform:
case SYS_Debug:
error("SetSystemVar(): read only identifier");
default:
g_systemVars[varId] = newValue;
}
}
int SysVar(int varId) {
if (varId < 0 || varId >= SV_TOPVALID)
error("SystemVar(): out of range identifier");
switch (varId) {
case SV_LANGUAGE:
return TextLanguage();
case SV_SAMPLE_LANGUAGE:
return SampleLanguage();
case SV_SUBTITLES:
// FIXME: This isn't currently defined
return false;
//return _vm->_config->_useSubtitles;
case SV_SAVED_GAME_EXISTS:
return NewestSavedGame() != -1;
case SYS_Debug:
// FIXME: This isn't currently defined
return false;
//return bDebuggingAllowed;
default:
return g_systemVars[varId];
}
}
void SaveSysVars(int *pSv) {
memcpy(pSv, g_systemVars, sizeof(g_systemVars));
}
void RestoreSysVars(int *pSv) {
memcpy(g_systemVars, pSv, sizeof(g_systemVars));
}
void SetSysString(int number, SCNHANDLE hString) {
assert(number >= 0 && number < SS_MAX_VALID);
g_systemStrings[number] = hString;
}
SCNHANDLE SysString(int number) {
assert(number >= 0 && number < SS_MAX_VALID);
return g_systemStrings[number];
}
/**
* Gets the no blocking flag. Note that for convenience, the systemVars array
* entry is used even for Tinsel 1, which originally used a separate variable.
*/
bool GetNoBlocking() {
return SysVar(ISV_NO_BLOCKING);
}
/**
* Sets the no blocking flag. Note that for convenience, the systemVars array
* entry is used even for Tinsel 1, which originally used a separate variable.
*/
void SetNoBlocking(bool flag) {
SetSysVar(ISV_NO_BLOCKING, flag);
}
} // End of namespace Tinsel

168
engines/tinsel/sysvar.h Normal file
View File

@@ -0,0 +1,168 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* System variable handling.
*/
#ifndef TINSEL_SYSVAR_H // prevent multiple includes
#define TINSEL_SYSVAR_H
namespace Tinsel {
typedef enum { SV_DEFAULT_INV,
SV_CONV_TOPY, // Y-offset of Conversation(TOP)
SV_CONV_BOTY, // Y-offset of Conversation(BOT)
SV_CONV_MINX, // Minimum distance from side
SV_CONV_MINY, // Minimum distance from top
SV_CONV_ABOVE_Y, // Distance above actor
SV_CONV_BELOW_Y, // Distance below actor
SV_LANGUAGE,
SV_SAMPLE_LANGUAGE,
SV_SUBTITLES,
SV_SAVED_GAME_EXISTS,
SV_CONVERSATIONWAITS, // } Do they wait for
SV_SPEECHWAITS, // } scrolls to complete?
SV_ENABLEPOINTTAG, // Enable PointTag()
SV_ENABLEPRINTCURSOR, // Enable cursor with PrintCursor()
SV_SCROLL_XTRIGGER, // }
SV_SCROLL_XDISTANCE, // }
SV_SCROLL_XSPEED, // } Scroll parameters!
SV_SCROLL_YTRIGGERTOP, // }
SV_SCROLL_YTRIGGERBOT, // }
SV_SCROLL_YDISTANCE, // }
SV_SCROLL_YSPEED, // }
SV_SPEECHDELAY, // Delay 'twixt text/animation and sample
SV_MUSICDIMFACTOR, // dimVolume = volume - volume/SV_MDF
SV_TAGCOLOR, // if set, default actor's text color gets poked in here
SV_USER1,
SV_USER2,
SV_USER3,
SV_USER4,
SV_USER5,
SV_USER6,
SV_MinimumXoffset,
SV_MaximumXoffset,
SV_MinimumYoffset,
SV_MaximumYoffset,
// dimVolume = volume - volume/DF
SYS_DefaultFxDimFactor, // To this at start of scene
SYS_SceneFxDimFactor, // Alter within scene
SYS_HighlightRGB,
SYS_Platform, // Hardware platform **READ ONLY**
SYS_Debug, // TRUE for debug build/'cheat'**READ ONLY**
ISV_DIVERT_ACTOR_T2 = 0x28,
ISV_NO_BLOCKING_T2 = 0x29,
ISV_GHOST_ACTOR_T2 = 0x2A,
ISV_GHOST_BASE_T2 = 0x2B,
ISV_GHOST_COLOR_T2 = 0x2C,
SV_TOPVALID_T2 = 0x2D,
SV_SPRITER_SCENE_ID = 0x2F, // Noir, loaded scene
ISV_DIVERT_ACTOR_T3 = 0x32,
ISV_NO_BLOCKING_T3 = 0x33,
ISV_GHOST_ACTOR_T3 = 0x34,
ISV_GHOST_BASE_T3 = 0x35,
ISV_GHOST_COLOR_T3 = 0x36,
SV_SPRITER_SCALE = 0x37, // Noir, scale used for 3D rendering
SV_SPRITER_OVERLAY = 0x38, // Noir, if additional model is loaded
SV_TOPVALID_T3 } SYSVARS;
#define ISV_DIVERT_ACTOR ((TinselVersion == 3) ? ISV_DIVERT_ACTOR_T3 : ISV_DIVERT_ACTOR_T2)
#define ISV_NO_BLOCKING ((TinselVersion == 3) ? ISV_NO_BLOCKING_T3 : ISV_NO_BLOCKING_T2)
#define ISV_GHOST_ACTOR ((TinselVersion == 3) ? ISV_GHOST_ACTOR_T3 : ISV_GHOST_ACTOR_T2)
#define ISV_GHOST_BASE ((TinselVersion == 3) ? ISV_GHOST_BASE_T3 : ISV_GHOST_BASE_T2)
#define ISV_GHOST_COLOR ((TinselVersion == 3) ? ISV_GHOST_COLOR_T3 : ISV_GHOST_COLOR_T2)
#define SV_TOPVALID ((TinselVersion == 3) ? SV_TOPVALID_T3 : SV_TOPVALID_T2)
typedef enum {
// Main Menu
SS_LOAD_OPTION, //
SS_SAVE_OPTION, //
SS_RESTART_OPTION, //
SS_SOUND_OPTION, //
SS_CONTROL_OPTION, //
SS_SUBTITLES_OPTION, //
SS_QUIT_OPTION, //
SS_RESUME_OPTION, //
SS_LOAD_HEADING,
SS_SAVE_HEADING,
SS_RESTART_HEADING,
SS_QUIT_HEADING,
SS_MVOL_SLIDER,
SS_SVOL_SLIDER,
SS_VVOL_SLIDER,
SS_DCLICK_SLIDER,
SS_DCLICK_TEST,
SS_SWAP_TOGGLE,
SS_TSPEED_SLIDER,
SS_STITLE_TOGGLE,
SS_HOPPER1, // Hopper scene menu heading
SS_SOUND_HEADING,
SS_CONTROLS_HEADING,
SS_LANGUAGE_SELECT,
// Noir only:
SS_NOIR1,
SS_NOIR2,
SS_NOIR3,
SS_NOIR4,
SS_MAX_VALID
} BOLLOX;
void InitSysVars();
void SetSysVar(int varId, int newValue);
int SysVar(int varId);
void SaveSysVars(int *pSv);
void RestoreSysVars(int *pSv);
void SetSysString(int number, SCNHANDLE hString);
SCNHANDLE SysString(int number);
bool GetNoBlocking();
void SetNoBlocking(bool flag);
} // End of namespace Tinsel
#endif

292
engines/tinsel/text.cpp Normal file
View File

@@ -0,0 +1,292 @@
/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
* Text utilities.
*/
#include "tinsel/dw.h"
#include "tinsel/graphics.h" // object plotting
#include "tinsel/handle.h"
#include "tinsel/sched.h" // process scheduler defines
#include "tinsel/strres.h" // g_bMultiByte
#include "tinsel/text.h" // text defines
namespace Tinsel {
//----------------- LOCAL GLOBAL DATA --------------------
/** TinselV3, base color for the text color replacement */
static uint32 g_t3fontBaseColor;
/**
* Returns the length of one line of a string in pixels.
* @param szStr String
* @param pFont Which font to use for dimensions
*/
int StringLengthPix(char *szStr, const FONT *pFont) {
int strLen; // accumulated length of string
byte c;
SCNHANDLE hImg;
// while not end of string or end of line
for (strLen = 0; (c = *szStr) != EOS_CHAR && c != LF_CHAR; szStr++) {
if (g_bMultiByte) {
if (c & 0x80)
c = ((c & ~0x80) << 8) + *++szStr;
}
hImg = pFont->fontDef[c];
if (hImg) {
// there is a IMAGE for this character
const IMAGE *pChar = _vm->_handle->GetImage(hImg);
// add width of font bitmap
strLen += pChar->imgWidth;
delete pChar;
} else
// use width of space character
strLen += pFont->spaceSize;
// finally add the inter-character spacing
strLen += pFont->xSpacing;
}
// return length of line in pixels - minus inter-char spacing for last character
strLen -= pFont->xSpacing;
return (strLen > 0) ? strLen : 0;
}
/**
* Returns the justified x start position of a line of text.
* @param szStr String to output
* @param xPos X position of string
* @param pFont Which font to use
* @param mode Mode flags for the string
*/
int JustifyText(char *szStr, int xPos, const FONT *pFont, int mode) {
if (mode & TXT_CENTER) {
// center justify the text
// adjust x positioning by half the length of line in pixels
xPos -= StringLengthPix(szStr, pFont) / 2;
} else if (mode & TXT_RIGHT) {
// right justify the text
// adjust x positioning by length of line in pixels
xPos -= StringLengthPix(szStr, pFont);
}
// return text line x start position
return xPos;
}
/**
* Main text outputting routine. If a object list is specified a
* multi-object is created for the whole text and a pointer to the head
* of the list is returned.
* @param pList Object list to add text to
* @param szStr String to output
* @param color Color for monochrome text
* @param xPos X position of string
* @param yPos Y position of string
* @param hFont Which font to use
* @param mode Mode flags for the string
* @param sleepTime Sleep time between each character (if non-zero)
*/
OBJECT *ObjectTextOut(OBJECT **pList, char *szStr, int color,
int xPos, int yPos, SCNHANDLE hFont, int mode, int sleepTime) {
int xJustify; // x position of text after justification
int yOffset; // offset to next line of text
OBJECT *pFirst; // head of multi-object text list
OBJECT *pChar = 0; // object ptr for the character
byte c;
SCNHANDLE hImg;
// make sure there is a linked list to add text to
assert(pList);
// get font pointer
FONT *pFont = _vm->_handle->GetFont(hFont);
const OBJ_INIT *pFontInit = &pFont->fontInit;
// init head of text list
pFirst = nullptr;
// get image for capital W
SCNHANDLE imgHandle = pFont->fontDef[(int)'W'];
assert(imgHandle);
// get height of capital W for offset to next line
const IMAGE *pImgCapitalW = _vm->_handle->GetImage(imgHandle);
yOffset = pImgCapitalW->imgHeight & ~C16_FLAG_MASK;
delete pImgCapitalW;
while (*szStr) {
// x justify the text according to the mode flags
xJustify = JustifyText(szStr, xPos, pFont, mode);
// repeat until end of string or end of line
while ((c = *szStr) != EOS_CHAR && c != LF_CHAR) {
if (g_bMultiByte) {
if (c & 0x80)
c = ((c & ~0x80) << 8) + *++szStr;
}
hImg = pFont->fontDef[c];
if (hImg == 0) {
// no image for this character
// add font spacing for a space character
xJustify += pFont->spaceSize;
} else { // printable character
int aniX, aniY; // char image animation offsets
// allocate and init a character object
if (pFirst == NULL)
// first time - init head of list
pFirst = pChar = InitObject(pFontInit);
else
// chain to multi-char list
pChar = pChar->pSlave = InitObject(pFontInit);
// convert image handle to pointer
const IMAGE *pImg = _vm->_handle->GetImage(hImg);
// fill in character object
pChar->hImg = hImg; // image def
pChar->width = pImg->imgWidth; // width of chars bitmap
pChar->height = pImg->imgHeight & ~C16_FLAG_MASK; // height of chars bitmap
pChar->hBits = pImg->hImgBits; // bitmap
// check for absolute positioning
if (mode & TXT_ABSOLUTE)
pChar->flags |= DMA_ABS;
// set characters color - only effective for mono fonts
pChar->constant = color;
// set the base font color to be replaced with supplied color, only for Tinsel V3
g_t3fontBaseColor = (TinselVersion == 3) ? pFont->baseColor : 0;
// get Y animation offset
GetAniOffset(hImg, pChar->flags, &aniX, &aniY);
// set x position - ignore animation point
pChar->xPos = intToFrac(xJustify);
// set y position - adjust for animation point
pChar->yPos = intToFrac(yPos - aniY);
if (mode & TXT_SHADOW) {
// we want to shadow the character
OBJECT *pShad;
// allocate a object for the shadow and chain to multi-char list
pShad = pChar->pSlave = AllocObject();
// copy the character for a shadow
CopyObject(pShad, pChar);
// add shadow offsets to characters position
pShad->xPos += intToFrac(pFont->xShadow);
pShad->yPos += intToFrac(pFont->yShadow);
// shadow is behind the character
pShad->zPos--;
// shadow is always mono
pShad->flags = DMA_CNZ | DMA_CHANGED;
// check for absolute positioning
if (mode & TXT_ABSOLUTE)
pShad->flags |= DMA_ABS;
// shadow always uses first palette entry
// should really alloc a palette here also ????
pShad->constant = 1;
// add shadow to object list
InsertObject(pList, pShad);
}
// add character to object list
InsertObject(pList, pChar);
// move to end of list
if (pChar->pSlave)
pChar = pChar->pSlave;
// add character spacing
xJustify += pImg->imgWidth;
delete pImg;
}
// finally add the inter-character spacing
xJustify += pFont->xSpacing;
// next character in string
++szStr;
}
// adjust the text y position and add the inter-line spacing
yPos += yOffset + pFont->ySpacing;
// check for newline
if (c == LF_CHAR)
// next character in string
++szStr;
}
delete pFont;
// return head of list
return pFirst;
}
/**
* Is there an image for this character in this font?
* @param hFont which font to use
* @param c character to test
*/
bool IsCharImage(SCNHANDLE hFont, char c) {
byte c2 = (byte)c;
// Inventory save game name editor needs to be more clever for
// multi-byte characters. This bodge will stop it erring.
if (g_bMultiByte && (c2 & 0x80))
return false;
// get font pointer
FONT *pFont = _vm->_handle->GetFont(hFont);
bool result = pFont->fontDef[c2] != 0;
delete pFont;
return result;
}
uint32 t3GetBaseColor()
{
return g_t3fontBaseColor;
}
} // End of namespace Tinsel

Some files were not shown because too many files have changed in this diff Show More