Initial commit

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

2
engines/toon/POTFILES Normal file
View File

@@ -0,0 +1,2 @@
engines/toon/metaengine.cpp
engines/toon/toon.cpp

797
engines/toon/anim.cpp Normal file
View File

@@ -0,0 +1,797 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, MojoTouch has
* exclusively licensed this code on March 23th, 2024, to be used in
* closed-source products.
* Therefore, any contributions (commits) to it will also be dual-licensed.
*
*/
#include "common/debug.h"
#include "common/rect.h"
#include "toon/anim.h"
#include "toon/toon.h"
#include "toon/tools.h"
namespace Toon {
bool Animation::loadAnimation(const Common::String &file) {
debugC(1, kDebugAnim, "loadAnimation(%s)", file.c_str());
uint32 fileSize = 0;
uint8 *fileData = _vm->resources()->getFileData(Common::Path(file), &fileSize);
if (!fileData)
return false;
Common::strlcpy(_name, "not_loaded", sizeof(_name));
if (!Common::String((char *)fileData, 12).equals("KevinAguilar"))
return false;
Common::strlcpy(_name, file.c_str(), sizeof(_name));
_shadowFlag = Common::String(_name).contains("SHADOW");
uint32 headerSize = READ_LE_UINT32(fileData + 16);
uint32 uncompressedBytes = READ_LE_UINT32(fileData + 20);
uint32 compressedBytes = READ_LE_UINT32(fileData + 24);
_numFrames = READ_LE_UINT32(fileData + 28);
_x1 = READ_LE_UINT32(fileData + 32);
_y1 = READ_LE_UINT32(fileData + 36);
_x2 = READ_LE_UINT32(fileData + 40);
_y2 = READ_LE_UINT32(fileData + 44);
_paletteEntries = READ_LE_UINT32(fileData + 56);
// CHECKME: Useless variable _fps
_fps = READ_LE_UINT32(fileData + 60);
uint32 paletteSize = READ_LE_UINT32(fileData + 64);
uint8 *currentData = fileData + 68;
if (_paletteEntries) {
if (paletteSize) {
delete[] _palette;
_palette = new uint8[paletteSize];
memcpy(_palette, currentData, paletteSize);
currentData += paletteSize;
} else {
_palette = 0;
}
}
byte *finalBuffer = new byte[uncompressedBytes];
if (uncompressedBytes > compressedBytes)
decompressLZSS(currentData, finalBuffer, uncompressedBytes);
else
memcpy(finalBuffer, currentData, uncompressedBytes);
if (READ_LE_UINT32(finalBuffer) == 0x12345678) {
uint8 *data = finalBuffer;
delete[] _frames;
_frames = new AnimationFrame[_numFrames];
for (int32 e = 0; e < _numFrames; e++) {
if (READ_LE_UINT32(data) != 0x12345678) {
delete[] finalBuffer;
return false;
}
int32 oldRef = READ_LE_UINT32(data + 4);
uint32 compressedSize = READ_LE_UINT32(data + 8);
uint32 decompressedSize = READ_LE_UINT32(data + 12);
_frames[e]._x1 = READ_LE_UINT32(data + 16);
_frames[e]._y1 = READ_LE_UINT32(data + 20);
_frames[e]._x2 = READ_LE_UINT32(data + 24);
_frames[e]._y2 = READ_LE_UINT32(data + 28);
uint8 *imageData = data + headerSize;
uint32 decompressedLZSSDataSize = 0;
_frames[e]._dataSize = 0;
if (oldRef != -1 || decompressedSize == 0) {
_frames[e]._ref = oldRef;
_frames[e]._data = nullptr;
_frames[e]._dataSize = 0;
} else {
_frames[e]._ref = -1;
_frames[e]._data = new uint8[decompressedSize];
if (compressedSize < decompressedSize) {
decompressedLZSSDataSize = decompressLZSS(imageData, _frames[e]._data, decompressedSize);
// assert(decompressedSize == decompressedLZSSDataSize);
_frames[e]._dataSize = decompressedLZSSDataSize;
} else {
memcpy(_frames[e]._data, imageData, compressedSize);
_frames[e]._dataSize = compressedSize;
}
}
data += headerSize + compressedSize;
}
}
_vm->resources()->purgeFileData();
delete[] finalBuffer;
return true;
}
Animation::Animation(ToonEngine *vm) : _vm(vm) {
_palette = nullptr;
_numFrames = 0;
_frames = nullptr;
memset(_name, 0, sizeof(_name));
_shadowFlag = false;
_x1 = _y1 = _x2 = _y2 = 0;
_fps = 0;
_paletteEntries = 0;
}
Animation::~Animation() {
delete[] _palette;
for (int32 i = 0; i < _numFrames; i++) {
delete[] _frames[i]._data;
}
delete[] _frames;
}
Common::Rect Animation::getRect() {
debugC(5, kDebugAnim, "getRect");
return Common::Rect(_x1, _y1, _x2, _y2);
}
void Animation::drawFrame(Graphics::Surface &surface, int32 frame, int16 xx, int16 yy) {
debugC(3, kDebugAnim, "drawFrame(surface, %d, %d, %d)", frame, xx, yy);
if (frame < 0)
frame = 0;
if (frame >= _numFrames)
frame = _numFrames - 1;
if (_numFrames == 0)
return;
int16 dataFrame = frame;
if (_frames[frame]._ref != -1)
dataFrame = _frames[frame]._ref;
if (!_frames[dataFrame]._data)
return;
int16 rectX = _frames[frame]._x2 - _frames[frame]._x1;
int16 rectY = _frames[frame]._y2 - _frames[frame]._y1;
int16 offsX = 0;
int16 offsY = 0;
_vm->addDirtyRect(xx + _x1 + _frames[frame]._x1, yy + _y1 + _frames[frame]._y1, xx + rectX + _x1 + _frames[frame]._x1 , yy + rectY + _y1 + _frames[frame]._y1);
if (xx + _x1 + _frames[frame]._x1 < 0) {
offsX = -(xx + _x1 + _frames[frame]._x1);
}
if (offsX >= rectX)
return;
else
rectX -= offsX;
if (yy + _y1 + _frames[frame]._y1 < 0) {
offsY = -(yy + _y1 + _frames[frame]._y1);
}
if (offsY >= rectY)
return;
else
rectY -= offsY;
if (rectX + xx + _x1 + _frames[frame]._x1 >= surface.w)
rectX = surface.w - xx - _x1 - _frames[frame]._x1;
if (rectX < 0)
return;
if (rectY + yy + _y1 + _frames[frame]._y1 >= surface.h)
rectY = surface.h - yy - _y1 - _frames[frame]._y1;
if (rectY < 0)
return;
int32 destPitch = surface.pitch;
uint8 *srcRow = _frames[dataFrame]._data + offsX + (_frames[frame]._x2 - _frames[frame]._x1) * offsY;
uint8 *curRow = (uint8 *)surface.getBasePtr(xx + _x1 + _frames[frame]._x1 + offsX, yy + _frames[frame]._y1 + _y1 + offsY);
for (int16 y = 0; y < rectY; y++) {
uint8 *cur = curRow;
uint8 *c = srcRow + y * (_frames[frame]._x2 - _frames[frame]._x1);
for (int16 x = 0; x < rectX; x++) {
if (*c)
*cur = *c;
c++;
cur++;
}
curRow += destPitch;
}
}
void Animation::drawFrameWithMask(Graphics::Surface &surface, int32 frame, int16 xx, int16 yy, int32 zz, Picture *mask) {
debugC(1, kDebugAnim, "drawFrameWithMask(surface, %d, %d, %d, %d, mask)", frame, xx, yy, zz);
warning("STUB: drawFrameWithMask()");
}
void Animation::drawFrameWithMaskAndScale(Graphics::Surface &surface, int32 frame, int16 xx, int16 yy, int32 zz, Picture *mask, int32 scale) {
debugC(5, kDebugAnim, "drawFrameWithMaskAndScale(surface, %d, %d, %d, %d, mask, %d, %s)", frame, xx, yy, zz, scale, _name);
// assert(frame < _numFrames && frame >= 0);
// TODO Why do we go to int16 here from int32?
int16 dataFrame = frame;
if (_frames[frame]._ref != -1)
dataFrame = _frames[frame]._ref;
// assert(dataFrame < _numFrames && dataFrame >= 0);
int16 rectX = _frames[frame]._x2 - _frames[frame]._x1;
int16 rectY = _frames[frame]._y2 - _frames[frame]._y1;
int16 finalWidth = rectX * scale / 1024;
int16 finalHeight = rectY * scale / 1024;
// compute final x1, y1, x2, y2
int16 xx1 = xx + _x1 + _frames[frame]._x1 * scale / 1024;
int16 yy1 = yy + _y1 + _frames[frame]._y1 * scale / 1024;
int16 xx2 = xx1 + finalWidth;
int16 yy2 = yy1 + finalHeight;
int16 w = _frames[frame]._x2 - _frames[frame]._x1;
_vm->addDirtyRect(xx1, yy1, xx2, yy2);
int32 destPitch = surface.pitch;
// assert(mask != nullptr);
int32 destPitchMask = mask->getWidth();
// assert(_frames[dataFrame]._data != nullptr);
uint8 *c = _frames[dataFrame]._data;
uint8 *curRow = (uint8 *)surface.getPixels();
// assert(mask->getDataPtr() != nullptr);
uint8 *curRowMask = mask->getDataPtr();
const uint32 maskDataSize = mask->getWidth() * mask->getHeight();
for (int16 y = yy1; y < yy2; ++y) {
for (int16 x = xx1; x < xx2; ++x) {
if (x < 0 || x >= 1280 || y < 0 || y >= 400)
continue;
uint8 *cur = curRow + x + y * destPitch;
uint32 nextMaskPos = x + y * destPitchMask;
// find the good c
int16 xs = (x - xx1) * 1024 / scale;
int16 ys = (y - yy1) * 1024 / scale;
// TODO Maybe check if we overread c here
// assert(ys * w + xs >= 0 && ys * w + xs < _frames[dataFrame]._dataSize);
uint8 cc = c[ys * w + xs];
if (cc && nextMaskPos < maskDataSize && (*(curRowMask + nextMaskPos)) >= zz) {
if (_shadowFlag) {
*cur = _vm->getShadowLUT()[*cur];
} else {
*cur = cc;
}
}
}
}
}
void Animation::applyPalette(int32 offset, int32 srcOffset, int32 numEntries) {
debugC(1, kDebugAnim, "applyPalette(%d, %d, %d)", offset, srcOffset, numEntries);
_vm->setPaletteEntries(_palette + srcOffset, offset, numEntries);
}
Common::Rect Animation::getFrameRect(int32 frame) {
debugC(4, kDebugAnim, "getFrameRect(%d)", frame);
if ((frame < 0) || (frame >= _numFrames)) {
return Common::Rect();
}
if (_frames[frame]._ref != -1)
frame = _frames[frame]._ref;
return Common::Rect(_frames[frame]._x1, _frames[frame]._y1, _frames[frame]._x2, _frames[frame]._y2);
}
int16 Animation::getFrameWidth(int32 frame) {
debugC(4, kDebugAnim, "getFrameWidth(%d)", frame);
if ((frame < 0) || (frame >= _numFrames))
return 0;
return _frames[frame]._x2 - _frames[frame]._x1;
}
int16 Animation::getFrameHeight(int32 frame) {
debugC(4, kDebugAnim, "getFrameHeight(%d)", frame);
if (frame < 0 || frame >= _numFrames)
return 0;
return _frames[frame]._y2 - _frames[frame]._y1;
}
int16 Animation::getWidth() const {
return _x2 - _x1;
}
int16 Animation::getHeight() const {
return _y2 - _y1;
}
void Animation::drawFontFrame(Graphics::Surface &surface, int32 frame, int16 xx, int16 yy, byte *colorMap) {
debugC(4, kDebugAnim, "drawFontFrame(surface, %d, %d, %d, colorMap)", frame, xx, yy);
if (frame < 0)
frame = 0;
if (frame >= _numFrames)
frame = _numFrames - 1;
if (_numFrames == 0)
return;
int16 dataFrame = frame;
if (_frames[frame]._ref != -1)
dataFrame = _frames[frame]._ref;
int16 rectX = _frames[frame]._x2 - _frames[frame]._x1;
int16 rectY = _frames[frame]._y2 - _frames[frame]._y1;
if ((xx + _x1 + _frames[frame]._x1 < 0) || (yy + _y1 + _frames[frame]._y1 < 0))
return;
if (rectX + xx + _x1 + _frames[frame]._x1 >= surface.w)
rectX = surface.w - xx - _x1 - _frames[frame]._x1;
if (rectX < 0)
return;
if (rectY + yy + _y1 + _frames[frame]._y1 >= surface.h)
rectY = surface.h - yy - _y1 - _frames[frame]._y1;
if (rectY < 0)
return;
int32 destPitch = surface.pitch;
uint8 *c = _frames[dataFrame]._data;
uint8 *curRow = (uint8 *)surface.getBasePtr(xx + _x1 + _frames[frame]._x1, yy + _frames[frame]._y1 + _y1);
for (int16 y = 0; y < rectY; y++) {
unsigned char *cur = curRow;
for (int16 x = 0; x < rectX; x++) {
if (*c && *c < 4)
*cur = colorMap[*c];
c++;
cur++;
}
curRow += destPitch;
}
}
void Animation::drawFrameOnPicture(int32 frame, int16 xx, int16 yy) {
debugC(1, kDebugAnim, "drawFrameOnPicture(%d, %d, %d)", frame, xx, yy);
if (frame < 0)
frame = 0;
if (frame >= _numFrames)
frame = _numFrames - 1;
if (_numFrames == 0)
return;
if (_frames[frame]._ref != -1)
frame = _frames[frame]._ref;
int16 rectX = _frames[frame]._x2 - _frames[frame]._x1;
int16 rectY = _frames[frame]._y2 - _frames[frame]._y1;
Picture *pic = _vm->getPicture();
if ((xx + _x1 + _frames[frame]._x1 < 0) || (yy + _y1 + _frames[frame]._y1 < 0))
return;
if (rectX + xx + _x1 + _frames[frame]._x1 >= pic->getWidth())
rectX = pic->getWidth() - xx - _x1 - _frames[frame]._x1;
if (rectX < 0)
return;
if (rectY + yy + _y1 + _frames[frame]._y1 >= pic->getHeight())
rectY = pic->getHeight() - yy - _y1 - _frames[frame]._y1;
if (rectY < 0)
return;
int32 destPitch = pic->getWidth();
uint8 *c = _frames[frame]._data;
uint8 *curRow = (uint8 *)pic->getDataPtr() + (yy + _frames[frame]._y1 + _y1) * destPitch + (xx + _x1 + _frames[frame]._x1);
for (int16 y = 0; y < rectY; y++) {
unsigned char *cur = curRow;
for (int16 x = 0; x < rectX; x++) {
if (*c)
*cur = *c;
c++;
cur++;
}
curRow += destPitch;
}
}
void AnimationInstance::update(int32 timeIncrement) {
debugC(5, kDebugAnim, "update(%d)", timeIncrement);
if (_currentFrame == -1)
return;
if (_rangeStart == _rangeEnd) {
_currentFrame = _rangeStart;
return;
}
if (_playing) {
_currentTime += timeIncrement;
_currentFrame = _currentTime / (1000 / _fps);
}
if (_looping) {
_currentFrame = (_currentFrame % (_rangeEnd - _rangeStart + 1)) + _rangeStart;
} else {
if (_currentFrame >= _rangeEnd - _rangeStart) {
_playing = false;
_currentFrame = _rangeEnd;
} else {
_currentFrame = _rangeStart + _currentFrame;
}
}
}
AnimationInstance::AnimationInstance(ToonEngine *vm, AnimationInstanceType type) : _vm(vm) {
_id = 0;
_type = type;
_animation = nullptr;
_currentFrame = 0;
_currentTime = 0;
_fps = 15;
_looping = true;
_playing = false;
_rangeEnd = 0;
_useMask = false;
_alignBottom = false;
_rangeStart = 0;
_scale = 1024;
_x = 0;
_y = 0;
_z = 0;
_layerZ = 0;
_visible = false;
}
void AnimationInstance::render() {
debugC(5, kDebugAnim, "AnimationInstance::render()");
if (_visible && _animation != nullptr) {
int32 frame = _currentFrame;
if (frame < 0)
frame = 0;
if (frame >= _animation->_numFrames)
frame = _animation->_numFrames - 1;
int16 x = _x;
int16 y = _y;
if (_alignBottom) {
int32 offsetX = (_animation->_x2 - _animation->_x1) / 2 * (_scale - 1024);
int32 offsetY = (_animation->_y2 - _animation->_y1) * (_scale - 1024);
x -= offsetX >> 10;
y -= offsetY >> 10;
}
if (_useMask) {
//if (_scale == 100) { // 100% scale
// _animation->drawFrameWithMask(_vm->getMainSurface(), _currentFrame, _x, _y, _z, _vm->getMask());
//} else {
_animation->drawFrameWithMaskAndScale(_vm->getMainSurface(), frame, x, y, _z, _vm->getMask(), _scale);
//}
} else {
_animation->drawFrame(_vm->getMainSurface(), frame, _x, _y);
}
}
}
void AnimationInstance::renderOnPicture() {
debugC(5, kDebugAnim, "renderOnPicture()");
if (_visible && _animation != nullptr)
_animation->drawFrameOnPicture(_currentFrame, _x, _y);
}
void AnimationInstance::playAnimation() {
debugC(6, kDebugAnim, "playAnimation()");
_playing = true;
}
void AnimationInstance::setAnimation(Animation *animation, bool setRange) {
debugC(5, kDebugAnim, "setAnimation(animation)");
_animation = animation;
if (animation && setRange) {
_rangeStart = 0;
_rangeEnd = animation->_numFrames - 1;
}
}
void AnimationInstance::setAnimationRange(int32 rangeStart, int32 rangeEnd) {
debugC(5, kDebugAnim, "setAnimationRange(%d, %d)", rangeStart, rangeEnd);
_rangeStart = rangeStart;
_rangeEnd = rangeEnd;
if (_currentFrame < _rangeStart)
_currentFrame = _rangeStart;
if (_currentFrame > _rangeEnd)
_currentFrame = _rangeEnd;
}
void AnimationInstance::setPosition(int16 x, int16 y, int32 z, bool relative) {
debugC(5, kDebugAnim, "setPosition(%d, %d, %d, %d)", x, y, z, (relative) ? 1 : 0);
if (relative || _animation == nullptr) {
_x = x;
_y = y;
_z = z;
} else {
_x = x - _animation->_x1;
_y = y - _animation->_y1;
_z = z;
}
}
void AnimationInstance::moveRelative(int16 dx, int16 dy, int32 dz) {
debugC(1, kDebugAnim, "moveRelative(%d, %d, %d)", dx, dy, dz);
_x += dx;
_y += dy;
_z += dz;
}
void AnimationInstance::forceFrame(int32 position) {
debugC(5, kDebugAnim, "forceFrame(%d)", position);
_currentFrame = position;
_rangeStart = position;
_rangeEnd = position;
}
void AnimationInstance::setFrame(int32 position) {
debugC(5, kDebugAnim, "setFrame(%d)", position);
_currentFrame = position;
}
void AnimationInstance::setFps(int32 fps) {
debugC(4, kDebugAnim, "setFps(%d)", fps);
_fps = fps;
}
void AnimationInstance::stopAnimation() {
debugC(5, kDebugAnim, "stopAnimation()");
_playing = false;
}
void AnimationInstance::setVisible(bool visible) {
debugC(1, kDebugAnim, "setVisible(%d)", (visible) ? 1 : 0);
_visible = visible;
}
void AnimationInstance::setScale(int32 scale, bool align) {
debugC(4, kDebugAnim, "setScale(%d)", scale);
_scale = scale;
_alignBottom = align;
}
void AnimationInstance::setUseMask(bool useMask) {
debugC(1, kDebugAnim, "setUseMask(%d)", (useMask) ? 1 : 0);
_useMask = useMask;
}
void AnimationInstance::getRect(int16 *x1, int16 *y1, int16 *x2, int16 *y2) const {
debugC(5, kDebugAnim, "getRect(%d, %d, %d, %d)", *x1, *y1, *x2, *y2);
int16 rectX = _animation->_frames[_currentFrame]._x2 - _animation->_frames[_currentFrame]._x1;
int16 rectY = _animation->_frames[_currentFrame]._y2 - _animation->_frames[_currentFrame]._y1;
int16 finalWidth = rectX * _scale / 1024;
int16 finalHeight = rectY * _scale / 1024;
// compute final x1, y1, x2, y2
*x1 = _x + _animation->_x1 + _animation->_frames[_currentFrame]._x1 * _scale / 1024;
*y1 = _y + _animation->_y1 + _animation->_frames[_currentFrame]._y1 * _scale / 1024;
*x2 = *x1 + finalWidth;
*y2 = *y1 + finalHeight;
}
void AnimationInstance::setX(int16 x, bool relative) {
debugC(1, kDebugAnim, "setX(%d, %d)", x, (relative) ? 1 : 0);
if (relative || _animation == nullptr)
_x = x;
else
_x = x - _animation->_x1;
}
void AnimationInstance::setY(int16 y, bool relative) {
debugC(1, kDebugAnim, "setY(%d, %d)", y, (relative) ? 1 : 0);
if (relative || _animation == nullptr)
_y = y;
else
_y = y - _animation->_y1;
}
void AnimationInstance::setZ(int32 z, bool relative) {
debugC(1, kDebugAnim, "setZ(%d, %d)", z, (relative) ? 1 : 0);
_z = z;
}
void AnimationInstance::setLayerZ(int32 z) {
_layerZ = z;
if (_vm->getAnimationManager()->hasInstance(this))
_vm->getAnimationManager()->updateInstance(this);
}
int32 AnimationInstance::getLayerZ() const {
return _layerZ;
}
int16 AnimationInstance::getX2() const {
return _x + _animation->_x1;
}
int16 AnimationInstance::getY2() const {
return _y + _animation->_y1;
}
int32 AnimationInstance::getZ2() const {
return _z;
}
void AnimationInstance::save(Common::WriteStream *stream) {
// we don't load the animation here
// it must be loaded externally to avoid leaks.
stream->writeSint32LE(_currentFrame);
stream->writeSint32LE(_currentTime);
stream->writeSint32LE(_layerZ);
stream->writeSint32LE(_x);
stream->writeSint32LE(_y);
stream->writeSint32LE(_z);
stream->writeSint32LE(_scale);
stream->writeSint32LE(_playing);
stream->writeSint32LE(_looping);
stream->writeSint32LE(_rangeStart);
stream->writeSint32LE(_rangeEnd);
stream->writeSint32LE(_rangeStart);
stream->writeSint32LE(_fps);
stream->writeSint32LE(_id);
stream->writeSint32LE(_type);
stream->writeSint32LE(_visible);
stream->writeSint32LE(_useMask);
}
void AnimationInstance::load(Common::ReadStream *stream) {
_currentFrame = stream->readSint32LE();
_currentTime = stream->readSint32LE();
_layerZ = stream->readSint32LE();
_x = stream->readSint32LE();
_y = stream->readSint32LE();
_z = stream->readSint32LE();
_scale = stream->readSint32LE();
_playing = stream->readSint32LE();
_looping = stream->readSint32LE();
_rangeStart = stream->readSint32LE();
_rangeEnd = stream->readSint32LE();
_rangeStart = stream->readSint32LE();
_fps = stream->readSint32LE();
_id = stream->readSint32LE();
_type = (AnimationInstanceType)stream->readSint32LE();
_visible = stream->readSint32LE();
_useMask = stream->readSint32LE();
}
void AnimationInstance::setLooping(bool enable) {
debugC(6, kDebugAnim, "setLooping(%d)", (enable) ? 1 : 0);
_looping = enable;
}
void AnimationInstance::reset() {
_currentFrame = 0;
_currentTime = 0;
}
AnimationManager::AnimationManager(ToonEngine *vm) : _vm(vm) {
}
bool AnimationManager::hasInstance(AnimationInstance* instance) {
for (uint32 i = 0; i < _instances.size(); i++) {
if (_instances[i] == instance)
return true;
}
return false;
}
void AnimationManager::updateInstance(AnimationInstance* instance) {
// simply remove and readd the instance in the ordered list
removeInstance(instance);
addInstance(instance);
}
void AnimationManager::addInstance(AnimationInstance *instance) {
// if the instance already exists, we skip the add
for (uint32 i = 0; i < _instances.size(); i++) {
if (_instances[i] == instance)
return;
}
int32 found = -1;
// here we now do an ordered insert (closer to the original game)
for (uint32 i = 0; i < _instances.size(); i++) {
if (_instances[i]->getLayerZ() >= instance->getLayerZ()) {
found = i;
break;
}
}
if (found == -1)
_instances.push_back(instance);
else
_instances.insert_at(found, instance);
}
void AnimationManager::removeInstance(AnimationInstance *instance) {
debugC(1, kDebugAnim, "removeInstance(instance)");
int32 found = -1;
for (uint32 i = 0; i < _instances.size(); i++) {
if (_instances[i] == instance) {
found = i;
break;
}
}
if (found > -1)
_instances.remove_at(found);
}
void AnimationManager::removeAllInstances(AnimationInstanceType type) {
debugC(1, kDebugAnim, "removeInstance(type)");
for (int32 i = (int32)_instances.size(); i >= 0; i--) {
if (_instances[i]->getType() & type)
_instances.remove_at(i);
}
}
void AnimationManager::update(int32 timeIncrement) {
debugC(5, kDebugAnim, "update(%d)", timeIncrement);
for (uint32 i = 0; i < _instances.size(); i++)
_instances[i]->update(timeIncrement);
}
void AnimationManager::render() {
debugC(5, kDebugAnim, "AnimationManager::render()");
for (uint32 i = 0; i < _instances.size(); i++) {
if (_instances[i]->getVisible())
_instances[i]->render();
}
}
AnimationInstance *AnimationManager::createNewInstance(AnimationInstanceType type) {
return new AnimationInstance(_vm, type);
}
} // End of namespace Toon

205
engines/toon/anim.h Normal file
View File

