Initial commit
This commit is contained in:
2
engines/toon/POTFILES
Normal file
2
engines/toon/POTFILES
Normal file
@@ -0,0 +1,2 @@
|
||||
engines/toon/metaengine.cpp
|
||||
engines/toon/toon.cpp
|
||||
797
engines/toon/anim.cpp
Normal file
797
engines/toon/anim.cpp
Normal 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
205
engines/toon/anim.h
Normal 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
638
engines/toon/audio.cpp
Normal 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
188
engines/toon/audio.h
Normal 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
1433
engines/toon/character.cpp
Normal file
File diff suppressed because it is too large
Load Diff
156
engines/toon/character.h
Normal file
156
engines/toon/character.h
Normal 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
|
||||
3
engines/toon/configure.engine
Normal file
3
engines/toon/configure.engine
Normal 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
34
engines/toon/console.cpp
Normal 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
42
engines/toon/console.h
Normal 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
|
||||
51
engines/toon/conversation.cpp
Normal file
51
engines/toon/conversation.cpp
Normal 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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
53
engines/toon/conversation.h
Normal file
53
engines/toon/conversation.h
Normal 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
3
engines/toon/credits.pl
Normal file
@@ -0,0 +1,3 @@
|
||||
begin_section("Toon");
|
||||
add_person("Sylvain Dupont", "SylvainTV", "");
|
||||
end_section();
|
||||
196
engines/toon/detection.cpp
Normal file
196
engines/toon/detection.cpp
Normal 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
139
engines/toon/drew.cpp
Normal 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
55
engines/toon/drew.h
Normal 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
145
engines/toon/flux.cpp
Normal 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
54
engines/toon/flux.h
Normal 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
512
engines/toon/font.cpp
Normal 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
96
engines/toon/font.h
Normal 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
159
engines/toon/hotspot.cpp
Normal 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
76
engines/toon/hotspot.h
Normal 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
267
engines/toon/metaengine.cpp
Normal 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
35
engines/toon/module.mk
Normal 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
188
engines/toon/movie.cpp
Normal 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
73
engines/toon/movie.h
Normal 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
459
engines/toon/path.cpp
Normal 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
105
engines/toon/path.h
Normal 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
365
engines/toon/picture.cpp
Normal 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
73
engines/toon/picture.h
Normal 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
303
engines/toon/resource.cpp
Normal 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
103
engines/toon/resource.h
Normal 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
524
engines/toon/script.cpp
Normal 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
156
engines/toon/script.h
Normal 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
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
177
engines/toon/script_func.h
Normal 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
270
engines/toon/state.cpp
Normal 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
103
engines/toon/state.h
Normal 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
122
engines/toon/subtitles.cpp
Normal 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
65
engines/toon/subtitles.h
Normal 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
100
engines/toon/text.cpp
Normal 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
54
engines/toon/text.h
Normal 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
138
engines/toon/tools.cpp
Normal 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
47
engines/toon/tools.h
Normal 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
5710
engines/toon/toon.cpp
Normal file
File diff suppressed because it is too large
Load Diff
474
engines/toon/toon.h
Normal file
474
engines/toon/toon.h
Normal 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
|
||||
Reference in New Issue
Block a user