Eugene Sandulenko 09ad067fa5 DIRECTOR: Coordinates are in fact signed. Check for sanity.
Original parses negative widths and just skips such sprites.
Do the same.
2020-06-01 18:52:43 +02:00

530 lines
16 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "common/system.h"
#include "common/substream.h"
#include "graphics/primitives.h"
#include "director/director.h"
#include "director/cast.h"
#include "director/frame.h"
#include "director/score.h"
#include "director/sprite.h"
#include "director/util.h"
namespace Director {
Frame::Frame(DirectorEngine *vm, int numChannels) {
_vm = vm;
_transDuration = 0;
_transType = kTransNone;
_transArea = 0;
_transChunkSize = 0;
_tempo = 0;
_numChannels = numChannels;
_sound1 = 0;
_sound2 = 0;
_soundType1 = 0;
_soundType2 = 0;
_actionId = 0;
_skipFrameFlag = 0;
_blend = 0;
_palette = NULL;
_colorTempo = 0;
_colorSound1 = 0;
_colorSound2 = 0;
_colorScript = 0;
_colorTrans = 0;
_sprites.resize(_numChannels + 1);
for (uint16 i = 0; i < _sprites.size(); i++) {
Sprite *sp = new Sprite();
_sprites[i] = sp;
}
}
Frame::Frame(const Frame &frame) {
_vm = frame._vm;
_numChannels = frame._numChannels;
_actionId = frame._actionId;
_transArea = frame._transArea;
_transDuration = frame._transDuration;
_transType = frame._transType;
_transChunkSize = frame._transChunkSize;
_tempo = frame._tempo;
_sound1 = frame._sound1;
_sound2 = frame._sound2;
_soundType1 = frame._soundType1;
_soundType2 = frame._soundType2;
_skipFrameFlag = frame._skipFrameFlag;
_blend = frame._blend;
_colorTempo = frame._colorTempo;
_colorSound1 = frame._colorSound1;
_colorSound2 = frame._colorSound2;
_colorScript = frame._colorScript;
_colorTrans = frame._colorTrans;
_palette = new PaletteInfo();
debugC(1, kDebugLoading, "Frame. action: %d transType: %d transDuration: %d", _actionId, _transType, _transDuration);
_sprites.resize(_numChannels + 1);
for (uint16 i = 0; i <= _numChannels; i++) {
_sprites[i] = new Sprite(*frame._sprites[i]);
}
}
Frame::~Frame() {
delete _palette;
for (uint16 i = 0; i < _sprites.size(); i++)
delete _sprites[i];
}
void Frame::readChannel(Common::SeekableSubReadStreamEndian &stream, uint16 offset, uint16 size) {
if (offset >= 32) {
if (size <= 16)
readSprite(stream, offset, size);
else {
// read > 1 sprites channel
while (size > 16) {
byte spritePosition = (offset - 32) / 16;
uint16 nextStart = (spritePosition + 1) * 16 + 32;
uint16 needSize = nextStart - offset;
readSprite(stream, offset, needSize);
offset += needSize;
size -= needSize;
}
readSprite(stream, offset, size);
}
} else {
readMainChannels(stream, offset, size);
}
}
void Frame::readChannels(Common::ReadStreamEndian *stream) {
byte unk[48];
if (_vm->getVersion() < 4) {
// Sound/Tempo/Transition
_actionId = stream->readByte();
_soundType1 = stream->readByte(); // type: 0x17 for sounds (sound is cast id), 0x16 for MIDI (sound is cmd id)
uint8 transFlags = stream->readByte(); // 0x80 is whole stage (vs changed area), rest is duration in 1/4ths of a second
if (transFlags & 0x80)
_transArea = 1;
else
_transArea = 0;
_transDuration = (transFlags & 0x7f) * 250; // Duration is in 1/4 secs
_transChunkSize = stream->readByte();
_tempo = stream->readByte();
_transType = static_cast<TransitionType>(stream->readByte());
_sound1 = stream->readUint16();
if (_vm->getPlatform() == Common::kPlatformMacintosh) {
_sound2 = stream->readUint16();
_soundType2 = stream->readByte();
} else {
stream->read(unk, 3);
if (unk[0] != 0 || unk[1] != 0 || unk[2] != 0)
warning("Frame::readChannels(): unk1: %x unk2: %x unk3: %x", unk[0], unk[1], unk[2]);
}
_skipFrameFlag = stream->readByte();
_blend = stream->readByte();
if (_vm->getPlatform() != Common::kPlatformMacintosh) {
_sound2 = stream->readUint16();
_soundType2 = stream->readByte();
}
// palette
uint16 palette = stream->readUint16();
if (palette) {
warning("Frame::readChannels(): STUB: Palette info");
}
debugC(8, kDebugLoading, "Frame::readChannels(): %d %d %d %d %d %d %d %d %d %d %d", _actionId, _soundType1, _transDuration, _transChunkSize, _tempo, _transType, _sound1, _skipFrameFlag, _blend, _sound2, _soundType2);
_palette = new PaletteInfo();
_palette->firstColor = stream->readByte(); // for cycles. note: these start at 0x80 (for pal entry 0)!
_palette->lastColor = stream->readByte();
_palette->flags = stream->readByte();
_palette->speed = stream->readByte();
_palette->frameCount = stream->readUint16();
_palette->cycleCount = stream->readUint16();
stream->read(unk, 6);
if (_vm->getPlatform() == Common::kPlatformMacintosh)
stream->read(unk, 3);
} else if (_vm->getVersion() == 4) {
// Sound/Tempo/Transition
_actionId = stream->readByte();
_soundType1 = stream->readByte(); // type: 0x17 for sounds (sound is cast id), 0x16 for MIDI (sound is cmd id)
uint8 transFlags = stream->readByte(); // 0x80 is whole stage (vs changed area), rest is duration in 1/4ths of a second
if (transFlags & 0x80)
_transArea = 1;
else
_transArea = 0;
_transDuration = (transFlags & 0x7f) * 250; // Duration is 1/4 secs
_transChunkSize = stream->readByte();
_tempo = stream->readByte();
_transType = static_cast<TransitionType>(stream->readByte());
_sound1 = stream->readUint16();
_sound2 = stream->readUint16();
_soundType2 = stream->readByte();
_skipFrameFlag = stream->readByte();
_blend = stream->readByte();
_colorTempo = stream->readByte();
_colorSound1 = stream->readByte();
_colorSound2 = stream->readByte();
_actionId = stream->readUint16();
_colorScript = stream->readByte();
_colorTrans = stream->readByte();
// palette
uint16 palette = stream->readUint16();
if (palette) {
warning("Frame::readChannels(): STUB: Palette info");
}
debugC(8, kDebugLoading, "Frame::readChannels(): %d %d %d %d %d %d %d %d %d %d %d", _actionId, _soundType1, _transDuration, _transChunkSize, _tempo, _transType, _sound1, _skipFrameFlag, _blend, _sound2, _soundType2);
_palette = new PaletteInfo();
_palette->firstColor = stream->readByte(); // for cycles. note: these start at 0x80 (for pal entry 0)!
_palette->lastColor = stream->readByte();
_palette->flags = stream->readByte();
_palette->speed = stream->readByte();
_palette->frameCount = stream->readUint16();
_palette->cycleCount = stream->readUint16();
_palette->fade = stream->readByte();
_palette->delay = stream->readByte();
_palette->style = stream->readByte();
stream->readByte();
stream->readUint16();
stream->readUint16();
_palette->colorCode = stream->readByte();
stream->readByte();
} else if (_vm->getVersion() == 5) {
// Sound/Tempo/Transition channel
stream->read(unk, 24);
// palette
stream->read(unk, 24);
} else {
// Sound[2]
// palette
// Transition
// Tempo
// Script
}
_transChunkSize = CLIP<byte>(_transChunkSize, 0, 128);
_transDuration = CLIP<uint16>(_transDuration, 0, 32000); // restrict to 32 secs
for (int i = 0; i < _numChannels; i++) {
Sprite &sprite = *_sprites[i + 1];
if (_vm->getVersion() <= 4) {
sprite._scriptId = stream->readByte();
sprite._spriteType = (SpriteType)stream->readByte();
sprite._enabled = sprite._spriteType != kInactiveSprite;
if (_vm->getVersion() == 4) {
sprite._foreColor = _vm->transformColor((uint8)stream->readByte());
sprite._backColor = _vm->transformColor((uint8)stream->readByte());
} else {
// Normalize D2 and D3 colors from -128 ... 127 to 0 ... 255.
sprite._foreColor = _vm->transformColor((128 + stream->readByte()) & 0xff);
sprite._backColor = _vm->transformColor((128 + stream->readByte()) & 0xff);
}
sprite._thickness = stream->readByte();
sprite._inkData = stream->readByte();
sprite._castId = stream->readUint16();
sprite._startPoint.y = (int16)stream->readUint16();
sprite._startPoint.x = (int16)stream->readUint16();
sprite._currentPoint = sprite._startPoint;
sprite._height = (int16)stream->readUint16();
sprite._width = (int16)stream->readUint16();
if (_vm->getVersion() == 4) {
sprite._scriptId = stream->readUint16();
// & 0x0f scorecolor
// 0x10 forecolor is rgb
// 0x20 bgcolor is rgb
// 0x40 editable
// 0x80 moveable
sprite._colorcode = stream->readByte();
sprite._blendAmount = stream->readByte();
}
} else if (_vm->getVersion() == 5) {
sprite._spriteType = (SpriteType)stream->readByte();
sprite._inkData = stream->readByte();
sprite._castIndex = stream->readUint16();
sprite._castId = stream->readUint16();
sprite._scriptCastIndex = stream->readUint16();
sprite._scriptId = stream->readUint16();
sprite._foreColor = _vm->transformColor((uint8)stream->readByte());
sprite._backColor = _vm->transformColor((uint8)stream->readByte());
sprite._startPoint.y = (int16)stream->readUint16();
sprite._startPoint.x = (int16)stream->readUint16();
sprite._height = (int16)stream->readUint16();
sprite._width = (int16)stream->readUint16();
sprite._colorcode = stream->readByte();
sprite._blendAmount = stream->readByte();
sprite._thickness = stream->readByte();
stream->readByte(); // unused
} else if (_vm->getVersion() == 6) {
sprite._spriteType = (SpriteType)stream->readByte();
sprite._inkData = stream->readByte();
sprite._foreColor = _vm->transformColor((uint8)stream->readByte());
sprite._backColor = _vm->transformColor((uint8)stream->readByte());
sprite._castIndex = stream->readUint16();
sprite._castId = stream->readUint16();
/* uint32 spriteId = */stream->readUint32();
sprite._startPoint.y = (int16)stream->readUint16();
sprite._startPoint.x = (int16)stream->readUint16();
sprite._height = (int16)stream->readUint16();
sprite._width = (int16)stream->readUint16();
sprite._colorcode = stream->readByte();
sprite._blendAmount = stream->readByte();
sprite._thickness = stream->readByte();
stream->readByte(); // unused
}
// Sometimes removed sprites leave garbage in the channel
// We set it to zero, so then could skip
if (sprite._width <= 0 || sprite._height <= 0)
sprite._width = sprite._height = 0;
sprite._ink = static_cast<InkType>(sprite._inkData & 0x3f);
sprite._editable = ((sprite._colorcode & 0x40) == 0x40);
sprite._moveable = ((sprite._colorcode & 0x80) == 0x80);
if (sprite._inkData & 0x40)
sprite._trails = 1;
else
sprite._trails = 0;
sprite._moveable = ((sprite._colorcode & 0x80) == 0x80);
if (sprite._castId) {
debugC(4, kDebugLoading, "CH: %-3d castId: %03d(%s) [flags:%04x [ink: %x trails: %d line: %d], %dx%d@%d,%d type: %d fg: %d bg: %d] script: %d, flags2: %x, unk2: %x, unk3: %x",
i + 1, sprite._castId, numToCastNum(sprite._castId), sprite._inkData,
sprite._ink, sprite._trails, sprite._thickness, sprite._width, sprite._height,
sprite._startPoint.x, sprite._startPoint.y,
sprite._spriteType, sprite._foreColor, sprite._backColor, sprite._scriptId, sprite._colorcode, sprite._blendAmount, sprite._unk3);
} else {
debugC(4, kDebugLoading, "CH: %-3d castId: 000", i + 1);
}
}
}
void Frame::readMainChannels(Common::SeekableSubReadStreamEndian &stream, uint16 offset, uint16 size) {
uint16 finishPosition = offset + size;
while (offset < finishPosition) {
switch(offset) {
case kScriptIdPosition:
_actionId = stream.readByte();
offset++;
break;
case kSoundType1Position:
_soundType1 = stream.readByte();
offset++;
break;
case kTransFlagsPosition: {
uint8 transFlags = stream.readByte();
if (transFlags & 0x80)
_transArea = 1;
else
_transArea = 0;
_transDuration = (transFlags & 0x7f) * 250; // Duration is in 1/4 secs
offset++;
}
break;
case kTransChunkSizePosition:
_transChunkSize = stream.readByte();
offset++;
break;
case kTempoPosition:
_tempo = stream.readByte();
offset++;
break;
case kTransTypePosition:
_transType = static_cast<TransitionType>(stream.readByte());
offset++;
break;
case kSound1Position:
_sound1 = stream.readUint16();
offset+=2;
break;
case kSkipFrameFlagsPosition:
_skipFrameFlag = stream.readByte();
offset++;
break;
case kBlendPosition:
_blend = stream.readByte();
offset++;
break;
case kSound2Position:
_sound2 = stream.readUint16();
offset += 2;
break;
case kSound2TypePosition:
_soundType2 = stream.readByte();
offset += 1;
break;
case kPalettePosition:
if (stream.readUint16())
readPaletteInfo(stream);
offset += 16;
break;
default:
offset++;
stream.readByte();
debugC(1, kDebugLoading, "Frame::readMainChannels: Field Position %d, Finish Position %d", offset, finishPosition);
break;
}
}
debugC(1, kDebugLoading, "Frame::readChannels(): %d %d %d %d %d %d %d %d %d %d %d", _actionId, _soundType1, _transDuration, _transChunkSize, _tempo, _transType, _sound1, _skipFrameFlag, _blend, _sound2, _soundType2);
}
void Frame::readPaletteInfo(Common::SeekableSubReadStreamEndian &stream) {
_palette->firstColor = stream.readByte();
_palette->lastColor = stream.readByte();
_palette->flags = stream.readByte();
_palette->speed = stream.readByte();
_palette->frameCount = stream.readUint16();
stream.skip(8); // unknown
}
void Frame::readSprite(Common::SeekableSubReadStreamEndian &stream, uint16 offset, uint16 size) {
uint16 spritePosition = (offset - 32) / 16;
uint16 spriteStart = spritePosition * 16 + 32;
uint16 fieldPosition = offset - spriteStart;
uint16 finishPosition = fieldPosition + size;
Sprite &sprite = *_sprites[spritePosition];
int x1 = 0;
int x2 = 0;
while (fieldPosition < finishPosition) {
switch (fieldPosition) {
case kSpritePositionUnk1:
x1 = stream.readByte();
fieldPosition++;
break;
case kSpritePositionEnabled:
sprite._enabled = (stream.readByte() != 0);
fieldPosition++;
break;
case kSpritePositionUnk2:
x2 = stream.readUint16();
fieldPosition += 2;
break;
case kSpritePositionFlags:
sprite._thickness = stream.readByte();
sprite._inkData = stream.readByte();
sprite._ink = static_cast<InkType>(sprite._inkData & 0x3f);
if (sprite._inkData & 0x40)
sprite._trails = 1;
else
sprite._trails = 0;
fieldPosition += 2;
break;
case kSpritePositionCastId:
sprite._castId = stream.readUint16();
fieldPosition += 2;
break;
case kSpritePositionY:
sprite._startPoint.y = stream.readUint16();
fieldPosition += 2;
break;
case kSpritePositionX:
sprite._startPoint.x = stream.readUint16();
fieldPosition += 2;
break;
case kSpritePositionWidth:
sprite._width = stream.readUint16();
fieldPosition += 2;
break;
case kSpritePositionHeight:
sprite._height = stream.readUint16();
fieldPosition += 2;
break;
default:
// end of channel, go to next sprite channel
readSprite(stream, spriteStart + 16, finishPosition - fieldPosition);
fieldPosition = finishPosition;
break;
}
}
warning("Frame::readSprite(): %03d(%d)[%x,%x,%02x %02x,%d/%d/%d/%d]", sprite._castId, sprite._enabled, x1, x2, sprite._thickness, sprite._inkData, sprite._startPoint.x, sprite._startPoint.y, sprite._width, sprite._height);
}
} // End of namespace Director