@@ -0,0 +1,205 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, MojoTouch has
* exclusively licensed this code on March 23th, 2024, to be used in
* closed-source products.
* Therefore, any contributions (commits) to it will also be dual-licensed.
*
*/
#ifndef TOON_ANIM_H
#define TOON_ANIM_H
#include "common/stream.h"
#include "common/array.h"
#include "common/func.h"
#include "graphics/surface.h"
#include "toon/script.h"
namespace Toon {
class Picture;
class ToonEngine;
struct AnimationFrame {
int16 _x1;
int16 _y1;
int16 _x2;
int16 _y2;
int32 _ref;
uint8 *_data;
uint32 _dataSize;
};
class Animation {
public:
Animation(ToonEngine *vm);
~Animation();
int16 _x1;
int16 _y1;
int16 _x2;
int16 _y2;
int32 _numFrames;
int32 _fps;
AnimationFrame *_frames;
uint8 *_palette;
int32 _paletteEntries;
char _name[32];
bool _shadowFlag;
bool loadAnimation(const Common::String &file);
void drawFrame(Graphics::Surface &surface, int32 frame, int16 x, int16 y);
void drawFontFrame(Graphics::Surface &surface, int32 frame, int16 x, int16 y, byte *colorMap);
void drawFrameOnPicture(int32 frame, int16 x, int16 y);
void drawFrameWithMask(Graphics::Surface &surface, int32 frame, int16 xx, int16 yy, int32 zz, Picture *mask);
void drawFrameWithMaskAndScale(Graphics::Surface &surface, int32 frame, int16 xx, int16 yy, int32 zz, Picture *mask, int32 scale);
// void drawStrip(int32 offset = 0);
void applyPalette(int32 offset, int32 srcOffset, int32 numEntries);
Common::Rect getFrameRect(int32 frame);
int16 getFrameWidth(int32 frame);
int16 getFrameHeight(int32 frame);
int16 getWidth() const;
int16 getHeight() const;
Common::Rect getRect();
protected:
ToonEngine *_vm;
};
enum AnimationInstanceType {
kAnimationCharacter = 1,
kAnimationScene = 2,
kAnimationCursor = 4
};
class AnimationInstance {
public:
AnimationInstance(ToonEngine *vm, AnimationInstanceType type);
void update(int32 timeIncrement);
void render();
void renderOnPicture();
void setAnimation(Animation *animation, bool setRange = true);
void playAnimation();
void setAnimationRange(int32 rangeStart, int32 rangeEnd);
void setFps(int32 fps);
void setLooping(bool enable);
void stopAnimation();
void setFrame(int32 position);
void forceFrame(int32 position);
void setPosition(int16 x, int16 y, int32 z, bool relative = false);
Animation *getAnimation() const { return _animation; }
void setScale(int32 scale, bool align = false);
void setVisible(bool visible);
bool getVisible() const { return _visible; }
void setUseMask(bool useMask);
void moveRelative(int16 dx, int16 dy, int32 dz);
void getRect(int16 *x1, int16 *y1, int16 *x2, int16 *y2) const;
int16 getX() const { return _x; }
int16 getY() const { return _y; }
int32 getZ() const { return _z; }
int16 getX2() const;
int16 getY2() const;
int32 getZ2() const;
int32 getFrame() const { return _currentFrame; }
void reset();
void save(Common::WriteStream *stream);
void load(Common::ReadStream *stream);
void setId(int32 id) { _id = id; }
int32 getId() const { return _id; }
void setX(int16 x, bool relative = false);
void setY(int16 y, bool relative = false);
void setZ(int32 z, bool relative = false);
void setLayerZ(int32 layer);
int32 getLayerZ() const;
AnimationInstanceType getType() const { return _type; }
protected:
int32 _currentFrame;
int32 _currentTime;
int32 _fps;
Animation *_animation;
int16 _x;
int16 _y;
int32 _z;
int32 _layerZ;
int32 _rangeStart;
int32 _rangeEnd;
int32 _scale;
int32 _id;
AnimationInstanceType _type;
bool _useMask;
bool _playing;
bool _looping;
bool _visible;
bool _alignBottom;
ToonEngine *_vm;
};
class AnimationManager {
public:
AnimationManager(ToonEngine *vm);
AnimationInstance *createNewInstance(AnimationInstanceType type);
void addInstance(AnimationInstance *instance);
void removeInstance(AnimationInstance *instance);
void updateInstance(AnimationInstance* instance);
void removeAllInstances(AnimationInstanceType type);
void render();
void update(int32 timeIncrement);
bool hasInstance(AnimationInstance* instance);
protected:
ToonEngine *_vm;
Common::Array<AnimationInstance *> _instances;
};
class SceneAnimation {
public:
AnimationInstance *_originalAnimInstance;
AnimationInstance *_animInstance;
Animation *_animation;
int32 _id;
bool _active;
void load(ToonEngine *vm, Common::ReadStream *stream);
void save(ToonEngine *vm, Common::WriteStream *stream);
};
class SceneAnimationScript {
public:
EMCData *_data;
EMCState _state;
uint32 _lastTimer;
bool _frozen;
bool _frozenForConversation;
bool _active;
};
} // End of namespace Toon
#endif

638
engines/toon/audio.cpp Normal file
View File

@@ -0,0 +1,638 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, MojoTouch has
* exclusively licensed this code on March 23th, 2024, to be used in
* closed-source products.
* Therefore, any contributions (commits) to it will also be dual-licensed.
*
*/
#include "common/debug.h"
#include "toon/audio.h"
#include "common/memstream.h"
#include "common/substream.h"
#include "audio/decoders/adpcm_intern.h"
namespace Toon {
AudioManager::AudioManager(ToonEngine *vm, Audio::Mixer *mixer) : _vm(vm), _mixer(mixer) {
for (int32 i = 0; i < 16; i++)
_channels[i] = nullptr;
for (int32 i = 0; i < 4; i++)
_audioPacks[i] = nullptr;
for (int32 i = 0; i < 4; i++) {
_ambientSFXs[i]._delay = 0;
_ambientSFXs[i]._enabled = false;
_ambientSFXs[i]._id = -1;
_ambientSFXs[i]._channel = -1;
_ambientSFXs[i]._lastTimer = 0;
_ambientSFXs[i]._volume = 255;
}
_voiceMuted = false;
_musicMuted = false;
_sfxMuted = false;
_currentMusicChannel = 0;
}
AudioManager::~AudioManager(void) {
_mixer->stopAll();
for (int32 i = 0; i < 4; i++) {
closeAudioPack(i);
}
}
void AudioManager::muteMusic(bool muted) {
setMusicVolume(muted ? 0 : 255);
_musicMuted = muted;
}
void AudioManager::muteVoice(bool muted) {
if (voiceStillPlaying() && _channels[2]) {
_channels[2]->setVolume(muted ? 0 : 255);
}
_voiceMuted = muted;
}
void AudioManager::muteSfx(bool muted) {
_sfxMuted = muted;
}
void AudioManager::removeInstance(AudioStreamInstance *inst) {
debugC(1, kDebugAudio, "removeInstance(inst)");
for (int32 i = 0; i < 16; i++) {
if (inst == _channels[i])
_channels[i] = nullptr;
}
}
int AudioManager::playMusic(const Common::String &dir, const Common::String &music) {
debugC(1, kDebugAudio, "playMusic(%s, %s)", dir.c_str(), music.c_str());
// two musics can be played at same time
Common::Path path;
if (dir == "") {
path = Common::Path(Common::String::format("%s.MUS", music.c_str()));
} else {
path = Common::Path(Common::String::format("ACT%d/%s/%s.MUS", _vm->state()->_currentChapter, dir.c_str(), music.c_str()));
}
if (_currentMusicName == music)
return -1;
_currentMusicName = music;
Common::SeekableReadStream *srs = _vm->resources()->openFile(path);
if (!srs)
return -1;
// see what channel to take
// if the current channel didn't really start. reuse this one
if (_channels[_currentMusicChannel] && _channels[_currentMusicChannel]->isPlaying()) {
if (_channels[_currentMusicChannel]->getPlayedSampleCount() < 500) {
_channels[_currentMusicChannel]->stop(false);
_currentMusicChannel = 1 - _currentMusicChannel;
}
else
{
_channels[_currentMusicChannel]->stop(true);
}
}
// go to the next channel
_currentMusicChannel = 1 - _currentMusicChannel;
// if it's already playing.. stop it quickly (no fade)
if (_channels[_currentMusicChannel] && _channels[_currentMusicChannel]->isPlaying()) {
_channels[_currentMusicChannel]->stop(false);
}
// no need to delete instance here; it will automatically be deleted by the mixer when it is done with it
_channels[_currentMusicChannel] = new AudioStreamInstance(this, _mixer, srs, true, true);
_channels[_currentMusicChannel]->setVolume(_musicMuted ? 0 : 255);
_channels[_currentMusicChannel]->play(true, Audio::Mixer::kMusicSoundType);
return _currentMusicChannel;
}
bool AudioManager::voiceStillPlaying() {
if (!_channels[2])
return false;
return _channels[2]->isPlaying();
}
void AudioManager::playVoice(int32 id, bool genericVoice) {
debugC(1, kDebugAudio, "playVoice(%d, %d)", id, (genericVoice) ? 1 : 0);
if (voiceStillPlaying()) {
// stop current voice
_channels[2]->stop(false);
}
Common::SeekableReadStream *stream;
if (genericVoice)
stream = _audioPacks[0]->getStream(id);
else
stream = _audioPacks[1]->getStream(id);
// no need to delete channel 2, it will be deleted by the mixer when the stream is finished
_channels[2] = new AudioStreamInstance(this, _mixer, stream, false, true);
_channels[2]->play(false, Audio::Mixer::kSpeechSoundType);
_channels[2]->setVolume(_voiceMuted ? 0 : 255);
}
int32 AudioManager::playSFX(int32 id, int volume , bool genericSFX) {
debugC(4, kDebugAudio, "playSFX(%d, %d)", id, (genericSFX) ? 1 : 0);
// find a free SFX channel
Common::SeekableReadStream *stream;
if (genericSFX)
stream = _audioPacks[2]->getStream(id, true);
else
stream = _audioPacks[3]->getStream(id, true);
if (stream->size() == 0)
return -1;
for (int32 i = 3; i < 16; i++) {
if (!_channels[i]) {
_channels[i] = new AudioStreamInstance(this, _mixer, stream, false, true);
_channels[i]->play(false, Audio::Mixer::kSFXSoundType);
_channels[i]->setVolume(_sfxMuted ? 0 : volume);
return i;
}
}
return -1;
}
void AudioManager::stopAllSfxs() {
for (int32 i = 3; i < 16; i++) {
if (_channels[i] && _channels[i]->isPlaying()) {
_channels[i]->stop(false);
}
}
}
void AudioManager::stopCurrentVoice() {
debugC(1, kDebugAudio, "stopCurrentVoice()");
if (_channels[2] && _channels[2]->isPlaying())
_channels[2]->stop(false);
}
void AudioManager::closeAudioPack(int32 id) {
delete _audioPacks[id];
_audioPacks[id] = nullptr;
}
bool AudioManager::loadAudioPack(int32 id, const Common::Path &indexFile, const Common::Path &packFile) {
debugC(4, kDebugAudio, "loadAudioPack(%d, %s, %s)", id, indexFile.toString().c_str(), packFile.toString().c_str());
closeAudioPack(id);
_audioPacks[id] = new AudioStreamPackage(_vm);
return _audioPacks[id]->loadAudioPackage(indexFile, packFile);
}
void AudioManager::setMusicVolume(uint8 volume) {
debugC(1, kDebugAudio, "setMusicVolume(%d)", volume);
if (_channels[0])
_channels[0]->setVolume(volume);
if (_channels[1])
_channels[1]->setVolume(volume);
}
void AudioManager::stopMusicChannel(int channelId, bool fade) {
if (_channels[channelId])
_channels[channelId]->stop(fade);
if (_currentMusicChannel == channelId)
// clean _currentMusicName too
_currentMusicName = "";
}
void AudioManager::stopMusic(bool fade) {
debugC(1, kDebugAudio, "stopMusic()");
stopMusicChannel(0, fade);
stopMusicChannel(1, fade);
}
AudioStreamInstance::AudioStreamInstance(AudioManager *man, Audio::Mixer *mixer, Common::SeekableReadStream *stream , bool looping, bool deleteFileStreamAtEnd) {
_compBufferSize = 0;
_buffer = nullptr;
_bufferSize = 0;
_bufferMaxSize = 0;
_mixer = mixer;
_compBuffer = nullptr;
_bufferOffset = 0;
_lastSample = 0;
_lastStepIndex = 0;
_file = stream;
_fadingIn = false;
_fadingOut = false;
_fadeTime = 0;
_stopped = false;
_volume = 255;
_totalSize = stream->size();
_currentReadSize = 8;
_man = man;
_looping = looping;
_musicAttenuation = 1000;
_deleteFileStream = deleteFileStreamAtEnd;
_playedSamples = 0;
// preload one packet
if (_totalSize > 0) {
_file->skip(8);
readPacket();
} else {
stopNow();
}
_soundType = Audio::Mixer::kPlainSoundType;
}
AudioStreamInstance::~AudioStreamInstance() {
delete[] _buffer;
delete[] _compBuffer;
if (_man)
_man->removeInstance(this);
if (_deleteFileStream) {
delete _file;
}
}
int AudioStreamInstance::readBuffer(int16 *buffer, const int numSamples) {
debugC(5, kDebugAudio, "readBuffer(buffer, %d)", numSamples);
if (_stopped)
return 0;
handleFade(numSamples);
int32 leftSamples = numSamples;
int32 destOffset = 0;
if ((_bufferOffset + leftSamples) * 2 >= _bufferSize) {
if (_bufferSize - _bufferOffset * 2 > 0) {
memcpy(buffer, &_buffer[_bufferOffset], _bufferSize - _bufferOffset * 2);
leftSamples -= (_bufferSize - _bufferOffset * 2) / 2;
destOffset += (_bufferSize - _bufferOffset * 2) / 2;
}
if (!readPacket())
return 0;
_bufferOffset = 0;
}
if (leftSamples >= 0) {
memcpy(buffer + destOffset, &_buffer[_bufferOffset], MIN(leftSamples * 2, _bufferSize));
_bufferOffset += leftSamples;
}
_playedSamples += numSamples;
return numSamples;
}
bool AudioStreamInstance::readPacket() {
debugC(5, kDebugAudio, "readPacket()");
if (_file->eos() || (_currentReadSize >= _totalSize)) {
if (_looping) {
_file->seek(8);
_currentReadSize = 8;
_lastSample = 0;
_lastStepIndex = 0;
} else {
_bufferSize = 0;
stopNow();
return false;
}
}
int16 numCompressedBytes = _file->readSint16LE();
int16 numDecompressedBytes = _file->readSint16LE();
_file->readSint32LE();
if (numCompressedBytes > _compBufferSize) {
delete[] _compBuffer;
_compBufferSize = numCompressedBytes;
_compBuffer = new uint8[_compBufferSize];
}
if (numDecompressedBytes > _bufferMaxSize) {
delete[] _buffer;
_bufferMaxSize = numDecompressedBytes;
_buffer = new int16[numDecompressedBytes];
}
_bufferSize = numDecompressedBytes;
_file->read(_compBuffer, numCompressedBytes);
_currentReadSize += 8 + numCompressedBytes;
decodeADPCM(_compBuffer, _buffer, numCompressedBytes);
return true;
}
void AudioStreamInstance::decodeADPCM(uint8 *comp, int16 *dest, int32 packetSize) {
debugC(5, kDebugAudio, "decodeADPCM(comp, dest, %d)", packetSize);
// standard IMA ADPCM decoding
int32 numSamples = 2 * packetSize;
int32 samp = _lastSample;
int32 stepIndex = _lastStepIndex;
for (int32 i = 0; i < numSamples; i++) {
uint8 comm = *comp;
bool isOddSample = (i & 1);
uint8 code;
if (!isOddSample)
code = comm & 0xf;
else
code = (comm & 0xf0) >> 4;
uint8 sample = code & 0x7;
int32 step = Audio::Ima_ADPCMStream::_imaTable[stepIndex];
int32 E = step >> 3;
if (sample & 4)
E += step;
if (sample & 2)
E += step >> 1;
if (sample & 1)
E += step >> 2;
stepIndex += Audio::ADPCMStream::_stepAdjustTable[sample];
stepIndex = CLIP<int32>(stepIndex, 0, ARRAYSIZE(Audio::Ima_ADPCMStream::_imaTable) - 1);
if (code & 0x8)
samp -= E;
else
samp += E;
samp = CLIP<int32>(samp, -32768, 32767);
*dest = samp;
if (isOddSample)
comp++;
dest++;
}
_lastSample = samp;
_lastStepIndex = stepIndex;
}
void AudioStreamInstance::play(bool fade, Audio::Mixer::SoundType soundType) {
debugC(1, kDebugAudio, "play(%d)", (fade) ? 1 : 0);
_stopped = false;
_fadingIn = fade;
_fadeTime = 0;
_soundType = soundType;
_musicAttenuation = 1000; // max volume
_mixer->playStream(soundType, &_handle, this, -1);
handleFade(0);
}
void AudioStreamInstance::handleFade(int32 numSamples) {
debugC(5, kDebugAudio, "handleFade(%d)", numSamples);
// Fading enabled only for music
if (_soundType != Audio::Mixer::kMusicSoundType)
return;
int32 finalVolume = _volume;
if (_fadingOut) {
_fadeTime += numSamples;
if (_fadeTime > 40960) {
_fadeTime = 40960;
stopNow();
_fadingOut = false;
}
finalVolume = _volume - _fadeTime * _volume / 40960;
} else {
if (_fadingIn) {
_fadeTime += numSamples;
if (_fadeTime > 40960) {
_fadeTime = 40960;
_fadingIn = false;
}
finalVolume = _volume * _fadeTime / 40960;
}
}
// the music is too loud when someone is talking
// smoothing to avoid big volume changes
if (_man->voiceStillPlaying()) {
_musicAttenuation -= numSamples >> 4;
if (_musicAttenuation < 250)
_musicAttenuation = 250;
} else {
_musicAttenuation += numSamples >> 5;
if (_musicAttenuation > 1000)
_musicAttenuation = 1000;
}
_mixer->setChannelVolume(_handle, finalVolume * _musicAttenuation / 1000);
}
void AudioStreamInstance::stop(bool fade /*= false*/) {
debugC(1, kDebugAudio, "stop(%d)", (fade) ? 1 : 0);
if (fade) {
if (!_fadingOut) {
_fadingIn = false;
_fadingOut = true;
_fadeTime = 0;
}
} else {
stopNow();
}
}
void AudioStreamInstance::stopNow() {
debugC(1, kDebugAudio, "stopNow()");
_stopped = true;
}
void AudioStreamInstance::setVolume(int32 volume) {
debugC(1, kDebugAudio, "setVolume(%d)", volume);
_volume = volume;
_mixer->setChannelVolume(_handle, volume);
}
AudioStreamPackage::AudioStreamPackage(ToonEngine *vm) : _vm(vm) {
_indexBuffer = nullptr;
_file = nullptr;
}
AudioStreamPackage::~AudioStreamPackage() {
delete[] _indexBuffer;
delete _file;
}
bool AudioStreamPackage::loadAudioPackage(const Common::Path &indexFile, const Common::Path &streamFile) {
debugC(4, kDebugAudio, "loadAudioPackage(%s, %s)", indexFile.toString().c_str(), streamFile.toString().c_str());
uint32 size = 0;
uint8 *fileData = _vm->resources()->getFileData(indexFile, &size);
if (!fileData)
return false;
delete[] _indexBuffer;
_indexBuffer = new uint32[size / 4];
memcpy(_indexBuffer, fileData, size);
_file = _vm->resources()->openFile(streamFile);
if (!_file)
return false;
return true;
}
void AudioStreamPackage::getInfo(int32 id, int32 *offset, int32 *size) {
debugC(1, kDebugAudio, "getInfo(%d, offset, size)", id);
*offset = READ_LE_UINT32(_indexBuffer + id);
*size = READ_LE_UINT32(_indexBuffer + id + 1) - READ_LE_UINT32(_indexBuffer + id);
}
Common::SeekableReadStream *AudioStreamPackage::getStream(int32 id, bool ownMemory) {
debugC(1, kDebugAudio, "getStream(%d, %d)", id, (ownMemory) ? 1 : 0);
int32 offset = 0;
int32 size = 0;
getInfo(id, &offset, &size);
if (ownMemory) {
byte *memory = (byte *)malloc(size);
_file->seek(offset);
_file->read(memory, size);
return new Common::MemoryReadStream(memory, size, DisposeAfterUse::YES);
} else {
return new Common::SeekableSubReadStream(_file, offset, size + offset);
}
}
void AudioManager::startAmbientSFX(int32 id, int32 delay, int32 mode, int32 volume)
{
int32 found = -1;
for (int32 i = 0; i < 4; i++) {
if (!_ambientSFXs[i]._enabled) {
found = i;
break;
}
}
if (found < 0)
return;
_ambientSFXs[found]._lastTimer = _vm->getOldMilli() - 1;
_ambientSFXs[found]._delay = delay;
_ambientSFXs[found]._enabled = true;
_ambientSFXs[found]._mode = mode;
_ambientSFXs[found]._volume = volume;
_ambientSFXs[found]._id = id;
updateAmbientSFX();
}
void AudioManager::setAmbientSFXVolume(int32 id, int volume) {
for (int32 i = 0; i < 4; i++) {
AudioAmbientSFX* ambient = &_ambientSFXs[i];
if (ambient->_id == id && ambient->_enabled) {
ambient->_volume = volume;
if (ambient->_channel >= 0 && _channels[ambient->_channel] && _channels[ambient->_channel]->isPlaying()) {
_channels[ambient->_channel]->setVolume(volume);
}
break;
}
}
}
void AudioManager::killAmbientSFX(int32 id)
{
for (int32 i = 0; i < 4; i++) {
AudioAmbientSFX* ambient = &_ambientSFXs[i];
if (ambient->_id == id && ambient->_enabled) {
ambient->_enabled = false;
ambient->_id = -1;
if (ambient->_channel >= 0 && _channels[ambient->_channel]) {
_channels[ambient->_channel]->stop(false);
}
}
}
}
void AudioManager::killAllAmbientSFX()
{
for (int32 i = 0; i < 4; i++) {
AudioAmbientSFX* ambient = &_ambientSFXs[i];
if (ambient->_enabled) {
ambient->_enabled = false;
ambient->_id = -1;
if (ambient->_channel >= 0 && _channels[ambient->_channel] && _channels[ambient->_channel]->isPlaying()) {
_channels[ambient->_channel]->stop(false);
}
ambient->_channel = -1;
}
}
}
void AudioManager::updateAmbientSFX()
{
if (_vm->getMoviePlayer()->isPlaying())
return;
for (int32 i = 0; i < 4; i++) {
AudioAmbientSFX* ambient = &_ambientSFXs[i];
if (ambient->_enabled && (ambient->_channel < 0 || !(_channels[ambient->_channel] && _channels[ambient->_channel]->isPlaying()))) {
if (ambient->_mode == 1) {
if (_vm->randRange(0, 32767) < ambient->_delay) {
ambient->_channel = playSFX(ambient->_id, ambient->_volume, false);
}
} else {
if (_vm->getOldMilli() > ambient->_lastTimer) {
ambient->_channel = playSFX(ambient->_id, ambient->_volume, false);
ambient->_lastTimer = _vm->getOldMilli(); // + 60 * _vm->getTickLength() * ambient->_delay;
}
}
}
}
}
} // End of namespace Toon

188
engines/toon/audio.h Normal file
View File

@@ -0,0 +1,188 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, MojoTouch has
* exclusively licensed this code on March 23th, 2024, to be used in
* closed-source products.
* Therefore, any contributions (commits) to it will also be dual-licensed.
*
*/
#ifndef TOON_AUDIO_H
#define TOON_AUDIO_H
#include "audio/audiostream.h"
#include "audio/mixer.h"
#include "toon/toon.h"
namespace Toon {
// used for music/voice/everything
class AudioManager;
class AudioStreamInstance : public Audio::AudioStream {
public:
AudioStreamInstance(AudioManager *man, Audio::Mixer *mixer, Common::SeekableReadStream *stream, bool looping = false, bool deleteFileStreamAtEnd = false);
~AudioStreamInstance() override;
void play(bool fade = false, Audio::Mixer::SoundType soundType = Audio::Mixer::kMusicSoundType);
void stop(bool fade = false);
bool isPlaying() {
return !_stopped;
}
bool isLooping() {
return _looping;
}
bool isFading() {
return _fadingIn || _fadingOut;
}
int32 getPlayedSampleCount() {
return _playedSamples;
}
void setVolume(int32 volume);
protected:
int readBuffer(int16 *buffer, const int numSamples) override;
bool isStereo() const override {
return false;
}
int getRate() const override {
return 22100;
}
bool endOfData() const override {
return _stopped;
}
void handleFade(int32 numSamples);
void stopNow();
bool readPacket();
void decodeADPCM(uint8 *comp, int16 *dest, int32 packetSize);
Common::SeekableReadStream *_file;
bool _fadingIn;
bool _fadingOut;
int32 _fadeTime;
uint8 *_compBuffer;
int16 *_buffer;
int32 _bufferSize;
int32 _bufferMaxSize;
int32 _bufferOffset;
int32 _compBufferSize;
Audio::SoundHandle _handle;
Audio::Mixer::SoundType _soundType;
Audio::Mixer *_mixer;
int32 _lastSample;
int32 _lastStepIndex;
bool _stopped;
AudioManager *_man;
int32 _totalSize;
int32 _currentReadSize;
bool _looping;
int32 _volume;
int32 _musicAttenuation;
bool _deleteFileStream;
int32 _playedSamples;
};
class AudioStreamPackage {
public:
AudioStreamPackage(ToonEngine *vm);
~AudioStreamPackage();
bool loadAudioPackage(const Common::Path &indexFile, const Common::Path &streamFile);
void getInfo(int32 id, int32 *offset, int32 *size);
Common::SeekableReadStream *getStream(int32 id, bool ownMemory = false);
protected:
Common::SeekableReadStream *_file;
uint32 *_indexBuffer;
ToonEngine *_vm;
};
struct AudioAmbientSFX {
int32 _id;
int32 _volume;
int32 _lastTimer;
int32 _delay;
int32 _mode;
int32 _channel;
bool _enabled;
};
class AudioManager {
public:
void removeInstance(AudioStreamInstance *inst); // called by destructor
AudioManager(ToonEngine *vm, Audio::Mixer *mixer);
~AudioManager();
bool voiceStillPlaying();
int playMusic(const Common::String &dir, const Common::String &music);
void playVoice(int32 id, bool genericVoice);
int32 playSFX(int32 id, int volume, bool genericSFX);
void stopCurrentVoice();
void stopAllSfxs();
void setMusicVolume(uint8 volume);
void stopMusicChannel(int channelId, bool fade);
void stopMusic(bool fade = true);
void muteVoice(bool mute);
void muteMusic(bool mute);
void muteSfx(bool mute);
bool isVoiceMuted() const { return _voiceMuted; }
bool isMusicMuted() const { return _musicMuted; }
bool isSfxMuted() const { return _sfxMuted; }
void startAmbientSFX(int32 id, int32 delay, int32 mode, int32 volume);
void killAmbientSFX(int32 id);
void killAllAmbientSFX();
void updateAmbientSFX();
void setAmbientSFXVolume(int32 id, int volume);
void closeAudioPack(int32 id);
bool loadAudioPack(int32 id, const Common::Path &indexFile, const Common::Path &packFile);
AudioStreamInstance *_channels[16]; // 0-1 : music
// 2 : voice
// 3-15 : SFX
AudioStreamPackage *_audioPacks[4]; // 0 : generic streams
// 1 : local streams
// 2 : generic SFX
// 3 : local SFX
int _currentMusicChannel;
Common::String _currentMusicName;
ToonEngine *_vm;
Audio::Mixer *_mixer;
protected:
bool _voiceMuted;
bool _musicMuted;
bool _sfxMuted;
AudioAmbientSFX _ambientSFXs[4];
};
} // End of namespace Toon
#endif

1433
engines/toon/character.cpp Normal file

File diff suppressed because it is too large Load Diff

156
engines/toon/character.h Normal file
View File

