Initial import of the work in progress M4 engine

svn-id: r31600
This commit is contained in:
Filippos Karapetis 2008-04-20 14:47:37 +00:00
parent d0590a09ea
commit 7ca439f410
60 changed files with 20007 additions and 0 deletions

202
engines/m4/actor.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 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#include "common/system.h"
#include "common/array.h"
#include "m4/actor.h"
#include "m4/m4_views.h"
#include "m4/assets.h"
namespace M4 {
#define WALKER_BURGER "Wilbur0%i" // wilbur, with a number denoting his current direction
Actor::Actor(M4Engine *vm) : _vm(vm) {
_scaling = 100;
_direction = 5;
_walkerSprites.resize(10);
loadWalkers();
}
Actor::~Actor() {
unloadWalkers();
}
int Actor::getWalkerWidth() { return _walkerSprites[kFacingSouth]->getFrame(0)->w; }
int Actor::getWalkerHeight() { return _walkerSprites[kFacingSouth]->getFrame(0)->h; }
void Actor::placeWalkerSpriteAt(int spriteNum, int x, int y) {
if (_direction < 1 || _direction > 9) {
warning("Direction is %i, fixing", _direction);
_direction = 1; // TODO: this is a temporary fix
}
SpriteInfo info;
info.sprite = _walkerSprites[_direction]->getFrame(spriteNum);
info.hotX = info.hotY = 0;
info.width = info.sprite->w;
info.height = info.sprite->h;
info.scaleX = info.scaleY = _scaling;
info.palette = _walkerSprites[_direction]->getPalette();
info.inverseColorTable = _vm->_scene->getInverseColorTable();
_vm->_scene->drawSprite(x, y, info, Common::Rect(640, 400));
}
void Actor::loadWalkers() {
for (uint8 i = 1; i < 10; i++) {
if (i == 6)
continue; // walker sprite 6 is unused
loadWalkerDirection(i);
}
}
void Actor::loadWalkerDirection(uint8 direction) {
char name[20];
Common::SeekableReadStream *walkerS;
if (_vm->getGameType() == GType_Burger) {
sprintf(name, WALKER_BURGER, direction);
} else {
//warning("Actor::loadWalkerDirection: unspecified walker type, not loading walker");
// TODO: Master Lu walkers
return;
}
walkerS = _vm->res()->get(name);
_walkerSprites.insert_at(direction, new SpriteAsset(_vm, walkerS, walkerS->size(), name));
_vm->res()->toss(name);
}
void Actor::unloadWalkers() {
for (uint8 i = 9; i > 0; i--) {
if (i == 6)
continue; // walker sprite 6 is unused
SpriteAsset *tempSprite = _walkerSprites[i];
_walkerSprites.remove_at(i);
if (tempSprite)
delete tempSprite;
}
}
void Actor::setWalkerPalette() {
_vm->_palette->setPalette(_walkerSprites[kFacingSouthEast]->getPalette(), 0,
_walkerSprites[kFacingSouthEast]->getColorCount());
}
Inventory::Inventory(M4Engine *vm) : _vm(vm) {
}
Inventory::~Inventory() {
_inventory.clear();
}
void Inventory::registerObject(char* name, int32 scene, int32 icon) {
InventoryObject *newObject = new InventoryObject();
int newObjectIndex = 0;
// Capitalize registered inventory object names
str_upper(name);
newObject->name = strdup(name);
newObject->scene = scene;
newObject->icon = icon;
newObjectIndex = _inventory.size();
_inventory.push_back(newObject);
if (scene == BACKPACK)
addToBackpack(newObjectIndex);
}
void Inventory::moveObject(char* name, int32 scene) {
uint i = 0;
for (i = 0; i < _inventory.size(); i++) {
if (!scumm_stricmp(_inventory[i]->name, name)) {
if (_inventory[i]->scene == BACKPACK && scene != BACKPACK)
removeFromBackpack(i);
_inventory[i]->scene = scene;
if (scene == BACKPACK)
addToBackpack(i);
return;
}
}
}
void Inventory::addToBackpack(uint32 objectIndex) {
_vm->_interfaceView->inventoryAdd(_inventory[objectIndex]->name, "", _inventory[objectIndex]->icon);
}
void Inventory::removeFromBackpack(uint32 objectIndex) {
_vm->_interfaceView->inventoryRemove(_inventory[objectIndex]->name);
}
bool Inventory::isInCurrentScene(char* name) {
return (getScene(name) == _vm->_scene->getCurrentScene());
}
int Inventory::getScene(char* name) {
uint i = 0;
for (i = 0; i < _inventory.size(); i++) {
if (!scumm_stricmp(_inventory[i]->name, name))
return _inventory[i]->scene;
}
return UNKNOWN_OBJECT;
}
int Inventory::getIcon(char* name) {
uint i = 0;
for (i = 0; i < _inventory.size(); i++) {
if (!scumm_stricmp(_inventory[i]->name, name))
return _inventory[i]->icon;
}
return UNKNOWN_OBJECT;
}
int Inventory::getIndex(char* name) {
uint i = 0;
for (i = 0; i < _inventory.size(); i++) {
if (!scumm_stricmp(_inventory[i]->name, name))
return i;
}
return UNKNOWN_OBJECT;
}
void Inventory::clear() {
for (uint i = 0; i < _inventory.size(); i++) {
delete _inventory[i]->name;
delete _inventory[i];
_inventory.remove_at(i);
}
}
} // End of namespace M4

117
engines/m4/actor.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 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#ifndef M4_ACTOR_H
#define M4_ACTOR_H
#include "common/array.h"
#include "m4/m4.h"
#include "m4/scene.h"
#include "m4/graphics.h"
#include "m4/assets.h"
namespace M4 {
struct InventoryObject {
const char* name;
int32 scene;
int32 icon;
};
enum inventoryObjectFlags {
UNKNOWN_OBJECT = 997,
BACKPACK = 998,
NOWHERE = 999
};
enum WalkerDirection {
kFacingNorth = 1, // has shadow
kFacingNorthEast = 2, // has shadow
kFacingEast = 3, // has shadow
kFacingSouthEast = 4, // has shadow
kFacingSouth = 5, // has shadow
// 6 is unused
kFacingSouthAlt = 7, // no shadow
kFacingSouthWest = 8, // no shadow
kFacingWest = 9 // no shadow
};
class Actor {
public:
Actor(M4Engine *vm);
~Actor();
void placeWalkerSpriteAt(int spriteNum, int x, int y);
void setWalkerScaling(int scaling) { _scaling = scaling; }
int getWalkerScaling() { return _scaling; }
void setWalkerDirection(uint8 direction) { _direction = direction; }
uint8 getWalkerDirection() { return _direction; }
void setWalkerPalette();
int getWalkerWidth();
int getWalkerHeight();
private:
M4Engine *_vm;
int _scaling;
uint8 _direction;
Common::Array<SpriteAsset*> _walkerSprites;
void loadWalkers();
void loadWalkerDirection(uint8 direction);
void unloadWalkers();
};
// TODO: perhaps the inventory and its view could be merged?
// TODO: the original game capitalizes all inventory object names
// internally, which we do as well, but perhaps we could make sure
// that all object names are parsed with the same case and avoid
// case-insensitive string comparing through scumm_stricmp, using
// the normal strcmp method instead
class Inventory {
public:
Inventory(M4Engine *vm);
~Inventory();
void clear();
void registerObject(char* name, int32 scene, int32 icon);
void moveObject(char* name, int32 scene);
void giveToPlayer(char* name) { moveObject(name, BACKPACK); }
void addToBackpack(uint32 objectIndex);
void removeFromBackpack(uint32 objectIndex);
bool isInBackpack(char* name) { return (getScene(name) == BACKPACK); }
bool isInScene(char* name, int32 scene) { return (getScene(name) == scene); }
bool isInCurrentScene(char* name);
int getScene(char* name);
int getIcon(char* name);
int getIndex(char* name);
int getTotalItems() { return _inventory.size(); }
private:
M4Engine *_vm;
Common::Array<InventoryObject *> _inventory;
};
} // End of namespace M4
#endif

210
engines/m4/animation.cpp Normal file
View file

@ -0,0 +1,210 @@
/* 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.
*
* $URL$
* $Id$
*
*/
#include "m4/assets.h"
#include "m4/animation.h"
#include "m4/compression.h"
namespace M4 {
// TODO: this code needs cleanup
Animation::Animation(M4Engine *vm) {
_vm = vm;
_playing = false;
}
void Animation::loadFullScreen(const char *filename) {
_vm->_palette->deleteAllRanges();
load(filename);
}
void Animation::load(const char *filename) {
MadsPack anim(filename, _vm);
char buffer[20];
// Chunk 1: header
// header
// TODO: there are some unknown fields here, plus we don't read
// the entire chunk
Common::SeekableReadStream *animStream = anim.getItemStream(0);
Common::SeekableReadStream *spriteSeriesStream;
//printf("Chunk 0, size %i\n", animStream->size());
_seriesCount = animStream->readUint16LE();
_frameCount = animStream->readUint16LE();
_frameEntryCount = animStream->readUint16LE();
// Unknown
for (int i = 0; i < 43; i++)
animStream->readByte();
_spriteSeriesNames = new Common::String[_seriesCount];
printf("%i sprite series\n", _seriesCount);
// TODO: for now, we only load the first sprite series
if (_seriesCount > 1)
printf("TODO: Anim has %i sprite series, for now, we only load the first one\n", _seriesCount);
_seriesCount = 1; // TODO
for (int i = 0; i < _seriesCount; i++) {
animStream->read(buffer, 13);
_spriteSeriesNames[i] = Common::String(buffer);
//printf("%03d: %s\n", i, _spriteSeriesNames[i].c_str());
spriteSeriesStream = _vm->res()->get(_spriteSeriesNames[i].c_str());
_spriteSeries = new SpriteAsset(_vm, spriteSeriesStream,
spriteSeriesStream->size(), _spriteSeriesNames[i].c_str());
_vm->res()->toss(_spriteSeriesNames[i].c_str());
// Adjust the palette of the sprites in the sprite series
// so that they can be displayed on screen correctly
RGBList *palData = new RGBList(_spriteSeries->getColorCount(), _spriteSeries->getPalette(), true);
_vm->_palette->addRange(palData);
for (int k = 0; k < _spriteSeries->getCount(); k++) {
M4Sprite *spr = _spriteSeries->getFrame(k);
spr->translate(palData); // sprite pixel translation
}
}
//printf("End pos: %i\n", animStream->pos());
delete animStream;
// ------------------
// Chunk 2: anim info
AnimationFrame frame;
animStream = anim.getItemStream(1);
//printf("Chunk 1, size %i\n", animStream->size());
_frameEntries = new AnimationFrame[_frameEntryCount];
for (int i = 0; i < _frameEntryCount; i++) {
frame.animFrameIndex = animStream->readUint16LE();
frame.u = animStream->readByte();
frame.seriesIndex = animStream->readByte();
frame.seriesFrameIndex = animStream->readUint16LE();
frame.x = animStream->readUint16LE();
frame.y = animStream->readUint16LE();
frame.v = animStream->readByte();
frame.w = animStream->readByte();
_frameEntries[i] = frame;
/*
printf(
"animFrameIndex = %4d, "
"u = %3d, "
"seriesIndex = %3d, "
"seriesFrameIndex = %6d, "
"x = %3d, "
"y = %3d, "
"v = %3d, "
"w = %3d\n",
frame.animFrameIndex,
frame.u,
frame.seriesIndex,
frame.seriesFrameIndex,
frame.x,
frame.y,
frame.v,
frame.w
);
*/
}
//printf("End pos: %i\n", animStream->pos());
delete animStream;
// Chunk 3: unknown (seems to be sound data?)
// TODO
}
Animation::~Animation() {
//delete[] _spriteSeriesNames;
//delete[] _spriteSeries;
//delete[] _frameEntries;
}
void Animation::start() {
_curFrame = 0;
_curFrameEntry = 0;
//for (int i = 0; i < _seriesCount; i++) {
//_spriteSeries[i] = new SpriteSeries((char*)_spriteSeriesNames[i].c_str());
//}
_playing = true;
updateAnim();
}
bool Animation::updateAnim() {
if (!_playing)
return true;
// Get the scene background surface
M4Surface *bg = _vm->_scene->getBackgroundSurface();
while (_frameEntries[_curFrameEntry].animFrameIndex == _curFrame) {
AnimationFrame *frame = &_frameEntries[_curFrameEntry];
int seriesFrameIndex = (frame->seriesFrameIndex & 0x7FFF) - 1;
// Write the sprite onto the screen
M4Sprite *spr = _spriteSeries->getFrame(seriesFrameIndex);
// FIXME: We assume that the transparent color is the color of the top left pixel
byte *transparentColor = (byte *)spr->pixels;
// FIXME: correct x, y
spr->copyTo(bg, frame->x, frame->y, (int)*transparentColor);
// HACK: wait a bit
g_system->delayMillis(100);
//printf("_curFrameEntry = %d\n", _curFrameEntry);
_curFrameEntry++;
}
//printf("_curFrame = %d\n", _curFrame);
_curFrame++;
if (_curFrame >= _frameCount) // anim done
stop();
return _curFrame >= _frameCount;
}
void Animation::stop() {
_playing = false;
for (int i = 0; i < _seriesCount; i++) {
// TODO: cleanup
//delete _spriteSeries[i];
//_spriteSeries[i] = NULL;
}
}
} // End of namespace M4

69
engines/m4/animation.h Normal file
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 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#ifndef M4_ANIMATION_H
#define M4_ANIMATION_H
#include "m4/m4.h"
#include "m4/graphics.h"
#include "m4/assets.h"
namespace M4 {
struct AnimationFrame {
uint16 animFrameIndex;
byte u;
byte seriesIndex;
uint16 seriesFrameIndex;
uint16 x, y;
byte v, w;
};
class Animation {
public:
Animation(M4Engine *vm);
~Animation();
void load(const char *filename);
void loadFullScreen(const char *filename);
void start();
bool updateAnim();
void stop();
private:
bool _playing;
M4Engine *_vm;
int _seriesCount;
int _frameCount;
int _frameEntryCount;
AnimationFrame *_frameEntries;
Common::String *_spriteSeriesNames;
SpriteAsset *_spriteSeries;
int _curFrame, _curFrameEntry;
};
} // End of namespace M4
#endif

544
engines/m4/assets.cpp Normal file
View file

@ -0,0 +1,544 @@
/* 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.
*
* $URL$
* $Id$
*
*/
#include "m4/assets.h"
#include "m4/globals.h"
#include "m4/compression.h"
namespace M4 {
BaseAsset::BaseAsset(M4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name) : _vm(vm) {
}
BaseAsset::~BaseAsset() {
}
MachineAsset::MachineAsset(M4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name) : BaseAsset(vm, stream, size, name) {
uint32 stateCount = stream->readUint32LE();
for (uint32 curState = 0; curState < stateCount; curState++) {
uint32 stateOffset = stream->readUint32LE();
_stateTable.push_back(stateOffset);
}
_codeSize = size - 4 - 4 * stateCount;
_code = new byte[_codeSize];
stream->read(_code, _codeSize);
}
MachineAsset::~MachineAsset() {
delete[] _code;
}
void MachineAsset::getCode(byte *&code, uint32 &codeSize) {
code = _code;
codeSize = _codeSize;
}
uint32 MachineAsset::getStateOffset(uint32 state) {
assert(state < _stateTable.size());
return _stateTable[state];
}
SequenceAsset::SequenceAsset(M4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name) : BaseAsset(vm, stream, size, name) {
_localVarCount = stream->readUint32LE();
_codeSize = size - 4;
_code = new byte[_codeSize];
stream->read(_code, _codeSize);
}
SequenceAsset::~SequenceAsset() {
delete[] _code;
}
void SequenceAsset::getCode(byte *&code, uint32 &codeSize) {
code = _code;
codeSize = _codeSize;
}
DataAsset::DataAsset(M4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name) : BaseAsset(vm, stream, size, name) {
_recCount = stream->readUint32LE();
_recSize = stream->readUint32LE();
_dataSize = _recCount * _recSize;
_data = new long[_dataSize];
for (uint32 i = 0; i < _dataSize; i++)
_data[i] = (long)stream->readUint32LE();
}
DataAsset::~DataAsset() {
delete _data;
}
long *DataAsset::getRow(int index) {
assert(index < _recCount);
return &_data[_recSize * index];
}
SpriteAsset::SpriteAsset(M4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name, bool asStream) : BaseAsset(vm, stream, size, name) {
_stream = stream;
if (_vm->isM4()) {
loadM4SpriteAsset(vm, stream, asStream);
} else {
loadMadsSpriteAsset(vm, stream);
}
}
void SpriteAsset::loadM4SpriteAsset(M4Engine *vm, Common::SeekableReadStream* stream, bool asStream) {
bool isBigEndian = false;
uint32 frameOffset;
uint32 header = _stream->readUint32LE();
if (header == HEAD_M4SS) {
printf("LE-encoded sprite\n");
} else {
printf("BE-encoded sprite\n");
isBigEndian = true;
}
_srcSize = parseSprite(isBigEndian);
_stream->readUint32LE();
_frameRate = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
_pixelSpeed = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
_maxWidth = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
_maxHeight = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
_stream->skip(6 * 4);
_frameCount = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
printf("SpriteAsset::SpriteAsset() srcSize = %d; frameRate = %04X; pixelSpeed = %04X; maxWidth = %d; maxHeight = %d; frameCount = %d\n", _srcSize, _frameRate, _pixelSpeed, _maxWidth, _maxHeight, _frameCount);
for (int curFrame = 0; curFrame < _frameCount; curFrame++) {
frameOffset = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
_frameOffsets.push_back(frameOffset);
}
_frameOffsets.push_back(_srcSize - 48 - _frameCount * 4);
_frameStartOffset = _stream->pos();
// We don't need to load frames when streaming
if (asStream)
return;
for (int curFrame = 0; curFrame < _frameCount; curFrame++) {
frameOffset = _frameStartOffset + _frameOffsets[curFrame];
_stream->seek(frameOffset);
SpriteAssetFrame frame;
loadFrameHeader(frame, isBigEndian);
// Load & unpack RLE data if it's not a streaming animation
if (frame.stream != 1) {
frame.frame = new M4Sprite(stream, frame.x, frame.y, frame.w, frame.h, true, frame.comp);
#if 0
char fn[512];
sprintf(fn, "%04d.raw", curFrame);
FILE *h = fopen(fn, "wb");
fwrite((byte*)frame.frame->getData(), frame.w * frame.h, 1, h);
fclose(h);
#endif
}
_frames.push_back(frame);
}
}
void SpriteAsset::loadMadsSpriteAsset(M4Engine *vm, Common::SeekableReadStream* stream) {
int curFrame = 0;
uint32 frameOffset = 0;
MadsPack sprite(stream);
_frameRate = 0;
_pixelSpeed = 0;
_maxWidth = 0;
_maxHeight = 0;
Common::SeekableReadStream *spriteStream = sprite.getItemStream(0);
for (int i = 0; i < 19; i++) {
spriteStream->readUint16LE();
}
_frameCount = spriteStream->readUint16LE();
// we skip the rest of the data
delete spriteStream;
// Get the palette data
spriteStream = sprite.getItemStream(2);
int numColors = 0;
RGB8 *palData = Palette::decodeMadsPalette(spriteStream, &numColors);
Common::copy(palData, &palData[numColors], &_palette[0]);
if (numColors < 256)
Common::set_to((byte *)&_palette[numColors], (byte *)&_palette[256], 0);
_colorCount = numColors;
delete[] palData;
delete spriteStream;
spriteStream = sprite.getItemStream(1);
Common::SeekableReadStream *spriteDataStream = sprite.getItemStream(3);
SpriteAssetFrame frame;
for (curFrame = 0; curFrame < _frameCount; curFrame++) {
frame.comp = 0;
frameOffset = spriteStream->readUint32LE();
_frameOffsets.push_back(frameOffset);
spriteStream->readUint32LE(); // frame size
frame.x = spriteStream->readUint16LE();
frame.y = spriteStream->readUint16LE();
frame.w = spriteStream->readUint16LE();
frame.h = spriteStream->readUint16LE();
if (curFrame == 0)
printf("%i frames, x = %i, y = %i, w = %i, h = %i\n", _frameCount, frame.x, frame.y, frame.w, frame.h);
frame.frame = new M4Sprite(spriteDataStream, frame.x, frame.y, frame.w, frame.h, false);
_frames.push_back(frame);
}
delete spriteStream;
delete spriteDataStream;
}
SpriteAsset::~SpriteAsset() {
for (Common::Array<SpriteAssetFrame>::iterator it = _frames.begin(); it != _frames.end(); it++) {
delete (*it).frame;
}
}
int32 SpriteAsset::parseSprite(bool isBigEndian) {
uint32 format, chunkType, chunkSize = 0;
_colorCount = 0;
format = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
chunkType = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
if (chunkType == CELS__PAL) {
chunkSize = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
uint32 numColors = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
// TODO
//if (palette) {
// TODO: A sprite set palette specifies the indexes, which need not start at
// index 0. For now, I'm simply preloading the currently active palette
// before starting to replace existing entries
_vm->_palette->grabPalette((byte *) _palette, 0, 256);
_colorCount = 0;
for (uint32 i = 0; i < numColors; i++) {
uint32 paletteEntry = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
uint index = (paletteEntry >> 24) & 0xFF;
_palette[index].r = ((paletteEntry >> 16) & 0xFF) << 2;
_palette[index].g = ((paletteEntry >> 8) & 0xFF) << 2;
_palette[index].b = (paletteEntry & 0xFF) << 2;
_colorCount = MAX(_colorCount, index);
}
/*
} else {
stream.seek(colorCount, )
data += colorCount * 4;
}
*/
chunkType = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
}
if (chunkType == CELS___SS) {
chunkSize = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
} else {
warning("SpriteAsset::parseSprite() Expected chunk type %08X, got %08X", CELS___SS, chunkType);
}
return chunkSize;
}
void SpriteAsset::loadFrameHeader(SpriteAssetFrame &frameHeader, bool isBigEndian) {
_stream->readUint32LE();
frameHeader.stream = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
frameHeader.x = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
frameHeader.y = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
frameHeader.w = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
frameHeader.h = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
frameHeader.comp = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
frameHeader.frame = NULL;
_stream->seek(8 * 4, SEEK_CUR);
//printf("SpriteAsset::loadFrameHeader() stream = %d; x = %d; y = %d; w = %d; h = %d; comp = %d\n", frameHeader.stream, frameHeader.x, frameHeader.y, frameHeader.w, frameHeader.h, frameHeader.comp);
}
M4Sprite *SpriteAsset::getFrame(int frameIndex) {
return _frames[frameIndex].frame;
}
void SpriteAsset::loadStreamingFrame(M4Sprite *frame, int frameIndex, int destX, int destY) {
uint32 frameOffset = _frameStartOffset + _frameOffsets[frameIndex];
_stream->seek(frameOffset);
SpriteAssetFrame frameHeader;
loadFrameHeader(frameHeader);
if (frameHeader.w > 0 && frameHeader.h > 0) {
Common::MemoryReadStream *frameData = _stream->readStream(getFrameSize(frameIndex));
if (frameHeader.stream) {
frame->loadDeltaRle(frameData, destX - frameHeader.x, destY - frameHeader.y);
} else {
frame->loadRle(frameData);
}
delete frameData;
}
}
RGBList *SpriteAsset::getRgbList() {
RGBList *result = new RGBList(_colorCount);
Common::copy((byte *)&_palette[0], (byte *)&_palette[_colorCount], (byte *)result->data());
return result;
}
void SpriteAsset::translate(RGBList *list, bool isTransparent) {
for (int frameIndex = 0; frameIndex < _frameCount; ++frameIndex)
_frames[frameIndex].frame->translate(list, isTransparent);
}
int32 SpriteAsset::getFrameSize(int index) {
/*
if (index + 1 == _frameCount) {
} else {
}
*/
return _frameOffsets[index + 1] - _frameOffsets[index];
}
AssetManager::AssetManager(M4Engine *vm) {
_vm = vm;
/* Initialize asset arrays */
for (int i = 0; i < 256; i++) {
_MACH[i] = NULL;
_SEQU[i] = NULL;
_DATA[i] = NULL;
_CELS[i] = NULL;
}
}
AssetManager::~AssetManager() {
// unload all remaining assets
clearAssets(kAssetTypeMACH, 0, 255);
clearAssets(kAssetTypeSEQU, 0, 255);
clearAssets(kAssetTypeCELS, 0, 255);
clearAssets(kAssetTypeDATA, 0, 255);
}
bool AssetManager::clearAssets(AssetType assetType, int32 minHash, int32 maxHash) {
minHash = MAX(0, minHash);
maxHash = MIN(maxHash, 255);
switch (assetType) {
case kAssetTypeMACH:
for (int i = minHash; i <= maxHash; i++)
if (_MACH[i]) {
delete _MACH[i];
_MACH[i] = NULL;
}
break;
case kAssetTypeSEQU:
for (int i = minHash; i <= maxHash; i++)
if (_SEQU[i]) {
delete _SEQU[i];
_SEQU[i] = NULL;
}
break;
case kAssetTypeDATA:
for (int i = minHash; i <= maxHash; i++)
if (_DATA[i]) {
delete _DATA[i];
_DATA[i] = NULL;
}
break;
case kAssetTypeCELS:
for (int i = minHash; i <= maxHash; i++)
if (_CELS[i]) {
delete _CELS[i];
_CELS[i] = NULL;
}
break;
}
// FIXME: no value is returned, returning true for now
return true;
}
bool AssetManager::loadAsset(const char *assetName, RGB8 *palette) {
printf("AssetManager::loadAsset() %s\n", assetName);
// TODO, better use MemoryReadStreamEndian?
//convertAssetToLE(assetData, assetSize);
Common::SeekableReadStream *assetS = _vm->res()->get(assetName);
while (assetS->pos() + 12 < assetS->size()) {
uint32 chunkType, chunkSize, chunkHash;
chunkType = assetS->readUint32LE();
chunkSize = assetS->readUint32LE() - 12; // sub 12 for the chunk header
chunkHash = assetS->readUint32LE();
printf("hash = %d\n", chunkHash);
// Until loading code is complete, so that chunks not fully read are skipped correctly
uint32 nextOfs = assetS->pos() + chunkSize;
switch (chunkType) {
case CHUNK_MACH:
printf("MACH\n");
clearAssets(kAssetTypeMACH, chunkHash, chunkHash);
_MACH[chunkHash] = new MachineAsset(_vm, assetS, chunkSize, assetName);
break;
case CHUNK_SEQU:
printf("SEQU\n");
clearAssets(kAssetTypeSEQU, chunkHash, chunkHash);
_SEQU[chunkHash] = new SequenceAsset(_vm, assetS, chunkSize, assetName);
break;
case CHUNK_DATA:
printf("DATA\n");
clearAssets(kAssetTypeDATA, chunkHash, chunkHash);
_DATA[chunkHash] = new DataAsset(_vm, assetS, chunkSize, assetName);
break;
case CHUNK_CELS:
printf("CELS\n");
clearAssets(kAssetTypeCELS, chunkHash, chunkHash);
_CELS[chunkHash] = new SpriteAsset(_vm, assetS, chunkSize, assetName);
break;
default:
printf("AssetManager::loadAsset() Unknown chunk type %08X\n", chunkType);
}
// Until loading code is complete (see above)
assetS->seek(nextOfs);
}
_vm->res()->toss(assetName);
// FIXME: no value is returned, returning true for now
return true;
}
int32 AssetManager::addSpriteAsset(const char *assetName, int32 hash, RGB8 *palette) {
bool alreadyLoaded = false;
if (hash < 0) {
for (int i = 0; i <= 255; i++) {
if (_CELS[i] != NULL) {
if (_CELS[i]->getName() == assetName) {
alreadyLoaded = true;
hash = i;
break;
}
} else {
hash = i;
break;
}
}
} else {
alreadyLoaded = _CELS[hash] != NULL && _CELS[hash]->getName() == assetName;
}
/* Not loaded and no empty slots */
if (hash < 0)
return -1;
if (!alreadyLoaded) {
printf("AssetManager::addSpriteAsset() asset %s not loaded, loading into %d\n", assetName, hash);
clearAssets(kAssetTypeCELS, hash, hash);
Common::SeekableReadStream *assetS = _vm->res()->get(assetName);
_CELS[hash] = new SpriteAsset(_vm, assetS, assetS->size(), assetName);
_vm->res()->toss(assetName);
} else {
printf("AssetManager::addSpriteAsset() asset %s already loaded in %d\n", assetName, hash);
/* TODO/FIXME
if (_CELS[hash].palOffset >= 0 && palette)
restorePalette(palette, _CELS[hash].data + _CELS[hash].palOffset);
*/
}
return hash;
}
void AssetManager::restorePalette(RGB8 *palette, byte *data) {
// TODO
}
void AssetManager::convertAssetToLE(byte *assetData, uint32 assetSize) {
}
MachineAsset *AssetManager::getMachine(int32 hash) {
assert(_MACH[hash] != NULL);
return _MACH[hash];
}
SequenceAsset *AssetManager::getSequence(int32 hash) {
assert(_SEQU[hash] != NULL);
return _SEQU[hash];
}
DataAsset *AssetManager::getData(int32 hash) {
assert(_DATA[hash] != NULL);
return _DATA[hash];
}
SpriteAsset *AssetManager::getSprite(int32 hash) {
assert(_CELS[hash] != NULL);
return _CELS[hash];
}
M4Sprite *AssetManager::getSpriteFrame(int32 hash, int frameIndex) {
assert(_CELS[hash] != NULL);
return _CELS[hash]->getFrame(frameIndex);
}
int32 AssetManager::getSpriteFrameCount(int32 hash) {
assert(_CELS[hash] != NULL);
return _CELS[hash]->getCount();
}
} // End of namespace M4

184
engines/m4/assets.h Normal file
View file

@ -0,0 +1,184 @@
/* 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.
*
* $URL$
* $Id$
*
*/
#ifndef M4_ASSETS_H
#define M4_ASSETS_H
#include "common/scummsys.h"
#include "common/stream.h"
#include "m4/sprite.h"
namespace M4 {
// Sequence chunks
#define CHUNK_SCEN MKID_BE('SCEN')
#define CHUNK_MACH MKID_BE('MACH')
#define CHUNK_SEQU MKID_BE('SEQU')
#define CHUNK_DATA MKID_BE('DATA')
#define CHUNK_CELS MKID_BE('CELS')
// Sprite chunks
#define HEAD_M4SS MKID_BE('M4SS') //'M4SS'
#define CELS__PAL MKID_BE(' PAL') //' PAL'
#define CELS___SS MKID_BE(' SS') //' SS'
class M4Engine;
class BaseAsset {
public:
BaseAsset(M4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name);
~BaseAsset();
const Common::String getName() const { return _name; }
protected:
M4Engine *_vm;
Common::String _name;
};
class MachineAsset : public BaseAsset {
public:
MachineAsset(M4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name);
~MachineAsset();
void getCode(byte *&code, uint32 &codeSize);
uint32 getStateOffset(uint32 state);
protected:
Common::Array<uint32> _stateTable;
byte *_code;
uint32 _codeSize;
};
class SequenceAsset : public BaseAsset {
public:
SequenceAsset(M4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name);
~SequenceAsset();
void getCode(byte *&code, uint32 &codeSize);
int localVarCount() const { return _localVarCount; }
protected:
int _localVarCount;
byte *_code;
uint32 _codeSize;
};
class DataAsset : public BaseAsset {
public:
DataAsset(M4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name);
~DataAsset();
int getCount() const { return _recCount; }
long *getRow(int index);
protected:
long *_data;
uint32 _recSize, _dataSize;
int _recCount;
};
struct SpriteAssetFrame {
uint32 stream;
int x, y, w, h;
uint32 comp;
M4Sprite *frame;
};
class SpriteAsset : public BaseAsset {
public:
SpriteAsset(M4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name, bool asStream = false);
~SpriteAsset();
void loadM4SpriteAsset(M4Engine *vm, Common::SeekableReadStream* stream, bool asStream);
void loadMadsSpriteAsset(M4Engine *vm, Common::SeekableReadStream* stream);
int32 getCount() { return _frameCount; }
int32 getFrameRate() const { return _frameRate; }
int32 getPixelSpeed() const { return _pixelSpeed; }
int32 getFrameWidth(int index);
int32 getFrameHeight(int index);
int32 getMaxFrameWidth() const { return _maxWidth; }
int32 getMaxFrameHeight() const { return _maxHeight; }
M4Sprite *getFrame(int frameIndex);
void loadStreamingFrame(M4Sprite *frame, int frameIndex, int destX, int destY);
RGB8* getPalette() { return _palette; }
int getColorCount() { return _colorCount; }
RGBList *getRgbList();
void translate(RGBList *list, bool isTransparent = false);
int32 getFrameSize(int index);
M4Sprite *operator[](int index) { return getFrame(index); }
protected:
RGB8 _palette[256];
uint32 _colorCount;
uint32 _srcSize;
int32 _frameRate, _pixelSpeed;
int _maxWidth, _maxHeight;
int _frameCount;
Common::Array<uint32> _frameOffsets;
Common::Array<SpriteAssetFrame> _frames;
uint32 _frameStartOffset;
Common::SeekableReadStream *_stream;
int32 parseSprite(bool isBigEndian = false);
void loadFrameHeader(SpriteAssetFrame &frameHeader, bool isBigEndian = false);
};
enum AssetType {
kAssetTypeMACH,
kAssetTypeSEQU,
kAssetTypeDATA,
kAssetTypeCELS
};
enum CallbackHandlers {
kCallbackTriggerDispatch
};
class AssetManager {
public:
AssetManager(M4Engine *vm);
~AssetManager();
bool clearAssets(AssetType assetType, int32 minHash, int32 maxHash);
bool loadAsset(const char *assetName, RGB8 *palette);
int32 addSpriteAsset(const char *assetName, int32 hash, RGB8 *palette);
// TODO: Move to Palette class
void restorePalette(RGB8 *palette, byte *data);
MachineAsset *getMachine(int32 hash);
SequenceAsset *getSequence(int32 hash);
DataAsset *getData(int32 hash);
SpriteAsset *getSprite(int32 hash);
M4Sprite *getSpriteFrame(int32 hash, int frameIndex);
int32 getSpriteFrameCount(int32 hash);
protected:
// TODO: Check if we need _vm
M4Engine *_vm;
MachineAsset *_MACH[256];
SequenceAsset *_SEQU[256];
DataAsset *_DATA[256];
SpriteAsset *_CELS[256];
void convertAssetToLE(byte *assetData, uint32 assetSize);
};
} // End of namespace M4
#endif

85
engines/m4/burger_data.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 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#ifndef M4_BURGER_DATA_H
#define M4_BURGER_DATA_H
#include "m4/graphics.h"
#include "m4/actor.h"
namespace M4 {
InventoryObject burger_inventory [] = {
// name scene icon
//-------------------- ----- -----
{ "empty jug", 303, 14 },
{ "distilled juice", 999, 15 },
{ "broken puz dispenser", 999, 16 },
{ "puz dispenser", 999, 17 },
{ "broken mouse trap", 999, 18 },
{ "mouse trap", 999, 19 },
{ "kindling", 999, 20 },
{ "burning kindling", 999, 21 },
{ "lights", 508, 22 },
{ "lights on", 508, 23 },
{ "bottle", 999, 24 },
{ "carrot juice", 999, 25 },
{ "soapy water", 999, 26 },
{ "iron filings", 999, 27 },
{ "waxed hair", 999, 28 },
{ "fish", 999, 29 },
{ "hook", 999, 30 },
{ "keys", 999, 31 },
{ "records", 999, 32 },
{ "collar", 999, 33 },
{ "amp", 999, 34 },
{ "rubber gloves", 999, 35 },
{ "sock", 504, 36 },
{ "jaws of life", 999, 37 },
{ "deed", 999, 38 },
{ "burger morsel", 999, 39 },
{ "whistle", 999, 40 },
{ "coin", 999, 41 },
{ "matches", 999, 42 },
{ "phone cord", 999, 43 },
{ "kibble", 602, 44 }, // picked up from tray
{ "pantyhose", 999, 45 },
{ "fan belt", 999, 46 },
{ "spring", 999, 47 },
{ "mirror", 999, 48 },
{ "grate", 999, 49 },
{ "ray gun", 604, 50 }, // given to Wilbur when he enters 604
{ "grasshoppers", 999, 51 },
{ "rolling pin", 999, 52 },
{ "rubber duck", 999, 53 },
{ "ladder", 999, 54 },
{ "money", 999, 55 },
{ "crow bar", 999, 56 },
{ "Wilbur", 999, 57 }
};
} // End of namespace M4
#endif

189
engines/m4/compression.cpp Normal file
View file

@ -0,0 +1,189 @@
/* 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.
*
* $URL$
* $Id$
*
*/
#include "m4/compression.h"
#include "m4/m4.h"
namespace M4 {
const char *madsPackString = "MADSPACK";
bool MadsPack::isCompressed(Common::SeekableReadStream *stream) {
// Check whether the passed stream is packed
char tempBuffer[8];
stream->seek(0);
if (stream->read(tempBuffer, 8) == 8) {
if (!strncmp(tempBuffer, madsPackString, 8))
return true;
}
return false;
}
MadsPack::MadsPack(Common::SeekableReadStream *stream) {
initialise(stream);
}
MadsPack::MadsPack(const char *resourceName, M4Engine* vm) {
Common::SeekableReadStream *stream = vm->_resourceManager->get(resourceName);
initialise(stream);
vm->_resourceManager->toss(resourceName);
}
void MadsPack::initialise(Common::SeekableReadStream *stream) {
if (!MadsPack::isCompressed(stream))
error("Attempted to decompress a resource that was not MadsPacked");
stream->seek(14);
_count = stream->readUint16LE();
_items = new MadsPackEntry[_count];
byte *headerData = new byte[0xA0];
byte *header = headerData;
stream->read(headerData, 0xA0);
for (int i = 0; i < _count; ++i, header += 10) {
// Get header data
_items[i].hash = READ_LE_UINT16(header);
_items[i].size = READ_LE_UINT32(header + 2);
_items[i].compressedSize = READ_LE_UINT32(header + 6);
_items[i].data = new byte[_items[i].size];
if (_items[i].size == _items[i].compressedSize) {
// Entry isn't compressed
stream->read(_items[i].data, _items[i].size);
} else {
// Decompress the entry
byte *compressedData = new byte[_items[i].compressedSize];
stream->read(compressedData, _items[i].compressedSize);
FabDecompressor fab;
fab.decompress(compressedData, _items[i].compressedSize, _items[i].data, _items[i].size);
delete[] compressedData;
}
}
delete[] headerData;
_dataOffset = stream->pos();
}
MadsPack::~MadsPack() {
for (int i = 0; i < _count; ++i)
delete[] _items[i].data;
delete[] _items;
}
//--------------------------------------------------------------------------
const char *FabInputExceededError = "FabDecompressor - Passed end of input buffer during decompression";
const char *FabOutputExceededError = "FabDecompressor - Decompressed data exceeded specified size";
void FabDecompressor::decompress(const byte *srcData, int srcSize, byte *destData, int destSize) {
byte copyLen, copyOfsShift, copyOfsMask, copyLenMask;
unsigned long copyOfs;
byte *destP;
// Validate that the data starts with the FAB header
if (strncmp((const char *)srcData, "FAB", 3) != 0)
error("FabDecompressor - Invalid compressed data");
int shiftVal = srcData[3];
if ((shiftVal < 10) || (shiftVal > 13))
error("FabDecompressor - Invalid shift start");
copyOfsShift = 16 - shiftVal;
copyOfsMask = 0xFF << (shiftVal - 8);
copyLenMask = (1 << copyOfsShift) - 1;
copyOfs = 0xFFFF0000;
destP = destData;
// Initialise data fields
_srcData = srcData;
_srcP = _srcData + 6;
_srcSize = srcSize;
_bitsLeft = 16;
_bitBuffer = READ_LE_UINT16(srcData + 4);
for (;;) {
if (getBit() == 0) {
if (getBit() == 0) {
copyLen = ((getBit() << 1) | getBit()) + 2;
copyOfs = *_srcP++ | 0xFFFFFF00;
} else {
copyOfs = (((_srcP[1] >> copyOfsShift) | copyOfsMask) << 8) | _srcP[0];
copyLen = _srcP[1] & copyLenMask;
_srcP += 2;
if (copyLen == 0) {
copyLen = *_srcP++;
if (copyLen == 0)
break;
else if (copyLen == 1)
continue;
else
copyLen++;
} else {
copyLen += 2;
}
copyOfs |= 0xFFFF0000;
}
while (copyLen-- > 0) {
if (destP - destData == destSize)
error(FabOutputExceededError);
*destP = destP[(signed int)copyOfs];
destP++;
}
} else {
if (_srcP - srcData == srcSize)
error(FabInputExceededError);
if (destP - destData == destSize)
error(FabOutputExceededError);
*destP++ = *_srcP++;
}
}
if (destP - destData != destSize)
error("FabDecompressor - Decompressed data does not match header decompressed size");
}
int FabDecompressor::getBit() {
_bitsLeft--;
if (_bitsLeft == 0) {
if (_srcP - _srcData == _srcSize)
error(FabInputExceededError);
_bitBuffer = (READ_LE_UINT16(_srcP) << 1) | (_bitBuffer & 1);
_srcP += 2;
_bitsLeft = 16;
}
int bit = _bitBuffer & 1;
_bitBuffer >>= 1;
return bit;
}
} // End of namespace M4

82
engines/m4/compression.h Normal file
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 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#ifndef M4_COMPRESSION_H
#define M4_COMPRESSION_H
#include "common/scummsys.h"
#include "common/stream.h"
#include "common/endian.h"
#include "m4/m4.h"
namespace M4 {
struct MadsPackEntry {
public:
uint16 hash;
uint32 size;
uint32 compressedSize;
byte *data;
};
class MadsPack {
private:
MadsPackEntry *_items;
int _count;
int _dataOffset;
void initialise(Common::SeekableReadStream *stream);
public:
static bool isCompressed(Common::SeekableReadStream *stream);
MadsPack(Common::SeekableReadStream *stream);
MadsPack(const char *resourceName, M4Engine* _vm);
~MadsPack();
int getCount() const { return _count; }
MadsPackEntry &getItem(int index) const { return _items[index]; }
MadsPackEntry &operator[](int index) const { return _items[index]; }
Common::MemoryReadStream *getItemStream(int index) {
return new Common::MemoryReadStream(_items[index].data, _items[index].size, false);
}
int getDataOffset() const { return _dataOffset; }
};
class FabDecompressor {
private:
int _bitsLeft;
uint32 _bitBuffer;
const byte *_srcData, *_srcP;
int _srcSize;
int getBit();
public:
void decompress(const byte *srcData, int srcSize, byte *destData, int destSize);
};
} // End of namespace M4
#endif

287
engines/m4/console.cpp Normal file
View file

@ -0,0 +1,287 @@
/* 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.
*
* $URL$
* $Id$
*
*/
#include "m4/m4.h"
#include "m4/console.h"
#include "m4/scene.h"
namespace M4 {
Console::Console(M4Engine *vm) : GUI::Debugger() {
_vm = vm;
DCmd_Register("scene", WRAP_METHOD(Console, cmdLoadScene));
DCmd_Register("start", WRAP_METHOD(Console, cmdStartingScene));
DCmd_Register("scene_info", WRAP_METHOD(Console, cmdSceneInfo));
DCmd_Register("show_hotspots", WRAP_METHOD(Console, cmdShowHotSpots));
DCmd_Register("list_hotspots", WRAP_METHOD(Console, cmdListHotSpots));
DCmd_Register("play_sound", WRAP_METHOD(Console, cmdPlaySound));
DCmd_Register("play_dsr_sound", WRAP_METHOD(Console, cmdPlayDSRSound));
DCmd_Register("show_resources", WRAP_METHOD(Console, cmdShowResources));
DCmd_Register("show_codes", WRAP_METHOD(Console, cmdShowCodes));
DCmd_Register("dump_file", WRAP_METHOD(Console, cmdDumpFile));
DCmd_Register("sprite", WRAP_METHOD(Console, cmdShowSprite));
DCmd_Register("start_conv", WRAP_METHOD(Console, cmdStartConversation));
DCmd_Register("textview", WRAP_METHOD(Console, cmdShowTextview));
DCmd_Register("animview", WRAP_METHOD(Console, cmdShowAnimview));
DCmd_Register("anim", WRAP_METHOD(Console, cmdPlayAnimation));
}
Console::~Console() {
}
bool Console::cmdLoadScene(int argc, const char **argv) {
if (argc != 2) {
DebugPrintf("Usage: %s <scene number>\n", argv[0]);
return true;
} else {
if (_vm->isM4())
_vm->_kernel->newRoom = atoi(argv[1]);
else
_vm->_scene->loadScene(atoi(argv[1]));
return false;
}
}
bool Console::cmdStartingScene(int argc, const char **argv) {
if (_vm->getGameType() != GType_Riddle) {
if (_vm->isM4())
_vm->_kernel->newRoom = FIRST_SCENE;
else
_vm->_scene->loadScene(FIRST_SCENE);
return false;
} else {
DebugPrintf("%s: Riddle of Master Lu is not supported", argv[0]);
return true;
}
}
bool Console::cmdShowHotSpots(int argc, const char **argv) {
_vm->_scene->showHotSpots();
return false;
}
bool Console::cmdSceneInfo(int argc, const char **argv) {
DebugPrintf("Current scene is: %i\n", _vm->_scene->getCurrentScene());
if (_vm->isM4()) {
DebugPrintf("Scene resources:\n");
DebugPrintf("artBase: %s\n", _vm->_scene->getSceneResources().artBase);
DebugPrintf("pictureBase: %s\n", _vm->_scene->getSceneResources().pictureBase);
DebugPrintf("hotspotCount: %i\n", _vm->_scene->getSceneResources().hotspotCount);
DebugPrintf("parallaxCount: %i\n", _vm->_scene->getSceneResources().parallaxCount);
DebugPrintf("propsCount: %i\n", _vm->_scene->getSceneResources().propsCount);
DebugPrintf("frontY: %i\n", _vm->_scene->getSceneResources().frontY);
DebugPrintf("backY: %i\n", _vm->_scene->getSceneResources().backY);
DebugPrintf("frontScale: %i\n", _vm->_scene->getSceneResources().frontScale);
DebugPrintf("backScale: %i\n", _vm->_scene->getSceneResources().backScale);
DebugPrintf("depthTable: ");
for (uint i = 0; i < 16; i++)
DebugPrintf("%i ", _vm->_scene->getSceneResources().depthTable[i]);
DebugPrintf("\n");
DebugPrintf("railNodeCount: %i\n", _vm->_scene->getSceneResources().railNodeCount);
}
return true;
}
bool Console::cmdListHotSpots(int argc, const char **argv) {
DebugPrintf("Scene hotspots\n");
_vm->_scene->getSceneResources().hotspots->dump();
if (_vm->isM4()) {
DebugPrintf("Scene parallax\n");
_vm->_scene->getSceneResources().parallax->dump();
DebugPrintf("Scene props\n");
_vm->_scene->getSceneResources().props->dump();
}
return true;
}
bool Console::cmdPlaySound(int argc, const char **argv) {
if (argc != 2) {
DebugPrintf("Usage: %s <sound file>\n", argv[0]);
} else {
_vm->_sound->playSound(argv[1], 255, false);
}
return true;
}
bool Console::cmdPlayDSRSound(int argc, const char **argv) {
if (argc != 2 && argc != 3) {
DebugPrintf("Usage: %s <sound index> <DSR file>\n", argv[0]);
DebugPrintf("The DSR file parameter is optional, and specifies which DSR to load\n", argv[0]);
} else {
if (argc == 3)
_vm->_sound->loadDSRFile(argv[2]);
_vm->_sound->playDSRSound(atoi(argv[1]), 255, false);
}
return true;
}
bool Console::cmdShowResources(int argc, const char **argv) {
_vm->res()->dump();
return true;
}
bool Console::cmdShowCodes(int argc, const char **argv) {
if (_vm->getGameType() != GType_RexNebular)
_vm->_scene->showCodes();
else
DebugPrintf("Pathfinding codes not done yet for Rex Nebular");
return false;
}
bool Console::cmdDumpFile(int argc, const char **argv) {
if (argc != 2 && argc != 3) {
DebugPrintf("Usage: %s <file> <uncompress>\n", argv[0]);
DebugPrintf("If uncompress is 1, the file is uncompressed (for MADS games)\n");
} else {
if (argc == 2) {
_vm->dumpFile(strdup(argv[1]));
} else {
if (argc == 3 && atoi(argv[2]) == 1)
_vm->dumpFile(strdup(argv[1]), true);
else
_vm->dumpFile(strdup(argv[1]));
}
}
return true;
}
bool Console::cmdShowSprite(int argc, const char **argv) {
View *view = _vm->_viewManager->getView(VIEWID_SCENE);
if (view == NULL)
DebugPrintf("The scene view isn't currently active\n");
else if (argc < 2)
DebugPrintf("Usage: %s resource_name\n", argv[0]);
else {
char resourceName[20];
strncpy(resourceName, argv[1], 15);
resourceName[15] = '\0';
if (!strchr(resourceName, '.'))
strcat(resourceName, ".SS");
_vm->_viewManager->moveToFront(view);
Common::SeekableReadStream *data = _vm->res()->get(resourceName);
SpriteAsset *asset = new SpriteAsset(_vm, data, data->size(), resourceName);
_vm->res()->toss(resourceName);
RGBList *palData = new RGBList(asset->getColorCount(), asset->getPalette(), true);
_vm->_palette->addRange(palData);
// Get the scene background surface
M4Surface *bg = _vm->_scene->getBackgroundSurface();
// Write the sprite onto the screen
int x = 0, y = 0, yMax = 0;
for (int index = 0; index < asset->getCount(); index++) {
M4Sprite *spr = asset->getFrame(index);
spr->translate(palData); // sprite pixel translation
if ((x + spr->width() >= bg->width()) && (yMax != 0)) {
x = 0;
y += yMax;
yMax = 0;
}
if (y >= bg->height())
break;
// FIXME: We assume that the transparent color is the color of the top left pixel
byte *transparentColor = (byte *)spr->pixels;
spr->copyTo(bg, x, y, (int)*transparentColor);
x += spr->width();
yMax = MAX(yMax, spr->height());
}
view->restore(0, 0, view->width(), view->height());
return false;
}
return true;
}
bool Console::cmdStartConversation(int argc, const char **argv) {
if (argc != 2) {
DebugPrintf("Usage: %s <conversation file name>\n", argv[0]);
return true;
} else {
_vm->_converse->startConversation(argv[1]);
return false;
}
}
bool Console::cmdShowTextview(int argc, const char **argv) {
if (argc != 2) {
DebugPrintf("Usage: %s <txr resource>\n", argv[0]);
return true;
}
_vm->_viewManager->showTextView(argv[1], false);
return false;
}
bool Console::cmdShowAnimview(int argc, const char **argv) {
if (argc != 2) {
DebugPrintf("Usage: %s <res resource>\n", argv[0]);
return true;
}
char resName[80];
strcpy(resName, "@");
strcat(resName, *argv[1] == '@' ? argv[1] + 1 : argv[1]);
_vm->_viewManager->showAnimView(resName, false);
return false;
}
bool Console::cmdPlayAnimation(int argc, const char **argv) {
View *view = _vm->_viewManager->getView(VIEWID_SCENE);
if (view == NULL) {
DebugPrintf("The scene view isn't currently active\n");
} else if (argc != 2 && argc != 3) {
DebugPrintf("Usage: %s <anim resource (*.aa)> <fullscreen>\n", argv[0]);
DebugPrintf("If fullscreen is 1, the screen palette is replaced with the palette of the animation\n");
} else {
char resourceName[20];
strncpy(resourceName, argv[1], 15);
resourceName[15] = '\0';
if (!strchr(resourceName, '.'))
strcat(resourceName, ".AA");
_vm->_viewManager->moveToFront(view);
if (argc == 3 && atoi(argv[2]) == 1)
_vm->_animation->loadFullScreen(resourceName);
else
_vm->_animation->load(resourceName);
_vm->_animation->start();
view->restore(0, 0, view->width(), view->height());
return false;
}
return true;
}
} // End of namespace M4

64
engines/m4/console.h Normal file
View file

@ -0,0 +1,64 @@
/* 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.
*
* $URL$
* $Id$
*
*/
#ifndef M4_CONSOLE_H
#define M4_CONSOLE_H
#include "gui/debugger.h"
namespace M4 {
class M4Engine;
class Console : public GUI::Debugger {
public:
Console(M4Engine *vm);
virtual ~Console(void);
private:
bool cmdLoadScene(int argc, const char **argv);
bool cmdStartingScene(int argc, const char **argv);
bool cmdSceneInfo(int argc, const char **argv);
bool cmdShowHotSpots(int argc, const char **argv);
bool cmdListHotSpots(int argc, const char **argv);
bool cmdPlaySound(int argc, const char **argv);
bool cmdPlayDSRSound(int argc, const char **argv);
bool cmdShowResources(int argc, const char **argv);
bool cmdShowCodes(int argc, const char **argv);
bool cmdDumpFile(int argc, const char **argv);
bool cmdShowSprite(int argc, const char **argv);
bool cmdStartConversation(int argc, const char **argv);
bool cmdShowTextview(int argc, const char **argv);
bool cmdShowAnimview(int argc, const char **argv);
bool cmdPlayAnimation(int argc, const char **argv);
private:
M4Engine *_vm;
};
} // End of namespace M4
#endif

1211
engines/m4/converse.cpp Normal file

File diff suppressed because it is too large Load diff

200
engines/m4/converse.h Normal file
View file

@ -0,0 +1,200 @@
/* 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.
*
* $URL$
* $Id$
*
*/
#ifndef M4_CONVERSE_H
#define M4_CONVERSE_H
#include "common/array.h"
#include "common/hashmap.h"
#include "m4/globals.h"
#include "m4/m4.h"
#include "m4/viewmgr.h"
namespace M4 {
enum ConversationState {
kConversationOptionsShown = 0,
kEntryIsActive = 1,
kReplyIsActive = 2,
kNoConversation = 3
};
enum EntryType {
kVariable = 0,
kNode = 1,
kLinearNode = 2,
kEntry = 3,
kReply = 4,
kWeightedReply = 5
};
// Flags are:
// bit 0: if it's 1, the entry is "initial", i.e. not hidden when the dialog starts
// bit 1: if it's 1, the entry persists if selected
enum EntryFlags {
kEntryInitial = 1, // byte 0
kEntryPersists = 2 // byte 1
};
enum EntryActionType {
kUnknownAction = 0,
kGotoEntry = 1,
kHideEntry = 2,
kUnhideEntry = 3,
kDestroyEntry = 4,
kAssignValue = 5,
kExitConv = 6
};
enum LogicOp {
kOpPercent = 405,
kOpGreaterOrEqual = 421,
kOpLessOrEqual = 420,
kOpGreaterThan = 413,
kOpLessThan = 412,
kOpNotEqual = 422,
kOpCondNotEqual = 448,
kOpAssign = 407,
kOpAnd = 444,
kOpOr = 445
};
struct Condition {
int32 offset;
int32 op;
int32 val;
};
struct EntryAction {
int32 targetOffset; // absolute offset (inside the *.chk file) of the action's target
int32 value; // the value set by assignment chunks
EntryActionType actionType;
EntryType targetType;
int32 nodeId;
int32 entryId;
bool isConditional;
Condition condition;
};
struct ConvEntry {
EntryType entryType;
int32 id;
int32 offset; // absolute offset inside the *.chk file, referenced by other chunks
int32 size; // entry action data size (for MADS games)
int32 flags;
int32 fallthroughMinEntries;
int32 fallthroughOffset;
int32 weight; // weight for weighted replies
int32 totalWeight;
uint16 entryCount; // entries inside this node (for MADS games)
char voiceFile[10];
char text[512];
bool autoSelect;
bool visible;
bool isConditional;
Condition condition;
Common::Array<EntryAction*>actions;
Common::Array<ConvEntry*>entries;
};
struct EntryInfo {
EntryType targetType;
int32 nodeIndex;
int32 entryIndex;
};
struct MessageEntry {
Common::Array<char*>messageStrings;
};
enum ConverseStyle {CONVSTYLE_EARTH, CONVSTYLE_SPACE};
typedef Common::HashMap<Common::String,EntryInfo,Common::IgnoreCase_Hash,Common::IgnoreCase_EqualTo> OffsetHashMap;
typedef Common::HashMap<Common::String,int32,Common::IgnoreCase_Hash,Common::IgnoreCase_EqualTo> ConvVarHashMap;
class ConversationView: public View {
public:
ConversationView(M4Engine *vm);
~ConversationView();
void setNode(int32 nodeIndex);
void onRefresh(RectList *rects, M4Surface *destSurface);
bool onEvent(M4EventType eventType, int param, int x, int y, bool &captureEvents);
int32 getCurrentNodeIndex() { return _currentNodeIndex; }
void selectEntry(int entryIndex);
private:
void updateState();
void playNextReply();
int32 _currentNodeIndex;
Common::Array<ConvEntry *> _activeItems;
int _highlightedIndex;
int _xEnd;
bool _entriesShown;
ConversationState _conversationState;
SndHandle *_currentHandle;
};
class Converse {
public:
Converse(M4Engine *vm) : _vm(vm) {}
~Converse() {}
void startConversation(const char *convName, bool showConversebox = true, ConverseStyle style = CONVSTYLE_EARTH );
void endConversation();
const EntryInfo* getEntryInfo(int32 offset);
ConvEntry *getNode(int32 index) { return _convNodes[index]; }
void setValue(int32 offset, int32 value);
const int32 getValue(int32 offset);
bool evaluateCondition(int32 leftVal, int32 op, int32 rightVal);
bool performAction(EntryAction *action);
/*
void resume() { play(); }
void play();
*/
private:
M4Engine *_vm;
Common::Array<ConvEntry*>_convNodes;
Common::Array<MessageEntry*>_madsMessageList;
Common::Array<char *>_convStrings;
bool _playerCommandsAllowed;
bool _interfaceWasVisible;
ConverseStyle _style;
OffsetHashMap _offsetMap;
ConvVarHashMap _variables;
void loadConversation(const char *convName);
void loadConversationMads(const char *convName);
void readConvEntryActions(Common::SubReadStream *convS, ConvEntry *curEntry);
void setEntryInfo(int32 offset, EntryType type, int32 nodeIndex, int32 entryIndex);
};
} // End of namespace M4
#endif

345
engines/m4/detection.cpp Normal file
View file

@ -0,0 +1,345 @@
/* 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.
*
* $URL$
* $Id$
*
*/
#include "base/plugins.h"
#include "common/advancedDetector.h"
#include "m4/m4.h"
#include "m4/resource.h"
namespace M4 {
struct M4GameDescription {
Common::ADGameDescription desc;
int gameType;
uint32 features;
};
int M4Engine::getGameType() const { return _gameDescription->gameType; }
uint32 M4Engine::getFeatures() const { return _gameDescription->features; }
Common::Language M4Engine::getLanguage() const { return _gameDescription->desc.language; }
Common::Platform M4Engine::getPlatform() const { return _gameDescription->desc.platform; }
}
static const PlainGameDescriptor m4Games[] = {
{"m4", "MADS/M4 engine game"},
{"riddle", "Riddle of Master Lu: Believe it or Not!"},
{"burger", "Orion Burger"},
{"rex", "Rex Nebular and the Cosmic Gender Bender"},
{"dragon", "DragonSphere"},
{"dragoncd", "DragonSphere CD"},
{"phantom", "Return of the Phantom"},
{"phantomcd", "Return of the Phantom CD"},
{0, 0}
};
namespace M4 {
const char *M4Engine::getGameFile(int fileType) {
for (int i = 0; _gameDescription->desc.filesDescriptions[i].fileName; i++) {
if (_gameDescription->desc.filesDescriptions[i].fileType == fileType)
return _gameDescription->desc.filesDescriptions[i].fileName;
}
return NULL;
}
static const M4GameDescription gameDescriptions[] = {
{
{
"burger",
"",
{
{ "burger.has", kFileTypeHash, "10c8064e9c771072122f50737ac97245", 730771},
{ NULL, 0, NULL, 0}
},
Common::EN_ANY,
Common::kPlatformPC,
Common::ADGF_NO_FLAGS
},
GType_Burger,
kFeaturesCD
},
{
{
"burger",
"",
{
{ "burger.has", kFileTypeHash, "55be8693a4c36e7efcdca0f0c77758ae", 724191},
{ NULL, 0, NULL, 0}
},
Common::DE_DEU,
Common::kPlatformPC,
Common::ADGF_NO_FLAGS
},
GType_Burger,
kFeaturesCD
},
{
{
"riddle",
"",
{
{ "ripley.has", kFileTypeHash, "056d517360c89eb4c297a319f21a7071", 700469},
{ NULL, 0, NULL, 0}
},
Common::EN_ANY,
Common::kPlatformPC,
Common::ADGF_NO_FLAGS
},
GType_Riddle,
kFeaturesCD
},
{
{
"riddle",
"",
{
{ "ripley.has", kFileTypeHash, "d073582c9011d44dd0d7e2ede317a86d", 700469},
{ NULL, 0, NULL, 0}
},
Common::EN_ANY,
Common::kPlatformPC,
Common::ADGF_NO_FLAGS
},
GType_Riddle,
kFeaturesCD
},
{
{
"riddle",
"",
{
{ "ripley.has", kFileTypeHash, "d9e9f8befec432a047b1047fb2bc7aea", 710997},
{ NULL, 0, NULL, 0}
},
Common::DE_DEU,
Common::kPlatformPC,
Common::ADGF_NO_FLAGS
},
GType_Riddle,
kFeaturesCD
},
{
{
"riddle",
"",
{
{ "ripley.has", kFileTypeHash, "3d48c5700785d11e6a5bc832b95be918", 701973},
{ NULL, 0, NULL, 0}
},
Common::FR_FRA,
Common::kPlatformPC,
Common::ADGF_NO_FLAGS
},
GType_Riddle,
kFeaturesCD
},
{ // Demo
{
"riddle",
"Demo",
{
{ "ripley.has", kFileTypeHash, "3a90dd7052860b6e246ec7e0aaf202f6", 104527},
{ NULL, 0, NULL, 0}
},
Common::EN_ANY,
Common::kPlatformPC,
Common::ADGF_DEMO
},
GType_Riddle,
kFeaturesDemo
},
{
{
"rex",
"",
{
{ "global.hag", kFileTypeHAG, "0530cbeee109fc79cc24421128dea1ce", 2083078},
{ NULL, 0, NULL, 0}
},
Common::EN_ANY,
Common::kPlatformPC,
Common::ADGF_NO_FLAGS
},
GType_RexNebular,
kFeaturesNone
},
{ // Demo
{
"rex",
"Demo",
{
{ "global.hag", kFileTypeHAG, "d5a481d14bc1bda66e46965a39badcc7", 220429},
{ NULL, 0, NULL, 0}
},
Common::EN_ANY,
Common::kPlatformPC,
Common::ADGF_DEMO
},
GType_RexNebular,
kFeaturesDemo
},
{
{
"dragon",
"",
{
{ "global.hag", kFileTypeHAG, "c3a6877665e7f21bf3d2b1e667155562", 2320567},
{ NULL, 0, NULL, 0}
},
Common::EN_ANY,
Common::kPlatformPC,
Common::ADGF_NO_FLAGS
},
GType_DragonSphere,
kFeaturesNone
},
{ // CD version
{
"dragoncd",
"",
{
{ "global.hag", kFileTypeHAG, "c3a6877665e7f21bf3d2b1e667155562", 2320567},
{ "speech.hag", kFileTypeHAG, NULL, -1},
{ NULL, 0, NULL, 0}
},
Common::EN_ANY,
Common::kPlatformPC,
Common::ADGF_NO_FLAGS
},
GType_DragonSphere,
kFeaturesCD
},
{ // Demo
{
"dragon",
"Demo",
{
{ "global.hag", kFileTypeHAG, "541e12d8e9aad0c65d65f150de47582e", 212718},
{ NULL, 0, NULL, 0}
},
Common::EN_ANY,
Common::kPlatformPC,
Common::ADGF_DEMO
},
GType_DragonSphere,
kFeaturesDemo
},
{
{
"phantom",
"",
{
{ "global.hag", kFileTypeHAG, "bdce9ca93a015f0883d1bc0fabd0cdfa", 812150},
{ NULL, 0, NULL, 0}
},
Common::EN_ANY,
Common::kPlatformPC,
Common::ADGF_NO_FLAGS
},
GType_Phantom,
kFeaturesNone
},
{ // CD version
{
"phantomcd",
"",
{
{ "global.hag", kFileTypeHAG, "8a51c984eb4c64e8b30a7e6670f6bddb", 101154000},
{ NULL, 0, NULL, 0}
},
Common::EN_ANY,
Common::kPlatformPC,
Common::ADGF_NO_FLAGS
},
GType_Phantom,
kFeaturesCD
},
{ // CD version
{
"phantom",
"Demo",
{
{ "global.hag", kFileTypeHAG, "e810adbc6fac77ac2fec841a9ec5e20e", 115266},
{ NULL, 0, NULL, 0}
},
Common::EN_ANY,
Common::kPlatformPC,
Common::ADGF_DEMO
},
GType_Phantom,
kFeaturesDemo
},
{ { NULL, NULL, { { NULL, 0, NULL, 0 } }, Common::UNK_LANG, Common::kPlatformUnknown, Common::ADGF_NO_FLAGS }, 0, 0 }
};
}
static const Common::ADParams detectionParams = {
// Pointer to ADGameDescription or its superset structure
(const byte *)M4::gameDescriptions,
// Size of that superset structure
sizeof(M4::M4GameDescription),
// Number of bytes to compute MD5 sum for
5000,
// List of all engine targets
m4Games,
// Structure for autoupgrading obsolete targets
0,
// Name of single gameid (optional)
"m4",
// List of files for file-based fallback detection (optional)
0,
// Flags
0
};
class M4MetaEngine : public Common::AdvancedMetaEngine {
public:
M4MetaEngine() : Common::AdvancedMetaEngine(detectionParams) {}
virtual const char *getName() const {
return "MADS/M4 engine";
}
virtual const char *getCopyright() const {
return "Riddle of Master Lu & Orion Burger (C) Sanctuary Woods";
}
virtual bool createInstance(OSystem *syst, Engine **engine, const Common::ADGameDescription *desc) const;
};
bool M4MetaEngine::createInstance(OSystem *syst, Engine **engine, const Common::ADGameDescription *desc) const {
const M4::M4GameDescription *gd = (const M4::M4GameDescription *)desc;
if (gd) {
*engine = new M4::M4Engine(syst, gd);
}
return gd != 0;
}
REGISTER_PLUGIN(M4, PLUGIN_TYPE_ENGINE, M4MetaEngine);

351
engines/m4/events.cpp Normal file
View file

@ -0,0 +1,351 @@
/* 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.
*
* $URL$
* $Id$
*
*/
// TODO: There is a 'please_hyperwalk' variable that gets accessed that is meant to be global, but
// at the moment it's implemented as a local variable
#include "graphics/cursorman.h"
#include "m4/events.h"
#include "m4/graphics.h"
#include "m4/scene.h"
#include "m4/viewmgr.h"
#include "m4/m4.h"
namespace M4 {
bool please_hyperwalk = false;
/*--------------------------------------------------------------------------*
* Events *
* *
* Implements an interface to the event system *
*--------------------------------------------------------------------------*/
Events::Events(M4Engine *vm) : _vm(vm) {
_mouseState = MSTATE_NO_EVENT;
quitFlag = false;
_keyCode = 0;
_console = new Console(_vm);
_mouseButtons = 0;
}
M4EventType Events::handleEvents() {
static int oldX = -1, oldY = -1;
static uint32 dclickTime = 0;
// Handle event types
while (g_system->getEventManager()->pollEvent(_event)) {
switch (_event.type) {
case Common::EVENT_QUIT:
quitFlag = true;
break;
case Common::EVENT_KEYDOWN:
if (_event.kbd.flags == Common::KBD_CTRL) {
if (_event.kbd.keycode == Common::KEYCODE_d)
_console->attach();
_console->onFrame();
}
_keyCode = (int)_event.kbd.keycode;
break;
case Common::EVENT_LBUTTONDOWN:
case Common::EVENT_LBUTTONUP:
case Common::EVENT_RBUTTONDOWN:
case Common::EVENT_RBUTTONUP:
case Common::EVENT_MBUTTONDOWN:
case Common::EVENT_MBUTTONUP:
case Common::EVENT_MOUSEMOVE:
case Common::EVENT_WHEELUP:
case Common::EVENT_WHEELDOWN:
_vm->_mouse->handleEvent(_event);
break;
default:
break;
}
}
_mouseButtons = g_system->getEventManager()->getButtonState();
// State machine for moving between states
switch (_mouseState) {
case MSTATE_NO_EVENT:
if (_mouseButtons & LEFT_BUTTON_DOWN) {
if ((dclickTime != 0) && (g_system->getMillis() < dclickTime)) {
_mouseState = MSTATE_DOUBLECLICK_DOWN;
dclickTime = 0;
return MEVENT_DOUBLECLICK;
}
dclickTime = 0;
_mouseState = MSTATE_LEFT_CLICK_DOWN;
return MEVENT_LEFT_CLICK;
}
if (_mouseButtons & RIGHT_BUTTON_DOWN) {
_mouseState = MSTATE_RIGHT_CLICK_DOWN;
return MEVENT_RIGHT_CLICK;
}
if ((_event.mouse.x != oldX) || (_event.mouse.y != oldY)) {
oldX = _event.mouse.x; oldY = _event.mouse.y;
return MEVENT_MOVE;
}
return MEVENT_NO_EVENT;
case MSTATE_LEFT_CLICK_DOWN:
if (!(_mouseButtons & LEFT_BUTTON_DOWN)) {
dclickTime = g_system->getMillis() + 1000 * 15 / 60;
_mouseState = MSTATE_NO_EVENT;
return MEVENT_LEFT_RELEASE;
}
if ((_event.mouse.x != oldX) || (_event.mouse.y != oldY)) {
oldX = _event.mouse.x; oldY = _event.mouse.y;
return MEVENT_LEFT_DRAG;
}
return MEVENT_LEFT_HOLD;
case MSTATE_RIGHT_CLICK_DOWN:
if (!(_mouseButtons & RIGHT_BUTTON_DOWN)) {
_mouseState = MSTATE_NO_EVENT;
please_hyperwalk = true;
return MEVENT_RIGHT_RELEASE;
}
if ((_event.mouse.x != oldX) || (_event.mouse.y != oldY)) {
oldX = _event.mouse.x; oldY = _event.mouse.y;
return MEVENT_RIGHT_DRAG;
}
return MEVENT_RIGHT_HOLD;
case MSTATE_DOUBLECLICK_DOWN:
if (!(_mouseButtons & LEFT_BUTTON_DOWN)) {
_mouseState = MSTATE_NO_EVENT;
return MEVENT_DOUBLECLICK_RELEASE;
}
if ((_event.mouse.x != oldX) || (_event.mouse.y != oldY)) {
oldX = _event.mouse.x; oldY = _event.mouse.y;
return MEVENT_DOUBLECLICK_DRAG;
}
return MEVENT_DOUBLECLICK_HOLD;
default:
return MEVENT_NO_EVENT;
}
}
bool Events::kbdCheck(uint32 &keyCode) {
if (_keyCode == 0)
return false;
keyCode = _keyCode;
_keyCode = 0;
return true;
}
/*--------------------------------------------------------------------------*
* Mouse *
* *
* Implements an interface to the mouse *
*--------------------------------------------------------------------------*/
Mouse::Mouse(M4Engine *vm) : _vm(vm) {
_locked = false;
_cursorOn = false;
_cursor = NULL;
_cursorSprites = NULL;
resetMouse();
}
Mouse::~Mouse() {
if (_cursorSprites)
delete _cursorSprites;
}
bool Mouse::init(const char *seriesName, RGB8 *palette) {
Common::SeekableReadStream *stream = _vm->res()->get(seriesName);
int colorCount = 0;
RGB8* cursorPalette;
_cursorSprites = new SpriteAsset(_vm, stream, stream->size(), seriesName);
// Remove cursor special pixels and set the mouse cursor hotspot in MADS games
if (!_vm->isM4()) {
byte *data = NULL;
for (int i = 0; i < _cursorSprites->getCount(); i++) {
bool hotSpotSet = false;
for (int x = 0; x < _cursorSprites->getFrame(i)->width(); x++) {
for (int y = 0; y < _cursorSprites->getFrame(i)->height(); y++) {
data = _cursorSprites->getFrame(i)->getBasePtr(x, y);
if (*data == 1) {
// It seems that some cursors have more than one hotspot
// In such a case, the first hotspot seems to set the x and
// the second one the y hotspot offset
if (!hotSpotSet) {
_cursorSprites->getFrame(i)->xOffset = x;
_cursorSprites->getFrame(i)->yOffset = y;
hotSpotSet = true;
} else {
_cursorSprites->getFrame(i)->yOffset = y;
}
*data = 0;
}
} // for y
} // for x
} // for i
}
colorCount = _cursorSprites->getColorCount();
cursorPalette = _cursorSprites->getPalette();
_vm->_palette->setPalette(cursorPalette, 0, colorCount);
//printf("Cursor count: %d\n", _cursorSprites->getCount());
_vm->res()->toss(seriesName);
_currentCursor = -1;
return true;
}
bool Mouse::setCursorNum(int cursorIndex) {
if ((cursorIndex < 0) || (cursorIndex >= (int)_cursorSprites->getCount()))
return false;
_lockedCursor = cursorIndex;
if (_locked)
// Cursor is locked, so don't go ahead with changing cursor
return true;
_currentCursor = _lockedCursor;
_cursor = _cursorSprites->getFrame(cursorIndex);
// Set the cursor to the sprite
CursorMan.replaceCursor((const byte *)_cursor->getData(), _cursor->w, _cursor->h, _cursor->xOffset, _cursor->yOffset, 0);
return true;
}
int Mouse::cursorCount() {
return _cursorSprites->getCount();
}
void Mouse::cursorOn() {
_cursorOn = true;
CursorMan.showMouse(!inHideArea());
}
void Mouse::cursorOff() {
_cursorOn = false;
CursorMan.showMouse(false);
}
void Mouse::lockCursor(int cursorIndex) {
_locked = false;
setCursorNum(cursorIndex);
_locked = true;
}
void Mouse::unlockCursor() {
_locked = false;
setCursorNum(_lockedCursor);
}
const char *Mouse::getVerb() {
switch (_vm->_mouse->getCursorNum()) {
case CURSOR_LOOK:
return "LOOK AT";
case CURSOR_TAKE:
return "TAKE";
case CURSOR_USE:
return "GEAR";
default:
return NULL;
}
}
void Mouse::resetMouse() {
_hideRect.left = -1;
_hideRect.top = -1;
_hideRect.right = -1;
_hideRect.bottom = -1;
_showRect.left = -1;
_showRect.top = -1;
_showRect.right = -1;
_showRect.bottom = -1;
}
void Mouse::setHideRect(Common::Rect &r) {
_hideRect = r;
}
void Mouse::setShowRect(Common::Rect &r) {
_showRect = r;
}
const Common::Rect *Mouse::getHideRect() {
return &_hideRect;
}
const Common::Rect *Mouse::getShowRect() {
if ((_showRect.top == -1) || (_showRect.left == -1)) {
// Show rectangle uninitialised - set it to current screen dimensions
_showRect.top = 0;
_showRect.left = 0;
_showRect.right = _vm->_screen->width() - 1;
_showRect.bottom = _vm->_screen->height() -1;
}
return &_showRect;
}
void Mouse::handleEvent(Common::Event &event) {
_currentPos.x = event.mouse.x;
_currentPos.y = event.mouse.y;
// If mouse is turned on, check to see if the position is in the hide rect, or outside the show rect.
// If so, handle toggling the visibility of the mouse
bool showFlag = !inHideArea();
if (_cursorOn && (CursorMan.isVisible() != showFlag)) {
CursorMan.showMouse(showFlag);
}
}
bool Mouse::inHideArea() {
// Returns true if the mouse is inside a specified hide rect, or if a show rect is specified and
// the mouse is currently outside it
if ((_currentPos.x >= _hideRect.left) && (_currentPos.x <= _hideRect.right) &&
(_currentPos.y >= _hideRect.top) && (_currentPos.y <= _hideRect.bottom))
// Inside a hide area
return true;
if ((_showRect.top == -1) && (_showRect.left == -1))
// No show rect defined
return false;
// Return true if the mouse is outside the show area
return (_currentPos.x < _showRect.left) || (_currentPos.x > _showRect.right) ||
(_currentPos.y < _showRect.top) || (_currentPos.y > _showRect.bottom);
}
} // End of namespace M4

132
engines/m4/events.h Normal file
View file

@ -0,0 +1,132 @@
/* 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.
*
* $URL$
* $Id$
*
*/
#ifndef M4_EVENTS_H
#define M4_EVENTS_H
#include "common/events.h"
#include "common/rect.h"
#include "m4/globals.h"
#include "m4/assets.h"
#include "m4/sprite.h"
#include "m4/graphics.h"
#include "m4/console.h"
namespace M4 {
#define LEFT_BUTTON_DOWN 1 << 0
#define RIGHT_BUTTON_DOWN 1 << 1
enum M4EventType {
MEVENT_NO_EVENT, MEVENT_MOVE,
MEVENT_LEFT_CLICK, MEVENT_LEFT_HOLD, MEVENT_LEFT_DRAG, MEVENT_LEFT_RELEASE,
MEVENT_RIGHT_CLICK, MEVENT_RIGHT_HOLD, MEVENT_RIGHT_DRAG, MEVENT_RIGHT_RELEASE,
MEVENT_BOTH_CLICK, MEVENT_BOTH_HOLD, MEVENT_BOTH_DRAG, MEVENT_BOTH_RELEASE,
MEVENT_DOUBLECLICK, MEVENT_DOUBLECLICK_HOLD, MEVENT_DOUBLECLICK_DRAG, MEVENT_DOUBLECLICK_RELEASE,
KEVENT_KEY
};
enum M4MouseState {
MSTATE_NO_EVENT, MSTATE_LEFT_CLICK_DOWN, MSTATE_RIGHT_CLICK_DOWN, MSTATE_BOTH_CLICK_DOWN,
MSTATE_DOUBLECLICK_DOWN
};
enum M4CommonCursors {
CURSOR_ARROW = 0,
CURSOR_HOURGLASS = 5,
CURSOR_LOOK = 6,
CURSOR_TAKE = 8,
CURSOR_USE = 9
};
class M4Sprite;
class SpriteAsset;
class Events {
private:
M4Engine *_vm;
Common::Event _event;
M4MouseState _mouseState;
int _keyCode;
int _mouseButtons;
Console *_console;
public:
bool quitFlag;
Events(M4Engine *vm);
Common::Event &event() { return _event; }
Common::EventType type() { return _event.type; }
// M4-centric methods
M4EventType handleEvents();
bool kbdCheck(uint32 &keyCode);
int getMouseButtonsState() { return _mouseButtons; }
Console* getConsole() { return _console; }
};
class Mouse {
private:
M4Engine *_vm;
int _currentCursor, _lockedCursor;
bool _locked;
bool _cursorOn;
M4Sprite *_cursor;
SpriteAsset *_cursorSprites;
Common::Rect _hideRect, _showRect;
Common::Point _currentPos;
void handleEvent(Common::Event &event);
bool inHideArea();
friend class Events;
public:
Mouse(M4Engine *vm);
~Mouse();
bool init(const char *seriesName, RGB8 *palette);
bool setCursorNum(int cursorIndex);
int getCursorNum() { return _currentCursor; }
int cursorCount();
Common::Point currentPos() const { return _currentPos; }
M4Sprite *cursor() { return _cursor; }
void cursorOn();
void cursorOff();
bool getCursorOn() { return _cursorOn; }
void lockCursor(int cursorIndex);
void unlockCursor();
const char *getVerb();
void resetMouse();
void setHideRect(Common::Rect &r);
void setShowRect(Common::Rect &r);
const Common::Rect *getHideRect();
const Common::Rect *getShowRect();
};
}
#endif

267
engines/m4/font.cpp Normal file
View file

@ -0,0 +1,267 @@
/* 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.
*
* $URL$
* $Id$
*
*/
#include "m4/font.h"
#include "m4/m4.h"
#include "m4/compression.h"
namespace M4 {
Font::Font(M4Engine *vm) : _vm(vm) {
_sysFont = true;
_filename = NULL;
//TODO: System font
_fontColors[0] = _vm->_palette->BLACK;
_fontColors[1] = _vm->_palette->WHITE;
_fontColors[2] = _vm->_palette->BLACK;
_fontColors[3] = _vm->_palette->DARK_GRAY;
}
void Font::setFont(const char *filename) {
if ((_filename != NULL) && (strcmp(filename, _filename) == 0))
// Already using specified font, so don't bother reloading
return;
_sysFont = false;
_filename = filename;
if (_vm->isM4())
setFontM4(filename);
else
setFontMads(filename);
}
void Font::setFontM4(const char *filename) {
Common::SeekableReadStream *fontFile = _vm->res()->openFile(filename);
if (fontFile->readUint32LE() != MKID_BE('FONT')) {
printf("Font::Font: FONT tag expected\n");
return;
}
_maxHeight = fontFile->readByte();
_maxWidth = fontFile->readByte();
uint32 fontSize = fontFile->readUint32LE();
//printf("Font::Font: _maxWidth = %d, _maxHeight = %d, fontSize = %d\n", _maxWidth, _maxHeight, fontSize);
if (fontFile->readUint32LE() != MKID_BE('WIDT')) {
printf("Font::Font: WIDT tag expected\n");
return;
}
_charWidths = new uint8[256];
fontFile->read(_charWidths, 256);
if (fontFile->readUint32LE() != MKID_BE('OFFS')) {
printf("Font::Font: OFFS tag expected\n");
return;
}
_charOffs = new uint16[256];
for (int i = 0; i < 256; i++)
_charOffs[i] = fontFile->readUint16LE();
if (fontFile->readUint32LE() != MKID_BE('PIXS')) {
printf("Font::Font: PIXS tag expected\n");
return;
}
_charData = new uint8[fontSize];
fontFile->read(_charData, fontSize);
_vm->res()->toss(filename);
}
void Font::setFontMads(const char *filename) {
MadsPack fontData(filename, _vm);
Common::SeekableReadStream *fontFile = fontData.getItemStream(0);
_maxHeight = fontFile->readByte();
_maxWidth = fontFile->readByte();
_charWidths = new uint8[128];
// Char data is shifted by 1
_charWidths[0] = 0;
fontFile->read(_charWidths + 1, 127);
fontFile->readByte(); // remainder
_charOffs = new uint16[128];
uint32 startOffs = 2 + 128 + 256;
uint32 fontSize = fontFile->size() - startOffs;
// Char data is shifted by 1
_charOffs[0] = 0;
for (int i = 1; i < 128; i++)
_charOffs[i] = fontFile->readUint16LE() - startOffs;
fontFile->readUint16LE(); // remainder
_charData = new uint8[fontSize];
fontFile->read(_charData, fontSize);
delete fontFile;
}
Font::~Font() {
if (!_sysFont) {
delete[] _charWidths;
delete[] _charOffs;
delete[] _charData;
}
}
void Font::setColor(uint8 color) {
if (_sysFont)
_fontColors[1] = color;
else
_fontColors[3] = color;
}
void Font::setColors(uint8 alt1, uint8 alt2, uint8 foreground) {
if (_sysFont)
_fontColors[1] = foreground;
else {
_fontColors[1] = alt1;
_fontColors[2] = alt2;
_fontColors[3] = foreground;
}
}
int32 Font::write(M4Surface *surface, const char *text, int x, int y, int width, int spaceWidth, uint8 colors[]) {
/*TODO
if (custom_ascii_converter) { // if there is a function to convert the extended ASCII characters
custom_ascii_converter(out_string); // call it with the string
}
*/
if (width > 0)
width = MIN(surface->width(), x + width);
else
width = surface->width();
x++;
y++;
int skipY = 0;
if (y < 0) {
skipY = -y;
y = 0;
}
int height = MAX(0, _maxHeight - skipY);
if (height == 0)
return x;
int bottom = y + height - 1;
if (bottom > surface->height() - 1) {
height -= MIN(height, bottom - (surface->height() - 1));
}
if (height <= 0)
return x;
uint8 *destPtr = (uint8*)surface->getBasePtr(x, y);
uint8 *oldDestPtr = destPtr;
int xPos = x;
while (*text) {
char theChar = (*text++) & 0x7F;
int charWidth = _charWidths[theChar];
if (charWidth > 0) {
if (xPos + charWidth >= width)
return xPos;
uint8 *charData = &_charData[_charOffs[theChar]];
int bpp = charWidth / 4 + 1;
if (!_vm->isM4()) {
if (charWidth > 12)
bpp = 4;
else if (charWidth > 8)
bpp = 3;
else if (charWidth > 4)
bpp = 2;
else
bpp = 1;
}
if (skipY != 0)
charData += bpp * skipY;
for (int i = 0; i < height; i++) {
for (int j = 0; j < bpp; j++) {
if (*charData & 0xc0)
*destPtr = colors[(*charData & 0xc0) >> 6];
destPtr++;
if (*charData & 0x30)
*destPtr = colors[(*charData & 0x30) >> 4];
destPtr++;
if (*charData & 0x0C)
*destPtr = colors[(*charData & 0x0C) >> 2];
destPtr++;
if (*charData & 0x03)
*destPtr = colors[*charData & 0x03];
destPtr++;
charData++;
}
destPtr += surface->width() - bpp * 4;
}
destPtr = oldDestPtr + charWidth + spaceWidth;
oldDestPtr = destPtr;
}
xPos += charWidth + spaceWidth;
}
surface->freeData();
return xPos;
}
int32 Font::getWidth(char *text, int spaceWidth) {
/*
if (custom_ascii_converter) { // if there is a function to convert the extended ASCII characters
custom_ascii_converter(out_string); // call it with the string
}
*/
int width = 0;
while (*text)
width += _charWidths[*text++ & 0x7F] + spaceWidth;
return width;
}
} // End of namespace M4

91
engines/m4/font.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 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#ifndef M4_FONT_H
#define M4_FONT_H
#include "common/util.h"
#include "common/endian.h"
#include "m4/graphics.h"
/*
TODO:
- make a FontSystem class that creates/manages the fonts
(similar to FileSystem)
*/
namespace M4 {
#define FONT_MENU "fontmenu.fnt"
#define FONT_INTERFACE "fontintr.fnt"
#define FONT_TINY "small.fnt"
#define FONT_SMALL "small.fnt"
#define FONT_MEDIUM "medium.fnt"
#define FONT_LINE "fontline.fnt"
#define FONT_CONVERSATION "fontconv.fnt"
#define FONT_4X6 "4x6pp.fnt"
#define FONT_5X6 "5x6pp.fnt"
#define FONT_CONVERSATION_MADS "fontconv.ff"
#define FONT_INTERFACE_MADS "fontintr.ff"
#define FONT_MAIN_MADS "fontmain.ff"
#define FONT_MENU_MADS "fontmenu.ff" // Not in Rex (uses bitmap files for menu strings)
#define FONT_MISC_MADS "fontmisc.ff"
#define FONT_TELE_MADS "fonttele.ff" // Not in Phantom
#define FONT_PHAN_MADS "fontphan.ff" // Phantom only
class Font {
public:
Font(M4Engine *vm);
~Font();
void setFont(const char *filename);
void setColor(uint8 color);
void setColors(uint8 alt1, uint8 alt2, uint8 foreground);
int32 getWidth(char *text, int spaceWidth = -1);
int32 getHeight() const { return _maxHeight; }
int32 write(M4Surface *surface, const char *text, int x, int y, int width, int spaceWidth, uint8 colors[]);
int32 writeString(M4Surface *surface, const char *text, int x, int y, int width = 0, int spaceWidth = -1) {
return write(surface, text, x, y, width, spaceWidth, _fontColors);
}
private:
void setFontM4(const char *filename);
void setFontMads(const char *filename);
M4Engine *_vm;
uint8 _maxWidth, _maxHeight;
uint8 *_charWidths;
uint16 *_charOffs;
uint8 *_charData;
bool _sysFont;
const char *_filename;
uint8 _fontColors[4];
};
} // End of namespace M4
#endif

447
engines/m4/globals.cpp Normal file
View file

@ -0,0 +1,447 @@
/* 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.
*
* $URL$
* $Id$
*
*/
#include "m4/m4.h"
#include "m4/globals.h"
#include "m4/graphics.h"
#include "m4/gui.h"
#include "m4/viewmgr.h"
#include "m4/script.h"
#include "m4/m4_views.h"
#include "m4/compression.h"
namespace M4 {
Kernel::Kernel(M4Engine *vm) : _vm(vm) {
daemonTriggerAvailable = true;
firstFadeColorIndex = 0;
paused = false;
betweenRooms = false;
currentSection = 0;
newSection = 0;
previousSection = 0;
currentRoom = 0;
newRoom = 0;
previousRoom = 0;
trigger = 0;
triggerMode = KT_DAEMON;
_globalDaemonFn = NULL;
_globalParserFn = NULL;
_sectionInitFn = NULL;
_sectionDaemonFn = NULL;
_sectionParserFn = NULL;
_roomInitFn = NULL;
_roomDaemonFn = NULL;
_roomPreParserFn = NULL;
_roomParserFn = NULL;
}
int32 Kernel::createTrigger(int32 triggerNum) {
if (triggerNum < 0)
return triggerNum;
else
return triggerNum | (currentRoom << 16) | (triggerMode << 28);
}
bool Kernel::sendTrigger(int32 triggerNum) {
return handleTrigger(createTrigger(triggerNum));
}
bool Kernel::handleTrigger(int32 triggerNum) {
printf("betweenRooms = %d; triggerNum = %08X\n", betweenRooms, triggerNum);
if (betweenRooms)
return true;
if (triggerNum < 0)
return false;
KernelTriggerType saveTriggerMode = triggerMode;
int32 saveTrigger = trigger;
bool result = false;
int room = (triggerNum >> 16) & 0xFFF;
printf("room = %d; currentRoom = %d\n", room, currentRoom); fflush(stdout);
if (room != currentRoom) {
printf("Kernel::handleTrigger() Trigger from another room\n");
return false;
}
trigger = triggerNum & 0xFFFF;
KernelTriggerType mode = (KernelTriggerType)(triggerNum >> 28);
switch (mode) {
case KT_PREPARSE:
if (trigger < 32000) {
triggerMode = KT_PREPARSE;
roomPreParser();
result = true;
}
break;
case KT_PARSE:
if (trigger < 32000) {
triggerMode = KT_PARSE;
// TODO player.commandReady = TRUE;
roomParser();
/* TODO
if (player.commandReady)
globalParser();
*/
result = true;
}
break;
case KT_DAEMON:
printf("KT_DAEMON\n");
fflush(stdout);
triggerMode = KT_DAEMON;
daemonTriggerAvailable = false;
roomDaemon();
if (daemonTriggerAvailable) {
daemonTriggerAvailable = false;
sectionDaemon();
}
if (daemonTriggerAvailable) {
daemonTriggerAvailable = false;
globalDaemon();
}
break;
default:
printf("Kernel::handleTrigger() Unknown trigger mode %d\n", mode);
}
triggerMode = saveTriggerMode;
trigger = saveTrigger;
return result;
}
void Kernel::loadGlobalScriptFunctions() {
_globalDaemonFn = _vm->_script->loadFunction("global_daemon");
_globalParserFn = _vm->_script->loadFunction("global_parser");
}
void Kernel::loadSectionScriptFunctions() {
char tempFnName[128];
snprintf(tempFnName, 128, "section_init_%d", currentSection);
_sectionInitFn = _vm->_script->loadFunction(tempFnName);
snprintf(tempFnName, 128, "section_daemon_%d", currentSection);
_sectionDaemonFn = _vm->_script->loadFunction(tempFnName);
snprintf(tempFnName, 128, "section_parser_%d", currentSection);
_sectionParserFn = _vm->_script->loadFunction(tempFnName);
}
void Kernel::loadRoomScriptFunctions() {
char tempFnName[128];
snprintf(tempFnName, 128, "room_init_%d", currentRoom);
_roomInitFn = _vm->_script->loadFunction(tempFnName);
snprintf(tempFnName, 128, "room_daemon_%d", currentRoom);
_roomDaemonFn = _vm->_script->loadFunction(tempFnName);
snprintf(tempFnName, 128, "room_pre_parser_%d", currentRoom);
_roomPreParserFn = _vm->_script->loadFunction(tempFnName);
snprintf(tempFnName, 128, "room_parser_%d", currentRoom);
_roomParserFn = _vm->_script->loadFunction(tempFnName);
}
void Kernel::globalDaemon() {
if (_globalDaemonFn)
_vm->_script->runFunction(_globalDaemonFn);
else {
printf("Kernel::globalDaemon() _globalDaemonFn is NULL\n");
}
}
void Kernel::globalParser() {
if (_globalParserFn)
_vm->_script->runFunction(_globalParserFn);
else {
printf("Kernel::globalParser() _globalParserFn is NULL\n");
}
}
void Kernel::sectionInit() {
if (_sectionInitFn)
_vm->_script->runFunction(_sectionInitFn);
else {
printf("Kernel::sectionInit() _sectionInitFn is NULL\n");
}
}
void Kernel::sectionDaemon() {
if (_sectionDaemonFn)
_vm->_script->runFunction(_sectionDaemonFn);
else {
printf("Kernel::sectionDaemon() _sectionDaemonFn is NULL\n");
}
}
void Kernel::sectionParser() {
if (_sectionParserFn)
_vm->_script->runFunction(_sectionParserFn);
else {
printf("Kernel::sectionParser() _sectionParserFn is NULL\n");
}
}
void Kernel::roomInit() {
if (_roomInitFn)
_vm->_script->runFunction(_roomInitFn);
else {
printf("Kernel::roomInit() _roomInitFn is NULL\n");
}
}
void Kernel::roomDaemon() {
if (_roomDaemonFn)
_vm->_script->runFunction(_roomDaemonFn);
else {
printf("Kernel::roomDaemon() _roomDaemonFn is NULL\n");
}
}
void Kernel::roomPreParser() {
if (_roomPreParserFn)
_vm->_script->runFunction(_roomPreParserFn);
else {
printf("Kernel::roomPreParser() _roomPreParserFn is NULL\n");
}
}
void Kernel::roomParser() {
if (_roomParserFn)
_vm->_script->runFunction(_roomParserFn);
else {
printf("Kernel::roomParser() _roomParserFn is NULL\n");
}
}
void Kernel::pauseGame(bool value) {
paused = value;
if (paused) pauseEngines();
else unpauseEngines();
}
void Kernel::pauseEngines() {
// TODO: A proper implementation of game pausing. At the moment I'm using a hard-coded
// check in events.cpp on Kernel::paused to prevent any events going to the scene
}
void Kernel::unpauseEngines() {
// TODO: A proper implementation of game unpausing
}
//--------------------------------------------------------------------------
Globals::Globals(M4Engine *vm): _vm(vm) {
}
Globals::~Globals() {
for(uint32 i = 0; i < _madsVocab.size(); i++)
free(_madsVocab[i]);
_madsVocab.clear();
for(uint32 i = 0; i < _madsQuotes.size(); i++)
free(_madsQuotes[i]);
_madsQuotes.clear();
_madsMessages.clear();
}
bool Globals::isInterfaceVisible() {
return _vm->_interfaceView->isVisible();
}
void Globals::loadMadsVocab() {
Common::SeekableReadStream *vocabS = _vm->res()->get("vocab.dat");
int curPos = 0;
char buffer[30];
strcpy(buffer, "");
while(!vocabS->eos()) {
buffer[curPos++] = vocabS->readByte();
if (buffer[curPos - 1] == '\0') {
// end of string, add it to the strings list
_madsVocab.push_back(strdup(buffer));
curPos = 0;
strcpy(buffer, "");
}
}
_vm->res()->toss("vocab.dat");
}
void Globals::loadMadsQuotes() {
Common::SeekableReadStream *quoteS = _vm->res()->get("quotes.dat");
int curPos = 0;
char buffer[128];
strcpy(buffer, "");
while(!quoteS->eos()) {
buffer[curPos++] = quoteS->readByte();
if (buffer[curPos - 1] == '\0') {
// end of string, add it to the strings list
_madsQuotes.push_back(strdup(buffer));
curPos = 0;
strcpy(buffer, "");
}
}
_vm->res()->toss("quotes.dat");
}
void Globals::loadMadsMessagesInfo() {
Common::SeekableReadStream *messageS = _vm->res()->get("messages.dat");
int16 count = messageS->readUint16LE();
//printf("%i messages\n", count);
for (int i = 0; i < count; i++) {
MessageItem *curMessage = new MessageItem();
curMessage->id = messageS->readUint32LE();
curMessage->offset = messageS->readUint32LE();
curMessage->uncompSize = messageS->readUint16LE();
if (i > 0)
_madsMessages[i - 1]->compSize = curMessage->offset - _madsMessages[i - 1]->offset;
if (i == count - 1)
curMessage->compSize = messageS->size() - curMessage->offset;
//printf("id: %i, offset: %i, uncomp size: %i\n", curMessage->id, curMessage->offset, curMessage->uncompSize);
_madsMessages.push_back(curMessage);
}
_vm->res()->toss("messages.dat");
}
char* Globals::loadMessage(uint32 index) {
if (index > _madsMessages.size() - 1) {
warning("Invalid message index: %i", index);
return NULL;
}
FabDecompressor fab;
byte *compData = new byte[_madsMessages[index]->compSize];
byte *buffer = new byte[_madsMessages[index]->uncompSize];
Common::SeekableReadStream *messageS = _vm->res()->get("messages.dat");
messageS->seek(_madsMessages[index]->offset, SEEK_SET);
messageS->read(compData, _madsMessages[index]->compSize);
fab.decompress(compData, _madsMessages[index]->compSize, buffer, _madsMessages[index]->uncompSize);
for (int i = 0; i < _madsMessages[index]->uncompSize - 1; i++)
if (buffer[i] == '\0') buffer[i] = '\n';
_vm->res()->toss("messages.dat");
return (char*)buffer;
}
//--------------------------------------------------------------------------
Player::Player(M4Engine *vm) : _vm(vm) {
commandsAllowed = true;
needToWalk = false;
readyToWalk = false;
waitingForWalk = false;
commandReady = false;
strcpy(verb, "");
strcpy(noun, "");
strcpy(prep, "");
strcpy(object, "");
}
void Player::setCommandsAllowed(bool value) {
setCommandsAllowedFlag = true;
commandsAllowed = value;
if (value) {
// Player commands are enabled again
_vm->_mouse->lockCursor(CURSOR_ARROW);
//_vm->_interfaceView->cancelSentence();
} else {
// Player commands are disabled, so show hourglass cursor
_vm->_mouse->lockCursor(CURSOR_HOURGLASS);
}
}
bool Player::said(const char *word1, const char *word2, const char *word3) {
const char *words[3];
words[0] = word1;
words[1] = word2;
words[2] = word2;
for (int i = 0; i < 3; i++) {
if (words[i])
if ((scumm_stricmp(noun, words[i])) &&
(scumm_stricmp(object, words[i])) &&
(scumm_stricmp(verb, words[i])))
return false;
}
return true;
}
bool Player::saidAny(const char *word1, const char *word2, const char *word3,
const char *word4, const char *word5, const char *word6, const char *word7,
const char *word8, const char *word9, const char *word10) {
const char *words[10];
words[0] = word1;
words[1] = word2;
words[2] = word3;
words[3] = word4;
words[4] = word5;
words[5] = word6;
words[6] = word7;
words[7] = word8;
words[8] = word9;
words[9] = word10;
for (int i = 0; i < 10; i++) {
if (words[i]) {
if (!scumm_stricmp(noun, words[i]))
return true;
if (!scumm_stricmp(object, words[i]))
return true;
if (!scumm_stricmp(verb, words[i]))
return true;
}
}
return false;
}
} // End of namespace M4

221
engines/m4/globals.h 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 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#ifndef M4_GLOBALS_H
#define M4_GLOBALS_H
#include "common/array.h"
#include "common/rect.h"
#include "common/scummsys.h"
namespace M4 {
class M4Engine;
class ScriptInterpreter;
class ScriptFunction;
// Globals
enum WoodScriptGlobals {
kGlobTime = 0,
kGlobTimeDelta = 1,
kGlobMinY = 2,
kGlobMaxY = 3,
kGlobMinScale = 4,
kGlobMaxScale = 5,
kGlobScaler = 6,
kGlobTemp1 = 7,
kGlobTemp2 = 8,
kGlobTemp3 = 9,
kGlobTemp4 = 10,
kGlobTemp5 = 11,
kGlobTemp6 = 12,
kGlobTemp7 = 13,
kGlobTemp8 = 14,
kGlobTemp9 = 15,
kGlobTemp10 = 16,
kGlobTemp11 = 17,
kGlobTemp12 = 18,
kGlobTemp13 = 19,
kGlobTemp14 = 20,
kGlobTemp15 = 21,
kGlobTemp16 = 22,
kGlobTemp17 = 23,
kGlobTemp18 = 24,
kGlobTemp19 = 25,
kGlobTemp20 = 26,
kGlobTemp21 = 27,
kGlobTemp22 = 28,
kGlobTemp23 = 29,
kGlobTemp24 = 30,
kGlobTemp25 = 31,
kGlobTemp26 = 32,
kGlobTemp27 = 33,
kGlobTemp28 = 34,
kGlobTemp29 = 35,
kGlobTemp30 = 36,
kGlobTemp31 = 37,
kGlobTemp32 = 38
};
const uint32 SERIES_FORWARD = 0;
const uint32 SERIES_PINGPONG = 1;
const uint32 SERIES_BACKWARD = 2;
const uint32 SERIES_RANDOM = 4;
const uint32 SERIES_NO_TOSS = 8;
const uint32 SERIES_STICK = 16;
const uint32 SERIES_LOOP_TRIGGER = 32;
const uint32 SERIES_LOAD_PALETTE = 64;
const uint32 SERIES_HORZ_FLIP =128;
enum KernelTriggerType {
KT_PARSE = 1,
KT_DAEMON,
KT_PREPARSE
};
class Kernel {
private:
M4Engine *_vm;
ScriptFunction *_globalDaemonFn, *_globalParserFn;
ScriptFunction *_sectionInitFn, *_sectionDaemonFn, *_sectionParserFn;
ScriptFunction *_roomInitFn, *_roomDaemonFn, *_roomPreParserFn, *_roomParserFn;
void pauseEngines();
void unpauseEngines();
public:
Kernel(M4Engine *vm);
// TODO: Move to some palette/fading class
int fadeUpDuration, firstFadeColorIndex;
int minPalEntry, maxPalEntry;
bool paused;
//machine* myWalker;
bool repeatedlyCallDeamon;
bool daemonTriggerAvailable;
bool betweenRooms;
int currentSection, newSection, previousSection;
int currentRoom, newRoom, previousRoom;
int32 trigger;
KernelTriggerType triggerMode;
int32 createTrigger(int32 triggerNum);
bool sendTrigger(int32 triggerNum);
bool handleTrigger(int32 triggerNum);
void loadGlobalScriptFunctions();
void loadSectionScriptFunctions();
void loadRoomScriptFunctions();
void globalDaemon();
void globalParser();
void sectionInit();
void sectionDaemon();
void sectionParser();
void roomInit();
void roomDaemon();
void roomPreParser();
void roomParser();
void pauseGame(bool value);
};
#define TOTAL_NUM_VARIABLES 256
class Globals {
private:
struct MessageItem {
uint32 id;
uint32 offset;
uint16 uncompSize;
uint16 compSize;
};
M4Engine *_vm;
Common::Array<char* > _madsVocab;
Common::Array<char* > _madsQuotes;
Common::Array<MessageItem* > _madsMessages;
public:
Globals(M4Engine *vm);
~Globals();
bool isInterfaceVisible();
// M4 variables
bool invSuppressClickSound;
void loadMadsVocab();
uint32 getVocabSize() { return _madsVocab.size(); }
char* getVocab(uint32 index) { return _madsVocab[index]; }
void loadMadsQuotes();
uint32 getQuotesSize() { return _madsQuotes.size(); }
char* getQuote(uint32 index) { return _madsQuotes[index]; }
void loadMadsMessagesInfo();
uint32 getMessagesSize() { return _madsMessages.size(); }
char* loadMessage(uint32 index);
};
#define PLAYER_FIELD_LENGTH 40
class Player {
public:
Player(M4Engine *vm);
void setCommandsAllowed(bool value);
// Variables
Common::Point position; // Player's current position
int facing; // Facing direction
char verb[PLAYER_FIELD_LENGTH]; // Action strings
char noun[PLAYER_FIELD_LENGTH];
char prep[PLAYER_FIELD_LENGTH];
char object[PLAYER_FIELD_LENGTH];
Common::String assetName, shadowName;
int walkerType, shadowType;
bool needToWalk, readyToWalk, waitingForWalk;
bool commandsAllowed;
bool commandReady;
bool visible;
bool beenInRoomBefore;
bool walkerInCurrentRoom;
int32 walkerTriggerNum;
int walkFacing;
bool setCommandsAllowedFlag;
bool said(const char *word1, const char *word2 = NULL, const char *word3 = NULL);
bool saidAny(const char *word1, const char *word2, const char *word3,
const char *word4, const char *word5, const char *word6, const char *word7,
const char *word8, const char *word9, const char *word10);
private:
M4Engine *_vm;
};
} // End of namespace M4
#endif

1074
engines/m4/graphics.cpp Normal file

File diff suppressed because it is too large Load diff

222
engines/m4/graphics.h Normal file
View file

@ -0,0 +1,222 @@
/* 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.
*
* $URL$
* $Id$
*
*/
#ifndef M4_GRAPHICS_H
#define M4_GRAPHICS_H
#include "common/rect.h"
#include "common/system.h"
#include "common/stream.h"
#include "graphics/surface.h"
#include "m4/globals.h"
namespace M4 {
struct BGR8 {
uint8 b, g, r;
};
struct RGB8 {
uint8 r, g, b, u;
};
//later use ScummVM's Rect?
struct M4Rect {
int32 x1, y1, x2, y2;
};
class M4Surface;
// RGBList
// Implements a list of RGB entries
class RGBList {
private:
int _size;
RGB8 *_data;
byte *_palIndexes;
bool _freeData;
public:
RGBList(int numEntries = 256, RGB8 *srcData = NULL, bool freeData = true);
~RGBList();
RGB8 *data() { return _data; }
byte *palIndexes() { return _palIndexes; }
int size() { return _size; }
};
// M4Surface
// Class representing either a buffered surface or the physical screen.
class M4Sprite;
struct SpriteInfo {
M4Sprite *sprite;
int hotX, hotY;
int width, height;
int scaleX, scaleY;
uint8 encoding;
byte *inverseColorTable;
RGB8 *palette;
};
class M4Surface : public Graphics::Surface {
private:
byte _color;
bool _isScreen;
void rexLoadBackground(Common::SeekableReadStream *source, RGBList **palData = NULL);
void madsLoadBackground(int roomNumber, RGBList **palData = NULL);
void m4LoadBackground(Common::SeekableReadStream *source);
public:
M4Surface(bool isScreen = false) {
create(g_system->getWidth(), g_system->getHeight(), 1);
_isScreen = isScreen;
}
M4Surface(int Width, int Height) { create(Width, Height, 1); _isScreen = false; }
// loads a .COD file into the M4Surface
// TODO: maybe move this to the rail system? check where it makes sense
// The sprite drawing needs this, too, so should be more global.
void loadCodesM4(Common::SeekableReadStream *source);
void loadCodesMads(Common::SeekableReadStream *source);
// loads the specified background
void loadBackground(int sceneNumber, RGBList **palData = NULL);
void loadBackgroundRiddle(const char *sceneName);
void madsloadInterface(int index, RGBList **palData);
void setColor(byte value) { _color = value; }
byte getColor() { return _color; }
void vLine(int x, int y1, int y2);
void hLine(int x1, int x2, int y);
void vLineXor(int x, int y1, int y2);
void hLineXor(int x1, int x2, int y);
void line(int x1, int y1, int x2, int y2, byte color);
void frameRect(int x1, int y1, int x2, int y2);
void fillRect(int x1, int y1, int x2, int y2);
void drawSprite(int x, int y, SpriteInfo &info, const Common::Rect &clipRect);
// Surface methods
int width() { return w; }
int height() { return h; }
void setSize(int sizeX, int sizeY) { create(sizeX, sizeY, 1); }
byte *getData();
byte *getBasePtr(int x, int y);
void freeData();
void empty();
void frameRect(const Common::Rect &r, uint8 color);
void fillRect(const Common::Rect &r, uint8 color);
void copyFrom(M4Surface *src, const Common::Rect &srcBounds, int destX, int destY,
int transparentColor = -1);
void update() {
if (_isScreen) {
g_system->copyRectToScreen((const byte *)pixels, pitch, 0, 0, w, h);
g_system->updateScreen();
}
}
// copyTo methods
void copyTo(M4Surface *dest, int transparentColor = -1) {
dest->copyFrom(this, Common::Rect(width(), height()), 0, 0, transparentColor);
}
void copyTo(M4Surface *dest, int x, int y, int transparentColor = -1) {
dest->copyFrom(this, Common::Rect(width(), height()), x, y, transparentColor);
}
void copyTo(M4Surface *dest, const Common::Rect &srcBounds, int destX, int destY,
int transparentColor = -1) {
dest->copyFrom(this, srcBounds, destX, destY, transparentColor);
}
void translate(RGBList *list, bool isTransparent = false);
};
enum FadeType {FT_TO_GREY, FT_TO_COLOR, FT_TO_BLOCK};
class Palette {
private:
M4Engine *_vm;
bool _colorsChanged;
bool _fading_in_progress;
byte _originalPalette[256 * 4];
byte _fadedPalette[256 * 4];
int _usageCount[256];
void reset();
public:
Palette(M4Engine *vm);
void setPalette(const byte *colors, uint start, uint num);
void setPalette(const RGB8 *colors, uint start, uint num);
void grabPalette(byte *colors, uint start, uint num);
void grabPalette(RGB8 *colors, uint start, uint num) {
grabPalette((byte *)colors, start, num);
}
uint8 palIndexFromRgb(byte r, byte g, byte b, RGB8 *paletteData = NULL);
void fadeToGreen(int numSteps, uint delayAmount);
void fadeFromGreen(int numSteps, uint delayAmount, bool fadeToBlack);
void fadeIn(int numSteps, uint delayAmount, RGB8 *destPalette, int numColors);
void fadeIn(int numSteps, uint delayAmount, RGBList *destPalette);
static RGB8 *decodeMadsPalette(Common::SeekableReadStream *palStream, int *numColors);
int setMadsPalette(Common::SeekableReadStream *palStream, int indexStart = 0);
void setMadsSystemPalette();
// Methods used for reference counting color usage
void resetColorCounts();
void blockRange(int startIndex, int size);
void addRange(RGBList *list);
void deleteRange(RGBList *list);
void deleteAllRanges();
// Color indexes
uint8 BLACK;
uint8 BLUE;
uint8 GREEN;
uint8 CYAN;
uint8 RED;
uint8 VIOLET;
uint8 BROWN;
uint8 LIGHT_GRAY;
uint8 DARK_GRAY;
uint8 LIGHT_BLUE;
uint8 LIGHT_GREEN;
uint8 LIGHT_CYAN;
uint8 LIGHT_RED;
uint8 PINK;
uint8 YELLOW;
uint8 WHITE;
};
void decompressRle(byte *rleData, int rleSize, byte *celData, int w, int h);
void decompressRle(Common::SeekableReadStream &rleData, byte *celData, int w, int h);
int scaleValue(int value, int scale, int err);
} // End of namespace M4
#endif

1217
engines/m4/gui.cpp Normal file

File diff suppressed because it is too large Load diff

446
engines/m4/gui.h Normal file
View file

@ -0,0 +1,446 @@
/* 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.
*
* $URL$
* $Id$
*
*/
#ifndef M4_GUI_H
#define M4_GUI_H
#include "common/list.h"
#include "common/rect.h"
#include "m4/viewmgr.h"
#include "m4/events.h"
#include "m4/globals.h"
#include "m4/graphics.h"
#include "m4/saveload.h"
#include "m4/sprite.h"
#include "m4/assets.h"
namespace M4 {
class GUIObject;
class MenuObject;
class GUIObjectButton;
class SpriteAsset;
class View;
enum MenuType {GAME_MENU, OPTIONS_MENU, SAVE_MENU, LOAD_MENU, MAIN_MENU};
enum {
BTNID_QUIT = 1,
BTNID_OPTIONS = 2,
BTNID_RESUME = 3,
BTNID_SAVE = 4,
BTNID_LOAD = 5,
BTNID_MAIN = 6,
OPTIONID_DONE = 1,
OPTIONID_CANCEL = 2,
OPTIONID_HSLIDER_DIGI = 3,
OPTIONID_HSLIDER_MIDI = 4,
SAVELOADID_SAVE = 1,
SAVELOADID_CANCELSAVE = 2,
SAVELOADID_LOAD = 3,
SAVELOADID_CANCELLOAD = 4,
SLTAG_SAVELOAD = 100,
SLTAG_SAVELOAD_LABEL = 101,
SLTAG_CANCEL = 102,
SLTAG_VSLIDER = 103,
SLTAG_THUMBNAIL = 104,
SLTAG_SLOTS_START = 1001,
SLTAG_TEXTFIELD = 2000
};
enum ObjectType {
OBJTYPE_BUTTON,
OBJTYPE_SLIDER,
OBJTYPE_OM_SWITCH_ON,
OBJTYPE_OM_SWITCH_OFF,
OBJTYPE_OM_DONE,
OBJTYPE_OM_CANCEL,
OBJTYPE_SL_SAVE,
OBJTYPE_SL_LOAD,
OBJTYPE_SL_CANCEL,
OBJTYPE_SL_TEXT
};
enum GameMenuSpriteType {
GM_DIALOG_BOX,
GM_BUTTON_GREYED,
GM_BUTTON_NORMAL,
GM_BUTTON_MOUSEOVER,
GM_BUTTON_PRESSED
};
enum OptionMenuSpriteType {
OM_DIALOG_BOX,
OM_SLIDER_BTN_NORMAL,
OM_SLIDER_BTN_MOUSEOVER,
OM_SLIDER_BTN_PRESSED,
OM_SLIDER_BAR,
OM_DONE_BTN_GREYED,
OM_DONE_BTN_NORMAL,
OM_DONE_BTN_MOUSEOVER,
OM_DONE_BTN_PRESSED,
OM_CANCEL_BTN_NORMAL,
OM_CANCEL_BTN_MOUSEOVER,
OM_CANCEL_BTN_PRESSED
};
enum SaveLoadSpriteType {
SL_DIALOG_BOX,
SL_EMPTY_THUMBNAIL,
SL_SAVE_BTN_GREYED,
SL_SAVE_BTN_NORMAL,
SL_SAVE_BTN_MOUSEOVER,
SL_SAVE_BTN_PRESSED,
SL_LOAD_BTN_GREYED,
SL_LOAD_BTN_NORMAL,
SL_LOAD_BTN_MOUSEOVER,
SL_LOAD_BTN_PRESSED,
SL_CANCEL_BTN_NORMAL,
SL_CANCEL_BTN_MOUSEOVER,
SL_CANCEL_BTN_PRESSED,
SL_UP_BTN_GREYED,
SL_UP_BTN_NORMAL,
SL_UP_BTN_MOUSEOVER,
SL_UP_BTN_PRESSED,
SL_DOWN_BTN_GREYED,
SL_DOWN_BTN_NORMAL,
SL_DOWN_BTN_MOUSEOVER,
SL_DOWN_BTN_PRESSED,
SL_SAVE_LABEL,
SL_LOAD_LABEL,
SL_SLIDER_BTN_NORMAL,
SL_SLIDER_BTN_MOUSEOVER,
SL_SLIDER_BTN_PRESSED,
SL_LINE_NORMAL,
SL_LINE_MOUSEOVER,
SL_LINE_PRESSED,
SL_SCROLLBAR
};
enum TextColors {
TEXT_COLOR_NORMAL = 1,
TEXT_COLOR_GREYED = 1,
TEXT_COLOR_MOUSEOVER = 2,
TEXT_COLOR_PRESSED = 2,
TEXT_COLOR_GREYED_HILIGHT = 236,
TEXT_COLOR_GREYED_FOREGROUND = 131,
TEXT_COLOR_GREYED_SHADOW = 186,
TEXT_COLOR_NORMAL_HILIGHT = 129,
TEXT_COLOR_NORMAL_FOREGROUND = 130,
TEXT_COLOR_NORMAL_SHADOW = 236,
TEXT_COLOR_MOUSEOVER_HILIGHT = 129,
TEXT_COLOR_MOUSEOVER_FOREGROUND = 130,
TEXT_COLOR_MOUSEOVER_SHADOW = 236,
TEXT_COLOR_PRESSED_HILIGHT = 236,
TEXT_COLOR_PRESSED_FOREGROUND = 130,
TEXT_COLOR_PRESSED_SHADOW = 129
};
#define MENU_SS_FIELD_NORMAL 5
#define MENU_SS_FIELD_MOUSEOVER 6
#define MENU_SS_H_SLIDER_NORMAL 5
#define MENU_SS_H_SLIDER_MOUSE_OVER 6
#define MENU_SS_H_SLIDER_PRESSED 7
#define MENU_SS_SWITCH_ON_NORMAL 8
#define MENU_SS_SWITCH_ON_MOUSEOVER 9
#define MENU_SS_SWITCH_ON_PRESSED 13
#define MENU_SS_SWITCH_OFF_PRESSED 10
#define MENU_SS_SWITCH_OFF_NORMAL 11
#define MENU_SS_SWITCH_OFF_MOUSEOVER 12
#define MENU_GUI "gui menu"
#define MENU_GAME "gamemenu"
#define MENU_OPTIONS "opmenu"
#define MENU_ERROR "errmenu"
#define MENU_SAVELOAD "slmenu"
#define MENU_BURGMAIN "903menu"
#define MENU_BURGDEMOMAIN "901menu"
#define SL_NUM_VISIBLE_SLOTS 8
#define SL_THUMBNAIL_WIDTH 215
#define SL_THUMBNAIL_HEIGHT 162
enum MenuObjectState {OS_GREYED = 0, OS_NORMAL = 1, OS_MOUSEOVER = 2, OS_PRESSED = 3};
class DialogView: public View {
public:
DialogView(M4Engine *Vm, const Common::Rect &viewBounds, bool transparent = false):
View(Vm, viewBounds, transparent) {};
DialogView(M4Engine *Vm, int x = 0, int y = 0, bool transparent = false):
View(Vm, x, y, transparent) {};
M4Engine *vm() { return _vm; }
virtual SpriteAsset *sprites() = 0;
virtual MenuType getMenuType() = 0;
virtual MenuObject *getItem(int objectId) { return NULL; }
virtual void refresh(const Common::Rect &areaRect) {}
virtual void close();
int _topSaveSlotIndex, _selectedSlot;
int _highlightedSlot;
bool _deleteSaveDesc;
M4Surface *_savegameThumbnail;
};
class GUIObject {
protected:
View *_parent;
Common::Rect _bounds;
M4Surface *_background;
public:
GUIObject(View *owner, const Common::Rect &bounds);
virtual ~GUIObject() {}
bool isInside(int x, int y) { return _bounds.contains(x, y); }
Common::Rect getBounds() const { return _bounds; }
virtual void onRefresh() {}
};
class MenuObject: public GUIObject {
public:
typedef void (*Callback)(DialogView *view, MenuObject *item);
protected:
MenuObject::Callback _callback;
ObjectType _objectType;
MenuObjectState _objectState;
bool _transparent;
int _objectId;
public:
MenuObject(DialogView *owner, int objectId, int xs, int ys, int width, int height,
bool greyed = false, bool transparent = false);
~MenuObject();
DialogView *parent() { return (DialogView *)_parent; }
MenuObjectState getState() { return _objectState; }
virtual void setState(MenuObjectState state) {
_objectState = state;
onRefresh();
}
int getObjectId() { return _objectId; }
void onExecute();
virtual bool onEvent(M4EventType event, int param, int x, int y, MenuObject *&currentItem) { return false; }
};
class MenuButton: public MenuObject {
public:
MenuButton(DialogView *owner, int buttonId, int xs, int ys, int width, int height,
Callback callbackFn = NULL, bool greyed = false, bool transparent = false,
ObjectType buttonType = OBJTYPE_BUTTON);
void onRefresh();
bool onEvent(M4EventType event, int param, int x, int y, MenuObject *&currentItem);
bool getToggled() { return _objectType == OBJTYPE_OM_SWITCH_ON; }
};
enum MenuHorizSliderState {HSLIDER_THUMB_NORMAL = 0, HSLIDER_THUMB_MOUSEOVER = 1, HSLIDER_THUMB_PRESSED = 2};
#define SLIDER_BAR_COLOR 129
class MenuHorizSlider: public MenuObject {
protected:
MenuHorizSliderState _sliderState;
Common::Point _thumbSize;
int _maxThumbX;
int _percent;
int _thumbX;
public:
MenuHorizSlider(DialogView *owner, int sliderId, int xs, int ys, int width, int height,
int initialPercentage, Callback callbackFn = NULL, bool transparent = false);
void onRefresh();
bool onEvent(M4EventType event, int param, int x, int y, MenuObject *&currentItem);
int percent() { return _percent; }
};
enum MenuVertSliderState {
VSLIDER_NONE = 0x0000,
VSLIDER_UP = 0x0010,
VSLIDER_PAGE_UP = 0x0020,
VSLIDER_THUMBNAIL = 0x0030,
VSLIDER_PAGE_DOWN = 0x0040,
VSLIDER_DOWN = 0x0050
};
class MenuVertSlider: public MenuObject {
protected:
MenuVertSliderState _sliderState;
Common::Point _thumbSize;
int _percent;
int _thumbY;
int _minThumbY;
int _maxThumbY;
MenuVertSliderState getSliderArea(int y);
void updateThumbnails() {}
public:
MenuVertSlider(DialogView *owner, int sliderId, int xs, int ys, int width, int height,
int initialPercentage, Callback callbackFn = NULL, bool transparent = false);
void onRefresh();
bool onEvent(M4EventType event, int param, int x, int y, MenuObject *&currentItem);
MenuVertSliderState sliderState() { return _sliderState; }
int percent() { return _percent; }
void setPercentage(int value);
};
class MenuMessage: public MenuObject {
public:
MenuMessage(DialogView *owner, int objectId, int x, int y, int w, int h, bool transparent = false);
void onRefresh();
};
class MenuImage: public MenuObject {
private:
M4Surface *_sprite;
public:
MenuImage(DialogView *owner, int objectId, int xs, int ys, int width, int height,
M4Surface *image, bool transparent = false);
void onRefresh();
const M4Surface *sprite() { return _sprite; }
void setSprite(M4Surface *v) {
_sprite = v;
onRefresh();
}
};
class MenuSaveLoadText: public MenuButton {
private:
bool _loadFlag;
const char *_displayText;
int _displayValue;
int _index;
bool _visible;
public:
MenuSaveLoadText(DialogView *owner, int textId, int xs, int ys, int width, int height,
Callback callbackFn = NULL, bool greyed = false, bool transparent = false,
bool loadFlag = false, const char *displayText = NULL, int displayValue = 0);
void onRefresh();
bool onEvent(M4EventType event, int param, int x, int y, MenuObject *&currentItem);
void setDisplay(int value, const char *text) { _displayValue = value; _displayText = text; }
int getIndex() { return _index; }
const char *getText() { return _displayText; }
bool getVisible() const { return _visible; }
void setVisible(bool value);
};
class MenuTextField: public MenuObject {
private:
int _displayValue;
char _displayText[MAX_SAVEGAME_NAME];
int _pixelWidth;
char *_promptEnd;
char *_cursor;
public:
MenuTextField(DialogView *owner, int fieldId, int xs, int ys, int width, int height,
bool greyed, Callback callbackFn = NULL, const char *displayText = NULL,
int displayValue = 0, bool transparent = true);
void onRefresh();
bool onEvent(M4EventType event, int param, int x, int y, MenuObject *&currentItem);
const char *getText() { return _displayText; }
int getDisplayValue() { return _displayValue; }
};
class GUIRect: public GUIObject {
private:
int _tag;
public:
GUIRect(View *owner, const Common::Rect &bounds, int tag): GUIObject(owner, bounds) { _tag = tag; };
virtual bool onEvent(M4EventType eventType, int param, int x, int y, GUIObject *&currentItem) { return false; }
int getTag() const { return _tag; }
};
enum GUIButtonState {BUTTON_NORMAL, BUTTON_MOUSEOVER, BUTTON_PRESSED};
class GUIButton: public GUIRect {
protected:
M4Surface *_normalSprite, *_mouseOverSprite, *_pressedSprite;
GUIButtonState _buttonState;
bool _visible;
bool _tracking;
public:
GUIButton(View *owner, const Common::Rect &bounds, int tag,
M4Surface *normalSprite, M4Surface *mouseOverSprite, M4Surface *pressedSprite);
void onRefresh();
bool onEvent(M4EventType eventType, int param, int x, int y, GUIObject *&currentItem);
GUIButtonState getState() const { return _buttonState; }
};
class GUITextField: public GUIRect {
private:
Common::String _text;
public:
GUITextField(View *owner, const Common::Rect &bounds);
void setText(const char *value) {
_text = value;
onRefresh();
}
void onRefresh();
};
class Dialogs {
public:
void keyMouseCollision() {}
};
}
#endif

293
engines/m4/hotspot.cpp Normal file
View file

@ -0,0 +1,293 @@
/* 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.
*
* $URL$
* $Id$
*
*/
#include "m4/m4.h"
#include "m4/events.h"
#include "m4/hotspot.h"
#include "gui/debugger.h"
namespace M4 {
/*
HotSpot
*/
HotSpot::HotSpot(int x1, int y1, int x2, int y2) :
_vocab(NULL), _verb(NULL), _prep(NULL), _sprite(NULL) {
_rect.left = x1;
_rect.top = y1;
_rect.right = x2 + 1;
_rect.bottom = y2 + 1;
_active = true;
_syntax = 0;
_cursor = 0;
_facing = 5;
_feetX = 32767;
_feetY = 32767;
}
HotSpot::~HotSpot() {
}
void HotSpot::setRect(int x1, int y1, int x2, int y2) {
_rect.left = x1;
_rect.top = y1;
_rect.right = x2 + 1;
_rect.bottom = y2 + 1;
}
void HotSpot::setFeet(int x, int y) {
_feetX = x;
_feetY = y;
}
void HotSpot::setVocab(const char *value) {
free(_vocab);
_vocab = strdup(value);
}
void HotSpot::setVerb(const char *value) {
free(_verb);
_verb = strdup(value);
}
void HotSpot::setPrep(const char *value) {
free(_prep);
_prep = strdup(value);
}
void HotSpot::setSprite(const char *value) {
free(_sprite);
_sprite = strdup(value);
}
Common::Rect HotSpot::getRect() const {
Common::Rect tempRect;
tempRect.left = _rect.left;
tempRect.top = _rect.top;
tempRect.right = _rect.right - 1;
tempRect.bottom = _rect.bottom - 1;
return tempRect;
}
/*
HotSpotList
*/
HotSpotList::HotSpotList() {
}
HotSpotList::~HotSpotList() {
clear();
}
int HotSpotList::add(HotSpot *hotspot, bool head) {
if (head || _hotspots.size() == 0) {
_hotspots.insert_at(0, hotspot);
return 0;
} else {
int32 area = hotspot->area();
int index = _hotspots.size();
for (uint i = 0; i < _hotspots.size(); i++) {
if (area < _hotspots[i]->area()) {
index = i;
break;
}
}
_hotspots.insert_at(index, hotspot);
return index;
}
}
void HotSpotList::remove(HotSpot *hotspot) {
unlink(hotspot);
delete hotspot; //TODO: check this?
}
void HotSpotList::unlink(HotSpot *hotspot) {
uint index = 0;
while (index < _hotspots.size()) {
if (_hotspots[index] == hotspot) {
_hotspots.remove_at(index);
} else {
index++;
}
}
}
void HotSpotList::clear() {
for (uint i = 0; i < _hotspots.size(); i++)
delete _hotspots[i];
_hotspots.clear();
}
HotSpot *HotSpotList::findByXY(int x, int y) {
for (uint i = 0; i < _hotspots.size(); i++) {
if (_hotspots[i]->getActive() && _hotspots[i]->pointInside(x, y)) {
return _hotspots[i];
}
}
return NULL;
}
void HotSpotList::setActive(const char *name, bool active) {
for (uint i = 0; i < _hotspots.size(); i++) {
if (!scumm_stricmp(_hotspots[i]->_vocab, name)) {
_hotspots[i]->setActive(active);
}
}
}
void HotSpotList::setActiveXY(const char *name, int x, int y, bool active) {
for (uint i = 0; i < _hotspots.size(); i++) {
if (_hotspots[i]->pointInside(x, y) && !scumm_stricmp(_hotspots[i]->_vocab, name)) {
_hotspots[i]->setActive(active);
}
}
}
void HotSpotList::dump() {
_vm->_events->getConsole()->DebugPrintf("%d hotspots in the list\n", _hotspots.size());
for (uint index = 0; index < _hotspots.size(); index++) {
_vm->_events->getConsole()->DebugPrintf("(%d): %p x1 = %d; y1 = %d; x2 = %d; y2 = %d\n",
index, (void *)_hotspots[index],
_hotspots[index]->_rect.left, _hotspots[index]->_rect.top,
_hotspots[index]->_rect.right - 1, _hotspots[index]->_rect.bottom - 1);
}
}
void HotSpotList::loadHotSpotsM4(Common::SeekableReadStream* hotspotStream, int hotspotCount) {
uint32 x1, y1, x2, y2;
char buffer[256];
uint32 strLength = 0;
uint32 index = 0;
HotSpot *currentHotSpot;
uint32 feetX, feetY;
for (int i = 0; i < hotspotCount; i++) {
x1 = hotspotStream->readUint32LE();
y1 = hotspotStream->readUint32LE();
x2 = hotspotStream->readUint32LE();
y2 = hotspotStream->readUint32LE();
index = add(new HotSpot(x1, y1, x2, y2), i == 0);
currentHotSpot = get(index);
feetX = hotspotStream->readUint32LE();
feetY = hotspotStream->readUint32LE();
currentHotSpot->setFeet(feetX, feetY);
currentHotSpot->setFacing((uint8)hotspotStream->readByte());
currentHotSpot->setActive(hotspotStream->readByte() != 0);
currentHotSpot->setCursor((uint8)hotspotStream->readByte());
hotspotStream->readByte(); // syntax (unused)
hotspotStream->readUint32LE(); // vocabID
hotspotStream->readUint32LE(); // verbID
strLength = hotspotStream->readUint32LE(); // vocabLength
hotspotStream->read(buffer, strLength); // vocab (the hotspot's name)
// Capitalize the hotspot's name here
str_upper(buffer);
currentHotSpot->setVocab(buffer);
// Verbs are used internally by the game scripts in Orion Burger
strLength = hotspotStream->readUint32LE(); // verbLength
hotspotStream->read(buffer, strLength); // verb
// Capitalize the hotspot's verb here
str_upper(buffer);
currentHotSpot->setVerb(buffer);
strLength = hotspotStream->readUint32LE(); // prepLength
hotspotStream->read(buffer, strLength); // prep
str_upper(buffer);
/* Hotspot names for non-English versions are stored in prep.
Prep can be set two ways: For English versions, copy the
text from vocab. For non-English versions, use the prep text
from the room file.
*/
if (strlen(buffer) > 0 && strcmp(buffer, "--") != 0 && strcmp(buffer, "ON") != 0)
currentHotSpot->setPrep(buffer);
else
currentHotSpot->setPrep(currentHotSpot->getVocab());
// The following values are not used at all by Orion Burger
strLength = hotspotStream->readUint32LE(); // spriteLength
hotspotStream->read(buffer, strLength); // sprite
hotspotStream->readUint16LE(); // sprite hash
}
}
void HotSpotList::loadHotSpotsMads(Common::SeekableReadStream* hotspotStream, int hotspotCount) {
uint16 x1, y1, x2, y2;
HotSpot *currentHotSpot;
uint16 feetX, feetY;
uint16 index = 0;
for (int i = 0; i < hotspotCount; i++) {
x1 = hotspotStream->readUint16LE();
y1 = hotspotStream->readUint16LE();
x2 = hotspotStream->readUint16LE();
y2 = hotspotStream->readUint16LE();
index = add(new HotSpot(x1, y1, x2, y2), i == 0);
currentHotSpot = get(index);
//printf("x1, y1, x2, y2: %i %i %i %i\n", x1, y1, x2, y2);
feetX = hotspotStream->readUint16LE();
feetY = hotspotStream->readUint16LE();
currentHotSpot->setFeet(feetX, feetY);
currentHotSpot->setFacing((uint8)hotspotStream->readByte());
index = hotspotStream->readByte(); // unknown (initial facing?)
hotspotStream->readByte(); // unused (always 255)
index = hotspotStream->readByte(); // cursor
if (index == 0)
currentHotSpot->setCursor(0);
else
currentHotSpot->setCursor(index - 1);
// Rex Nebular doesn't have this field
if (_vm->getGameType() != GType_RexNebular) {
// This looks to be some sort of bitmask. Perhaps it signifies
// the valid verbs for this hotspot
index = hotspotStream->readUint16LE(); // unknown
//printf("%i ", index);
}
index = hotspotStream->readUint16LE(); // noun index
currentHotSpot->setVocabID(index);
currentHotSpot->setVocab(_vm->_globals->getVocab(index - 1));
index = hotspotStream->readUint16LE(); // verb index (default left click verb)
currentHotSpot->setVerbID(index);
if (index != 0) {
currentHotSpot->setVerb(_vm->_globals->getVocab(index - 1));
} else {
currentHotSpot->setVerb("");
}
//printf("%s ", currentHotSpot->getVerb());
//printf("%s ", currentHotSpot->getVocab());
//printf("\n");
}
}
} // End of namespace M4

120
engines/m4/hotspot.h Normal file
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 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#ifndef M4_HOTSPOT_H
#define M4_HOTSPOT_H
#include "common/array.h"
#include "common/rect.h"
#include "common/util.h"
/*
TODO:
- check if hotspot_duplicate is needed
NOTES:
- hotspot_add_dynamic unused in Orion Burger
*/
//???: should String be used instead of char* here?
namespace M4 {
class HotSpot {
friend class HotSpotList;//just for debugging, to be removed later
public:
HotSpot(int x1 = 0, int y1 = 0, int x2 = 0, int y2 = 0);
~HotSpot();
void setVocab(const char *value);
void setVocabID(int32 value) { _vocabID = value; }
void setVerb(const char *value);
void setVerbID(int32 value) { _verbID = value; }
void setPrep(const char *value);
void setSprite(const char *value);
void setActive(bool value) { _active = value; }
void setCursor(uint8 value) { _cursor = value; }
void setRect(int x1, int y1, int x2, int y2);
void setFeet(int x, int y);
void setFacing(uint8 facing) { _facing = facing; }
char *getVocab() const { return _vocab; }
int32 getVocabID() { return _vocabID; }
char *getVerb() const { return _verb; }
int32 getVerbID() { return _verbID; }
char *getPrep() const { return _prep; }
char *getSprite() const { return _sprite; }
uint8 getCursor() const { return _cursor; }
bool getActive() const { return _active; }
uint8 getFacing() const { return _facing; }
int getFeetX() { return _feetX; }
int getFeetY() { return _feetY; }
Common::Rect getRect() const;
int32 area() const { return (_rect.width() - 1) * (_rect.height() - 1); }
bool pointInside(int x, int y) { return _rect.contains(x, y); }
private:
char *_vocab, *_verb, *_prep, *_sprite;
Common::Rect _rect;
bool _active;
int _feetX, _feetY;
uint8 _facing, _cursor;
// Unused in Orion Burger, used in MADS games
uint8 _syntax;
int32 _vocabID, _verbID;
//TODO: check if this is actually needed by the game
int16 _hash;
};
class HotSpotList {
public:
HotSpotList();
~HotSpotList();
int add(HotSpot *hotspot, bool head = false);
HotSpot *get(int index) { return _hotspots[index]; }
void remove(HotSpot *hotspot);
void unlink(HotSpot *hotspot);
void clear();
HotSpot *findByXY(int x, int y);
void setActive(const char *name, bool active);
void setActiveXY(const char *name, int x, int y, bool active);
void dump();
void loadHotSpotsM4(Common::SeekableReadStream* hotspotStream, int hotspotCount);
void loadHotSpotsMads(Common::SeekableReadStream* hotspotStream, int hotspotCount);
private:
typedef Common::Array<HotSpot*> HotSpotArray;
HotSpotArray _hotspots;
};
} // End of namespace M4
#endif

555
engines/m4/m4.cpp Normal file
View file

@ -0,0 +1,555 @@
/* 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.
*
* $URL$
* $Id$
*
*/
//#define SCRIPT_TEST
//#define INTRO_TEST
#include "m4/globals.h"
#include "m4/burger_data.h"
#include "m4/m4.h"
#include "m4/resource.h"
#include "m4/sprite.h"
#include "m4/hotspot.h"
#include "m4/font.h"
#include "m4/rails.h"
#include "m4/midi.h"
#include "m4/events.h"
#include "m4/graphics.h"
#include "m4/viewmgr.h"
#include "m4/gui.h"
#include "m4/woodscript.h"
#include "m4/actor.h"
#include "m4/sound.h"
#include "m4/rails.h"
#include "m4/script.h"
#include "m4/compression.h"
#include "m4/animation.h"
#include "m4/m4_menus.h"
#include "m4/m4_views.h"
#include "m4/mads_anim.h"
#include "m4/mads_menus.h"
#include "common/file.h"
#include "common/events.h"
#include "common/endian.h"
#include "common/system.h"
#include "common/config-manager.h"
#include "engines/engine.h"
#include "graphics/surface.h"
#include "sound/mididrv.h"
namespace M4 {
// FIXME: remove global
M4Engine *_vm;
void escapeHotkeyHandler(M4Engine *vm, View *view, uint32 key) {
// For now, simply exit the game
vm->_events->quitFlag = true;
}
// Temporary hotkey handler for use in testing the TextviewView class
void textviewHotkeyHandler(M4Engine *vm, View *view, uint32 key) {
// Deactivate the scene if it's currently active
View *sceneView = vm->_viewManager->getView(VIEWID_SCENE);
if (sceneView != NULL)
vm->_viewManager->deleteView(sceneView);
// Activate the textview view
vm->_font->setFont(FONT_CONVERSATION_MADS);
TextviewView *textView = new TextviewView(vm);
vm->_viewManager->addView(textView);
textView->setScript("quotes", NULL);
}
void saveGameHotkeyHandler(M4Engine *vm, View *view, uint32 key) {
// TODO: See CreateF2SaveMenu - save menu should only be activated when
// certain conditions are met, such as player_commands_allowed, and isInterfaceVisible
vm->loadMenu(SAVE_MENU, true);
}
void loadGameHotkeyHandler(M4Engine *vm, View *view, uint32 key) {
// TODO: See CreateF3LoadMenu - save menu should only be activated when
// certain conditions are met, such as player_commands_allowed, and isInterfaceVisible
vm->loadMenu(LOAD_MENU, true);
}
void gameMenuHotkeyHandler(M4Engine *vm, View *view, uint32 key) {
vm->loadMenu(GAME_MENU);
}
M4Engine::M4Engine(OSystem *syst, const M4GameDescription *gameDesc) :
Engine(syst), _gameDescription(gameDesc) {
// FIXME
_vm = this;
Common::File::addDefaultDirectory(_gameDataPath);
Common::File::addDefaultDirectory("goodstuf");
Common::File::addDefaultDirectory("resource");
Common::addSpecialDebugLevel(kDebugScript, "script", "Script debug level");
Common::addSpecialDebugLevel(kDebugConversations, "conversations", "Conversations debugging");
}
M4Engine::~M4Engine() {
delete _globals;
delete _midi;
delete _saveLoad;
delete _kernel;
delete _player;
delete _mouse;
delete _events;
delete _font;
delete _actor;
// delete _scene; // deleted by the viewmanager
delete _dialogs;
delete _screen;
delete _inventory;
delete _viewManager;
delete _rails;
delete _converse;
delete _script;
delete _ws;
delete _random;
delete _animation;
delete _palette;
delete _resourceManager;
}
int M4Engine::init() {
// Initialize backend
_system->beginGFXTransaction();
initCommonGFX(isM4());
if (isM4())
_system->initSize(640, 480);
else
_system->initSize(320, 200);
_system->endGFXTransaction();
_screen = new M4Surface(true); // Special form for creating screen reference
int midiDriver = MidiDriver::detectMusicDriver(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MIDI);
bool native_mt32 = ((midiDriver == MD_MT32) || ConfMan.getBool("native_mt32"));
MidiDriver *driver = MidiDriver::createMidi(midiDriver);
if (native_mt32)
driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
_midi = new MidiPlayer(this, driver);
_midi->setGM(true);
_midi->setNativeMT32(native_mt32);
_globals = new Globals(this);
if (isM4())
_resourceManager = new M4ResourceManager(this);
else
_resourceManager = new MADSResourceManager(this);
_saveLoad = new SaveLoad(this);
_palette = new Palette(this);
_mouse = new Mouse(this);
_events = new Events(this);
_kernel = new Kernel(this);
_player = new Player(this);
_font = new Font(this);
if (getGameType() == GType_Burger) {
_actor = new Actor(this);
_interfaceView = new GameInterfaceView(this);
_conversationView = new ConversationView(this);
} else {
_actor = NULL;
}
_rails = new Rails(); // needs to be initialized before _scene
_scene = new Scene(this);
_dialogs = new Dialogs();
_viewManager = new ViewManager(this);
_inventory = new Inventory(this);
_sound = new Sound(this, _mixer, 255);
_converse = new Converse(this);
_script = new ScriptInterpreter(this);
_ws = new WoodScript(this);
_animation = new Animation(this);
//_callbacks = new Callbacks(this);
_random = new Common::RandomSource();
g_system->getEventManager()->registerRandomSource(*_random, "m4");
return 0;
}
void M4Engine::eventHandler() {
M4EventType event;
uint32 keycode = 0;
if ((event = _events->handleEvents()) != MEVENT_NO_EVENT) {
if (_viewManager->viewCount() > 0)
_viewManager->handleMouseEvents(event);
}
if (_events->kbdCheck(keycode))
_viewManager->handleKeyboardEvents(keycode);
}
bool M4Engine::delay(int duration, bool keyAborts, bool clickAborts) {
uint32 endTime = g_system->getMillis() + duration;
M4EventType event;
uint32 keycode = 0;
while (!_events->quitFlag && (g_system->getMillis() < endTime)) {
event = _events->handleEvents();
if (clickAborts && (event == MEVENT_LEFT_RELEASE) || (event == MEVENT_RIGHT_RELEASE))
return true;
if (_events->kbdCheck(keycode)) {
if (keyAborts)
return true;
}
g_system->delayMillis(10);
}
return false;
}
void M4Engine::loadMenu(MenuType menuType, bool loadSaveFromHotkey, bool calledFromMainMenu) {
if (isM4() && (menuType != MAIN_MENU)) {
bool menuActive = _viewManager->getView(VIEWID_MENU) != NULL;
if (!menuActive)
_palette->fadeToGreen(M4_DIALOG_FADE_STEPS, M4_DIALOG_FADE_DELAY);
}
View *view;
switch (menuType) {
case MAIN_MENU:
if (getGameType() == GType_RexNebular)
view = new RexMainMenuView(this);
else if (getGameType() == GType_DragonSphere)
view = new DragonMainMenuView(this);
else
view = new MadsMainMenuView(this);
break;
case GAME_MENU:
view = new OrionMenuView(this, 200, 100, GAME_MENU, calledFromMainMenu, loadSaveFromHotkey);
break;
case OPTIONS_MENU:
view = new OrionMenuView(this, 172, 160, OPTIONS_MENU, calledFromMainMenu, loadSaveFromHotkey);
break;
case LOAD_MENU:
case SAVE_MENU:
view = new OrionMenuView(this, 145, 10, menuType, calledFromMainMenu, loadSaveFromHotkey);
break;
default:
error("Unknown menu type");
break;
}
_viewManager->addView(view);
_viewManager->moveToFront(view);
}
int M4Engine::go() {
if (isM4())
return goM4();
else
return goMADS();
}
int M4Engine::goMADS() {
_palette->setMadsSystemPalette();
_mouse->init("cursor.ss", NULL);
_mouse->setCursorNum(0);
// Load MADS data files
_globals->loadMadsVocab(); // vocab.dat
_globals->loadMadsQuotes(); // quotes.dat
_globals->loadMadsMessagesInfo(); // messages.dat
// TODO: objects.dat
// TODO: hoganus.dat (what is it used for?)
// Test code to dump all messages to the console
//for (int i = 0; i < _globals->getMessagesSize(); i++)
//printf("%s\n----------\n", _globals->loadMessage(i));
if ((getGameType() == GType_RexNebular) || (getGameType() == GType_DragonSphere)) {
loadMenu(MAIN_MENU);
} else {
if (getGameType() == GType_DragonSphere) {
_scene->loadScene(FIRST_SCENE);
} else if (getGameType() == GType_Phantom) {
//_scene->loadScene(FIRST_SCENE);
_scene->loadScene(106); // a more interesting scene
}
_viewManager->addView(_scene);
_font->setFont(FONT_MAIN_MADS);
_font->setColors(2, 1, 3);
_font->writeString(_scene->getBackgroundSurface(), "Testing the M4/MADS ScummVM engine", 5, 160, 310, 2);
_font->writeString(_scene->getBackgroundSurface(), "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 5, 180, 310, 2);
if (getGameType() == GType_DragonSphere) {
//_scene->showMADSV2TextBox("Test", 10, 10, NULL);
}
_mouse->cursorOn();
}
_viewManager->systemHotkeys().add(Common::KEYCODE_ESCAPE, &escapeHotkeyHandler);
_viewManager->systemHotkeys().add(Common::KEYCODE_KP_MULTIPLY, &textviewHotkeyHandler);
// Load the general game SFX/voices
if (getGameType() == GType_RexNebular) {
_sound->loadDSRFile("rex009.dsr");
} else if (getGameType() == GType_Phantom) {
_sound->loadDSRFile("phan009.dsr");
} else if (getGameType() == GType_DragonSphere) {
_sound->loadDSRFile("drag009.dsr");
}
uint32 nextFrame = g_system->getMillis();
while (!_events->quitFlag) {
eventHandler();
_animation->updateAnim();
// Call the updateState method of all views
_viewManager->updateState();
if (g_system->getMillis() >= nextFrame) {
_viewManager->refreshAll();
nextFrame = g_system->getMillis();// + GAME_FRAME_DELAY;
}
g_system->delayMillis(10);
}
return 0;
}
int M4Engine::goM4() {
_script->open("m4.dat");
#ifdef SCRIPT_TEST
#if 0
ScriptFunction *func = _script->loadFunction("room_parser_142");
_script->runFunction(func);
#endif
#if 1
ScriptFunction *func = _script->loadFunction("room_daemon_951");
for (int i = 1; i < 58; i++) {
_vm->_kernel->trigger = i;
_script->runFunction(func);
printf("=================================\n");
fflush(stdout);
}
#endif
return 0;
#endif
// Set up the inventory
// Set up the game interface view
//_interfaceView->inventoryAdd("Money", "", 55); // Sample item
if (getGameType() == GType_Burger) {
for (int i = 0; i < ARRAYSIZE(burger_inventory); i++) {
char* itemName = strdup(burger_inventory[i].name);
_inventory->registerObject(itemName, burger_inventory[i].scene,
burger_inventory[i].icon);
_inventory->addToBackpack(i); // debug: this adds ALL objects to the player's backpack
}
_viewManager->addView(_interfaceView);
}
// Show intro
if (getGameType() == GType_Burger)
_kernel->newRoom = TITLE_SCENE_BURGER;
else
_scene->getBackgroundSurface()->loadBackgroundRiddle("main menu");
_viewManager->addView(_scene);
// Setup game wide hotkeys. Note that Orion Burger used F2/F3 for Save/Restore,
// but for standardisation with most other games, F5/F7 are also mapped
_viewManager->systemHotkeys().add(Common::KEYCODE_ESCAPE, &escapeHotkeyHandler);
_viewManager->systemHotkeys().add(Common::KEYCODE_F2, &saveGameHotkeyHandler);
_viewManager->systemHotkeys().add(Common::KEYCODE_F3, &loadGameHotkeyHandler);
_viewManager->systemHotkeys().add(Common::KEYCODE_F5, &saveGameHotkeyHandler);
_viewManager->systemHotkeys().add(Common::KEYCODE_F7, &loadGameHotkeyHandler);
_viewManager->systemHotkeys().add(Common::KEYCODE_F9, &gameMenuHotkeyHandler);
// Start playing Orion Burger intro music
//_midi->playMusic("999intro", 255, false, -1, -1);
// TODO: start playing intro animations
// TODO: Master Lu
// Test for mouse
_mouse->init("cursor", NULL);
_mouse->setCursorNum(0);
_mouse->cursorOn();
_ws->assets()->loadAsset("SHOW SCRIPT", NULL);
_ws->assets()->loadAsset("STREAM SCRIPT", NULL);
#ifdef INTRO_TEST
int curPart = 0;
Machine *mach = NULL;
#endif
_ws->setSurfaceView(_scene);
uint32 nextFrame = g_system->getMillis();
while (!_events->quitFlag) {
// This should probably be moved to either Scene or Kernel
if (_kernel->currentRoom != _kernel->newRoom) {
_ws->clear();
_kernel->currentSection = _kernel->newRoom / 100;
_kernel->currentRoom = _kernel->newRoom;
_scene->loadScene(_kernel->currentRoom);
_ws->setBackgroundSurface(_scene->getBackgroundSurface());
_ws->setInverseColorTable(_scene->getInverseColorTable());
_kernel->loadSectionScriptFunctions();
_kernel->loadRoomScriptFunctions();
_kernel->roomInit();
#ifdef INTRO_TEST
if (_kernel->currentRoom == 951) {
curPart = 0;
mach = _ws->streamSeries("PLANET X HILLTOP A", 1, 0x1000, 0);
}
#endif
}
eventHandler();
// Call the updateState method of all views
_viewManager->updateState();
// Handle frame updates
if (g_system->getMillis() >= nextFrame) {
#ifdef INTRO_TEST
// Orion Burger intro test (scene 951)
// This is ugly and bad, machine is not deleted so there's a huge memory
// leak too. But hey, we can see some of the intro!
if (mach && mach->getState() == -1) {
if (curPart == 0)
mach = _ws->streamSeries("Planet X Low Ground Shot", 1, 0x1000, 0);
else if (curPart == 1)
mach = _ws->streamSeries("Planet X Hilltop B", 1, 0x1000, 0);
else if (curPart == 2)
mach = _ws->streamSeries("Space Station Panorama A", 1, 0x1000, 0);
else if (curPart == 3)
mach = _ws->streamSeries("Cargo Transfer Area A", 1, 0x1000, 0);
else if (curPart == 4)
mach = _ws->streamSeries("VP's Office A", 1, 0x1000, 0);
else if (curPart == 5)
mach = _ws->streamSeries("Hologram", 1, 0x1000, 0);
else if (curPart == 6)
mach = _ws->streamSeries("VP's Office B", 1, 0x1000, 0);
else if (curPart == 7)
mach = _ws->streamSeries("Cargo Transfer Area B", 1, 0x1000, 0);
else if (curPart == 8)
mach = _ws->streamSeries("Cargo Transfer Controls", 1, 0x1000, 0);
else if (curPart == 9)
mach = _ws->streamSeries("Space Station Panorama B", 1, 0x1000, 0);
// This last scene is from the rolling demo
//else if (curPart == 10)
// mach = _ws->streamSeries("Call To Action", 1, 0x1000, 0);
curPart++;
}
#endif
_ws->update();
_viewManager->refreshAll();
nextFrame = g_system->getMillis();// + GAME_FRAME_DELAY;
// TEST STUFF ONLY
if (_player->commandReady) {
_kernel->roomParser();
_player->commandReady = false;
}
}
g_system->delayMillis(10);
}
return 0;
}
void M4Engine::dumpFile(const char* filename, bool uncompress) {
Common::SeekableReadStream *fileS = res()->get(filename);
byte buffer[256];
FILE *destFile = fopen(filename, "wb");
int bytesRead = 0;
printf("Dumping %s, size: %i\n", filename, fileS->size());
if (!uncompress) {
while(!fileS->eos()) {
bytesRead = fileS->read(buffer, 256);
fwrite(buffer, bytesRead, 1, destFile);
}
} else {
MadsPack packData(fileS);
Common::MemoryReadStream *sourceUnc;
for (int i = 0; i < packData.getCount(); i++) {
sourceUnc = packData.getItemStream(i);
printf("Dumping compressed chunk %i of %i, size is %i\n", i + 1, packData.getCount(), sourceUnc->size());
while(!sourceUnc->eos()) {
bytesRead = sourceUnc->read(buffer, 256);
fwrite(buffer, bytesRead, 1, destFile);
}
delete sourceUnc;
}
}
fclose(destFile);
res()->toss(filename);
res()->purge();
}
} // End of namespace M4

184
engines/m4/m4.h Normal file
View file

@ -0,0 +1,184 @@
/* 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.
*
* $URL$
* $Id$
*
*/
#ifndef M4_H
#define M4_H
#include "common/scummsys.h"
#include "common/util.h"
#include "engines/engine.h"
#include "m4/globals.h"
#include "m4/graphics.h"
#include "m4/resource.h"
#include "m4/saveload.h"
#include "m4/viewmgr.h"
#include "m4/gui.h"
#include "m4/events.h"
#include "m4/font.h"
#include "m4/scene.h"
#include "m4/actor.h"
#include "m4/sound.h"
#include "m4/rails.h"
#include "m4/converse.h"
#include "m4/animation.h"
//#define DUMP_SCRIPTS
namespace M4 {
class MidiPlayer;
class FileSystem;
class ResourceManager;
class Mouse;
class Events;
class Scene;
class ViewManager;
class View;
class Inventory;
class GameInterfaceView;
class ConversationView;
class Actor;
class Converse;
class ScriptInterpreter;
class WoodScript;
class Animation;
enum M4GameType {
GType_Riddle = 1,
GType_Burger,
GType_RexNebular,
GType_DragonSphere,
GType_Phantom
};
enum Features {
kFeaturesNone = 0,
kFeaturesCD = 1 << 0,
kFeaturesDemo = 1 << 1
};
enum {
kFileTypeHash,
kFileTypeHAG
};
enum {
kDebugScript = 1 << 0,
kDebugConversations = 2 << 0
};
#define MESSAGE_BASIC 1
#define MESSAGE_INTERMEDIATE 2
#define MESSAGE_DETAILED 3
struct M4GameDescription;
#define GAME_FRAME_DELAY 50
FORCEINLINE void str_lower(char *s) { while (*s) { *s = tolower(*s); s++; } }
FORCEINLINE void str_upper(char *s) { while (*s) { *s = toupper(*s); s++; } }
FORCEINLINE long FixedMul(long a, long b) { return (long)(((float)a * (float)b) / 65536.0); }
FORCEINLINE long FixedDiv(long a, long b) { return (long)(((float)a / (float)b) * 65536.0); }
class M4Engine : public Engine {
private:
int goMADS();
int goM4();
protected:
int init();
int go();
void shutdown();
MidiPlayer *_midi;
public:
M4Engine(OSystem *syst, const M4GameDescription *gameDesc);
virtual ~M4Engine();
int getGameType() const;
uint32 getFeatures() const;
Common::Language getLanguage() const;
Common::Platform getPlatform() const;
bool isM4() const { return (getGameType() == GType_Riddle) || (getGameType() == GType_Burger); }
const char *getGameFile(int fileType);
Common::EventManager *eventMan() { return _eventMan; }
OSystem *system() { return _system; }
const M4GameDescription *_gameDescription;
ResourceManager *res() const { return _resourceManager; }
MidiPlayer *midi() { return _midi; }
Common::SaveFileManager *saveManager() { return _saveFileMan; }
void dumpFile(const char* filename, bool uncompress = false);
void eventHandler();
bool delay(int duration, bool keyAborts = true, bool clickAborts = true);
void loadMenu(MenuType menuType, bool loadSaveFromHotkey = false,
bool calledFromMainMenu = false);
// TODO: eventually these have to be removed
int32 seed;
void imath_seed(int32 seednum) { seed = seednum; }
uint32 imath_random() { return(seed = (25173*seed + 13849) & 0xffff); }
int32 imath_ranged_rand(int32 a, int32 b) { return (a + (((1 + ABS<int32>(b-a))*imath_random())>>16)); }
long imath_ranged_rand16(long a, long b) { return ((a + FixedMul(1+ABS<int32>(b-a),imath_random()))); }
//
ResourceManager *_resourceManager;
SaveLoad *_saveLoad;
ViewManager *_viewManager;
Palette *_palette;
Kernel *_kernel;
Globals *_globals;
Player *_player;
Mouse *_mouse;
Events *_events;
Font *_font;
Actor *_actor;
Scene *_scene;
Dialogs *_dialogs;
M4Surface *_screen;
Inventory *_inventory;
GameInterfaceView *_interfaceView;
ConversationView *_conversationView;
Sound *_sound;
Rails *_rails;
Converse *_converse;
ScriptInterpreter *_script;
WoodScript *_ws;
Animation *_animation;
Common::RandomSource *_random;
};
// FIXME: remove global
extern M4Engine *_vm;
} // End of namespace M4
#endif

727
engines/m4/m4_menus.cpp Normal file
View file

@ -0,0 +1,727 @@
/* 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.
*
* $URL$
* $Id$
*
*/
#include "common/algorithm.h" // for find()
#include "gui/dialog.h"
#include "gui/message.h"
#include "m4/m4_menus.h"
#include "m4/m4_views.h"
#include "m4/woodscript.h"
#include "m4/midi.h"
namespace M4 {
const char *EmptySaveString = "<empty>";
//--------------------------------------------------------------------------
// Callback methods
//
// Following is a set of callback methods used to handle the execution
// of buttons in the various dialogs
//--------------------------------------------------------------------------
// General function which simply closes the active menu
void OrionCallbacks::closeMenuFn(DialogView *view, MenuObject *item) {
view->close();
}
void OrionCallbacks::closeMenuFn(OrionMenuView *view) {
closeMenuFn(view, NULL);
}
/* Game menu functions */
void OrionCallbacks::gameOptionsMenuFn(DialogView *view, MenuObject *item) {
view->vm()->loadMenu(OPTIONS_MENU);
view->close();
}
void OrionCallbacks::gameSaveGameFn(DialogView *view, MenuObject *item) {
view->vm()->loadMenu(SAVE_MENU);
view->close();
}
void OrionCallbacks::gameLoadGameFn(DialogView *view, MenuObject *item) {
view->vm()->loadMenu(LOAD_MENU);
view->close();
}
void OrionCallbacks::gameExitFn(DialogView *view, MenuObject *item) {
view->vm()->_events->quitFlag = true;
view->close();
}
/* Options menu */
void OrionCallbacks::optionsDigiSliderFn(DialogView *view, MenuObject *item) {
// Digi volume slider changed
int percent = ((MenuHorizSlider *) item)->percent();
view->vm()->_sound->setVolume(percent * 255 / 100);
}
void OrionCallbacks::optionsMidiSliderFn(DialogView *view, MenuObject *item) {
// Midi volume slider changed
int percent = ((MenuHorizSlider *) item)->percent();
view->vm()->midi()->setVolume(percent * 255 / 100);
}
void OrionCallbacks::optionsScrollingFn(DialogView *view, MenuObject *item) {
// TODO: Change current Digi volume settings here
}
void OrionCallbacks::optionsCancelFn(DialogView *view, MenuObject *item) {
// TODO: Reset original option settings here
OrionMenuView *vw = (OrionMenuView *) view;
vw->vm()->midi()->setVolume(vw->_originalMidiVolume);
vw->vm()->loadMenu(GAME_MENU);
vw->close();
}
void OrionCallbacks::optionsDoneFn(DialogView *view, MenuObject *item) {
view->vm()->loadMenu(GAME_MENU);
view->close();
}
void OrionCallbacks::optionsReturnFn(OrionMenuView *view) {
optionsDoneFn(view, NULL);
}
void OrionCallbacks::optionsEscapeFn(OrionMenuView *view) {
optionsCancelFn(view, NULL);
}
/* Save/Load dialog functions */
// Save the current game
void OrionCallbacks::saveLoadSaveFn(DialogView *view, MenuObject *item) {
if (view->_selectedSlot == -1)
return;
MenuTextField *textItem = (MenuTextField *) view->getItem(SLTAG_TEXTFIELD);
if (!textItem)
return;
textItem->setState(OS_NORMAL);
// Save the game
bool succeeded = view->vm()->_saveLoad->save(view->_selectedSlot + 1, textItem->getText());
if (!succeeded) {
GUI::MessageDialog dialog("Save game failed!");
dialog.runModal();
}
// Close the menu
closeMenuFn(view, item);
}
void OrionCallbacks::saveLoadLoadFn(DialogView *view, MenuObject *item) {
// TODO: load the selected save game
closeMenuFn(view, item);
}
void OrionCallbacks::saveLoadSlotFn(DialogView *view, MenuObject *item) {
OrionMenuView *vw = (OrionMenuView *) view;
MenuSaveLoadText *button = (MenuSaveLoadText *) item;
view->_selectedSlot = button->getIndex();
view->_deleteSaveDesc = true;
// Disable all the slots except the selected one
for (int index = 0; index < SL_NUM_VISIBLE_SLOTS; ++index) {
MenuSaveLoadText *currentItem = (MenuSaveLoadText *) view->getItem(SLTAG_SLOTS_START + index);
if (currentItem->getIndex() != button->getIndex()) {
currentItem->setState(OS_GREYED);
}
}
// Get a copy of the slot bounds
Common::Rect slotBounds = button->getBounds();
if (view->getMenuType() == SAVE_MENU) {
// Add in a text field for entry of the savegame name
vw->items().push_back(new MenuTextField(view, SLTAG_TEXTFIELD,
slotBounds.left, slotBounds.top, slotBounds.width(), slotBounds.height(), false,
saveLoadSaveFn, (button->getText() == EmptySaveString) ? NULL : button->getText(),
button->getIndex() + 1));
} else {
vw->items().push_back(new MenuTextField(view, SLTAG_TEXTFIELD,
slotBounds.left, slotBounds.top, slotBounds.width(), slotBounds.height(), true,
saveLoadLoadFn, button->getText(), button->getIndex() + 1));
}
// Hide the existing slot
button->setVisible(false);
// Disable the slider
MenuVertSlider *slider = (MenuVertSlider *) view->getItem(SLTAG_VSLIDER);
slider->setState(OS_GREYED);
// Enable the save/load button
MenuButton *btn = (MenuButton *) view->getItem(SLTAG_SAVELOAD);
btn->setState(OS_NORMAL);
}
void OrionCallbacks::saveLoadCancelFn(DialogView *view, MenuObject *item) {
OrionMenuView *vw = (OrionMenuView *) view;
if (view->_selectedSlot != -1) {
// Pressed cancel with a save selected, so revert back to no selection
// Re-enable all the other slots
for (int index = 0; index < SL_NUM_VISIBLE_SLOTS; ++index) {
if (index != view->_selectedSlot) {
MenuSaveLoadText *currentItem = (MenuSaveLoadText *) view->getItem(SLTAG_SLOTS_START + index);
currentItem->setState(OS_NORMAL);
}
}
// Show the previously hidden slot again
MenuSaveLoadText *slot = (MenuSaveLoadText *) view->getItem(SLTAG_SLOTS_START + view->_selectedSlot);
slot->setVisible(true);
slot->setState(OS_NORMAL);
// Remove the text selection
MenuTextField *textField = (MenuTextField *) view->getItem(SLTAG_TEXTFIELD);
delete textField;
vw->items().remove(textField);
// Set button enablement
MenuButton *btn = (MenuButton *) view->getItem(SLTAG_SAVELOAD);
btn->setState(OS_GREYED);
btn = (MenuButton *) view->getItem(SLTAG_CANCEL);
btn->setState(OS_NORMAL);
// Re-enable the slider
MenuVertSlider *slider = (MenuVertSlider *) view->getItem(SLTAG_VSLIDER);
slider->setState(OS_NORMAL);
view->_selectedSlot = -1;
} else {
// Close the dialog
if (vw->_loadSaveFromHotkey)
// Since dialog was called from hotkey, return directly to the game
closeMenuFn(view, item);
else {
// Return to the game menu
view->vm()->loadMenu(GAME_MENU);
view->close();
}
}
}
void OrionCallbacks::saveLoadSliderFn(DialogView *view, MenuObject *item) {
OrionMenuView *vw = (OrionMenuView *) view;
MenuVertSlider *slider = (MenuVertSlider *) item;
if (slider->sliderState() == VSLIDER_THUMBNAIL) {
// Callback generated by slider thumb, so set top slot using slider percentage
vw->setTopSaveSlot(slider->percent() * 89 / 100);
} else {
int newIndex = view->_topSaveSlotIndex;
switch (slider->sliderState()) {
case VSLIDER_UP:
if (newIndex > 0)
--newIndex;
break;
case VSLIDER_PAGE_UP:
if (newIndex > 0)
newIndex = MAX(newIndex - 10, 0);
break;
case VSLIDER_PAGE_DOWN:
if (newIndex < 89)
newIndex = MIN(newIndex + 10, 89);
break;
case VSLIDER_DOWN:
if (newIndex < 89)
++newIndex;
break;
default:
break;
}
if (newIndex != view->_topSaveSlotIndex) {
// Set the new top slot
vw->setTopSaveSlot(newIndex);
// Set the new slider position
slider->setPercentage(newIndex * 100 / 89);
}
}
}
void OrionCallbacks::saveLoadEscapeFn(OrionMenuView *view) {
saveLoadCancelFn(view, NULL);
}
void OrionCallbacks::saveLoadReturnFn(OrionMenuView *view) {
MenuTextField *textItem = (MenuTextField *) view->getItem(SLTAG_TEXTFIELD);
if (textItem) {
if (view->getMenuType() == SAVE_MENU)
saveLoadSaveFn(view, NULL);
else
saveLoadLoadFn(view, NULL);
}
}
//--------------------------------------------------------------------------
OrionMenuView::OrionMenuView(M4Engine *Vm, int x, int y, MenuType menuType, bool calledFromMainMenu,
bool loadSaveFromHotkey): DialogView(Vm, x, y, true) {
_menuType = menuType;
_screenType = VIEWID_MENU;
_screenFlags.layer = LAYER_MENU;
_screenFlags.get = SCREVENT_ALL;
_screenFlags.blocks = SCREVENT_ALL;
_screenFlags.immovable = true;
//_screenFlags.immovable = false; // uncomment to make menu movable
_coords.left = x;
_coords.top = y;
_currentItem = NULL;
_escapeHandler = &OrionCallbacks::closeMenuFn;
_returnHandler = NULL;
_saveNames = NULL;
_savegameThumbnail = NULL;
_deleteSaveDesc = false;
_closeFlag = false;
_calledFromMainMenu = calledFromMainMenu;
_loadSaveFromHotkey = loadSaveFromHotkey;
_interfaceWasVisible = _vm->_interfaceView->isVisible();
if (_interfaceWasVisible)
_vm->_interfaceView->hide();
_vm->_mouse->setCursorNum(CURSOR_ARROW);
switch (menuType) {
case GAME_MENU:
loadSprites(MENU_GAME);
// Add menu contents
_menuObjects.push_back(new MenuButton(this, BTNID_MAIN, 45, 53, 24, 24, &OrionCallbacks::closeMenuFn));
_menuObjects.push_back(new MenuButton(this, BTNID_OPTIONS, 45, 94, 24, 24, &OrionCallbacks::gameOptionsMenuFn));
_menuObjects.push_back(new MenuButton(this, BTNID_RESUME, 45, 135, 24, 24, &OrionCallbacks::closeMenuFn));
_menuObjects.push_back(new MenuButton(this, BTNID_QUIT, 141, 135, 24, 24, &OrionCallbacks::gameExitFn));
_menuObjects.push_back(new MenuButton(this, BTNID_SAVE, 141, 53, 24, 24, &OrionCallbacks::gameSaveGameFn, _calledFromMainMenu));
_menuObjects.push_back(new MenuButton(this, BTNID_LOAD, 141, 94, 24, 24, &OrionCallbacks::gameLoadGameFn,
!_vm->_saveLoad->hasSaves()));
_escapeHandler = &OrionCallbacks::closeMenuFn;
_returnHandler = &OrionCallbacks::closeMenuFn;
break;
case OPTIONS_MENU:
loadSprites(MENU_OPTIONS);
// Store the original settings in case user aborts dialog
_originalMidiVolume = _vm->midi()->getVolume();
// Add menu contents
// TODO: Currently the Digi slider isn't hooked up to anything
_menuObjects.push_back(new MenuButton(this, OPTIONID_CANCEL, 93, 141, 74, 43,
&OrionCallbacks::optionsCancelFn, false, false, OBJTYPE_OM_CANCEL));
_menuObjects.push_back(new MenuButton(this, OPTIONID_DONE, 168, 141, 74, 43,
&OrionCallbacks::optionsDoneFn, false, false, OBJTYPE_OM_DONE));
_menuObjects.push_back(new MenuHorizSlider(this, OPTIONID_HSLIDER_MIDI, 47, 64, 212, 24,
_originalMidiVolume * 100 / 255, &OrionCallbacks::optionsMidiSliderFn, true));
_menuObjects.push_back(new MenuHorizSlider(this, OPTIONID_HSLIDER_DIGI, 47, 104, 212, 24,
0, &OrionCallbacks::optionsDigiSliderFn, true));
_escapeHandler = &OrionCallbacks::optionsEscapeFn;
_returnHandler = &OrionCallbacks::optionsReturnFn;
break;
case SAVE_MENU:
case LOAD_MENU:
loadSprites(MENU_SAVELOAD);
// Set up the defaults for the window
_topSaveSlotIndex = 0;
_selectedSlot = -1;
_highlightedSlot = -1;
_saveNames = _vm->_saveLoad->getSaves();
// Set up menu elements
_menuObjects.push_back(new MenuMessage(this, SLTAG_SAVELOAD_LABEL, 50, 241, 70, 16));
_menuObjects.push_back(new MenuButton(this, SLTAG_SAVELOAD, 214, 384, 72, 41,
(menuType == SAVE_MENU) ? &OrionCallbacks::saveLoadSaveFn : &OrionCallbacks::saveLoadLoadFn,
true, true, (menuType == SAVE_MENU) ? OBJTYPE_SL_SAVE : OBJTYPE_SL_LOAD));
_menuObjects.push_back(new MenuButton(this, SLTAG_CANCEL, 139, 384, 74, 43,
&OrionCallbacks::saveLoadCancelFn, false, false, OBJTYPE_SL_CANCEL));
_menuObjects.push_back(new MenuVertSlider(this, SLTAG_VSLIDER, 291, 255, 23, 127, 0,
&OrionCallbacks::saveLoadSliderFn));
if (_menuType == SAVE_MENU)
_savegameThumbnail = createThumbnail();
_menuObjects.push_back(new MenuImage(this, SLTAG_THUMBNAIL, 66, 28, 215, 162,
(_savegameThumbnail == NULL) ? _sprites->getFrame(SL_EMPTY_THUMBNAIL) : _savegameThumbnail));
{
SaveGameIterator slot = _saveNames->begin();
for (uint slotIndex = 0; slotIndex < SL_NUM_VISIBLE_SLOTS; ++slotIndex, ++slot) {
// Get save slot
bool isEmpty = (slotIndex >= _saveNames->size()) || (*slot).empty();
_menuObjects.push_back(new MenuSaveLoadText(this, SLTAG_SLOTS_START + slotIndex,
50, 256 + slotIndex * 15, 238, 15, &OrionCallbacks::saveLoadSlotFn,
(menuType == LOAD_MENU) && isEmpty, true, (menuType == LOAD_MENU),
isEmpty ? EmptySaveString : slot->c_str(), slotIndex + 1));
}
}
_escapeHandler = &OrionCallbacks::saveLoadEscapeFn;
_returnHandler = &OrionCallbacks::saveLoadReturnFn;
break;
default:
error("Unknown menu type");
break;
}
// Draw all the items onto the background surface
for (MenuObjectsIterator i = _menuObjects.begin(); i != _menuObjects.end(); ++i)
(*i)->onRefresh();
}
OrionMenuView::~OrionMenuView() {
delete _sprites;
for (MenuObjectList::iterator i = _menuObjects.begin(); i != _menuObjects.end(); ++i)
delete *i;
_menuObjects.clear();
if (_saveNames)
delete _saveNames;
if (_savegameThumbnail)
delete _savegameThumbnail;
}
bool OrionMenuView::loadSprites(const char *seriesName) {
Common::SeekableReadStream *data = _vm->res()->get(seriesName);
RGB8 *palette;
_sprites = new SpriteAsset(_vm, data, data->size(), seriesName);
palette = _sprites->getPalette();
_vm->_palette->setPalette(palette, 0, _sprites->getColorCount());
_vm->res()->toss(seriesName);
// Update the palette
//_vm->setPalette((byte *) _menuPalette, 59, 197);
// The first sprite is the menu background
M4Sprite *bg = _sprites->getFrame(0);
this->setSize(bg->width(), bg->height());
_coords.setWidth(bg->width());
_coords.setHeight(bg->height());
bg->copyTo(this);
return true;
}
// Creates a thumbnail based on the current background screen
M4Surface *OrionMenuView::createThumbnail() {
M4Surface srcSurface(_vm->_screen->width(), _vm->_screen->height());
M4Surface *result = new M4Surface(_vm->_screen->width() / 3, _vm->_screen->height() / 3);
// Translate the scene data
_vm->_scene->onRefresh(NULL, &srcSurface);
byte *srcP = (byte *)srcSurface.pixels;
byte *destP = (byte *)result->pixels;
for (int yCtr = 0; yCtr < _vm->_scene->height() / 3; ++yCtr, srcP += g_system->getWidth() * 3) {
byte *src0P = srcP;
byte *src1P = srcP + _vm->_screen->width();
byte *src2P = src1P + _vm->_screen->width();
for (int xCtr = 0; xCtr < result->width(); ++xCtr) {
*destP = (byte)((uint32)((
*src0P + *(src0P + 1) + *(src0P + 2) +
*src1P + *(src1P + 1) + *(src1P + 2) +
*src2P + *(src2P + 1) + *(src2P + 2)) / 9));
if (*destP == 0)
*destP = 21;
++destP;
src0P += 3;
src1P += 3;
src2P += 3;
}
}
// Translate the game interface view - since it's using standard colors that can't be
// averaged, simply take the top left pixel of every 3x3 pixel block
_vm->_interfaceView->onRefresh(NULL, &srcSurface);
destP = (byte *)result->pixels + (_vm->_screen->width() / 3) * (_vm->_interfaceView->bounds().top / 3);
int yStart = _vm->_interfaceView->bounds().top;
int yEnd = MIN(_vm->_screen->height() - 1, (int) _vm->_interfaceView->bounds().bottom - 1);
for (int yCtr = yStart; yCtr <= yEnd; yCtr += 3) {
srcP = (byte *)srcSurface.pixels + (yCtr * _vm->_screen->width());
for (int xCtr = 0; xCtr < result->width(); ++xCtr, srcP += 3)
*destP++ = *srcP;
}
return result;
}
void OrionMenuView::destroyView() {
M4Engine *engine = _vm;
bool interfaceVisible = _interfaceWasVisible;
engine->_viewManager->deleteView(this);
// Fade the game back in if no menu views are active (such as if a button was pressed in one menu
// to activate another menu dialog)
bool fadeIn = engine->_viewManager->getView(VIEWID_MENU) == NULL;
if (fadeIn) {
bool fadeToBlack = engine->_events->quitFlag;
engine->_ws->update();
engine->_palette->fadeFromGreen(M4_DIALOG_FADE_STEPS, M4_DIALOG_FADE_DELAY, fadeToBlack);
if (interfaceVisible)
engine->_interfaceView->show();
}
}
bool OrionMenuView::onEvent(M4EventType eventType, int param, int x, int y, bool &captureEvents) {
static Common::Point movingPos(0, 0);
static bool movingFlag = false;
bool handledFlag = false;
int localX, localY;
MenuObjectsIterator i;
if (!_screenFlags.visible)
return false;
if (!movingFlag)
captureEvents = false;
// If the escape key is pressed, then pass onto the Escape handler
if (eventType == KEVENT_KEY) {
if ((param == Common::KEYCODE_ESCAPE) && (_escapeHandler != NULL)) {
// Execute the Escape handler function
_currentItem = NULL;
captureEvents = false;
_escapeHandler(this);
destroyView();
return true;
}
if (((param == Common::KEYCODE_RETURN) || (param == Common::KEYCODE_KP_ENTER)) &&
(_returnHandler != NULL)) {
// Execute the Return handler function
_currentItem = NULL;
captureEvents = false;
_returnHandler(this);
return true;
}
MenuTextField *textItem = (MenuTextField *) getItem(SLTAG_TEXTFIELD);
if (textItem && textItem->onEvent(KEVENT_KEY, param, x, y, _currentItem))
return true;
}
// Convert the screen position to a relative position within the menu surface
localX = x - _coords.left;
localY = y - _coords.top;
// If there is an active object handling events, pass it on until it releases control
if (_currentItem) {
handledFlag = _currentItem->onEvent(eventType, param, localX, localY, _currentItem);
if (_closeFlag) {
// Dialog has been flagged to be closed
captureEvents = false;
destroyView();
return true;
}
if (_currentItem) {
captureEvents =
(Common::find(_menuObjects.begin(), _menuObjects.end(), _currentItem) != _menuObjects.end());
if (!captureEvents)
// The menu object is no longer active, so reset current item
_currentItem = NULL;
} else {
captureEvents = false;
}
if (handledFlag)
return true;
}
if (eventType == KEVENT_KEY) {
// Handle keypresses by looping through the item list to see if any of them want it
for (i = _menuObjects.begin(); (i != _menuObjects.end()) && !handledFlag; ++i) {
MenuObject *menuObj = *i;
MenuObject *dummyItem;
handledFlag = menuObj->onEvent(eventType, param, localX, localY, dummyItem);
}
return handledFlag;
} else {
// Handle mouse events by scanning the item list to see if the cursor is within any
for (i = _menuObjects.begin(); (i != _menuObjects.end()) && !handledFlag; ++i) {
MenuObject *menuObj = *i;
if (menuObj->isInside(localX, localY)) {
// Found an item, so pass it the event
menuObj->onEvent(eventType, param, localX, localY, _currentItem);
if (_closeFlag) {
// Dialog has been flagged to be closed
captureEvents = false;
destroyView();
return true;
}
if (_currentItem) {
captureEvents =
(Common::find(_menuObjects.begin(), _menuObjects.end(), _currentItem) != _menuObjects.end());
if (!captureEvents)
// The menu object is no longer active, so reset current item
_currentItem = NULL;
} else {
captureEvents = false;
}
return true;
}
}
}
// None of the items have handled the event, so fall back on menu-wide event handling
switch (eventType) {
case MEVENT_LEFT_CLICK:
case MEVENT_DOUBLECLICK:
if (!_screenFlags.immovable) {
// Move the entire dialog
captureEvents = true;
movingFlag = true;
movingPos.x = x;
movingPos.y = y;
}
break;
case MEVENT_LEFT_DRAG:
case MEVENT_DOUBLECLICK_DRAG:
if (movingFlag) {
moveRelative(x - movingPos.x, y - movingPos.y);
movingPos.x = x;
movingPos.y = y;
}
break;
case MEVENT_LEFT_RELEASE:
case MEVENT_DOUBLECLICK_RELEASE:
captureEvents = false;
movingFlag = false;
break;
default:
break;
}
return true;
}
MenuObject *OrionMenuView::getItem(int objectId) {
MenuObjectsIterator i;
for (i = _menuObjects.begin(); i != _menuObjects.end(); ++i) {
MenuObject *obj = *i;
if (obj->getObjectId() == objectId)
return obj;
}
return NULL;
}
void OrionMenuView::setTopSaveSlot(int slotNumber) {
_topSaveSlotIndex = MAX(MIN(slotNumber, 89), 0);
// Update the details of the load/save slots
// Get save slot
SaveGameIterator slot = _saveNames->begin();
for (int i = 0; i < _topSaveSlotIndex; ++i)
++slot;
for (uint index = 0; index < SL_NUM_VISIBLE_SLOTS; ++index, ++slot) {
MenuSaveLoadText *item = (MenuSaveLoadText *) getItem(SLTAG_SLOTS_START + index);
uint slotIndex = _topSaveSlotIndex + index;
bool isEmpty = (slotIndex >= _saveNames->size()) || slot->empty();
item->setDisplay(slotIndex + 1, isEmpty ? EmptySaveString : slot->c_str());
item->setState((_menuType == SAVE_MENU) || !isEmpty ? OS_NORMAL : OS_GREYED);
}
}
void OrionMenuView::refresh(const Common::Rect &areaRect) {
// Copy the selected portion of the background
_sprites->getFrame(0)->copyTo(this, areaRect, areaRect.left, areaRect.top);
for (MenuObjectsIterator i = _menuObjects.begin(); i != _menuObjects.end(); ++i) {
MenuObject *obj = *i;
if (obj->getBounds().intersects(areaRect))
obj->onRefresh();
}
}
}

106
engines/m4/m4_menus.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 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#ifndef M4_M4_MENUS_H
#define M4_M4_MENUS_H
#include "common/list.h"
#include "common/ptr.h"
#include "m4/viewmgr.h"
#include "m4/m4.h"
#include "m4/gui.h"
#include "m4/saveload.h"
namespace M4 {
#define M4_DIALOG_FADE_STEPS 5
#define M4_DIALOG_FADE_DELAY 30
typedef Common::List<MenuObject *> MenuObjectList;
class OrionMenuView: public DialogView {
typedef MenuObjectList::iterator MenuObjectsIterator;
private:
MenuType _menuType;
SpriteAsset *_sprites;
MenuObjectList _menuObjects;
MenuObject *_currentItem;
typedef void (*Callback)(OrionMenuView *view);
OrionMenuView::Callback _escapeHandler, _returnHandler;
bool _closeFlag;
bool _calledFromMainMenu;
bool _interfaceWasVisible;
int _firstSlotIndex;
bool loadSprites(const char *seriesName);
M4Surface *createThumbnail();
void destroyView();
public:
OrionMenuView(M4Engine *vm, int x, int y, MenuType menuType, bool calledFromMainMenu,
bool loadSaveFromHotkey);
~OrionMenuView();
MenuType getMenuType() { return _menuType; }
SpriteAsset *sprites() { return _sprites; }
MenuObjectList &items() { return _menuObjects; }
MenuObject *getItem(int objectId);
void setTopSaveSlot(int slotNumber);
void refresh(const Common::Rect &areaRect);
void close() { _closeFlag = true; }
bool onEvent(M4EventType eventType, int param, int x, int y, bool &captureEvents);
int _originalMidiVolume;
SaveGameList *_saveNames;
bool _loadSaveFromHotkey;
};
class OrionCallbacks {
public:
static void closeMenuFn(DialogView *view, MenuObject *item);
static void closeMenuFn(OrionMenuView *view);
static void gameOptionsMenuFn(DialogView *view, MenuObject *item);
static void gameSaveGameFn(DialogView *view, MenuObject *item);
static void gameLoadGameFn(DialogView *view, MenuObject *item);
static void gameExitFn(DialogView *view, MenuObject *item);
static void optionsDigiSliderFn(DialogView *view, MenuObject *item);
static void optionsMidiSliderFn(DialogView *view, MenuObject *item);
static void optionsScrollingFn(DialogView *view, MenuObject *item);
static void optionsCancelFn(DialogView *view, MenuObject *item);
static void optionsDoneFn(DialogView *view, MenuObject *item);
static void optionsReturnFn(OrionMenuView *view);
static void optionsEscapeFn(OrionMenuView *view);
static void saveLoadSaveFn(DialogView *view, MenuObject *item);
static void saveLoadLoadFn(DialogView *view, MenuObject *item);
static void saveLoadSlotFn(DialogView *view, MenuObject *item);
static void saveLoadCancelFn(DialogView *view, MenuObject *item);
static void saveLoadSliderFn(DialogView *view, MenuObject *item);
static void saveLoadEscapeFn(OrionMenuView *view);
static void saveLoadReturnFn(OrionMenuView *view);
};
}
#endif

345
engines/m4/m4_views.cpp Normal file
View file

@ -0,0 +1,345 @@
/* 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.
*
* $URL$
* $Id$
*
*/
#include "m4/m4_views.h"
#include "m4/events.h"
#include "m4/font.h"
#include "m4/globals.h"
#include "m4/m4.h"
namespace M4 {
GUIInventory::GUIInventory(View *owner, M4Engine *vm, const Common::Rect &bounds, int horizCells,
int vertCells, int cellWidth, int cellHeight, int tag): GUIRect(owner, bounds, tag) {
_vm = vm;
_cellCount.x = horizCells;
_cellCount.y = vertCells;
_cellSize.x = cellWidth;
_cellSize.y = cellHeight;
// Validate the cell settings
if ((_cellCount.x * _cellSize.x > _bounds.width()) ||
(_cellCount.y * _cellSize.y > _bounds.height()))
error("Cell settings for inventory display exceeded control size");
_visible = true;
_scrollPosition = 0;
_scrollable = false;
_highlightedIndex = -1;
_selectedIndex = -1;
}
void GUIInventory::onRefresh() {
_parent->fillRect(_bounds, _vm->_palette->BLACK);
//_parent->frameRect(_bounds, _vm->_palette->LIGHT_GRAY);
if (_visible) {
//kernel_trigger_dispatch(kernel_trigger_create(TRIG_INV_CLICK));
_scrollable = false;
// Get to the starting inventory position for display
ItemsIterator i = _inventoryItems.begin();
int index = _scrollPosition;
while (index-- > 0) ++i;
// Loop through displaying entries
for (index = 0; (i != _inventoryItems.end()) && (index < _cellCount.x * _cellCount.y); ++index, ++i) {
GUIInventoryItem *item = (*i).get();
const Common::Point cellPos = getCellPosition(index);
/* Common::Rect cellBounds(_bounds.left + cellPos.x + xOffset,
_bounds.top + cellPos.y + yOffset,
_bounds.left + cellPos.x + xOffset + _cellSize.x,
_bounds.top + cellPos.y + _cellSize.y);*/
Common::Rect cellBounds(_bounds.left + cellPos.x, _bounds.top + cellPos.y,
_bounds.left + cellPos.x + _cellSize.x, _bounds.top + cellPos.y + _cellSize.y);
Common::Point iconPt(
cellBounds.left + (cellBounds.width() - item->icon->width()) / 2,
cellBounds.top + (cellBounds.height() - item->icon->height()) / 2);
item->icon->copyTo(_parent, iconPt.x, iconPt.y, 0);
if (_highlightedIndex == index)
_parent->frameRect(Common::Rect(iconPt.x - 2, iconPt.y - 2,
iconPt.x + item->icon->width() + 2, iconPt.y + item->icon->height() + 2),
_vm->_palette->LIGHT_GRAY);
}
}
}
bool GUIInventory::onEvent(M4EventType eventType, int param, int x, int y, GUIObject *&currentItem) {
bool result = false;
int overIndex = getInsideIndex(x, y);
bool isPressed = (eventType == MEVENT_LEFT_CLICK) || (eventType == MEVENT_LEFT_HOLD) ||
(eventType == MEVENT_LEFT_DRAG);
ItemsIterator curItem = _inventoryItems.begin();
if (isPressed) {
if (_selectedIndex == -1 && overIndex != -1) {
setHighlight(overIndex);
_selectedIndex = overIndex;
for (int i = 0; i < _scrollPosition + _selectedIndex; i++)
++curItem;
if (_scrollPosition + _selectedIndex < (int)_inventoryItems.size())
_vm->_mouse->setCursorNum(curItem->get()->iconIndex);
result = true;
} else {
// We are over something being tracked
if (_selectedIndex == overIndex) {
setHighlight(overIndex);
result = true;
} else {
// Otherwise reset highlighting
setHighlight(-1);
result = false;
}
}
} else {
// No button pressed
if (_selectedIndex == overIndex) {
result = (_selectedIndex != -1);
} else {
result = (overIndex + _scrollPosition < (int)_inventoryItems.size());
if (result) {
for (int i = 0; i < overIndex + _scrollPosition; i++)
++curItem;
_vm->_interfaceView->setStatusText(curItem->get()->name);
}
}
// Stop tracking anything
setHighlight(overIndex);
}
return result;
}
void GUIInventory::add(const char *name, const char *verb, M4Surface *icon, int iconIndex) {
// First scan through the list to prevent duplicate objects
for (ItemsIterator i = _inventoryItems.begin(); i != _inventoryItems.end(); ++i) {
if (!strcmp(name, ((*i).get())->name))
return;
}
_inventoryItems.push_back(InventoryList::value_type(new GUIInventoryItem(name, verb, icon, iconIndex)));
}
bool GUIInventory::remove(const char *name) {
for (ItemsIterator i = _inventoryItems.begin(); i != _inventoryItems.end(); ++i) {
if (!strcmp(name, ((*i).get())->name)) {
_inventoryItems.erase(i);
_scrollPosition = 0;
return true;
}
}
return false;
}
int GUIInventory::getInsideIndex(int x, int y) {
if (!_bounds.contains(x, y))
return -1;
int localX = x - _bounds.left;
int localY = y - _bounds.top;
return (localX / _cellSize.x) * _cellCount.y + (localY / _cellSize.y);
}
const char *GUIInventory::getSelectedObjectName() {
if (_selectedIndex != -1) {
ItemsIterator curItem = _inventoryItems.begin();
for (int i = 0; i < _selectedIndex; i++)
++curItem;
return curItem->get()->name;
} else {
return NULL;
}
}
const Common::Point &GUIInventory::getCellPosition(int index) {
static Common::Point result;
if (_cellCount.x > _cellCount.y) {
// Horizontal orientation
result.x = (index / _cellCount.y) * _cellSize.x;
result.y = (index % _cellCount.y) * _cellSize.x;
} else {
// Vertical orientation
result.x = (index / _cellCount.x) * _cellSize.y;
result.y = (index % _cellCount.x) * _cellSize.y;
}
return result;
}
void GUIInventory::setHighlight(int index) {
if (_highlightedIndex == index)
return;
_highlightedIndex = index;
}
void GUIInventory::setScrollPosition(int value) {
if (value < 0)
return;
else if (value >= (int)_inventoryItems.size() - (_cellCount.x * _cellCount.y))
return;
_scrollPosition = value;
}
//--------------------------------------------------------------------------
const char *INTERFACE_SERIES = "999intr";
#define SPR(x) _sprites->getFrame(x)
GameInterfaceView::GameInterfaceView(M4Engine *vm):
View(vm, Common::Rect(0, vm->_screen->height() - INTERFACE_HEIGHT,
vm->_screen->width(), vm->_screen->height())),
_statusText(GUITextField(this, Common::Rect(200, 1, 450, 21))),
_inventory(GUIInventory(this, vm, Common::Rect(188, 22, 539, 97), 9, 1, 39, 75, 3)) {
_screenType = VIEWID_INTERFACE;
_screenFlags.layer = LAYER_INTERFACE;
_screenFlags.visible = false;
_screenFlags.get = SCREVENT_MOUSE;
_highlightedIndex = -1;
_selected = false;
Common::SeekableReadStream *data = _vm->res()->get(INTERFACE_SERIES);
RGB8 *palette;
_sprites = new SpriteAsset(_vm, data, data->size(), INTERFACE_SERIES);
palette = _sprites->getPalette();
//Palette.setPalette(palette, 0, _sprites->getColorCount());
_vm->res()->toss(INTERFACE_SERIES);
// Setup the interface buttons
_buttons.push_back(ButtonList::value_type(new GUIButton(this, Common::Rect(15, 35, 47, 66), 0, SPR(0), SPR(1), SPR(2)))); // look
_buttons.push_back(ButtonList::value_type(new GUIButton(this, Common::Rect(60, 35, 92, 66), 1, SPR(3), SPR(4), SPR(5)))); // take
_buttons.push_back(ButtonList::value_type(new GUIButton(this, Common::Rect(105, 35, 137, 66), 2, SPR(6), SPR(7), SPR(8)))); // manipulate
_buttons.push_back(ButtonList::value_type(new GUIButton(this, Common::Rect(580, 10, 620, 69), 3, SPR(69), SPR(70), SPR(71)))); // abduction
_buttons.push_back(ButtonList::value_type(new GUIButton(this, Common::Rect(582, 70, 619, 105), 4, SPR(76), SPR(77), SPR(78)))); // menu
_buttons.push_back(ButtonList::value_type(new GUIButton(this, Common::Rect(168, 22, 188, 97), 5, SPR(60), SPR(61), SPR(62)))); // Scroll left
_buttons.push_back(ButtonList::value_type(new GUIButton(this, Common::Rect(539, 22, 559, 97), 6, SPR(64), SPR(65), SPR(66)))); // Scroll right
}
#undef SPR
GameInterfaceView::~GameInterfaceView() {
delete _sprites;
}
void GameInterfaceView::setHighlightedButton(int index) {
if (index == _highlightedIndex)
return;
_selected = (index == -1);
_highlightedIndex = index;
}
bool GameInterfaceView::onEvent(M4EventType eventType, int param, int x, int y, bool &captureEvents) {
static bool selectionFlag = false;
if (eventType == MEVENT_LEFT_RELEASE)
selectionFlag = false;
captureEvents = isInside(x, y);
if (!captureEvents)
return false;
int localX = x - _coords.left;
int localY = y - _coords.top;
GUIObject *currentItem;
_statusText.onEvent(eventType, param, localX, localY, currentItem);
_inventory.onEvent(eventType, param, localX, localY, currentItem);
if (_vm->_mouse->getCursorNum() != CURSOR_LOOK &&
_vm->_mouse->getCursorNum() != CURSOR_TAKE &&
_vm->_mouse->getCursorNum() != CURSOR_USE &&
_vm->_interfaceView->_inventory.getSelectedIndex() == -1) {
if (_vm->_mouse->getCursorNum() != 0)
_vm->_mouse->setCursorNum(0);
}
for (ButtonsIterator i = _buttons.begin(); i != _buttons.end(); ++i) {
GUIButton *btn = (*i).get();
btn->onEvent(eventType, param, localX, localY, currentItem);
if ((btn->getState() == BUTTON_PRESSED) && !selectionFlag) {
selectionFlag = true;
_highlightedIndex = btn->getTag();
switch (_highlightedIndex) {
case 0:
_vm->_mouse->setCursorNum(CURSOR_LOOK);
break;
case 1:
_vm->_mouse->setCursorNum(CURSOR_TAKE);
break;
case 2:
_vm->_mouse->setCursorNum(CURSOR_USE);
break;
case 3:
// TODO: Jump to abduction
break;
case 4:
_vm->loadMenu(GAME_MENU);
break;
case 5:
_inventory.scrollLeft();
break;
case 6:
_inventory.scrollRight();
break;
default:
break;
}
}
}
return true;
}
void GameInterfaceView::onRefresh(RectList *rects, M4Surface *destSurface) {
empty();
_statusText.onRefresh();
_inventory.onRefresh();
for (ButtonsIterator i = _buttons.begin(); i != _buttons.end(); ++i)
((*i).get())->onRefresh();
View::onRefresh(rects, destSurface);
}
} // End of namespace M4

118
engines/m4/m4_views.h Normal file
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 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#ifndef M4_M4_VIEWS_H
#define M4_M4_VIEWS_H
#include "m4/gui.h"
#include "m4/viewmgr.h"
#include "common/rect.h"
#include "common/list.h"
#include "common/ptr.h"
namespace M4 {
class GUIInventoryItem {
public:
const char *name;
const char *verb;
M4Surface *icon;
int iconIndex;
GUIInventoryItem(const char *_name, const char *_verb, M4Surface *_icon, int _iconIndex) {
name = _name; _verb = verb; icon = _icon; iconIndex = _iconIndex;
}
};
class GUIInventory: public GUIRect {
typedef Common::List<Common::SharedPtr<GUIInventoryItem> > InventoryList;
typedef InventoryList::iterator ItemsIterator;
private:
InventoryList _inventoryItems;
Common::Point _cellSize;
Common::Point _cellCount;
bool _visible;
bool _scrollable;
int _scrollPosition;
int _highlightedIndex;
int _selectedIndex;
M4Engine *_vm;
public:
GUIInventory(View *owner, M4Engine *vm, const Common::Rect &bounds,
int horizCells, int vertCells, int cellWidth, int cellHeight, int tag);
void onRefresh();
bool onEvent(M4EventType eventType, int param, int x, int y, GUIObject *&currentItem);
void add(const char *name, const char *verb, M4Surface *icon, int iconIndex);
bool remove(const char *name);
int getInsideIndex(int x, int y);
int getSelectedIndex() { return _selectedIndex; }
const char *getSelectedObjectName();
void clearSelected() {
_selectedIndex = -1;
setHighlight(-1);
}
const Common::Point &getCellPosition(int index);
void setHighlight(int index);
bool needLeftButton() { return _scrollPosition != 0; }
bool needRightButton() {
return (uint)(_inventoryItems.size() - _scrollPosition) > (uint)(_cellCount.x * _cellCount.y);
}
void setScrollPosition(int value);
void scrollLeft() { setScrollPosition(_scrollPosition - 1); }
void scrollRight() { setScrollPosition(_scrollPosition + 1); }
void setVisible(bool value) { _visible = value; }
};
class GameInterfaceView: public View {
typedef Common::List<Common::SharedPtr<GUIButton> > ButtonList;
typedef ButtonList::iterator ButtonsIterator;
public:
SpriteAsset *_sprites;
ButtonList _buttons;
GUITextField _statusText;
GUIInventory _inventory;
int _highlightedIndex;
bool _selected;
private:
void setHighlightedButton(int index);
public:
GameInterfaceView(M4Engine *vm);
~GameInterfaceView();
void onRefresh(RectList *rects, M4Surface *destSurface);
bool onEvent(M4EventType eventType, int param, int x, int y, bool &captureEvents);
void setStatusText(const char *text) { _statusText.setText(text); }
void cancelSentence() { setStatusText(NULL); }
void inventoryAdd(const char *name, const char *verb, int iconIndex) {
_inventory.add(name, verb, _sprites->getFrame(iconIndex), iconIndex);
}
bool inventoryRemove(const char *name) { return _inventory.remove(name); }
};
}
#endif

705
engines/m4/mads_anim.cpp Normal file
View file

@ -0,0 +1,705 @@
/* 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.
*
* $URL$
* $Id$
*
*/
#include "m4/mads_anim.h"
#include "m4/m4.h"
namespace M4 {
#define TEXTVIEW_LINE_SPACING 2
#define TEXT_ANIMATION_DELAY 100
#define TV_NUM_FADE_STEPS 40
#define TV_FADE_DELAY_MILLI 50
TextviewView::TextviewView(M4Engine *vm):
View(vm, Common::Rect(0, 0, vm->_screen->width(), vm->_screen->height())),
_bgSurface(vm->_screen->width(), MADS_SURFACE_HEIGHT),
_textSurface(vm->_screen->width(), MADS_SURFACE_HEIGHT + vm->_font->getHeight() +
TEXTVIEW_LINE_SPACING) {
_screenType = VIEWID_TEXTVIEW;
_screenFlags.layer = LAYER_BACKGROUND;
_screenFlags.visible = true;
_screenFlags.get = SCREVENT_ALL;
_callback = NULL;
_script = NULL;
_spareScreen = NULL;
_bgCurrent = NULL;
_bgSpare = NULL;
reset();
// Set up system palette colors and the two colors for text display
_vm->_palette->setMadsSystemPalette();
RGB8 palData[3];
palData[0].r = palData[0].g = palData[0].b = 0;
palData[1].r = 0; palData[1].g = palData[1].b = 252;
palData[2].r = 0; palData[2].g = palData[2].b = 180;
_vm->_palette->setPalette(&palData[0], 4, 3);
_vm->_palette->blockRange(4, 3);
_vm->_font->setColors(5, 6, 4);
empty();
_bgSurface.empty();
_textSurface.empty();
int y = (height() - MADS_SURFACE_HEIGHT) / 2;
setColor(2);
hLine(0, width() - 1, y - 2);
hLine(0, width() - 1, height() - y + 1);
}
TextviewView::~TextviewView() {
if (_script)
_vm->res()->toss(_resourceName);
if (_spareScreen)
delete _spareScreen;
if (_bgCurrent)
delete _bgCurrent;
if (_bgSpare)
delete _bgSpare;
}
void TextviewView::reset() {
_bgSurface.empty();
_textSurface.empty();
_animating = false;
_panX = 0;
_panY = 0;
_panSpeed = 0;
_soundDriverLoaded = false;
Common::set_to(&_spareScreens[0], &_spareScreens[10], 0);
_scrollCount = 0;
_lineY = -1;
_scrollTimeout = 0;
_panCountdown = 0;
_processEvents = true;
}
void TextviewView::setScript(const char *resourceName, TextviewCallback callback) {
_callback = callback;
if (_script)
_vm->res()->toss(_resourceName);
if (_spareScreen) {
delete _spareScreen;
_spareScreen = NULL;
}
reset();
strncpy(_resourceName, resourceName, 15);
_resourceName[15] = '\0';
if (!strchr(_resourceName, '.'))
strcat(_resourceName, ".txr");
_script = _vm->res()->get(_resourceName);
processLines();
}
bool TextviewView::onEvent(M4EventType eventType, int param, int x, int y, bool &captureEvents) {
if (!_processEvents)
return false;
// Wait for the Escape key or a mouse press
if (((eventType == KEVENT_KEY) && (param == Common::KEYCODE_ESCAPE)) ||
(eventType == MEVENT_LEFT_RELEASE) || (eventType == MEVENT_RIGHT_RELEASE)) {
scriptDone();
captureEvents = false;
return true;
}
return false;
}
void TextviewView::updateState() {
if (!_animating)
return;
// Only update state if wait period has expired
uint32 currTime = g_system->getMillis();
// If a screen transition is in progress and it's time for another column, handle it
if (_spareScreen) {
byte *srcP = _spareScreen->getBasePtr(_translationX, 0);
byte *destP = _bgSurface.getBasePtr(_translationX, 0);
for (int y = 0; y < _bgSurface.height(); ++y, srcP += _spareScreen->width(),
destP += _bgSurface.width()) {
*destP = *srcP;
}
if (++_translationX >= _bgSurface.width()) {
// Surface transition is complete
delete _spareScreen;
_spareScreen = NULL;
_vm->_palette->deleteRange(_bgCurrent);
delete _bgCurrent;
_bgCurrent = _bgSpare;
_bgSpare = NULL;
}
}
// Make sure it's time for an update
if (currTime < _scrollTimeout)
return;
_scrollTimeout = g_system->getMillis() + TEXT_ANIMATION_DELAY;
// If any panning values are set, pan the background surface
if ((_panX != 0) || (_panY != 0)) {
if (_panCountdown > 0) {
--_panCountdown;
return;
}
// Handle horizontal panning
if (_panX != 0) {
byte *lineTemp = new byte[_panX];
for (int y = 0; y < _bgSurface.height(); ++y) {
byte *pixelsP = _bgSurface.getBasePtr(0, y);
// Copy the first X pixels into temp buffer, move the rest of the line
// to the start of the line, and then move temp buffer pixels to end of line
Common::copy(pixelsP, pixelsP + _panX, lineTemp);
Common::copy(pixelsP + _panX, pixelsP + _bgSurface.width(), pixelsP);
Common::copy(lineTemp, lineTemp + _panX, pixelsP + _bgSurface.width() - _panX);
}
delete[] lineTemp;
}
// Handle vertical panning
if (_panY != 0) {
// Store the bottom Y lines into a temp buffer, move the rest of the lines down,
// and then copy the stored lines back to the top of the screen
byte *linesTemp = new byte[_panY * _bgSurface.width()];
byte *pixelsP = _bgSurface.getBasePtr(0, _bgSurface.height() - _panY);
Common::copy(pixelsP, pixelsP + _bgSurface.width() * _panY, linesTemp);
for (int y = _bgSurface.height() - 1; y >= _panY; --y) {
byte *destP = _bgSurface.getBasePtr(0, y);
byte *srcP = _bgSurface.getBasePtr(0, y - _panY);
Common::copy(srcP, srcP + _bgSurface.width(), destP);
}
Common::copy(linesTemp, linesTemp + _panY * _bgSurface.width(), (byte *)_bgSurface.pixels);
delete[] linesTemp;
}
}
// Scroll the text surface up by one row
byte *pixelsP = (byte *)_textSurface.pixels;
Common::copy(pixelsP + width(), pixelsP + _textSurface.width() * _textSurface.height(), pixelsP);
pixelsP = _textSurface.getBasePtr(0, _textSurface.height() - 1);
Common::set_to(pixelsP, pixelsP + _textSurface.width(), _vm->_palette->BLACK);
if (_scrollCount > 0) {
// Handling final scrolling of text off of screen
if (--_scrollCount == 0) {
scriptDone();
return;
}
} else {
// Handling a text row
if (++_lineY == (_vm->_font->getHeight() + TEXTVIEW_LINE_SPACING))
processLines();
}
// Refresh the view
int yp = (height() - _bgSurface.height()) / 2;
_bgSurface.copyTo(this, 0, yp);
_textSurface.copyTo(this, Common::Rect(0, 0, _textSurface.width(), _bgSurface.height()),
0, yp, _vm->_palette->BLACK);
}
void TextviewView::scriptDone() {
TextviewCallback fn = _callback;
M4Engine *vm = _vm;
// Remove this view from manager and destroy it
_vm->_viewManager->deleteView(this);
if (fn)
fn(vm);
}
void TextviewView::processLines() {
if (_script->eos())
error("Attempted to read past end of response file");
while (!_script->eos()) {
_script->readLine(_currentLine, 79);
// Commented out line, so go loop for another
if (_currentLine[0] == '#')
continue;
// Process the line
char *cStart = strchr(_currentLine, '[');
if (cStart) {
while (cStart) {
// Loop for possible multiple commands on one line
char *cEnd = strchr(_currentLine, ']');
if (!cEnd)
error("Unterminated command '%s' in response file", _currentLine);
*cEnd = '\0';
processCommand();
// Copy rest of line (if any) to start of buffer
strcpy(_currentLine, cEnd + 1);
cStart = strchr(_currentLine, '[');
}
if (_currentLine[0]) {
processText();
break;
}
} else {
processText();
break;
}
}
}
void TextviewView::processCommand() {
char commandStr[80];
char *paramP;
strcpy(commandStr, _currentLine + 1);
str_upper(commandStr);
if (!strncmp(commandStr, "BACKGROUND", 10)) {
// Set the background
paramP = commandStr + 10;
int screenId = getParameter(&paramP);
_bgSurface.loadBackground(screenId, &_bgCurrent);
_vm->_palette->addRange(_bgCurrent);
_bgSurface.translate(_bgCurrent);
} else if (!strncmp(commandStr, "GO", 2)) {
_animating = true;
// Grab what the final palete will be
RGB8 destPalette[256];
_vm->_palette->grabPalette(destPalette, 0, 256);
// Copy the loaded background, if any, to the view surface
int yp = (height() - _bgSurface.height()) / 2;
_bgSurface.copyTo(this, 0, yp);
// Handle fade-in
_processEvents = false; // stop processing events during fade-in
_vm->_palette->fadeIn(TV_NUM_FADE_STEPS, TV_FADE_DELAY_MILLI, destPalette, 256);
_processEvents = true;
} else if (!strncmp(commandStr, "PAN", 3)) {
// Set panning values
paramP = commandStr + 3;
int panX = getParameter(&paramP);
int panY = getParameter(&paramP);
int panSpeed = getParameter(&paramP);
if ((panX != 0) || (panY != 0)) {
_panX = panX;
_panY = panY;
_panSpeed = panSpeed;
}
} else if (!strncmp(commandStr, "DRIVER", 6)) {
// Set the driver to use
// TODO: Handling of the sound drivers
} else if (!strncmp(commandStr, "SOUND", 5)) {
// Set sound number
paramP = commandStr + 5;
//int soundId = getParameter(&paramP);
//TODO: Proper handling of the sound drivers/sounds
//if (!_soundDriverLoaded)
// error("Attempted to set sound without loading any driver\n");
} else if (!strncmp(commandStr, "COLOR", 5) && ((commandStr[5] == '0') || (commandStr[5] == '1'))) {
// Set the text colors
int index = commandStr[5] - '0';
paramP = commandStr + 6;
RGB8 palEntry;
palEntry.r = getParameter(&paramP) << 2;
palEntry.g = getParameter(&paramP) << 2;
palEntry.b = getParameter(&paramP) << 2;
_vm->_palette->setPalette(&palEntry, 5 + index, 1);
} else if (!strncmp(commandStr, "SPARE", 5)) {
// Sets a secondary background number that can be later switched in with a PAGE command
paramP = commandStr + 6;
int spareIndex = commandStr[5] - '0';
if ((spareIndex >= 0) && (spareIndex <= 9)) {
int screenId = getParameter(&paramP);
_spareScreens[spareIndex] = screenId;
}
} else if (!strncmp(commandStr, "PAGE", 4)) {
// Signals to change to a previous specified secondary background
paramP = commandStr + 4;
int spareIndex = getParameter(&paramP);
// Only allow background switches if one isn't currently in progress
if (!_spareScreen && (_spareScreens[spareIndex] != 0)) {
_spareScreen = new M4Surface(width(), MADS_SURFACE_HEIGHT);
_spareScreen->loadBackground(_spareScreens[spareIndex], &_bgSpare);
_vm->_palette->addRange(_bgSpare);
_spareScreen->translate(_bgSpare);
_translationX = 0;
}
} else {
error("Unknown response command: '%s'", commandStr);
}
}
int TextviewView::getParameter(char **paramP) {
if ((**paramP != '=') && (**paramP != ','))
return 0;
int result = 0;
++*paramP;
while ((**paramP >= '0') && (**paramP <= '9')) {
result = result * 10 + (**paramP - '0');
++*paramP;
}
return result;
}
void TextviewView::processText() {
int lineWidth, xStart;
if (!strcmp(_currentLine, "***")) {
// Special signifier for end of script
_scrollCount = _vm->_font->getHeight() * 13;
_lineY = -1;
return;
}
_lineY = 0;
// Lines are always centered, except if line contains a '@', in which case the
// '@' marks the position that must be horizontally centered
char *centerP = strchr(_currentLine, '@');
if (centerP) {
*centerP = '\0';
xStart = (width() / 2) - _vm->_font->getWidth(_currentLine);
// Delete the @ character and shift back the remainder of the string
char *p = centerP + 1;
if (*p == ' ') ++p;
strcpy(centerP, p);
} else {
lineWidth = _vm->_font->getWidth(_currentLine);
xStart = (width() - lineWidth) / 2;
}
// Copy the text line onto the bottom of the textSurface surface, which will allow it
// to gradually scroll onto the screen
int yp = _textSurface.height() - _vm->_font->getHeight() - TEXTVIEW_LINE_SPACING;
_textSurface.fillRect(Common::Rect(0, yp, _textSurface.width(), _textSurface.height()),
_vm->_palette->BLACK);
_vm->_font->writeString(&_textSurface, _currentLine, xStart, yp);
}
//--------------------------------------------------------------------------
AnimviewView::AnimviewView(M4Engine *vm):
View(vm, Common::Rect(0, 0, vm->_screen->width(), vm->_screen->height())),
_bgSurface(vm->_screen->width(), MADS_SURFACE_HEIGHT) {
_screenType = VIEWID_ANIMVIEW;
_screenFlags.layer = LAYER_BACKGROUND;
_screenFlags.visible = true;
_screenFlags.get = SCREVENT_ALL;
_callback = NULL;
_script = NULL;
_palData = NULL;
_previousUpdate = 0;
_transition = kTransitionNone;
reset();
// Set up system palette colors
_vm->_palette->setMadsSystemPalette();
empty();
_bgSurface.empty();
int y = (height() - MADS_SURFACE_HEIGHT) / 2;
setColor(2);
hLine(0, width() - 1, y - 2);
hLine(0, width() - 1, height() - y + 1);
}
AnimviewView::~AnimviewView() {
if (_script)
_vm->res()->toss(_resourceName);
}
void AnimviewView::reset() {
_bgSurface.empty();
_soundDriverLoaded = false;
}
void AnimviewView::setScript(const char *resourceName, AnimviewCallback callback) {
_callback = callback;
if (_script)
_vm->res()->toss(_resourceName);
reset();
strncpy(_resourceName, resourceName, 15);
_resourceName[15] = '\0';
if (!strchr(_resourceName, '.'))
strcat(_resourceName, ".res");
_script = _vm->res()->get(_resourceName);
processLines();
}
bool AnimviewView::onEvent(M4EventType eventType, int param, int x, int y, bool &captureEvents) {
// Wait for the Escape key or a mouse press
if (((eventType == KEVENT_KEY) && (param == Common::KEYCODE_ESCAPE)) ||
(eventType == MEVENT_LEFT_RELEASE) || (eventType == MEVENT_RIGHT_RELEASE)) {
scriptDone();
captureEvents = false;
return true;
}
return false;
}
void AnimviewView::updateState() {
char bgFile[10];
int bgNumber = 0;
// Only update state if wait period has expired
if (_previousUpdate > 0) {
if (g_system->getMillis() - _previousUpdate < 3000) {
return;
} else {
// time for an update
_previousUpdate = g_system->getMillis();
}
} else {
_previousUpdate = g_system->getMillis();
return;
}
uint32 currTime = g_system->getMillis();
strncpy(bgFile, _currentFile, 5);
bgFile[0] = bgFile[2];
bgFile[1] = bgFile[3];
bgFile[2] = bgFile[4];
bgFile[3] = '\0';
bgNumber = atoi(bgFile);
sprintf(bgFile, "rm%i.art", bgNumber);
// Not all scenes have a background. If there is one, refresh it
if (_vm->_resourceManager->resourceExists(bgFile)) {
if (_palData) {
_vm->_palette->deleteRange(_palData);
delete _palData;
}
_bgSurface.loadBackground(bgNumber, &_palData);
_vm->_palette->addRange(_palData);
_bgSurface.translate(_palData);
}
// Grab what the final palete will be
RGB8 destPalette[256];
_vm->_palette->grabPalette(destPalette, 0, 256);
// Handle scene transition
switch (_transition) {
case kTransitionNone:
// nothing to do
break;
case kTransitionFadeIn:
case kTransitionFadeIn2:
_vm->_palette->fadeIn(TV_NUM_FADE_STEPS, TV_FADE_DELAY_MILLI, destPalette, 256);
break;
case kTransitionBoxInBottomLeft:
case kTransitionBoxInBottomRight:
case kTransitionBoxInTopLeft:
case kTransitionBoxInTopRight:
// unused
warning("Unsupported box in scene effect");
break;
case kTransitionPanLeftToRight:
// TODO
break;
case kTransitionPanRightToLeft:
// TODO
break;
case kTransitionCircleIn:
// TODO
break;
default:
// nothing to do
break;
}
// Refresh the view
int yp = (height() - _bgSurface.height()) / 2;
_bgSurface.copyTo(this, 0, yp);
// Read next line
processLines();
}
void AnimviewView::scriptDone() {
AnimviewCallback fn = _callback;
M4Engine *vm = _vm;
// Remove this view from manager and destroy it
_vm->_viewManager->deleteView(this);
if (fn)
fn(vm);
}
void AnimviewView::processLines() {
if (_script->eos()) {
// end of script, end animation
scriptDone();
return;
}
while (!_script->eos()) {
_script->readLine(_currentLine, 79);
// Process the line
char *cStart = strchr(_currentLine, '-');
if (cStart) {
while (cStart) {
// Loop for possible multiple commands on one line
char *cEnd = strchr(_currentLine, ' ');
if (!cEnd)
error("Unterminated command '%s' in response file", _currentLine);
*cEnd = '\0';
processCommand();
// Copy rest of line (if any) to start of buffer
// Don't use strcpy() here, because if the
// rest of the line is the longer of the two
// strings, the memory areas will overlap.
memmove(_currentLine, cEnd + 1, strlen(cEnd + 1) + 1);
cStart = strchr(_currentLine, '-');
}
if (_currentLine[0]) {
sprintf(_currentFile, "%s", _currentLine);
//printf("File: %s\n", _currentLine);
break;
}
} else {
sprintf(_currentFile, "%s", _currentLine);
//printf("File: %s\n", _currentLine);
break;
}
}
}
/*
Switches are: (taken from the help of the original executable)
-b Toggle background load status off/on.
-c:char Specify sound card id letter.
-g Stay in graphics mode on exit.
-h[:ex] Disable EMS/XMS high memory support.
-i Switch sound interrupts mode off/on.
-j Wait for music to finish at end.
-k Keystroke jumps to end instead of abort.
-m Operate in non-MADS mode.
-o:xxx Specify opening special effect.
-p Switch MADS path mode to CONCAT.
-r[:abn] Resynch timer (always, beginning, never).
-s:file Specify sound file.
-u[:...] Use DMA speech [optional: addr,type,irq,drq].
-w Toggle white bars off/on.
-x Exit immediately after last frame.
-y Do not clear screen initially
-z Display statistics after run.
Opening special effects are:
0: no effect
1: fade in
2: fade in (looks to be the same as 1)
3: box in from bottom left (unused)
4: box in from bottom right (unused)
5: box in from top left (unused)
6: box in from top right (unused)
7: pan in from left to right
8: pan in from right to left
9: circle in (new scene appears in a circle that expands)
Animview is ran like this from the original games:
animview.exe @resfilename -c:P,220,20 -u:220,20,07,01 -p -a:mainmenu -p
Note that the first -p is necessary to watch the animation, otherwise
the program just exits
To watch an animation within the *.res file, just run animview like this:
animview.exe -x -r:b -o:2 animfilename -p
*/
void AnimviewView::processCommand() {
char commandStr[80];
strcpy(commandStr, _currentLine + 1);
str_upper(commandStr);
char *param = commandStr;
if (!strncmp(commandStr, "X", 1)) {
//printf("X ");
} else if (!strncmp(commandStr, "W", 1)) {
//printf("W ");
} else if (!strncmp(commandStr, "R", 1)) {
param = param + 2;
//printf("R:%s ", param);
} else if (!strncmp(commandStr, "O", 1)) {
param = param + 2;
//printf("O:%i ", atoi(param));
_transition = atoi(param);
} else {
error("Unknown response command: '%s'", commandStr);
}
}
}

118
engines/m4/mads_anim.h Normal file
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 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#ifndef M4_MADS_ANIM_H
#define M4_MADS_ANIM_H
#include "m4/viewmgr.h"
namespace M4 {
enum SceneTransition {
kTransitionNone = 0,
kTransitionFadeIn = 1,
kTransitionFadeIn2 = 2,
kTransitionBoxInBottomLeft = 3,
kTransitionBoxInBottomRight = 4,
kTransitionBoxInTopLeft = 5,
kTransitionBoxInTopRight = 6,
kTransitionPanLeftToRight = 7,
kTransitionPanRightToLeft = 8,
kTransitionCircleIn = 9
};
typedef void (*TextviewCallback)(M4Engine *vm);
class TextviewView: public View {
private:
bool _animating;
char _resourceName[80];
Common::SeekableReadStream *_script;
uint16 _spareScreens[10];
M4Surface *_spareScreen;
RGBList *_bgCurrent, *_bgSpare;
int _translationX;
int _panX, _panY, _panSpeed;
int _panCountdown;
char _currentLine[80];
uint32 _scrollTimeout;
int _scrollCount;
int _lineY;
M4Surface _bgSurface;
M4Surface _textSurface;
TextviewCallback _callback;
bool _soundDriverLoaded;
bool _processEvents;
void reset();
void processLines();
void processCommand();
void processText();
int getParameter(char **paramP);
public:
TextviewView(M4Engine *vm);
~TextviewView();
void setScript(const char *resourceName, TextviewCallback callback);
bool isAnimating() { return _animating; }
void scriptDone();
bool onEvent(M4EventType eventType, int param1, int x, int y, bool &captureEvents);
void updateState();
};
typedef void (*AnimviewCallback)(M4Engine *vm);
class AnimviewView: public View {
private:
char _resourceName[80];
Common::SeekableReadStream *_script;
uint32 _previousUpdate;
char _currentLine[80];
char _currentFile[10];
M4Surface _bgSurface;
AnimviewCallback _callback;
bool _soundDriverLoaded;
RGBList *_palData;
int _transition;
void reset();
void processLines();
void processCommand();
public:
AnimviewView(M4Engine *vm);
~AnimviewView();
void setScript(const char *resourceName, AnimviewCallback callback);
void scriptDone();
bool onEvent(M4EventType eventType, int param1, int x, int y, bool &captureEvents);
void updateState();
};
}
#endif

586
engines/m4/mads_menus.cpp Normal file
View file

@ -0,0 +1,586 @@
/* 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.
*
* $URL$
* $Id$
*
*/
#include "m4/mads_menus.h"
#include "m4/m4.h"
namespace M4 {
#define REX_MENUSCREEN 990
#define PHANTOM_MENUSCREEN 920
#define DRAGON_MENUSCREEN 922
static Common::Point rexMenuItemPosList[6] = {
Common::Point(12, 68), Common::Point(12, 87), Common::Point(12, 107),
Common::Point(184, 75), Common::Point(245, 75), Common::Point(184, 99)
};
static Common::Point dragonMenuItemPosList[6] = {
Common::Point(46, 187), Common::Point(92, 187), Common::Point(138, 187),
Common::Point(184, 187), Common::Point(230, 187), Common::Point(276, 187)
};
#define DRAGON_MENU_BUTTON_W = 45
#define DRAGON_MENU_BUTTON_H = 11
RexMainMenuView::RexMainMenuView(M4Engine *vm):
View(vm, Common::Rect(0, 0, vm->_screen->width(), vm->_screen->height())) {
_screenType = VIEWID_MAINMENU;
_screenFlags.get = SCREVENT_ALL;
_delayTimeout = 0;
_menuItem = NULL;
_menuItemIndex = 0;
_frameIndex = 0;
_highlightedIndex = -1;
_skipFlag = false;
// Load the background for the Rex Nebular game
_bgSurface = new M4Surface(width(), MADS_SURFACE_HEIGHT);
_bgSurface->loadBackground(REX_MENUSCREEN, &_bgPalData);
_vm->_palette->addRange(_bgPalData);
_bgSurface->translate(_bgPalData);
int row = (height() - MADS_SURFACE_HEIGHT) / 2;
_bgSurface->copyTo(this, 0, row);
// Add in the bounding lines for the background
setColor(2);
hLine(0, width() - 1, row - 1);
hLine(0, width() - 1, height() - row + 1);
}
RexMainMenuView::~RexMainMenuView() {
if (_menuItem)
delete _menuItem;
_vm->_palette->deleteRange(_bgPalData);
delete _bgPalData;
delete _bgSurface;
for (uint i = 0; i < _itemPalData.size(); ++i) {
_vm->_palette->deleteRange(_itemPalData[i]);
delete _itemPalData[i];
}
}
bool RexMainMenuView::onEvent(M4EventType eventType, int param, int x, int y, bool &captureEvents) {
// Handle keypresses - these can be done at any time, even when the menu items are being drawn
if (eventType == KEVENT_KEY) {
switch (param) {
case Common::KEYCODE_ESCAPE:
case Common::KEYCODE_F6:
handleAction(EXIT);
break;
case Common::KEYCODE_F1:
handleAction(START_GAME);
break;
case Common::KEYCODE_F2:
handleAction(RESUME_GAME);
break;
case Common::KEYCODE_F3:
handleAction(SHOW_INTRO);
break;
case Common::KEYCODE_F4:
handleAction(CREDITS);
break;
case Common::KEYCODE_F5:
handleAction(QUOTES);
break;
case Common::KEYCODE_s:
// Goodness knows why, but Rex has a key to restart the menuitem animations
// Delete the current menu items
if (_menuItem)
delete _menuItem;
_vm->_palette->deleteRange(_bgPalData);
delete _bgPalData;
for (uint i = 0; i < _itemPalData.size(); ++i) {
_vm->_palette->deleteRange(_itemPalData[i]);
delete _itemPalData[i];
}
_itemPalData.clear();
// Reload the background surface, and restart the animation
_bgSurface->loadBackground(REX_MENUSCREEN, &_bgPalData);
_vm->_palette->addRange(_bgPalData);
_bgSurface->translate(_bgPalData);
_menuItemIndex = 0;
_skipFlag = false;
_menuItem = NULL;
_vm->_mouse->cursorOff();
break;
default:
// Any other key skips the menu animation
_skipFlag = true;
return false;
}
return true;
}
int row = (height() - MADS_SURFACE_HEIGHT) / 2;
int menuIndex;
switch (eventType) {
case MEVENT_LEFT_CLICK:
case MEVENT_LEFT_DRAG:
if (_vm->_mouse->getCursorOn()) {
menuIndex = getHighlightedItem(x, y);
if (menuIndex != _highlightedIndex) {
_bgSurface->copyTo(this, 0, row);
_highlightedIndex = menuIndex;
if (_highlightedIndex != -1) {
M4Sprite *spr = _menuItem->getFrame(_highlightedIndex);
const Common::Point &pt = rexMenuItemPosList[_highlightedIndex];
spr->copyTo(this, pt.x, row + pt.y, 0);
}
}
} else {
// Skip the menu animation
_skipFlag = true;
}
return true;
case MEVENT_LEFT_RELEASE:
if (_highlightedIndex != -1)
handleAction((MadsGameAction) _highlightedIndex);
return true;
default:
break;
}
return false;
}
void RexMainMenuView::updateState() {
char resName[20];
Common::SeekableReadStream *data;
int row = (height() - MADS_SURFACE_HEIGHT) / 2;
int itemSize;
uint32 currTime = g_system->getMillis();
if (currTime < _delayTimeout)
return;
_delayTimeout = currTime + MADS_MENU_ANIM_DELAY;
// Rex Nebular handling to cycle through the animated display of the menu items
if (_menuItemIndex == 7)
return;
// If the user has chosen to skip the menu animation, show the menu immediately
if (_skipFlag && !_vm->_mouse->getCursorOn()) {
// Clear any pending animation
_bgSurface->copyTo(this, 0, row);
// Quickly loop through all the menuitems to display each's final frame
while (_menuItemIndex < 7) {
if (_menuItem) {
// Draw the final frame of the menuitem
M4Sprite *spr = _menuItem->getFrame(0);
itemSize = _menuItem->getFrame(0)->height();
spr->copyTo(this, rexMenuItemPosList[_menuItemIndex - 1].x,
rexMenuItemPosList[_menuItemIndex - 1].y + row + (itemSize / 2) - (spr->height() / 2), 0);
delete _menuItem;
copyTo(_bgSurface, Common::Rect(0, row, width(), row + MADS_SURFACE_HEIGHT), 0, 0);
}
// Get the next sprite set
sprintf(resName, "RM%dA%d.SS", REX_MENUSCREEN, ++_menuItemIndex);
data = _vm->res()->get(resName);
_menuItem = new SpriteAsset(_vm, data, data->size(), resName);
_vm->res()->toss(resName);
// Slot it into available palette space
RGBList *palData = _menuItem->getRgbList();
_vm->_palette->addRange(palData);
_menuItem->translate(palData, true);
_itemPalData.push_back(palData);
}
_vm->_mouse->cursorOn();
return;
}
if ((_menuItemIndex == 0) || (_frameIndex == 0)) {
// Get the next menu item
if (_menuItem) {
delete _menuItem;
// Copy over the current display surface area to the background, so the final frame
// of the previous menuitem should be kept on the screen
copyTo(_bgSurface, Common::Rect(0, row, width(), row + MADS_SURFACE_HEIGHT), 0, 0);
}
// Get the next menuitem resource
sprintf(resName, "RM%dA%d.SS", REX_MENUSCREEN, ++_menuItemIndex);
data = _vm->res()->get(resName);
_menuItem = new SpriteAsset(_vm, data, data->size(), resName);
_vm->res()->toss(resName);
// Slot it into available palette space
RGBList *palData = _menuItem->getRgbList();
_vm->_palette->addRange(palData);
_menuItem->translate(palData, true);
_itemPalData.push_back(palData);
_frameIndex = _menuItem->getCount() - 1;
// If the final resource is now loaded, which contains the highlighted versions of
// each menuitem, then the startup animation is complete
if (_menuItemIndex == 7) {
_vm->_mouse->cursorOn();
return;
}
} else {
--_frameIndex;
}
// Move to the next menuitem frame
itemSize = _menuItem->getFrame(0)->height();
_bgSurface->copyTo(this, 0, row);
M4Sprite *spr = _menuItem->getFrame(_frameIndex);
spr->copyTo(this, rexMenuItemPosList[_menuItemIndex - 1].x, rexMenuItemPosList[_menuItemIndex - 1].y +
row + (itemSize / 2) - (spr->height() / 2), 0);
}
int RexMainMenuView::getHighlightedItem(int x, int y) {
y -= (height() - MADS_SURFACE_HEIGHT) / 2;
for (int index = 0; index < 6; ++index) {
const Common::Point &pt = rexMenuItemPosList[index];
M4Sprite *spr = _menuItem->getFrame(index);
if ((x >= pt.x) && (y >= pt.y) && (x < (pt.x + spr->width())) && (y < (pt.y + spr->height())))
return index;
}
return -1;
}
void RexMainMenuView::handleAction(MadsGameAction action) {
M4Engine *vm = _vm;
vm->_mouse->cursorOff();
vm->_viewManager->deleteView(this);
switch (action) {
case START_GAME:
case RESUME_GAME:
// Load a sample starting scene - note that, currently, calling loadScene automatically
// removes this menu screen from being displayed
vm->_mouse->cursorOn();
vm->_viewManager->addView(vm->_scene);
vm->_scene->loadScene(101);
return;
case SHOW_INTRO:
vm->_viewManager->showAnimView("@rexopen");
break;
case CREDITS:
vm->_viewManager->showTextView("credits");
return;
case QUOTES:
vm->_viewManager->showTextView("quotes");
return;
case EXIT:
{
// When the Exit action is done from the menu, show one of two possible advertisements
// Activate the scene display with the specified scene
bool altAdvert = vm->_random->getRandomNumber(1000) >= 500;
vm->_scene->loadScene(altAdvert ? 995 : 996);
vm->_viewManager->addView(vm->_scene);
vm->_viewManager->refreshAll();
vm->delay(10000);
vm->_events->quitFlag = true;
return;
}
break;
default:
break;
}
}
//--------------------------------------------------------------------------
MadsMainMenuView::MadsMainMenuView(M4Engine *vm):
View(vm, Common::Rect(0, 0, vm->_screen->width(), vm->_screen->height())) {
}
bool MadsMainMenuView::onEvent(M4EventType eventType, int param, int x, int y, bool &captureEvents) {
return false;
}
void MadsMainMenuView::updateState() {
// TODO: Implement me
}
//--------------------------------------------------------------------------
DragonMainMenuView::DragonMainMenuView(M4Engine *vm):
View(vm, Common::Rect(0, 0, vm->_screen->width(), vm->_screen->height())) {
_screenType = VIEWID_MAINMENU;
_screenFlags.get = SCREVENT_ALL;
_delayTimeout = 0;
_menuItem = NULL;
_menuItemIndex = 0;
_frameIndex = 0;
_highlightedIndex = -1;
_skipFlag = false;
// Load the background for the Dragonsphere game
this->loadBackground(942, &_bgPalData);
_vm->_palette->addRange(_bgPalData);
this->translate(_bgPalData);
}
DragonMainMenuView::~DragonMainMenuView() {
//if (_menuItem)
// delete _menuItem;
_vm->_palette->deleteRange(_bgPalData);
delete _bgPalData;
for (uint i = 0; i < _itemPalData.size(); ++i) {
_vm->_palette->deleteRange(_itemPalData[i]);
delete _itemPalData[i];
}
}
bool DragonMainMenuView::onEvent(M4EventType eventType, int param, int x, int y, bool &captureEvents) {
char resName[20];
Common::SeekableReadStream *data;
// Handle keypresses - these can be done at any time, even when the menu items are being drawn
if (eventType == KEVENT_KEY) {
switch (param) {
case Common::KEYCODE_ESCAPE:
case Common::KEYCODE_F6:
handleAction(EXIT);
break;
case Common::KEYCODE_F1:
handleAction(START_GAME);
break;
case Common::KEYCODE_F2:
handleAction(RESUME_GAME);
break;
case Common::KEYCODE_F3:
handleAction(SHOW_INTRO);
break;
case Common::KEYCODE_F4:
handleAction(CREDITS);
break;
default:
// Any other key skips the menu animation
_skipFlag = true;
return false;
}
return true;
}
int menuIndex;
switch (eventType) {
case MEVENT_LEFT_CLICK:
case MEVENT_LEFT_DRAG:
if (_vm->_mouse->getCursorOn()) {
menuIndex = getHighlightedItem(x, y);
if (menuIndex != _highlightedIndex) {
_highlightedIndex = menuIndex;
if (_highlightedIndex != -1) {
sprintf(resName, "MAIN%d.SS", menuIndex);
data = _vm->res()->get(resName);
_menuItem = new SpriteAsset(_vm, data, data->size(), resName);
_vm->res()->toss(resName);
M4Sprite *spr = _menuItem->getFrame(1);
spr->copyTo(this, spr->xOffset - 25, spr->yOffset - spr->height());
}
}
} else {
// Skip the menu animation
_skipFlag = true;
}
return true;
case MEVENT_LEFT_RELEASE:
if (_highlightedIndex != -1)
handleAction((MadsGameAction) _highlightedIndex);
return true;
default:
break;
}
return false;
}
void DragonMainMenuView::updateState() {
char resName[20];
Common::SeekableReadStream *data;
RGBList *palData;
M4Sprite *spr;
if (_menuItemIndex == 6)
return;
while (_menuItemIndex < 6) {
sprintf(resName, "MAIN%d.SS", _menuItemIndex);
data = _vm->res()->get(resName);
_menuItem = new SpriteAsset(_vm, data, data->size(), resName);
_vm->res()->toss(resName);
// Slot it into available palette space
palData = _menuItem->getRgbList();
_vm->_palette->addRange(palData);
_menuItem->translate(palData, true);
_itemPalData.push_back(palData);
spr = _menuItem->getFrame(0);
spr->copyTo(this, spr->xOffset - 25, spr->yOffset - spr->height());
if (_menuItemIndex != 5)
delete _menuItem;
_menuItemIndex++;
}
// Sphere
sprintf(resName, "RM920X0.SS");
data = _vm->res()->get(resName);
_menuItem = new SpriteAsset(_vm, data, data->size(), resName);
_vm->res()->toss(resName);
// Slot it into available palette space
palData = _menuItem->getRgbList();
_vm->_palette->addRange(palData);
_menuItem->translate(palData, true);
_itemPalData.push_back(palData);
spr = _menuItem->getFrame(0); // empty sphere
spr->copyTo(this, spr->xOffset - 75, spr->yOffset - spr->height());
spr = _menuItem->getFrame(1); // dragon inside sphere
spr->copyTo(this, spr->xOffset - 75, spr->yOffset - spr->height());
// Dragonsphere letters
sprintf(resName, "RM920X3.SS");
data = _vm->res()->get(resName);
_menuItem = new SpriteAsset(_vm, data, data->size(), resName);
_vm->res()->toss(resName);
// Slot it into available palette space
palData = _menuItem->getRgbList();
_vm->_palette->addRange(palData);
_menuItem->translate(palData, true);
_itemPalData.push_back(palData);
spr = _menuItem->getFrame(1);
// FIXME: We assume that the transparent color is the color of the top left pixel
byte *transparentColor = (byte *)spr->pixels;
spr->copyTo(this, spr->xOffset - 140, spr->yOffset - spr->height(), (int)*transparentColor);
_vm->_mouse->cursorOn();
}
int DragonMainMenuView::getHighlightedItem(int x, int y) {
y -= (height() - MADS_SURFACE_HEIGHT) / 2;
for (int index = 0; index < 6; ++index) {
const Common::Point &pt = dragonMenuItemPosList[index];
M4Sprite *spr = _menuItem->getFrame(0);
if ((x >= pt.x - 25) && (y >= pt.y - spr->height()) && (x < (pt.x - 25 + spr->width())) && (y < (pt.y))) {
printf("x = %d, y = %d, index = %d\n", x, y, index);
return index;
}
}
return -1;
}
void DragonMainMenuView::handleAction(MadsGameAction action) {
M4Engine *vm = _vm;
vm->_mouse->cursorOff();
vm->_viewManager->deleteView(this);
switch (action) {
case START_GAME:
case RESUME_GAME:
// Load a sample starting scene - note that, currently, calling loadScene automatically
// removes this menu screen from being displayed
vm->_mouse->cursorOn();
vm->_viewManager->addView(vm->_scene);
vm->_scene->loadScene(101);
return;
case SHOW_INTRO:
vm->_viewManager->showAnimView("@dragon");
break;
case CREDITS:
vm->_viewManager->showTextView("credits");
return;
case EXIT:
vm->_events->quitFlag = true;
return;
break;
default:
break;
}
}
}

91
engines/m4/mads_menus.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 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#ifndef M4_MADS_MENUS_H
#define M4_MADS_MENUS_H
#include "m4/viewmgr.h"
namespace M4 {
#define MADS_MENU_ANIM_DELAY 70
enum MadsGameAction {START_GAME, RESUME_GAME, SHOW_INTRO, CREDITS, QUOTES, EXIT};
class RexMainMenuView: public View {
private:
M4Surface *_bgSurface;
RGBList *_bgPalData;
int _menuItemIndex;
int _frameIndex;
bool _skipFlag;
SpriteAsset *_menuItem;
Common::Array<RGBList *> _itemPalData;
uint32 _delayTimeout;
int _highlightedIndex;
int getHighlightedItem(int x, int y);
void handleAction(MadsGameAction action);
public:
RexMainMenuView(M4Engine *vm);
~RexMainMenuView();
bool onEvent(M4EventType eventType, int param, int x, int y, bool &captureEvents);
void updateState();
};
class DragonMainMenuView: public View {
private:
//M4Surface *_bgSurface;
RGBList *_bgPalData;
int _menuItemIndex;
int _frameIndex;
bool _skipFlag;
SpriteAsset *_menuItem;
Common::Array<RGBList *> _itemPalData;
uint32 _delayTimeout;
int _highlightedIndex;
int getHighlightedItem(int x, int y);
void handleAction(MadsGameAction action);
public:
DragonMainMenuView(M4Engine *vm);
~DragonMainMenuView();
bool onEvent(M4EventType eventType, int param, int x, int y, bool &captureEvents);
void updateState();
};
class MadsMainMenuView: public View {
public:
MadsMainMenuView(M4Engine *vm);
bool onEvent(M4EventType eventType, int param, int x, int y, bool &captureEvents);
void updateState();
};
}
#endif

359
engines/m4/midi.cpp Normal file
View file

@ -0,0 +1,359 @@
/* 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.
*
* $URL$
* $Id$
*
*/
// FIXME: This is cribbed together from the SAGA music player. It needs cleanup
// and testing.
#include "m4/m4.h"
#include "m4/midi.h"
#include "common/stream.h"
namespace M4 {
MidiPlayer::MidiPlayer(M4Engine *vm, MidiDriver *driver) : _vm(vm), _midiData(NULL), _driver(driver), _isPlaying(false), _passThrough(false), _isGM(false) {
memset(_channel, 0, sizeof(_channel));
_masterVolume = 0;
_parser = MidiParser::createParser_SMF();
_parser->setMidiDriver(this);
_parser->setTimerRate(getBaseTempo());
open();
}
MidiPlayer::~MidiPlayer() {
_driver->setTimerCallback(NULL, NULL);
_parser->setMidiDriver(NULL);
stopMusic();
close();
delete _parser;
delete _midiData;
}
void MidiPlayer::setVolume(int volume) {
Common::StackLock lock(_mutex);
if (volume < 0)
volume = 0;
else if (volume > 255)
volume = 255;
if (_masterVolume == volume)
return;
_masterVolume = volume;
for (int i = 0; i < 16; ++i) {
if (_channel[i]) {
_channel[i]->volume(_channelVolume[i] * _masterVolume / 255);
}
}
}
int MidiPlayer::open() {
// Don't ever call open without first setting the output driver!
if (!_driver)
return 255;
int ret = _driver->open();
if (ret)
return ret;
_driver->setTimerCallback(this, &onTimer);
return 0;
}
void MidiPlayer::close() {
stopMusic();
if (_driver)
_driver->close();
_driver = 0;
}
void MidiPlayer::send(uint32 b) {
if (_passThrough) {
_driver->send(b);
return;
}
byte channel = (byte)(b & 0x0F);
if ((b & 0xFFF0) == 0x07B0) {
// Adjust volume changes by master volume
byte volume = (byte)((b >> 16) & 0x7F);
_channelVolume[channel] = volume;
volume = volume * _masterVolume / 255;
b = (b & 0xFF00FFFF) | (volume << 16);
} else if ((b & 0xF0) == 0xC0 && !_isGM && !_nativeMT32) {
b = (b & 0xFFFF00FF) | MidiDriver::_mt32ToGm[(b >> 8) & 0xFF] << 8;
}
else if ((b & 0xFFF0) == 0x007BB0) {
//Only respond to All Notes Off if this channel
//has currently been allocated
if (_channel[b & 0x0F])
return;
}
if (!_channel[channel])
_channel[channel] = (channel == 9) ? _driver->getPercussionChannel() : _driver->allocateChannel();
if (_channel[channel])
_channel[channel]->send(b);
}
void MidiPlayer::metaEvent(byte type, byte *data, uint16 length) {
switch (type) {
case 0x2F:
// End of track. (Not called when auto-looping.)
stopMusic();
break;
case 0x51:
// Set tempo. Handled by the standard MIDI parser already.
break;
default:
warning("Unhandled meta event: %02x", type);
break;
}
}
void MidiPlayer::onTimer(void *refCon) {
MidiPlayer *midi = (MidiPlayer *)refCon;
Common::StackLock lock(midi->_mutex);
if (midi->_isPlaying)
midi->_parser->onTimer();
}
void MidiPlayer::playMusic(const char *name, int32 vol, bool loop, int32 trigger, int32 scene) {
stopMusic();
char fullname[144];
_vm->res()->changeExtension(fullname, name, "HMP");
Common::SeekableReadStream *midiFile = _vm->res()->get(fullname);
byte *hmpData = new byte[midiFile->size()];
uint32 smfSize;
midiFile->read(hmpData, midiFile->size());
_midiData = convertHMPtoSMF(hmpData, midiFile->size(), smfSize);
delete[] hmpData;
_vm->res()->toss(fullname);
_vm->res()->purge();
if (_midiData) {
/*
FILE *out = fopen("music.mid", "wb");
fwrite(_midiData, smfSize, 1, out);
fclose(out);
*/
_parser->loadMusic(_midiData, smfSize);
_parser->property(MidiParser::mpAutoLoop, loop);
}
setVolume(255);
_isPlaying = true;
}
void MidiPlayer::stopMusic() {
Common::StackLock lock(_mutex);
_isPlaying = false;
if (_parser) {
_parser->unloadMusic();
}
delete[] _midiData;
_midiData = NULL;
}
// This function will convert HMP music into type 1 SMF, which our SMF parser
// will be able to handle. It is based on Hans de Goede's HMP 2 MIDI file
// converter, which in turn is "based on the conversion algorithms found in
// d1x, d2x-xl and jjffe". Hans's original code is licensed under the LGPL.
//
// TODO: It would probably be nicer to write a MIDI parser class to deal with
// HMP data directly. Though the multi-track nature of HMP makes that tricky.
byte *MidiPlayer::convertHMPtoSMF(byte *data, uint32 inSize, uint32 &outSize) {
Common::MemoryReadStream readS(data, inSize);
Common::MemoryWriteStreamDynamic writeS;
byte buf[8];
readS.read(buf, sizeof(buf));
if (memcmp(buf, "HMIMIDIP", 8) != 0) {
warning("convertHMPtoSMF: Invalid HMP header");
return NULL;
}
// Read the number of tracks. Note that all the tracks are still part
// of the same song, just like in type 1 SMF files.
readS.seek(0x30);
uint32 numTracks = readS.readUint32LE();
// The first track starts on offset 0x300. It's currently unknown what
// the skipped data is for.
readS.seek(0x300);
// For some reason, we skip the first track entirely.
byte a = readS.readByte();
byte b = readS.readByte();
byte c = readS.readByte();
while (a != 0xFF || b != 0x2F || c != 0x00) {
a = b;
b = c;
c = readS.readByte();
}
// The beginning of the MIDI header
static const byte midiHeader1[] = { 'M', 'T', 'h', 'd', 0, 0, 0, 6, 0, 1 };
// The last 2 bytes of the midi header and track 0
static const byte midiHeader2[] = { 0, 0xC0, 'M', 'T', 'r', 'k', 0, 0, 0, 0x0B, 0, 0xFF, 0x51, 0x03, 0x18, 0x80, 0, 0, 0xFF, 0x2F, 0 };
// Write the MIDI header
writeS.write(midiHeader1, sizeof(midiHeader1));
// Write the number of tracks
writeS.writeUint16BE(numTracks);
// Write the rest of the MIDI header and track 0.
writeS.write(midiHeader2, sizeof(midiHeader2));
// Read and convert all the tracks
for (uint i = 1; i < numTracks; i++) {
if (readS.readUint32LE() != i) {
warning("convertHMPtoSMF: Invalid HMP track number");
delete[] writeS.getData();
return NULL;
}
uint32 trackLength = readS.readUint32LE() - 12;
readS.readUint32LE(); // Unused?
// Write the track header
writeS.write("MTrk", 4);
// This is where we will write the length of the track.
uint32 trackLengthPos = writeS.pos();
writeS.writeUint32LE(0);
// In the original, this is cleared once at the beginning of
// the function, but surely the last command does not carry
// over to the next track?
byte lastCmd = 0;
// Now we can finally convert the track
uint32 endPos = readS.pos() + trackLength;
while (readS.pos() < endPos) {
// Convert the VLQ
byte vlq[4];
int j = -1;
do {
j++;
vlq[j] = readS.readByte();
} while (!(vlq[j] & 0x80));
for (int k = 0; k <= j; k++) {
a = vlq[j - k] & 0x7F;
if (k != j)
a |= 0x80;
writeS.writeByte(a);
}
a = readS.readByte();
if (a == 0xFF) {
// META event
b = readS.readByte();
c = readS.readByte();
writeS.writeByte(a);
writeS.writeByte(b);
writeS.writeByte(c);
if (c > 0) {
byte *metaBuf = new byte[c];
readS.read(metaBuf, c);
writeS.write(metaBuf, c);
delete[] metaBuf;
}
if (b == 0x2F) {
if (c != 0x00) {
warning("convertHMPtoSMF: End of track with non-zero size");
delete[] writeS.getData();
return NULL;
}
break;
}
} else {
if (a != lastCmd)
writeS.writeByte(a);
switch (a & 0xF0) {
case 0x80:
case 0x90:
case 0xA0:
case 0xB0:
case 0xE0:
b = readS.readByte();
c = readS.readByte();
writeS.writeByte(b);
writeS.writeByte(c);
break;
case 0xC0:
case 0xD0:
b = readS.readByte();
writeS.writeByte(b);
break;
default:
warning("convertHMPtoSMF: Invalid HMP command %02X", a);
delete[] writeS.getData();
return NULL;
}
lastCmd = a;
}
}
if (readS.pos() != endPos) {
warning("convertHMPtoSMF: Invalid track length");
delete[] writeS.getData();
return NULL;
}
WRITE_BE_UINT32(writeS.getData() + trackLengthPos, writeS.pos() - trackLengthPos - 4);
}
outSize = writeS.size();
return writeS.getData();
}
} // End of namespace M4

99
engines/m4/midi.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 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
// Music class
#ifndef M4_MIDI_H
#define M4_MIDI_H
#include "sound/mididrv.h"
#include "sound/midiparser.h"
#include "common/mutex.h"
namespace M4 {
class MidiPlayer : public MidiDriver {
public:
MidiPlayer(M4Engine *vm, MidiDriver *driver);
~MidiPlayer();
bool isPlaying() { return _isPlaying; }
void setVolume(int volume);
int getVolume() { return _masterVolume; }
void setNativeMT32(bool b) { _nativeMT32 = b; }
bool hasNativeMT32() { return _nativeMT32; }
void playMusic(const char *name, int32 vol, bool loop, int32 trigger, int32 scene);
void stopMusic();
void setPassThrough(bool b) { _passThrough = b; }
void setGM(bool isGM) { _isGM = isGM; }
//MidiDriver interface implementation
int open();
void close();
void send(uint32 b);
void metaEvent(byte type, byte *data, uint16 length);
void setTimerCallback(void *timerParam, void (*timerProc)(void *)) { }
uint32 getBaseTempo(void) { return _driver ? _driver->getBaseTempo() : 0; }
//Channel allocation functions
MidiChannel *allocateChannel() { return 0; }
MidiChannel *getPercussionChannel() { return 0; }
protected:
static void onTimer(void *data);
M4Engine *_vm;
byte *_midiData;
MidiChannel *_channel[16];
MidiDriver *_driver;
MidiParser *_parser;
byte _channelVolume[16];
bool _nativeMT32;
bool _isGM;
bool _passThrough;
bool _isPlaying;
bool _randomLoop;
byte _masterVolume;
byte *_musicData;
uint16 *_buf;
size_t _musicDataSize;
Common::Mutex _mutex;
byte *convertHMPtoSMF(byte *data, uint32 inSize, uint32 &outSize);
};
} // End of namespace M4
#endif

42
engines/m4/module.mk Normal file
View file

@ -0,0 +1,42 @@
MODULE := engines/m4
MODULE_OBJS = \
actor.o \
animation.o \
assets.o \
compression.o \
console.o \
converse.o \
detection.o \
events.o \
font.o \
globals.o \
graphics.o \
gui.o \
hotspot.o \
m4.o \
m4_menus.o \
m4_views.o \
mads_anim.o \
mads_menus.o \
midi.o \
rails.o \
resource.o \
saveload.o \
scene.o \
script.o \
sound.o \
sprite.o \
viewmgr.o \
woodscript.o \
ws_machine.o \
ws_sequence.o
# This module can be built as a plugin
ifdef BUILD_PLUGINS
PLUGIN := 1
endif
# Include common rules
include $(srcdir)/rules.mk

358
engines/m4/rails.cpp Normal file
View file

@ -0,0 +1,358 @@
/* 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.
*
* $URL$
* $Id$
*
*/
/*
TODO:
- rewrite functions (GetShortestPath etc.)
*/
#include "graphics/primitives.h"
#include "common/list.h"
#include "common/rect.h"
#include "common/util.h"
#include "m4/rails.h"
#include "m4/m4.h"
namespace M4 {
#define TOP_EDGE 1 << 0
#define LEFT_EDGE 1 << 1
#define BOTTOM_EDGE 1 << 2
#define RIGHT_EDGE 1 << 3
Rails::Rails() {
}
Rails::~Rails() {
clearRails();
}
void Rails::clearRails() {
uint32 i;
Common::List<NoWalkRect *>::iterator j;
RailNode *tempNode;
for (i = 0; i < _nodes.size(); i++) {
tempNode = _nodes[i];
_nodes.remove_at(i);
delete tempNode;
}
for (i = 0; i < _edges.size(); i++) {
_edges.remove_at(i);
}
for (j = _noWalkRects.begin(); j != _noWalkRects.end(); ++j)
delete (*j);
_noWalkRects.clear();
}
static void checkPoint(int x, int y, int color, void *data) {
IsWalkableData *isWalkableData = (IsWalkableData*)data;
if (!isWalkableData->result)
return;
else {
M4Surface *codes = isWalkableData->codes;
if (x >= 0 && x < codes->w && y >= 0 && y < codes->h) {
isWalkableData->result = !((*((uint8*)codes->getBasePtr(x, y))) & 0x10);
} else {
isWalkableData->result = false;
}
}
}
bool Rails::isLineWalkable(int x0, int y0, int x1, int y1) {
IsWalkableData isWalkableData;
isWalkableData.codes = _walkCodes;
isWalkableData.result = true;
Graphics::drawLine(x0, y0, x1, y1, 0, &checkPoint, &isWalkableData);
return isWalkableData.result;
}
// helper function
uint8 getEndCode(int32 x, int32 y, Common::Rect rect) {
uint8 endCode = 0;
endCode = (x < rect.left) ? LEFT_EDGE : endCode;
endCode = (x > rect.right) ? RIGHT_EDGE : endCode;
endCode = (y < rect.top) ? endCode | TOP_EDGE : endCode;
endCode = (y > rect.bottom) ? endCode | BOTTOM_EDGE : endCode;
return endCode;
}
bool Rails::lineCrossesRect(int32 x1, int32 y1, int32 x2, int32 y2, Common::Rect rect) {
int32 mX, mY;
int32 pX1 = x1, pX2 = x2, pY1 = y1, pY2 = y2;
uint8 endCode1, endCode2, midCode;
if (rect.left > rect.right || rect.top > rect.bottom)
return false;
// Cohen-Sutherland line clipping algorithm
endCode1 = getEndCode(pX1, pY1, rect);
endCode2 = getEndCode(pX2, pY2, rect);
if (!endCode1 || !endCode2) // if both endcodes are zero
return true; // point is inside the rectangle, therefore the line intersects
while (true) {
if (endCode1 & endCode2) // if both endcodes have a common bitset
return false; // line is completely off one edge
// calculate midpoint
mX = (pX1 + pX2)>>1;
mY = (pY1 + pY2)>>1;
// avoid round-off error: make sure that the midpoint isn't the same as one of the
// two endpoints
if (((mX == pX1) && (mY == pY1)) || ((mX == pX2) && (mY == pY2)))
return false;
midCode = getEndCode(mX, mY, rect);
if (!midCode) {
return true;
} else if (midCode & endCode1) {
// the midCode and an end point form a line segment completely off one edge, so
// remove that half of the line segment
pX1 = mX;
pY1 = mY;
endCode1 = midCode;
} else {
pX2 = mX;
pY2 = mY;
endCode2 = midCode;
}
}
}
bool Rails::linePassesThroughRect(int32 x1, int32 y1, int32 x2, int32 y2) {
if (_noWalkRects.empty())
return false;
bool intersected = false;
Common::List<NoWalkRect *>::iterator i;
for (i = _noWalkRects.begin(); i != _noWalkRects.end(); ++i) {
intersected = lineCrossesRect(x1, y1, x2, y2, Common::Rect((*i)->x1, (*i)->y1, (*i)->x2, (*i)->y2));
if (intersected)
break;
}
return intersected;
}
long SqrtF16(long n) {
uint32 r = 0, s;
uint32 v = (uint32)n;
for (int i = 15; i <= 0; i--) {
s = r + (1L << i * 2);
r >>= 1;
if (s <= v) {
v -= s;
r |= (1L << i * 2);
}
}
return (long)r;
}
void Rails::createEdge(int32 node1, int32 node2) {
uint32 index;
int32 x1, y1, x2, y2;
bool valid, finished;
long deltaX, deltaY, distance;
uint8 *walkCodePtr;
if ((node1 < 0) || (node1 >= MAXRAILNODES) || (node2 < 0) || (node2 >= MAXRAILNODES))
return;
if (node1 == node2)
return;
if (node2 < node1)
SWAP(node1, node2); // ensure node1 < node2
// Find the table entry i.e. tableWidth * node1 + node2 and then subtract
// n(n+1)/2, since only the upper triangle of the table is stored
index = (MAXRAILNODES-1) * node1 + node2 - 1 - (node1*(node1+1)>>1);
if (index > _edges.size() - 1)
_edges.resize(index + 1);
_edges.insert_at(index, 0);
valid = true;
walkCodePtr = NULL;
finished = false;
if (_nodes.size() <= (uint32)node1 || _nodes.size() <= (uint32)node2)
return;
x1 = _nodes[node1]->x;
y1 = _nodes[node1]->y;
x2 = _nodes[node2]->x;
y2 = _nodes[node2]->y;
// Make sure that the algorithm is symmetric
if (x2 < x1) {
SWAP(x1, x2);
SWAP(y1, y2);
}
valid = isLineWalkable(_nodes[node1]->x, _nodes[node1]->y,
_nodes[node2]->x, _nodes[node2]->y);
printf("test code says: %d\n", valid);
// Check if the line passes through a forbidden rectangle
if (valid) {
if (linePassesThroughRect(x1, y1, x2, y2)) {
valid = false;
}
}
if (valid) {
deltaX = ABS(((long)(x2 - x1)) << 16);
deltaY = ABS(((long)(y2 - y1)) << 16);
if ((deltaX >= 0x800000) || (deltaY >= 0x800000)) {
deltaX >>= 16;
deltaY >>= 16;
distance = (long)(SqrtF16(deltaX * deltaX + deltaY * deltaY) << 16);
} else {
distance = SqrtF16(FixedMul(deltaX, deltaX) + FixedMul(deltaY, deltaY)) << 8;
}
_edges.insert_at(index, (int16*)(distance >> 16));
}
printf("node1 = %d, node2 = %d, valid = %d\n", node1, node2, valid);
}
void Rails::restoreNodeEdges(int32 nodeID) {
for (int32 i = 0; i < MAXRAILNODES; i++) {
createEdge(i, nodeID);
}
}
void Rails::restoreEdgeList() {
int32 j;
for (int32 i = 0; i < MAXRAILNODES; i++) {
for (j = i + 1; j < MAXRAILNODES; j++) {
createEdge(i, j);
}
}
}
int32 Rails::addRailNode(int32 x, int32 y, bool restoreEdges) {
uint32 i = _nodes.size();
if (i >= MAXRAILNODES)
return -1;
RailNode *newNode = new RailNode();
newNode->nodeID = i;
newNode->x = x;
newNode->y = y;
_nodes.insert_at(i, newNode);
if (restoreEdges) {
for (uint32 j=0; j<_nodes.size(); j++)
createEdge(i, j);
}
return i;
}
bool Rails::removeRailNode(int32 nodeID, bool restoreEdges) {
if (nodeID < 0 || nodeID >= MAXRAILNODES)
return false;
if (_nodes.empty() || _edges.empty())
return false;
RailNode *tempNode = _nodes[nodeID];
_nodes.remove_at(nodeID);
delete tempNode;
if (restoreEdges) {
restoreNodeEdges(nodeID);
}
return true;
}
int16 Rails::getEdgeLength(int32 node1, int32 node2) {
int32 index;
if (_edges.empty() || node1 == node2)
return 0;
if (node2 < node1)
SWAP(node1, node2);
// Find the table entry i.e. tableWidth * node1 + node2 and then subtract
// n(n+1)/2, since only the upper triangle of the table is stored
index = (MAXRAILNODES-1)*node1 + node2 - 1 - (node1*(node1+1)>>1);
return *_edges[index];
}
void Rails::disposePath(RailNode *pathStart) {
RailNode *tempNode = pathStart;
while (tempNode) {
pathStart = pathStart->shortPath;
delete tempNode;
tempNode = pathStart;
}
}
static RailNode* duplicatePath(RailNode *pathStart) {
RailNode *newNode = NULL;
RailNode *firstNode = NULL;
RailNode *prevNode = NULL;
// A valid path is assumed
RailNode *pathNode = pathStart;
while (pathNode) {
newNode = new RailNode();
newNode->x = pathNode->x;
newNode->y = pathNode->y;
newNode->shortPath = NULL;
if (!firstNode)
firstNode = newNode;
else
prevNode->shortPath = newNode;
prevNode = newNode;
// Get the next node
pathNode = pathNode->shortPath;
}
return firstNode;
}
bool Rails::getShortestPath(int32 origID, int32 destID, RailNode **shortPath) {
// TODO
return true;
}
} // End of namespace M4

97
engines/m4/rails.h Normal file
View file

@ -0,0 +1,97 @@
/* 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.
*
* $URL$
* $Id$
*
*/
#ifndef M4_RAILS_H
#define M4_RAILS_H
#include "m4/graphics.h"
#include "common/list.h"
#include "common/array.h"
#include "common/rect.h"
// TODO: This needs cleaning up
namespace M4 {
#define MAXRAILNODES 32
#define PATH_END 0xffff
struct RailNode {
uint8 nodeID;
int32 x, y;
RailNode *shortPath;
int32 pathWeight;
};
struct NoWalkRect {
int32 x1, y1, x2, y2;
int32 alternateWalkToNode;
int32 walkAroundNode1;
int32 walkAroundNode2;
int32 walkAroundNode3;
int32 walkAroundNode4;
};
struct PathNode {
PathNode *next;
int8 nodeID;
};
struct IsWalkableData {
M4Surface *codes;
bool result;
};
class Rails {
public:
Rails();
~Rails();
void setCodeSurface(M4Surface *surface) { _walkCodes = surface; }
void clearRails();
int32 addRailNode(int32 x, int32 y, bool restoreEdges);
private:
Common::Array<RailNode *> _nodes;
Common::Array<int16 *> _edges;
Common::List<NoWalkRect *> _noWalkRects;
M4Surface *_walkCodes;
bool lineCrossesRect(int32 x1, int32 y1, int32 x2, int32 y2, Common::Rect rect);
bool linePassesThroughRect(int32 x1, int32 y1, int32 x2, int32 y2);
void createEdge(int32 node1, int32 node2);
bool removeRailNode(int32 nodeID, bool restoreEdges);
int16 getEdgeLength(int32 node1, int32 node2);
void restoreNodeEdges(int32 nodeID);
void restoreEdgeList();
void disposePath(RailNode *pathStart);
bool getShortestPath(int32 origID, int32 destID, RailNode **shortPath);
bool isLineWalkable(int x0, int y0, int x1, int y1);
};
} // End of namespace M4
#endif

436
engines/m4/resource.cpp Normal file
View file

@ -0,0 +1,436 @@
/* 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.
*
* $URL$
* $Id$
*
*/
#include "m4/m4.h"
#include "m4/resource.h"
#include "m4/events.h"
namespace M4 {
FileSystem::FileSystem(const char *hashFilename) {
for (int i = 0; i < 10; i++) {
_hagEntries[i].filename[0] = '\0';
_hagEntries[i].fileIndex = 0; // Was -1
_hagEntries[i].hagFile = 0;
}
Common::File hashFile;
uint32 hashSize;
hashFile.open(hashFilename);
if (!hashFile.isOpen()) {
printf("FileSystem::FileSystem: error opening hash %s\n", hashFilename);
}
hashSize = hashFile.readUint32LE();
//printf("FileSystem::FileSystem: hashSize = %d\n", hashSize);
/* load file records and add them to the hash list */
for (uint i = 0; i < hashSize; i++) {
HashFileEntry entry;
hashFile.read(entry.filename, kM4MaxFilenameSize);
str_lower(entry.filename);
entry.hagfile = hashFile.readByte();
hashFile.readByte();
entry.offset = hashFile.readUint32LE();
entry.size = hashFile.readUint32LE();
hashFile.readUint32LE();
if (entry.filename[0]) {
/*
printf(" filename: %s\n", entry.filename);
printf(" hagfile: %d\n", entry.hagfile);
printf(" disks: %d\n", entry.disks);
printf(" offset: %08X\n", entry.offset);
printf(" size: %d\n", entry.size);
printf(" next: %08X\n", entry.next);
*/
_fileEntries[entry.filename] = entry;
}
}
/* load hagfile records and update the list */
while (!hashFile.eof()) {
HashHagEntry entry;
hashFile.read(entry.filename, kM4MaxFilenameSize);
entry.fileIndex = hashFile.readByte();
if (hashFile.eof())
break;
changeExtension(_hagEntries[entry.fileIndex].filename, entry.filename, "HAG");
_hagEntries[entry.fileIndex].fileIndex = entry.fileIndex;
_hagEntries[entry.fileIndex].hagFile = new Common::File();
_hagEntries[entry.fileIndex].hagFile->open(_hagEntries[entry.fileIndex].filename);
if (!_hagEntries[entry.fileIndex].hagFile->isOpen()) {
printf("FileSystem::FileSystem: error opening hag %s\n", _hagEntries[entry.fileIndex].filename);
}
}
hashFile.close();
}
FileSystem::~FileSystem() {
for (int i = 0; i < 10; i++) {
if (_hagEntries[i].hagFile)
delete _hagEntries[i].hagFile;
}
}
Common::SeekableReadStream *FileSystem::loadFile(const char *resourceName, bool preloadFlag) {
const HashFileEntry *hfe = getHashFileEntry(resourceName);
Common::SeekableReadStream *result = NULL;
if (hfe) {
//printf("FileSystem::loadFile() success opening %s\n", filename);
HashHagEntry *hagEntry = &_hagEntries[hfe->hagfile];
if (preloadFlag) {
// Creates a MemoryReadStream object that contains all of the resource in memory
hagEntry->hagFile->seek(hfe->offset);
result = _hagEntries[hfe->hagfile].hagFile->readStream(hfe->size);
}
else
// Creates a SeekableSubReadStream, which will read the data in from disk as the
// caller reads in data
result = new Common::SeekableSubReadStream(hagEntry->hagFile, hfe->offset,
hfe->offset + hfe->size);
} else {
printf("FileSystem::loadFile() error opening %s\n", resourceName);
}
return result;
}
const FileSystem::HashFileEntry *FileSystem::getHashFileEntry(const char *filename) {
char resourceName[20];
strcpy(resourceName, filename);
str_lower(resourceName);
FileHashMap::const_iterator entry = _fileEntries.find(filename);
if (entry != _fileEntries.end())
return &(entry->_value);
else
return NULL;
}
void FileSystem::changeExtension(char *destName, const char *sourceName, const char *extension) {
if (sourceName != destName)
strcpy(destName, sourceName);
char *dot = strrchr(destName, '.');
if (dot != NULL)
*dot = 0;
sprintf(destName, "%s.%s", destName, extension);
str_upper(destName);
}
//--------------------------------------------------------------------------
ResourceManager::~ResourceManager() {
ResourceIterator i;
for (i = _resources.begin(); i != _resources.end(); ++i) {
Resource *r = (*i).get();
delete r->stream;
}
}
Common::SeekableReadStream *ResourceManager::get(const char *resourceName, bool preloadFlag) {
char lowerName[kM4MaxFilenameSize];
strcpy(lowerName, resourceName);
str_lower(lowerName);
// Check whether the resource is already loaded
ResourceIterator i;
for (i = _resources.begin(); i != _resources.end(); ++i) {
Resource *r = (*i).get();
if (!strcmp(r->name, resourceName)) {
// Just in case resource was marked to be purged, reactive it again
r->flags &= ~kResourcePurge;
// Return the existing copy of the resource
r->stream->seek(0, SEEK_SET);
return r->stream;
}
}
// the resource wasn't found in the list, load it from disk
Resource *newRes = new Resource();
strncpy(newRes->name, resourceName, 63);
newRes->name[63] = '\0';
newRes->flags = 0;
newRes->stream = loadResource(resourceName, preloadFlag);
_resources.push_back(ResourceList::value_type(newRes));
return newRes->stream;
}
void ResourceManager::toss(const char *resourceName) {
ResourceIterator i;
for (i = _resources.begin(); i != _resources.end(); ++i) {
Resource *r = (*i).get();
if (!strcmp(r->name, resourceName)) {
r->flags |= kResourcePurge;
//printf("M4ResourceManager::toss: mark resource %s to be purged\n", resourceName);
}
}
}
void ResourceManager::purge() {
ResourceIterator i = _resources.begin();
while (i != _resources.end()) {
Resource *r = (*i).get();
if (r->flags & kResourcePurge) {
delete r->stream;
i = _resources.erase(i);
} else {
++i;
}
}
}
void ResourceManager::dump() {
_vm->_events->getConsole()->DebugPrintf("Scene resources:\n");
int index = 0;
ResourceIterator i;
for (i = _resources.begin(); i != _resources.end(); ++i) {
Resource *r = (*i).get();
if (!(r->flags & kResourcePurge)) {
_vm->_events->getConsole()->DebugPrintf(
"Resource #%i, name: %s, handle pointer: %p, size: %d, flags: %02X\n",
index++, r->name, r->buffer, r->stream->size(), r->flags);
}
}
}
//--------------------------------------------------------------------------
const char *madsConcatString = "MADSCONCAT";
ResourceType MADSResourceManager::getResourceType(const char *resourceName) {
if (!strncmp(resourceName, "RM", 2)) {
// Room resource
return RESTYPE_ROOM;
} else if (!strncmp(resourceName, "SC", 2)) {
// SC resource
return RESTYPE_SC;
} else if (strstr(resourceName, ".TXT")) {
// Text resource
return RESTYPE_TEXT;
} else if (strstr(resourceName, ".QUO")) {
// QUO resource
return RESTYPE_QUO;
} else if (*resourceName == 'I') {
// I resource
return RESTYPE_I;
} else if (!strncmp(resourceName, "OB", 2)) {
// OB resource
return RESTYPE_OB;
} else if (!strncmp(resourceName, "FONT", 4)) {
// FONT resource
return RESTYPE_FONT;
} else if (!strncmp(resourceName, "SOUND", 5)) {
// SOUND resource
return RESTYPE_SOUND;
} else if (!strncmp(resourceName, "SPCHC", 5)) {
// SPEECH resource
return RESTYPE_SPEECH;
}
// Check for a known extension
const char *extPos = strchr(resourceName, '.');
if (extPos) {
++extPos;
if (!strcmp(extPos, "FL") || !strcmp(extPos, "LBM") || !strcmp(extPos, "ANM") ||
!strcmp(extPos, "AA") || !strcmp(extPos, "SS")) {
return RESTYPE_HAS_EXT;
}
}
return RESTYPE_NO_EXT;
}
const char *MADSResourceManager::getResourceFilename(const char *resourceName) {
static char outputFilename[64];
ResourceType resType = getResourceType(resourceName);
strcpy(outputFilename, "GLOBAL.HAG");
if ((resType == RESTYPE_ROOM) || (resType == RESTYPE_SC)) {
int value = atoi(resourceName + 2);
int hagFileNum = (resType == RESTYPE_ROOM) ? value / 100 : value;
if (hagFileNum > 0)
sprintf(outputFilename, "SECTION%d.HAG", hagFileNum);
}
if (resType == RESTYPE_SPEECH)
strcpy(outputFilename, "SPEECH.HAG");
return outputFilename;
}
Common::SeekableReadStream *MADSResourceManager::loadResource(const char *resourceName, bool loadFlag) {
Common::File hagFile;
uint32 offset, size;
// If the first character is a '@' then look for an external file
if (*resourceName == '@') {
++resourceName;
hagFile.open(resourceName);
if (loadFlag)
return hagFile.readStream(hagFile.size());
else
return new Common::SeekableSubReadStream(&hagFile, 0, hagFile.size());
}
// If the first character is the wildcard (resource indicator), skip over it
if (*resourceName == '*')
++resourceName;
char resName[20];
strcpy(resName, resourceName);
str_upper(resName);
hagFile.open(getResourceFilename(resName));
// Validate hag file header
char headerBuffer[16];
if ((hagFile.read(headerBuffer, 16) != 16) || (strncmp(headerBuffer, madsConcatString, 10) != 0))
error("Invalid HAG file opened");
int numEntries = hagFile.readUint16LE();
int resIndex = -1;
while (++resIndex < numEntries) {
// Read in the details of the next resource
char resourceBuffer[14];
offset = hagFile.readUint32LE();
size = hagFile.readUint32LE();
hagFile.read(resourceBuffer, 14);
if (!strcmp(resName, resourceBuffer))
break;
}
if (resIndex == numEntries)
error("Invalid resource '%s' specified", resourceName);
// Get the resource, either loading it in it's entirely or getting a stream reference
if (loadFlag) {
hagFile.seek(offset);
return hagFile.readStream(size);
} else {
return new Common::SeekableSubReadStream(&hagFile, offset, offset + size);
}
}
bool MADSResourceManager::resourceExists(const char *resourceName) {
Common::File hagFile;
uint32 offset, size;
// If the first character is the wildcard (resource indicator), skip over it
if (*resourceName == '*')
++resourceName;
char resName[20];
strcpy(resName, resourceName);
str_upper(resName);
hagFile.open(getResourceFilename(resName));
// Validate hag file header
char headerBuffer[16];
if ((hagFile.read(headerBuffer, 16) != 16) || (strncmp(headerBuffer, madsConcatString, 10) != 0))
error("Invalid HAG file opened");
int numEntries = hagFile.readUint16LE();
int resIndex = -1;
while (++resIndex < numEntries) {
// Read in the details of the next resource
char resourceBuffer[14];
offset = hagFile.readUint32LE();
size = hagFile.readUint32LE();
hagFile.read(resourceBuffer, 14);
if (!strcmp(resName, resourceBuffer))
break;
}
if (resIndex == numEntries)
return false;
else
return true;
}
//--------------------------------------------------------------------------
M4ResourceManager::M4ResourceManager(M4Engine *vm): ResourceManager(vm) {
_hfs = new FileSystem(_vm->getGameFile(kFileTypeHash));
}
M4ResourceManager::~M4ResourceManager() {
}
Common::SeekableReadStream *M4ResourceManager::loadResource(const char *resourceName, bool preloadFlag) {
//printf("M4ResourceManager::loadResource() loading resource %s\n", resourceName);
Common::SeekableReadStream* result = NULL;
if (_hfs) {
// actually load the resource
result = _hfs->loadFile(resourceName, preloadFlag);
if (!result) {
error("M4ResourceManager::loadResource() Resource %s not found", resourceName);
}
} else {
error("M4ResourceManager::loadResource() No FileSystem attached");
}
return result;
}
bool M4ResourceManager::resourceExists(const char *resourceName) {
return (_hfs->getHashFileEntry(resourceName) != NULL);
}
} // End of namespace M4

141
engines/m4/resource.h Normal file
View file

@ -0,0 +1,141 @@
/* 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.
*
* $URL$
* $Id$
*
*/
#ifndef M4_RESOURCE_H
#define M4_RESOURCE_H
#include "common/scummsys.h"
#include "common/stream.h"
#include "common/endian.h"
#include "common/file.h"
#include "common/hashmap.h"
#include "common/hash-str.h"
#include "common/list.h"
#include "common/ptr.h"
/*
TODO:
- change array to HashMap if it turns out to be slow
*/
namespace M4 {
#define MAX_RESOURCES 128
#define kM4MaxFilenameSize 33
enum {
kResourcePurge = 1 << 1
};
class FileSystem {
public:
struct HashHagEntry {
char filename[kM4MaxFilenameSize];
byte fileIndex;
Common::File *hagFile;
};
struct HashFileEntry {
char filename[kM4MaxFilenameSize];
byte hagfile;
uint32 offset, size;
};
FileSystem(const char *hashFilename);
~FileSystem();
Common::SeekableReadStream *loadFile(const char *resourceName, bool preloadFlag);
static void changeExtension(char *destName, const char *sourceName, const char *extension);
const HashFileEntry *getHashFileEntry(const char *filename);
private:
typedef Common::HashMap<Common::String,HashFileEntry,Common::IgnoreCase_Hash,Common::IgnoreCase_EqualTo> FileHashMap;
HashHagEntry _hagEntries[10]; // GLOBAL.HAG and SECTION1.HAG to SECTION9.HAG
FileHashMap _fileEntries;
};
struct Resource {
char name[64];
Common::SeekableReadStream *stream;
uint8 *buffer;
uint8 flags;
};
class ResourceManager {
protected:
typedef Common::List<Common::SharedPtr<Resource> > ResourceList;
typedef ResourceList::iterator ResourceIterator;
ResourceList _resources;
M4Engine *_vm;
virtual Common::SeekableReadStream *loadResource(const char *resourceName, bool loadFlag) = 0;
public:
ResourceManager(M4Engine *vm): _vm(vm) {};
virtual ~ResourceManager();
Common::SeekableReadStream *get(const char *resourceName, bool loadFlag = true);
void toss(const char *resourceName);
void purge();
void dump();
virtual bool resourceExists(const char *resourceName) = 0;
Common::SeekableReadStream *openFile(const char *resourceName) { return get(resourceName, false); }
void changeExtension(char *destName, const char *sourceName, const char *extension) {
FileSystem::changeExtension(destName, sourceName, extension);
}
};
enum ResourceType {RESTYPE_ROOM, RESTYPE_SC, RESTYPE_TEXT, RESTYPE_QUO, RESTYPE_I,
RESTYPE_OB, RESTYPE_FONT, RESTYPE_SOUND, RESTYPE_SPEECH, RESTYPE_HAS_EXT, RESTYPE_NO_EXT};
class MADSResourceManager: public ResourceManager {
private:
ResourceType getResourceType(const char *resourceName);
const char *getResourceFilename(const char *resourceName);
protected:
Common::SeekableReadStream *loadResource(const char *resourceName, bool loadFlag);
public:
MADSResourceManager(M4Engine *vm): ResourceManager(vm) {};
bool resourceExists(const char *resourceName);
};
class M4ResourceManager: public ResourceManager {
protected:
Common::SeekableReadStream *loadResource(const char *resourceName, bool loadFlag);
public:
M4ResourceManager(M4Engine *vm);
~M4ResourceManager();
bool resourceExists(const char *resourceName);
private:
FileSystem *_hfs;
};
} // End of namespace M4
#endif

168
engines/m4/saveload.cpp 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 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#include "common/file.h"
#include "common/savefile.h"
#include "m4/m4.h"
#include "m4/saveload.h"
#include "m4/sprite.h"
namespace M4 {
const char *orionSavesList = "saves.dir";
SaveLoad::SaveLoad(M4Engine *vm) : _vm(vm) {
// For Orion Burger, check the existance of a 'saves.dir' file to determine whether to
// act exactly like the original. Otherwise, we'll use the ScummVM standard, where we'll
// keep all the data for a savegame in a single file
Common::File file;
_emulateOriginal = file.exists(orionSavesList);
}
const char *SaveLoad::generateSaveName(int slotNumber) {
static char buffer[15];
sprintf(buffer, _emulateOriginal ? "burg%.3d.sav" : "burger.%.3d", slotNumber);
return buffer;
}
bool SaveLoad::hasSaves() {
// Return true if a savegame file exists for the first slot
if (_emulateOriginal) {
Common::File f;
return f.exists(generateSaveName(1));
} else {
Common::ReadStream *f = _vm->saveManager()->openForLoading(generateSaveName(1));
if (f == NULL)
return false;
delete f;
return true;
}
}
SaveGameList *SaveLoad::getSaves() {
SaveGameList *result = new SaveGameList();
char saveName[MAX_SAVEGAME_NAME];
Common::ReadStream *f = NULL;
if (_emulateOriginal) {
Common::File *saveFile = new Common::File();
saveFile->open(orionSavesList);
f = saveFile;
}
for (int slotNumber = 1; slotNumber <= 99; ++slotNumber) {
if (_emulateOriginal) {
// Read in savegame name from save directory
bool isPresent = (f->readByte() != 0);
f->read(&saveName[0], MAX_SAVEGAME_NAME);
if (isPresent)
result->push_back(Common::String(saveName));
else {
result->push_back(Common::String());
}
} else {
// Read in savegame name from savegame files directly
Common::ReadStream *saveFile = _vm->saveManager()->openForLoading(
generateSaveName(slotNumber));
if (!saveFile) {
// No savegame prsent at that slot
result->push_back(Common::String());
} else {
// Skip over byte offset
assert(saveFile->readUint32LE() < 0x100);
// Read in savegame name
saveFile->read(&saveName[0], MAX_SAVEGAME_NAME);
result->push_back(Common::String(saveName));
delete saveFile;
}
}
}
if (_emulateOriginal)
delete f;
return result;
}
M4Surface *SaveLoad::getThumbnail(int slotNumber) {
Common::SeekableReadStream *saveFile;
uint32 dataOffset;
if (_emulateOriginal) {
// Get savegame file from original game folder
Common::File *f = new Common::File();
if (!f->open(generateSaveName(slotNumber))) {
delete f;
return NULL;
}
saveFile = f;
} else {
// Open up savegame for access via savefile manager
saveFile = _vm->saveManager()->openForLoading(generateSaveName(slotNumber));
}
if (!saveFile)
return NULL;
dataOffset = saveFile->readUint32LE();
assert(dataOffset < 0x100);
saveFile->seek(dataOffset, SEEK_CUR);
// Read in the sprite data
saveFile->seek(16, SEEK_CUR);
int width = saveFile->readUint32LE();
int height = saveFile->readUint32LE();
saveFile->seek(21, SEEK_CUR);
saveFile->readUint32LE(); // sprite data size
M4Sprite *result = new M4Sprite(saveFile, 0, 0, width, height);
delete saveFile;
return result;
}
bool SaveLoad::load(int slotNumber) {
// TODO: Currently it's hardcoded to return a failure
return false;
}
bool SaveLoad::save(int slotNumber, Common::String saveName) {
// TODO: Currently it's hardcoded to return a failure
return false;
}
} // End of namespace M4

57
engines/m4/saveload.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 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#ifndef M4_SAVELOAD_H
#define M4_SAVELOAD_H
#include "m4/graphics.h"
#include "common/ptr.h"
#define MAX_SAVEGAME_NAME 80
namespace M4 {
typedef Common::List<Common::String> SaveGameList;
typedef SaveGameList::iterator SaveGameIterator;
class SaveLoad {
private:
M4Engine *_vm;
bool _emulateOriginal;
const char *generateSaveName(int slotNumber);
public:
SaveLoad(M4Engine *vm);
bool hasSaves();
SaveGameList *getSaves();
M4Surface *getThumbnail(int slotNumber);
bool load(int slotNumber);
bool save(int slotNumber, Common::String saveName);
};
}
#endif

669
engines/m4/scene.cpp Normal file
View file

@ -0,0 +1,669 @@
/* 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.
*
* $URL$
* $Id$
*
*/
#include "common/system.h"
#include "m4/globals.h"
#include "m4/scene.h"
#include "m4/events.h"
#include "m4/graphics.h"
#include "m4/rails.h"
#include "m4/font.h"
#include "m4/m4_views.h"
#include "m4/compression.h"
namespace M4 {
Scene::Scene(M4Engine *vm): View(vm, Common::Rect(0, 0, vm->_screen->width(), vm->_screen->height())) {
_screenType = VIEWID_SCENE;
_sceneResources.hotspots = new HotSpotList();
_sceneResources.parallax = new HotSpotList();
_sceneResources.props = new HotSpotList();
_backgroundSurface = new M4Surface();
_codeSurface = new M4Surface();
_madsInterfaceSurface = new M4Surface();
_sceneSprites = NULL;
_palData = NULL;
_interfacePal = NULL;
_inverseColorTable = NULL;
strcpy(_statusText, "");
_vm->_rails->setCodeSurface(_codeSurface);
}
Scene::~Scene() {
_sceneResources.hotspots->clear();
_sceneResources.parallax->clear();
_sceneResources.props->clear();
delete _sceneResources.hotspots;
delete _sceneResources.parallax;
delete _sceneResources.props;
delete _backgroundSurface;
delete _codeSurface;
delete _madsInterfaceSurface;
if (_sceneSprites)
delete _sceneSprites;
_vm->_palette->deleteAllRanges();
if (_palData)
delete _palData;
if (_interfacePal)
delete _interfacePal;
if (_inverseColorTable)
delete[] _inverseColorTable;
}
void Scene::loadScene(int sceneNumber) {
_currentScene = sceneNumber;
// Close the menu if it's active
if (!_vm->isM4()) {
View *mainMenu = _vm->_viewManager->getView(VIEWID_MAINMENU);
if (mainMenu != NULL) {
_vm->_viewManager->deleteView(mainMenu);
}
}
// TODO: Check if we were loading a game
// Load scene background and set palette
if (_palData) {
_vm->_palette->deleteRange(_palData);
delete _palData;
}
if (_interfacePal) {
_vm->_palette->deleteRange(_interfacePal);
delete _interfacePal;
}
if (_vm->isM4()) {
_backgroundSurface->loadBackground(sceneNumber);
_palData = NULL;
} else {
_backgroundSurface->loadBackground(sceneNumber, &_palData);
_vm->_palette->addRange(_palData);
_backgroundSurface->translate(_palData);
if (sceneNumber < 900) {
/*_backgroundSurface->fillRect(Common::Rect(0, MADS_SURFACE_HEIGHT,
_backgroundSurface->width(), _backgroundSurface->height()),
_vm->_palette->BLACK);*/
// TODO: interface palette
_madsInterfaceSurface->madsloadInterface(0, &_interfacePal);
_vm->_palette->addRange(_interfacePal);
_madsInterfaceSurface->translate(_interfacePal);
_backgroundSurface->copyFrom(_madsInterfaceSurface, Common::Rect(0, 0, 320, 44), 0, 200 - 44);
}
}
if (_vm->getGameType() == GType_Burger &&
sceneNumber != TITLE_SCENE_BURGER && sceneNumber != MAINMENU_SCENE_BURGER)
_vm->_interfaceView->setStatusText("");
// Load scene def file (*.CHK)
if (_vm->isM4()) {
loadSceneResources(sceneNumber);
loadSceneInverseColorTable(sceneNumber);
} else {
// Don't load other screen resources for system screens
if (sceneNumber >= 900)
return;
loadSceneHotSpotsMads(sceneNumber);
}
// TODO: set walker scaling
// TODO: destroy woodscript buffer
// Load scene walk path file (*.COD/*.WW?)
loadSceneCodes(sceneNumber);
// Load inverse color table file (*.IPL)
loadSceneInverseColorTable(sceneNumber);
if (_vm->isM4()) {
if (_vm->getGameType() != GType_Burger) {
// Load scene sprites file (*.SSB)
loadSceneSprites(sceneNumber);
// Load scene sprite codes file (*.SSC)
loadSceneSpriteCodes(sceneNumber);
}
if (sceneNumber != TITLE_SCENE_BURGER && sceneNumber != MAINMENU_SCENE_BURGER) {
_vm->_interfaceView->show();
showSprites();
}
}
// Purge resources
_vm->res()->purge();
}
void Scene::loadSceneResources(int sceneNumber) {
char filename[kM4MaxFilenameSize];
int i = 0, x = 0, y = 0;
sprintf(filename, "%i.chk", sceneNumber);
Common::SeekableReadStream *sceneS = _vm->res()->get(filename);
if (sceneS != NULL) {
sceneS->read(_sceneResources.artBase, MAX_CHK_FILENAME_SIZE);
sceneS->read(_sceneResources.pictureBase, MAX_CHK_FILENAME_SIZE);
_sceneResources.hotspotCount = sceneS->readUint32LE();
_sceneResources.parallaxCount = sceneS->readUint32LE();
_sceneResources.propsCount = sceneS->readUint32LE();
_sceneResources.frontY = sceneS->readUint32LE();
_sceneResources.backY = sceneS->readUint32LE();
_sceneResources.frontScale = sceneS->readUint32LE();
_sceneResources.backScale = sceneS->readUint32LE();
for (i = 0; i < 16; i++)
_sceneResources.depthTable[i] = sceneS->readUint16LE();
_sceneResources.railNodeCount = sceneS->readUint32LE();
// Clear rails from previous scene
_vm->_rails->clearRails();
for (i = 0; i < _sceneResources.railNodeCount; i++) {
x = sceneS->readUint32LE();
y = sceneS->readUint32LE();
if (_vm->_rails->addRailNode(x, y, true) < 0) {
warning("Too many rail nodes defined for scene");
}
}
// Clear current hotspot lists
_sceneResources.hotspots->clear();
_sceneResources.parallax->clear();
_sceneResources.props->clear();
_sceneResources.hotspots->loadHotSpotsM4(sceneS, _sceneResources.hotspotCount);
_sceneResources.parallax->loadHotSpotsM4(sceneS, _sceneResources.parallaxCount);
_sceneResources.props->loadHotSpotsM4(sceneS, _sceneResources.propsCount);
// Note that toss() deletes the MemoryReadStream
_vm->res()->toss(filename);
}
}
void Scene::loadSceneHotSpotsMads(int sceneNumber) {
char filename[kM4MaxFilenameSize];
sprintf(filename, "rm%i.hh", sceneNumber);
MadsPack hotSpotData(filename, _vm);
Common::SeekableReadStream *hotspotStream = hotSpotData.getItemStream(0);
int hotspotCount = hotspotStream->readUint16LE();
delete hotspotStream;
HotSpotList *hotspotList = _sceneResources.hotspots;
_sceneResources.hotspotCount = hotspotCount;
hotspotStream = hotSpotData.getItemStream(1);
// Clear current hotspot lists
_sceneResources.hotspots->clear();
_sceneResources.hotspots->loadHotSpotsMads(hotspotStream, _sceneResources.hotspotCount);
delete hotspotStream;
}
void Scene::loadSceneCodes(int sceneNumber, int index) {
char filename[kM4MaxFilenameSize];
Common::SeekableReadStream *sceneS;
if (_vm->isM4()) {
sprintf(filename, "%i.cod", sceneNumber);
sceneS = _vm->res()->openFile(filename);
_codeSurface->loadCodesM4(sceneS);
_vm->res()->toss(filename);
} else if (_vm->getGameType() == GType_Phantom || _vm->getGameType() == GType_DragonSphere) {
sprintf(filename, "rm%i.ww%i", sceneNumber, index);
MadsPack walkData(filename, _vm);
sceneS = walkData.getItemStream(0);
_codeSurface->loadCodesMads(sceneS);
_vm->res()->toss(filename);
} else if (_vm->getGameType() == GType_RexNebular) {
// TODO
return;
}
}
void Scene::loadSceneInverseColorTable(int sceneNumber) {
char filename[kM4MaxFilenameSize];
Common::SeekableReadStream *iplS;
if (_vm->isM4()) {
sprintf(filename, "%i.ipl", sceneNumber);
iplS = _vm->res()->openFile(filename);
if (_inverseColorTable)
delete[] _inverseColorTable;
_inverseColorTable = new byte[iplS->size()];
iplS->read(_inverseColorTable, iplS->size());
_vm->res()->toss(filename);
} else {
// TODO?
return;
}
}
void Scene::loadSceneSprites(int sceneNumber) {
char filename[kM4MaxFilenameSize];
sprintf(filename, "%i.ssb", sceneNumber);
Common::SeekableReadStream *sceneS = _vm->res()->get(filename);
_sceneSprites = new SpriteAsset(_vm, sceneS, sceneS->size(), filename);
_vm->res()->toss(filename);
printf("Scene has %d sprites, each one having %d colors\n", _sceneSprites->getCount(), _sceneSprites->getColorCount());
}
void Scene::loadSceneSpriteCodes(int sceneNumber) {
char filename[kM4MaxFilenameSize];
sprintf(filename, "%i.ssc", sceneNumber);
Common::SeekableReadStream *sceneS = _vm->res()->get(filename);
// TODO
if (sceneS != NULL) {
SpriteAsset* _sceneSpriteCodes = new SpriteAsset(_vm, sceneS, sceneS->size(), filename);
int colorCount = _sceneSpriteCodes->getColorCount();
// RGB8* spritePalette = _sceneSpriteCodes->getPalette();
//_vm->_palette->setPalette(spritePalette, 0, colorCount);
printf("Scene has %d sprite codes, each one having %d colors\n", _sceneSpriteCodes->getCount(), colorCount);
// Note that toss() deletes the MemoryReadStream
_vm->res()->toss(filename);
}
}
void Scene::showSprites() {
// TODO: This is all experimental code, it needs heavy restructuring
// and cleanup
// taken from set_walker_scaling() in adv_walk.cpp. A proper implementation will need
// to store these in global variables
int minScaling = FixedDiv(_sceneResources.backScale << 16, 100 << 16);
int maxScaling = FixedDiv(_sceneResources.frontScale << 16, 100 << 16);
int scaler;
_vm->_actor->setWalkerDirection(kFacingSouthEast);
//_vm->_actor->setWalkerPalette();
// taken from set_walker_scaling() in adv_walk.cpp
if (_sceneResources.frontY == _sceneResources.backY)
scaler = 0;
else
scaler = FixedDiv(maxScaling - minScaling,
(_sceneResources.frontY << 16) - (_sceneResources.backY << 16));
// FIXME: For now, we (incorrectly) scale the walker to 50% of the scene's max scaling
_vm->_actor->setWalkerScaling(scaler / 2);
// Test code to display the protagonist
_vm->_actor->placeWalkerSpriteAt(0, 320, 200);
// Test code to display scene sprites
// TODO
}
void Scene::checkHotspotAtMousePos(int x, int y) {
if (_vm->getGameType() == GType_Riddle)
return;
// TODO: loads of things to do here, only the mouse cursor and the status
// text is changed for now
// Only scene hotspots are checked for now, not parallax/props, as the
// latter ones are not used by Orion Burger
HotSpot *currentHotSpot = _sceneResources.hotspots->findByXY(x, y);
if (currentHotSpot != NULL && currentHotSpot->getActive()) {
if (_vm->_mouse->getCursorNum() != CURSOR_LOOK &&
_vm->_mouse->getCursorNum() != CURSOR_TAKE &&
_vm->_mouse->getCursorNum() != CURSOR_USE &&
_vm->_interfaceView->_inventory.getSelectedIndex() == -1) {
_vm->_mouse->setCursorNum(currentHotSpot->getCursor());
}
_vm->_interfaceView->setStatusText(currentHotSpot->getPrep());
} else {
if (_vm->_mouse->getCursorNum() != CURSOR_LOOK &&
_vm->_mouse->getCursorNum() != CURSOR_TAKE &&
_vm->_mouse->getCursorNum() != CURSOR_USE &&
_vm->_interfaceView->_inventory.getSelectedIndex() == -1) {
_vm->_mouse->setCursorNum(0);
} else {
}
}
}
void Scene::checkHotspotAtMousePosMads(int x, int y) {
HotSpot *currentHotSpot = _sceneResources.hotspots->findByXY(x, y);
if (currentHotSpot != NULL) {
_vm->_mouse->setCursorNum(currentHotSpot->getCursor());
// This is the "easy" interface, which updates the status text when the mouse is moved
// TODO: toggle this code for easy/normal interface mode
char statusText[50];
if (currentHotSpot->getVerbID() != 0) {
sprintf(statusText, "%s %s\n", currentHotSpot->getVerb(), currentHotSpot->getVocab());
} else {
sprintf(statusText, "%s %s\n", _vm->_globals->getVocab(kVerbWalkTo), currentHotSpot->getVocab());
}
statusText[0] = toupper(statusText[0]); // capitalize first letter
setMADSStatusText(statusText);
} else {
_vm->_mouse->setCursorNum(0);
setMADSStatusText("");
}
}
// Test function, shows all scene hotspots
void Scene::showHotSpots() {
int i = 0;
HotSpot *currentHotSpot;
// hotspots (green)
for (i = 0; i < _sceneResources.hotspotCount; i++) {
currentHotSpot = _sceneResources.hotspots->get(i);
_backgroundSurface->frameRect(currentHotSpot->getRect(), _vm->_palette->GREEN);
}
if (_vm->isM4()) {
// parallax (yellow)
for (i = 0; i < _sceneResources.parallaxCount; i++) {
currentHotSpot = _sceneResources.parallax->get(i);
_backgroundSurface->frameRect(currentHotSpot->getRect(), _vm->_palette->YELLOW);
}
// props (red)
for (i = 0; i < _sceneResources.propsCount; i++) {
currentHotSpot = _sceneResources.props->get(i);
_backgroundSurface->frameRect(currentHotSpot->getRect(), _vm->_palette->RED);
}
}
}
// Test function, shows all scene codes
void Scene::showCodes() {
uint8 *pixelData = (uint8*)_codeSurface->pixels;
for (int i = 0; i < _codeSurface->w * _codeSurface->h; i++)
if (pixelData[i] & 0x10)
pixelData[i] = 0xFF;
else
pixelData[i] = 0;
byte colors[256 * 4];
memset(colors, 0, sizeof(colors));
colors[255 * 4 + 0] = 255;
colors[255 * 4 + 1] = 255;
colors[255 * 4 + 2] = 255;
_vm->_palette->setPalette(colors, 0, 256);
_backgroundSurface->copyFrom(_codeSurface, Common::Rect(0, 0, 640, 480), 0, 0);
//_system->copyRectToScreen((byte *)codes->pixels, codes->w, 0, 0, codes->w, codes->h);
}
void Scene::playIntro() {
}
void Scene::update() {
// TODO: Needs a proper implementation
// NOTE: Don't copy the background when in M4 mode or WoodScript anims won't be shown
if (!_vm->isM4()) {
_backgroundSurface->copyTo(this);
if (_statusText[0]) {
// Text colors are inverted in Dragonsphere
if (_vm->getGameType() == GType_DragonSphere)
_vm->_font->setColors(_vm->_palette->BLACK, _vm->_palette->WHITE, _vm->_palette->BLACK);
else
_vm->_font->setColors(_vm->_palette->WHITE, _vm->_palette->BLACK, _vm->_palette->BLACK);
_vm->_font->setFont(FONT_MAIN_MADS);
_vm->_font->writeString(this, _statusText, (width() - _vm->_font->getWidth(_statusText)) / 2, 142, 0);
}
}
}
void Scene::onRefresh(RectList *rects, M4Surface *destSurface) {
update();
View::onRefresh(rects, destSurface);
}
bool Scene::onEvent(M4EventType eventType, int param1, int x, int y, bool &captureEvents) {
//if (_vm->getGameType() != GType_Burger)
// return false;
// If the game is currently paused, don't do any scene processing
if (_vm->_kernel->paused)
return false;
switch (eventType) {
case MEVENT_LEFT_CLICK:
{
if (_vm->getGameType() == GType_Burger) {
// Place a Wilbur sprite with the correct facing
HotSpot *currentHotSpot = _sceneResources.hotspots->findByXY(x, y);
if (currentHotSpot != NULL && currentHotSpot->getActive()) {
update();
_vm->_actor->setWalkerDirection(currentHotSpot->getFacing());
int posX = currentHotSpot->getFeetX();
int posY = currentHotSpot->getFeetY() -
scaleValue(_vm->_actor->getWalkerHeight(), _vm->_actor->getWalkerScaling(), 0);
//_vm->_actor->placeWalkerSpriteAt(0, posX, posY);
// Player said.... (for scene scripts)
printf("Player said: %s %s\n", currentHotSpot->getVerb(), currentHotSpot->getVocab());
// FIXME: This should be moved somewhere else, and is incomplete
if (_vm->_interfaceView->_inventory.getSelectedIndex() == -1) {
if (_vm->_mouse->getVerb() == NULL) {
strcpy(_vm->_player->verb, currentHotSpot->getVerb());
} else {
strcpy(_vm->_player->verb, _vm->_mouse->getVerb());
}
} else {
strcpy(_vm->_player->verb, _vm->_interfaceView->_inventory.getSelectedObjectName());
}
strcpy(_vm->_player->noun, currentHotSpot->getVocab());
strcpy(_vm->_player->object, "");
_vm->_player->commandReady = true;
printf("## Player said: %s %s\n", _vm->_player->verb, _vm->_player->noun);
}
}
if (!_vm->isM4()) {
HotSpot *currentHotSpot = _sceneResources.hotspots->findByXY(x, y);
if (currentHotSpot != NULL) {
char statusText[50];
if (currentHotSpot->getVerbID() != 0) {
sprintf(statusText, "%s %s\n", currentHotSpot->getVerb(), currentHotSpot->getVocab());
} else {
sprintf(statusText, "%s %s\n", _vm->_globals->getVocab(kVerbWalkTo), currentHotSpot->getVocab());
}
statusText[0] = toupper(statusText[0]); // capitalize first letter
setMADSStatusText(statusText);
}
}
}
break;
case MEVENT_RIGHT_CLICK:
if (_vm->getGameType() == GType_Burger) {
nextCommonCursor();
_vm->_interfaceView->_inventory.clearSelected();
}
break;
case MEVENT_MOVE:
if (_vm->isM4())
checkHotspotAtMousePos(x, y);
else
checkHotspotAtMousePosMads(x, y);
break;
default:
return false;
}
return true;
}
void Scene::nextCommonCursor() {
int cursorIndex = _vm->_mouse->getCursorNum();
switch (cursorIndex) {
case CURSOR_ARROW:
cursorIndex = CURSOR_LOOK;
break;
case CURSOR_LOOK:
cursorIndex = CURSOR_TAKE;
break;
case CURSOR_TAKE:
cursorIndex = CURSOR_USE;
break;
case CURSOR_USE:
cursorIndex = CURSOR_ARROW;
break;
default:
cursorIndex = CURSOR_ARROW;
}
_vm->_mouse->setCursorNum(cursorIndex);
}
enum boxSprites {
topLeft = 0,
topRight = 1,
bottomLeft = 2,
bottomRight = 3,
left = 4,
right = 5,
top = 6,
bottom = 7,
topMiddle = 8,
filler1 = 9,
filler2 = 10
// TODO: finish this
};
// TODO: calculate width and height, show text, show face if it exists
// TODO: this has been tested with Dragonsphere only, there are some differences
// in the sprites used in Phantom
void Scene::showMADSV2TextBox(char *text, int x, int y, char *faceName) {
int repeatX = 40; // FIXME: this is hardcoded
int repeatY = 30; // FIXME: this is hardcoded
int curX = x, curY = y;
int topRightX = x; // TODO: this is probably not needed
Common::SeekableReadStream *data = _vm->res()->get("box.ss");
SpriteAsset *boxSprites = new SpriteAsset(_vm, data, data->size(), "box.ss");
_vm->res()->toss("box.ss");
RGBList *palData = new RGBList(boxSprites->getColorCount(), boxSprites->getPalette(), true);
_vm->_palette->addRange(palData);
for (int i = 0; i < boxSprites->getCount(); i++)
boxSprites->getFrame(i)->translate(palData); // sprite pixel translation
// Top left corner
boxSprites->getFrame(topLeft)->copyTo(_backgroundSurface, x, curY);
curX += boxSprites->getFrame(topLeft)->width();
// Top line
for (int i = 0; i < repeatX; i++) {
boxSprites->getFrame(top)->copyTo(_backgroundSurface, curX, curY + 3);
curX += boxSprites->getFrame(top)->width();
}
// Top right corner
boxSprites->getFrame(topRight)->copyTo(_backgroundSurface, curX, curY);
topRightX = curX;
// Top middle
// FIXME: the transparent color for this is also the black border color
boxSprites->getFrame(topMiddle)->copyTo(_backgroundSurface,
x + (curX - x) / 2 - boxSprites->getFrame(topMiddle)->width() / 2,
curY - 5, 167);
curX = x;
curY += boxSprites->getFrame(topLeft)->height();
// -----------------------------------------------------------------------------------------------
// Draw contents
for (int i = 0; i < repeatY; i++) {
for (int j = 0; j < repeatX; j++) {
if (j == 0) {
boxSprites->getFrame(left)->copyTo(_backgroundSurface, curX + 3, curY);
curX += boxSprites->getFrame(left)->width();
} else if (j == repeatX - 1) {
curX = topRightX - 2;
boxSprites->getFrame(right)->copyTo(_backgroundSurface, curX + 3, curY + 1);
} else {
// TODO: the background of the contents follows a pattern which is not understood yet
if (j % 2 == 0) {
boxSprites->getFrame(filler1)->copyTo(_backgroundSurface, curX + 3, curY);
curX += boxSprites->getFrame(filler1)->width();
} else {
boxSprites->getFrame(filler2)->copyTo(_backgroundSurface, curX + 3, curY);
curX += boxSprites->getFrame(filler2)->width();
}
}
} // for j
curX = x;
curY += boxSprites->getFrame(left)->height();
} // for i
// -----------------------------------------------------------------------------------------------
curX = x;
// Bottom left corner
boxSprites->getFrame(bottomLeft)->copyTo(_backgroundSurface, curX, curY);
curX += boxSprites->getFrame(bottomLeft)->width();
// Bottom line
for (int i = 0; i < repeatX; i++) {
boxSprites->getFrame(bottom)->copyTo(_backgroundSurface, curX, curY + 1);
curX += boxSprites->getFrame(bottom)->width();
}
// Bottom right corner
boxSprites->getFrame(bottomRight)->copyTo(_backgroundSurface, curX, curY + 1);
}
} // End of namespace M4

124
engines/m4/scene.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 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#ifndef M4_SCENE_H
#define M4_SCENE_H
class View;
#include "m4/assets.h"
#include "m4/hotspot.h"
#include "m4/graphics.h"
#include "m4/viewmgr.h"
namespace M4 {
#define TITLE_SCENE_BURGER 951 // 951 = intro, 901 = demo menu, 971 = first scene
#define MAINMENU_SCENE_BURGER 903
#define FIRST_SCENE 101
#define MAX_CHK_FILENAME_SIZE 144
#define INTERFACE_HEIGHT 106
#define MADS_SURFACE_HEIGHT 156
enum MADSVerbs {
kVerbLook = 2,
kVerbTake = 3,
kVerbPush = 4,
kVerbOpen = 5,
kVerbPut = 6,
kVerbTalkTo = 7,
kVerbGive = 8,
kVerbPull = 9,
kVerbClose = 10,
kVerbThrow = 11,
kVerbWalkTo = 12
};
struct SceneResources {
char artBase[MAX_CHK_FILENAME_SIZE];
char pictureBase[MAX_CHK_FILENAME_SIZE];
int32 hotspotCount;
HotSpotList *hotspots;
int32 parallaxCount;
HotSpotList *parallax;
int32 propsCount;
HotSpotList *props;
int32 frontY, backY;
int32 frontScale, backScale;
int16 depthTable[16];
int32 railNodeCount; // # of rails
};
class Scene: public View {
public:
Scene(M4Engine *vm);
~Scene();
// TODO: perhaps move playIntro() someplace else?
void playIntro();
void loadScene(int sceneNumber);
void loadSceneResources(int sceneNumber);
void loadSceneHotSpotsMads(int sceneNumber);
void loadSceneCodes(int sceneNumber, int index = 0);
void loadSceneInverseColorTable(int sceneNumber);
void loadSceneSprites(int sceneNumber);
void loadSceneSpriteCodes(int sceneNumber);
void showSprites();
void checkHotspotAtMousePos(int x, int y);
void checkHotspotAtMousePosMads(int x, int y);
void showHotSpots();
void showCodes();
int getCurrentScene() { return _currentScene; }
SceneResources getSceneResources() { return _sceneResources; }
M4Surface *getBackgroundSurface() const { return _backgroundSurface; }
byte *getInverseColorTable() const { return _inverseColorTable; }
void update();
void setMADSStatusText(const char *text) { strcpy(_statusText, text); }
void showMADSV2TextBox(char *text, int x, int y, char *faceName);
void onRefresh(RectList *rects, M4Surface *destSurface);
bool onEvent(M4EventType eventType, int param1, int x, int y, bool &captureEvents);
private:
int _currentScene;
M4Surface *_backgroundSurface;
M4Surface *_codeSurface;
M4Surface *_madsInterfaceSurface;
byte *_inverseColorTable;
RGBList *_palData;
RGBList *_interfacePal;
SceneResources _sceneResources;
HotSpotList _sceneHotspots;
SpriteAsset *_sceneSprites;
SpriteAsset *_walkerSprite;
char _statusText[100];
void nextCommonCursor();
};
} // End of namespace M4
#endif

1406
engines/m4/script.cpp Normal file

File diff suppressed because it is too large Load diff

457
engines/m4/script.h Normal file
View file

@ -0,0 +1,457 @@
/* 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.
*
* $URL$
* $Id$
*
*/
#ifndef M4_SCRIPT_H
#define M4_SCRIPT_H
#include "common/file.h"
#include "common/stream.h"
#include "common/hashmap.h"
#include "common/str.h"
#include "common/stack.h"
#include "m4/woodscript.h"
namespace M4 {
const unsigned long kScriptFileMagic = 0x5845344D;
const unsigned long kScriptFileVersion = 1;
enum ScriptValueType {
kInteger,
kConstString,
kLogicVar,
kLogicVarRef,
kGameVar,
kKernelVar,
kDataRef,
kRegister,
kStackVar
};
enum ScriptDataType {
kStreamBreakSeries,
kStreamPlaySeries,
kSaidArray,
kParserArray,
kSpeechArray,
kCreditsArray,
kInvObj,
kMineRoom,
kButtonItem
};
class ScriptInterpreter;
class StringTable {
public:
StringTable();
~StringTable();
void load(Common::File *fd);
int size() { return _strings.size(); }
const char *operator[](uint32 index) const {
assert(index < _strings.size() );
return _strings[index];
}
protected:
Common::Array<const char*> _strings;
char *_stringsData;
};
struct ScriptValue {
ScriptValueType type;
union {
int value;
};
ScriptValue() : type(kInteger), value(0) {};
ScriptValue(ScriptValueType itype, int ivalue) : type(itype), value(ivalue) {};
ScriptValue(const int intValue) : type(kInteger), value(intValue) {};
ScriptValue& operator=(const int intValue) {
type = kInteger;
value = intValue;
return *this;
}
};
class ScriptDataItem {
public:
ScriptDataItem() : _inter(NULL) {}
ScriptDataItem(ScriptInterpreter *inter) : _inter(inter) {}
virtual ~ScriptDataItem() {}
virtual void load(Common::File *fd) = 0;
static int type() { return -1; }
protected:
ScriptInterpreter *_inter;
};
class ScriptDataCache {
public:
ScriptDataCache(ScriptInterpreter *inter) : _inter(inter) {
}
~ScriptDataCache() {
clear();
}
template<class T>
T *load(Common::File *fd, uint32 ofs) {
T *item;
if (_cache.contains(ofs)) {
item = (T*)(_cache[ofs]);
} else {
item = new T(_inter);
fd->seek(ofs + 4); // "+4" skips the data size
item->load(fd);
_cache[ofs] = item;
}
return item;
}
void clear() {
// TODO: Free all cached items
}
protected:
typedef Common::HashMap<uint32, ScriptDataItem*> CacheMap;
CacheMap _cache;
ScriptInterpreter *_inter;
};
struct SeriesStreamBreakItem {
int frameNum;
const char *digiName;
int digiChannel;
int digiVolume;
int trigger;
int flags;
ScriptValue variable;
int value;
};
class SeriesStreamBreakList : public ScriptDataItem {
public:
SeriesStreamBreakList(ScriptInterpreter *inter) : ScriptDataItem(inter) {}
~SeriesStreamBreakList();
void load(Common::File *fd);
int size() const { return _items.size(); }
SeriesStreamBreakItem *operator[](int index) const { return _items[index]; }
static int type() { return 0; }
protected:
Common::Array<SeriesStreamBreakItem*> _items;
};
struct SaidArrayItem {
const char *itemName;
const char *digiNameLook;
const char *digiNameTake;
const char *digiNameGear;
};
class SaidArray : public ScriptDataItem {
public:
SaidArray(ScriptInterpreter *inter) : ScriptDataItem(inter) {}
~SaidArray();
void load(Common::File *fd);
int size() const { return _items.size(); }
SaidArrayItem *operator[](int index) const { return _items[index]; }
static int type() { return 2; }
protected:
Common::Array<SaidArrayItem*> _items;
};
struct ParserArrayItem {
const char *w0;
const char *w1;
int trigger;
ScriptValue testVariable;
int testValue;
ScriptValue variable;
int value;
};
class ParserArray : public ScriptDataItem {
public:
ParserArray(ScriptInterpreter *inter) : ScriptDataItem(inter) {}
~ParserArray();
void load(Common::File *fd);
int size() const { return _items.size(); }
ParserArrayItem *operator[](int index) const { return _items[index]; }
static int type() { return 3; }
protected:
Common::Array<ParserArrayItem*> _items;
};
class ScriptFunction {
public:
ScriptFunction(ScriptInterpreter *inter);
~ScriptFunction();
void load(Common::File *fd);
void jumpAbsolute(uint32 ofs);
void jumpRelative(int32 ofs);
byte readByte();
uint32 readUint32();
protected:
ScriptInterpreter *_inter;
Common::MemoryReadStream *_code;
};
struct ScriptFunctionEntry {
uint32 offset;
ScriptFunction *func;
ScriptFunctionEntry(uint32 funcOffset) : offset(funcOffset), func(NULL) {
}
};
struct ScriptDataEntry {
uint32 offset;
ScriptDataType type;
ScriptDataEntry(uint32 dataOffset, ScriptDataType dataType) : offset(dataOffset), type(dataType) {
}
};
enum ScriptKernelVariable {
kGameLanguage,
kGameVersion,
kGameCurrentRoom,
kGameNewRoom,
kGamePreviousRoom,
kGameNewSection,
kKernelTrigger,
kKernelTriggerMode,
kKernelFirstFade,
kKernelSuppressFadeUp,
kKernelContinueHandlingTrigger,
kKernelUseDebugMonitor,
kPlayerPosX,
kPlayerPosY,
kPlayerFacing,
kPlayerScale,
kPlayerDepth,
kPlayerWalkX,
kPlayerWalkY,
kPlayerReadyToWalk,
kPlayerNeedToWalk,
kPlayerCommandReady,
kPlayerWalkerInThisScene,
kPlayerVerb,
kWalkerInitialized,
kCallDaemonEveryLoop,
kConvCurrentTalker,
kConvCurrentNode,
kConvCurrentEntry,
kConvSoundToPlay,
kInterfaceVisible
};
class ScriptInterpreter {
public:
ScriptInterpreter(M4Engine *vm);
~ScriptInterpreter();
/* Opens a M4 program file */
void open(const char *filename);
void close();
/* Loads a function via the index. Creates the function object if it's not already loaded. */
ScriptFunction *loadFunction(uint32 index);
/* Loads a function via the exported name. */
ScriptFunction *loadFunction(const Common::String &name);
/* Unload all loaded functions.
This should be called before entering a new room to free unused functions. */
void unloadFunctions();
//TODO void unloadData();
/* Executes a function. */
int runFunction(ScriptFunction *scriptFunction);
void push(const ScriptValue &value);
void pop(ScriptValue &value);
void dumpStack();
void dumpRegisters();
void dumpGlobalVars();
int toInteger(const ScriptValue &value);
const char *toString(const ScriptValue &value);
// Is this ok?
template<class T>
const T& toData(const ScriptValue &value) {
printf("ScriptInterpreter::toData() index = %d; type = %d; max = %d\n", value.value, _data[value.value]->type, _data.size());
assert((uint32)value.value < _data.size());
return *(_dataCache->load<T>(_scriptFile, _data[value.value]->offset));
}
const char *getGlobalString(int index) const {
return _constStrings[index];
}
const char *loadGlobalString(Common::File *fd);
void test();
protected:
M4Engine *_vm;
typedef Common::HashMap<Common::String, uint32, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> FunctionNameMap;
Common::File *_scriptFile;
/* An array of offset/ScriptFunction* pairs for each script function */
Common::Array<ScriptFunctionEntry*> _functions;
// DEBUG only
Common::Array<Common::String> _scriptFunctionNames;
Common::Array<ScriptDataEntry*> _data;
/* Maps function name -> index of function in _functions array */
FunctionNameMap _functionNames;
StringTable _constStrings;
/* The currently running function */
ScriptFunction *_runningFunction;
int _localStackPtr;
ScriptValue _registers[8];
ScriptValue _stack[512];
int _stackPtr;
int _globalVarCount;
ScriptValue _globalVars[1024];
int _logicGlobals[512];
int _cmpFlags;
ScriptDataCache *_dataCache;
int _lineNum;
typedef int (ScriptInterpreter::*KernelFunction)();
struct KernelFunctionEntry {
KernelFunction proc;
const char *desc;
};
const KernelFunctionEntry *_kernelFunctions;
uint16 _kernelFunctionsMax;
struct KernelVariableEntry {
ScriptKernelVariable var;
const char *desc;
};
const KernelVariableEntry *_kernelVars;
int16 _kernelVarsMax;
void initScriptKernel();
void loadValue(ScriptValue &value);
void writeValue(ScriptValue &value);
void copyValue(ScriptValue &destValue, ScriptValue &sourceValue);
void derefValue(ScriptValue &value);
void callKernelFunction(uint32 index);
ScriptValue getArg(uint32 index);
void dumpArgs(uint32 count);
void callFunction(uint32 index);
bool execOpcode(byte opcode);
// Kernel functions
int o1_handleStreamBreak();
int o1_handlePlayBreak();
int o1_dispatchTriggerOnSoundState();
int o1_getRangedRandomValue();
int o1_getTicks();
int o1_preloadSound();
int o1_unloadSound();
int o1_stopSound();
int o1_playSound();
int o1_playLoopingSound();
int o1_setSoundVolume();
int o1_getSoundStatus();
int o1_getSoundDuration();
int o1_loadSeries();
int o1_unloadSeries();
int o1_showSeries();
int o1_playSeries();
int o1_setSeriesFrameRate();
int o1_playBreakSeries();
int o1_preloadBreakSeries();
int o1_unloadBreakSeries();
int o1_startBreakSeries();
int o1_dispatchTrigger();
int o1_terminateMachine();
int o1_sendWoodScriptMessage();
int o1_runConversation();
int o1_loadConversation();
int o1_exportConversationValue();
int o1_exportConversationPointer();
int o1_fadeInit();
int o1_fadeSetStart();
int o1_fadeToBlack();
int o1_initPaletteCycle();
int o1_stopPaletteCycle();
int o1_setHotspot();
int o1_hideWalker();
int o1_showWalker();
int o1_setWalkerLocation();
int o1_setWalkerFacing();
int o1_walk();
int o1_overrideCrunchTime();
int o1_addBlockingRect();
int o1_triggerTimerProc();
int o1_setPlayerCommandsAllowed();
int o1_getPlayerCommandsAllowed();
int o1_updatePlayerInfo();
int o1_hasPlayerSaid();
int o1_hasPlayerSaidAny();
int o1_playerHotspotWalkOverride();
int o1_setPlayerFacingAngle();
int o1_disablePlayerFadeToBlack();
int o1_enablePlayer();
int o1_disablePlayer();
int o1_freshenSentence();
int o1_playerHasItem();
int o1_playerGiveItem();
int o1_moveObject();
int o1_setStopSoundsBetweenRooms();
int o1_backupPalette();
int o1_unloadWilburWalker();
int o1_globalTriggerProc();
int o1_wilburSpeech();
int o1_wilburSaid();
int o1_wilburParse();
int o1_wilburTalk();
int o1_wilburFinishedTalking();
//int ();
// Kernel vars
void getKernelVar(int index, ScriptValue &value);
void setKernelVar(int index, const ScriptValue &value);
};
} // End of namespace M4
#endif

136
engines/m4/scripttab.h Normal file
View file

@ -0,0 +1,136 @@
/* This file has been autogenerated by the linker.
Do not edit it or merge it with script.cpp! */
#define FUNCTION(x) { &ScriptInterpreter::x, #x }
static KernelFunctionEntry kernelFunctions[] = {
/* 000 */
FUNCTION(o1_handleStreamBreak),
FUNCTION(o1_handlePlayBreak),
FUNCTION(o1_dispatchTriggerOnSoundState),
FUNCTION(o1_getRangedRandomValue),
/* 004 */
FUNCTION(o1_getTicks),
FUNCTION(o1_preloadSound),
FUNCTION(o1_unloadSound),
FUNCTION(o1_stopSound),
/* 008 */
FUNCTION(o1_playSound),
FUNCTION(o1_playLoopingSound),
FUNCTION(o1_setSoundVolume),
FUNCTION(o1_getSoundStatus),
/* 012 */
FUNCTION(o1_getSoundDuration),
FUNCTION(o1_loadSeries),
FUNCTION(o1_unloadSeries),
FUNCTION(o1_showSeries),
/* 016 */
FUNCTION(o1_playSeries),
FUNCTION(o1_setSeriesFrameRate),
FUNCTION(o1_playBreakSeries),
FUNCTION(o1_preloadBreakSeries),
/* 020 */
FUNCTION(o1_unloadBreakSeries),
FUNCTION(o1_startBreakSeries),
FUNCTION(o1_dispatchTrigger),
FUNCTION(o1_terminateMachine),
/* 024 */
FUNCTION(o1_sendWoodScriptMessage),
FUNCTION(o1_runConversation),
FUNCTION(o1_runConversation),
FUNCTION(o1_loadConversation),
/* 028 */
FUNCTION(o1_exportConversationValue),
FUNCTION(o1_exportConversationPointer),
FUNCTION(o1_runConversation),
FUNCTION(o1_fadeInit),
/* 032 */
FUNCTION(o1_fadeSetStart),
FUNCTION(o1_fadeToBlack),
FUNCTION(o1_initPaletteCycle),
FUNCTION(o1_stopPaletteCycle),
/* 036 */
FUNCTION(o1_setHotspot),
FUNCTION(o1_hideWalker),
FUNCTION(o1_showWalker),
FUNCTION(o1_setWalkerLocation),
/* 040 */
FUNCTION(o1_setWalkerFacing),
FUNCTION(o1_walk),
FUNCTION(o1_overrideCrunchTime),
FUNCTION(o1_addBlockingRect),
/* 044 */
FUNCTION(o1_triggerTimerProc),
FUNCTION(o1_setPlayerCommandsAllowed),
FUNCTION(o1_getPlayerCommandsAllowed),
FUNCTION(o1_updatePlayerInfo),
/* 048 */
FUNCTION(o1_hasPlayerSaid),
FUNCTION(o1_hasPlayerSaidAny),
FUNCTION(o1_playerHotspotWalkOverride),
FUNCTION(o1_setPlayerFacingAngle),
/* 052 */
FUNCTION(o1_disablePlayerFadeToBlack),
FUNCTION(o1_enablePlayer),
FUNCTION(o1_disablePlayer),
FUNCTION(o1_freshenSentence),
/* 056 */
FUNCTION(o1_playerHasItem),
FUNCTION(o1_playerGiveItem),
FUNCTION(o1_moveObject),
FUNCTION(o1_setStopSoundsBetweenRooms),
/* 060 */
FUNCTION(o1_backupPalette),
FUNCTION(o1_unloadWilburWalker),
FUNCTION(o1_globalTriggerProc),
FUNCTION(o1_wilburSpeech),
/* 064 */
FUNCTION(o1_wilburParse),
FUNCTION(o1_wilburSaid),
FUNCTION(o1_wilburTalk),
FUNCTION(o1_wilburFinishedTalking)
};
#undef FUNCTION
#define VARIABLE(x) { x, #x }
static KernelVariableEntry kernelVars[] = {
/* 000 */
VARIABLE(kGameLanguage),
VARIABLE(kGameVersion),
VARIABLE(kGameCurrentRoom),
VARIABLE(kGameNewRoom),
/* 004 */
VARIABLE(kGamePreviousRoom),
VARIABLE(kGameNewSection),
VARIABLE(kKernelTrigger),
VARIABLE(kKernelTriggerMode),
/* 008 */
VARIABLE(kKernelFirstFade),
VARIABLE(kKernelSuppressFadeUp),
VARIABLE(kKernelContinueHandlingTrigger),
VARIABLE(kKernelUseDebugMonitor),
/* 012 */
VARIABLE(kPlayerPosX),
VARIABLE(kPlayerPosY),
VARIABLE(kPlayerFacing),
VARIABLE(kPlayerScale),
/* 016 */
VARIABLE(kPlayerDepth),
VARIABLE(kPlayerWalkX),
VARIABLE(kPlayerWalkY),
VARIABLE(kPlayerReadyToWalk),
/* 020 */
VARIABLE(kPlayerNeedToWalk),
VARIABLE(kPlayerCommandReady),
VARIABLE(kPlayerWalkerInThisScene),
VARIABLE(kPlayerVerb),
/* 024 */
VARIABLE(kWalkerInitialized),
VARIABLE(kCallDaemonEveryLoop),
VARIABLE(kConvCurrentTalker),
VARIABLE(kConvCurrentNode),
/* 028 */
VARIABLE(kConvCurrentEntry),
VARIABLE(kConvSoundToPlay),
VARIABLE(kInterfaceVisible)
};
#undef VARIABLE

283
engines/m4/sound.cpp Normal file
View file

@ -0,0 +1,283 @@
/* 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.
*
* $URL$
* $Id$
*
*/
#include "m4/m4.h"
#include "m4/sound.h"
#include "m4/compression.h"
#include "sound/audiostream.h"
#include "sound/mixer.h"
#include "common/stream.h"
namespace M4 {
Sound::Sound(M4Engine *vm, Audio::Mixer *mixer, int volume) :
_vm(vm), _mixer(mixer) {
for (int i = 0; i < SOUND_HANDLES; i++)
_handles[i].type = kFreeHandle;
_dsrFileLoaded = false;
setVolume(volume);
}
Sound::~Sound() {
unloadDSRFile();
}
SndHandle *Sound::getHandle() {
for (int i = 0; i < SOUND_HANDLES; i++) {
if (_handles[i].type == kFreeHandle)
return &_handles[i];
if (!_mixer->isSoundHandleActive(_handles[i].handle)) {
_handles[i].type = kFreeHandle;
return &_handles[i];
}
}
error("Sound::getHandle(): Too many sound handles");
return NULL;
}
bool Sound::isHandleActive(SndHandle *handle) {
return (_mixer->isSoundHandleActive(handle->handle));
}
void Sound::playSound(const char *soundName, int volume, bool loop, int channel) {
byte flags;
Common::SeekableReadStream *soundStream = _vm->res()->get(soundName);
SndHandle *handle;
if (channel < 0) {
handle = getHandle();
} else {
if (_handles[channel].type == kFreeHandle) {
handle = &_handles[channel];
} else {
warning("Attempted to play a sound on a channel that isn't free");
return;
}
}
int bufferSize = soundStream->size();
byte *buffer = new byte[bufferSize];
soundStream->read(buffer, bufferSize);
_vm->res()->toss(soundName);
handle->type = kEffectHandle;
flags = Audio::Mixer::FLAG_AUTOFREE;
flags |= Audio::Mixer::FLAG_UNSIGNED;
if (loop)
flags |= Audio::Mixer::FLAG_LOOP;
_vm->res()->toss(soundName);
// Sound format is 8bit mono, unsigned, 11025kHz
_mixer->playRaw(Audio::Mixer::kSFXSoundType, &handle->handle, buffer, bufferSize, 11025, flags, -1, volume);
}
void Sound::pauseSound() {
for (int i = 0; i < SOUND_HANDLES; i++) {
if (_handles[i].type == kEffectHandle)
_mixer->pauseHandle(_handles[i].handle, true);
}
}
void Sound::resumeSound() {
for (int i = 0; i < SOUND_HANDLES; i++) {
if (_handles[i].type == kEffectHandle)
_mixer->pauseHandle(_handles[i].handle, false);
}
}
void Sound::stopSound(int channel) {
if (channel >= 0) {
if (_handles[channel].type == kEffectHandle) {
_mixer->stopHandle(_handles[channel].handle);
_handles[channel].type = kFreeHandle;
return;
} else {
warning("Attempted to stop a sound on a channel that is already free");
return;
}
}
for (int i = 0; i < SOUND_HANDLES; i++) {
if (_handles[i].type == kEffectHandle) {
_mixer->stopHandle(_handles[i].handle);
_handles[i].type = kFreeHandle;
}
}
}
void Sound::playVoice(const char *soundName, int volume) {
byte flags;
Common::SeekableReadStream *soundStream = _vm->res()->get(soundName);
SndHandle *handle = getHandle();
byte *buffer;
buffer = new byte[soundStream->size()];
soundStream->read(buffer, soundStream->size());
handle->type = kEffectHandle;
flags = Audio::Mixer::FLAG_AUTOFREE;
flags |= Audio::Mixer::FLAG_UNSIGNED;
_vm->res()->toss(soundName);
// Voice format is 8bit mono, unsigned, 11025kHz
_mixer->playRaw(Audio::Mixer::kSFXSoundType, &handle->handle, buffer, soundStream->size(), 11025, flags, -1, volume);
}
void Sound::pauseVoice() {
for (int i = 0; i < SOUND_HANDLES; i++)
if (_handles[i].type == kVoiceHandle)
_mixer->pauseHandle(_handles[i].handle, true);
}
void Sound::resumeVoice() {
for (int i = 0; i < SOUND_HANDLES; i++)
if (_handles[i].type == kVoiceHandle)
_mixer->pauseHandle(_handles[i].handle, false);
}
void Sound::stopVoice() {
for (int i = 0; i < SOUND_HANDLES; i++)
if (_handles[i].type == kVoiceHandle) {
_mixer->stopHandle(_handles[i].handle);
_handles[i].type = kFreeHandle;
}
}
void Sound::stopAll() {
stopVoice();
stopSound();
}
void Sound::setVolume(int volume) {
_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, volume);
_mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, volume);
}
void Sound::loadDSRFile(const char *fileName) {
if (_dsrFileLoaded)
unloadDSRFile();
Common::SeekableReadStream *fileStream = _vm->res()->get(fileName);
sprintf(_dsrFile.fileName, "%s", fileName);
// Read header
_dsrFile.entryCount = fileStream->readUint16LE();
//printf("DSR has %i entries\n", _dsrFile.entryCount);
for (int i = 0; i < _dsrFile.entryCount; i++) {
DSREntry* newEntry = new DSREntry();
newEntry->frequency = fileStream->readUint16LE();
newEntry->channels = fileStream->readUint32LE();
newEntry->compSize = fileStream->readUint32LE();
newEntry->uncompSize = fileStream->readUint32LE();
newEntry->offset = fileStream->readUint32LE();
_dsrFile.dsrEntries.push_back(newEntry);
/*
printf("%i: ", i);
printf("frequency: %i ", newEntry->frequency);
printf("channels: %i ", newEntry->channels);
printf("comp: %i ", newEntry->compSize);
printf("uncomp: %i ", newEntry->uncompSize);
printf("offset: %i ", newEntry->offset);
printf("\n");
*/
}
_vm->res()->toss(fileName);
_dsrFileLoaded = true;
}
void Sound::unloadDSRFile() {
if (!_dsrFileLoaded)
return;
for (int i = 0; i < _dsrFile.entryCount; i++) {
_dsrFile.dsrEntries.remove_at(0);
}
_dsrFile.entryCount = 0;
strcpy(_dsrFile.fileName, "");
_dsrFileLoaded = false;
}
void Sound::playDSRSound(int soundIndex, int volume, bool loop) {
if (!_dsrFileLoaded) {
warning("DSR file not loaded, not playing sound");
return;
}
if (soundIndex < 0 || soundIndex > _dsrFile.entryCount - 1) {
warning("Invalid sound index: %i, not playing sound", soundIndex);
return;
}
byte flags;
SndHandle *handle = getHandle();
handle->type = kEffectHandle;
flags = Audio::Mixer::FLAG_AUTOFREE;
flags |= Audio::Mixer::FLAG_UNSIGNED;
if (loop)
flags |= Audio::Mixer::FLAG_LOOP;
// Get sound data
FabDecompressor fab;
byte *compData = new byte[_dsrFile.dsrEntries[soundIndex]->compSize];
byte *buffer = new byte[_dsrFile.dsrEntries[soundIndex]->uncompSize];
Common::SeekableReadStream *fileStream = _vm->res()->get(_dsrFile.fileName);
fileStream->seek(_dsrFile.dsrEntries[soundIndex]->offset, SEEK_SET);
fileStream->read(compData, _dsrFile.dsrEntries[soundIndex]->compSize);
_vm->res()->toss(_dsrFile.fileName);
fab.decompress(compData, _dsrFile.dsrEntries[soundIndex]->compSize,
buffer, _dsrFile.dsrEntries[soundIndex]->uncompSize);
// Play sound
_mixer->playRaw(Audio::Mixer::kSFXSoundType, &handle->handle, buffer,
_dsrFile.dsrEntries[soundIndex]->uncompSize,
_dsrFile.dsrEntries[soundIndex]->frequency, flags, -1, volume);
/*
// Dump the sound file
FILE *destFile = fopen("sound.raw", "wb");
fwrite(_dsrFile.dsrEntries[soundIndex]->data, _dsrFile.dsrEntries[soundIndex]->uncompSize, 1, destFile);
fclose(destFile);
*/
}
} // End of namespace M4

112
engines/m4/sound.h Normal file
View file

@ -0,0 +1,112 @@
/* 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.
*
* $URL$
* $Id$
*
*/
// Sound class
#ifndef M4_SOUND_H
#define M4_SOUND_H
#include "common/file.h"
#include "common/array.h"
#include "sound/mixer.h"
#include "sound/mp3.h"
#include "sound/vorbis.h"
#include "sound/flac.h"
namespace M4 {
#define SOUND_HANDLES 10
enum SOUND_FLAGS {
SOUND_LOOP = 1
};
enum sndHandleType {
kFreeHandle,
kEffectHandle,
kVoiceHandle
};
struct SndHandle {
Audio::SoundHandle handle;
sndHandleType type;
};
struct DSREntry {
int16 frequency;
int channels;
int32 compSize;
int32 uncompSize;
int32 offset;
};
struct DSRFile {
char fileName[20];
int entryCount;
Common::Array<DSREntry *> dsrEntries;
};
class M4Engine;
class Sound {
public:
Sound(M4Engine *vm, Audio::Mixer *mixer, int volume);
~Sound();
void playSound(const char *soundName, int volume, bool loop, int channel = -1);
void pauseSound();
void resumeSound();
void stopSound(int channel = -1);
void playVoice(const char *soundName, int volume);
void pauseVoice();
void resumeVoice();
void stopVoice();
void stopAll();
void setVolume(int volume);
bool isHandleActive(SndHandle *handle);
SndHandle *getHandle();
void loadDSRFile(const char *fileName);
void unloadDSRFile();
void playDSRSound(int soundIndex, int volume, bool loop);
private:
M4Engine *_vm;
Audio::Mixer *_mixer;
SndHandle _handles[SOUND_HANDLES];
DSRFile _dsrFile;
bool _dsrFileLoaded;
};
} // End of namespace M4
#endif

174
engines/m4/sprite.cpp Normal file
View file

@ -0,0 +1,174 @@
/* 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.
*
* $URL$
* $Id$
*
*/
#include "common/rect.h"
#include "m4/globals.h"
#include "m4/graphics.h"
#include "m4/m4.h"
#include "m4/resource.h"
#include "m4/sprite.h"
namespace M4 {
enum {
kEndOfLine = 0,
kEndOfSprite = 1,
kMarker = 2
};
M4Sprite::M4Sprite(Common::SeekableReadStream* source, int xOfs, int yOfs, int widthVal, int heightVal, bool decodeRle, uint8 encodingVal)
: M4Surface(widthVal, heightVal), encoding(encodingVal) {
if (_vm->isM4()) {
if (decodeRle) {
loadRle(source);
} else {
// Raw sprite data, load directly
byte *dst = getData();
source->read(dst, widthVal * heightVal);
}
} else {
loadMadsSprite(source);
}
xOffset = xOfs;
yOffset = yOfs;
}
void M4Sprite::loadRle(Common::SeekableReadStream* rleData) {
byte *dst = getData();
while (1) {
byte len = rleData->readByte();
if (len == 0) {
len = rleData->readByte();
if (len <= kMarker) {
if (len == kEndOfSprite)
break;
} else {
while (len--) {
*dst++ = rleData->readByte();
}
}
} else {
byte value = rleData->readByte();
while (len--)
*dst++ = value;
}
}
}
void M4Sprite::loadDeltaRle(Common::SeekableReadStream* rleData, int destX, int destY) {
int lineNum = 0;
byte *dst = getBasePtr(destX, destY);
while (1) {
byte len = rleData->readByte();
if (len == 0) {
len = rleData->readByte();
if (len <= kMarker) {
if (len == kEndOfLine) {
dst = getBasePtr(destX, destY + lineNum);
lineNum++;
} else if (len == kEndOfSprite)
break;
} else {
while (len--) {
byte pixel = rleData->readByte();
if (pixel == 0)
dst++;
else
*dst++ = pixel;
/* NOTE: The change below behaved differently than the old code,
so I put the old code back in again above.
If the pixel value is 0, nothing should be written to the
output buffer, since 0 means transparent. */
//*dst++ = (pixel == 0xFD) ? 0 : pixel;
}
}
} else {
byte value = rleData->readByte();
if (value == 0)
dst += len;
else
while (len--)
*dst++ = value;
}
}
}
// TODO: The sprite outlines (pixel value 0xFD) are not shown
void M4Sprite::loadMadsSprite(Common::SeekableReadStream* source) {
byte *outp, *lineStart;
bool newLine = false;
outp = getData();
lineStart = getData();
while (1) {
byte cmd1, cmd2, count, pixel;
if (newLine) {
outp = lineStart + w;
lineStart = outp;
newLine = false;
}
cmd1 = source->readByte();
if (cmd1 == 0xFC)
break;
else if (cmd1 == 0xFF)
newLine = true;
else if (cmd1 == 0xFD) {
while (!newLine) {
count = source->readByte();
if (count == 0xFF) {
newLine = true;
} else {
pixel = source->readByte();
while (count--)
*outp++ = (pixel == 0xFD) ? 0 : pixel;
}
}
} else {
while (!newLine) {
cmd2 = source->readByte();
if (cmd2 == 0xFF) {
newLine = true;
} else if (cmd2 == 0xFE) {
count = source->readByte();
pixel = source->readByte();
while (count--)
*outp++ = (pixel == 0xFD) ? 0 : pixel;
} else {
*outp++ = (cmd2 == 0xFD) ? 0 : cmd2;
}
}
}
}
}
} // End of namespace M4

122
engines/m4/sprite.h Normal file
View file

@ -0,0 +1,122 @@
/* 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.
*
* $URL$
* $Id$
*
*/
#ifndef M4_SPRITE_H
#define M4_SPRITE_H
#include "common/util.h"
#include "common/endian.h"
#include "common/stream.h"
#include "m4/graphics.h"
/*
TODO:
- change DrawRequestX and RendCell
*/
namespace M4 {
typedef struct
{
int32 x; // x position relative to GrBuff(0, 0)
int32 y; // y position relative to GrBuff(0, 0)
int32 scale_x; // x scale factor (can be negative for reverse draw)
int32 scale_y; // y scale factor (can't be negative)
uint8* depth_map; // depth code array for destination (doesn't care if srcDepth is 0)
BGR8 *Pal; // palette for shadow draw (doesn't care if SHADOW bit is not set in Src.encoding)
uint8* ICT; // Inverse Color Table (doesn't care if SHADOW bit is not set in Src.encoding)
uint8 depth; // depth code for source (0 if no depth processing)
} DrawRequestX;
typedef struct
{
uint32 Pack;
uint32 Stream;
long hot_x;
long hot_y;
uint32 Width;
uint32 Height;
uint32 Comp;
uint32 Reserved[8];
uint8* data;
} RendCell;
#define SS_HEADER_NUM_FIELDS 14
struct SpriteSeriesHeader {
uint32 header;
uint32 size;
uint32 packing;
uint32 frameRate;
uint32 pixSpeed;
uint32 maxWidth;
uint32 maxHeight;
uint32 reserved3;
uint32 reserved4;
uint32 reserved5;
uint32 reserved6;
uint32 reserved7;
uint32 reserved8;
uint32 count;
};
#define SF_HEADER_NUM_FIELDS 15
struct SpriteFrameHeader {
uint32 pack;
uint32 stream;
uint32 x;
uint32 y;
uint32 width;
uint32 height;
uint32 comp;
uint32 reserved1;
uint32 reserved2;
uint32 reserved3;
uint32 reserved4;
uint32 reserved5;
uint32 reserved6;
uint32 reserved7;
uint32 reserved8;
};
class M4Sprite: public M4Surface {
public:
int x, y;
int xOffset, yOffset;
uint8 encoding;
M4Sprite(M4Engine *vm): M4Surface() {}
M4Sprite(M4Engine *vm, int widthVal, int heightVal): M4Surface(widthVal, heightVal), xOffset(0), yOffset(0) {}
// Loads a sprite from the given stream, and optionally decompresses the RLE-encoded data
M4Sprite(Common::SeekableReadStream* source, int xOfs, int yOfs, int widthVal, int heightVal, bool decodeRle = true, uint8 encodingVal = 0);
// Loads an RLE compressed sprite; the surface must have been created before
void loadRle(Common::SeekableReadStream* rleData);
void loadDeltaRle(Common::SeekableReadStream* rleData, int destX, int destY);
void loadMadsSprite(Common::SeekableReadStream* source);
protected:
};
} // End of namespace M4
#endif

436
engines/m4/viewmgr.cpp Normal file
View file

@ -0,0 +1,436 @@
/* 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.
*
* $URL$
* $Id$
*
*/
// TODO: Views have a _coords rect, so I'm not sure if x/y is needed in the onRefresh
#include "m4/m4.h"
#include "m4/viewmgr.h"
#include "m4/mads_anim.h"
namespace M4 {
void returnToMainMenuFn(M4Engine *vm) {
vm->_palette->resetColorCounts();
vm->_palette->setMadsSystemPalette();
vm->loadMenu(MAIN_MENU);
}
RectList::RectList() {
}
RectList::~RectList() {
}
void RectList::addRect(int x1, int y1, int x2, int y2) {
addRect(Common::Rect(x1, y1, x2, y2));
}
void RectList::addRect(const Common::Rect &rect) {
/* TODO:
Implement the following:
- Don't add the Rect if it's contained in any Rect in the list
- Split up the Rect if it intersects any Rect in the list
and add the resulting partial Rects instead
*/
push_back(rect);
}
//--------------------------------------------------------------------------
HotkeyList::HotkeyList(View *owner) {
_view = owner;
}
HotkeyList::~HotkeyList() {
for (uint32 i = 0; i < _hotkeys.size(); i++)
delete _hotkeys[i];
}
void HotkeyList::add(uint32 key, Hotkey::Callback callback) {
_hotkeys.push_back(new Hotkey(key, callback));
}
void HotkeyList::remove(uint32 key) {
for (uint32 i = 0; i < _hotkeys.size(); i++) {
if (_hotkeys[i]->key == key) {
delete _hotkeys[i];
_hotkeys.remove_at(i);
break;
}
}
}
bool HotkeyList::call(uint32 key) {
for (uint32 i = 0; i < _hotkeys.size(); i++) {
if (_hotkeys[i]->key == key) {
if (_hotkeys[i]->callback)
(_hotkeys[i]->callback)(_vm, _view, key);
return true;
}
}
return false;
}
//--------------------------------------------------------------------------
// View constructor
View::View(M4Engine *vm, const Common::Rect &viewBounds, bool transparent):
_hotkeys(HotkeyList(this)), M4Surface(viewBounds.width(), viewBounds.height()), _vm(vm) {
SCREEN_FLAGS_DEFAULT;
_coords = viewBounds;
_transparent = transparent;
}
View::View(M4Engine *vm, int x, int y, bool transparent): _hotkeys(HotkeyList(this)), M4Surface(), _vm(vm) {
SCREEN_FLAGS_DEFAULT;
_coords.left = x;
_coords.top = y;
_coords.right = _vm->_screen->width();
_coords.bottom = _vm->_screen->height();
_transparent = transparent;
}
void View::getCoordinates(Common::Rect &rect) {
rect = _coords;
}
void View::extract(int *status) {
}
void View::show() {
_screenFlags.visible = true;
_vm->_viewManager->moveToFront(this);
_vm->_viewManager->restore(_coords);
}
void View::hide() {
_screenFlags.visible = false;
_vm->_viewManager->restore(_coords);
}
void View::moveToBack() {
_vm->_viewManager->moveToBack(this);
}
void View::moveAbsolute(int x, int y) {
// TODO: Handle clipping and offscreen
Common::Rect oldCoords = _coords;
_coords.moveTo(x, y);
_vm->_viewManager->restore(oldCoords);
_vm->_viewManager->restore(_coords);
}
void View::moveRelative(int x, int y) {
// TODO: Handle clipping and offscreen
Common::Rect oldCoords = _coords;
_coords.translate(x, y);
_vm->_viewManager->restore(oldCoords);
_vm->_viewManager->restore(_coords);
}
void View::resize(int newWidth, int newHeight) {
Common::Rect oldCoords = _coords;
if (newWidth >= 0)
_coords.setWidth(newWidth);
if (newHeight >= 0)
_coords.setHeight(newHeight);
_vm->_viewManager->restore(oldCoords);
_vm->_viewManager->restore(_coords);
}
void View::restore(int x1, int y1, int x2, int y2) {
_vm->_viewManager->restore(_coords.left + x1, _coords.top + y1, _coords.left + x2, _coords.top + y2);
}
void View::onRefresh(RectList *rects, M4Surface *destSurface) {
assert(destSurface);
if (rects == NULL)
// No rect list specified, so copy entire surface
copyTo(destSurface, _coords.left, _coords.top, _transparent ? 0 : -1);
else {
// Loop through the set of specified rectangles
RectList::iterator i;
for (i = rects->begin(); i != rects->end(); ++i) {
Common::Rect &destRect = *i;
Common::Rect srcBounds(destRect.left - _coords.left, destRect.top - _coords.top,
destRect.right - _coords.left, destRect.bottom - _coords.top);
copyTo(destSurface, srcBounds, destRect.left, destRect.top, _transparent ? 0 : -1);
}
}
}
//--------------------------------------------------------------------------
ViewManager::ViewManager(M4Engine *vm): _systemHotkeys(HotkeyList(NULL)), _vm(vm) {
_captureScreen = NULL;
_captureEvents = false;
}
ViewManager::~ViewManager() {
// Delete any remaining active views
ListIterator i;
for (i = _views.begin(); i != _views.end(); ++i)
delete (*i);
}
void ViewManager::addView(View *view) {
_views.push_back(view);
moveToFront(view);
}
// Warning: After calling this method, the passed view object will no longer be valid
void ViewManager::deleteView(View *view) {
_views.remove(view);
delete view;
}
void ViewManager::handleEvents(const Common::Event &event) {
}
void ViewManager::handleKeyboardEvents(uint32 keycode) {
Common::Point mousePos = _vm->_mouse->currentPos();
View *view;
bool blockedFlag;
bool foundFlag;
bool handledFlag;
// Scan view list for one which accepts or blocks keyboard events. If one is found,
// then the event is passed to it
view = NULL;
handledFlag = false;
foundFlag = false;
blockedFlag = false;
// Loop from the front to back view
ListIterator i;
for (i = _views.reverse_begin(); (i != _views.end()) && !foundFlag; --i) {
view = *i;
if (!view->isVisible()) continue;
if (view->screenFlags().blocks & SCREVENT_KEY)
blockedFlag = true;
if (view->screenFlags().get & SCREVENT_KEY) {
foundFlag = true;
handledFlag = (view->onEvent)(KEVENT_KEY, keycode, mousePos.x, mousePos.y, _captureEvents);
}
}
// Scan view list for one with a hotkey list, aborting if a view is found that either
// blocks keyboard events, or has a hotkey list that includes the keycode
blockedFlag = false;
for (i = _views.reverse_begin(); (i != _views.end()) && !foundFlag && !blockedFlag; --i) {
view = *i;
if (!view->isVisible()) continue;
if (view->screenFlags().blocks & SCREVENT_KEY)
blockedFlag = true;
if (view->screenFlags().get & SCREVENT_KEY) {
if (view->hotkeys().call(keycode)) {
handledFlag = true;
_captureEvents = false;
//_vm->_dialogs->keyMouseCollision(); // TODO
}
}
}
// Final check: if no view handled or blocked the key, check against the system hotkey list
if (!handledFlag && !blockedFlag) {
handledFlag = _systemHotkeys.call(keycode);
if (handledFlag) {
_captureEvents = false;
//_vm->_dialogs->keyMouseCollision(); // TODO
}
}
}
void ViewManager::handleMouseEvents(M4EventType event) {
Common::Point mousePos = _vm->_mouse->currentPos();
ListIterator i;
View *view;
bool blockedFlag;
bool foundFlag;
// If a window sets the _captureEvents flag to true, it will receive all events until
// it sets it to false, even if it's not the top window
if (_captureEvents) {
if (_captureScreen->screenFlags().get & SCREVENT_MOUSE)
(_captureScreen->onEvent)(event, 0, mousePos.x, mousePos.y, _captureEvents);
} else {
blockedFlag = false;
foundFlag = false;
view = NULL;
// Loop from the front to back view
for (i = _views.reverse_begin(); (i != _views.end()) && !foundFlag && !blockedFlag; --i) {
view = *i;
if (!view->isVisible()) continue;
if (view->screenFlags().blocks & SCREVENT_MOUSE)
blockedFlag = true;
if ((view->screenFlags().get & SCREVENT_MOUSE) && view->isInside(mousePos.x, mousePos.y))
foundFlag = true;
}
if (foundFlag)
view->onEvent(event, 0, mousePos.x, mousePos.y, _captureEvents);
else
_captureEvents = false;
if (_captureEvents)
_captureScreen = view;
}
}
void ViewManager::restore(int x1, int y1, int x2, int y2) {
RectList *rl = new RectList();
Common::Rect redrawBounds(x1, y1, x2, y2);
rl->addRect(x1, y1, x2, y2);
for (ListIterator i = _views.begin(); i != _views.end(); ++i) {
View *v = *i;
if (v->isVisible() && v->bounds().intersects(redrawBounds))
v->onRefresh(rl, _vm->_screen);
}
_vm->_screen->update();
}
void ViewManager::restore(const Common::Rect &rect) {
restore(rect.left, rect.top, rect.right, rect.bottom);
}
void ViewManager::moveToFront(View *view) {
if (_views.size() < 2)
return;
_views.remove(view);
ListIterator i = _views.begin();
while ((i != _views.end()) && ((*i)->layer() <= view->layer()))
++i;
_views.insert(i, view);
}
void ViewManager::moveToBack(View *view) {
if (_views.size() < 2)
return;
_views.remove(view);
ListIterator i = _views.begin();
while ((i != _views.end()) && ((*i)->layer() < view->layer()))
++i;
_views.insert(i, view);
}
View *ViewManager::getView(int screenType) {
ListIterator i = _views.begin();
while (i != _views.end()) {
if ((*i)->screenType() == screenType)
return *i;
++i;
}
return NULL;
}
void ViewManager::updateState() {
Common::List<View *> viewList = _views;
for (ListIterator i = viewList.begin(); i != viewList.end(); ++i) {
if (_vm->_events->quitFlag)
return;
View *v = *i;
v->updateState();
}
}
void ViewManager::refreshAll() {
_vm->_screen->empty();
for (ListIterator i = _views.begin(); i != _views.end(); ++i) {
View *v = *i;
if (v->isVisible())
v->onRefresh(NULL, _vm->_screen);
}
_vm->_screen->update();
}
void ViewManager::showTextView(const char *textViewName, bool returnToMainMenu) {
// Deactivate the scene if it's currently active
View *view = _vm->_viewManager->getView(VIEWID_SCENE);
if (view != NULL)
_vm->_viewManager->deleteView(view);
// Deactivate the main menu if it's currently active
view = _vm->_viewManager->getView(VIEWID_MAINMENU);
if (view != NULL)
_vm->_viewManager->deleteView(view);
// Activate the textview view
_vm->_font->setFont(FONT_CONVERSATION_MADS);
TextviewView *textView = new TextviewView(_vm);
_vm->_viewManager->addView(textView);
if (returnToMainMenu)
textView->setScript(textViewName, returnToMainMenuFn);
else
textView->setScript(textViewName, NULL);
}
void ViewManager::showAnimView(const char *animViewName, bool returnToMainMenu) {
// Deactivate the scene if it's currently active
View *view = _vm->_viewManager->getView(VIEWID_SCENE);
if (view != NULL)
_vm->_viewManager->deleteView(view);
// Deactivate the main menu if it's currently active
view = _vm->_viewManager->getView(VIEWID_MAINMENU);
if (view != NULL)
_vm->_viewManager->deleteView(view);
// Activate the animview view
AnimviewView *animView = new AnimviewView(_vm);
_vm->_viewManager->addView(animView);
if (returnToMainMenu)
animView->setScript(animViewName, returnToMainMenuFn);
else
animView->setScript(animViewName, NULL);
}
} // End of namespace M4

191
engines/m4/viewmgr.h Normal file
View file

@ -0,0 +1,191 @@
/* 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.
*
* $URL$
* $Id$
*
*/
#ifndef M4_VIEWMGR_H
#define M4_VIEWMGR_H
#include "common/array.h"
#include "common/list.h"
#include "common/events.h"
#include "common/rect.h"
#include "m4/globals.h"
#include "m4/events.h"
#include "m4/graphics.h"
namespace M4 {
class View;
class ViewManager;
enum {SCREEN_DIALOG, SCREEN_BUFFER, SCREEN_TEXT, SCREEN_TRANSPARENT};
enum ScreenEventType {SCREVENT_NONE = 0, SCREVENT_KEY = 1, SCREVENT_MOUSE = 2, SCREVENT_ALL = 3};
enum ScreenLayers {
LAYER_BACKGROUND = 0, LAYER_DRIFTER = 1, LAYER_INTERFACE = 1, LAYER_FLOATER = 2,
LAYER_SURFACE = 3, LAYER_MENU = 9, LAYER_MOUSE = 15
};
enum ViewIds {
VIEWID_MAINMENU = 1,
VIEWID_SCENE = 2,
VIEWID_TEXTVIEW = 3,
VIEWID_ANIMVIEW = 4,
VIEWID_MENU = 69,
VIEWID_CONVERSATION = 48,
VIEWID_INTERFACE = 49
};
struct ScreenFlags {
bool visible:1;
bool transparent:1;
bool immovable:1;
enum ScreenEventType blocks:2;
enum ScreenEventType get:2;
uint layer:4;
};
#define SCREEN_FLAGS_DEFAULT _screenFlags.layer = LAYER_DRIFTER; \
_screenFlags.get = SCREVENT_ALL; _screenFlags.blocks = SCREVENT_NONE; \
_screenFlags.visible = true;
#define SCREEN_FLAGS_ALERT _screenFlags.layer = LAYER_FLOATER \
_screenFlags.get = SCREVENT_ALL; _screenFlags.blocks = SCREVENT_ALL; \
_screenFlags.visible = true;
class RectList: public Common::Array<Common::Rect> {
public:
RectList();
~RectList();
void addRect(int x1, int y1, int x2, int y2);
void addRect(const Common::Rect &rect);
// Common::Rect& operator [](int idx) { return _rects[idx]; }
};
struct Hotkey {
public:
typedef void (*Callback)(M4Engine *vm, View *view, uint32 key);
Hotkey(uint32 keyVal, Hotkey::Callback callbackFn) : key(keyVal), callback(callbackFn) {};
uint32 key;
Hotkey::Callback callback;
};
class HotkeyList {
public:
HotkeyList(View *owner);
~HotkeyList();
void add(uint32 key, Hotkey::Callback callback);
void remove(uint32 key);
bool call(uint32 key);
private:
Common::Array<Hotkey*> _hotkeys;
View *_view;
};
class View: public M4Surface {
public:
View(M4Engine *vm, const Common::Rect &viewBounds, bool transparent = false);
View(M4Engine *vm, int x = 0, int y = 0, bool transparent = false);
virtual ~View() {}
void getCoordinates(Common::Rect &rect);
void extract(int *status);
virtual void show();
virtual void hide();
void moveToFront() {}
void moveToBack();
void moveAbsolute(int x, int y);
void moveRelative(int x, int y);
void resize(int newWidth, int newHeight);
void restore(int x1, int y1, int x2, int y2);
Common::Rect bounds() const { return _coords; }
bool isInside(int x, int y) const { return _coords.contains(x, y); }
ScreenFlags screenFlags() const { return _screenFlags; }
int screenType() const { return _screenType; }
bool isOffscreen() const { return !_screenFlags.visible; }
bool isTransparent() const { return _screenFlags.transparent; }
bool isVisible() const { return _screenFlags.visible; }
uint layer() const { return _screenFlags.layer; }
HotkeyList &hotkeys() { return _hotkeys; }
virtual void onRefresh(RectList *rects, M4Surface *destSurface);
virtual bool onEvent(M4EventType eventType, int param, int x, int y, bool &captureEvents) { return false; }
virtual void updateState() {};
protected:
M4Engine *_vm;
Common::Rect _coords;
HotkeyList _hotkeys;
int _screenType;
ScreenFlags _screenFlags;
bool _transparent;
};
class ViewManager {
private:
M4Engine *_vm;
HotkeyList _systemHotkeys;
Common::List<View *> _views;
View *_captureScreen;
bool _captureEvents;
public:
typedef Common::List<View *>::iterator ListIterator;
ViewManager(M4Engine *vm);
~ViewManager();
void addView(View *view);
void deleteView(View *view);
void handleEvents(const Common::Event &event);
void handleKeyboardEvents(uint32 keycode);
void handleMouseEvents(M4EventType event);
void restore(int x1, int y1, int x2, int y2);
void restore(const Common::Rect &rect);
void moveToFront(View *view);
void moveToBack(View *view);
Common::List<View *> views() const { return _views; }
bool contains(View *key) const {
return find(_views.begin(), _views.end(), key) != _views.end();
}
bool contains(int screenType) { return getView(screenType) != NULL; }
View *getView(int screenType);
int viewCount() { return _views.size(); }
void showTextView(const char *textViewName, bool returnToMainMenu = true);
void showAnimView(const char *animViewName, bool returnToMainMenu = true);
void updateState();
void refreshAll();
HotkeyList &systemHotkeys() { return _systemHotkeys; }
};
}
#endif

398
engines/m4/woodscript.cpp Normal file
View file

@ -0,0 +1,398 @@
/* 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.
*
* $URL$
* $Id$
*
*/
#include "m4/woodscript.h"
namespace M4 {
// FIXME: Put in Engine/WoodScript class
RGB8 _mainPalette[256];
//Woodscript Assembler/Compiler
int32 Bytecode::_dataFormats[] = {0, 5, 8, 12, 16};
Bytecode::Bytecode(WoodScript *ws, byte *code, int32 codeSize, Sequence *seq) {
_ws = ws;
_code = new Common::MemoryReadStream(code, codeSize);
_sequence = seq;
}
Bytecode::~Bytecode() {
delete _code;
}
int Bytecode::loadInstruction(Instruction &instruction) {
//printf("Bytecode::loadInstruction() ip = %08X\n", _code->pos());
int32 format, data;
uint32 code, code2;
code = _code->readUint32LE();
instruction.instr = (code >> 25) & 0xFF;
instruction.argp[0] = NULL;
instruction.argp[1] = NULL;
instruction.argp[2] = NULL;
instruction.argc = 0;
// Maybe make this a for-loop?
format = (code >> 22) & 7;
if (format) {
/* Load argument 1 */
data = code & 0xFFFF;
decodeArgument(format, data, instruction.argp[0], instruction.argv[0]);
instruction.argc++;
/* Load argument 2 */
format = (code >> 19) & 7;
if (format) {
code2 = _code->readUint32LE();
data = (code2 >> 16) & 0xFFFF;
decodeArgument(format, data, instruction.argp[1], instruction.argv[1]);
instruction.argc++;
/* Load argument 3 */
format = (code >> 16) & 7;
if (format) {
data = code2 & 0xFFFF;
decodeArgument(format, data, instruction.argp[2], instruction.argv[2]);
instruction.argc++;
}
}
}
return 0; //FIXME check if instruction size is needed by caller
}
void Bytecode::jumpAbsolute(int32 ofs) {
_code->seek(ofs * 4);
//printf("Bytecode::jumpAbsolute() ofs = %08X\n", _code->pos());
}
void Bytecode::jumpRelative(int32 ofs) {
_code->seek(ofs * 4, SEEK_CUR);
}
void Bytecode::setSequence(Sequence *seq) {
_sequence = seq;
}
void Bytecode::setCode(byte *code, int32 codeSize) {
delete _code;
_code = new Common::MemoryReadStream(code, codeSize);
}
Sequence *Bytecode::sequence() const {
assert(_sequence);
return _sequence;
}
bool Bytecode::decodeArgument(int32 format, int32 data, long *&arg, long &value) {
int32 index;
if (format == 1) {
if (data & 0x8000)
index = _sequence->indexReg();
else
index = data & 0x0FFF;
switch (data & 0x7000) {
case 0x0000:
arg = sequence()->getParentVarPtr(index);
value = *arg;
break;
case 0x1000:
arg = sequence()->getVarPtr(index);
value = *arg;
break;
case 0x2000:
arg = sequence()->getDataPtr(index);
value = *arg;
break;
}
} else if (format == 2) {
if (data & 0x8000)
index = _sequence->indexReg();
else
index = data & 0x0FFF;
arg = _ws->getGlobalPtr(index);
value = *arg;
} else {
if (data & 0x8000) {
value = -(data & 0x7FFF) << (_dataFormats[format - 3]);
} else {
value = (data & 0x7FFF) << (_dataFormats[format - 3]);
}
arg = &value;
}
return true;
}
WoodScript::WoodScript(M4Engine *vm) {
_vm = vm;
_machineId = 0;
_assets = new AssetManager(vm);
_globals = new long[256]; //FIXME Find out how many globals there should be
memset(_globals, 0, sizeof(long));
_backgroundSurface = NULL;
Common::Rect viewBounds = Common::Rect(0, 0, 640, 480);
//_surfaceView = new View(viewBounds);
}
WoodScript::~WoodScript() {
delete _assets;
delete[] _globals;
}
Sequence *WoodScript::createSequence(Machine *machine, int32 sequenceHash) {
Sequence *sequence = new Sequence(this, machine, sequenceHash);
_sequences.push_back(sequence);
_layers.push_back(sequence);
return sequence;
}
void WoodScript::runSequencePrograms() {
// A lot TODO
for (Common::Array<Sequence*>::iterator it = _sequences.begin(); it != _sequences.end(); it++) {
Sequence *sequence = *it;
if (sequence->isActive()) {
sequence->runProgram();
if (sequence->isTerminated() && sequence->hasEndOfSequenceRequestPending()) {
_endOfSequenceRequestList.push_back(sequence);
}
}
}
}
void WoodScript::runEndOfSequenceRequests() {
}
void WoodScript::runTimerSequenceRequests() {
}
Machine *WoodScript::createMachine(int32 machineHash, Sequence *parentSeq,
int32 dataHash, int32 dataRowIndex, int callbackHandler, const char *machineName) {
//printf("WoodScript::createMachine(%d)\n", machineHash); fflush(stdout);
Machine *machine = new Machine(this, machineHash, parentSeq, dataHash, dataRowIndex, callbackHandler, machineName, _machineId);
_machineId++;
_machines.push_back(machine);
// goto first state for initialization
machine->enterState();
return machine;
}
int32 WoodScript::loadSeries(const char* seriesName, int32 hash, RGB8* palette) {
return _assets->addSpriteAsset(seriesName, hash, palette);
}
void WoodScript::unloadSeries(int32 hash) {
_assets->clearAssets(kAssetTypeCELS, hash, hash);
}
void WoodScript::setSeriesFramerate(Machine *machine, int32 frameRate) {
}
Machine *WoodScript::playSeries(const char *seriesName, long layer, uint32 flags, int32 triggerNum,
int32 frameRate, int32 loopCount, int32 s, int32 x, int32 y,
int32 firstFrame, int32 lastFrame) {
//printf("WoodScript::playSeries(%s)\n", seriesName);
RGB8 *palette = NULL;
if (flags & SERIES_LOAD_PALETTE)
palette = &_mainPalette[0];
int32 spriteHash = _assets->addSpriteAsset(seriesName, -1, palette);
_globals[kGlobTemp1] = (long)spriteHash << 24;
_globals[kGlobTemp2] = layer << 16;
_globals[kGlobTemp3] = _vm->_kernel->createTrigger(triggerNum);
_globals[kGlobTemp4] = frameRate << 16;
_globals[kGlobTemp5] = loopCount << 16;
_globals[kGlobTemp6] = (s << 16) / 100;
_globals[kGlobTemp7] = x << 16;
_globals[kGlobTemp8] = y << 16;
_globals[kGlobTemp9] = firstFrame << 16;
_globals[kGlobTemp10] = lastFrame << 16;
_globals[kGlobTemp11] = (flags & SERIES_PINGPONG) ? 0x10000 : 0;
_globals[kGlobTemp12] = (flags & SERIES_BACKWARD) ? 0x10000 : 0;
_globals[kGlobTemp13] = (flags & SERIES_RANDOM) ? 0x10000 : 0;
_globals[kGlobTemp14] = (flags & SERIES_STICK) ? 0x10000 : 0;
_globals[kGlobTemp15] = (flags & SERIES_LOOP_TRIGGER) ? 0x10000 : 0;
_globals[kGlobTemp16] = (flags & SERIES_HORZ_FLIP) ? 0x10000 : 0;
return createMachine(0, NULL, -1, -1, kCallbackTriggerDispatch, seriesName);
}
Machine *WoodScript::showSeries(const char *seriesName, long layer, uint32 flags, int32 triggerNum,
int32 duration, int32 index, int32 s, int32 x, int32 y) {
RGB8 *palette = NULL;
if (flags & SERIES_LOAD_PALETTE)
palette = &_mainPalette[0];
int32 spriteHash = _assets->addSpriteAsset(seriesName, -1, palette);
_globals[kGlobTemp1] = spriteHash << 24;
_globals[kGlobTemp2] = layer << 16;
_globals[kGlobTemp3] = _vm->_kernel->createTrigger(triggerNum);
_globals[kGlobTemp4] = duration << 16;
_globals[kGlobTemp5] = index << 16;
_globals[kGlobTemp6] = (s << 16) / 100;
_globals[kGlobTemp7] = x << 16;
_globals[kGlobTemp8] = y << 16;
_globals[kGlobTemp14] = (flags & SERIES_STICK) ? 0x10000 : 0;
_globals[kGlobTemp16] = (flags & SERIES_HORZ_FLIP) ? 0x10000 : 0;
return createMachine(1, NULL, -1, -1, kCallbackTriggerDispatch, seriesName);
}
Machine *WoodScript::streamSeries(const char *seriesName, int32 frameRate, long layer, int32 triggerNum) {
//printf("WoodScript::streamSeries(%s)\n", seriesName);
_globals[kGlobTemp1] = frameRate << 16;
/* FIXME: Single frames from a stream series will be decompressed on-the-fly, contrary to
"normal" sprite series, to save some memory, and since no random access to single
frames is needed, this is ok.
*/
_globals[kGlobTemp4] = 0; // The actual stream is opened in the Sequence
_globals[kGlobTemp5] = 0;//TODO: kernel_trigger_create(triggerNum); // trigger
_globals[kGlobTemp6] = layer << 16; // layer
return createMachine(6, NULL, -1, -1, kCallbackTriggerDispatch, seriesName);
}
void WoodScript::update() {
// TODO: Don't show hidden sequences etc.
// TODO: For now, prevent any engine action if a menu is being displayed - eventually this should be
// changed to a proper check of the engine paused variable, which the menus should set while active
if (_vm->_viewManager->getView(VIEWID_MENU) != NULL)
return;
//TODO: Include _pauseTime
uint32 clockTime = g_system->getMillis() / 60; // FIXME: g_system
_globals[kGlobTimeDelta] = clockTime - _globals[kGlobTime];
_globals[kGlobTime] += _globals[kGlobTimeDelta];
runSequencePrograms();
if (_backgroundSurface) {
// FIXME: For now, copy the whole surface. Later, copy only the rectangles that need updating.
_backgroundSurface->copyTo(_surfaceView);
} else {
// "This should never happen."
_surfaceView->fillRect(Common::Rect(0, 0, 640, 480), 0);
}
{
// FIXME: This should be done when a new palette is set
byte palette[1024];
g_system->grabPalette(palette, 0, 256);
for (int i = 0; i < 256; i++) {
_mainPalette[i].r = palette[i * 4 + 0];
_mainPalette[i].g = palette[i * 4 + 1];
_mainPalette[i].b = palette[i * 4 + 2];
}
}
for (Common::Array<Sequence*>::iterator it = _layers.begin(); it != _layers.end(); it++) {
Sequence *sequence = *it;
// TODO: Use correct clipRect etc.
Common::Rect clipRect = Common::Rect(0, 0, 640, 480);
Common::Rect updateRect;
sequence->draw(_surfaceView, clipRect, updateRect);
}
// Handle end-of-sequence requests
if (_endOfSequenceRequestList.size() > 0) {
for (Common::Array<Sequence*>::iterator it = _endOfSequenceRequestList.begin(); it != _endOfSequenceRequestList.end(); it++) {
Sequence *sequence = *it;
EndOfSequenceRequestItem endOfSequenceRequestItem = sequence->getEndOfSequenceRequestItem();
sequence->getMachine()->execBlock(endOfSequenceRequestItem.codeOffset, endOfSequenceRequestItem.count);
}
_endOfSequenceRequestList.clear();
}
}
void WoodScript::clear() {
for (Common::Array<Sequence*>::iterator it = _sequences.begin(); it != _sequences.end(); it++)
delete *it;
_sequences.clear();
for (Common::Array<Machine*>::iterator it = _machines.begin(); it != _machines.end(); it++)
delete *it;
_machines.clear();
_layers.clear();
_endOfSequenceRequestList.clear();
}
void WoodScript::setDepthTable(int16 *depthTable) {
_depthTable = depthTable;
}
long *WoodScript::getGlobalPtr(int index) {
return &_globals[index];
}
long WoodScript::getGlobal(int index) {
return _globals[index];
}
void WoodScript::setGlobal(int index, long value) {
_globals[index] = value;
}
void WoodScript::setBackgroundSurface(M4Surface *backgroundSurface) {
_backgroundSurface = backgroundSurface;
}
void WoodScript::setSurfaceView(View *view) {
_surfaceView = view;
}
RGB8 *WoodScript::getMainPalette() const {
return _mainPalette;
}
}

351
engines/m4/woodscript.h Normal file
View file

@ -0,0 +1,351 @@
/* 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.
*
* $URL$
* $Id$
*
*/
#ifndef M4_WOODSCRIPT_H
#define M4_WOODSCRIPT_H
#include "common/scummsys.h"
#include "common/util.h"
#include "common/str.h"
#include "common/array.h"
#include "common/stream.h"
#include "graphics/surface.h"
#include "m4/globals.h"
#include "m4/assets.h"
#include "m4/resource.h"
#include "m4/sprite.h"
#include "m4/m4.h"
#include "m4/graphics.h"
#include "m4/viewmgr.h"
namespace M4 {
class M4Engine;
class WoodScript;
class Machine;
class Sequence;
class AssetManager;
class View;
struct Instruction {
int32 instr;
long *argp[3];
long argv[3];
int argc;
// Helper method; many opcode functions can get either a defined value or a random value
long getValue() {
if (argc == 3)
return _vm->imath_ranged_rand16(argv[1], argv[2]);
else
return argv[1];
}
};
class Bytecode {
public:
Bytecode(WoodScript *ws, byte *code, int32 codeSize, Sequence *seq);
~Bytecode();
int loadInstruction(Instruction &instruction);
void jumpAbsolute(int32 ofs);
void jumpRelative(int32 ofs);
void setSequence(Sequence *sequence);
void setCode(byte *code, int32 codeSize);
Sequence *sequence() const;
uint32 pos() const { return _code->pos() / 4; }
protected:
WoodScript *_ws;
Common::MemoryReadStream *_code;
Sequence *_sequence;
static int32 _dataFormats[];
bool decodeArgument(int32 format, int32 data, long *&arg, long &value);
};
struct EndOfSequenceRequestItem {
int32 codeOffset, count;
EndOfSequenceRequestItem() : codeOffset(-1) {}
bool isValid() const { return codeOffset >= 0; }
};
typedef Common::Array<EndOfSequenceRequestItem> EndOfSequenceRequestList;
class Sequence {
public:
Sequence(WoodScript *ws, Machine *machine, int32 sequenceHash);
~Sequence();
void pause();
void resume();
void issueEndOfSequenceRequest(int32 codeOffset, int32 count);
void cancelEndOfSequenceRequest();
bool runProgram();
bool changeProgram(int32 sequenceHash);
void clearVars();
long *getVarPtr(int index);
long *getParentVarPtr(int index);
long *getDataPtr(int index);
void setActive(bool active) { _active = active; }
bool isActive() const { return _active; }
bool isTerminated() const { return _terminated; }
void draw(M4Surface *surface, const Common::Rect &clipRect, Common::Rect &updateRect);
bool s1_end(Instruction &instruction);
bool s1_clearVars(Instruction &instruction);
bool s1_set(Instruction &instruction);
bool s1_compare(Instruction &instruction);
bool s1_add(Instruction &instruction);
bool s1_sub(Instruction &instruction);
bool s1_mul(Instruction &instruction);
bool s1_div(Instruction &instruction);
bool s1_and(Instruction &instruction);
bool s1_or(Instruction &instruction);
bool s1_not(Instruction &instruction);
bool s1_sin(Instruction &instruction);
bool s1_cos(Instruction &instruction);
bool s1_abs(Instruction &instruction);
bool s1_min(Instruction &instruction);
bool s1_max(Instruction &instruction);
bool s1_mod(Instruction &instruction);
bool s1_floor(Instruction &instruction);
bool s1_round(Instruction &instruction);
bool s1_ceil(Instruction &instruction);
bool s1_point(Instruction &instruction);
bool s1_dist2d(Instruction &instruction);
bool s1_crunch(Instruction &instruction);
bool s1_branch(Instruction &instruction);
bool s1_setFrame(Instruction &instruction);
bool s1_sendMessage(Instruction &instruction);
bool s1_push(Instruction &instruction);
bool s1_pop(Instruction &instruction);
bool s1_jumpSub(Instruction &instruction);
bool s1_return(Instruction &instruction);
bool s1_getFrameCount(Instruction &instruction);
bool s1_getFrameRate(Instruction &instruction);
bool s1_getCelsPixSpeed(Instruction &instruction);
bool s1_setIndex(Instruction &instruction);
bool s1_setLayer(Instruction &instruction);
bool s1_setDepth(Instruction &instruction);
bool s1_setData(Instruction &instruction);
bool s1_openStream(Instruction &instruction);
bool s1_streamNextFrame(Instruction &instruction);
bool s1_closeStream(Instruction &instruction);
int32 indexReg() const { return _indexReg; }
EndOfSequenceRequestItem getEndOfSequenceRequestItem() const { return _endOfSequenceRequest; }
bool hasEndOfSequenceRequestPending() const { return _endOfSequenceRequest.isValid(); }
void resetEndOfSequenceRequest() { _endOfSequenceRequest.codeOffset = -1; }
Machine *getMachine() const { return _machine; }
protected:
WoodScript *_ws;
Bytecode *_code;
long *_vars;
bool _active, _terminated;
Machine *_machine;
Sequence *_parentSequence;
int32 _layer;
int32 _startTime, _switchTime;
long *_dataRow;
int32 _localVarCount;
int32 _cmpFlags;
EndOfSequenceRequestItem _endOfSequenceRequest;
int32 _indexReg;
M4Sprite *_curFrame;
int32 _sequenceHash;
int32 _returnHashes[8]; //FIXME: Use constant instead of 8
uint32 _returnOffsets[8];
int32 _returnStackIndex;
Common::SeekableReadStream *_stream;
SpriteAsset *_streamSpriteAsset;
bool streamOpen();
bool streamNextFrame();
void streamClose();
};
class Machine {
public:
Machine(WoodScript *ws, int32 machineHash, Sequence *parentSeq, int32 dataHash,
int32 dataRowIndex, int callbackHandler, Common::String machineName, int32 id);
~Machine();
void clearMessages();
void clearPersistentMessages();
void restorePersistentMessages();
void sendMessage(uint32 messageHash, long messageValue, Machine *sender);
void resetSwitchTime();
bool changeSequenceProgram(int32 sequenceHash);
bool searchMessages(uint32 messageHash, uint32 messageValue, Machine *sender);
bool searchPersistentMessages(uint32 messageHash, uint32 messageValue, Machine *sender);
void enterState();
int32 execInstruction();
void execBlock(int32 offset, int32 count);
int32 getState() { return _currentState; }
int32 getId() const { return _id; }
bool m1_gotoState(Instruction &instruction);
bool m1_jump(Instruction &instruction);
bool m1_terminate(Instruction &instruction);
bool m1_startSequence(Instruction &instruction);
bool m1_pauseSequence(Instruction &instruction);
bool m1_resumeSequence(Instruction &instruction);
bool m1_storeValue(Instruction &instruction);
bool m1_sendMessage(Instruction &instruction);
bool m1_broadcastMessage(Instruction &instruction);
bool m1_replyMessage(Instruction &instruction);
bool m1_sendSystemMessage(Instruction &instruction);
bool m1_createMachine(Instruction &instruction);
bool m1_createMachineEx(Instruction &instruction);
bool m1_clearVars(Instruction &instruction);
void m1_onEndSequence(Instruction &instruction);
void m1_onMessage(Instruction &instruction);
void m1_switchLt(Instruction &instruction);
void m1_switchLe(Instruction &instruction);
void m1_switchEq(Instruction &instruction);
void m1_switchNe(Instruction &instruction);
void m1_switchGe(Instruction &instruction);
void m1_switchGt(Instruction &instruction);
long *dataRow() const { return _dataRow; }
Sequence *parentSequence() const { return _parentSequence; }
Common::String name() const { return _name; }
protected:
WoodScript *_ws;
Bytecode *_code;
Common::String _name;
Sequence *_sequence, *_parentSequence;
byte *_mach;
int32 _machHash, _machineCodeOffset;
int32 _stateCount, _stateTableOffset;
long *_dataRow;
int32 _id, _recursionLevel, _currentState, _targetCount;
/* TODO:
m->msgReplyXM = NULL;
m->CintrMsg = CintrMsg;
_walkPath
_messages
_persistentMessages
_usedPersistentMessages
*/
};
class WoodScript {
public:
WoodScript(M4Engine *vm);
~WoodScript();
Machine *createMachine(int32 machineHash, Sequence *parentSeq, int32 dataHash, int32 dataRowIndex, int callbackHandler, const char *machineName);
Sequence *createSequence(Machine *machine, int32 sequenceHash);
void runSequencePrograms();
void runEndOfSequenceRequests();
void runTimerSequenceRequests();
/* Series */
// Move to own class, e.g. SeriesPlayer
int32 loadSeries(const char* seriesName, int32 hash, RGB8* palette);
void unloadSeries(int32 hash);
void setSeriesFramerate(Machine *machine, int32 frameRate);
Machine *playSeries(const char *seriesName, long layer, uint32 flags, int32 triggerNum,
int32 frameRate, int32 loopCount, int32 s, int32 x, int32 y,
int32 firstFrame, int32 lastFrame);
Machine *showSeries(const char *seriesName, long layer, uint32 flags, int32 triggerNum,
int32 duration, int32 index, int32 s, int32 x, int32 y);
Machine *streamSeries(const char *seriesName, int32 frameRate, long layer, int32 triggerNum);
void update();
void clear();
/* Misc */
void setDepthTable(int16 *depthTable);
long *getGlobalPtr(int index);
long getGlobal(int index);
void setGlobal(int index, long value);
AssetManager *assets() const { return _assets; }
// Sets the untouched, clean surface which contains the room background
void setBackgroundSurface(M4Surface *backgroundSurface);
// Sets the view which is used for drawing
void setSurfaceView(View *view);
RGB8 *getMainPalette() const;
void setInverseColorTable(byte *inverseColorTable) { _inverseColorTable = inverseColorTable; }
byte *getInverseColorTable() const { return _inverseColorTable; }
protected:
M4Engine *_vm;
AssetManager *_assets;
Common::Array<Sequence*> _sequences, _layers;
Common::Array<Machine*> _machines;
int32 _machineId;
long *_globals;
Common::Array<Sequence*> _endOfSequenceRequestList;
int32 _indexReg;
/* Misc */
int16 *_depthTable;
byte *_inverseColorTable;
M4Surface *_backgroundSurface;
View *_surfaceView;
};
} // End of namespace M4
#endif

422
engines/m4/ws_machine.cpp Normal file
View file

@ -0,0 +1,422 @@
/* 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.
*
* $URL$
* $Id$
*
*/
#include "m4/woodscript.h"
namespace M4 {
bool (Machine::*machineCommandsTable[])(Instruction &instruction) = {
NULL,
NULL,//TODO: nop
&Machine::m1_gotoState,
&Machine::m1_jump,
&Machine::m1_terminate,
&Machine::m1_startSequence,
&Machine::m1_pauseSequence,
&Machine::m1_resumeSequence,
&Machine::m1_storeValue,
&Machine::m1_sendMessage,
&Machine::m1_broadcastMessage,
&Machine::m1_replyMessage,
&Machine::m1_sendSystemMessage,
&Machine::m1_createMachine,
&Machine::m1_createMachineEx,
&Machine::m1_clearVars
};
void (Machine::*machineConditionalsTable[])(Instruction &instruction) = {
NULL,//TODO: after
&Machine::m1_onEndSequence,
&Machine::m1_onMessage,
NULL,//TODO: on_p_msg
&Machine::m1_switchLt,
&Machine::m1_switchLe,
&Machine::m1_switchEq,
&Machine::m1_switchNe,
&Machine::m1_switchGe,
&Machine::m1_switchGt,
};
Machine::Machine(WoodScript *ws, int32 machineHash, Sequence *parentSeq, int32 dataHash,
int32 dataRowIndex, int callbackHandler, Common::String machineName, int32 id) {
_ws = ws;
_machHash = machineHash;
_name = machineName;
_id = id;
// initialize the machine's bytecode
MachineAsset *machineAsset = _ws->assets()->getMachine(_machHash);
byte *code;
uint32 codeSize;
machineAsset->getCode(code, codeSize);
_code = new Bytecode(_ws, code, codeSize, NULL);
// initialize the machine's data
if (dataHash >= 0) {
DataAsset *dataAsset = _ws->assets()->getData(dataHash);
_dataRow = dataAsset->getRow(dataRowIndex);
} else {
_dataRow = NULL;
}
_recursionLevel = 0;
_currentState = 0;
_sequence = NULL;
_parentSequence = parentSeq;
_targetCount = 0;
}
Machine::~Machine() {
delete _code;
}
void Machine::clearMessages() {
}
void Machine::clearPersistentMessages() {
}
void Machine::restorePersistentMessages() {
}
void Machine::sendMessage(uint32 messageHash, long messageValue, Machine *sender) {
}
void Machine::resetSwitchTime() {
}
bool Machine::changeSequenceProgram(int32 sequenceHash) {
return _sequence->changeProgram(sequenceHash);
}
bool Machine::searchMessages(uint32 messageHash, uint32 messageValue, Machine *sender) {
return false;
}
bool Machine::searchPersistentMessages(uint32 messageHash, uint32 messageValue, Machine *sender) {
return false;
}
void Machine::enterState() {
MachineAsset *machineAsset = _ws->assets()->getMachine(_machHash);
_code->jumpAbsolute(machineAsset->getStateOffset(_currentState));
int32 instruction = -1;
_recursionLevel++;
int32 oldId = _id;
int32 oldRecursionLevel = _recursionLevel;
while (instruction && instruction != 4 && _id == oldId && _recursionLevel == oldRecursionLevel) {
instruction = execInstruction();
}
if (instruction != 4 && _id == oldId && _recursionLevel == oldRecursionLevel) {
_recursionLevel--;
}
}
int32 Machine::execInstruction() {
//printf("Machine::execInstruction()\n"); fflush(stdout);
bool done = false;
Instruction instruction;
//Sequence *sequence;
int32 machID = _id;
_code->loadInstruction(instruction);
if (instruction.instr >= 64) {
if (machineConditionalsTable[instruction.instr - 64] != NULL)
(this->*machineConditionalsTable[instruction.instr - 64])(instruction);
/* The next line is to yield on unimplemented opcodes */
else { fflush(stdout); g_system->delayMillis(5000); }
} else if (instruction.instr > 0) {
if (machineCommandsTable[instruction.instr] != NULL)
done = !(this->*machineCommandsTable[instruction.instr])(instruction);
/* The next line is to yield on unimplemented opcodes */
else { fflush(stdout); g_system->delayMillis(5000); }
if (done) {
if (_id == machID) {
//TODO: Cancel all requests
if (_currentState == -1) {
// TODO: Set terminated flag and delete machine in WoodScript update
}
else {
// initialize new state
enterState();
}
}
}
}
return instruction.instr;
}
void Machine::execBlock(int32 offset, int32 count) {
// MachineAsset *machineAsset = _ws->assets()->getMachine(_machHash);
int32 startOffset = offset, endOffset = offset + count;
_recursionLevel++;
int32 oldId = _id;
int32 oldRecursionLevel = _recursionLevel;
_code->jumpAbsolute(offset);
int32 instruction = -1;
//printf("---------------------------------------\n"); fflush(stdout);
while (instruction && instruction != 4 && _id == oldId && _recursionLevel == oldRecursionLevel &&
_code->pos() >= (uint32)startOffset && _code->pos() < (uint32)endOffset) {
instruction = execInstruction();
//g_system->delayMillis(500);
}
//printf("---------------------------------------\n"); fflush(stdout);
if (instruction == 3) {
execInstruction();
}
if (instruction != 4 && _id == oldId && _recursionLevel == oldRecursionLevel) {
_recursionLevel--;
}
}
bool Machine::m1_gotoState(Instruction &instruction) {
//printf("Machine::m1_gotoState() state = %d\n", (int32)instruction.argv[0] >> 16);
_currentState = (int32)instruction.argv[0] >> 16;
_recursionLevel = 0;
return false;
}
bool Machine::m1_jump(Instruction &instruction) {
//printf("Machine::m1_jump() ofs = %08X\n", (int32)instruction.argv[0] >> 16);
_code->jumpRelative((int32)instruction.argv[0] >> 16);
return true;
}
bool Machine::m1_terminate(Instruction &instruction) {
//printf("Machine::m1_terminate()\n"); fflush(stdout);
_currentState = -1;
_recursionLevel = 0;
return false;
}
bool Machine::m1_startSequence(Instruction &instruction) {
//printf("Machine::m1_startSequence() sequence hash = %d\n", (uint32)instruction.argv[0] >> 16); fflush(stdout);
int32 sequenceHash = instruction.argv[0] >> 16;
if (_sequence == NULL) {
//printf("Machine::m1_startSequence() creating new sequence\n");
_sequence = _ws->createSequence(this, sequenceHash);
_code->setSequence(_sequence);
} else {
//printf("Machine::m1_startSequence() using existing sequence\n");
_sequence->changeProgram(sequenceHash);
//_code->setSequence(_sequence);
}
return true;
}
bool Machine::m1_pauseSequence(Instruction &instruction) {
//printf("Machine::m1_pauseSequence()\n"); fflush(stdout);
_sequence->pause();
return true;
}
bool Machine::m1_resumeSequence(Instruction &instruction) {
//printf("Machine::m1_resumeSequence()\n"); fflush(stdout);
_sequence->resume();
return true;
}
bool Machine::m1_storeValue(Instruction &instruction) {
//printf("Machine::m1_storeValue() %p = %d (%08X)\n", (void*)instruction.argp[0], (uint32)instruction.argv[1], (uint32)instruction.argv[1]);
*instruction.argp[0] = instruction.getValue();
return true;
}
bool Machine::m1_sendMessage(Instruction &instruction) {
//printf("Machine::m1_sendMessage() %p = %d (%08X)\n", (void*)instruction.argp[0], (uint32)instruction.argv[1], (uint32)instruction.argv[1]);
#if 0
//TODO
long messageValue;
if (instruction.argc == 3) {
messageValue = instruction.argv[2];
} else {
messageValue = 0;
}
//_ws->sendMessage((uint32)instruction.argv[1], messageValue, (uint32)instruction.argv[0] >> 16);
//void SendWSMessage(uint32 msgHash, long msgValue, machine *recvM, uint32 machHash, machine *sendM, int32 msgCount) {
#endif
return true;
}
bool Machine::m1_broadcastMessage(Instruction &instruction) {
//printf("Machine::m1_broadcastMessage() %p = %d (%08X)\n", (void*)instruction.argp[0], (uint32)instruction.argv[1], (uint32)instruction.argv[1]);
#if 0
//TODO
long messageValue;
if (instruction.argc == 3) {
messageValue = instruction.argv[2];
} else {
messageValue = 0;
}
//_ws->sendMessage((uint32)instruction.argv[1], messageValue, (uint32)instruction.argv[0] >> 16);
#endif
return true;
}
bool Machine::m1_replyMessage(Instruction &instruction) {
//printf("Machine::m1_replyMessage() messageHash = %d; messageValue = %d\n", (uint32)instruction.argv[0], (uint32)instruction.argv[1]);
#if 0
if (myArg2) {
msgValue = *myArg2;
}
else {
msgValue = 0;
}
SendWSMessage(*myArg1, msgValue, m->msgReplyXM, 0, m, 1);
#endif
return true;
}
bool Machine::m1_sendSystemMessage(Instruction &instruction) {
//printf("Machine::m1_sendSystemMessage() messageValue = %d\n", (uint32)instruction.argv[0]);
#if 0
#endif
return true;
}
bool Machine::m1_createMachine(Instruction &instruction) {
//printf("Machine::m1_createMachine()\n");
#if 0
#endif
return true;
}
bool Machine::m1_createMachineEx(Instruction &instruction) {
//printf("Machine::m1_createMachineEx()\n");
#if 0
#endif
return true;
}
bool Machine::m1_clearVars(Instruction &instruction) {
//printf("Machine::m1_clearVars()\n"); fflush(stdout);
_sequence->clearVars();
return true;
}
void Machine::m1_onEndSequence(Instruction &instruction) {
//printf("Machine::m1_onEndSequence() count = %08X\n", (uint32)instruction.argv[0] >> 16); fflush(stdout);
int32 count = instruction.argv[0] >> 16;
_sequence->issueEndOfSequenceRequest(_code->pos(), count);
_code->jumpRelative(count);
}
void Machine::m1_onMessage(Instruction &instruction) {
//printf("Machine::m1_onEndSequence() count = %08X\n", (uint32)instruction.argv[0] >> 16); fflush(stdout);
// TODO: Add message to list
int32 count = instruction.argv[0] >> 16;
_code->jumpRelative(count);
}
void Machine::m1_switchLt(Instruction &instruction) {
//printf("Machine::m1_switchLt() %d < %d -> %08X\n", (uint32)instruction.argv[1], (uint32)instruction.argv[2], (uint32)instruction.argv[0] >> 16); fflush(stdout);
if (instruction.argv[1] >= instruction.argv[2])
_code->jumpRelative(instruction.argv[0] >> 16);
}
void Machine::m1_switchLe(Instruction &instruction) {
//printf("Machine::m1_switchLe() %d <= %d -> %08X\n", (uint32)instruction.argv[1], (uint32)instruction.argv[2], (uint32)instruction.argv[0] >> 16); fflush(stdout);
if (instruction.argv[1] > instruction.argv[2])
_code->jumpRelative(instruction.argv[0] >> 16);
}
void Machine::m1_switchEq(Instruction &instruction) {
//printf("Machine::m1_switchEq() %d == %d -> %08X\n", (uint32)instruction.argv[1], (uint32)instruction.argv[2], (uint32)instruction.argv[0] >> 16); fflush(stdout);
if (instruction.argv[1] != instruction.argv[2])
_code->jumpRelative(instruction.argv[0] >> 16);
}
void Machine::m1_switchNe(Instruction &instruction) {
//printf("Machine::m1_switchNe() %d != %d -> %08X\n", (uint32)instruction.argv[1], (uint32)instruction.argv[2], (uint32)instruction.argv[0] >> 16); fflush(stdout);
if (instruction.argv[1] == instruction.argv[2])
_code->jumpRelative(instruction.argv[0] >> 16);
}
void Machine::m1_switchGe(Instruction &instruction) {
//printf("Machine::m1_switchGe() %d >= %d -> %08X\n", (uint32)instruction.argv[1], (uint32)instruction.argv[2], (uint32)instruction.argv[0] >> 16); fflush(stdout);
if (instruction.argv[1] < instruction.argv[2])
_code->jumpRelative(instruction.argv[0] >> 16);
}
void Machine::m1_switchGt(Instruction &instruction) {
//printf("Machine::m1_switchGt() %d > %d -> %08X\n", (uint32)instruction.argv[1], (uint32)instruction.argv[2], (uint32)instruction.argv[0] >> 16); fflush(stdout);
if (instruction.argv[1] <= instruction.argv[2])
_code->jumpRelative(instruction.argv[0] >> 16);
}
}

764
engines/m4/ws_sequence.cpp Normal file
View file

@ -0,0 +1,764 @@
/* 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.
*
* $URL$
* $Id$
*
*/
#include "m4/woodscript.h"
namespace M4 {
long sinCosTable[320] = {
0, 1608, 3215, 4821, 6423, 8022, 9616, 11204,
12785, 14359, 15923, 17479, 19024, 20557, 22078, 23586,
25079, 26557, 28020, 29465, 30893, 32302, 33692, 35061,
36409, 37736, 39039, 40319, 41575, 42806, 44011, 45189,
46340, 47464, 48558, 49624, 50660, 51665, 52639, 53581,
54491, 55368, 56212, 57022, 57797, 58538, 59243, 59913,
60547, 61144, 61705, 62228, 62714, 63162, 63571, 63943,
64276, 64571, 64826, 65043, 65220, 65358, 65457, 65516,
65536, 65516, 65457, 65358, 65220, 65043, 64826, 64571,
64276, 63943, 63571, 63162, 62714, 62228, 61705, 61144,
60547, 59913, 59243, 58538, 57797, 57022, 56212, 55368,
54491, 53581, 52639, 51665, 50660, 49624, 48558, 47464,
46340, 45189, 44011, 42806, 41575, 40319, 39039, 37736,
36409, 35061, 33692, 32302, 30893, 29465, 28020, 26557,
25079, 23586, 22078, 20557, 19024, 17479, 15923, 14359,
12785, 11204, 9616, 8022, 6423, 4821, 3215, 1608,
0, -1608, -3215, -4821, -6423, -8022, -9616, -11204,
-12785, -14359, -15923, -17479, -19024, -20557, -22078, -23586,
-25079, -26557, -28020, -29465, -30893, -32302, -33692, -35061,
-36409, -37736, -39039, -40319, -41575, -42806, -44011, -45189,
-46340, -47464, -48558, -49624, -50660, -51665, -52639, -53581,
-54491, -55368, -56212, -57022, -57797, -58538, -59243, -59913,
-60547, -61144, -61705, -62228, -62714, -63162, -63571, -63943,
-64276, -64571, -64826, -65043, -65220, -65358, -65457, -65516,
-65536, -65516, -65457, -65358, -65220, -65043, -64826, -64571,
-64276, -63943, -63571, -63162, -62714, -62228, -61705, -61144,
-60547, -59913, -59243, -58538, -57797, -57022, -56212, -55368,
-54491, -53581, -52639, -51665, -50660, -49624, -48558, -47464,
-46340, -45189, -44011, -42806, -41575, -40319, -39039, -37736,
-36409, -35061, -33692, -32302, -30893, -29465, -28020, -26557,
-25079, -23586, -22078, -20557, -19024, -17479, -15923, -14359,
-12785, -11204, -9616, -8022, -6423, -4821, -3215, -1608,
0, 1608, 3215, 4821, 6423, 8022, 9616, 11204,
12785, 14359, 15923, 17479, 19024, 20557, 22078, 23586,
25079, 26557, 28020, 29465, 30893, 32302, 33692, 35061,
36409, 37736, 39039, 40319, 41575, 42806, 44011, 45189,
46340, 47464, 48558, 49624, 50660, 51665, 52639, 53581,
54491, 55368, 56212, 57022, 57797, 58538, 59243, 59913,
60547, 61144, 61705, 62228, 62714, 63162, 63571, 63943,
64276, 64571, 64826, 65043, 65220, 65358, 65457, 65516
};
long *sinTable = &(sinCosTable[0]);
long *cosTable = &(sinCosTable[64]);
// FIXME: Tables
const int sequenceVariableCount = 33;
enum SequenceVariables {
kSeqVarTimer = 0,
kSeqVarTag = 1,
kSeqVarLayer = 2,
kSeqVarWidth = 3,
kSeqVarHeight = 4,
kSeqVarX = 5,
kSeqVarY = 6,
kSeqVarScale = 7,
kSeqVarR = 8,
kSeqVarSpriteHash = 9,
kSeqVarSpriteFrameNumber = 10,
kSeqVarSpriteFrameCount = 11,
kSeqVarSpriteFrameRate = 12,
kSeqVarSpriteFramePixelSpeed = 13,
kSeqVarTargetS = 14,
kSeqVarTargetR = 15,
kSeqVarTargetX = 16,
kSeqVarTargetY = 17,
kSeqVarDeltaS = 18,
kSeqVarDeltaR = 19,
kSeqVarDeltaX = 20,
kSeqVarDeltaY = 21,
kSeqVarVelocity = 22,
kSeqVarTheta = 23,
kSeqVarTemp1 = 24,
kSeqVarTemp2 = 25,
kSeqVarTemp3 = 26,
kSeqVarTemp4 = 27,
kSeqVarTemp5 = 28,
kSeqVarTemp6 = 29,
kSeqVarTemp7 = 30,
kSeqVarTemp8 = 31,
kSeqVarMachineID = 32
};
bool (Sequence::*sequenceCommandsTable[])(Instruction &instruction) = {
&Sequence::s1_end,
&Sequence::s1_clearVars,
&Sequence::s1_set,
&Sequence::s1_compare,
&Sequence::s1_add,
&Sequence::s1_sub,
&Sequence::s1_mul,
&Sequence::s1_div,
&Sequence::s1_and,
&Sequence::s1_or,
&Sequence::s1_not,
&Sequence::s1_sin,
&Sequence::s1_cos,
&Sequence::s1_abs,
&Sequence::s1_min,
&Sequence::s1_max,
&Sequence::s1_mod,
&Sequence::s1_floor,
&Sequence::s1_round,
&Sequence::s1_ceil,
&Sequence::s1_point,
&Sequence::s1_dist2d,
&Sequence::s1_crunch,
&Sequence::s1_branch,
&Sequence::s1_setFrame,
&Sequence::s1_sendMessage,
&Sequence::s1_push,
&Sequence::s1_pop,
&Sequence::s1_jumpSub,
&Sequence::s1_return,
&Sequence::s1_getFrameCount,
&Sequence::s1_getFrameRate,
&Sequence::s1_getCelsPixSpeed,
&Sequence::s1_setIndex,
&Sequence::s1_setLayer,
&Sequence::s1_setDepth,
&Sequence::s1_setData,
&Sequence::s1_openStream,
&Sequence::s1_streamNextFrame,
&Sequence::s1_closeStream
};
Sequence::Sequence(WoodScript *ws, Machine *machine, int32 sequenceHash) {
_ws = ws;
SequenceAsset *sequenceAsset = _ws->assets()->getSequence(sequenceHash);
// initialize the sequence's bytecode
byte *code;
uint32 codeSize;
sequenceAsset->getCode(code, codeSize);
_code = new Bytecode(_ws, code, codeSize, this);
_active = true;
_sequenceHash = sequenceHash;
_machine = machine;
_parentSequence = _machine->parentSequence();
_dataRow = _machine->dataRow();
_startTime = 0;
_switchTime = 0;
//TODO _flags = 0;
_localVarCount = sequenceAsset->localVarCount();
_vars = new long[sequenceVariableCount + _localVarCount];
_returnStackIndex = 0;
_layer = 0;
_terminated = false;
clearVars();
_vars[kSeqVarMachineID] = _machine->getId();
}
Sequence::~Sequence() {
delete _code;
}
void Sequence::pause() {
_active = false;
}
void Sequence::resume() {
_active = true;
}
void Sequence::issueEndOfSequenceRequest(int32 codeOffset, int32 count) {
//printf("Sequence::issueEndOfSequenceRequest(%04X, %04X)\n", codeOffset, count); fflush(stdout);
//g_system->delayMillis(5000);
_endOfSequenceRequest.codeOffset = codeOffset;
_endOfSequenceRequest.count = count;
}
void Sequence::cancelEndOfSequenceRequest() {
_endOfSequenceRequest.codeOffset = -1;
}
bool Sequence::runProgram() {
bool done = true;
//printf("_ws->getGlobal(kGlobTime) = %ld, _switchTime = %d\n", _ws->getGlobal(kGlobTime), _switchTime);
if (_switchTime >= 0 && _ws->getGlobal(kGlobTime) >= _switchTime)
done = false;
_vars[kSeqVarTimer] -= _ws->getGlobal(kGlobTimeDelta) << 16;
while (!done) {
Instruction instruction;
_code->loadInstruction(instruction);
if (sequenceCommandsTable[instruction.instr] != NULL)
done = !(this->*sequenceCommandsTable[instruction.instr])(instruction);
else { fflush(stdout); /*g_system->delayMillis(1000);*/ }
}
return _terminated;
}
void Sequence::clearVars() {
for (int i = 0; i < sequenceVariableCount + _localVarCount; i++)
_vars[i] = 0;
// set default scaling to 100%
_vars[kSeqVarScale] = 0x10000;
}
bool Sequence::changeProgram(int32 sequenceHash) {
SequenceAsset *sequenceAsset = _ws->assets()->getSequence(sequenceHash);
if (sequenceAsset->localVarCount() > _localVarCount) {
//printf("Sequence::changeProgram(%d) sequenceAsset->localVarCount() > _localVarCount\n", sequenceHash);
return false;
}
// Initialize the sequence's bytecode
byte *code;
uint32 codeSize;
sequenceAsset->getCode(code, codeSize);
_code->setCode(code, codeSize);
// Reset status variables
_switchTime = 0;
_active = true;
_terminated = false;
_endOfSequenceRequest.codeOffset = -1;
_sequenceHash = sequenceHash;
_returnStackIndex = 0;
return true;
}
long *Sequence::getVarPtr(int index) {
return &_vars[index];
}
long *Sequence::getParentVarPtr(int index) {
return _parentSequence->getVarPtr(index);
}
long *Sequence::getDataPtr(int index) {
return &_dataRow[index];
}
void Sequence::draw(M4Surface *surface, const Common::Rect &clipRect, Common::Rect &updateRect) {
SpriteInfo info;
info.sprite = _curFrame;
info.hotX = _curFrame->xOffset;
info.hotY = _curFrame->yOffset;
info.encoding = _curFrame->encoding;
info.inverseColorTable = _vm->_scene->getInverseColorTable();
info.palette = _ws->getMainPalette();
info.width = _curFrame->w;
info.height = _curFrame->h;
int32 scaler = FixedMul(_vars[kSeqVarScale], 100 << 16) >> 16;
info.scaleX = _vars[kSeqVarWidth] < 0 ? -scaler : scaler;
info.scaleY = scaler;
surface->drawSprite(_vars[kSeqVarX] >> 16, _vars[kSeqVarY] >> 16, info, clipRect);
}
bool Sequence::s1_end(Instruction &instruction) {
//printf("Sequence::s1_end()\n");
_terminated = true;
return false;
}
bool Sequence::s1_clearVars(Instruction &instruction) {
//printf("Sequence::s1_clearVars()\n");
clearVars();
_vars[kSeqVarMachineID] = _machine->getId();
return true;
}
bool Sequence::s1_set(Instruction &instruction) {
//printf("Sequence::s1_set()\n");
*instruction.argp[0] = instruction.getValue();
return true;
}
bool Sequence::s1_compare(Instruction &instruction) {
//printf("Sequence::s1_compare()\n");
long value = instruction.getValue();
if (instruction.argv[0] < value)
_cmpFlags = -1;
else if (instruction.argv[0] > value)
_cmpFlags = 1;
else
_cmpFlags = 0;
return true;
}
bool Sequence::s1_add(Instruction &instruction) {
//printf("Sequence::s1_add()\n");
*instruction.argp[0] += instruction.getValue();
return true;
}
bool Sequence::s1_sub(Instruction &instruction) {
//printf("Sequence::s1_sub()\n");
*instruction.argp[0] -= instruction.getValue();
return true;
}
bool Sequence::s1_mul(Instruction &instruction) {
//printf("Sequence::s1_mul()\n");
*instruction.argp[0] = FixedMul(instruction.argv[0], instruction.getValue());
return true;
}
bool Sequence::s1_div(Instruction &instruction) {
//printf("Sequence::s1_div()\n");
// TODO: Catch divisor = 0 in FixedDiv
*instruction.argp[0] = FixedDiv(instruction.argv[0], instruction.getValue());
return true;
}
bool Sequence::s1_and(Instruction &instruction) {
//printf("Sequence::s1_and()\n");
*instruction.argp[0] = instruction.argv[0] & instruction.getValue();
if (*instruction.argp[0])
_cmpFlags = 0;
else
_cmpFlags = 1;
return true;
}
bool Sequence::s1_or(Instruction &instruction) {
//printf("Sequence::s1_or()\n");
*instruction.argp[0] = instruction.argv[0] | instruction.getValue();
if (*instruction.argp[0])
_cmpFlags = 0;
else
_cmpFlags = 1;
return true;
}
bool Sequence::s1_not(Instruction &instruction) {
//printf("Sequence::s1_not()\n");
if (instruction.argv[0] == 0) {
*instruction.argp[0] = 0x10000;
_cmpFlags = 1;
} else {
*instruction.argp[0] = 0;
_cmpFlags = 0;
}
return true;
}
bool Sequence::s1_sin(Instruction &instruction) {
//printf("Sequence::s1_sin()\n");
int32 tempAngle = *instruction.argp[1] >> 16;
if (tempAngle < 0)
tempAngle = 0x0100 - ((-tempAngle) & 0xff);
else
tempAngle &= 0xff;
*instruction.argp[0] = -cosTable[tempAngle];
return true;
}
bool Sequence::s1_cos(Instruction &instruction) {
//printf("Sequence::s1_cos()\n");
int32 tempAngle = *instruction.argp[1] >> 16;
if (tempAngle < 0)
tempAngle = 0x0100 - ((-tempAngle) & 0xff);
else
tempAngle &= 0xff;
*instruction.argp[0] = sinTable[tempAngle];
return true;
}
bool Sequence::s1_abs(Instruction &instruction) {
//printf("Sequence::s1_abs()\n");
*instruction.argp[0] = ABS(instruction.argv[1]);
return true;
}
bool Sequence::s1_min(Instruction &instruction) {
//printf("Sequence::s1_min()\n");
*instruction.argp[0] = MIN(instruction.argv[1], instruction.argv[2]);
return true;
}
bool Sequence::s1_max(Instruction &instruction) {
//printf("Sequence::s1_max()\n");
*instruction.argp[0] = MAX(instruction.argv[1], instruction.argv[2]);
return true;
}
bool Sequence::s1_mod(Instruction &instruction) {
//printf("Sequence::s1_mod()\n");
*instruction.argp[0] = instruction.argv[0] % instruction.getValue();
return true;
}
bool Sequence::s1_floor(Instruction &instruction) {
//printf("Sequence::s1_floor()\n");
*instruction.argp[0] = instruction.getValue() & 0xffff0000;
return true;
}
bool Sequence::s1_round(Instruction &instruction) {
//printf("Sequence::s1_round()\n");
if ((*instruction.argp[1] & 0xffff) >= 0x8000)
*instruction.argp[0] = (*instruction.argp[1] + 0x10000) & 0xffff0000;
else
*instruction.argp[0] = *instruction.argp[1] & 0xffff0000;
return true;
}
bool Sequence::s1_ceil(Instruction &instruction) {
//printf("Sequence::s1_ceil()\n");
if ((*instruction.argp[1] & 0xffff) >= 0)
*instruction.argp[0] = (*instruction.argp[1] + 0x10000) & 0xffff0000;
else
*instruction.argp[0] = *instruction.argp[1] & 0xffff0000;
return true;
}
bool Sequence::s1_point(Instruction &instruction) {
printf("Sequence::s1_point()\n");
// TODO
return true;
}
bool Sequence::s1_dist2d(Instruction &instruction) {
printf("Sequence::s1_dist2d()\n");
// TODO
return true;
}
bool Sequence::s1_crunch(Instruction &instruction) {
//printf("Sequence::s1_crunch()\n");
long deltaTime;
if (instruction.argc == 2) {
deltaTime = _vm->imath_ranged_rand16(instruction.argv[0], instruction.argv[1]);
} else if (instruction.argc == 1) {
deltaTime = instruction.argv[0];
} else {
deltaTime = 0;
}
_startTime = _ws->getGlobal(kGlobTime);
//printf("deltaTime = %ld\n", deltaTime >> 16); fflush(stdout);
//g_system->delayMillis(5000);
if (deltaTime >= 0) {
_switchTime = _ws->getGlobal(kGlobTime) + (deltaTime >> 16);
//printf("_ws->getGlobal(kGlobTime) = %ld\n", _ws->getGlobal(kGlobTime)); fflush(stdout);
//g_system->delayMillis(5000);
} else {
_switchTime = -1;
}
// TODO: Update if walking etc.
return false;
}
bool Sequence::s1_branch(Instruction &instruction) {
//printf("Sequence::s1_branch()\n");
uint32 ofs = instruction.argv[1] >> 16;
switch (instruction.argv[0] >> 16) {
case 0: // jmp
_code->jumpRelative(ofs);
break;
case 1: // <
if (_cmpFlags < 0)
_code->jumpRelative(ofs);
break;
case 2: // <=
if (_cmpFlags <= 0)
_code->jumpRelative(ofs);
break;
case 3: // ==
if (_cmpFlags == 0)
_code->jumpRelative(ofs);
break;
case 4: // !=
if (_cmpFlags != 0)
_code->jumpRelative(ofs);
break;
case 5: // >=
if (_cmpFlags >= 0)
_code->jumpRelative(ofs);
break;
case 6: // >
if (_cmpFlags > 0)
_code->jumpRelative(ofs);
break;
}
return true;
}
bool Sequence::s1_setFrame(Instruction &instruction) {
//printf("Sequence::s1_setFrame()\n");
int32 frameIndex;
if (instruction.argc == 3) {
frameIndex = _vm->imath_ranged_rand(instruction.argv[1] >> 16, instruction.argv[2] >> 16);
} else if (instruction.argc == 2) {
frameIndex = instruction.argv[1] >> 16;
} else {
frameIndex = (instruction.argv[0] & 0xFF0000) >> 16;
}
//printf("Sequence::s1_setFrame() spriteHash = %d\n", (uint32)instruction.argv[0] >> 24);
//printf("Sequence::s1_setFrame() frameIndex = %d\n", frameIndex);
SpriteAsset *spriteAsset = _ws->assets()->getSprite((uint32)instruction.argv[0] >> 24);
_curFrame = spriteAsset->getFrame(frameIndex);
return true;
}
bool Sequence::s1_sendMessage(Instruction &instruction) {
printf("Sequence::s1_sendMessage()\n");
// TODO
return true;
}
bool Sequence::s1_push(Instruction &instruction) {
printf("Sequence::s1_push()\n");
// TODO
return true;
}
bool Sequence::s1_pop(Instruction &instruction) {
printf("Sequence::s1_pop()\n");
// TODO
return true;
}
bool Sequence::s1_jumpSub(Instruction &instruction) {
//printf("Sequence::s1_jumpSub()\n");
_returnHashes[_returnStackIndex] = _sequenceHash;
_returnOffsets[_returnStackIndex] = _code->pos();
_returnStackIndex++;
_sequenceHash = instruction.argv[0] >> 16;
SequenceAsset *sequenceAsset = _ws->assets()->getSequence(_sequenceHash);
// initialize the sequence's bytecode
byte *code;
uint32 codeSize;
sequenceAsset->getCode(code, codeSize);
_code->setCode(code, codeSize);
return true;
}
bool Sequence::s1_return(Instruction &instruction) {
//printf("Sequence::s1_return()\n");
if (_returnStackIndex <= 0)
return s1_end(instruction);
_returnStackIndex--;
_sequenceHash = _returnHashes[_returnStackIndex];
uint32 ofs = _returnOffsets[_returnStackIndex];
SequenceAsset *sequenceAsset = _ws->assets()->getSequence(_sequenceHash);
// initialize the sequence's bytecode
byte *code;
uint32 codeSize;
sequenceAsset->getCode(code, codeSize);
_code->setCode(code, codeSize);
_code->jumpAbsolute(ofs);
return true;
}
bool Sequence::s1_getFrameCount(Instruction &instruction) {
//printf("Sequence::s1_getFrameCount()\n");
SpriteAsset *spriteAsset = _ws->assets()->getSprite(instruction.argv[1] >> 24);
*instruction.argp[0] = spriteAsset->getCount() << 16;
return true;
}
bool Sequence::s1_getFrameRate(Instruction &instruction) {
//printf("Sequence::s1_getFrameRate()\n");
SpriteAsset *spriteAsset = _ws->assets()->getSprite(instruction.argv[1] >> 24);
*instruction.argp[0] = spriteAsset->getFrameRate();
return true;
}
bool Sequence::s1_getCelsPixSpeed(Instruction &instruction) {
printf("Sequence::s1_getCelsPixSpeed()\n");
// TODO
return true;
}
bool Sequence::s1_setIndex(Instruction &instruction) {
printf("Sequence::s1_setIndex()\n");
// TODO
return true;
}
bool Sequence::s1_setLayer(Instruction &instruction) {
printf("Sequence::s1_setLayer()\n");
//TODO
return true;
}
bool Sequence::s1_setDepth(Instruction &instruction) {
printf("Sequence::s1_setDepth()\n");
//TODO
return true;
}
bool Sequence::s1_setData(Instruction &instruction) {
printf("Sequence::s1_setData()\n");
//TODO
return true;
}
bool Sequence::s1_openStream(Instruction &instruction) {
//printf("Sequence::s1_openStream()\n");
_stream = _vm->res()->openFile(_machine->name().c_str());
streamOpen();
return true;
}
bool Sequence::s1_streamNextFrame(Instruction &instruction) {
//printf("Sequence::s1_streamNextFrame()\n");
streamNextFrame();
return true;
}
bool Sequence::s1_closeStream(Instruction &instruction) {
printf("Sequence::s1_closeStream()\n");
//TODO
return true;
}
bool Sequence::streamOpen() {
_streamSpriteAsset = new SpriteAsset(_vm, _stream, _stream->size(), "stream", true);
_vars[kSeqVarSpriteFrameNumber] = -0x10000;
_vars[kSeqVarSpriteFrameCount] = _streamSpriteAsset->getCount() << 16;
_vars[kSeqVarSpriteFrameRate] = _streamSpriteAsset->getFrameRate() << 16;
//printf("Sequence::streamOpen() frames = %d; max = %d x %d\n", _streamSpriteAsset->getCount(), _streamSpriteAsset->getMaxFrameWidth(), _streamSpriteAsset->getMaxFrameHeight());
//fflush(stdout);
_curFrame = new M4Sprite(_vm, _streamSpriteAsset->getMaxFrameWidth(), _streamSpriteAsset->getMaxFrameHeight());
streamNextFrame();
// TODO: Just a hack to see the series with the correct palette.
_vm->_palette->setPalette(_streamSpriteAsset->getPalette(), 0, 256);
return true;
}
bool Sequence::streamNextFrame() {
_vars[kSeqVarSpriteFrameNumber] += 0x10000;
int32 frameNum = _vars[kSeqVarSpriteFrameNumber] >> 16;
if (frameNum >= _streamSpriteAsset->getCount()) {
// End reached
return false;
}
_streamSpriteAsset->loadStreamingFrame(_curFrame, frameNum, _vars[kSeqVarX], _vars[kSeqVarY]);
_vars[kSeqVarWidth] = _curFrame->w << 16;
_vars[kSeqVarHeight] = _curFrame->h << 16;
return true;
}
void Sequence::streamClose() {
_stream = NULL;
_vm->res()->toss(_machine->name().c_str());
//_vm->res()->purge();
delete _streamSpriteAsset;
delete _curFrame;
_stream = NULL;
_streamSpriteAsset = NULL;
_curFrame = NULL;
}
}