@@ -0,0 +1,156 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, MojoTouch has
* exclusively licensed this code on March 23th, 2024, to be used in
* closed-source products.
* Therefore, any contributions (commits) to it will also be dual-licensed.
*
*/
#ifndef TOON_CHARACTER_H
#define TOON_CHARACTER_H
#include "common/array.h"
#include "common/rect.h"
#include "toon/toon.h"
namespace Toon {
class ToonEngine;
struct SpecialCharacterAnimation {
char _filename[9]; // 0
byte _flag1; // 9
short _offsetX; // 10
short _offsetY; // 12
short _unused; // 14
short _unused2; // 16
byte _flags2; // 18
byte _flags3; // 19
byte _flags4; // 20
byte _flags5; // 21
byte _flags6; // 22
byte _flags7; // 23
byte _flags8; // 24
byte _flags9; // 25
};
class Character {
public:
Character(ToonEngine *vm);
virtual ~Character();
virtual void init();
virtual int32 getId();
virtual void setId(int32 id);
virtual void setFacing(int32 facing);
virtual void forceFacing(int32 facing);
virtual int32 getFacing();
virtual void setAnimScript(int32 animScriptId);
virtual void setSceneAnimationId(int32 sceneAnimationId);
virtual void setDefaultSpecialAnimationId(int32 defaultAnimationId);
virtual int32 getAnimScript();
virtual int32 getSceneAnimationId();
virtual void setFlag(int flag);
virtual int32 getFlag();
virtual int32 getAnimFlag();
virtual void setAnimFlag(int32 flag);
virtual void setPosition(int16 x, int16 y);
virtual void forcePosition(int16 x, int16 y);
virtual int16 getX();
virtual int16 getY();
virtual int16 getFinalX();
virtual int16 getFinalY();
virtual bool walkTo(int16 newPosX, int16 newPosY);
virtual bool getVisible();
virtual void setVisible(bool visible);
virtual bool loadWalkAnimation(const Common::String &animName);
virtual bool loadIdleAnimation(const Common::String &animName);
virtual bool loadTalkAnimation(const Common::String &animName);
virtual bool loadShadowAnimation(const Common::String &animName);
virtual bool setupPalette();
virtual void playStandingAnim();
virtual void playWalkAnim(int32 start, int32 end);
virtual void playTalkAnim();
virtual void playAnim(int32 animId, int32 unused, int32 flags);
virtual void update(int32 timeIncrement);
virtual int32 getScale();
virtual AnimationInstance *getAnimationInstance();
virtual void setAnimationInstance(AnimationInstance *instance);
virtual void save(Common::WriteStream *stream);
virtual void load(Common::ReadStream *stream, int32 saveGameVersion);
virtual void stopWalk();
virtual void stopSpecialAnim();
virtual void updateIdle();
virtual int32 getRandomIdleAnim() { return 0; }
virtual void updateTimers(int32 relativeAdd);
virtual void setTalking(bool talking) { _isTalking = talking; }
virtual bool isTalking() { return _isTalking; }
virtual void resetScale() {}
virtual void plotPath(Graphics::Surface& surface);
int32 getFacingFromDirection(int16 dx, int16 dy);
const SpecialCharacterAnimation *getSpecialAnimation(int32 characterId, int32 animationId);
protected:
ToonEngine *_vm;
int32 _id;
int32 _animScriptId;
int32 _animSpecialId;
int32 _animSpecialDefaultId;
int32 _sceneAnimationId;
int32 _lineToSayId;
int32 _time;
int16 _x;
int16 _y;
int32 _z;
int16 _finalX;
int16 _finalY;
int32 _facing;
int32 _flags;
int32 _animFlags;
int32 _scale;
int32 _nextIdleTime;
bool _visible;
bool _blockingWalk;
int32 _speed;
int32 _lastWalkTime;
int32 _numPixelToWalk;
bool _isTalking;
AnimationInstance *_animationInstance;
AnimationInstance *_shadowAnimationInstance;
Animation *_walkAnim;
Animation *_idleAnim;
Animation *_talkAnim;
Animation *_shadowAnim;
Animation *_specialAnim;
Common::Array<Common::Point> _currentPath;
uint32 _currentPathNode;
int32 _currentWalkStamp;
int32 _currentFacingStamp;
};
} // End of namespace Toon
#endif

View File

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

34
engines/toon/console.cpp Normal file
View File

@@ -0,0 +1,34 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "toon/console.h"
#include "toon/toon.h"
namespace Toon {
ToonConsole::ToonConsole(ToonEngine *vm) : GUI::Debugger(), _vm(vm) {
assert(_vm);
}
ToonConsole::~ToonConsole() {
}
} // End of namespace Toon

42
engines/toon/console.h Normal file
View File

@@ -0,0 +1,42 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef TOON_CONSOLE_H
#define TOON_CONSOLE_H
#include "gui/debugger.h"
namespace Toon {
class ToonEngine;
class ToonConsole : public GUI::Debugger {
public:
ToonConsole(ToonEngine *vm);
~ToonConsole(void) override;
private:
ToonEngine *_vm;
};
} // End of namespace Toon
#endif

View File

@@ -0,0 +1,51 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, MojoTouch has
* exclusively licensed this code on March 23th, 2024, to be used in
* closed-source products.
* Therefore, any contributions (commits) to it will also be dual-licensed.
*
*/
#include "toon/conversation.h"
namespace Toon {
void Conversation::save(Common::WriteStream *stream, int16 *conversationDataBase) {
stream->writeSint32BE(_enable);
for (int32 i = 0; i < 10; i++) {
stream->writeSint32BE(state[i]._data2);
stream->writeSint16BE(state[i]._data3);
stream->writeSint32BE((int16 *)state[i]._data4 - conversationDataBase);
}
}
void Conversation::load(Common::ReadStream *stream, int16 *conversationDataBase) {
_enable = stream->readSint32BE();
for (int32 i = 0; i < 10; i++) {
state[i]._data2 = stream->readSint32BE();
state[i]._data3 = stream->readSint16BE();
state[i]._data4 = conversationDataBase + stream->readSint32BE();
}
}
}

View File

@@ -0,0 +1,53 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, MojoTouch has
* exclusively licensed this code on March 23th, 2024, to be used in
* closed-source products.
* Therefore, any contributions (commits) to it will also be dual-licensed.
*
*/
#ifndef TOON_CONVERSATION_H
#define TOON_CONVERSATION_H
#include "engines/engine.h"
#include "common/stream.h"
namespace Toon {
class Conversation {
public:
int32 _enable; // 00
struct ConvState {
int32 _data2; // 04
int16 _data3; // 08
void *_data4; // 10
} state[10];
void save(Common::WriteStream *stream, int16 *conversationDataBase);
void load(Common::ReadStream *stream, int16 *conversationDataBase);
};
} // End of namespace Toon
#endif

3
engines/toon/credits.pl Normal file
View File

@@ -0,0 +1,3 @@
begin_section("Toon");
add_person("Sylvain Dupont", "SylvainTV", "");
end_section();

196
engines/toon/detection.cpp Normal file
View File

@@ -0,0 +1,196 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, MojoTouch has
* exclusively licensed this code on March 23th, 2024, to be used in
* closed-source products.
* Therefore, any contributions (commits) to it will also be dual-licensed.
*
*/
#include "engines/advancedDetector.h"
#include "base/plugins.h"
#include "toon/toon.h"
static const PlainGameDescriptor toonGames[] = {
{ "toon", "Toonstruck" },
{ 0, 0 }
};
static const DebugChannelDef debugFlagList[] = {
{Toon::kDebugAnim, "Anim", "Animation debug level"},
{Toon::kDebugCharacter, "Character", "Character debug level"},
{Toon::kDebugAudio, "Audio", "Audio debug level"},
{Toon::kDebugHotspot, "Hotspot", "Hotspot debug level"},
{Toon::kDebugFont, "Font", "Font debug level"},
{Toon::kDebugPath, "Path", "Path debug level"},
{Toon::kDebugMovie, "Movie", "Movie debug level"},
{Toon::kDebugPicture, "Picture", "Picture debug level"},
{Toon::kDebugResource, "Resource", "Resource debug level"},
{Toon::kDebugState, "State", "State debug level"},
{Toon::kDebugTools, "Tools", "Tools debug level"},
{Toon::kDebugText, "Text", "Text debug level"},
DEBUG_CHANNEL_END
};
namespace Toon {
static const ADGameDescription gameDescriptions[] = {
{
"toon", "",
{
{"local.pak", 0, "3290209ef9bc92692108dd2f45df0736", 3237611},
{"arcaddbl.svl", 0, "c418478cd2833c7c983799f948af41ac", 7844688},
{"study.svl", 0, "281efa3f33f6712c0f641a605f4d40fd", 2511090},
AD_LISTEND
},
Common::EN_ANY, Common::kPlatformDOS, ADGF_NO_FLAGS, GUIO1(GUIO_NOMIDI)
},
{
"toon", "",
{
{"local.pak", 0, "517132c3575b38806d1e7b6f59848072", 3224044},
{"arcaddbl.svl", 0, "ff74008827b62fbef1f46f104c438e44", 9699256},
{"study.svl", 0, "df056b94ea83f1ed92a539cf636053ab", 2542668},
AD_LISTEND
},
Common::FR_FRA, Common::kPlatformDOS, ADGF_NO_FLAGS, GUIO1(GUIO_NOMIDI)
},
{
"toon", "",
{
{"local.pak", 0, "bf5da4c03f78ffbd643f12122319366e", 3250841},
{"arcaddbl.svl", 0, "7a0d74f4d66d1c722b946abbeb0834ef", 9122249},
{"study.svl", 0, "72fe96a9e10967d3138e918295babc42", 2910283},
AD_LISTEND
},
Common::DE_DEU, Common::kPlatformDOS, ADGF_NO_FLAGS, GUIO1(GUIO_NOMIDI)
},
{
"toon", "",
{
{"local.pak", 0, "e8645168a247e2abdbfc2f9fa9d1c0fa", 3232222},
{"arcaddbl.svl", 0, "7893ac4cc78d51356baa058bbee7aa28", 8275016},
{"study.svl", 0, "b6b1ee2d9d94d53d305856039ab7bde7", 2634620},
AD_LISTEND
},
Common::ES_ESP, Common::kPlatformDOS, ADGF_NO_FLAGS, GUIO1(GUIO_NOMIDI)
},
{
"toon", "",
{
{"local.pak", 0, "48ec60709bebbdeff791d55ee18ec910", 3417846},
{"arcaddbl.svl", 0, "1d1b96e317e03ffd3874a8ebe59556f3", 6246232},
{"study.svl", 0, "d4aff126ee27be3c3d25e2996369d7cb", 2324368},
},
Common::RU_RUS, Common::kPlatformDOS, ADGF_NO_FLAGS, GUIO1(GUIO_NOMIDI)
},
{
"toon", "",
{
{"toonfont.caf", 0, "28f3210c901c86cd585d69eda3a2fd37", 30364},
{"local.pak", 0, "3290209ef9bc92692108dd2f45df0736", 3237611},
{"arcaddbl.svl", 0, "c418478cd2833c7c983799f948af41ac", 7844688},
{"study.svl", 0, "281efa3f33f6712c0f641a605f4d40fd", 2511090},
AD_LISTEND
},
Common::HE_ISR, Common::kPlatformDOS, ADGF_NO_FLAGS, GUIO1(GUIO_NOMIDI)
},
{
"toon", "Demo",
{
{"local.pak", 0, "bf5da4c03f78ffbd643f12122319366e", 3250841},
{"wacexdbl.emc", 0, "cfbc2156a31b294b038204888407ebc8", 6974},
{"generic.svl", 0, "5eb99850ada22f0b8cf6392262d4dd07", 9404599},
{"onetime.pak", 0, "b344aee5fd43b7438c322fb29f860205", 468667 },
AD_LISTEND
},
Common::DE_DEU, Common::kPlatformDOS, ADGF_DEMO, GUIO1(GUIO_NOMIDI)
},
{
"toon", "Demo",
{
{"local.pak", 0, "8ef3368078b9ea70b305c04db826feea", 2680573},
{"generic.svl", 0, "5c42724bb93b360dca7044d6b7ef26e5", 7739319},
AD_LISTEND
},
Common::EN_ANY, Common::kPlatformDOS, ADGF_DEMO, GUIO1(GUIO_NOMIDI)
},
{
// English 2-CD "Sold out" release
"toon", "",
{
{"local.pak", 0, "3290209ef9bc92692108dd2f45df0736", 3237611},
{"generic.svl", 0, "331eead1d20af7ee809a9e2f35b8362f", 6945180},
AD_LISTEND
},
Common::EN_ANY, Common::kPlatformDOS, ADGF_NO_FLAGS, GUIO1(GUIO_NOMIDI)
},
AD_TABLE_END_MARKER
};
static const ADFileBasedFallback fileBasedFallback[] = {
{ &gameDescriptions[0], { "local.pak", "arcaddbl.svl", "study.svl", 0 } }, // default to english version
{ 0, { 0 } }
};
} // End of namespace Toon
static const char * const directoryGlobs[] = {
"misc",
"act1",
"arcaddbl",
"act2",
"study",
0
};
class ToonMetaEngineDetection : public AdvancedMetaEngineDetection<ADGameDescription> {
public:
ToonMetaEngineDetection() : AdvancedMetaEngineDetection(Toon::gameDescriptions, toonGames) {
_maxScanDepth = 3;
_directoryGlobs = directoryGlobs;
}
ADDetectedGame fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist, ADDetectedGameExtraInfo **extra) const override {
return detectGameFilebased(allFiles, Toon::fileBasedFallback);
}
const char *getName() const override {
return "toon";
}
const char *getEngineName() const override {
return "Toonstruck";
}
const char *getOriginalCopyright() const override {
return "Toonstruck (C) 1996 Virgin Interactive";
}
const DebugChannelDef *getDebugChannels() const override {
return debugFlagList;
}
};
REGISTER_PLUGIN_STATIC(TOON_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, ToonMetaEngineDetection);

139
engines/toon/drew.cpp Normal file
View File

@@ -0,0 +1,139 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, MojoTouch has
* exclusively licensed this code on March 23th, 2024, to be used in
* closed-source products.
* Therefore, any contributions (commits) to it will also be dual-licensed.
*
*/
#include "common/debug.h"
#include "toon/drew.h"
namespace Toon {
CharacterDrew::CharacterDrew(ToonEngine *vm) : Character(vm) {
_id = 0;
_blockingWalk = true;
_animationInstance = vm->getAnimationManager()->createNewInstance(kAnimationCharacter);
_animationInstance->setUseMask(true);
vm->getAnimationManager()->addInstance(_animationInstance);
_currentScale = 1024;
}
CharacterDrew::~CharacterDrew() {
}
bool CharacterDrew::setupPalette() {
debugC(1, kDebugCharacter, "setupPalette()");
if (_walkAnim != nullptr) {
_walkAnim->applyPalette(129, 129 * 3, 63);
return true;
}
return false;
}
void CharacterDrew::setPosition(int16 x, int16 y) {
debugC(5, kDebugCharacter, "setPosition(%d, %d)", x, y);
_z = _vm->getLayerAtPoint(x, y);
int newScale = _vm->getScaleAtPoint(x, y);
if (newScale > 0)
_scale = newScale;
// work out position and scale of the character sprite
int32 width = _walkAnim->getWidth() * _scale / 1024;
int32 height = 210 * _scale / 1024;
_animationInstance->setPosition(x - width / 2, y - height, _z , false);
_animationInstance->setScale(_scale);
// work out position and scale of the shadow below character
int32 shadowWidth = _shadowAnim->getWidth() * _scale / 1024;
int32 shadowHeight = _shadowAnim->getHeight() * _scale / 1024;
_shadowAnimationInstance->setPosition(x - shadowWidth / 2, y - shadowHeight / 2 - 4 , _z , false);
_shadowAnimationInstance->setScale(_scale);
_x = x;
_y = y;
_animationInstance->setLayerZ(_y);
}
void CharacterDrew::playStandingAnim() {
debugC(4, kDebugCharacter, "playStandingAnim()");
stopSpecialAnim();
_animationInstance->setAnimation(_walkAnim);
int standingFrames = _vm->isEnglishDemo() ? 3 : 2;
_animationInstance->setFrame(_facing * standingFrames);
_shadowAnimationInstance->setFrame(_facing);
_animationInstance->setAnimationRange(_facing * standingFrames, _facing * standingFrames);
_animationInstance->stopAnimation();
_animationInstance->setLooping(true);
//setVisible(true);
}
void CharacterDrew::playWalkAnim(int32 start, int32 end) {
debugC(4, kDebugCharacter, "playWalkAnim(%d, %d)", start, end);
stopSpecialAnim();
_animationInstance->setAnimation(_walkAnim);
_shadowAnimationInstance->setFrame(_facing);
int walkAnimOffset = _vm->isEnglishDemo() ? 24 : 16;
_animationInstance->setAnimationRange(walkAnimOffset + _facing * 14, walkAnimOffset + _facing * 14 + 13);
_animationInstance->playAnimation();
_animationInstance->setFps(16);
_animationInstance->setLooping(true);
//setVisible(true);
}
void CharacterDrew::update(int32 timeIncrement) {
debugC(5, kDebugCharacter, "update(%d)", timeIncrement);
Character::update(timeIncrement);
if (_currentScale > _scale) {
_scale += timeIncrement * 2;
if (_scale > _currentScale)
_scale = _currentScale;
} else if (_currentScale < _scale) {
_scale -= timeIncrement * 2;
if (_scale < _currentScale)
_scale = _currentScale;
}
setPosition(_x, _y);
}
int32 CharacterDrew::getRandomIdleAnim() {
debugC(3, kDebugCharacter, "getRandomIdleAnim()");
static const int32 idle[] = { 6, 9, 10, 11, 12 };
return idle[_vm->randRange(0, 4)];
}
void CharacterDrew::resetScale()
{
_scale = _currentScale;
setPosition(_x, _y);
}
} // End of namespace Toon

55
engines/toon/drew.h Normal file
View File

@@ -0,0 +1,55 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, MojoTouch has
* exclusively licensed this code on March 23th, 2024, to be used in
* closed-source products.
* Therefore, any contributions (commits) to it will also be dual-licensed.
*
*/
#ifndef TOON_DREW_H
#define TOON_DREW_H
#include "toon/character.h"
namespace Toon {
class ToonEngine;
class CharacterDrew : public Character {
public:
CharacterDrew(ToonEngine *vm);
~CharacterDrew() override;
bool setupPalette() override;
void playStandingAnim() override;
void setPosition(int16 x, int16 y) override;
void resetScale() override;
void update(int32 timeIncrement) override;
void playWalkAnim(int32 start, int32 end) override;
int32 getRandomIdleAnim() override;
protected:
int32 _currentScale;
};
} // End of namespace Toon
#endif

145
engines/toon/flux.cpp Normal file
View File

@@ -0,0 +1,145 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, MojoTouch has
* exclusively licensed this code on March 23th, 2024, to be used in
* closed-source products.
* Therefore, any contributions (commits) to it will also be dual-licensed.
*
*/
#include "common/debug.h"
#include "toon/flux.h"
namespace Toon {
CharacterFlux::CharacterFlux(ToonEngine *vm) : Character(vm) {
_id = 1;
_animationInstance = vm->getAnimationManager()->createNewInstance(kAnimationCharacter);
_animationInstance->setUseMask(true);
vm->getAnimationManager()->addInstance(_animationInstance);
}
CharacterFlux::~CharacterFlux() {
}
void CharacterFlux::playStandingAnim() {
debugC(4, kDebugCharacter, "playStandingAnim()");
_animationInstance->setAnimation(_walkAnim);
_animationInstance->setFrame(_facing * 3);
_animationInstance->setAnimationRange(_facing * 3, _facing * 3);
_animationInstance->stopAnimation();
_animationInstance->setLooping(true);
//setVisible(true);
}
void CharacterFlux::setVisible(bool visible) {
if (_vm->state()->_currentChapter == 2) {
Character::setVisible(false);
} else {
Character::setVisible(visible);
}
}
void CharacterFlux::playWalkAnim(int32 start, int32 end) {
debugC(4, kDebugCharacter, "playWalkAnim(%d, %d)", start, end);
_animationInstance->setAnimation(_walkAnim);
_animationInstance->setAnimationRange(24 + _facing * 10, 24 + _facing * 10 + 9);
_animationInstance->playAnimation();
_animationInstance->setFps(16);
_animationInstance->setLooping(true);
}
int32 CharacterFlux::fixFacingForAnimation(int32 originalFacing, int32 animationId) {
static const byte fixFluxAnimationFacing[] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xee, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x55,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
byte animFacingFlag = fixFluxAnimationFacing[animationId];
int32 v5 = 1 << originalFacing;
int32 v6 = 1 << originalFacing;
int32 facingMask = 0;
do {
if (v6 & animFacingFlag) {
facingMask = v6;
} else if (v5 & animFacingFlag) {
facingMask = v5;
}
v5 >>= 1;
v6 <<= 1;
} while (!facingMask);
int32 finalFacing = 0;
for (finalFacing = 0; ; ++finalFacing) {
facingMask >>= 1;
if (!facingMask)
break;
}
return finalFacing;
}
void CharacterFlux::setPosition(int16 x, int16 y) {
debugC(5, kDebugCharacter, "setPosition(%d, %d)", x, y);
_z = _vm->getLayerAtPoint(x, y);
_scale = _vm->getScaleAtPoint(x, y);
int32 width = _walkAnim->getWidth() * _scale / 1024;
int32 height = 165 * _scale / 1024;
_animationInstance->setPosition(x - width / 2, y - height, _z , false);
_animationInstance->setScale(_scale);
// in original code, flux shadow scale is 3/4 of real scale
int32 shadowScale = _scale * 3 / 4;
// work out position and scale of the shadow below character
int32 shadowWidth = _shadowAnim->getWidth() * shadowScale / 1024;
int32 shadowHeight = _shadowAnim->getHeight() * shadowScale / 1024;
_shadowAnimationInstance->setPosition(x - shadowWidth / 2, y - shadowHeight / 2 , _z , false);
_shadowAnimationInstance->setScale(shadowScale);
_x = x;
_y = y;
_finalX = x;
_finalY = y;
_animationInstance->setLayerZ(_y);
}
void CharacterFlux::update(int32 timeIncrement) {
debugC(5, kDebugCharacter, "update(%d)", timeIncrement);
Character::update(timeIncrement);
setPosition(_x, _y);
}
int32 CharacterFlux::getRandomIdleAnim() {
debugC(3, kDebugCharacter, "getRandomIdleAnim()");
// English demo only has part of the idle animations (the first 3 entries).
static const int32 idle[] = { 0xf, 0x21, 0x22, 0xe, 0x24, 0x25, 0x27 };
return idle[_vm->randRange(0, _vm->isEnglishDemo() ? 2 : 6)];
}
} // End of namespace Toon

54
engines/toon/flux.h Normal file
View File

@@ -0,0 +1,54 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, MojoTouch has
* exclusively licensed this code on March 23th, 2024, to be used in
* closed-source products.
* Therefore, any contributions (commits) to it will also be dual-licensed.
*
*/
#ifndef TOON_FLUX_H
#define TOON_FLUX_H
#include "toon/character.h"
class ToonEngine;
namespace Toon {
class CharacterFlux : public Character {
public:
CharacterFlux(ToonEngine *vm);
~CharacterFlux() override;
void setPosition(int16 x, int16 y) override;
void playStandingAnim() override;
void playWalkAnim(int32 start, int32 end) override;
void update(int32 timeIncrement) override;
int32 getRandomIdleAnim() override;
void setVisible(bool visible) override;
static int32 fixFacingForAnimation(int32 originalFacing, int32 animationId);
};
} // End of namespace Toon
#endif

512
engines/toon/font.cpp Normal file
View File

@@ -0,0 +1,512 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, MojoTouch has
* exclusively licensed this code on March 23th, 2024, to be used in
* closed-source products.
* Therefore, any contributions (commits) to it will also be dual-licensed.
*
*/
#include "common/debug.h"
#include "common/rect.h"
#include "common/unicode-bidi.h"
#include "toon/font.h"
namespace Toon {
FontRenderer::FontRenderer(ToonEngine *vm) : _vm(vm) {
_currentFontColor[0] = 0;
_currentFontColor[1] = 0xc8;
_currentFontColor[2] = 0xcb;
_currentFontColor[3] = 0xce;
_currentFont = nullptr;
_currentDemoFont = nullptr;
}
FontRenderer::~FontRenderer() {
if (_currentDemoFont)
delete _currentDemoFont;
}
// mapping extended characters required for foreign versions to font (animation)
static const byte map_textToFont[0x80] = {
'?', '?', '?', '?', 0x03, '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', // 0x8x
'?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', // 0x9x
'?', 0x09, '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', // 0xAx
'?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', 0x0a, // 0xBx
'?', '?', '?', '?', 0x1d, '?', '?', 0x02, '?', '?', '?', '?', '?', '?', '?', '?', // 0xCx
'?', 0x0b, '?', '?', '?', '?', 0x1e, '?', '?', '?', '?', 0x20, 0x1f, '?', '?', 0x19, // 0xDx
0x0d, 0x04, 0x0e, '?', 0x1a, '?', '?', 0x18, 0x10, 0x0f, 0x12, 0x11, 0x09, 0x05, 0x14, 0x13, // 0xEx
0x23, 0x08, 0x23, 0x06, 0x15, 0x23, 0x1b, 0x23, 0x23, 0x16, 0x07, 0x17, 0x1c, 0x23, 0x23, 0x23 // 0xFx
};
static const byte hebrew_map_textToFont[0x80] = {
'?', '?', '?', '?', 0x03, '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', // 0x8x
'?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', // 0x9x
'?', 0x09, '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', // 0xAx
'?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', 0x0a, // 0xBx
'?', '?', '?', '?', 0x1d, '?', '?', 0x02, '?', '?', '?', '?', '?', '?', '?', '?', // 0xCx
'?', 0x0b, '?', '?', '?', '?', 0x1e, '?', '?', '?', '?', 0x20, 0x1f, '?', '?', 0x19, // 0xDx
0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, // 0xEx
0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x17, 0x1c, 0x23, 0x23, 0x23 // 0xFx
};
byte FontRenderer::textToFont(byte c) {
// No need to remap simple characters.
if (c < 0x80)
return c;
// The Spanish version shows grave accent over the 'e' when it should
// be acute. This happens both in the original interpreter and when
// using the common map which works for other languages, so we add a
// special case for it.
if (_vm->_language == Common::ES_ESP && c == 0xe9)
return 0x10;
if (_vm->_language == Common::HE_ISR)
return hebrew_map_textToFont[c - 0x80];
// Use the common map to convert the extended characters.
return map_textToFont[c - 0x80];
}
void FontRenderer::renderText(int16 x, int16 y, const Common::String &origText, int32 mode) {
debugC(5, kDebugFont, "renderText(%d, %d, %s, %d)", x, y, origText.c_str(), mode);
int16 xx, yy;
computeSize(origText, &xx, &yy);
if (mode & 2) {
y -= yy / 2;
} else if (mode & 4) {
y -= yy;
}
if (mode & 1) {
x -= xx / 2;
}
_vm->addDirtyRect(x, y, x + xx, y + yy);
int16 curX = x;
int16 curY = y;
int32 height = 0;
const byte *text = (const byte *)origText.c_str();
if (_vm->_language == Common::HE_ISR)
text = (const byte *)Common::convertBiDiString((const char *) text, Common::kWindows1255).c_str();
while (*text) {
byte curChar = *text;
if (curChar == 13) {
curY = curY + height;
height = 0;
curX = x;
} else {
curChar = textToFont(curChar);
if (_currentFont) {
_currentFont->drawFontFrame(_vm->getMainSurface(), curChar, curX, curY, _currentFontColor);
} else {
_currentDemoFont->drawGlyph(_vm->getMainSurface(), curChar, curX, curY, _currentFontColor);
}
curX = curX + MAX<int32>((_currentFont ? _currentFont->getFrameWidth(curChar) :
_currentDemoFont->getGlyphWidth(curChar)) - 2, 0);
height = MAX<int32>(height, _currentFont ? _currentFont->getFrameHeight(curChar) :
_currentDemoFont->getHeight());
}
text++;
}
}
void FontRenderer::computeSize(const Common::String &origText, int16 *retX, int16 *retY) {
debugC(4, kDebugFont, "computeSize(%s, retX, retY)", origText.c_str());
int16 lineWidth = 0;
int16 lineHeight = 0;
int16 totalHeight = 0;
int16 totalWidth = 0;
int16 lastLineHeight = 0;
const byte *text = (const byte *)origText.c_str();
while (*text) {
byte curChar = *text;
if (curChar == 13) {
totalWidth = MAX(totalWidth, lineWidth);
totalHeight += lineHeight;
lineHeight = 0;
lineWidth = 0;
lastLineHeight = 0;
} else if (curChar < 32) {
text++;
continue;
} else {
curChar = textToFont(curChar);
int16 charWidth = (_currentFont ? _currentFont->getFrameWidth(curChar) :
_currentDemoFont->getGlyphWidth(curChar)) - 1;
int16 charHeight = _currentFont ? _currentFont->getFrameHeight(curChar) :
_currentDemoFont->getHeight();
lineWidth += charWidth;
lineHeight = MAX(lineHeight, charHeight);
// The character may be offset, so the height doesn't
// really tell how far it will stick out. For now,
// assume we only need to take the lower bound into
// consideration.
//Common::Rect charRect = _currentFont->getFrameRect(curChar);
lastLineHeight = MAX(lastLineHeight, _currentFont ? _currentFont->getHeight() :
(int16)_currentDemoFont->getHeight());
}
text++;
}
totalHeight += lastLineHeight;
totalWidth = MAX(totalWidth, lineWidth);
*retX = totalWidth;
*retY = totalHeight;
}
void FontRenderer::setFont(Animation *font) {
debugC(5, kDebugFont, "setFont(font)");
_currentFont = font;
}
void FontRenderer::setFontColorByCharacter(int32 characterId) {
debugC(5, kDebugFont, "setFontColorByCharacter(%d)", characterId);
// unfortunately this table was hardcoded in the original executable
static const byte colorsByCharacters[] = {
0xe0, 0xdc, 0xc8, 0xd6, 0xc1, 0xc8, 0xe9, 0xde, 0xc8, 0xeb, 0xe8, 0xc8,
0xd1, 0xcf, 0xc8, 0xdb, 0xd5, 0xc8, 0xfb, 0xfa, 0xc8, 0xd9, 0xd7, 0xc8,
0xe8, 0xe4, 0xc8, 0xe9, 0xfa, 0xc8, 0xeb, 0xe4, 0xc8, 0xeb, 0xe4, 0xc8,
0xd2, 0xea, 0xc8, 0xd3, 0xd0, 0xc8, 0xe1, 0xdd, 0xc8, 0xd9, 0xd7, 0xc8,
0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8,
0xd2, 0xcf, 0xc8, 0xd1, 0xcf, 0xc8, 0xd9, 0xd7, 0xc8, 0xe3, 0xdd, 0xc8,
0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8,
0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8,
0xd9, 0xd7, 0xc8, 0xe6, 0xe4, 0xc8, 0xd9, 0xd7, 0xc8, 0xcd, 0xca, 0xc8,
0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xeb, 0xe8, 0xc8, 0xdb, 0xd5, 0xc8,
0xe0, 0xdc, 0xc8, 0xd6, 0xc1, 0xc8, 0xd3, 0xd0, 0xc8, 0xd1, 0xcf, 0xc8,
0xe6, 0xe4, 0xc8, 0xd1, 0xcf, 0xc8, 0xd2, 0xcf, 0xc8, 0xcc, 0xcb, 0xc8,
0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8,
0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8,
0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8, 0xd9, 0xd7, 0xc8
};
setFontColor(colorsByCharacters[characterId * 3 + 2], colorsByCharacters[characterId * 3 + 1], colorsByCharacters[characterId * 3 + 0]);
}
void FontRenderer::setFontColor(int32 fontColor1, int32 fontColor2, int32 fontColor3) {
debugC(5, kDebugFont, "setFontColor(%d, %d, %d)", fontColor1, fontColor2, fontColor3);
// The English demo uses a different palette assignment.
_currentFontColor[0] = 0;
_currentFontColor[1] = _currentFont ? fontColor1 : fontColor3;
_currentFontColor[2] = _currentFont ? fontColor2 : fontColor1;
_currentFontColor[3] = _currentFont ? fontColor3 : fontColor2;
}
void FontRenderer::renderMultiLineText(int16 x, int16 y, const Common::String &origText, int32 mode, Graphics::Surface &frame) {
debugC(5, kDebugFont, "renderMultiLineText(%d, %d, %s, %d)", x, y, origText.c_str(), mode);
// divide the text in several lines
// based on number of characters or size of lines.
byte text[1024];
memset(text, 0, 1024);
Common::strlcpy((char *)text, origText.c_str(), 1024);
byte *lines[16];
memset(lines, 0, 16 * sizeof(byte *));
int32 lineSize[16];
memset(lineSize, 0, 16 * sizeof(int32));
int32 numLines = 0;
byte *it = text;
int16 maxWidth = 0;
int16 curWidth = 0;
while (true) {
byte *lastLine = it;
byte *lastSpace = it;
int32 lastSpaceX = 0;
int32 curLetterNr = 0;
curWidth = 0;
while (*it && curLetterNr < 50 && curWidth < 580) {
byte curChar = *it;
if (curChar == 32) {
lastSpace = it;
lastSpaceX = curWidth;
} else
curChar = textToFont(curChar);
int width = (_currentFont ? _currentFont->getFrameWidth(curChar) :
_currentDemoFont->getGlyphWidth(curChar)) - 2;
curWidth += MAX(width, 0);
it++;
curLetterNr++;
}
if (*lastLine == 0)
break;
lines[numLines] = lastLine;
if (*it == 0)
lineSize[numLines] = curWidth;
else
lineSize[numLines] = lastSpaceX;
if (lineSize[numLines] > maxWidth)
maxWidth = lineSize[numLines];
lastLine = lastSpace + 1;
numLines++;
if (*it == 0)
break;
it = lastLine;
*lastSpace = 0;
if (numLines >= 16)
break;
}
if (curWidth > maxWidth) {
maxWidth = curWidth;
}
//numLines++;
// get font height (assumed to be constant)
int16 height = _currentFont ? _currentFont->getHeight() : _currentDemoFont->getHeight();
int32 textSize = (height - 2) * numLines;
y = y - textSize;
if (y < 30)
y = 30;
if (y + textSize > 370)
y = 370 - textSize;
x -= _vm->state()->_currentScrollValue;
// adapt x
if (x - 30 - maxWidth / 2 < 0)
x = maxWidth / 2 + 30;
if (x + 30 + (maxWidth / 2) > TOON_SCREEN_WIDTH)
x = TOON_SCREEN_WIDTH - (maxWidth / 2) - 30;
// we have good coordinates now, we can render the multi line
int16 curX = x;
int16 curY = y;
for (int32 i = 0; i < numLines; i++) {
const byte *line = lines[i];
if (_vm->_language == Common::HE_ISR)
line = (const byte *)Common::convertBiDiString((const char *) line, Common::kWindows1255).c_str();
curX = x - lineSize[i] / 2;
_vm->addDirtyRect(curX + _vm->state()->_currentScrollValue, curY, curX + lineSize[i] + _vm->state()->_currentScrollValue + 2, curY + height);
while (*line) {
byte curChar = textToFont(*line);
if (curChar != 32) {
if (_currentFont) {
_currentFont->drawFontFrame(frame, curChar, curX + _vm->state()->_currentScrollValue, curY, _currentFontColor);
} else {
_currentDemoFont->drawGlyph(frame, curChar, curX + _vm->state()->_currentScrollValue, curY, _currentFontColor);
}
}
curX = curX + MAX<int32>((_currentFont ? _currentFont->getFrameWidth(curChar) : _currentDemoFont->getGlyphWidth(curChar)) - 2, 0);
//height = MAX(height, _currentFont->getFrameHeight(curChar));
line++;
}
curY += height;
}
}
bool FontRenderer::loadDemoFont(const Common::Path &filename) {
uint32 fileSize = 0;
uint8 *fileData = _vm->resources()->getFileData(filename, &fileSize);
if (!fileData)
return false;
uint16 dataSize = READ_LE_UINT16(fileData);
if (dataSize != fileSize)
return false;
// Offsets of the various parts of the font.
uint16 fontDataOffset = READ_LE_UINT16(fileData + 4);
uint16 glyphOffsetTableOffset = READ_LE_UINT16(fileData + 6);
uint16 glyphWidthDataOffset = READ_LE_UINT16(fileData + 8);
uint16 glyphDataOffset = READ_LE_UINT16(fileData + 10);
uint16 glyphHeightDataOffset = READ_LE_UINT16(fileData + 12);
// Generic font data.
uint8 numGlyphs = *(fileData + fontDataOffset + 3);
uint8 glyphWidth = *(fileData + fontDataOffset + 4);
uint8 glyphHeight = *(fileData + fontDataOffset + 5);
if (_currentDemoFont)
delete _currentDemoFont;
_currentDemoFont = new DemoFont(glyphWidth, glyphHeight, numGlyphs);
// Copy the data for each glyph to the DemoFont.
for (int i = 0; i < numGlyphs; i++) {
// Read glyph dimensions.
GlyphDimensions dimensions;
dimensions.width = *(fileData + glyphWidthDataOffset + i);
dimensions.heightOffset = *(fileData + glyphHeightDataOffset + (2 * i));
dimensions.height = *(fileData + glyphHeightDataOffset + (2 * i) + 1);
_currentDemoFont->setGlyphDimensions(i, dimensions);
// Determine start of data for this glyph.
uint16 currentGlyphDataOffset = READ_LE_UINT16(fileData + glyphOffsetTableOffset + (i * 2));
assert(currentGlyphDataOffset >= glyphDataOffset);
// Get pointers for the file and DemoFont data for this glyph.
uint8 *srcGlyphDataPtr = fileData + currentGlyphDataOffset;
uint8 *dstGlyphDataPtr = _currentDemoFont->getGlyphData(i);
// Pad the glyph data with zeroes at the start for the height offset.
uint16 offsetBytes = dimensions.heightOffset * dimensions.width;
memset(dstGlyphDataPtr, 0, offsetBytes);
dstGlyphDataPtr += offsetBytes;
// Read each line of pixels.
for (int j = 0; j < dimensions.height; j++) {
// Each nibble has data for one pixel, so alternately read the low
// and high nibble. If the number of pixels in a line is odd, one
// nibble is discarded.
bool lowNibble = true;
uint8 glyphByte = 0;
// Read each pixel in this line.
for (int k = 0; k < dimensions.width; k++) {
if (lowNibble) {
// Copy the data byte.
glyphByte = *srcGlyphDataPtr++;
// Read the low nibble.
*dstGlyphDataPtr++ = glyphByte & 0xF;
} else {
// Read the high nibble.
*dstGlyphDataPtr++ = glyphByte >> 4;
}
// Switch to the other nibble.
lowNibble = !lowNibble;
}
}
}
return true;
}
DemoFont::DemoFont(uint8 glyphWidth, uint8 glyphHeight, uint16 numGlyphs) {
_glyphWidth = glyphWidth;
_glyphHeight = glyphHeight;
_numGlyphs = numGlyphs;
// Allocate room for the full height and width for each glyph. A glyph
// might not need this if the width is less, but it makes lookup easier.
_glyphData = new uint8[_numGlyphs * _glyphWidth * _glyphHeight];
_glyphDimensions = new GlyphDimensions[_numGlyphs];
}
DemoFont::~DemoFont() {
delete[] _glyphData;
delete[] _glyphDimensions;
}
uint8 *DemoFont::getGlyphData() {
return _glyphData;
}
uint8 *DemoFont::getGlyphData(uint8 glyphNum) {
assert(glyphNum < _numGlyphs);
return _glyphData + (glyphNum * _glyphWidth * _glyphHeight);
}
uint8 DemoFont::getGlyphWidth(uint8 glyphNum) {
assert(glyphNum < _numGlyphs);
return _glyphDimensions[glyphNum].width;
}
uint8 DemoFont::getHeight() {
return _glyphHeight;
}
void DemoFont::setGlyphDimensions(uint8 glyphNum, GlyphDimensions &glyphOffsets) {
assert(glyphNum < _numGlyphs);
_glyphDimensions[glyphNum] = glyphOffsets;
}
void DemoFont::drawGlyph(Graphics::Surface &surface, int32 glyphNum, int16 xx, int16 yy, byte *colorMap) {
debugC(4, kDebugFont, "drawGlyph(surface, %d, %d, %d, colorMap)", glyphNum, xx, yy);
// Clip character into range.
if (glyphNum < 0)
glyphNum = 0;
if (glyphNum >= _numGlyphs)
glyphNum = _numGlyphs - 1;
if (_numGlyphs == 0)
return;
int16 rectX = getGlyphWidth(glyphNum);
int16 rectY = getHeight();
// Clip glyph dimensions to surface.
if (rectX + xx >= surface.w)
rectX = surface.w - xx;
if (rectX < 0)
return;
if (rectY + yy >= surface.h)
rectY = surface.h - yy;
if (rectY < 0)
return;
// Copy the glyph data to the surface.
int32 destPitch = surface.pitch;
uint8 *c = getGlyphData(glyphNum);
uint8 *curRow = (uint8 *)surface.getBasePtr(xx, yy);
for (int16 y = 0; y < rectY; y++) {
unsigned char *cur = curRow;
for (int16 x = 0; x < rectX; x++) {
if (*c && *c < 4)
*cur = colorMap[*c];
c++;
cur++;
}
curRow += destPitch;
}
}
} // End of namespace Toon

96
engines/toon/font.h Normal file
View File

@@ -0,0 +1,96 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, MojoTouch has
* exclusively licensed this code on March 23th, 2024, to be used in
* closed-source products.
* Therefore, any contributions (commits) to it will also be dual-licensed.
*
*/
#ifndef TOON_FONT_H
#define TOON_FONT_H
#include "toon/toon.h"
namespace Toon {
class DemoFont;
class FontRenderer {
public:
FontRenderer(ToonEngine *vm);
~FontRenderer();
void setFont(Animation *font);
bool loadDemoFont(const Common::Path &filename);
void computeSize(const Common::String &origText, int16 *retX, int16 *retY);
void renderText(int16 x, int16 y, const Common::String &origText, int32 mode);
void renderMultiLineText(int16 x, int16 y, const Common::String &origText, int32 mode, Graphics::Surface &frame);
void setFontColorByCharacter(int32 characterId);
void setFontColor(int32 fontColor1, int32 fontColor2, int32 fontColor3);
protected:
Animation *_currentFont;
DemoFont *_currentDemoFont;
ToonEngine *_vm;
byte _currentFontColor[4];
byte textToFont(byte c);
};
struct GlyphDimensions {
uint8 width;
uint8 heightOffset; // # lines from top
uint8 height;
GlyphDimensions() {
width = 0;
heightOffset = 0;
height = 0;
}
};
// The font format used by the English demo.
class DemoFont {
public:
DemoFont(uint8 glyphWidth, uint8 glyphHeight, uint16 numGlyphs);
~DemoFont();
uint8 *getGlyphData();
uint8 *getGlyphData(uint8 glyphNum);
uint8 getGlyphWidth(uint8 glyphNum);
uint8 getHeight();
void setGlyphDimensions(uint8 glyphNum, GlyphDimensions &glyphDimensions);
void drawGlyph(Graphics::Surface &surface, int32 glyphNum, int16 xx, int16 yy, byte *colorMap);
protected:
uint16 _numGlyphs;
uint8 _glyphWidth;
uint8 _glyphHeight;
uint8 *_glyphData;
GlyphDimensions *_glyphDimensions;
};
} // End of namespace Toon
#endif

159
engines/toon/hotspot.cpp Normal file
View File

@@ -0,0 +1,159 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, MojoTouch has
* exclusively licensed this code on March 23th, 2024, to be used in
* closed-source products.
* Therefore, any contributions (commits) to it will also be dual-licensed.
*
*/
#include "common/debug.h"
#include "common/compression/rnc_deco.h"
#include "toon/hotspot.h"
namespace Toon {
Hotspots::Hotspots(ToonEngine *vm) : _vm(vm) {
_items = nullptr;
_numItems = 0;
}
Hotspots::~Hotspots() {
delete[] _items;
}
void Hotspots::load(Common::ReadStream *Stream) {
delete[] _items;
_numItems = Stream->readSint16BE();
_items = new HotspotData[_numItems];
for (int32 i = 0; i < _numItems; i++) {
for (int32 a = 0; a < 256; a++)
_items[i].setData(a, Stream->readSint16BE());
}
}
void Hotspots::save(Common::WriteStream *Stream) {
Stream->writeSint16BE(_numItems);
for (int32 i = 0; i < _numItems; i++) {
for (int32 a = 0; a < 256; a++)
Stream->writeSint16BE(_items[i].getData(a));
}
}
int32 Hotspots::findBasedOnCorner(int16 x, int16 y) {
debugC(1, kDebugHotspot, "findBasedOnCorner(%d, %d)", x, y);
for (int32 i = 0; i < _numItems; i++) {
if (x == _items[i].getX1()) {
if (y == _items[i].getY1()) {
if (_items[i].getMode() == -1)
return _items[i].getRef();
return i;
}
}
}
return -1;
}
int32 Hotspots::find(int16 x, int16 y) {
debugC(6, kDebugHotspot, "find(%d, %d)", x, y);
int32 priority = -1;
int32 foundId = -1;
int32 testId = -1;
for (int i = 0; i < _numItems; i++) {
if (x >= _items[i].getX1() && x <= _items[i].getX2() && y >= _items[i].getY1() && y <= _items[i].getY2()) {
if (_items[i].getMode() == -1)
testId = _items[i].getRef();
else
testId = i;
if (_items[testId].getPriority() > priority) {
foundId = testId;
priority = _items[testId].getPriority();
}
}
}
return foundId;
}
bool Hotspots::loadRif(const Common::Path &rifName, const Common::Path &additionalRifName) {
debugC(1, kDebugHotspot, "loadRif(%s, %s)", rifName.toString().c_str(), additionalRifName.toString().c_str());
uint32 size = 0;
uint8 *rifData = _vm->resources()->getFileData(rifName, &size);
if (!rifData)
return false;
uint32 size2 = 0;
uint8 *rifData2 = 0;
// English demo seems to have some invalid additional Rif data so do not load it
if (!_vm->isEnglishDemo() && !additionalRifName.empty())
rifData2 = _vm->resources()->getFileData(additionalRifName, &size2);
// figure out the number of hotspots based on file size
int32 rifsize = READ_BE_UINT32(&rifData[4]);
int32 rifsize2 = 0;
if (size2)
rifsize2 = READ_BE_UINT32(&rifData2[4]);
_numItems = (rifsize + rifsize2) / 512;
delete[] _items;
_items = new HotspotData[_numItems];
// RIFs are compressed in RNC1
Common::RncDecoder decoder;
decoder.unpackM1(rifData, size, _items);
if (rifsize2) {
Common::RncDecoder decoder2;
decoder2.unpackM1(rifData2 , size2, _items + (rifsize >> 9));
for (int32 i = 0; i < (rifsize2 >> 9); i++) {
HotspotData *hot = _items + (rifsize >> 9) + i;
hot->setData(0, hot->getX1() + 1280);
hot->setData(2, hot->getX2() + 1280);
if (hot->getMode() == -1)
hot->setData(5, hot->getRef() + (rifsize >> 9));
}
}
return true;
}
HotspotData *Hotspots::get(int32 id) {
debugC(5, kDebugHotspot, "get(%d)", id);
if (id < 0 || id >= _numItems)
return 0;
else
return &_items[id];
}
} // End of namespace Toon

76
engines/toon/hotspot.h Normal file
View File

@@ -0,0 +1,76 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, MojoTouch has
* exclusively licensed this code on March 23th, 2024, to be used in
* closed-source products.
* Therefore, any contributions (commits) to it will also be dual-licensed.
*
*/
#ifndef TOON_HOTSPOT_H
#define TOON_HOTSPOT_H
#include "toon/toon.h"
#include "toon/tools.h"
namespace Toon {
class HotspotData {
public:
int16 getX1() const { return READ_LE_INT16(_data + 0); }
int16 getY1() const { return READ_LE_INT16(_data + 1); }
int16 getX2() const { return READ_LE_INT16(_data + 2); }
int16 getY2() const { return READ_LE_INT16(_data + 3); }
int16 getMode() const { return READ_LE_INT16(_data + 4); }
int16 getRef() const { return READ_LE_INT16(_data + 5); }
int16 getPriority() const { return READ_LE_INT16(_data + 7); }
int16 getType() const { return READ_LE_INT16(_data + 8); }
int16 getData(int32 id) const { return READ_LE_INT16(_data + id); }
void setData(int32 id, int16 val) { WRITE_LE_UINT16(&_data[id], val); }
private:
int16 _data[256];
};
class Hotspots {
public:
Hotspots(ToonEngine *vm);
~Hotspots();
bool loadRif(const Common::Path &rifName, const Common::Path &additionalRifName);
int32 find(int16 x, int16 y);
int32 findBasedOnCorner(int16 x, int16 y);
HotspotData *get(int32 id);
int32 getCount() const { return _numItems; }
void load(Common::ReadStream *Stream);
void save(Common::WriteStream *Stream);
protected:
HotspotData *_items;
int32 _numItems;
ToonEngine *_vm;
};
} // End of namespace Toon
#endif

267
engines/toon/metaengine.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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, MojoTouch has
* exclusively licensed this code on March 23th, 2024, to be used in
* closed-source products.
* Therefore, any contributions (commits) to it will also be dual-licensed.
*
*/
#include "common/config-manager.h"
#include "engines/advancedDetector.h"
#include "common/savefile.h"
#include "common/system.h"
#include "common/translation.h"
#include "backends/keymapper/action.h"
#include "backends/keymapper/keymapper.h"
#include "backends/keymapper/standard-actions.h"
#include "base/plugins.h"
#include "graphics/thumbnail.h"
#include "toon/toon.h"
class ToonMetaEngine : public AdvancedMetaEngine<ADGameDescription> {
public:
const char *getName() const override {
return "toon";
}
bool hasFeature(MetaEngineFeature f) const override;
Common::Error createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const override;
int getMaximumSaveSlot() const override;
SaveStateList listSaves(const char *target) const override;
SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const override;
bool removeSaveState(const char *target, int slot) const override;
Common::KeymapArray initKeymaps(const char *target) const override;
};
bool ToonMetaEngine::hasFeature(MetaEngineFeature f) const {
return
(f == kSupportsListSaves) ||
(f == kSupportsLoadingDuringStartup) ||
(f == kSupportsDeleteSave) ||
(f == kSavesSupportMetaInfo) ||
(f == kSavesSupportThumbnail) ||
(f == kSavesSupportCreationDate) ||
(f == kSavesSupportPlayTime) ||
(f == kSimpleSavesNames);
}
bool ToonMetaEngine::removeSaveState(const char *target, int slot) const {
Common::String fileName = Common::String::format("%s.%03d", target, slot);
return g_system->getSavefileManager()->removeSavefile(fileName);
}
Common::KeymapArray ToonMetaEngine::initKeymaps(const char *target) const {
using namespace Common;
using namespace Toon;
Keymap *engineKeyMap = new Keymap(Keymap::kKeymapTypeGame, "toon-default", _("Default keymappings"));
Keymap *gameKeyMap = new Keymap(Keymap::kKeymapTypeGame, "game-shortcuts", _("Game keymappings"));
Action *act;
act = new Action(kStandardActionLeftClick, _("Left click"));
act->setLeftClickEvent();
act->addDefaultInputMapping("MOUSE_LEFT");
act->addDefaultInputMapping("JOY_A");
engineKeyMap->addAction(act);
act = new Action(kStandardActionRightClick, _("Right click"));
act->setRightClickEvent();
act->addDefaultInputMapping("MOUSE_RIGHT");
act->addDefaultInputMapping("JOY_B");
engineKeyMap->addAction(act);
act = new Action("ESCAPE", _("Skip intro"));
act->setCustomEngineActionEvent(kActionEscape);
act->addDefaultInputMapping("ESCAPE");
act->addDefaultInputMapping("JOY_BACK");
gameKeyMap->addAction(act);
// I18N: Skips current line being spoken by a character
act = new Action("STOPCURRENTVOICE", _("Stop current voice"));
act->setCustomEngineActionEvent(kActionStopCurrentVoice);
act->addDefaultInputMapping("ESCAPE");
act->addDefaultInputMapping("SPACE");
act->addDefaultInputMapping("JOY_X");
gameKeyMap->addAction(act);
act = new Action("SAVEGAME", _("Save game"));
act->setCustomEngineActionEvent(kActionSaveGame);
act->addDefaultInputMapping("F5");
act->addDefaultInputMapping("JOY_LEFT_SHOULDER");
gameKeyMap->addAction(act);
act = new Action("LOADGAME", _("Load game"));
act->setCustomEngineActionEvent(kActionLoadGame);
act->addDefaultInputMapping("F6");
act->addDefaultInputMapping("JOY_RIGHT_SHOULDER");
gameKeyMap->addAction(act);
act = new Action("SUBTITLES", _("Toggle subtitles"));
act->setCustomEngineActionEvent(kActionSubtitles);
act->addDefaultInputMapping("t");
act->addDefaultInputMapping("JOY_UP");
gameKeyMap->addAction(act);
act = new Action("MUTEMUSIC", _("Mute music"));
act->setCustomEngineActionEvent(kActionMuteMusic);
act->addDefaultInputMapping("m");
act->addDefaultInputMapping("JOY_DOWN");
gameKeyMap->addAction(act);
act = new Action("SPEECHMUTE", _("Mute speech"));
act->setCustomEngineActionEvent(kActionSpeechMute);
act->addDefaultInputMapping("d");
act->addDefaultInputMapping("JOY_LEFT");
gameKeyMap->addAction(act);
act = new Action("SFXMUTE", _("Mute sound effects"));
act->setCustomEngineActionEvent(kActionSFXMute);
act->addDefaultInputMapping("s");
act->addDefaultInputMapping("JOY_RIGHT");
gameKeyMap->addAction(act);
act = new Action("SHOWOPTIONS", _("Show options"));
act->setCustomEngineActionEvent(kActionShowOptions);
act->addDefaultInputMapping("F1");
act->addDefaultInputMapping("JOY_Y");
gameKeyMap->addAction(act);
KeymapArray keymaps(2);
keymaps[0] = engineKeyMap;
keymaps[1] = gameKeyMap;
return keymaps;
}
int ToonMetaEngine::getMaximumSaveSlot() const { return MAX_SAVE_SLOT; }
SaveStateList ToonMetaEngine::listSaves(const char *target) const {
Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
Common::StringArray filenames;
Common::String pattern = target;
pattern += ".###";
filenames = saveFileMan->listSavefiles(pattern);
SaveStateList saveList;
for (Common::StringArray::const_iterator filename = filenames.begin(); filename != filenames.end(); ++filename) {
// Obtain the last 3 digits of the filename, since they correspond to the save slot
int slotNum = atoi(filename->c_str() + filename->size() - 3);
if (slotNum >= 0 && slotNum <= MAX_SAVE_SLOT) {
Common::InSaveFile *file = saveFileMan->openForLoading(*filename);
if (file) {
int32 version = file->readSint32BE();
if ( (version < 4) || (version > TOON_SAVEGAME_VERSION) ) {
delete file;
continue;
}
// read name
uint16 nameSize = file->readUint16BE();
if (nameSize >= 255) {
delete file;
continue;
}
char name[256];
file->read(name, nameSize);
name[nameSize] = 0;
saveList.push_back(SaveStateDescriptor(this, slotNum, name));
delete file;
}
}
}
// Sort saves based on slot number.
Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
SaveStateDescriptor ToonMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
Common::String fileName = Common::String::format("%s.%03d", target, slot);
Common::InSaveFile *file = g_system->getSavefileManager()->openForLoading(fileName);
if (file) {
int32 version = file->readSint32BE();
if ( (version < 4) || (version > TOON_SAVEGAME_VERSION) ) {
delete file;
return SaveStateDescriptor();
}
uint32 saveNameLength = file->readUint16BE();
char saveName[256];
file->read(saveName, saveNameLength);
saveName[saveNameLength] = 0;
SaveStateDescriptor desc(this, slot, saveName);
Graphics::Surface *thumbnail = nullptr;
if (!Graphics::loadThumbnail(*file, thumbnail, false)) {
delete file;
return SaveStateDescriptor();
}
desc.setThumbnail(thumbnail);
uint32 saveDate = file->readUint32BE();
uint16 saveTime = file->readUint16BE();
int day = (saveDate >> 24) & 0xFF;
int month = (saveDate >> 16) & 0xFF;
int year = saveDate & 0xFFFF;
desc.setSaveDate(year, month, day);
int hour = (saveTime >> 8) & 0xFF;
int minutes = saveTime & 0xFF;
desc.setSaveTime(hour, minutes);
if (version >= 5) {
uint32 playTimeMsec = file->readUint32BE();
desc.setPlayTime(playTimeMsec);
}
delete file;
return desc;
}
return SaveStateDescriptor();
}
Common::Error ToonMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
*engine = new Toon::ToonEngine(syst, desc);
return Common::kNoError;
}
#if PLUGIN_ENABLED_DYNAMIC(TOON)
REGISTER_PLUGIN_DYNAMIC(TOON, PLUGIN_TYPE_ENGINE, ToonMetaEngine);
#else
REGISTER_PLUGIN_STATIC(TOON, PLUGIN_TYPE_ENGINE, ToonMetaEngine);
#endif

35
engines/toon/module.mk Normal file
View File

@@ -0,0 +1,35 @@
MODULE := engines/toon
MODULE_OBJS := \
anim.o \
audio.o \
character.o \
console.o \
conversation.o \
drew.o \
flux.o \
font.o \
hotspot.o \
metaengine.o \
movie.o \
path.o \
picture.o \
resource.o \
script.o \
script_func.o \
state.o \
subtitles.o \
text.o \
tools.o \
toon.o
# This module can be built as a plugin
ifeq ($(ENABLE_TOON), DYNAMIC_PLUGIN)
PLUGIN := 1
endif
# Include common rules
include $(srcdir)/rules.mk
# Detection objects
DETECT_OBJS += $(MODULE)/detection.o

188
engines/toon/movie.cpp Normal file
View File

@@ -0,0 +1,188 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, MojoTouch has
* exclusively licensed this code on March 23th, 2024, to be used in
* closed-source products.
* Therefore, any contributions (commits) to it will also be dual-licensed.
*
*/
#include "common/debug.h"
#include "common/events.h"
#include "common/keyboard.h"
#include "common/stream.h"
#include "common/system.h"
#include "graphics/paletteman.h"
#include "graphics/surface.h"
#include "toon/audio.h"
#include "toon/movie.h"
#include "toon/toon.h"
#include "toon/subtitles.h"
namespace Toon {
ToonstruckSmackerDecoder::ToonstruckSmackerDecoder() : Video::SmackerDecoder() {
_lowRes = false;
}
void ToonstruckSmackerDecoder::handleAudioTrack(byte track, uint32 chunkSize, uint32 unpackedSize) {
debugC(6, kDebugMovie, "handleAudioTrack(%d, %d, %d)", track, chunkSize, unpackedSize);
if (track == 1 && chunkSize == 4) {
/* uint16 width = */ _fileStream->readUint16LE();
uint16 height = _fileStream->readUint16LE();
_lowRes = (height == getHeight() / 2);
} else {
Video::SmackerDecoder::handleAudioTrack(track, chunkSize, unpackedSize);
}
}
bool ToonstruckSmackerDecoder::loadStream(Common::SeekableReadStream *stream) {
if (!Video::SmackerDecoder::loadStream(stream))
return false;
_lowRes = false;
return true;
}
Video::SmackerDecoder::SmackerVideoTrack *ToonstruckSmackerDecoder::createVideoTrack(uint32 width, uint32 height, uint32 frameCount, const Common::Rational &frameRate, uint32 flags, uint32 version) const {
return Video::SmackerDecoder::createVideoTrack(width, height, frameCount, frameRate, (height == 200) ? 4 : flags, version);
}
// decoder is deallocated with Movie destruction i.e. new ToonstruckSmackerDecoder is needed
Movie::Movie(ToonEngine *vm , ToonstruckSmackerDecoder *decoder) {
_vm = vm;
_playing = false;
_decoder = decoder;
_subtitle = new SubtitleRenderer(_vm);
}
Movie::~Movie() {
delete _decoder;
delete _subtitle;
}
void Movie::init() const {
}
void Movie::play(const Common::Path &video, int32 flags) {
debugC(1, kDebugMovie, "play(%s, %d)", video.toString().c_str(), flags);
bool isFirstIntroVideo = false;
if (video == "209_1M.SMK")
isFirstIntroVideo = true;
_playing = true;
if (flags & 1)
_vm->getAudioManager()->setMusicVolume(0);
if (!_decoder->loadFile(video)) {
if (flags & 2)
return;
error("Unable to play video %s", video.toString().c_str());
}
_subtitle->load(video);
playVideo(isFirstIntroVideo);
_vm->flushPalette(true);
if (flags & 1)
_vm->getAudioManager()->setMusicVolume(_vm->getAudioManager()->isMusicMuted() ? 0 : 255);
_decoder->close();
_playing = false;
}
void Movie::playVideo(bool isFirstIntroVideo) {
debugC(1, kDebugMovie, "playVideo(isFirstIntroVideo: %d)", isFirstIntroVideo);
_decoder->start();
while (!_vm->shouldQuit() && !_decoder->endOfVideo()) {
if (_decoder->needsUpdate()) {
const Graphics::Surface *frame = _decoder->decodeNextFrame();
byte unused = 0;
if (frame) {
if (_decoder->isLowRes()) {
// handle manually 2x scaling here
Graphics::Surface* surf = _vm->_system->lockScreen();
for (int y = 0; y < frame->h / 2; y++) {
memcpy(surf->getBasePtr(0, y * 2 + 0), frame->getBasePtr(0, y), frame->pitch);
memcpy(surf->getBasePtr(0, y * 2 + 1), frame->getBasePtr(0, y), frame->pitch);
}
_vm->_system->unlockScreen();
} else {
_vm->_system->copyRectToScreen(frame->getPixels(), frame->pitch, 0, 0, frame->w, frame->h);
int32 currentFrame = _decoder->getCurFrame();
// find unused color key to replace with subtitles color
uint len = frame->w * frame->h;
const byte *pixels = (const byte *)frame->getPixels();
bool counts[256];
memset(counts, false, sizeof(counts));
for (uint i = 0; i < len; i++) {
counts[pixels[i]] = true;
}
// 0 is already used for the border color and should not be used here, so it can be skipped over.
for (int j = 1; j < 256; j++) {
if (!counts[j]) {
unused = j;
break;
}
}
_subtitle->render(*frame, currentFrame, unused);
// WORKAROUND: There is an encoding glitch in the first intro video. This hides this using the adjacent pixels.
if (isFirstIntroVideo) {
// int32 currentFrame = _decoder->getCurFrame();
if (currentFrame >= 956 && currentFrame <= 1038) {
debugC(1, kDebugMovie, "Triggered workaround for glitch in first intro video...");
_vm->_system->copyRectToScreen(frame->getBasePtr(frame->w-188, 123), frame->pitch, frame->w-188, 124, 188, 1);
_vm->_system->copyRectToScreen(frame->getBasePtr(frame->w-188, 126), frame->pitch, frame->w-188, 125, 188, 1);
_vm->_system->copyRectToScreen(frame->getBasePtr(0, 125), frame->pitch, 0, 126, 64, 1);
_vm->_system->copyRectToScreen(frame->getBasePtr(0, 128), frame->pitch, 0, 127, 64, 1);
}
}
}
}
byte subtitleColor[3] = {0xff, 0xff, 0x0};
_vm->_system->getPaletteManager()->setPalette(_decoder->getPalette(), 0, 256);
if (unused) {
_vm->_system->getPaletteManager()->setPalette(subtitleColor, unused, 1);
}
_vm->_system->updateScreen();
}
Common::Event event;
while (_vm->_system->getEventManager()->pollEvent(event))
if ((event.type == Common::EVENT_CUSTOM_ENGINE_ACTION_START && event.customType == kActionEscape)) {
_vm->dirtyAllScreen();
return;
}
_vm->_system->delayMillis(10);
}
_vm->dirtyAllScreen();
return;
}
} // End of namespace Toon

73
engines/toon/movie.h Normal file
View File

@@ -0,0 +1,73 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, MojoTouch has
* exclusively licensed this code on March 23th, 2024, to be used in
* closed-source products.
* Therefore, any contributions (commits) to it will also be dual-licensed.
*
*/
#ifndef TOON_MOVIE_H
#define TOON_MOVIE_H
#include "toon/toon.h"
#include "video/smk_decoder.h"
namespace Toon {
class SubtitleRenderer;
class ToonstruckSmackerDecoder : public Video::SmackerDecoder {
public:
ToonstruckSmackerDecoder();
bool loadStream(Common::SeekableReadStream *stream) override;
bool isLowRes() { return _lowRes; }
protected:
void handleAudioTrack(byte track, uint32 chunkSize, uint32 unpackedSize) override;
SmackerVideoTrack *createVideoTrack(uint32 width, uint32 height, uint32 frameCount, const Common::Rational &frameRate, uint32 flags, uint32 version) const override;
private:
bool _lowRes;
};
class Movie {
public:
Movie(ToonEngine *vm, ToonstruckSmackerDecoder *decoder);
virtual ~Movie(void);
void init() const;
void play(const Common::Path &video, int32 flags = 0);
bool isPlaying() { return _playing; }
protected:
void playVideo(bool isFirstIntroVideo);
ToonEngine *_vm;
ToonstruckSmackerDecoder *_decoder;
bool _playing;
SubtitleRenderer *_subtitle;
};
} // End of namespace Toon
#endif

459
engines/toon/path.cpp Normal file
View File

@@ -0,0 +1,459 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, MojoTouch has
* exclusively licensed this code on March 23th, 2024, to be used in
* closed-source products.
* Therefore, any contributions (commits) to it will also be dual-licensed.
*
*/
#include "common/debug.h"
#include "toon/path.h"
namespace Toon {
PathFindingHeap::PathFindingHeap() {
_count = 0;
_size = 0;
_data = nullptr;
}
PathFindingHeap::~PathFindingHeap() {
free(_data);
_data = nullptr;
}
void PathFindingHeap::init(int32 size) {
debugC(1, kDebugPath, "init(%d)", size);
_size = size;
free(_data);
_data = (HeapDataGrid *)malloc(sizeof(HeapDataGrid) * _size);
if (_data != nullptr) {
memset(_data, 0, sizeof(HeapDataGrid) * _size);
} else {
error("Could not allocate PathFindingHeap size: %d", _size);
}
_count = 0;
}
void PathFindingHeap::unload() {
_count = 0;
_size = 0;
free(_data);
_data = nullptr;
}
void PathFindingHeap::clear() {
debugC(1, kDebugPath, "clear()");
_count = 0;
memset(_data, 0, sizeof(HeapDataGrid) * _size);
}
void PathFindingHeap::push(int16 x, int16 y, uint16 weight) {
debugC(2, kDebugPath, "push(%d, %d, %d)", x, y, weight);
if (_count == _size) {
// Increase size by 50%
uint32 newSize = _size + (_size / 2) + 1;
HeapDataGrid *newData;
newData = (HeapDataGrid *)realloc(_data, sizeof(HeapDataGrid) * newSize);
if (newData == nullptr) {
warning("Aborting attempt to push onto PathFindingHeap at maximum size: %d", _count);
return;
}
memset(newData + _size, 0, sizeof(HeapDataGrid) * (newSize - _size));
_data = newData;
_size = newSize;
}
_data[_count]._x = x;
_data[_count]._y = y;
_data[_count]._weight = weight;
_count++;
uint32 lMax = _count - 1;
uint32 lT = 0;
while (true) {
if (lMax <= 0)
break;
lT = (lMax - 1) / 2;
if (_data[lT]._weight > _data[lMax]._weight) {
HeapDataGrid temp;
temp = _data[lT];
_data[lT] = _data[lMax];
_data[lMax] = temp;
lMax = lT;
} else {
break;
}
}
}
void PathFindingHeap::pop(int16 *x, int16 *y, uint16 *weight) {
debugC(2, kDebugPath, "pop(x, y, weight)");
if (!_count) {
warning("Attempt to pop empty PathFindingHeap!");
return;
}
*x = _data[0]._x;
*y = _data[0]._y;
*weight = _data[0]._weight;
_data[0] = _data[--_count];
if (!_count)
return;
uint32 lMin = 0;
uint32 lT = 0;
while (true) {
lT = (lMin * 2) + 1;
if (lT < _count) {
if (lT < _count - 1) {
if (_data[lT + 1]._weight < _data[lT]._weight)
lT++;
}
if (_data[lT]._weight <= _data[lMin]._weight) {
HeapDataGrid temp;
temp = _data[lMin];
_data[lMin] = _data[lT];
_data[lT] = temp;
lMin = lT;
} else {
break;
}
} else {
break;
}
}
}
PathFinding::PathFinding() : _blockingRects{{0}} {
_width = 0;
_height = 0;
_heap = new PathFindingHeap();
_sq = nullptr;
_numBlockingRects = 0;
_currentMask = nullptr;
}
PathFinding::~PathFinding(void) {
if (_heap)
_heap->unload();
delete _heap;
delete[] _sq;
}
void PathFinding::init(Picture *mask) {
debugC(1, kDebugPath, "init(mask)");
_width = mask->getWidth();
_height = mask->getHeight();
_currentMask = mask;
_heap->unload();
_heap->init(500);
delete[] _sq;
_sq = new uint16[_width * _height];
}
bool PathFinding::isLikelyWalkable(int16 x, int16 y) {
for (uint8 i = 0; i < _numBlockingRects; i++) {
if (_blockingRects[i][4] == 0) {
if (x >= _blockingRects[i][0] && x <= _blockingRects[i][2] && y >= _blockingRects[i][1] && y < _blockingRects[i][3])
return false;
} else {
int16 dx = abs(_blockingRects[i][0] - x);
int16 dy = abs(_blockingRects[i][1] - y);
if ((dx << 8) / _blockingRects[i][2] < (1 << 8) && (dy << 8) / _blockingRects[i][3] < (1 << 8)) {
return false;
}
}
}
return true;
}
bool PathFinding::isWalkable(int16 x, int16 y) {
debugC(2, kDebugPath, "isWalkable(%d, %d)", x, y);
return (_currentMask->getData(x, y) & 0x1f) > 0;
}
bool PathFinding::findClosestWalkingPoint(int16 xx, int16 yy, int16 *fxx, int16 *fyy, int16 origX, int16 origY) {
debugC(1, kDebugPath, "findClosestWalkingPoint(%d, %d, fxx, fyy, %d, %d)", xx, yy, origX, origY);
int32 currentFound = -1;
int32 dist = -1;
int32 dist2 = -1;
if (origX == -1)
origX = xx;
if (origY == -1)
origY = yy;
for (int16 y = 0; y < _height; y++) {
for (int16 x = 0; x < _width; x++) {
if (isWalkable(x, y) && isLikelyWalkable(x, y)) {
int32 ndist = (x - xx) * (x - xx) + (y - yy) * (y - yy);
int32 ndist2 = (x - origX) * (x - origX) + (y - origY) * (y - origY);
if (currentFound < 0 || ndist < dist || (ndist == dist && ndist2 < dist2)) {
dist = ndist;
dist2 = ndist2;
currentFound = y * _width + x;
}
}
}
}
if (currentFound != -1) {
*fxx = currentFound % _width;
*fyy = currentFound / _width;
return true;
} else {
*fxx = 0;
*fyy = 0;
return false;
}
}
void PathFinding::walkLine(int16 x, int16 y, int16 x2, int16 y2) {
uint32 bx = x << 16;
int32 dx = x2 - x;
uint32 by = y << 16;
int32 dy = y2 - y;
uint32 adx = abs(dx);
uint32 ady = abs(dy);
int32 t = 0;
if (adx <= ady)
t = ady;
else
t = adx;
int32 cdx = (dx << 16) / t;
int32 cdy = (dy << 16) / t;
_tempPath.clear();
for (int32 i = t; i > 0; i--) {
_tempPath.insert_at(0, Common::Point(bx >> 16, by >> 16));
bx += cdx;
by += cdy;
}
_tempPath.insert_at(0, Common::Point(x2, y2));
}
bool PathFinding::lineIsWalkable(int16 x, int16 y, int16 x2, int16 y2) {
uint32 bx = x << 16;
int32 dx = x2 - x;
uint32 by = y << 16;
int32 dy = y2 - y;
uint32 adx = abs(dx);
uint32 ady = abs(dy);
int32 t = 0;
if (adx <= ady)
t = ady;
else
t = adx;
int32 cdx = (dx << 16) / t;
int32 cdy = (dy << 16) / t;
for (int32 i = t; i > 0; i--) {
if (!isWalkable(bx >> 16, by >> 16))
return false;
bx += cdx;
by += cdy;
}
return true;
}
bool PathFinding::findPath(int16 x, int16 y, int16 destx, int16 desty) {
debugC(1, kDebugPath, "findPath(%d, %d, %d, %d)", x, y, destx, desty);
if (x == destx && y == desty) {
_tempPath.clear();
return true;
}
// ignore path finding if the character is outside the screen
if (x < 0 || x > 1280 || y < 0 || y > 400 || destx < 0 || destx > 1280 || desty < 0 || desty > 400) {
_tempPath.clear();
return true;
}
// first test direct line
if (lineIsWalkable(x, y, destx, desty)) {
walkLine(x, y, destx, desty);
return true;
}
// no direct line, we use the standard A* algorithm
memset(_sq , 0, _width * _height * sizeof(uint16));
_heap->clear();
int16 curX = x;
int16 curY = y;
uint16 curWeight = 0;
_sq[curX + curY *_width] = 1;
_heap->push(curX, curY, abs(destx - x) + abs(desty - y));
while (_heap->getCount()) {
_heap->pop(&curX, &curY, &curWeight);
int32 curNode = curX + curY * _width;
int16 endX = MIN<int16>(curX + 1, _width - 1);
int16 endY = MIN<int16>(curY + 1, _height - 1);
int16 startX = MAX<int16>(curX - 1, 0);
int16 startY = MAX<int16>(curY - 1, 0);
bool next = false;
for (int16 px = startX; px <= endX && !next; px++) {
for (int16 py = startY; py <= endY && !next; py++) {
if (px != curX || py != curY) {
uint16 wei = abs(px - curX) + abs(py - curY);
if (isWalkable(px, py)) { // walkable ?
int32 curPNode = px + py * _width;
uint32 sum = _sq[curNode] + wei * (1 + (isLikelyWalkable(px, py) ? 5 : 0));
if (sum > (uint32)0xFFFF) {
warning("PathFinding::findPath sum exceeds maximum representable!");
sum = (uint32)0xFFFF;
}
if (_sq[curPNode] > sum || !_sq[curPNode]) {
_sq[curPNode] = sum;
uint32 newWeight = _sq[curPNode] + abs(destx - px) + abs(desty - py);
if (newWeight > (uint32)0xFFFF) {
warning("PathFinding::findPath newWeight exceeds maximum representable!");
newWeight = (uint16)0xFFFF;
}
_heap->push(px, py, newWeight);
if (!newWeight)
next = true; // we found it !
}
}
}
}
}
}
// let's see if we found a result !
if (!_sq[destx + desty * _width]) {
// didn't find anything
_tempPath.clear();
return false;
}
curX = destx;
curY = desty;
Common::Array<Common::Point> retPath;
retPath.push_back(Common::Point(curX, curY));
uint16 bestscore = _sq[destx + desty * _width];
bool retVal = false;
while (true) {
int16 bestX = -1;
int16 bestY = -1;
int16 endX = MIN<int16>(curX + 1, _width - 1);
int16 endY = MIN<int16>(curY + 1, _height - 1);
int16 startX = MAX<int16>(curX - 1, 0);
int16 startY = MAX<int16>(curY - 1, 0);
for (int16 px = startX; px <= endX; px++) {
for (int16 py = startY; py <= endY; py++) {
if (px != curX || py != curY) {
int32 PNode = px + py * _width;
if (_sq[PNode] && (isWalkable(px, py))) {
if (_sq[PNode] < bestscore) {
bestscore = _sq[PNode];
bestX = px;
bestY = py;
}
}
}
}
}
if (bestX < 0 || bestY < 0)
break;
retPath.push_back(Common::Point(bestX, bestY));
if ((bestX == x && bestY == y)) {
_tempPath.clear();
for (uint32 i = 0; i < retPath.size(); i++)
_tempPath.push_back(retPath[i]);
retVal = true;
break;
}
curX = bestX;
curY = bestY;
}
return retVal;
}
void PathFinding::addBlockingRect(int16 x1, int16 y1, int16 x2, int16 y2) {
debugC(1, kDebugPath, "addBlockingRect(%d, %d, %d, %d)", x1, y1, x2, y2);
if (_numBlockingRects >= kMaxBlockingRects) {
warning("Maximum number of %d Blocking Rects reached!", kMaxBlockingRects);
return;
}
_blockingRects[_numBlockingRects][0] = x1;
_blockingRects[_numBlockingRects][1] = y1;
_blockingRects[_numBlockingRects][2] = x2;
_blockingRects[_numBlockingRects][3] = y2;
_blockingRects[_numBlockingRects][4] = 0;
_numBlockingRects++;
}
void PathFinding::addBlockingEllipse(int16 x1, int16 y1, int16 w, int16 h) {
debugC(1, kDebugPath, "addBlockingEllipse(%d, %d, %d, %d)", x1, y1, w, h);
if (_numBlockingRects >= kMaxBlockingRects) {
warning("Maximum number of %d Blocking Rects reached!", kMaxBlockingRects);
return;
}
_blockingRects[_numBlockingRects][0] = x1;
_blockingRects[_numBlockingRects][1] = y1;
_blockingRects[_numBlockingRects][2] = w;
_blockingRects[_numBlockingRects][3] = h;
_blockingRects[_numBlockingRects][4] = 1;
_numBlockingRects++;
}
} // End of namespace Toon

105
engines/toon/path.h Normal file
View File

@@ -0,0 +1,105 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, MojoTouch has
* exclusively licensed this code on March 23th, 2024, to be used in
* closed-source products.
* Therefore, any contributions (commits) to it will also be dual-licensed.
*
*/
#ifndef TOON_PATH_H
#define TOON_PATH_H
#include "common/array.h"
#include "common/rect.h"
#include "toon/toon.h"
namespace Toon {
// binary heap system for fast A*
class PathFindingHeap {
public:
PathFindingHeap();
~PathFindingHeap();
void push(int16 x, int16 y, uint16 weight);
void pop(int16 *x, int16 *y, uint16 *weight);
void init(int32 size);
void clear();
void unload();
uint32 getCount() { return _count; }
private:
struct HeapDataGrid {
int16 _x, _y;
uint16 _weight;
};
HeapDataGrid *_data;
uint32 _size;
uint32 _count;
};
class PathFinding {
public:
PathFinding();
~PathFinding();
void init(Picture *mask);
bool findPath(int16 x, int16 y, int16 destX, int16 destY);
bool findClosestWalkingPoint(int16 xx, int16 yy, int16 *fxx, int16 *fyy, int16 origX = -1, int16 origY = -1);
bool isWalkable(int16 x, int16 y);
bool isLikelyWalkable(int16 x, int16 y);
bool lineIsWalkable(int16 x, int16 y, int16 x2, int16 y2);
void walkLine(int16 x, int16 y, int16 x2, int16 y2);
void resetBlockingRects() { _numBlockingRects = 0; }
void addBlockingRect(int16 x1, int16 y1, int16 x2, int16 y2);
void addBlockingEllipse(int16 x1, int16 y1, int16 w, int16 h);
uint32 getPathNodeCount() const { return _tempPath.size(); }
int16 getPathNodeX(uint32 nodeId) const { return _tempPath[(_tempPath.size() - 1) - nodeId].x; }
int16 getPathNodeY(uint32 nodeId) const { return _tempPath[(_tempPath.size() - 1) - nodeId].y; }
private:
static const uint8 kMaxBlockingRects = 16;
Picture *_currentMask;
PathFindingHeap *_heap;
uint16 *_sq;
int16 _width;
int16 _height;
Common::Array<Common::Point> _tempPath;
int16 _blockingRects[kMaxBlockingRects][5];
uint8 _numBlockingRects;
};
} // End of namespace Toon
#endif

365
engines/toon/picture.cpp Normal file
View File

@@ -0,0 +1,365 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, MojoTouch has
* exclusively licensed this code on March 23th, 2024, to be used in
* closed-source products.
* Therefore, any contributions (commits) to it will also be dual-licensed.
*
*/
#include "toon/picture.h"
#include "toon/tools.h"
#include "common/debug.h"
#include "common/rect.h"
#include "common/compression/rnc_deco.h"
#include "common/stack.h"
namespace Toon {
bool Picture::loadPicture(const Common::Path &file) {
debugC(1, kDebugPicture, "loadPicture(%s)", file.toString().c_str());
uint32 size = 0;
uint8 *fileData = _vm->resources()->getFileData(file, &size);
if (!fileData)
return false;
uint32 compId = READ_BE_UINT32(fileData);
switch (compId) {
case kCompLZSS: {
uint32 dstsize = READ_LE_UINT32(fileData + 4);
_data = new uint8[dstsize];
decompressLZSS(fileData + 8, _data, dstsize);
// size can only be 640x400 or 1280x400
if (dstsize > TOON_SCREEN_WIDTH * TOON_SCREEN_HEIGHT + 768)
_width = TOON_BACKBUFFER_WIDTH;
else
_width = TOON_SCREEN_WIDTH;
_height = TOON_SCREEN_HEIGHT;
// do we have a palette ?
_paletteEntries = (dstsize & 0x7ff) / 3;
_useFullPalette = (_paletteEntries == 256);
// _useFullPalette = true;
if (_paletteEntries) {
_palette = new uint8[_paletteEntries * 3];
memcpy(_palette, _data + dstsize - (dstsize & 0x7ff), _paletteEntries * 3);
_vm->fixPaletteEntries(_palette, _paletteEntries);
} else {
_palette = nullptr;
}
return true;
}
case kCompSPCN: {
uint32 decSize = READ_LE_UINT32(fileData + 10);
_data = new uint8[decSize + 100];
_paletteEntries = READ_LE_UINT16(fileData + 14) / 3;
_useFullPalette = (_paletteEntries == 256);
if (_paletteEntries) {
_palette = new uint8[_paletteEntries * 3];
memcpy(_palette, fileData + 16, _paletteEntries * 3);
_vm->fixPaletteEntries(_palette, _paletteEntries);
} else {
_palette = nullptr;
}
// size can only be 640x400 or 1280x400
if (decSize > TOON_SCREEN_WIDTH * TOON_SCREEN_HEIGHT + 768)
_width = TOON_BACKBUFFER_WIDTH;
else
_width = TOON_SCREEN_WIDTH;
_height = TOON_SCREEN_HEIGHT;
// decompress the picture into our buffer
decompressSPCN(fileData + 16 + _paletteEntries * 3, _data, decSize);
return true;
}
case kCompRNC1: {
Common::RncDecoder rnc;
// allocate enough place
uint32 decSize = READ_BE_UINT32(fileData + 4);
_data = new uint8[decSize];
rnc.unpackM1(fileData, size, _data);
// size can only be 640x400 or 1280x400
if (decSize > TOON_SCREEN_WIDTH * TOON_SCREEN_HEIGHT + 768)
_width = TOON_BACKBUFFER_WIDTH;
else
_width = TOON_SCREEN_WIDTH;
_height = TOON_SCREEN_HEIGHT;
return true;
}
case kCompRNC2: {
Common::RncDecoder rnc;
// allocate enough place
uint32 decSize = READ_BE_UINT32(fileData + 4);
_data = new uint8[decSize];
decSize = rnc.unpackM2(fileData, _data);
if (decSize > TOON_SCREEN_WIDTH * TOON_SCREEN_HEIGHT + 768)
_width = TOON_BACKBUFFER_WIDTH;
else
_width = TOON_SCREEN_WIDTH;
_height = TOON_SCREEN_HEIGHT;
return true;
}
default:
break;
}
return false;
}
Picture::Picture(ToonEngine *vm) : _vm(vm) {
_data = nullptr;
_palette = nullptr;
_width = 0;
_height = 0;
_paletteEntries = 0;
_useFullPalette = false;
}
Picture::~Picture() {
delete[] _data;
delete[] _palette;
}
void Picture::setupPalette() {
debugC(1, kDebugPicture, "setupPalette()");
if (_palette != nullptr) {
if (_useFullPalette)
_vm->setPaletteEntries(_palette, 0, 256);
else
_vm->setPaletteEntries(_palette, 1, 128);
}
}
void Picture::drawMask(Graphics::Surface &surface, int16 x, int16 y, int16 dx, int16 dy) {
debugC(1, kDebugPicture, "drawMask(surface, %d, %d, %d, %d)", x, y, dx, dy);
for (int32 i = 0; i < 128; ++i) {
byte color[3];
color[0] = i * 2;
color[1] = i * 2;
color[2] = 255 - i * 2;
_vm->setPaletteEntries(color, i, 1);
}
int16 rx = MIN<int16>(_width, surface.w - x);
int16 ry = MIN<int16>(_height, surface.h - y);
if (rx < 0 || ry < 0)
return;
int32 destPitch = surface.pitch;
int32 srcPitch = _width;
uint8 *c = _data + _width * dy + dx;
uint8 *curRow = (uint8 *)surface.getBasePtr(x, y);
if (_width > dx) {
for (int16 yy = 0; yy < ry; ++yy) {
uint8 *curSrc = c;
uint8 *cur = curRow;
for (int16 xx = 0; xx < rx; ++xx) {
//*cur = (*curSrc >> 5) * 8; // & 0x1f;
*cur = (*curSrc & 0x1f) ? 127 : 0;
++curSrc;
++cur;
}
curRow += destPitch;
c += srcPitch;
}
}
}
void Picture::drawWithRectList(Graphics::Surface& surface, int16 x, int16 y, int16 dx, int16 dy, Common::Array<Common::Rect>& rectArray) {
int16 rx = MIN<int16>(_width, surface.w - x);
int16 ry = MIN<int16>(_height, surface.h - y);
if (rx < 0 || ry < 0)
return;
int32 destPitch = surface.pitch;
int32 srcPitch = _width;
for (uint32 i = 0; i < rectArray.size(); ++i) {
Common::Rect rect = rectArray[i];
int16 fillRx = MIN<int32>(rx, rect.right - rect.left);
int16 fillRy = MIN<int32>(ry, rect.bottom - rect.top);
if (_width > dx + rect.left) {
// Sometimes rect dimensions refer to the larger width ie. 1280, while the picture width is 640
// The if clause above is a simple check to avoid those cases.
// TODO maybe something smarter could be done to prevent adding such problematic rectangles to the list
uint8 *c = _data + _width * (dy + rect.top) + (dx + rect.left);
uint8 *curRow = (uint8 *)surface.getBasePtr(x + rect.left, y + rect.top);
for (int16 yy = 0; yy < fillRy; ++yy) {
uint8 *curSrc = c;
uint8 *cur = curRow;
for (int16 xx = 0; xx < fillRx; ++xx) {
*cur = *curSrc; // Here is where the drawing happens (starting from rect Top Left)
++curSrc; // This goes to the next entry of _data (to be read)
++cur; // This goes to the next address BasePtr of surface (to write there, on surface)
}
c += srcPitch; // data "row" is increased by srcPitch (_width)
curRow += destPitch; // surface row is increased by destPitch
}
}
}
}
void Picture::draw(Graphics::Surface &surface, int16 x, int16 y, int16 dx, int16 dy) {
debugC(6, kDebugPicture, "draw(surface, %d, %d, %d, %d)", x, y, dx, dy);
int16 rx = MIN<int16>(_width, surface.w - x);
int16 ry = MIN<int16>(_height, surface.h - y);
if (rx < 0 || ry < 0)
return;
int32 destPitch = surface.pitch;
int32 srcPitch = _width;
uint8 *c = _data + _width * dy + dx;
uint8 *curRow = (uint8 *)surface.getBasePtr(x, y);
if (_width > dx) {
for (int16 yy = 0; yy < ry; ++yy) {
uint8 *curSrc = c;
uint8 *cur = curRow;
for (int16 xx = 0; xx < rx; ++xx) {
*cur = *curSrc;
++curSrc;
++cur;
}
curRow += destPitch;
c += srcPitch;
}
}
}
uint8 Picture::getData(int16 x, int16 y) {
debugC(6, kDebugPicture, "getData(%d, %d)", x, y);
if (!_data)
return 0;
return _data[y * _width + x];
}
// use original work from johndoe
void Picture::floodFillNotWalkableOnMask(int16 x, int16 y) {
debugC(1, kDebugPicture, "floodFillNotWalkableOnMask(%d, %d)", x, y);
// Stack-based floodFill algorithm based on
// https://web.archive.org/web/20100825020453/http://student.kuleuven.be/~m0216922/CG/files/floodfill.cpp
Common::Stack<Common::Point> stack;
stack.push(Common::Point(x, y));
while (!stack.empty()) {
Common::Point pt = stack.pop();
while (_data[pt.x + pt.y * _width] & 0x1F && pt.y >= 0)
--pt.y;
++pt.y;
bool spanLeft = false;
bool spanRight = false;
uint32 nextDataPos = pt.x + pt.y * _width;
while (pt.y < _height && _data[nextDataPos] & 0x1F) {
_data[nextDataPos] &= 0xE0;
if (!spanLeft && pt.x > 0 && _data[nextDataPos - 1] & 0x1F) {
stack.push(Common::Point(pt.x - 1, pt.y));
spanLeft = 1;
} else if (spanLeft && pt.x > 0 && !(_data[nextDataPos - 1] & 0x1F)) {
spanLeft = 0;
}
if (!spanRight && pt.x < _width - 1 && _data[nextDataPos + 1] & 0x1F) {
stack.push(Common::Point(pt.x + 1, pt.y));
spanRight = 1;
} else if (spanRight && pt.x < _width - 1 && !(_data[nextDataPos + 1] & 0x1F)) {
spanRight = 0;
}
++pt.y;
nextDataPos = pt.x + pt.y * _width;
}
}
}
void Picture::drawLineOnMask(int16 x, int16 y, int16 x2, int16 y2, bool walkable) {
debugC(1, kDebugPicture, "drawLineOnMask(%d, %d, %d, %d, %d)", x, y, x2, y2, (walkable) ? 1 : 0);
static int16 lastX = 0;
static int16 lastY = 0;
if (x == -1) {
x = lastX;
y = lastY;
}
uint32 bx = x << 16;
int16 dx = x2 - x;
uint32 by = y << 16;
int16 dy = y2 - y;
uint16 adx = abs(dx);
uint16 ady = abs(dy);
int16 t = 0;
if (adx <= ady)
t = ady;
else
t = adx;
int32 cdx = (dx << 16) / t;
int32 cdy = (dy << 16) / t;
for (int16 i = t; i > 0; --i) {
int32 rx = bx >> 16;
int32 ry = by >> 16;
if ( rx >= 0 && rx < _width-1 && ry >= 0 && ry < _height) { // sanity check: some lines in the game
// were drawing outside the screen causing corruption
if (!walkable) {
_data[_width * ry + rx] &= 0xe0;
_data[_width * ry + rx + 1] &= 0xe0;
} else {
int32 v = _data[_width * (by >> 16) + rx - 1];
_data[_width * ry + rx] = v;
_data[_width * ry + rx + 1] = v;
}
}
bx += cdx;
by += cdy;
}
}
} // End of namespace Toon

73
engines/toon/picture.h Normal file
View File

@@ -0,0 +1,73 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, MojoTouch has
* exclusively licensed this code on March 23th, 2024, to be used in
* closed-source products.
* Therefore, any contributions (commits) to it will also be dual-licensed.
*
*/
#ifndef TOON_PICTURE_H
#define TOON_PICTURE_H
#include "common/stream.h"
#include "common/array.h"
#include "common/func.h"
#include "common/path.h"
#include "toon/toon.h"
namespace Toon {
class ToonEngine;
class Picture {
public:
Picture(ToonEngine *vm);
~Picture();
bool loadPicture(const Common::Path &file);
void setupPalette();
void draw(Graphics::Surface &surface, int16 x, int16 y, int16 dx, int16 dy);
void drawWithRectList(Graphics::Surface& surface, int16 x, int16 y, int16 dx, int16 dy, Common::Array<Common::Rect>& rectArray);
void drawMask(Graphics::Surface &surface, int16 x, int16 y, int16 dx, int16 dy);
void drawLineOnMask(int16 x, int16 y, int16 x2, int16 y2, bool walkable);
void floodFillNotWalkableOnMask(int16 x, int16 y);
uint8 getData(int16 x, int16 y);
uint8 *getDataPtr() { return _data; }
int16 getWidth() const { return _width; }
int16 getHeight() const { return _height; }
protected:
int16 _width;
int16 _height;
uint8 *_data;
uint8 *_palette; // need to be copied at 3-387
int32 _paletteEntries;
bool _useFullPalette;
ToonEngine *_vm;
};
} // End of namespace Toon
#endif

303
engines/toon/resource.cpp Normal file
View File

@@ -0,0 +1,303 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, MojoTouch has
* exclusively licensed this code on March 23th, 2024, to be used in
* closed-source products.
* Therefore, any contributions (commits) to it will also be dual-licensed.
*
*/
#include "toon/resource.h"
#include "common/debug.h"
#include "common/file.h"
#include "common/memstream.h"
#include "common/substream.h"
#include "toon/toon.h"
namespace Toon {
Resources::Resources(ToonEngine *vm) : _vm(vm), _cacheSize(0) {
_resourceCache.clear();
}
Resources::~Resources() {
while (!_resourceCache.empty()) {
CacheEntry *temp = _resourceCache.back();
_resourceCache.pop_back();
delete temp;
}
while (!_pakFiles.empty()) {
PakFile *temp = _pakFiles.back();
_pakFiles.pop_back();
delete temp;
}
purgeFileData();
}
void Resources::removePackageFromCache(const Common::Path &packName) {
// I'm not sure what's a good strategy here. It seems unnecessary to
// actually remove the cached resources, because the player may be
// wandering back and forth between rooms. So for now, do nothing.
}
bool Resources::getFromCache(const Common::Path &fileName, uint32 *fileSize, uint8 **fileData) {
for (Common::Array<CacheEntry *>::iterator entry = _resourceCache.begin(); entry != _resourceCache.end(); ++entry) {
if ((*entry)->_data && (*entry)->_fileName.equalsIgnoreCase(fileName)) {
debugC(5, kDebugResource, "getFromCache(%s) - Got %d bytes from %s", fileName.toString().c_str(), (*entry)->_size, (*entry)->_packName.toString().c_str());
(*entry)->_age = 0;
*fileSize = (*entry)->_size;
*fileData = (*entry)->_data;
return true;
}
}
return false;
}
void Resources::addToCache(const Common::Path &packName, const Common::Path &fileName, uint32 fileSize, uint8 *fileData) {
debugC(5, kDebugResource, "addToCache(%s, %s, %d) - Total Size: %d", packName.toString().c_str(), fileName.toString().c_str(), fileSize, _cacheSize + fileSize);
for (Common::Array<CacheEntry *>::iterator entry = _resourceCache.begin(); entry != _resourceCache.end(); ++entry) {
if ((*entry)->_data) {
(*entry)->_age++;
}
}
_cacheSize += fileSize;
while (_cacheSize > MAX_CACHE_SIZE) {
CacheEntry *bestEntry = nullptr;
for (Common::Array<CacheEntry *>::iterator entry = _resourceCache.begin(); entry != _resourceCache.end(); ++entry) {
if ((*entry)->_data) {
if (!bestEntry || ((*entry)->_age >= bestEntry->_age && (*entry)->_size >= bestEntry->_size)) {
bestEntry = *entry;
}
}
}
if (!bestEntry)
break;
free(bestEntry->_data);
bestEntry->_data = nullptr;
_cacheSize -= bestEntry->_size;
debugC(5, kDebugResource, "Freed %s (%s) to reclaim %d bytes", bestEntry->_fileName.toString().c_str(), bestEntry->_packName.toString().c_str(), bestEntry->_size);
}
for (Common::Array<CacheEntry *>::iterator entry = _resourceCache.begin(); entry != _resourceCache.end(); ++entry) {
if (!(*entry)->_data) {
(*entry)->_packName = packName;
(*entry)->_fileName = fileName;
(*entry)->_age = 0;
(*entry)->_size = fileSize;
(*entry)->_data = fileData;
return;
}
}
CacheEntry *entry = new CacheEntry();
entry->_packName = packName;
entry->_fileName = fileName;
entry->_size = fileSize;
entry->_data = fileData;
_resourceCache.push_back(entry);
}
bool Resources::openPackage(const Common::Path &fileName) {
debugC(1, kDebugResource, "openPackage(%s)", fileName.toString().c_str());
Common::File file;
bool opened = file.open(fileName);
if (!opened)
return false;
PakFile *pakFile = new PakFile();
pakFile->open(&file, fileName);
file.close();
_pakFiles.push_back(pakFile);
return true;
}
void Resources::closePackage(const Common::Path &fileName) {
removePackageFromCache(fileName);
for (uint32 i = 0; i < _pakFiles.size(); i++) {
if (_pakFiles[i]->getPackName() == fileName) {
delete _pakFiles[i];
_pakFiles.remove_at(i);
return;
}
}
}
uint8 *Resources::getFileData(const Common::Path &fileName, uint32 *fileSize) {
debugC(4, kDebugResource, "getFileData(%s, fileSize)", fileName.toString().c_str());
// first try to find files outside of .pak
// some patched files have not been included in package.
if (Common::File::exists(fileName)) {
Common::File file;
bool opened = file.open(fileName);
if (!opened)
return nullptr;
*fileSize = file.size();
uint8 *memory = (uint8 *)new uint8[*fileSize];
file.read(memory, *fileSize);
file.close();
_allocatedFileData.push_back(memory);
return memory;
} else {
uint32 locFileSize = 0;
uint8 *locFileData = nullptr;
if (getFromCache(fileName, &locFileSize, &locFileData)) {
*fileSize = locFileSize;
return locFileData;
}
for (uint32 i = 0; i < _pakFiles.size(); i++) {
locFileData = _pakFiles[i]->getFileData(fileName, &locFileSize);
if (locFileData) {
*fileSize = locFileSize;
addToCache(_pakFiles[i]->getPackName(), fileName, locFileSize, locFileData);
return locFileData;
}
}
return nullptr;
}
}
Common::SeekableReadStream *Resources::openFile(const Common::Path &fileName) {
debugC(1, kDebugResource, "openFile(%s)", fileName.toString().c_str());
// first try to find files outside of .pak
// some patched files have not been included in package.
if (Common::File::exists(fileName)) {
Common::File file;
if (file.open(fileName)) {
Common::SeekableReadStream *stream = file.readStream(file.size());
file.close();
return stream;
} else {
return nullptr;
}
} else {
for (uint32 i = 0; i < _pakFiles.size(); i++) {
Common::SeekableReadStream *stream = nullptr;
stream = _pakFiles[i]->createReadStream(fileName);
if (stream)
return stream;
}
return nullptr;
}
}
void Resources::purgeFileData() {
for (uint32 i = 0; i < _allocatedFileData.size(); i++) {
delete[] _allocatedFileData[i];
}
_allocatedFileData.clear();
}
Common::SeekableReadStream *PakFile::createReadStream(const Common::Path &fileName) {
debugC(1, kDebugResource, "createReadStream(%s)", fileName.toString().c_str());
uint32 fileSize = 0;
uint8 *buffer = getFileData(fileName, &fileSize);
if (buffer)
return new Common::MemoryReadStream(buffer, fileSize, DisposeAfterUse::YES);
else
return nullptr;
}
uint8 *PakFile::getFileData(const Common::Path &fileName, uint32 *fileSize) {
debugC(4, kDebugResource, "getFileData(%s, fileSize)", fileName.toString().c_str());
for (uint32 i = 0; i < _numFiles; i++) {
if (fileName.equalsIgnoreCase(_files[i]._name)) {
Common::File file;
if (file.open(_packName)) {
*fileSize = _files[i]._size;
file.seek(_files[i]._offset);
// Use malloc() because that's what MemoryReadStream
// uses to dispose of the memory when it's done.
uint8 *buffer = (uint8 *)malloc(*fileSize);
file.read(buffer, *fileSize);
file.close();
return buffer;
}
}
}
return 0;
}
void PakFile::open(Common::SeekableReadStream *rs, const Common::Path &packName) {
debugC(1, kDebugResource, "open(rs)");
char buffer[64];
int32 currentPos = 0;
_numFiles = 0;
_packName = packName;
while (1) {
rs->seek(currentPos);
rs->read(buffer, 64);
int32 offset = READ_LE_UINT32(buffer);
char *name = buffer + 4;
if (!*name)
break;
int32 nameSize = strlen(name) + 1;
int32 nextOffset = READ_LE_UINT32(buffer + 4 + nameSize);
currentPos += 4 + nameSize;
PakFile::File newFile;
Common::strlcpy(newFile._name, name, sizeof(newFile._name));
newFile._offset = offset;
newFile._size = nextOffset - offset;
_numFiles++;
_files.push_back(newFile);
}
}
void PakFile::close() {
}
PakFile::PakFile() {
_numFiles = 0;
}
PakFile::~PakFile() {
close();
}
} // End of namespace Toon

103
engines/toon/resource.h Normal file
View File

@@ -0,0 +1,103 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, MojoTouch has
* exclusively licensed this code on March 23th, 2024, to be used in
* closed-source products.
* Therefore, any contributions (commits) to it will also be dual-licensed.
*
*/
#ifndef TOON_RESOURCE_H
#define TOON_RESOURCE_H
#include "common/array.h"
#include "common/str.h"
#include "common/file.h"
#include "common/stream.h"
#define MAX_CACHE_SIZE (4 * 1024 * 1024)
namespace Toon {
class PakFile {
public:
PakFile();
~PakFile();
void open(Common::SeekableReadStream *rs, const Common::Path &packName);
uint8 *getFileData(const Common::Path &fileName, uint32 *fileSize);
Common::Path getPackName() { return _packName; }
Common::SeekableReadStream *createReadStream(const Common::Path &fileName);
void close();
protected:
struct File {
char _name[13];
int32 _offset;
int32 _size;
};
Common::Path _packName;
uint32 _numFiles;
Common::Array<File> _files;
};
class ToonEngine;
class CacheEntry {
public:
CacheEntry() : _age(0), _size(0), _data(0) {}
~CacheEntry() {
free(_data);
}
Common::Path _packName;
Common::Path _fileName;
uint32 _age;
uint32 _size;
uint8 *_data;
};
class Resources {
public:
Resources(ToonEngine *vm);
~Resources();
bool openPackage(const Common::Path &file);
void closePackage(const Common::Path &fileName);
Common::SeekableReadStream *openFile(const Common::Path &file);
uint8 *getFileData(const Common::Path &fileName, uint32 *fileSize); // this memory must be copied to your own structures!
void purgeFileData();
protected:
ToonEngine *_vm;
Common::Array<uint8 *> _allocatedFileData;
Common::Array<PakFile *> _pakFiles;
uint32 _cacheSize;
Common::Array<CacheEntry *> _resourceCache;
void removePackageFromCache(const Common::Path &packName);
bool getFromCache(const Common::Path &fileName, uint32 *fileSize, uint8 **fileData);
void addToCache(const Common::Path &packName, const Common::Path &fileName, uint32 fileSize, uint8 *fileData);
};
} // End of namespace Toon
#endif

524
engines/toon/script.cpp Normal file
View File

@@ -0,0 +1,524 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, MojoTouch has
* exclusively licensed this code on March 23th, 2024, to be used in
* closed-source products.
* Therefore, any contributions (commits) to it will also be dual-licensed.
*
*/
#include "common/debug.h"
#include "common/endian.h"
#include "common/stream.h"
#include "toon/toon.h"
#include "toon/script.h"
namespace Toon {
EMCInterpreter::EMCInterpreter(ToonEngine *vm) : _vm(vm), _scriptData(0), _filename(0) {
#define OPCODE(x) { &EMCInterpreter::x, #x }
static const OpcodeEntry opcodes[] = {
// 0x00
OPCODE(op_jmp),
OPCODE(op_setRetValue),
OPCODE(op_pushRetOrPos),
OPCODE(op_push),
// 0x04
OPCODE(op_push),
OPCODE(op_pushReg),
OPCODE(op_pushBPNeg),
OPCODE(op_pushBPAdd),
// 0x08
OPCODE(op_popRetOrPos),
OPCODE(op_popReg),
OPCODE(op_popBPNeg),
OPCODE(op_popBPAdd),
// 0x0C
OPCODE(op_addSP),
OPCODE(op_subSP),
OPCODE(op_sysCall),
OPCODE(op_ifNotJmp),
// 0x10
OPCODE(op_negate),
OPCODE(op_eval),
OPCODE(op_setRetAndJmp)
};
_opcodes = opcodes;
#undef OPCODE
_parameter = 0;
}
EMCInterpreter::~EMCInterpreter() {
}
bool EMCInterpreter::callback(Common::IFFChunk &chunk) {
switch (chunk._type) {
case MKTAG('T','E','X','T'):
delete[] _scriptData->text;
_scriptData->text = new byte[chunk._size];
assert(_scriptData->text);
if (chunk._stream->read(_scriptData->text, chunk._size) != chunk._size)
error("Couldn't read TEXT chunk from file '%s'", _filename);
break;
case MKTAG('O','R','D','R'):
delete[] _scriptData->ordr;
_scriptData->ordr = new uint16[chunk._size >> 1];
assert(_scriptData->ordr);
if (chunk._stream->read(_scriptData->ordr, chunk._size) != chunk._size)
error("Couldn't read ORDR chunk from file '%s'", _filename);
for (int i = (chunk._size >> 1) - 1; i >= 0; --i)
_scriptData->ordr[i] = READ_BE_UINT16(&_scriptData->ordr[i]);
break;
case MKTAG('D','A','T','A'):
delete[] _scriptData->data;
_scriptData->data = new uint16[chunk._size >> 1];
assert(_scriptData->data);
if (chunk._stream->read(_scriptData->data, chunk._size) != chunk._size)
error("Couldn't read DATA chunk from file '%s'", _filename);
for (int i = (chunk._size >> 1) - 1; i >= 0; --i)
_scriptData->data[i] = READ_BE_UINT16(&_scriptData->data[i]);
break;
default:
warning("Unexpected chunk '%s' of size %d found in file '%s'", Common::tag2string(chunk._type).c_str(), chunk._size, _filename);
}
return false;
}
bool EMCInterpreter::load(const char *filename, EMCData *scriptData, const Common::Array<const OpcodeV2 *> *opcodes) {
Common::SeekableReadStream *stream = _vm->resources()->openFile(filename);
if (!stream) {
error("Couldn't open script file '%s'", filename);
return false; // for compilers that don't support NORETURN
}
memset(scriptData, 0, sizeof(EMCData));
_scriptData = scriptData;
_filename = filename;
IFFParser iff(*stream);
Common::Functor1Mem< Common::IFFChunk &, bool, EMCInterpreter > c(this, &EMCInterpreter::callback);
iff.parse(c);
if (!_scriptData->ordr)
error("No ORDR chunk found in file: '%s'", filename);
if (!_scriptData->data)
error("No DATA chunk found in file: '%s'", filename);
if (stream->err())
error("Read error while parsing file '%s'", filename);
delete stream;
_scriptData->sysFuncs = opcodes;
Common::strlcpy(_scriptData->filename, filename, 13);
_scriptData = 0;
_filename = 0;
return true;
}
void EMCInterpreter::unload(EMCData *data) {
if (!data)
return;
delete[] data->text;
data->text = nullptr;
delete[] data->ordr;
data->ordr = nullptr;
delete[] data->data;
data->data = nullptr;
}
void EMCInterpreter::init(EMCState *scriptStat, const EMCData *data) {
scriptStat->dataPtr = data;
scriptStat->ip = 0;
scriptStat->stack[EMCState::kStackLastEntry] = 0;
scriptStat->bp = EMCState::kStackSize + 1;
scriptStat->sp = EMCState::kStackLastEntry;
scriptStat->running = false;
}
bool EMCInterpreter::start(EMCState *script, int function) {
if (!script->dataPtr)
return false;
uint16 functionOffset = script->dataPtr->ordr[function];
if (functionOffset == 0xFFFF)
return false;
script->ip = &script->dataPtr->data[functionOffset + 1];
return true;
}
bool EMCInterpreter::isValid(EMCState *script) {
if (script->ip == nullptr || script->dataPtr == nullptr || _vm->shouldQuitGame())
return false;
return true;
}
bool EMCInterpreter::run(EMCState *script) {
if (script->running) {
// Prevents nested call of same (already running script)
return false;
}
_parameter = 0;
if (script->ip == nullptr) {
return false;
}
script->running = true;
// Should be no Problem at all to cast to uint32 here, since that's the biggest ptrdiff the original
// would allow, of course that's not realistic to happen to be somewhere near the limit of uint32 anyway.
// instOffset is the offset of currect instruction from the start of the script data
const uint32 instOffset = (uint32)((const byte *)script->ip - (const byte *)script->dataPtr->data);
int16 code = *script->ip++; // get the next instruction (and increase instruction pointer)
int16 opcode = (code >> 8) & 0x1F; // get the opCode from the instruction
if (code & 0x8000) {
opcode = 0;
_parameter = code & 0x7FFF;
} else if (code & 0x4000) {
_parameter = (int8)(code);
} else if (code & 0x2000) {
_parameter = *script->ip++;
} else {
_parameter = 0;
}
if (opcode > 18) {
error("Unknown script opcode: %d in file '%s' at offset 0x%.08X", opcode, script->dataPtr->filename, instOffset);
} else {
static bool EMCDebug = false;
if (EMCDebug) {
debugC(5, 0, "[0x%.08X] EMCInterpreter::%s([%d/%u])", instOffset * 2, _opcodes[opcode].desc, _parameter, (uint)_parameter);
//printf( "[0x%.08X] EMCInterpreter::%s([%d/%u])\n", instOffset, _opcodes[opcode].desc, _parameter, (uint)_parameter);
}
(this->*(_opcodes[opcode].proc))(script);
}
script->running = false;
return (script->ip != nullptr);
}
#pragma mark -
#pragma mark - Command implementations
#pragma mark -
void EMCInterpreter::op_jmp(EMCState *script) {
script->ip = script->dataPtr->data + _parameter;
}
void EMCInterpreter::op_setRetValue(EMCState *script) {
script->retValue = _parameter;
}
void EMCInterpreter::op_pushRetOrPos(EMCState *script) {
switch (_parameter) {
case 0:
// store retValue in next free stack slot (from 99 moving "downwards" to 0)
script->stack[--script->sp] = script->retValue;
break;
case 1:
// store offset of next instruction (from script->dataPtr->data) in stack slot
// store script->bp in stack slot
// set script->bp to current free stack slot + 2 (essentially it's the stack slot before we pushed here)
script->stack[--script->sp] = script->ip - script->dataPtr->data + 1;
script->stack[--script->sp] = script->bp;
script->bp = script->sp + 2;
break;
default:
script->ip = nullptr;
}
}
void EMCInterpreter::op_push(EMCState *script) {
script->stack[--script->sp] = _parameter;
}
void EMCInterpreter::op_pushReg(EMCState *script) {
script->stack[--script->sp] = script->regs[_parameter];
}
void EMCInterpreter::op_pushBPNeg(EMCState *script) {
script->stack[--script->sp] = script->stack[(-(int32)(_parameter + 2)) + script->bp];
}
void EMCInterpreter::op_pushBPAdd(EMCState *script) {
script->stack[--script->sp] = script->stack[(_parameter - 1) + script->bp];
}
void EMCInterpreter::op_popRetOrPos(EMCState *script) {
switch (_parameter) {
case 0:
script->retValue = script->stack[script->sp++];
break;
case 1:
if (script->sp >= EMCState::kStackLastEntry) {
// Nothing to pop
script->ip = nullptr;
} else {
// set the base "pointer" to the value of script->stack[] of the script->sp slot (entry)
// and increase the script->sp slot index (it now points to a slot with the value of the offset used below).
// script->ip is set to point at an offset of stack[script->sp] after dataPtr->data.
// and increase (again) the script->sp slot index.
script->bp = script->stack[script->sp++];
script->ip = script->dataPtr->data + script->stack[script->sp++];
}
break;
default:
script->ip = nullptr;
}
}
void EMCInterpreter::op_popReg(EMCState *script) {
script->regs[_parameter] = script->stack[script->sp++];
}
void EMCInterpreter::op_popBPNeg(EMCState *script) {
script->stack[(-(int32)(_parameter + 2)) + script->bp] = script->stack[script->sp++];
}
void EMCInterpreter::op_popBPAdd(EMCState *script) {
script->stack[(_parameter - 1) + script->bp] = script->stack[script->sp++];
}
void EMCInterpreter::op_addSP(EMCState *script) {
script->sp += _parameter;
}
void EMCInterpreter::op_subSP(EMCState *script) {
script->sp -= _parameter;
}
void EMCInterpreter::op_sysCall(EMCState *script) {
const uint8 id = _parameter;
assert(script->dataPtr->sysFuncs);
assert(id < script->dataPtr->sysFuncs->size());
if ((*script->dataPtr->sysFuncs)[id] && ((*script->dataPtr->sysFuncs)[id])->isValid()) {
script->retValue = (*(*script->dataPtr->sysFuncs)[id])(script);
} else {
script->retValue = 0;
warning("Unimplemented system call 0x%.02X/%d used in file '%s'", id, id, script->dataPtr->filename);
}
}
void EMCInterpreter::op_ifNotJmp(EMCState *script) {
if (!script->stack[script->sp++]) {
_parameter &= 0x7FFF;
script->ip = script->dataPtr->data + _parameter;
}
}
void EMCInterpreter::op_negate(EMCState *script) {
int16 value = script->stack[script->sp];
switch (_parameter) {
case 0:
if (!value)
script->stack[script->sp] = 1;
else
script->stack[script->sp] = 0;
break;
case 1:
script->stack[script->sp] = -value;
break;
case 2:
script->stack[script->sp] = ~value;
break;
default:
warning("Unknown negation func: %d", _parameter);
script->ip = nullptr;
}
}
void EMCInterpreter::op_eval(EMCState *script) {
int16 ret = 0;
bool error = false;
int16 val1 = script->stack[script->sp++];
int16 val2 = script->stack[script->sp++];
switch (_parameter) {
case 0:
ret = (val2 && val1) ? 1 : 0;
break;
case 1:
ret = (val2 || val1) ? 1 : 0;
break;
case 2:
ret = (val1 == val2) ? 1 : 0;
break;
case 3:
ret = (val1 != val2) ? 1 : 0;
break;
case 4:
ret = (val1 > val2) ? 1 : 0;
break;
case 5:
ret = (val1 >= val2) ? 1 : 0;
break;
case 6:
ret = (val1 < val2) ? 1 : 0;
break;
case 7:
ret = (val1 <= val2) ? 1 : 0;
break;
case 8:
ret = val1 + val2;
break;
case 9:
ret = val2 - val1;
break;
case 10:
ret = val1 * val2;
break;
case 11:
ret = val2 / val1;
break;
case 12:
ret = val2 >> val1;
break;
case 13:
ret = val2 << val1;
break;
case 14:
ret = val1 & val2;
break;
case 15:
ret = val1 | val2;
break;
case 16:
ret = val2 % val1;
break;
case 17:
ret = val1 ^ val2;
break;
default:
warning("Unknown evaluate func: %d", _parameter);
error = true;
}
if (error)
script->ip = nullptr;
else
script->stack[--script->sp] = ret;
}
void EMCInterpreter::op_setRetAndJmp(EMCState *script) {
if (script->sp >= EMCState::kStackLastEntry) {
script->ip = nullptr;
} else {
script->retValue = script->stack[script->sp++];
uint16 temp = script->stack[script->sp++];
script->stack[EMCState::kStackLastEntry] = 0;
script->ip = &script->dataPtr->data[temp];
}
}
void EMCInterpreter::saveState(EMCState *script, Common::WriteStream *stream) {
stream->writeSint16LE(script->bp);
stream->writeSint16LE(script->sp);
if (script->ip == nullptr) {
stream->writeSint16LE(-1);
} else {
stream->writeSint16LE(script->ip - script->dataPtr->data);
}
for (int32 i = 0; i < EMCState::kStackSize; i++) {
stream->writeSint16LE(script->stack[i]);
}
for (int32 i = 0; i < 30; i++) {
stream->writeSint16LE(script->regs[i]);
}
stream->writeSint16LE(script->retValue);
stream->writeByte(script->running);
}
void EMCInterpreter::loadState(EMCState *script, Common::ReadStream *stream) {
script->bp = stream->readSint16LE();
script->sp = stream->readSint16LE();
int16 scriptIp = stream->readSint16LE();
if (scriptIp == -1) {
script->ip = nullptr;
} else {
script->ip = scriptIp + script->dataPtr->data;
}
for (int32 i = 0; i < EMCState::kStackSize; i++) {
script->stack[i] = stream->readSint16LE();
}
for (int32 i = 0; i < 30; i++) {
script->regs[i] = stream->readSint16LE();
}
script->retValue = stream->readSint16LE();
script->running = stream->readByte();
}
} // End of namespace Toon

156
engines/toon/script.h Normal file
View File

@@ -0,0 +1,156 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, MojoTouch has
* exclusively licensed this code on March 23th, 2024, to be used in
* closed-source products.
* Therefore, any contributions (commits) to it will also be dual-licensed.
*
*/
#ifndef TOON_SCRIPT_H
#define TOON_SCRIPT_H
#include "common/stream.h"
#include "common/array.h"
#include "common/func.h"
#include "common/formats/iff_container.h"
// Based on Kyra script interpretor
namespace Toon {
struct EMCState;
class ScriptFunc;
typedef Common::Functor1Mem<EMCState *, int32, ScriptFunc> OpcodeV2;
struct EMCData {
char filename[13];
byte *text;
uint16 *data;
uint16 *ordr;
uint16 dataSize;
const Common::Array<const OpcodeV2 *> *sysFuncs;
};
struct EMCState {
enum {
kStackSize = 100,
kStackLastEntry = kStackSize - 1
};
const uint16 *ip;
const EMCData *dataPtr;
int16 retValue;
uint16 bp;
uint16 sp;
int16 regs[30]; // VM registers
int16 stack[kStackSize]; // VM stack
bool running;
};
#define stackPos(x) (state->stack[state->sp+x])
#define stackPosString(x) ((const char *)&state->dataPtr->text[READ_BE_UINT16(&state->dataPtr->text[stackPos(x)<<1])])
class Resource;
class ToonEngine;
class IFFParser : public Common::IFFParser {
public:
IFFParser(Common::ReadStream &input) : Common::IFFParser(&input) {
// It seems Westwood missunderstood the 'size' field of the FORM chunk.
//
// For EMC scripts (type EMC2) it's filesize instead of filesize - 8,
// means accidentally including the 8 bytes used by the chunk header for the FORM
// chunk.
//
// For TIM scripts (type AVFS) it's filesize - 12 instead of filesize - 8,
// means it will not include the size of the 'type' field in the FORM chunk,
// instead of only not including the chunk header size.
//
// Both lead to some problems in our IFF parser, either reading after the end
// of file or producing a "Chunk overread" error message. To work around this
// we need to adjust the size field properly.
if (_formType == MKTAG('E','M','C','2'))
_formChunk.size -= 8;
else if (_formType == MKTAG('A','V','F','S'))
_formChunk.size += 4;
}
};
class EMCInterpreter {
public:
EMCInterpreter(ToonEngine *vm);
~EMCInterpreter();
bool load(const char *filename, EMCData *data, const Common::Array<const OpcodeV2 *> *opcodes);
void unload(EMCData *data);
void init(EMCState *scriptState, const EMCData *data);
bool start(EMCState *script, int function);
void saveState(EMCState *script, Common::WriteStream *stream);
void loadState(EMCState *script, Common::ReadStream *stream);
bool isValid(EMCState *script);
bool run(EMCState *script);
protected:
ToonEngine *_vm;
int16 _parameter;
const char *_filename;
EMCData *_scriptData;
bool callback(Common::IFFChunk &chunk);
typedef void (EMCInterpreter::*OpcodeProc)(EMCState *);
struct OpcodeEntry {
OpcodeProc proc;
const char *desc;
};
const OpcodeEntry *_opcodes;
private:
void op_jmp(EMCState *);
void op_setRetValue(EMCState *);
void op_pushRetOrPos(EMCState *);
void op_push(EMCState *);
void op_pushReg(EMCState *);
void op_pushBPNeg(EMCState *);
void op_pushBPAdd(EMCState *);
void op_popRetOrPos(EMCState *);
void op_popReg(EMCState *);
void op_popBPNeg(EMCState *);
void op_popBPAdd(EMCState *);
void op_addSP(EMCState *);
void op_subSP(EMCState *);
void op_sysCall(EMCState *);
void op_ifNotJmp(EMCState *);
void op_negate(EMCState *);
void op_eval(EMCState *);
void op_setRetAndJmp(EMCState *);
};
} // End of namespace Toon
#endif

1237
engines/toon/script_func.cpp Normal file

File diff suppressed because it is too large Load Diff

177
engines/toon/script_func.h Normal file
View File

@@ -0,0 +1,177 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, MojoTouch has
* exclusively licensed this code on March 23th, 2024, to be used in
* closed-source products.
* Therefore, any contributions (commits) to it will also be dual-licensed.
*
*/
#ifndef SCRIPT_FUNC_H
#define SCRIPT_FUNC_H
#include "common/array.h"
#include "toon/script.h"
namespace Toon {
class ScriptFunc;
typedef Common::Functor1Mem<EMCState *, int32, ScriptFunc> OpcodeV2;
class ScriptFunc {
public:
ScriptFunc(ToonEngine *vm);
~ScriptFunc(void);
Common::Array<const OpcodeV2 *> _opcodes;
ToonEngine *_vm;
#define SYSFUNC(x) int32 x(EMCState *)
SYSFUNC(sys_Cmd_Dummy);
SYSFUNC(sys_Cmd_Change_Actor_X_And_Y);
SYSFUNC(sys_Cmd_Init_Talking_Character);
SYSFUNC(sys_Cmd_Draw_Actor_Standing);
SYSFUNC(sys_Cmd_Get_Actor_X);
SYSFUNC(sys_Cmd_Get_Actor_Y);
SYSFUNC(sys_Cmd_Get_Actor_Facing);
SYSFUNC(sys_Cmd_Get_Last_Scene);
SYSFUNC(sys_Cmd_Debug_Print);
SYSFUNC(sys_Cmd_Flip_Screens);
SYSFUNC(sys_Cmd_Play_Flic);
SYSFUNC(sys_Cmd_Force_Facing);
SYSFUNC(sys_Cmd_Restart_Thread);
SYSFUNC(sys_Cmd_Walk_Actor_To_Point);
SYSFUNC(sys_Cmd_Set_Sack_Visible);
SYSFUNC(sys_Cmd_Set_Actor_Facing);
SYSFUNC(sys_Cmd_Confiscate_Inventory);
SYSFUNC(sys_Cmd_Character_Talks);
SYSFUNC(sys_Cmd_Visited_Scene);
SYSFUNC(sys_Cmd_Query_Rif_Flag);
SYSFUNC(sys_Cmd_Query_Scroll);
SYSFUNC(sys_Cmd_Set_Initial_Location);
SYSFUNC(sys_Cmd_Make_Line_Non_Walkable);
SYSFUNC(sys_Cmd_Make_Line_Walkable);
SYSFUNC(sys_Cmd_Walk_Actor_On_Condition);
SYSFUNC(sys_Cmd_Set_Actor_Facing_Point);
SYSFUNC(sys_Cmd_Set_Inventory_Slot);
SYSFUNC(sys_Cmd_Get_Inventory_Slot);
SYSFUNC(sys_Cmd_Add_Item_To_Inventory);
SYSFUNC(sys_Cmd_Set_Actor_RGB_Modifiers);
SYSFUNC(sys_Cmd_Init_Conversation_AP);
SYSFUNC(sys_Cmd_Actor_Talks);
SYSFUNC(sys_Cmd_Say_Lines);
SYSFUNC(sys_Cmd_Set_Rif_Flag);
SYSFUNC(sys_Cmd_Empty_Inventory);
SYSFUNC(sys_Cmd_Set_Anim_Scale_Size);
SYSFUNC(sys_Cmd_Delete_Item_From_Inventory);
SYSFUNC(sys_Cmd_Specific_Item_In_Inventory);
SYSFUNC(sys_Cmd_Run_Script);
SYSFUNC(sys_Cmd_Query_Game_Flag);
SYSFUNC(sys_Cmd_Reset_Game_Flag);
SYSFUNC(sys_Cmd_Set_Game_Flag);
SYSFUNC(sys_Cmd_Create_Mouse_Item);
SYSFUNC(sys_Cmd_Destroy_Mouse_Item);
SYSFUNC(sys_Cmd_Get_Mouse_State);
SYSFUNC(sys_Cmd_Hide_Mouse);
SYSFUNC(sys_Cmd_Exit_Conversation);
SYSFUNC(sys_Cmd_Set_Mouse_Pos);
SYSFUNC(sys_Cmd_Show_Mouse);
SYSFUNC(sys_Cmd_In_Close_Up);
SYSFUNC(sys_Cmd_Set_Scroll_Lock);
SYSFUNC(sys_Cmd_Fill_Area_Non_Walkable);
SYSFUNC(sys_Cmd_Set_Scroll_Coords);
SYSFUNC(sys_Cmd_Hide_Cutaway);
SYSFUNC(sys_Cmd_Show_Cutaway);
SYSFUNC(sys_Cmd_Pause_Ticks);
SYSFUNC(sys_Cmd_In_Conversation);
SYSFUNC(sys_Cmd_Character_Talking);
SYSFUNC(sys_Cmd_Set_Flux_Facing_Point);
SYSFUNC(sys_Cmd_Set_Flux_Facing);
SYSFUNC(sys_Cmd_Set_Flux_Coords);
SYSFUNC(sys_Cmd_Set_Flux_Visible);
SYSFUNC(sys_Cmd_Get_Flux_X);
SYSFUNC(sys_Cmd_Get_Flux_Y);
SYSFUNC(sys_Cmd_Get_Flux_Facing);
SYSFUNC(sys_Cmd_Get_Flux_Flags);
SYSFUNC(sys_Cmd_Query_Flux_Coords);
SYSFUNC(sys_Cmd_Have_A_Conversation);
SYSFUNC(sys_Cmd_Walk_Flux_To_Point);
SYSFUNC(sys_Cmd_Query_Scene_Anim_Loaded);
SYSFUNC(sys_Cmd_Play_Flux_Anim);
SYSFUNC(sys_Cmd_Set_Anim_Priority);
SYSFUNC(sys_Cmd_Place_Scene_Anim);
SYSFUNC(sys_Cmd_Update_Scene_Animations);
SYSFUNC(sys_Cmd_Get_Drew_Scale);
SYSFUNC(sys_Cmd_Query_Drew_Flags);
SYSFUNC(sys_Cmd_Set_Music);
SYSFUNC(sys_Cmd_Query_Speech);
SYSFUNC(sys_Cmd_Enter_New_Scene);
SYSFUNC(sys_Cmd_Enter_Same_Scene);
SYSFUNC(sys_Cmd_Is_Pixel_Walkable);
SYSFUNC(sys_Cmd_Show_Screen);
SYSFUNC(sys_Cmd_Hide_Screen);
SYSFUNC(sys_Cmd_Set_Special_Enter_X_And_Y);
SYSFUNC(sys_Cmd_Get_Mouse_X);
SYSFUNC(sys_Cmd_Get_Mouse_Y);
SYSFUNC(sys_Cmd_Fade_Palette);
SYSFUNC(sys_Cmd_Music_Enabled);
SYSFUNC(sys_Cmd_Random);
SYSFUNC(sys_Cmd_Wait_Key);
SYSFUNC(sys_Cmd_Draw_Scene_Anim_WSA_Frame_To_Back);
SYSFUNC(sys_Cmd_Set_Scene_Anim_Wait);
SYSFUNC(sys_Cmd_Init_Scene_Anim);
SYSFUNC(sys_Cmd_Set_Scene_Animation_Active_Flag);
SYSFUNC(sys_Cmd_Draw_Scene_Anim_WSA_Frame);
SYSFUNC(sys_Cmd_Move_Scene_Anim);
SYSFUNC(sys_Cmd_Run_Actor_Default_Script);
SYSFUNC(sys_Cmd_Set_Location_Data);
SYSFUNC(sys_Cmd_Set_CountDown_Timer);
SYSFUNC(sys_Cmd_Query_CountDown_Timer);
SYSFUNC(sys_Cmd_Proceed_To_Next_Chapter);
SYSFUNC(sys_Cmd_Play_Sfx_Plus);
SYSFUNC(sys_Cmd_Play_Sfx);
SYSFUNC(sys_Cmd_Set_Ambient_Sfx);
SYSFUNC(sys_Cmd_Kill_Ambient_Sfx);
SYSFUNC(sys_Cmd_Set_Ambient_Sfx_Plus);
SYSFUNC(sys_Cmd_Set_Ambient_Volume);
SYSFUNC(sys_Cmd_Freeze_Scene_Animation);
SYSFUNC(sys_Cmd_Unfreeze_Scene_Animation);
SYSFUNC(sys_Cmd_Scene_Animation_Frozen);
SYSFUNC(sys_Cmd_Set_Script_Game_Data_Global);
SYSFUNC(sys_Cmd_Get_Script_Game_Data_Global);
SYSFUNC(sys_Cmd_Say_Line);
SYSFUNC(sys_Cmd_Knight_Puzzle_Get_Coord);
SYSFUNC(sys_Cmd_Add_Scene_Anim);
SYSFUNC(sys_Cmd_Remove_Scene_Anim);
SYSFUNC(sys_Cmd_Disable_Timer);
SYSFUNC(sys_Cmd_Enable_Timer);
SYSFUNC(sys_Cmd_Set_Timer);
SYSFUNC(sys_Cmd_Set_Palette_Color);
SYSFUNC(sys_Cmd_Number_Of_NPCs);
SYSFUNC(sys_Cmd_Get_Config_Language);
SYSFUNC(sys_Cmd_Get_Actor_Final_X);
SYSFUNC(sys_Cmd_Get_Actor_Final_Y);
};
} // End of namespace Toon
#endif

270
engines/toon/state.cpp Normal file
View File

@@ -0,0 +1,270 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, MojoTouch has
* exclusively licensed this code on March 23th, 2024, to be used in
* closed-source products.
* Therefore, any contributions (commits) to it will also be dual-licensed.
*
*/
#include "common/debug.h"
#include "toon/state.h"
#include "toon/toon.h"
namespace Toon {
void Location::save(Common::WriteStream *stream) {
stream->write(_cutaway, 64);
stream->write(_music, 64);
stream->write(_name, 64);
stream->writeSint16BE(_numRifBoxes);
stream->writeSint16BE(_numSceneAnimations);
stream->writeSByte(_visited);
for (int32 i = 0; i < _numRifBoxes * 2; i++) {
stream->writeSint16BE(_rifBoxesFlags[i]);
}
}
void Location::load(Common::ReadStream *stream) {
stream->read(_cutaway, 64);
stream->read(_music, 64);
stream->read(_name, 64);
_numRifBoxes = stream->readSint16BE();
_numSceneAnimations = stream->readSint16BE();
_visited = stream->readSByte();
for (int32 i = 0; i < _numRifBoxes * 2; i++) {
_rifBoxesFlags[i] = stream->readSint16BE();
}
}
State::State(void) {
for (int32 i = 0; i < 256; i++) {
_locations[i]._visited = false;
_locations[i]._numSceneAnimations = 0;
_locations[i]._numRifBoxes = 0;
}
memset(_gameFlag, 0, sizeof(_gameFlag));
memset(_gameGlobalData, -1, sizeof(_gameGlobalData));
for (int32 i = 0; i < 2; i++) {
_timerEnabled[i] = false;
_timerTimeout[i] = 0;
_timerDelay[i] = -1;
}
_lastVisitedScene = -1;
_currentScene = -1;
_currentScrollLock = false;
_currentScrollValue = 0;
_gameTimer = 0;
_currentChapter = 1;
_showConversationIcons = false;
_inMenu = false;
_inCloseUp = false;
_inConversation = false;
_mouseState = -1;
_mouseHidden = false;
_firstConverstationLine = false;
_sackVisible = false; // to change
_inCutaway = false;
_inInventory = false;
_numInventoryItems = 0; //To chhange
_numConfiscatedInventoryItems = 0;
_nextSpecialEnterX = -1;
_nextSpecialEnterY = -1;
#if 0
for (int i = 0; i < 30; i++) {
_inventory[i] = 90 + i;
if (_inventory[i] == 41)
_inventory[i] = 42;
}
_inventory[0] = 53;
_inventory[1] = 22;
_inventory[2] = 93;
_inventory[3] = 49;
_inventory[4] = 47;
_inventory[5] = 14;
_numInventoryItems = 6; //To change
#endif
memset(_conversationState, 0, sizeof(Conversation) * 60);
_conversationData = nullptr;
_currentConversationId = -1;
_exitConversation = true;
}
State::~State(void) {
}
int32 State::getGameFlag(int32 flagId) {
return (_gameFlag[flagId >> 3] & (1 << (flagId & 7))) != 0;
}
bool State::hasItemInInventory(int32 item) {
debugC(1, kDebugState, "hasItemInInventory(%d)", item);
for (int32 i = 0; i < _numInventoryItems; i++) {
if (_inventory[i] == item)
return true;
}
return false;
}
void State::save(Common::WriteStream *stream) {
for (int32 i = 0; i < 256; i++) {
_locations[i].save(stream);
}
for (int32 i = 0; i < 256; i++) {
stream->writeSint16BE(_gameGlobalData[i]);
}
for (int32 i = 0; i < 256; i++) {
stream->writeSint16BE(_gameFlag[i]);
}
stream->writeSint16BE(_lastVisitedScene);
stream->writeSint16BE(_currentScene);
stream->writeSint16BE(_currentScrollValue);
stream->writeSByte(_currentScrollLock);
for (int32 i = 0; i < 35; i++) {
stream->writeSint16BE(_inventory[i]);
}
for (int32 i = 0; i < 35; i++) {
stream->writeSint16BE(_confiscatedInventory[i]);
}
stream->writeSint32BE(_numInventoryItems);
stream->writeSint32BE(_numConfiscatedInventoryItems);
stream->writeSByte(_inCloseUp);
stream->writeSByte(_inCutaway);
stream->writeSByte(_inConversation);
stream->writeSByte(_inInventory);
stream->writeSByte(_showConversationIcons);
stream->writeSint16BE(_mouseState);
stream->writeSint16BE(_currentConversationId);
stream->writeSByte(_firstConverstationLine);
stream->writeSByte(_exitConversation);
stream->writeSByte(_mouseHidden);
stream->writeSByte(_sackVisible);
stream->writeSint32BE(_gameTimer);
stream->writeSByte(_currentChapter);
stream->writeByte(_timerEnabled[0]);
stream->writeByte(_timerEnabled[1]);
stream->writeSint32BE(_timerTimeout[0]);
stream->writeSint32BE(_timerTimeout[1]);
stream->writeSint32BE(_timerDelay[0]);
stream->writeSint32BE(_timerDelay[1]);
}
void State::load(Common::ReadStream *stream) {
for (int32 i = 0; i < 256; i++) {
_locations[i].load(stream);
}
for (int32 i = 0; i < 256; i++) {
_gameGlobalData[i] = stream->readSint16BE();
}
for (int32 i = 0; i < 256; i++) {
_gameFlag[i] = stream->readSint16BE();
}
_lastVisitedScene = stream->readSint16BE();
_currentScene = stream->readSint16BE();
_currentScrollValue = stream->readSint16BE();
_currentScrollLock = stream->readSByte();
for (int32 i = 0; i < 35; i++) {
_inventory[i] = stream->readSint16BE();
}
for (int32 i = 0; i < 35; i++) {
_confiscatedInventory[i] = stream->readSint16BE();
}
_numInventoryItems = stream->readSint32BE();
_numConfiscatedInventoryItems = stream->readSint32BE();
_inCloseUp = stream->readSByte();
_inCutaway = stream->readSByte();
_inConversation = stream->readSByte();
_inInventory = stream->readSByte();
_showConversationIcons = stream->readSByte();
_mouseState = stream->readSint16BE();
_currentConversationId = stream->readSint16BE();
_firstConverstationLine = stream->readSByte();
_exitConversation = stream->readSByte();
_mouseHidden = stream->readSByte();
_sackVisible = stream->readSByte();
_gameTimer = stream->readSint32BE();
_currentChapter = stream->readSByte();
_timerEnabled[0] = stream->readByte();
_timerEnabled[1] = stream->readByte();
_timerTimeout[0] = stream->readSint32BE();
_timerTimeout[1] = stream->readSint32BE();
_timerDelay[0] = stream->readSint32BE();
_timerDelay[1] = stream->readSint32BE();
}
void State::loadConversations(Common::ReadStream *stream) {
for (int32 i = 0; i < 60; i++) {
_conversationState[i].load(stream, _conversationData);
}
}
void State::saveConversations(Common::WriteStream *stream) {
for (int32 i = 0; i < 60; i++) {
_conversationState[i].save(stream, _conversationData);
}
}
} // End of namespace Toon

103
engines/toon/state.h Normal file
View File

@@ -0,0 +1,103 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, MojoTouch has
* exclusively licensed this code on March 23th, 2024, to be used in
* closed-source products.
* Therefore, any contributions (commits) to it will also be dual-licensed.
*
*/
#ifndef TOON_STATE_H
#define TOON_STATE_H
#include "common/file.h"
#include "common/str.h"
#include "toon/conversation.h"
namespace Toon {
struct Location {
char _name[64];
char _music[64];
char _cutaway[64];
bool _visited;
int32 _numSceneAnimations;
int32 _flags;
int32 _numRifBoxes;
int16 _rifBoxesFlags[256];
void save(Common::WriteStream *stream);
void load(Common::ReadStream *stream);
};
class State {
public:
State(void);
~State(void);
Location _locations[256];
int16 _gameGlobalData[256];
uint8 _gameFlag[256];
int16 _lastVisitedScene;
int16 _currentScene;
int16 _currentScrollValue;
bool _currentScrollLock;
int16 _inventory[35];
int16 _confiscatedInventory[35];
int32 _numInventoryItems;
int32 _numConfiscatedInventoryItems;
bool _inMenu;
bool _inCloseUp;
bool _inCutaway;
bool _inConversation;
bool _inInventory;
bool _showConversationIcons;
int16 _mouseState;
int16 *_conversationData;
Conversation _conversationState[60];
int16 _currentConversationId;
bool _firstConverstationLine;
bool _exitConversation;
bool _mouseHidden;
bool _sackVisible;
int32 _gameTimer;
int8 _currentChapter;
int32 _nextSpecialEnterX;
int32 _nextSpecialEnterY;
bool _timerEnabled[2];
int32 _timerTimeout[2];
int32 _timerDelay[2];
int32 getGameFlag(int32 flagId);
bool hasItemInInventory(int32 item);
void load(Common::ReadStream *stream);
void save(Common::WriteStream *stream);
void loadConversations(Common::ReadStream *stream);
void saveConversations(Common::WriteStream *stream);
};
} // End of namespace Toon
#endif

122
engines/toon/subtitles.cpp 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, MojoTouch has
* exclusively licensed this code on March 23th, 2024, to be used in
* closed-source products.
* Therefore, any contributions (commits) to it will also be dual-licensed.
*
*/
#include "common/debug.h"
#include "common/rect.h"
#include "common/system.h"
#include "toon/subtitles.h"
namespace Toon {
SubtitleRenderer::SubtitleRenderer(ToonEngine *vm) : _vm(vm) {
_subSurface = new Graphics::Surface();
_subSurface->create(TOON_SCREEN_WIDTH, TOON_SCREEN_HEIGHT, Graphics::PixelFormat::createFormatCLUT8());
_hasSubtitles = false;
}
SubtitleRenderer::~SubtitleRenderer() {
if (_subSurface) {
_subSurface->free();
delete _subSurface;
}
}
void SubtitleRenderer::render(const Graphics::Surface &frame, uint32 frameNumber, byte color) {
if (!_hasSubtitles || _tw.empty() || !_vm->showConversationText()) {
return;
}
_subSurface->copyFrom(frame);
// char strf[384] = {0};
// Common::sprintf_s(strf, "Time passed: %d", frameNumber);
// _vm->drawCostumeLine(0, 0, strf, _subSurface);
// _vm->_system->copyRectToScreen(_subSurface->getBasePtr(0, 0), _subSurface->pitch, 0, 0, _subSurface->w, _subSurface->h);
if (frameNumber > _tw.front()._endFrame) {
_tw.pop_front();
if (_tw.empty()) {
return;
}
}
if (frameNumber < _tw.front()._startFrame) {
return;
}
_vm->drawCustomText(TOON_SCREEN_WIDTH / 2, TOON_SCREEN_HEIGHT, _tw.front()._text.c_str(), _subSurface, color);
_vm->_system->copyRectToScreen(_subSurface->getBasePtr(0, 0), _subSurface->pitch, 0, 0, _subSurface->w, _subSurface->h);
}
bool SubtitleRenderer::load(const Common::Path &video) {
// warning(video.c_str());
_hasSubtitles = false;
Common::String subfile(video.baseName());
Common::String ext("tss");
subfile.replace(subfile.size() - ext.size(), ext.size(), ext);
Common::ScopedPtr<Common::SeekableReadStream> subsStream(_vm->resources()->openFile(Common::Path(subfile)));
if (subsStream == nullptr) {
return false;
}
Common::String line;
int lineNo = 0;
_tw.clear();
while (!subsStream->eos() && !subsStream->err()) {
line = subsStream->readLine();
lineNo++;
if (line.empty() || line[0] == '#') {
continue;
}
const char *ptr = line.c_str();
int startFrame = strtoul(ptr, const_cast<char **>(&ptr), 10);
int endFrame = strtoul(ptr, const_cast<char **>(&ptr), 10);
while (*ptr && Common::isSpace(*ptr))
ptr++;
if (startFrame > endFrame) {
warning("%s:%d: startFrame (%d) > endFrame (%d)", subfile.c_str(), lineNo, startFrame, endFrame);
continue;
}
_tw.push_back(TimeWindow(startFrame, endFrame, ptr));
}
_hasSubtitles = true;
return _hasSubtitles;
}
}

65
engines/toon/subtitles.h Normal file
View File

@@ -0,0 +1,65 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, MojoTouch has
* exclusively licensed this code on March 23th, 2024, to be used in
* closed-source products.
* Therefore, any contributions (commits) to it will also be dual-licensed.
*
*/
#ifndef TOON_SUBTITLES_H
#define TOON_SUBTITLES_H
#include "graphics/surface.h"
#include "toon/toon.h"
namespace Toon {
class TimeWindow {
public:
uint16 _startFrame;
uint16 _endFrame;
Common::String _text;
TimeWindow(int startFrame, int endFrame, const Common::String &text) {
_startFrame = startFrame;
_endFrame = endFrame;
_text = text;
}
};
class SubtitleRenderer {
public:
SubtitleRenderer(ToonEngine *vm);
~SubtitleRenderer();
bool load(const Common::Path &video);
void render(const Graphics::Surface &frame, uint32 frameNumber, byte color);
protected:
ToonEngine *_vm;
Graphics::Surface *_subSurface;
bool _hasSubtitles;
Common::List<TimeWindow> _tw;
};
} // End of namespace Toon
#endif

100
engines/toon/text.cpp Normal file
View File

@@ -0,0 +1,100 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, MojoTouch has
* exclusively licensed this code on March 23th, 2024, to be used in
* closed-source products.
* Therefore, any contributions (commits) to it will also be dual-licensed.
*
*/
#include "common/debug.h"
#include "toon/text.h"
namespace Toon {
TextResource::TextResource(ToonEngine *vm) : _vm(vm) {
_numTexts = 0;
_textData = nullptr;
}
TextResource::~TextResource(void) {
delete[] _textData;
}
bool TextResource::loadTextResource(const Common::Path &fileName) {
debugC(1, kDebugText, "loadTextResource(%s)", fileName.toString().c_str());
uint32 fileSize = 0;
uint8 *data = _vm->resources()->getFileData(fileName, &fileSize);
if (!data)
return false;
delete[] _textData;
_textData = new uint8[fileSize];
memcpy(_textData, data, fileSize);
_numTexts = READ_LE_UINT16(data);
return true;
}
int32 TextResource::getNext(int32 offset) {
debugC(1, kDebugText, "getNext(%d)", offset);
uint16 *table = (uint16 *)_textData + 1;
int a = getId(offset);
return READ_LE_UINT16(table + a + 1);
}
int32 TextResource::getId(int32 offset) {
debugC(1, kDebugText, "getId(%d)", offset);
uint16 *table = (uint16 *)_textData + 1;
int32 found = -1;
for (int32 i = 0; i < _numTexts; i++) {
if (offset == READ_LE_UINT16(table + i)) {
found = i;
break;
}
}
return found;
}
char *TextResource::getText(int32 offset) {
debugC(6, kDebugText, "getText(%d)", offset);
uint16 *table = (uint16 *)_textData + 1;
int32 found = -1;
for (int32 i = 0; i < _numTexts; i++) {
if (offset == READ_LE_UINT16(table + i)) {
found = i;
break;
}
}
if (found < 0)
return nullptr;
int32 realOffset = READ_LE_UINT16((uint16 *)_textData + 1 + _numTexts + found);
return (char *)_textData + realOffset;
}
} // End of namespace Toon

54
engines/toon/text.h Normal file
View File

@@ -0,0 +1,54 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, MojoTouch has
* exclusively licensed this code on March 23th, 2024, to be used in
* closed-source products.
* Therefore, any contributions (commits) to it will also be dual-licensed.
*
*/
#ifndef TOON_TEXT_H
#define TOON_TEXT_H
#include "toon/toon.h"
namespace Toon {
class TextResource {
public:
TextResource(ToonEngine *vm);
~TextResource(void);
bool loadTextResource(const Common::Path &fileName);
char *getText(int32 id);
int32 getId(int32 offset);
int32 getNext(int32 offset);
protected:
int32 _numTexts;
uint8 *_textData;
ToonEngine *_vm;
};
} // End of namespace Toon
#endif

138
engines/toon/tools.cpp Normal file
View File

@@ -0,0 +1,138 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, MojoTouch has
* exclusively licensed this code on March 23th, 2024, to be used in
* closed-source products.
* Therefore, any contributions (commits) to it will also be dual-licensed.
*
*/
#include "common/debug.h"
#include "toon/tools.h"
#include "toon/toon.h"
namespace Toon {
uint32 decompressLZSS(byte *src, byte *dst, int dstsize) {
debugC(5, kDebugTools, "decompressLZSS(src, dst, %d)", dstsize);
byte *srcp = src;
byte *dstp = dst;
uint16 bitbuf;
int32 len, ofs;
len = 0;
while (dstsize > 0) {
bitbuf = 0x100 | *(srcp++);
while (bitbuf != 1 && dstsize > 0) {
if (bitbuf & 1) {
ofs = READ_LE_UINT16(srcp);
srcp += 2;
len = ((ofs & 0xF000) >> 12) + 3;
ofs = ofs | 0xF000;
dstsize -= len;
if (dstsize < 0)
break;
while (len--) {
*dstp = *(byte *)(dstp + (signed short)ofs);
dstp++;
}
} else {
len = 0;
while ((bitbuf & 2) == 0) {
len++;
bitbuf >>= 1;
}
len++;
dstsize -= len;
if (dstsize < 0)
break;
while (len--)
*(dstp++) = *(srcp++);
}
bitbuf >>= 1;
}
}
if (len == -1 && dstsize == 0) {
return (dstp - dst);
}
len += dstsize;
if (len < 0)
return 0;
while (len--)
*(dstp++) = *(srcp++);
return (dstp - dst);
}
uint32 decompressSPCN(byte *src, byte *dst, uint32 dstsize) {
debugC(1, kDebugTools, "decompressSPCN(src, dst, %d)", dstsize);
byte *srcp = src;
byte *dstp = dst, *dste = dst + dstsize;
byte val;
uint16 len, ofs;
if (!(*srcp & 0x80)) srcp++;
while (dstp < dste) {
val = *(srcp++);
if (val & 0x80) {
if (val & 0x40) {
if (val == 0xFE) {
len = READ_LE_UINT16(srcp);
while (len--)
*(dstp++) = srcp[2];
srcp += 3;
} else {
if (val == 0xFF) {
len = READ_LE_UINT16(srcp);
srcp += 2;
} else {
len = (val & 0x3F) + 3;
}
ofs = READ_LE_UINT16(srcp);
srcp += 2;
while (len--) {
*dstp = *(byte *)(dstp - ofs);
dstp++;
}
}
} else {
len = val & 0x3F;
while (len--)
*(dstp++) = *(srcp++);
}
} else {
len = (val >> 4) + 3;
ofs = ((val & 0x0F) << 8) | *(srcp++);
while (len--) {
*dstp = *(byte *)(dstp - ofs);
dstp++;
}
}
}
return (dstp - dst);
}
} // End of namespace Toon

47
engines/toon/tools.h Normal file
View File

@@ -0,0 +1,47 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, MojoTouch has
* exclusively licensed this code on March 23th, 2024, to be used in
* closed-source products.
* Therefore, any contributions (commits) to it will also be dual-licensed.
*
*/
#ifndef TOON_TOOLS_H
#define TOON_TOOLS_H
#include "common/scummsys.h"
#include "common/endian.h"
namespace Toon {
const uint32 kCompLZSS = 0x4C5A5353;
const uint32 kCompSPCN = 0x5350434E;
const uint32 kCompRNC1 = 0x524E4301;
const uint32 kCompRNC2 = 0x524E4302;
uint32 decompressSPCN(byte *src, byte *dst, uint32 dstsize);
uint32 decompressLZSS(byte *src, byte *dst, int dstsize);
} // End of namespace Toon
#endif

5710
engines/toon/toon.cpp Normal file

File diff suppressed because it is too large Load Diff

474
engines/toon/toon.h Normal file
View File

@@ -0,0 +1,474 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* This file is dual-licensed.
* In addition to the GPLv3 license mentioned above, MojoTouch has
* exclusively licensed this code on March 23th, 2024, to be used in
* closed-source products.
* Therefore, any contributions (commits) to it will also be dual-licensed.
*
*/
#ifndef TOON_TOON_H
#define TOON_TOON_H
#include "engines/engine.h"
#include "graphics/surface.h"
#include "common/random.h"
#include "common/error.h"
#include "toon/resource.h"
#include "toon/script.h"
#include "toon/script_func.h"
#include "toon/state.h"
#include "toon/picture.h"
#include "toon/anim.h"
#include "toon/movie.h"
#include "toon/font.h"
#include "toon/text.h"
#include "toon/audio.h"
#include "toon/console.h"
namespace Common {
class MemoryWriteStreamDynamic;
}
struct ADGameDescription;
#define TOON_DAT_VER_MAJ 0 // 1 byte
#define TOON_DAT_VER_MIN 4 // 1 byte
#define TOON_SAVEGAME_VERSION 6
#define DATAALIGNMENT 4
#define MAX_SAVE_SLOT 99
#define TOON_SCREEN_WIDTH 640
#define TOON_SCREEN_HEIGHT 400
#define TOON_BACKBUFFER_WIDTH 1280
#define TOON_BACKBUFFER_HEIGHT 400
/**
* This is the namespace of the Toon engine.
*
* Status of this engine: ???
*
* Games using this engine:
* - Toonstruck
*/
namespace Toon {
enum TOONAction {
kActionNone,
kActionEscape,
kActionStopCurrentVoice,
kActionSaveGame,
kActionLoadGame,
kActionSubtitles,
kActionMuteMusic,
kActionSpeechMute,
kActionSFXMute,
kActionShowOptions
};
enum ToonGameType {
GType_TOON = 1
};
enum ToonDebugChannels {
kDebugAnim = 1,
kDebugCharacter,
kDebugAudio,
kDebugHotspot,
kDebugFont,
kDebugPath,
kDebugMovie,
kDebugPicture,
kDebugResource,
kDebugState,
kDebugTools,
kDebugText,
};
class Picture;
class Movie;
class Hotspots;
class Character;
class CharacterDrew;
class CharacterFlux;
class FontRenderer;
class TextResource;
class AudioManager;
class PathFinding;
class ToonEngine : public Engine {
public:
ToonEngine(OSystem *syst, const ADGameDescription *gameDescription);
~ToonEngine() override;
const ADGameDescription *_gameDescription;
Common::Language _language;
byte _numVariant;
byte _gameVariant;
char **_locationDirNotVisited;
char **_locationDirVisited;
char **_specialInfoLine;
Common::Error run() override;
bool showMainMenu(bool &loadedGame);
bool showOptions();
bool showQuitConfirmationDialogue();
void init();
bool loadToonDat();
char **loadTextsVariants(Common::File &in);
void unloadTextsVariants(char **texts);
void unloadToonDat();
void setPaletteEntries(uint8 *palette, int32 offset, int32 num);
void fixPaletteEntries(uint8 *palette, int num);
void flushPalette(bool deferFlushToNextRender = true);
void parseInput();
void initChapter();
void initFonts();
void setFont(bool alternative);
void loadScene(int32 SceneId, bool forGameLoad = false);
void exitScene();
void loadCursor();
void setCursor(int32 type, bool inventory = false, int32 offsetX = 0, int offsetY = 0);
void loadAdditionalPalette(const Common::Path &fileName, int32 mode);
void setupGeneralPalette();
void render();
void update(int32 timeIncrement);
void doFrame();
void updateAnimationSceneScripts(int32 timeElapsed);
void updateCharacters(int32 timeElapsed);
void setSceneAnimationScriptUpdate(bool enable);
bool isUpdatingSceneAnimation();
int32 getCurrentUpdatingSceneAnimation();
int32 randRange(int32 minStart, int32 maxStart);
void selectHotspot();
void clickEvent();
int32 runEventScript(int32 x, int32 y, int32 mode, int32 id, int32 scriptId);
void flipScreens();
void drawInfoLine();
void drawConversationLine();
const char *getLocationString(int32 locationId, bool alreadyVisited);
int32 getScaleAtPoint(int32 x, int32 y);
int32 getZAtPoint(int32 x, int32 y);
int32 getLayerAtPoint(int32 x, int32 y);
int32 characterTalk(int32 dialogid, bool blocking = true);
int32 simpleCharacterTalk(int32 dialogid);
void sayLines(int numLines, int dialogId);
void haveAConversation(int32 convId);
void processConversationClick(Conversation *conv, int32 status);
int32 runConversationCommand(int16 **command);
void prepareConversations();
void drawConversationIcons();
void simpleUpdate(bool waitCharacterToTalk = false);
int32 waitTicks(int32 numTicks, bool breakOnMouseClick);
void copyToVirtualScreen(bool updateScreen = true);
void getMouseEvent();
int32 showInventory();
void drawSack();
void addItemToInventory(int32 item);
void deleteItemFromInventory(int32 item);
void replaceItemFromInventory(int32 item, int32 destItem);
void rearrangeInventory();
void createMouseItem(int32 item);
void deleteMouseItem();
void showCutaway(const Common::Path &cutawayPicture);
void hideCutaway();
void drawPalette();
void newGame();
void playSoundWrong();
void playSFX(int32 id, int32 volume);
void storeRifFlags(int32 location);
void restoreRifFlags(int32 location);
void getTextPosition(int32 characterId, int32 *retX, int32 *retY);
int32 getConversationFlag(int32 locationId, int32 param);
int32 getSpecialInventoryItem(int32 item);
Character *getCharacterById(int32 charId);
Common::String getSavegameName(int nr);
bool loadGame(int32 slot);
bool saveGame(int32 slot, const Common::String &saveGameDesc);
void fadeIn(int32 numFrames);
void fadeOut(int32 numFrames);
void initCharacter(int32 characterId, int32 animScriptId, int32 animToPlayId, int32 sceneAnimationId);
int32 handleInventoryOnFlux(int32 itemId);
int32 handleInventoryOnInventory(int32 itemDest, int32 itemSrc);
int32 handleInventoryOnDrew(int32 itemId);
int32 pauseSceneAnimationScript(int32 animScriptId, int32 tickToWait);
void updateTimer(int32 timeIncrement);
Common::Path createRoomFilename(const Common::String &name);
void createShadowLUT();
void playTalkAnimOnCharacter(int32 animID, int32 characterId, bool talker);
void updateScrolling(bool force, int32 timeIncrement);
void enableTimer(int32 timerId);
void setTimer(int32 timerId, int32 timerWait);
void disableTimer(int32 timerId);
void updateTimers();
void makeLineNonWalkable(int32 x, int32 y, int32 x2, int32 y2);
void makeLineWalkable(int32 x, int32 y, int32 x2, int32 y2);
void renderInventory();
void viewInventoryItem(const Common::Path &str, int32 lineId, int32 itemDest);
void storePalette();
void restorePalette();
const char *getSpecialConversationMusic(int32 locationId);
void playRoomMusic();
void waitForScriptStep();
void doMagnifierEffect();
void drawCustomText(int16 x, int16 y, const char *line, Graphics::Surface *frame, byte color);
bool showConversationText() const;
bool canSaveGameStateCurrently(Common::U32String *msg = nullptr) override;
bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override;
void pauseEngineIntern(bool pause) override;
void syncSoundSettings() override;
Resources *resources() {
return _resources;
}
State *state() {
return _gameState;
}
Graphics::Surface &getMainSurface() {
return *_mainSurface;
}
Picture *getMask() {
return _currentMask;
}
Picture *getPicture() {
return _currentPicture;
}
AnimationManager *getAnimationManager() {
return _animationManager;
}
Movie *getMoviePlayer() {
return _moviePlayer;
}
SceneAnimation *getSceneAnimation(int32 id) {
return &_sceneAnimations[id];
}
SceneAnimationScript *getSceneAnimationScript(int32 id) {
return &_sceneAnimationScripts[id];
}
EMCInterpreter *getScript() {
return _script;
}
Hotspots *getHotspots() {
return _hotspots;
}
Character *getCharacter(int32 charId) {
return _characters[charId];
}
uint8 *getShadowLUT() {
return _shadowLUT;
}
int32 getCurrentLineToSay() {
return _currentTextLineId;
}
int32 getCurrentCharacterTalking() {
return _currentTextLineCharacterId;
}
CharacterDrew *getDrew() {
return (CharacterDrew *)_drew;
}
CharacterFlux *getFlux() {
return (CharacterFlux *)_flux;
}
int32 getTickLength() {
return _tickLength;
}
int32 getOldMilli() {
return _oldTimer2;
}
AudioManager *getAudioManager() {
return _audioManager;
}
int32 getScriptRegionNested() {
return _currentScriptRegion;
}
int32 getMouseX() {
return _mouseX;
}
int32 getMouseY() {
return _mouseY;
}
PathFinding *getPathFinding() {
return _pathFinding;
}
bool isEnglishDemo() {
return _isEnglishDemo;
}
Common::WriteStream *getSaveBufferStream();
bool shouldQuitGame() const {
return _shouldQuit;
}
Common::Error saveGameState(int slot, const Common::String &desc, bool isAutosave = false) override {
return (saveGame(slot, desc) ? Common::kNoError : Common::kWritingFailed);
}
Common::Error loadGameState(int slot) override {
return (loadGame(slot) ? Common::kNoError : Common::kReadingFailed);
}
bool hasFeature(EngineFeature f) const override {
return
(f == kSupportsSubtitleOptions) ||
(f == kSupportsReturnToLauncher) ||
(f == kSupportsLoadingDuringRuntime) ||
(f == kSupportsSavingDuringRuntime);
}
void dirtyAllScreen();
void addDirtyRect(int32 left, int32 top, int32 right, int32 bottom);
void clearDirtyRects();
protected:
int32 _tickLength;
Resources *_resources;
TextResource *_genericTexts;
TextResource *_roomTexts;
State *_gameState;
uint8 *_finalPalette;
uint8 *_backupPalette;
uint8 *_additionalPalette1;
uint8 *_additionalPalette2;
bool _additionalPalette2Present;
uint8 *_cutawayPalette;
uint8 *_universalPalette;
uint8 *_fluxPalette;
uint8 *_roomScaleData;
uint8 *_shadowLUT;
Picture *_currentPicture;
Picture *_currentMask;
Picture *_currentCutaway;
Picture *_inventoryPicture;
PathFinding *_pathFinding;
EMCInterpreter *_script;
EMCData _scriptData;
EMCState _scriptState[4];
int32 _currentScriptRegion; // script region ( nested script run )
ScriptFunc *_script_func;
SceneAnimation _sceneAnimations[64];
SceneAnimationScript _sceneAnimationScripts[64];
int32 _lastProcessedSceneScript;
bool _animationSceneScriptRunFlag;
bool _updatingSceneScriptRunFlag;
Graphics::Surface *_mainSurface;
Common::Array<Common::Rect> _dirtyRects;
Common::Array<Common::Rect> _oldDirtyRects;
bool _dirtyAll;
AnimationInstance *_cursorAnimationInstance;
Animation *_cursorAnimation;
Animation *_dialogIcons;
Animation *_inventoryIcons;
Animation *_inventoryIconSlots;
int32 _cursorOffsetX;
int32 _cursorOffsetY;
char *_currentTextLine;
int32 _currentTextLineId;
int32 _currentTextLineX;
int32 _currentTextLineY;
int32 _currentTextLineCharacterId;
int32 _oldScrollValue;
AnimationManager *_animationManager;
Character *_characters[32];
Character *_drew;
Character *_flux;
Hotspots *_hotspots;
int32 _currentHotspotItem;
bool _shouldQuit;
int32 _scriptStep;
int32 _mouseX;
int32 _mouseY;
int32 _mouseButton;
int32 _lastMouseButton;
int32 _oldTimer;
int32 _oldTimer2;
int32 _lastRenderTime;
Movie *_moviePlayer;
Common::RandomSource _rnd;
FontRenderer *_fontRenderer;
Animation *_fontToon;
Animation *_fontEZ;
Animation *_currentFont;
Common::String *_currentDemoFont;
AudioManager *_audioManager;
Common::MemoryWriteStreamDynamic *_saveBufferStream;
int16 *_conversationData;
bool _firstFrame;
bool _isDemo;
bool _isEnglishDemo;
bool _showConversationText;
int _textSpeed;
bool _useAlternativeFont;
bool _needPaletteFlush;
bool _noMusicDriver; // If "Music Device" is set to "No Music" from Audio tab
};
} // End of namespace Toon
#endif