/* 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 . * */ #include "common/memstream.h" #include "common/substream.h" #include "director/director.h" #include "director/frame.h" #include "director/score.h" #include "director/movie.h" #include "director/sprite.h" namespace Director { Frame::Frame(Score *score, int numChannels) { _score = score; _vm = score->getMovie()->getVM(); _numChannels = numChannels; reset(); } void Frame::reset() { // Reset main channels _mainChannels = MainChannels(); _sprites.resize(_numChannels + 1); for (uint16 i = 0; i < _sprites.size(); i++) { if (_sprites[i]) delete _sprites[i]; Sprite *sp = new Sprite(this); _sprites[i] = sp; } } Frame::Frame(const Frame &frame) { _vm = frame._vm; _numChannels = frame._numChannels; _mainChannels.actionId = frame._mainChannels.actionId; _mainChannels.scriptSpriteListIdx = frame._mainChannels.scriptSpriteListIdx; _mainChannels.behaviors = frame._mainChannels.behaviors; _mainChannels.scriptSpriteInfo = frame._mainChannels.scriptSpriteInfo; _mainChannels.transDuration = frame._mainChannels.transDuration; _mainChannels.transArea = frame._mainChannels.transArea; _mainChannels.transChunkSize = frame._mainChannels.transChunkSize; _mainChannels.transType = frame._mainChannels.transType; _mainChannels.trans = frame._mainChannels.trans; _mainChannels.transSpriteListIdx = frame._mainChannels.transSpriteListIdx; _mainChannels.transSpriteInfo = frame._mainChannels.transSpriteInfo; _mainChannels.palette = frame._mainChannels.palette; _mainChannels.tempo = frame._mainChannels.tempo; _mainChannels.tempoSpriteListIdx = frame._mainChannels.tempoSpriteListIdx; _mainChannels.tempoCuePoint = frame._mainChannels.tempoCuePoint; _mainChannels.tempoSpriteInfo = frame._mainChannels.tempoSpriteInfo; _mainChannels.scoreCachedTempo = frame._mainChannels.scoreCachedTempo; _mainChannels.scoreCachedPaletteId = frame._mainChannels.scoreCachedPaletteId; _mainChannels.sound1 = frame._mainChannels.sound1; _mainChannels.soundType1 = frame._mainChannels.soundType1; _mainChannels.sound1SpriteListIdx = frame._mainChannels.sound1SpriteListIdx; _mainChannels.sound1SpriteInfo = frame._mainChannels.sound1SpriteInfo; _mainChannels.sound2 = frame._mainChannels.sound2; _mainChannels.soundType2 = frame._mainChannels.soundType2; _mainChannels.sound2SpriteListIdx = frame._mainChannels.sound2SpriteListIdx; _mainChannels.sound2SpriteInfo = frame._mainChannels.sound2SpriteInfo; _mainChannels.colorTempo = frame._mainChannels.colorTempo; _mainChannels.colorSound1 = frame._mainChannels.colorSound1; _mainChannels.colorSound2 = frame._mainChannels.colorSound2; _mainChannels.colorScript = frame._mainChannels.colorScript; _mainChannels.colorTrans = frame._mainChannels.colorTrans; _mainChannels.skipFrameFlag = frame._mainChannels.skipFrameFlag; _mainChannels.blend = frame._mainChannels.blend; _score = frame._score; debugC(1, kDebugLoading, "Frame. action: %s transType: %d transDuration: %d", _mainChannels.actionId.asString().c_str(), _mainChannels.transType, _mainChannels.transDuration); _sprites.resize(_numChannels + 1); for (uint16 i = 0; i <= _numChannels; i++) { _sprites[i] = new Sprite(*frame._sprites[i]); } } Frame::~Frame() { for (uint16 i = 0; i < _sprites.size(); i++) delete _sprites[i]; } void Frame::readChannel(Common::MemoryReadStreamEndian &stream, uint16 offset, uint16 size, uint16 version) { debugC(6, kDebugLoading, "Frame::readChannel(..., offset=%d, size=%d, version=%x (v%d))", offset, size, version, humanVersion(version)); if (version < kFileVer400) { readChannelD2(stream, offset, size); } else if (version >= kFileVer400 && version < kFileVer500) { readChannelD4(stream, offset, size); } else if (version >= kFileVer500 && version < kFileVer600) { readChannelD5(stream, offset, size); } else if (version >= kFileVer600 && version < kFileVer700) { readChannelD6(stream, offset, size); } else if (version >= kFileVer700 && version < kFileVer1100) { readChannelD7(stream, offset, size); } else { error("Frame::readChannel(): Unsupported Director version: %d", version); } } void Frame::writeMainChannels(Common::SeekableWriteStream *writeStream, uint16 version) { debugC(6, kDebugLoading, "Frame::writeChannel: writing main channels for version v%d (%d)", humanVersion(version), version); if (version >= kFileVer400 && version < kFileVer500) { writeMainChannelsD4(writeStream); } else if (version >= kFileVer500 && version < kFileVer600) { writeMainChannelsD5(writeStream); } else if (version >= kFileVer600 && version < kFileVer700) { writeMainChannelsD6(writeStream); } else if (version >= kFileVer700 && version < kFileVer1100) { writeMainChannelsD7(writeStream); } else { warning("Frame::writeChannel(): Unsupported Director version: %d", version); } } /************************** * * D2 Loading * **************************/ void Frame::readChannelD2(Common::MemoryReadStreamEndian &stream, uint16 offset, uint16 size) { if (offset < kMainChannelSizeD2) { uint16 needSize = MIN(size, (uint16)(kMainChannelSizeD2 - offset)); readMainChannelsD2(stream, offset, needSize); size -= needSize; offset += needSize; } if (offset >= kMainChannelSizeD2) { byte spritePosition = (offset - kMainChannelSizeD2) / kSprChannelSizeD2; uint16 nextStart = (spritePosition + 1) * kSprChannelSizeD2 + kMainChannelSizeD2; while (size > 0) { uint16 needSize = MIN((uint16)(nextStart - offset), size); readSpriteD2(stream, offset, needSize); offset += needSize; size -= needSize; nextStart += kSprChannelSizeD2; } } } void Frame::readMainChannelsD2(Common::MemoryReadStreamEndian &stream, uint16 offset, uint16 size) { if (debugChannelSet(8, kDebugLoading)) { debugC(8, kDebugLoading, "Frame::readMainChannelsD2(): %d byte header", size); stream.hexdump(size); } uint32 initPos = stream.pos(); uint32 finishPosition = initPos + size; byte unk[6]; while (stream.pos() < finishPosition) { switch (stream.pos() - initPos + offset) { // Sound/Tempo/Transition case 0: _mainChannels.actionId = CastMemberID(stream.readByte(), DEFAULT_CAST_LIB); break; case 1: // type: 0x17 for sounds (sound is cast id), 0x16 for MIDI (sound is cmd id) _mainChannels.soundType1 = stream.readByte(); break; case 2: { // 0x80 is whole stage (vs changed area), rest is duration in 1/4ths of a second uint8 transFlags = stream.readByte(); if (transFlags & 0x80) _mainChannels.transArea = 1; else _mainChannels.transArea = 0; _mainChannels.transDuration = (transFlags & 0x7f) * 250; // Duration is in 1/4 secs } break; case 3: _mainChannels.transChunkSize = stream.readByte(); break; case 4: _mainChannels.tempo = stream.readByte(); if (_mainChannels.tempo && _mainChannels.tempo <= 120) _mainChannels.scoreCachedTempo = _mainChannels.tempo; break; case 5: _mainChannels.transType = static_cast(stream.readByte()); break; case 6: _mainChannels.sound1 = CastMemberID(stream.readUint16(), DEFAULT_CAST_LIB); break; case 8: _mainChannels.sound2 = CastMemberID(stream.readUint16(), DEFAULT_CAST_LIB); break; case 10: _mainChannels.soundType2 = stream.readByte(); break; case 11: _mainChannels.skipFrameFlag = stream.readByte(); break; case 12: _mainChannels.blend = stream.readByte(); break; case 13: stream.read(unk, 1); if (unk[0]) debugC(8, kDebugLoading, "Frame::readMainChannelsD2(): STUB: unk1: 0x%02x", unk[0]); break; case 14: stream.read(unk, 2); if (unk[0] || unk[1]) debugC(8, kDebugLoading, "Frame::readMainChannelsD2(): STUB: unk2: 0x%02x 0x%02x", unk[0], unk[1]); break; // Palette case 16: { int16 paletteId = stream.readSint16(); if (paletteId == 0) { _mainChannels.palette.paletteId = CastMemberID(0, 0); } else if (paletteId < 0) { _mainChannels.palette.paletteId = CastMemberID(paletteId, -1); } else { _mainChannels.palette.paletteId = CastMemberID(paletteId, DEFAULT_CAST_LIB); } if (!g_director->hasPalette(_mainChannels.palette.paletteId)) _mainChannels.palette.paletteId = CastMemberID(); if (!_mainChannels.palette.paletteId.isNull()) _mainChannels.scoreCachedPaletteId = _mainChannels.palette.paletteId; } break; case 18: // loop points for color cycling _mainChannels.palette.firstColor = g_director->transformColor(stream.readByte() ^ 0x80); _mainChannels.palette.lastColor = g_director->transformColor(stream.readByte() ^ 0x80); break; case 20: _mainChannels.palette.flags = stream.readByte(); _mainChannels.palette.colorCycling = (_mainChannels.palette.flags & 0x80) != 0; _mainChannels.palette.normal = (_mainChannels.palette.flags & 0x60) == 0x00; _mainChannels.palette.fadeToBlack = (_mainChannels.palette.flags & 0x60) == 0x60; _mainChannels.palette.fadeToWhite = (_mainChannels.palette.flags & 0x60) == 0x40; _mainChannels.palette.autoReverse = (_mainChannels.palette.flags & 0x10) != 0; _mainChannels.palette.overTime = (_mainChannels.palette.flags & 0x04) != 0; _mainChannels.palette.speed = stream.readByte(); break; case 22: _mainChannels.palette.frameCount = stream.readUint16(); break; case 24: _mainChannels.palette.cycleCount = stream.readUint16(); break; case 26: stream.read(unk, 6); if (unk[0] || unk[1] || unk[2] || unk[3] || unk[4] || unk[5]) debugC(8, kDebugLoading, "Frame::readMainChannelsD2(): STUB: unk1: %02x %02x %02x %02x %02x %02x", unk[0], unk[1], unk[2], unk[3], unk[4], unk[5]); break; default: // This means that a `case` label has to be split at this position error("Frame::readMainChannelsD2(): Miscomputed field position: %" PRId64, stream.pos() - initPos + offset); break; } } if (stream.pos() > finishPosition) { // This means that the relevant `case` label reads too many bytes and must be split error("Frame::readMainChannelsD2(): Read %" PRId64 " extra bytes", stream.pos() - finishPosition); } _mainChannels.transChunkSize = CLIP(_mainChannels.transChunkSize, 0, 128); _mainChannels.transDuration = CLIP(_mainChannels.transDuration, 0, 32000); // restrict to 32 secs } void Frame::readSpriteD2(Common::MemoryReadStreamEndian &stream, uint16 offset, uint16 size) { uint16 spritePosition = (offset - kMainChannelSizeD2) / kSprChannelSizeD2; uint16 spriteStart = spritePosition * kSprChannelSizeD2 + kMainChannelSizeD2; uint16 fieldPosition = offset - spriteStart; debugC(5, kDebugLoading, "Frame::readSpriteD2(): sprite: %d offset: %d size: %d, field: %d", spritePosition, offset, size, fieldPosition); if (debugChannelSet(8, kDebugLoading)) { stream.hexdump(size); } Sprite &sprite = *_sprites[spritePosition + 1]; uint32 initPos = stream.pos(); uint32 finishPosition = initPos + size; readSpriteDataD2(stream, sprite, initPos - fieldPosition, finishPosition); if (stream.pos() > finishPosition) { // This means that the relevant `case` label reads too many bytes and must be split error("Frame::readSpriteD2(): Read %" PRId64 " extra bytes", stream.pos() - finishPosition); } } void readSpriteDataD2(Common::SeekableReadStreamEndian &stream, Sprite &sprite, uint32 startPosition, uint32 finishPosition) { while (stream.pos() < finishPosition) { switch (stream.pos() - startPosition) { case 0: sprite._scriptId = CastMemberID(stream.readByte(), DEFAULT_CAST_LIB); break; case 1: if (sprite._puppet) { stream.readByte(); } else { sprite._spriteType = (SpriteType)stream.readByte(); sprite._enabled = sprite._spriteType != kInactiveSprite; } break; case 2: if (sprite._puppet) { stream.readByte(); } else { // Normalize D2 and D3 colors from -128 ... 127 to 0 ... 255. sprite._foreColor = g_director->transformColor(stream.readByte() ^ 0x80); } break; case 3: if (sprite._puppet) { stream.readByte(); } else { // Normalize D2 and D3 colors from -128 ... 127 to 0 ... 255. sprite._backColor = g_director->transformColor(stream.readByte() ^ 0x80); } break; case 4: if (sprite._puppet) { stream.readByte(); } else { sprite._thickness = stream.readByte() & 0x7f; } break; case 5: if (sprite._puppet) { stream.readByte(); } else { sprite._inkData = stream.readByte(); sprite._ink = static_cast(sprite._inkData & 0x3f); sprite._trails = sprite._inkData & 0x40 ? true : false; sprite._stretch = sprite._inkData & 0x80 ? true : false; } break; case 6: if (sprite._puppet) { stream.readUint16(); } else { if (sprite.isQDShape()) { sprite._pattern = stream.readUint16(); sprite._castId = CastMemberID(0, 0); } else { sprite._pattern = 0; sprite._castId = CastMemberID(stream.readUint16(), DEFAULT_CAST_LIB); } } break; case 8: if (sprite._puppet) { stream.readUint16(); } else { sprite._startPoint.y = (int16)stream.readUint16(); } break; case 10: if (sprite._puppet) { stream.readUint16(); } else { sprite._startPoint.x = (int16)stream.readUint16(); } break; case 12: if (sprite._puppet) { stream.readUint16(); } else { sprite._height = (int16)stream.readUint16(); } break; case 14: if (sprite._puppet) { stream.readUint16(); } else { sprite._width = (int16)stream.readUint16(); } break; default: // This means that a `case` label has to be split at this position error("readSpriteDataD2(): Miscomputed field position: %" PRId64, stream.pos() - startPosition); } } // 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; } /************************** * * D4 Loading * **************************/ void Frame::readChannelD4(Common::MemoryReadStreamEndian &stream, uint16 offset, uint16 size) { // 40 bytes header if (offset < kMainChannelSizeD4) { uint16 needSize = MIN(size, (uint16)(kMainChannelSizeD4 - offset)); readMainChannelsD4(stream, offset, needSize); size -= needSize; offset += needSize; } if (offset >= kMainChannelSizeD4) { byte spritePosition = (offset - kMainChannelSizeD4) / kSprChannelSizeD4; uint16 nextStart = (spritePosition + 1) * kSprChannelSizeD4 + kMainChannelSizeD4; while (size > 0) { uint16 needSize = MIN((uint16)(nextStart - offset), size); readSpriteD4(stream, offset, needSize); offset += needSize; size -= needSize; nextStart += kSprChannelSizeD4; } } } void Frame::readMainChannelsD4(Common::MemoryReadStreamEndian &stream, uint16 offset, uint16 size) { if (debugChannelSet(8, kDebugLoading)) { debugC(8, kDebugLoading, "Frame::readMainChannelsD4(): %d byte header", size); stream.hexdump(size); } uint32 initPos = stream.pos(); uint32 finishPosition = initPos + size; int unk1; while (stream.pos() < finishPosition) { switch (stream.pos() - initPos + offset) { // Sound/Tempo/Transition case 0: unk1 = stream.readByte(); if (unk1) warning("Frame::readMainChannelsD4(): STUB: unk1: %d 0x%x", unk1, unk1); break; case 1: _mainChannels.soundType1 = stream.readByte(); // type: 0x17 for sounds (sound is cast id), 0x16 for MIDI (sound is cmd id) break; case 2: { uint8 transFlags = stream.readByte(); if (transFlags & 0x80) // 0x80 is whole stage (vs changed area), rest is duration in 1/4ths of a second _mainChannels.transArea = 1; else _mainChannels.transArea = 0; _mainChannels.transDuration = (transFlags & 0x7f) * 250; // Duration is in 1/4 secs } break; case 3: _mainChannels.transChunkSize = stream.readByte(); break; case 4: _mainChannels.tempo = stream.readByte(); if (_mainChannels.tempo && _mainChannels.tempo <= 120) _mainChannels.scoreCachedTempo = _mainChannels.tempo; break; case 5: _mainChannels.transType = static_cast(stream.readByte()); break; case 6: _mainChannels.sound1 = CastMemberID(stream.readUint16(), DEFAULT_CAST_LIB); break; case 8: _mainChannels.sound2 = CastMemberID(stream.readUint16(), DEFAULT_CAST_LIB); break; case 10: _mainChannels.soundType2 = stream.readByte(); break; case 11: _mainChannels.skipFrameFlag = stream.readByte(); break; case 12: _mainChannels.blend = stream.readByte(); break; case 13: _mainChannels.colorTempo = stream.readByte(); break; case 14: _mainChannels.colorSound1 = stream.readByte(); break; case 15: _mainChannels.colorSound2 = stream.readByte(); break; case 16: _mainChannels.actionId = CastMemberID(stream.readUint16(), DEFAULT_CAST_LIB); break; case 18: _mainChannels.colorScript = stream.readByte(); break; case 19: _mainChannels.colorTrans = stream.readByte(); break; // Palette, 20 bytes case 20: { int16 paletteId = stream.readSint16(); if (paletteId == 0) { _mainChannels.palette.paletteId = CastMemberID(0, 0); } else if (paletteId < 0) { _mainChannels.palette.paletteId = CastMemberID(paletteId, -1); } else { _mainChannels.palette.paletteId = CastMemberID(paletteId, DEFAULT_CAST_LIB); } if (!_mainChannels.palette.paletteId.isNull()) _mainChannels.scoreCachedPaletteId = _mainChannels.palette.paletteId; } break; case 22: // loop points for color cycling _mainChannels.palette.firstColor = g_director->transformColor(stream.readByte() ^ 0x80); // 22 _mainChannels.palette.lastColor = g_director->transformColor(stream.readByte() ^ 0x80); // 23 break; case 24: _mainChannels.palette.flags = stream.readByte(); // 24 _mainChannels.palette.colorCycling = (_mainChannels.palette.flags & 0x80) != 0; _mainChannels.palette.normal = (_mainChannels.palette.flags & 0x60) == 0x00; _mainChannels.palette.fadeToBlack = (_mainChannels.palette.flags & 0x60) == 0x60; _mainChannels.palette.fadeToWhite = (_mainChannels.palette.flags & 0x60) == 0x40; _mainChannels.palette.autoReverse = (_mainChannels.palette.flags & 0x10) != 0; _mainChannels.palette.overTime = (_mainChannels.palette.flags & 0x04) != 0; _mainChannels.palette.speed = stream.readByte(); // 25 break; case 26: _mainChannels.palette.frameCount = stream.readUint16(); // 26 break; case 28: _mainChannels.palette.cycleCount = stream.readUint16(); // 28 break; case 30: _mainChannels.palette.fade = stream.readByte(); // 30 _mainChannels.palette.delay = stream.readByte(); // 31 _mainChannels.palette.style = stream.readByte(); // 32 break; case 33: unk1 = stream.readByte(); if (unk1) warning("Frame::readMainChannelsD4(): STUB: unk2: %d 0x%x", unk1, unk1); break; case 34: unk1 = stream.readUint16(); if (unk1) warning("Frame::readMainChannelsD4(): STUB: unk3: %d 0x%x", unk1, unk1); break; case 36: unk1 = stream.readUint16(); if (unk1) warning("Frame::readMainChannelsD4(): STUB: unk4: %d 0x%x", unk1, unk1); break; case 38: _mainChannels.palette.colorCode = stream.readByte(); break; case 39: unk1 = stream.readByte(); if (unk1) warning("Frame::readMainChannelsD4(): STUB: unk5: 0x%02x", unk1); break; default: // This means that a `case` label has to be split at this position error("Frame::readMainChannelsD4(): Miscomputed field position: %" PRId64, stream.pos() - initPos + offset); break; } } if (stream.pos() > finishPosition) { // This means that the relevant `case` label reads too many bytes and must be split error("Frame::readMainChannelsD4(): Read %" PRId64 " extra bytes", stream.pos() - finishPosition); } _mainChannels.transChunkSize = CLIP(_mainChannels.transChunkSize, 0, 128); _mainChannels.transDuration = CLIP(_mainChannels.transDuration, 0, 32000); // restrict to 32 secs } void Frame::writeMainChannelsD4(Common::SeekableWriteStream *writeStream) { writeStream->writeByte(0); // Unknown: Sound/Tempo/Transition // 0 writeStream->writeByte(_mainChannels.soundType1); // 1 writeStream->writeByte((_mainChannels.transArea ? 0x80 : 0x00) | ((_mainChannels.transDuration / 250) & 0x7F)); // 2 writeStream->writeByte(_mainChannels.transChunkSize); // 3 writeStream->writeByte(_mainChannels.tempo); // 4 writeStream->writeByte(_mainChannels.transType); // 5 writeStream->writeUint16BE(_mainChannels.sound1.member); // 6, 7 writeStream->writeUint16BE(_mainChannels.sound2.member); // 8, 9 writeStream->writeByte(_mainChannels.soundType2); // 10 writeStream->writeByte(_mainChannels.skipFrameFlag); // 11 writeStream->writeByte(_mainChannels.blend); // 12 writeStream->writeByte(_mainChannels.colorTempo); // 13 writeStream->writeByte(_mainChannels.colorSound1); // 14 writeStream->writeByte(_mainChannels.colorSound2); // 15 writeStream->writeUint16BE(_mainChannels.actionId.member); // 16, 17 writeStream->writeByte(_mainChannels.colorScript); // 18 writeStream->writeByte(_mainChannels.colorTrans); // 19 // palette writeStream->writeSint16BE(_mainChannels.palette.paletteId.member); // 20, 21 writeStream->writeByte(_mainChannels.palette.firstColor ^ 0x80); // 22 writeStream->writeByte(_mainChannels.palette.lastColor ^ 0x80); // 23 writeStream->writeByte(_mainChannels.palette.flags); // 24 writeStream->writeByte(_mainChannels.palette.speed); // 25 writeStream->writeUint16BE(_mainChannels.palette.frameCount); // 26, 27 writeStream->writeUint16BE(_mainChannels.palette.cycleCount); // 28, 29 writeStream->writeByte(_mainChannels.palette.fade); // 30 writeStream->writeByte(_mainChannels.palette.delay); // 31 writeStream->writeByte(_mainChannels.palette.style); // 32 writeStream->writeByte(0); // Unknown // 33 writeStream->writeUint16BE(0); // Unknown // 34, 35 writeStream->writeUint16BE(0); // Unknown // 36, 37 writeStream->writeByte(_mainChannels.palette.colorCode); // 38 writeStream->writeByte(0); // Unknown // 39 } void Frame::readSpriteD4(Common::MemoryReadStreamEndian &stream, uint16 offset, uint16 size) { uint16 spritePosition = (offset - kMainChannelSizeD4) / kSprChannelSizeD4; uint16 spriteStart = spritePosition * kSprChannelSizeD4 + kMainChannelSizeD4; uint16 fieldPosition = offset - spriteStart; debugC(5, kDebugLoading, "Frame::readSpriteD4(): sprite: %d offset: %d size: %d, field: %d", spritePosition, offset, size, fieldPosition); if (debugChannelSet(8, kDebugLoading)) { stream.hexdump(size); } Sprite &sprite = *_sprites[spritePosition + 1]; uint32 initPos = stream.pos(); uint32 finishPosition = initPos + size; readSpriteDataD4(stream, sprite, initPos - fieldPosition, finishPosition); if (stream.pos() > finishPosition) { // This means that the relevant `case` label reads too many bytes and must be split error("Frame::readSpriteD4(): Read %" PRId64 " extra bytes", stream.pos() - finishPosition); } } void readSpriteDataD4(Common::SeekableReadStreamEndian &stream, Sprite &sprite, uint32 startPosition, uint32 finishPosition) { debugC(8, kDebugLoading, "stream.pos(): %0x, startPosition: %d, finishPosition: %d", (int)stream.pos(), startPosition, finishPosition); while (stream.pos() < finishPosition) { switch (stream.pos() - startPosition) { case 0: sprite._scriptId = CastMemberID(stream.readByte(), DEFAULT_CAST_LIB); break; case 1: if (sprite._puppet) { stream.readByte(); } else { sprite._spriteType = (SpriteType)stream.readByte(); sprite._enabled = sprite._spriteType != kInactiveSprite; } break; case 2: if (sprite._puppet) { stream.readByte(); } else { sprite._foreColor = g_director->transformColor((uint8)stream.readByte()); } break; case 3: if (sprite._puppet) { stream.readByte(); } else { sprite._backColor = g_director->transformColor((uint8)stream.readByte()); } break; case 4: if (sprite._puppet) { stream.readByte(); } else { sprite._thickness = stream.readByte(); } break; case 5: if (sprite._puppet) { stream.readByte(); } else { sprite._inkData = stream.readByte(); sprite._ink = static_cast(sprite._inkData & 0x3f); sprite._trails = sprite._inkData & 0x40 ? true : false; sprite._stretch = sprite._inkData & 0x80 ? true : false; } break; case 6: if (sprite._puppet) { stream.readUint16(); } else { if (sprite.isQDShape()) { sprite._pattern = stream.readUint16(); } else { sprite._castId = CastMemberID(stream.readUint16(), DEFAULT_CAST_LIB); } } break; case 8: if (sprite._puppet) { stream.readUint16(); } else { sprite._startPoint.y = (int16)stream.readUint16(); } break; case 10: if (sprite._puppet) { stream.readUint16(); } else { sprite._startPoint.x = (int16)stream.readUint16(); } break; case 12: if (sprite._puppet) { stream.readUint16(); } else { sprite._height = (int16)stream.readUint16(); } break; case 14: if (sprite._puppet) { stream.readUint16(); } else { sprite._width = (int16)stream.readUint16(); } break; case 16: sprite._scriptId = CastMemberID(stream.readUint16(), DEFAULT_CAST_LIB); break; case 18: if (sprite._puppet) { stream.readByte(); } else { // & 0x0f scorecolor // 0x10 forecolor is rgb // 0x20 bgcolor is rgb // 0x40 editable // 0x80 moveable sprite._colorcode = stream.readByte(); sprite._editable = ((sprite._colorcode & 0x40) == 0x40); sprite._moveable = ((sprite._colorcode & 0x80) == 0x80); sprite._moveable = ((sprite._colorcode & 0x80) == 0x80); } break; case 19: if (sprite._puppet) { stream.readByte(); } else { sprite._blendAmount = stream.readByte(); } break; default: // This means that a `case` label has to be split at this position error("readSpriteDataD4(): Miscomputed field position: %" PRId64, stream.pos() - startPosition); } } // 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; } void writeSpriteDataD4(Common::SeekableWriteStream *writeStream, Sprite &sprite) { writeStream->writeByte(sprite._scriptId.member); // 0 writeStream->writeByte((byte) sprite._spriteType); // 1 writeStream->writeByte(sprite._foreColor); // 2 writeStream->writeByte(sprite._backColor); // 3 writeStream->writeByte(sprite._thickness); // 4 writeStream->writeByte(sprite._inkData); // 5 if (sprite.isQDShape()) { writeStream->writeUint16BE(sprite._pattern); // 6, 7 } else { writeStream->writeUint16BE(sprite._castId.member); // 6, 7 } writeStream->writeUint16BE(sprite._startPoint.y); // 8, 9 writeStream->writeUint16BE(sprite._startPoint.x); // 10, 11 writeStream->writeUint16BE(sprite._height); // 12, 13 writeStream->writeUint16BE(sprite._width); // 14, 15 writeStream->writeUint16BE(sprite._scriptId.member); // 16, 17 writeStream->writeByte(sprite._colorcode); // 18 writeStream->writeByte(sprite._blendAmount); // 19 } /************************** * * D5 Loading * **************************/ void Frame::readChannelD5(Common::MemoryReadStreamEndian &stream, uint16 offset, uint16 size) { // 48 bytes header if (offset < kMainChannelSizeD5) { uint16 needSize = MIN(size, (uint16)(kMainChannelSizeD5 - offset)); readMainChannelsD5(stream, offset, needSize); size -= needSize; offset += needSize; } if (offset >= kMainChannelSizeD5) { byte spritePosition = (offset - kMainChannelSizeD5) / kSprChannelSizeD5; uint16 nextStart = (spritePosition + 1) * kSprChannelSizeD5 + kMainChannelSizeD5; while (size > 0) { uint16 needSize = MIN((uint16)(nextStart - offset), size); readSpriteD5(stream, offset, needSize); offset += needSize; size -= needSize; nextStart += kSprChannelSizeD5; } } } void Frame::readMainChannelsD5(Common::MemoryReadStreamEndian &stream, uint16 offset, uint16 size) { if (debugChannelSet(8, kDebugLoading)) { debugC(8, kDebugLoading, "Frame::readMainChannelsD5(): %d byte header", size); stream.hexdump(size); } uint32 initPos = stream.pos(); uint32 finishPosition = initPos + size; byte unk[12]; while (stream.pos() < finishPosition) { switch (stream.pos() - initPos + offset) { // Sound/Tempo/Transition case 0: _mainChannels.actionId.castLib = stream.readUint16(); break; case 2: _mainChannels.actionId.member = stream.readUint16(); break; case 4: _mainChannels.sound1.castLib = stream.readUint16(); break; case 6: _mainChannels.sound1.member = stream.readUint16(); break; case 8: _mainChannels.sound2.castLib = stream.readUint16(); break; case 10: _mainChannels.sound2.member = stream.readUint16(); break; case 12: _mainChannels.trans.castLib = stream.readUint16(); break; case 14: _mainChannels.trans.member = stream.readUint16(); break; case 16: _mainChannels.colorTempo = stream.readByte(); break; case 17: _mainChannels.colorSound1 = stream.readByte(); break; case 18: _mainChannels.colorSound2 = stream.readByte(); break; case 19: _mainChannels.colorScript = stream.readByte(); break; case 20: _mainChannels.colorTrans = stream.readByte(); break; case 21: _mainChannels.tempo = stream.readByte(); if (_mainChannels.tempo && _mainChannels.tempo <= 120) _mainChannels.scoreCachedTempo = _mainChannels.tempo; break; case 22: stream.read(unk, 2); // alignment bytes if (unk[0] || unk[1]) warning("Frame::readMainChannelsD5(): STUB: unk4: 0x%02x 0x%02x", unk[0], unk[1]); break; // Palette case 24: _mainChannels.palette.paletteId.castLib = stream.readSint16(); break; case 26: _mainChannels.palette.paletteId.member = stream.readSint16(); if (!g_director->hasPalette(_mainChannels.palette.paletteId)) _mainChannels.palette.paletteId = CastMemberID(); if (!_mainChannels.palette.paletteId.isNull()) _mainChannels.scoreCachedPaletteId = _mainChannels.palette.paletteId; break; case 28: _mainChannels.palette.speed = stream.readByte(); // 28 _mainChannels.palette.flags = stream.readByte(); // 29 _mainChannels.palette.colorCycling = (_mainChannels.palette.flags & 0x80) != 0; _mainChannels.palette.normal = (_mainChannels.palette.flags & 0x60) == 0x00; _mainChannels.palette.fadeToBlack = (_mainChannels.palette.flags & 0x60) == 0x60; _mainChannels.palette.fadeToWhite = (_mainChannels.palette.flags & 0x60) == 0x40; _mainChannels.palette.autoReverse = (_mainChannels.palette.flags & 0x10) != 0; _mainChannels.palette.overTime = (_mainChannels.palette.flags & 0x04) != 0; break; case 30: _mainChannels.palette.firstColor = g_director->transformColor(stream.readByte() ^ 0x80); // 30 _mainChannels.palette.lastColor = g_director->transformColor(stream.readByte() ^ 0x80); // 31 break; case 32: _mainChannels.palette.frameCount = stream.readUint16(); // 32 break; case 34: _mainChannels.palette.cycleCount = stream.readUint16(); // 34 break; case 36: _mainChannels.palette.fade = stream.readByte(); break; case 37: _mainChannels.palette.delay = stream.readByte(); break; case 38: _mainChannels.palette.style = stream.readByte(); break; case 39: _mainChannels.palette.colorCode = stream.readByte(); break; case 40: { stream.read(unk, 8); // alignment bytes Common::String s; for (int i = 0; i < 8; i++) s += Common::String::format("0x%02x ", unk[i]); warning("Frame::readMainChannelsD5(): STUB: unk7: %s", s.c_str()); } break; default: // This means that a `case` label has to be split at this position error("Frame::readMainChannelsD5(): Miscomputed field position: %" PRId64, stream.pos() - initPos + offset); break; } } if (stream.pos() > finishPosition) { // This means that the relevant `case` label reads too many bytes and must be split error("Frame::readMainChannelsD5(): Read %" PRId64 " extra bytes", stream.pos() - finishPosition); } _mainChannels.transChunkSize = CLIP(_mainChannels.transChunkSize, 0, 128); _mainChannels.transDuration = CLIP(_mainChannels.transDuration, 0, 32000); // restrict to 32 secs } void Frame::writeMainChannelsD5(Common::SeekableWriteStream *writeStream) { writeStream->writeUint16BE(_mainChannels.actionId.castLib); // 0, 1 writeStream->writeUint16BE(_mainChannels.actionId.member); // 2, 3 writeStream->writeUint16BE(_mainChannels.sound1.castLib); // 4, 5 writeStream->writeUint16BE(_mainChannels.sound1.member); // 6, 7 writeStream->writeUint16BE(_mainChannels.sound2.castLib); // 8, 9 writeStream->writeUint16BE(_mainChannels.sound2.member); // 10, 11 writeStream->writeUint16BE(_mainChannels.trans.member); // 12, 13 writeStream->writeUint16BE(_mainChannels.trans.member); // 14, 15 writeStream->writeByte(_mainChannels.colorTempo); // 16 writeStream->writeByte(_mainChannels.colorSound1); // 17 writeStream->writeByte(_mainChannels.colorSound2); // 18 writeStream->writeByte(_mainChannels.colorScript); // 19 writeStream->writeByte(_mainChannels.colorTrans); // 20 writeStream->writeByte(_mainChannels.tempo); // 21 writeStream->writeUint16BE(0); // Unknown // 22, 23 writeStream->writeSint16BE(_mainChannels.palette.paletteId.castLib); // 24, 25 writeStream->writeSint16BE(_mainChannels.palette.paletteId.member); // 26, 27 writeStream->writeByte(_mainChannels.palette.speed); // 28 writeStream->writeByte(_mainChannels.palette.flags); // 29 writeStream->writeByte(_mainChannels.palette.firstColor ^ 0x80); // 30 writeStream->writeByte(_mainChannels.palette.lastColor ^ 0x80); // 31 writeStream->writeUint16BE(_mainChannels.palette.frameCount); // 32, 33 writeStream->writeUint16BE(_mainChannels.palette.cycleCount); // 34, 35 writeStream->writeByte(_mainChannels.palette.fade); // 36 writeStream->writeByte(_mainChannels.palette.delay); // 37 writeStream->writeByte(_mainChannels.palette.style); // 38 writeStream->writeByte(_mainChannels.palette.colorCode); // 39 writeStream->writeUint64BE(0); // Unknown // 40, 41, 42, 43, 44, 45, 46, 47 } void Frame::readSpriteD5(Common::MemoryReadStreamEndian &stream, uint16 offset, uint16 size) { uint16 spritePosition = (offset - kMainChannelSizeD5) / kSprChannelSizeD5; uint16 spriteStart = spritePosition * kSprChannelSizeD5 + kMainChannelSizeD5; uint16 fieldPosition = offset - spriteStart; debugC(5, kDebugLoading, "Frame::readSpriteD5(): sprite: %d offset: %d size: %d, field: %d", spritePosition, offset, size, fieldPosition); if (debugChannelSet(8, kDebugLoading)) { stream.hexdump(size); } Sprite &sprite = *_sprites[spritePosition + 1]; uint32 initPos = stream.pos(); uint32 finishPosition = initPos + size; readSpriteDataD5(stream, sprite, initPos - fieldPosition, finishPosition); if (fieldPosition > finishPosition) { // This means that the relevant `case` label reads too many bytes and must be split error("Frame::readSpriteD5(): Read %" PRId64 " extra bytes", stream.pos() - finishPosition); } // 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; } void readSpriteDataD5(Common::SeekableReadStreamEndian &stream, Sprite &sprite, uint32 startPosition, uint32 finishPosition) { while (stream.pos() < finishPosition) { switch (stream.pos() - startPosition) { case 0: if (sprite._puppet) { stream.readByte(); } else { sprite._spriteType = (SpriteType)stream.readByte(); } break; case 1: if (sprite._puppet) { stream.readByte(); } else { sprite._inkData = stream.readByte(); sprite._ink = static_cast(sprite._inkData & 0x3f); sprite._trails = sprite._inkData & 0x40 ? true : false; sprite._stretch = sprite._inkData & 0x80 ? true : false; } break; case 2: if (sprite._puppet) { stream.readSint16(); } else { int castLib = stream.readSint16(); sprite._castId = CastMemberID(sprite._castId.member, castLib); } break; case 4: if (sprite._puppet) { stream.readUint16(); } else { uint16 memberID = stream.readUint16(); sprite._castId = CastMemberID(memberID, sprite._castId.castLib); // Inherit castLib from previous frame } break; case 6: { int scriptCastLib = stream.readSint16(); sprite._scriptId = CastMemberID(sprite._scriptId.member, scriptCastLib); } break; case 8: { uint16 scriptMemberID = stream.readUint16(); sprite._scriptId = CastMemberID(scriptMemberID, sprite._scriptId.castLib); // Inherit castLib from previous frame } break; case 10: if (sprite._puppet) { stream.readByte(); } else { sprite._foreColor = g_director->transformColor((uint8)stream.readByte()); } break; case 11: if (sprite._puppet) { stream.readByte(); } else { sprite._backColor = g_director->transformColor((uint8)stream.readByte()); } break; case 12: if (sprite._puppet) { stream.readUint16(); } else { sprite._startPoint.y = (int16)stream.readUint16(); } break; case 14: if (sprite._puppet) { stream.readUint16(); } else { sprite._startPoint.x = (int16)stream.readUint16(); } break; case 16: if (sprite._puppet) { stream.readUint16(); } else { sprite._height = (int16)stream.readUint16(); } break; case 18: if (sprite._puppet) { stream.readUint16(); } else { sprite._width = (int16)stream.readUint16(); } break; case 20: if (sprite._puppet) { stream.readByte(); } else { // & 0x0f scorecolor // 0x10 forecolor is rgb // 0x20 bgcolor is rgb // 0x40 editable // 0x80 moveable sprite._colorcode = stream.readByte(); sprite._editable = ((sprite._colorcode & 0x40) == 0x40); sprite._moveable = ((sprite._colorcode & 0x80) == 0x80); sprite._moveable = ((sprite._colorcode & 0x80) == 0x80); } break; case 21: if (sprite._puppet) { stream.readByte(); } else { sprite._blendAmount = stream.readByte(); } break; case 22: if (sprite._puppet) { stream.readByte(); } else { sprite._thickness = stream.readByte(); } break; case 23: (void)stream.readByte(); // unused break; default: // This means that a `case` label has to be split at this position error("readSpriteDataD5(): Miscomputed field position: %" PRId64, stream.pos() - startPosition); } } } void writeSpriteDataD5(Common::SeekableWriteStream *writeStream, Sprite &sprite) { writeStream->writeByte(sprite._spriteType); // 0 writeStream->writeByte(sprite._inkData); // 1 writeStream->writeSint16BE(sprite._castId.castLib); // 2, 3 writeStream->writeUint16BE(sprite._castId.member); // 4, 5 writeStream->writeSint16BE(sprite._scriptId.castLib); // 6, 7 writeStream->writeUint16BE(sprite._scriptId.member); // 8, 9 writeStream->writeByte(sprite._foreColor); // 10 writeStream->writeByte(sprite._backColor); // 11 writeStream->writeUint16BE(sprite._startPoint.y); // 12, 13 writeStream->writeUint16BE(sprite._startPoint.x); // 14, 15 writeStream->writeUint16BE(sprite._height); // 16, 17 writeStream->writeUint16BE(sprite._width); // 18, 19 writeStream->writeByte(sprite._colorcode); // 20 writeStream->writeByte(sprite._blendAmount); // 21 writeStream->writeByte(sprite._thickness); // 22 writeStream->writeByte(0); // 23, unused } /************************** * * D6 Loading * **************************/ void Frame::readChannelD6(Common::MemoryReadStreamEndian &stream, uint16 offset, uint16 size) { // 48 bytes header if (offset < kMainChannelSizeD6) { uint16 needSize = MIN(size, (uint16)(kMainChannelSizeD6 - offset)); readMainChannelsD6(stream, offset, needSize); size -= needSize; offset += needSize; } if (offset >= kMainChannelSizeD6) { byte spritePosition = (offset - kMainChannelSizeD6) / kSprChannelSizeD6; uint16 nextStart = (spritePosition + 1) * kSprChannelSizeD6 + kMainChannelSizeD6; while (size > 0) { uint16 needSize = MIN((uint16)(nextStart - offset), size); readSpriteD6(stream, offset, needSize); offset += needSize; size -= needSize; nextStart += kSprChannelSizeD6; } } } void Frame::readMainChannelsD6(Common::MemoryReadStreamEndian &stream, uint16 offset, uint16 size) { if (debugChannelSet(8, kDebugLoading)) { debugC(8, kDebugLoading, "Frame::readMainChannelsD6(): %d byte header", size); stream.hexdump(size); } uint32 initPos = stream.pos(); uint32 finishPosition = initPos + size; byte unk[16]; while (stream.pos() < finishPosition) { switch (stream.pos() - initPos + offset) { // Script case 0+0: _mainChannels.actionId.castLib = stream.readUint16(); break; case 0+2: _mainChannels.actionId.member = stream.readUint16(); break; case 0+4: _mainChannels.scriptSpriteListIdx = stream.readUint32(); break; case 0+6: _mainChannels.scriptSpriteListIdx = stream.readUint16(); break; case 0+8: _mainChannels.colorScript = stream.readByte(); break; case 0+9: { int bytes = MIN(finishPosition - stream.pos(), 15); stream.read(unk, bytes); // alignment bytes hexdumpIfNotZero(unk, bytes, "Frame::readMainChannelsD6(): script.unk: "); } break; // Tempo case 24+0: _mainChannels.tempoSpriteListIdx = stream.readUint32(); break; case 24+2: _mainChannels.tempoSpriteListIdx = stream.readUint16(); break; case 24+4: _mainChannels.tempoCuePoint = stream.readUint16(); break; case 24+6: _mainChannels.tempo = stream.readByte(); if (_mainChannels.tempo && _mainChannels.tempo <= 120) _mainChannels.scoreCachedTempo = _mainChannels.tempo; break; case 24+7: _mainChannels.colorTempo = stream.readByte(); break; case 24+8: { int bytes = MIN(finishPosition - stream.pos(), 16); stream.read(unk, bytes); // alignment bytes hexdumpIfNotZero(unk, bytes, "Frame::readMainChannelsD6(): tempo.unk: "); } break; // Transition case 48+0: _mainChannels.trans.castLib = stream.readUint16(); break; case 48+2: _mainChannels.trans.member = stream.readUint16(); break; case 48+4: _mainChannels.transSpriteListIdx = stream.readUint32(); break; case 48+6: _mainChannels.transSpriteListIdx = stream.readUint16(); break; case 48+8: _mainChannels.colorTrans = stream.readByte(); break; case 48+9: { int bytes = MIN(finishPosition - stream.pos(), 15); stream.read(unk, bytes); // alignment bytes hexdumpIfNotZero(unk, bytes, "Frame::readMainChannelsD6(): trans.unk: "); } break; // Sound2 case 72+0: _mainChannels.sound2.castLib = stream.readUint16(); break; case 72+2: _mainChannels.sound2.member = stream.readUint16(); break; case 72+4: _mainChannels.sound2SpriteListIdx = stream.readUint32(); break; case 72+6: _mainChannels.sound2SpriteListIdx = stream.readUint16(); break; case 72+8: _mainChannels.colorSound2 = stream.readByte(); break; case 72+9: { int bytes = MIN(finishPosition - stream.pos(), 15); stream.read(unk, bytes); // alignment bytes hexdumpIfNotZero(unk, bytes, "Frame::readMainChannelsD6(): sound2.unk: "); } break; // Sound1 case 96+0: _mainChannels.sound1.castLib = stream.readUint16(); break; case 96+2: _mainChannels.sound1.member = stream.readUint16(); break; case 96+4: _mainChannels.sound1SpriteListIdx = stream.readUint32(); break; case 96+6: _mainChannels.sound1SpriteListIdx = stream.readUint16(); break; case 96+8: _mainChannels.colorSound1 = stream.readByte(); break; case 96+9: { int bytes = MIN(finishPosition - stream.pos(), 15); stream.read(unk, bytes); // alignment bytes hexdumpIfNotZero(unk, bytes, "Frame::readMainChannelsD6(): sound1.unk: "); } break; // Palette case 120+0: _mainChannels.palette.paletteId.castLib = stream.readSint16(); break; case 120+2: _mainChannels.palette.paletteId.member = stream.readSint16(); if (!g_director->hasPalette(_mainChannels.palette.paletteId)) _mainChannels.palette.paletteId = CastMemberID(); if (!_mainChannels.palette.paletteId.isNull()) _mainChannels.scoreCachedPaletteId = _mainChannels.palette.paletteId; break; case 120+4: _mainChannels.palette.speed = stream.readByte(); // 52 _mainChannels.palette.flags = stream.readByte(); // 53 _mainChannels.palette.colorCycling = (_mainChannels.palette.flags & 0x80) != 0; _mainChannels.palette.normal = (_mainChannels.palette.flags & 0x60) == 0x00; _mainChannels.palette.fadeToBlack = (_mainChannels.palette.flags & 0x60) == 0x60; _mainChannels.palette.fadeToWhite = (_mainChannels.palette.flags & 0x60) == 0x40; _mainChannels.palette.autoReverse = (_mainChannels.palette.flags & 0x10) != 0; _mainChannels.palette.overTime = (_mainChannels.palette.flags & 0x04) != 0; break; case 120+6: _mainChannels.palette.firstColor = g_director->transformColor(stream.readByte() ^ 0x80); // 51 _mainChannels.palette.lastColor = g_director->transformColor(stream.readByte() ^ 0x80); // 52 break; case 120+8: _mainChannels.palette.frameCount = stream.readUint16(); // 53 break; case 120+10: _mainChannels.palette.cycleCount = stream.readUint16(); // 55 break; case 120+12: _mainChannels.palette.fade = stream.readByte(); break; case 120+13: _mainChannels.palette.delay = stream.readByte(); break; case 120+14: _mainChannels.palette.style = stream.readByte(); break; case 120+15: _mainChannels.palette.colorCode = stream.readByte(); break; case 120+16: _mainChannels.palette.spriteListIdx = stream.readUint32(); break; case 120+18: _mainChannels.palette.spriteListIdx = stream.readUint16(); break; case 120+20: { int bytes = MIN(finishPosition - stream.pos(), 4); stream.read(unk, bytes); // alignment bytes hexdumpIfNotZero(unk, bytes, "Frame::readMainChannelsD6(): palette.unk: "); } break; // 144 bytes (24 * 6) default: // This means that a `case` label has to be split at this position error("Frame::readMainChannelsD6(): Miscomputed field position: %" PRId64, stream.pos() - initPos + offset); break; } } if (stream.pos() > finishPosition) { // This means that the relevant `case` label reads too many bytes and must be split error("Frame::readMainChannelsD6(): Read %" PRId64 " extra bytes", stream.pos() - finishPosition); } _mainChannels.transChunkSize = CLIP(_mainChannels.transChunkSize, 0, 128); _mainChannels.transDuration = CLIP(_mainChannels.transDuration, 0, 32000); // restrict to 32 secs } static void writePadding(Common::SeekableWriteStream *writeStream, int size) { for (int i = 0; i < size; i++) { writeStream->writeByte(0); } } void Frame::writeMainChannelsD6(Common::SeekableWriteStream *writeStream) { // Script writeStream->writeUint32BE(_mainChannels.actionId.castLib); // 0+0 writeStream->writeUint16BE(_mainChannels.actionId.member); // 0+2 writeStream->writeUint32BE(_mainChannels.scriptSpriteListIdx); // 0+4 writeStream->writeByte(_mainChannels.colorScript); // 0+8 writePadding(writeStream, 15); // 0+9 // Tempo writeStream->writeUint32BE(_mainChannels.tempoSpriteListIdx); // 24+0 writeStream->writeUint16BE(_mainChannels.tempoCuePoint); // 24+4 writeStream->writeByte(_mainChannels.tempo); // 24+6 writeStream->writeByte(_mainChannels.colorTempo); // 24+7 writePadding(writeStream, 16); // 24+8 // Transition writeStream->writeUint16BE(_mainChannels.trans.castLib); // 48+0 writeStream->writeUint16BE(_mainChannels.trans.member); // 48+2 writeStream->writeUint32BE(_mainChannels.transSpriteListIdx); // 48+4 writeStream->writeByte(_mainChannels.colorTrans); // 48+8 writePadding(writeStream, 15); // 48+9 // Sound2 writeStream->writeUint16BE(_mainChannels.sound2.castLib); // 72+0 writeStream->writeUint16BE(_mainChannels.sound2.member); // 72+2 writeStream->writeUint32BE(_mainChannels.sound2SpriteListIdx); // 72+4 writeStream->writeByte(_mainChannels.colorSound2); // 72+8 writePadding(writeStream, 15); // 72+9 // Sound1 writeStream->writeUint16BE(_mainChannels.sound1.castLib); // 96+0 writeStream->writeUint16BE(_mainChannels.sound1.member); // 96+2 writeStream->writeUint32BE(_mainChannels.sound1SpriteListIdx); // 96+4 writeStream->writeByte(_mainChannels.colorSound1); // 96+8 writePadding(writeStream, 15); // 96+9-23 // Palette writeStream->writeUint16BE(_mainChannels.palette.paletteId.castLib); // 120+0 writeStream->writeUint16BE(_mainChannels.palette.paletteId.member); // 120+2 writeStream->writeByte(_mainChannels.palette.speed); // 120+4 writeStream->writeByte(_mainChannels.palette.flags); // 120+5 writeStream->writeByte(_mainChannels.palette.firstColor); // 120+6 writeStream->writeByte(_mainChannels.palette.lastColor); // 120+7 writeStream->writeUint16BE(_mainChannels.palette.frameCount); // 120+8 writeStream->writeUint16BE(_mainChannels.palette.cycleCount); // 120+10 writeStream->writeByte(_mainChannels.palette.fade); // 120+12 writeStream->writeByte(_mainChannels.palette.delay); // 120+13 writeStream->writeByte(_mainChannels.palette.style); // 120+14 writeStream->writeByte(_mainChannels.palette.colorCode); // 120+15 writeStream->writeUint32BE(_mainChannels.palette.spriteListIdx); // 120+16 writePadding(writeStream, 4); // 120+20 } void Frame::readSpriteD6(Common::MemoryReadStreamEndian &stream, uint16 offset, uint16 size) { uint16 spritePosition = (offset - kMainChannelSizeD6) / kSprChannelSizeD6; uint16 spriteStart = spritePosition * kSprChannelSizeD6 + kMainChannelSizeD6; uint16 fieldPosition = offset - spriteStart; debugC(5, kDebugLoading, "Frame::readSpriteD6(): sprite: %d offset: %d size: %d, field: %d", spritePosition, offset, size, fieldPosition); if (debugChannelSet(8, kDebugLoading)) { stream.hexdump(size); } Sprite &sprite = *_sprites[spritePosition + 1]; uint32 initPos = stream.pos(); uint32 finishPosition = initPos + size; readSpriteDataD6(stream, sprite, initPos - fieldPosition, finishPosition); if (stream.pos() > finishPosition) { // This means that the relevant `case` label reads too many bytes and must be split error("Frame::readSpriteD6(): Read %" PRId64 " extra bytes", stream.pos() - finishPosition); } // 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; } void readSpriteDataD6(Common::SeekableReadStreamEndian &stream, Sprite &sprite, uint32 startPosition, uint32 finishPosition) { while (stream.pos() < finishPosition) { switch (stream.pos() - startPosition) { case 0: if (sprite._puppet) { stream.readByte(); } else { sprite._spriteType = (SpriteType)stream.readByte(); debugC(6, kDebugLoading, " sprite._spriteType: %d", sprite._spriteType); } break; case 1: if (sprite._puppet || sprite.getAutoPuppet(kAPInk)) { stream.readByte(); } else { sprite._inkData = stream.readByte(); sprite._ink = static_cast(sprite._inkData & 0x3f); sprite._trails = sprite._inkData & 0x40 ? true : false; sprite._stretch = sprite._inkData & 0x80 ? true : false; debugC(6, kDebugLoading, " sprite._inkData: 0x%02x", sprite._inkData); debugC(6, kDebugLoading, " sprite._ink: %d", sprite._ink); debugC(6, kDebugLoading, " sprite._trails: %d", sprite._trails); debugC(6, kDebugLoading, " sprite._stretch: %d", sprite._stretch); } break; case 2: if (sprite._puppet || sprite.getAutoPuppet(kAPForeColor)) { stream.readByte(); } else { sprite._foreColor = g_director->transformColor((uint8)stream.readByte()); debugC(6, kDebugLoading, " sprite._foreColor: 0x%02x", sprite._foreColor); } break; case 3: if (sprite._puppet || sprite.getAutoPuppet(kAPBackColor)) { stream.readByte(); } else { sprite._backColor = g_director->transformColor((uint8)stream.readByte()); debugC(6, kDebugLoading, " sprite._backColor: 0x%02x", sprite._backColor); } break; case 4: if (sprite._puppet || sprite.getAutoPuppet(kAPCast)) { stream.readSint16(); } else { int castLib = stream.readSint16(); sprite._castId = CastMemberID(sprite._castId.member, castLib); debugC(6, kDebugLoading, " sprite._castId: %d", sprite._castId.member); } break; case 6: if (sprite._puppet || sprite.getAutoPuppet(kAPCast)) { stream.readUint16(); } else { uint16 memberID = stream.readUint16(); sprite._castId = CastMemberID(memberID, sprite._castId.castLib); // Inherit castLib from previous frame debugC(6, kDebugLoading, " sprite._castId: %d", sprite._castId.member); } break; case 8: if (sprite._puppet || sprite.getAutoPuppet(kAPCast)) { stream.readUint32(); } else { sprite._spriteListIdx = stream.readUint32(); debugC(6, kDebugLoading, " sprite._spriteListIdx: %d", sprite._spriteListIdx); } break; case 10: // This field could be optimized if (sprite._puppet || sprite.getAutoPuppet(kAPCast)) { stream.readUint16(); } else { sprite._spriteListIdx = stream.readUint16(); debugC(6, kDebugLoading, " sprite._spriteListIdx (16): %d", sprite._spriteListIdx); } break; case 12: if (sprite._puppet || sprite.getAutoPuppet(kAPLoc)) { stream.readUint16(); } else { sprite._startPoint.y = (int16)stream.readUint16(); debugC(6, kDebugLoading, " sprite._startPoint.y: %d", sprite._startPoint.y); } break; case 14: if (sprite._puppet || sprite.getAutoPuppet(kAPLoc)) { stream.readUint16(); } else { sprite._startPoint.x = (int16)stream.readUint16(); debugC(6, kDebugLoading, " sprite._startPoint.x: %d", sprite._startPoint.x);} break; case 16: if (sprite._puppet || sprite.getAutoPuppet(kAPHeight)) { stream.readUint16(); } else { sprite._height = (int16)stream.readUint16(); debugC(6, kDebugLoading, " sprite._height: %d", sprite._height); } break; case 18: if (sprite._puppet || sprite.getAutoPuppet(kAPWidth)) { stream.readUint16(); } else { sprite._width = (int16)stream.readUint16(); debugC(6, kDebugLoading, " sprite._width: %d", sprite._width); } break; case 20: if (sprite._puppet || sprite.getAutoPuppet(kAPMoveable)) { stream.readByte(); } else { // & 0x0f scorecolor // 0x10 forecolor is rgb // 0x20 bgcolor is rgb // 0x40 editable // 0x80 moveable sprite._colorcode = stream.readByte(); sprite._editable = ((sprite._colorcode & 0x40) == 0x40); sprite._moveable = ((sprite._colorcode & 0x80) == 0x80); sprite._moveable = ((sprite._colorcode & 0x80) == 0x80); debugC(6, kDebugLoading, " sprite._colorcode: 0x%02x", sprite._colorcode); debugC(6, kDebugLoading, " sprite._editable: %d", sprite._editable); debugC(6, kDebugLoading, " sprite._moveable: %d", sprite._moveable); } break; case 21: if (sprite._puppet) { stream.readByte(); } else { sprite._blendAmount = stream.readByte(); debugC(6, kDebugLoading, " sprite._blendAmount: %d", sprite._blendAmount); } break; case 22: if (sprite._puppet) { stream.readByte(); } else { sprite._thickness = stream.readByte(); debugC(6, kDebugLoading, " sprite._thickness: %d", sprite._thickness); } break; case 23: (void)stream.readByte(); // unused break; default: // This means that a `case` label has to be split at this position error("readSpriteDataD6(): Miscomputed field position: %" PRId64, stream.pos() - startPosition); } } } void writeSpriteDataD6(Common::SeekableWriteStream *writeStream, Sprite &sprite) { writeStream->writeByte(sprite._spriteType); // 0 writeStream->writeByte(sprite._inkData); // 1 writeStream->writeByte(sprite._foreColor); // 2 writeStream->writeByte(sprite._backColor); // 3 writeStream->writeSint16BE(sprite._castId.castLib); // 4, 5 writeStream->writeUint16BE(sprite._castId.member); // 6, 7 writeStream->writeUint32BE(sprite._spriteListIdx); // 8, 9, 10, 11 writeStream->writeUint16BE(sprite._startPoint.y); // 12, 13 writeStream->writeUint16BE(sprite._startPoint.x); // 14, 15 writeStream->writeUint16BE(sprite._height); // 16, 17 writeStream->writeUint16BE(sprite._width); // 18, 19 writeStream->writeByte(sprite._colorcode); // 20 writeStream->writeByte(sprite._blendAmount); // 21 writeStream->writeByte(sprite._thickness); // 22 writeStream->writeByte(0); // 23, unused } /************************** * * D7 Loading * **************************/ void Frame::readChannelD7(Common::MemoryReadStreamEndian &stream, uint16 offset, uint16 size) { // 48 bytes header if (offset < kMainChannelSizeD7) { uint16 needSize = MIN(size, (uint16)(kMainChannelSizeD7 - offset)); readMainChannelsD7(stream, offset, needSize); size -= needSize; offset += needSize; } if (offset >= kMainChannelSizeD7) { byte spritePosition = (offset - kMainChannelSizeD7) / kSprChannelSizeD7; uint16 nextStart = (spritePosition + 1) * kSprChannelSizeD7 + kMainChannelSizeD7; while (size > 0) { uint16 needSize = MIN((uint16)(nextStart - offset), size); readSpriteD7(stream, offset, needSize); offset += needSize; size -= needSize; nextStart += kSprChannelSizeD7; } } } void Frame::readMainChannelsD7(Common::MemoryReadStreamEndian &stream, uint16 offset, uint16 size) { if (debugChannelSet(8, kDebugLoading)) { debugC(8, kDebugLoading, "Frame::readMainChannelsD7(): %d byte header", size); stream.hexdump(size); } uint32 initPos = stream.pos(); uint32 finishPosition = initPos + size; byte unk[40]; while (stream.pos() < finishPosition) { switch (stream.pos() - initPos + offset) { // Script case 0+0: _mainChannels.actionId.castLib = stream.readUint16(); break; case 0+2: _mainChannels.actionId.member = stream.readUint16(); break; case 0+4: _mainChannels.scriptSpriteListIdx = stream.readUint32(); break; case 0+6: _mainChannels.scriptSpriteListIdx = stream.readUint16(); break; case 0+8: _mainChannels.colorScript = stream.readByte(); break; case 0+9: { int bytes = MIN(finishPosition - stream.pos(), 39); stream.read(unk, bytes); // alignment bytes hexdumpIfNotZero(unk, bytes, "Frame::readMainChannelsD6(): script.unk: "); } break; // Tempo case 48+0: _mainChannels.tempoSpriteListIdx = stream.readUint32(); break; case 48+2: _mainChannels.tempoSpriteListIdx = stream.readUint16(); break; case 48+4: _mainChannels.tempoCuePoint = stream.readUint16(); break; case 48+6: _mainChannels.tempo = stream.readByte(); if (_mainChannels.tempo && _mainChannels.tempo <= 120) _mainChannels.scoreCachedTempo = _mainChannels.tempo; break; case 48+7: _mainChannels.colorTempo = stream.readByte(); break; case 48+8: { int bytes = MIN(finishPosition - stream.pos(), 40); stream.read(unk, bytes); // alignment bytes hexdumpIfNotZero(unk, bytes, "Frame::readMainChannelsD6(): tempo.unk: "); } break; // Transition case 96+0: _mainChannels.trans.castLib = stream.readUint16(); break; case 96+2: _mainChannels.trans.member = stream.readUint16(); break; case 96+4: _mainChannels.transSpriteListIdx = stream.readUint32(); break; case 96+6: _mainChannels.transSpriteListIdx = stream.readUint16(); break; case 96+8: _mainChannels.colorTrans = stream.readByte(); break; case 96+9: { int bytes = MIN(finishPosition - stream.pos(), 39); stream.read(unk, bytes); // alignment bytes hexdumpIfNotZero(unk, bytes, "Frame::readMainChannelsD6(): trans.unk: "); } break; // Sound2 case 144+0: _mainChannels.sound2.castLib = stream.readUint16(); break; case 144+2: _mainChannels.sound2.member = stream.readUint16(); break; case 144+4: _mainChannels.sound2SpriteListIdx = stream.readUint32(); break; case 144+6: _mainChannels.sound2SpriteListIdx = stream.readUint16(); break; case 144+8: _mainChannels.colorSound2 = stream.readByte(); break; case 144+9: { int bytes = MIN(finishPosition - stream.pos(), 39); stream.read(unk, bytes); // alignment bytes hexdumpIfNotZero(unk, bytes, "Frame::readMainChannelsD6(): sound2.unk: "); } break; // Sound1 case 192+0: _mainChannels.sound1.castLib = stream.readUint16(); break; case 192+2: _mainChannels.sound1.member = stream.readUint16(); break; case 192+4: _mainChannels.sound1SpriteListIdx = stream.readUint32(); break; case 192+6: _mainChannels.sound1SpriteListIdx = stream.readUint16(); break; case 192+8: _mainChannels.colorSound1 = stream.readByte(); break; case 192+9: { int bytes = MIN(finishPosition - stream.pos(), 39); stream.read(unk, bytes); // alignment bytes hexdumpIfNotZero(unk, bytes, "Frame::readMainChannelsD6(): sound1.unk: "); } break; // Palette case 240+0: _mainChannels.palette.paletteId.castLib = stream.readSint16(); break; case 240+2: _mainChannels.palette.paletteId.member = stream.readSint16(); if (!g_director->hasPalette(_mainChannels.palette.paletteId)) _mainChannels.palette.paletteId = CastMemberID(); if (!_mainChannels.palette.paletteId.isNull()) _mainChannels.scoreCachedPaletteId = _mainChannels.palette.paletteId; break; case 240+4: _mainChannels.palette.speed = stream.readByte(); // 52 _mainChannels.palette.flags = stream.readByte(); // 53 _mainChannels.palette.colorCycling = (_mainChannels.palette.flags & 0x80) != 0; _mainChannels.palette.normal = (_mainChannels.palette.flags & 0x60) == 0x00; _mainChannels.palette.fadeToBlack = (_mainChannels.palette.flags & 0x60) == 0x60; _mainChannels.palette.fadeToWhite = (_mainChannels.palette.flags & 0x60) == 0x40; _mainChannels.palette.autoReverse = (_mainChannels.palette.flags & 0x10) != 0; _mainChannels.palette.overTime = (_mainChannels.palette.flags & 0x04) != 0; break; case 240+6: _mainChannels.palette.firstColor = g_director->transformColor(stream.readByte() ^ 0x80); // 51 _mainChannels.palette.lastColor = g_director->transformColor(stream.readByte() ^ 0x80); // 52 break; case 240+8: _mainChannels.palette.frameCount = stream.readUint16(); // 53 break; case 240+10: _mainChannels.palette.cycleCount = stream.readUint16(); // 55 break; case 240+12: _mainChannels.palette.fade = stream.readByte(); break; case 240+13: _mainChannels.palette.delay = stream.readByte(); break; case 240+14: _mainChannels.palette.style = stream.readByte(); break; case 240+15: _mainChannels.palette.colorCode = stream.readByte(); break; case 240+16: _mainChannels.palette.spriteListIdx = stream.readUint32(); break; case 240+18: _mainChannels.palette.spriteListIdx = stream.readUint16(); break; case 240+20: { int bytes = MIN(finishPosition - stream.pos(), 28); stream.read(unk, bytes); // alignment bytes hexdumpIfNotZero(unk, bytes, "Frame::readMainChannelsD6(): palette.unk: "); } break; // 288 bytes (48 * 6) default: // This means that a `case` label has to be split at this position error("Frame::readMainChannelsD7(): Miscomputed field position: %" PRId64, stream.pos() - initPos + offset); break; } } if (stream.pos() > finishPosition) { // This means that the relevant `case` label reads too many bytes and must be split error("Frame::readMainChannelsD7(): Read %" PRId64 " extra bytes", stream.pos() - finishPosition); } _mainChannels.transChunkSize = CLIP(_mainChannels.transChunkSize, 0, 128); _mainChannels.transDuration = CLIP(_mainChannels.transDuration, 0, 32000); // restrict to 32 secs } void Frame::writeMainChannelsD7(Common::SeekableWriteStream *writeStream) { // Script writeStream->writeUint32BE(_mainChannels.actionId.castLib); // 0+0 writeStream->writeUint16BE(_mainChannels.actionId.member); // 0+2 writeStream->writeUint32BE(_mainChannels.scriptSpriteListIdx); // 0+4 writeStream->writeByte(_mainChannels.colorScript); // 0+8 writePadding(writeStream, 39); // 0+9 // Tempo writeStream->writeUint32BE(_mainChannels.tempoSpriteListIdx); // 48+0 writeStream->writeUint16BE(_mainChannels.tempoCuePoint); // 48+4 writeStream->writeByte(_mainChannels.tempo); // 48+6 writeStream->writeByte(_mainChannels.colorTempo); // 48+7 writePadding(writeStream, 40); // 48+8 // Transition writeStream->writeUint16BE(_mainChannels.trans.castLib); // 96+0 writeStream->writeUint16BE(_mainChannels.trans.member); // 96+2 writeStream->writeUint32BE(_mainChannels.transSpriteListIdx); // 96+4 writeStream->writeByte(_mainChannels.colorTrans); // 96+8 writePadding(writeStream, 39); // 96+9 // Sound2 writeStream->writeUint16BE(_mainChannels.sound2.castLib); // 144+0 writeStream->writeUint16BE(_mainChannels.sound2.member); // 144+2 writeStream->writeUint32BE(_mainChannels.sound2SpriteListIdx); // 144+4 writeStream->writeByte(_mainChannels.colorSound2); // 144+8 writePadding(writeStream, 39); // 144+9 // Sound1 writeStream->writeUint16BE(_mainChannels.sound1.castLib); // 192+0 writeStream->writeUint16BE(_mainChannels.sound1.member); // 192+2 writeStream->writeUint32BE(_mainChannels.sound1SpriteListIdx); // 192+4 writeStream->writeByte(_mainChannels.colorSound1); // 192+8 writePadding(writeStream, 39); // 192+9-23 // Palette writeStream->writeUint16BE(_mainChannels.palette.paletteId.castLib); // 240+0 writeStream->writeUint16BE(_mainChannels.palette.paletteId.member); // 240+2 writeStream->writeByte(_mainChannels.palette.speed); // 240+4 writeStream->writeByte(_mainChannels.palette.flags); // 240+5 writeStream->writeByte(_mainChannels.palette.firstColor); // 240+6 writeStream->writeByte(_mainChannels.palette.lastColor); // 240+7 writeStream->writeUint16BE(_mainChannels.palette.frameCount); // 240+8 writeStream->writeUint16BE(_mainChannels.palette.cycleCount); // 240+10 writeStream->writeByte(_mainChannels.palette.fade); // 240+12 writeStream->writeByte(_mainChannels.palette.delay); // 240+13 writeStream->writeByte(_mainChannels.palette.style); // 240+14 writeStream->writeByte(_mainChannels.palette.colorCode); // 240+15 writeStream->writeUint32BE(_mainChannels.palette.spriteListIdx); // 240+16 writePadding(writeStream, 28); // 240+20 } void Frame::readSpriteD7(Common::MemoryReadStreamEndian &stream, uint16 offset, uint16 size) { uint16 spritePosition = (offset - kMainChannelSizeD7) / kSprChannelSizeD7; uint16 spriteStart = spritePosition * kSprChannelSizeD7 + kMainChannelSizeD7; uint16 fieldPosition = offset - spriteStart; debugC(5, kDebugLoading, "Frame::readSpriteD7(): sprite: %d offset: %d size: %d, field: %d", spritePosition, offset, size, fieldPosition); if (debugChannelSet(8, kDebugLoading)) { stream.hexdump(size); } Sprite &sprite = *_sprites[spritePosition + 1]; uint32 initPos = stream.pos(); uint32 finishPosition = initPos + size; readSpriteDataD7(stream, sprite, initPos - fieldPosition, finishPosition); if (stream.pos() > finishPosition) { // This means that the relevant `case` label reads too many bytes and must be split error("Frame::readSpriteD7(): Read %" PRId64 " extra bytes", stream.pos() - finishPosition); } // 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; } void readSpriteDataD7(Common::SeekableReadStreamEndian &stream, Sprite &sprite, uint32 startPosition, uint32 finishPosition) { byte unk[12]; while (stream.pos() < finishPosition) { switch (stream.pos() - startPosition) { case 0: if (sprite._puppet) { stream.readByte(); } else { sprite._spriteType = (SpriteType)stream.readByte(); } break; case 1: if (sprite._puppet || sprite.getAutoPuppet(kAPInk)) { stream.readByte(); } else { sprite._inkData = stream.readByte(); sprite._ink = static_cast(sprite._inkData & 0x3f); sprite._trails = sprite._inkData & 0x40 ? true : false; sprite._stretch = sprite._inkData & 0x80 ? true : false; } break; case 2: if (sprite._puppet || sprite.getAutoPuppet(kAPForeColor)) { stream.readByte(); } else { sprite._foreColor = g_director->transformColor((uint8)stream.readByte()); } break; case 3: if (sprite._puppet || sprite.getAutoPuppet(kAPBackColor)) { stream.readByte(); } else { sprite._backColor = g_director->transformColor((uint8)stream.readByte()); } break; case 4: if (sprite._puppet || sprite.getAutoPuppet(kAPCast)) { stream.readSint16(); } else { int castLib = stream.readSint16(); sprite._castId = CastMemberID(sprite._castId.member, castLib); } break; case 6: if (sprite._puppet || sprite.getAutoPuppet(kAPCast)) { stream.readUint16(); } else { uint16 memberID = stream.readUint16(); sprite._castId = CastMemberID(memberID, sprite._castId.castLib); // Inherit castLib from previous frame } break; case 8: if (sprite._puppet || sprite.getAutoPuppet(kAPCast)) { stream.readUint32(); } else { sprite._spriteListIdx = stream.readUint32(); } break; case 10: // This field could be optimized if (sprite._puppet || sprite.getAutoPuppet(kAPCast)) { stream.readUint16(); } else { sprite._spriteListIdx = stream.readUint16(); } break; case 12: if (sprite._puppet || sprite.getAutoPuppet(kAPLoc)) { stream.readUint16(); } else { sprite._startPoint.y = (int16)stream.readUint16(); } break; case 14: if (sprite._puppet || sprite.getAutoPuppet(kAPLoc)) { stream.readUint16(); } else { sprite._startPoint.x = (int16)stream.readUint16(); } break; case 16: if (sprite._puppet || sprite.getAutoPuppet(kAPHeight)) { stream.readUint16(); } else { sprite._height = (int16)stream.readUint16(); } break; case 18: if (sprite._puppet || sprite.getAutoPuppet(kAPWidth)) { stream.readUint16(); } else { sprite._width = (int16)stream.readUint16(); } break; case 20: if (sprite._puppet || sprite.getAutoPuppet(kAPMoveable)) { stream.readByte(); } else { // & 0x0f scorecolor // 0x10 forecolor is rgb // 0x20 bgcolor is rgb // 0x40 editable // 0x80 moveable sprite._colorcode = stream.readByte(); sprite._editable = ((sprite._colorcode & 0x40) == 0x40); sprite._moveable = ((sprite._colorcode & 0x80) == 0x80); sprite._moveable = ((sprite._colorcode & 0x80) == 0x80); } break; case 21: if (sprite._puppet) { stream.readByte(); } else { sprite._blendAmount = stream.readByte(); } break; case 22: if (sprite._puppet) { stream.readByte(); } else { sprite._thickness = stream.readByte(); } break; case 23: sprite._flags = stream.readByte(); break; case 24: if (sprite._puppet || sprite.getAutoPuppet(kAPForeColor)) { stream.readByte(); } else { sprite._fgColorG = (uint8)stream.readByte(); } break; case 25: if (sprite._puppet || sprite.getAutoPuppet(kAPBackColor)) { stream.readByte(); } else { sprite._bgColorG = (uint8)stream.readByte(); } break; case 26: if (sprite._puppet || sprite.getAutoPuppet(kAPForeColor)) { stream.readByte(); } else { sprite._fgColorB = (uint8)stream.readByte(); } break; case 27: if (sprite._puppet || sprite.getAutoPuppet(kAPBackColor)) { stream.readByte(); } else { sprite._bgColorB = (uint8)stream.readByte(); } break; case 28: sprite._angleRot = stream.readUint32(); break; case 30: // half of the field sprite._angleRot = stream.readUint16(); break; case 32: sprite._angleSkew = stream.readUint32(); break; case 36: stream.read(unk, 12); // alignment bytes hexdumpIfNotZero(unk, 12, "Frame::readSpriteDataD7(): sprite.unk: "); break; default: // This means that a `case` label has to be split at this position error("readSpriteDataD7(): Miscomputed field position: %" PRId64, stream.pos() - startPosition); } } } void writeSpriteDataD7(Common::SeekableWriteStream *writeStream, Sprite &sprite) { writeStream->writeByte(sprite._spriteType); // 0 writeStream->writeByte(sprite._inkData); // 1 writeStream->writeByte(sprite._foreColor); // 2 writeStream->writeByte(sprite._backColor); // 3 writeStream->writeSint16BE(sprite._castId.castLib); // 4, 5 writeStream->writeUint16BE(sprite._castId.member); // 6, 7 writeStream->writeUint32BE(sprite._spriteListIdx); // 8, 9, 10, 11 writeStream->writeUint16BE(sprite._startPoint.y); // 12, 13 writeStream->writeUint16BE(sprite._startPoint.x); // 14, 15 writeStream->writeUint16BE(sprite._height); // 16, 17 writeStream->writeUint16BE(sprite._width); // 18, 19 writeStream->writeByte(sprite._colorcode); // 20 writeStream->writeByte(sprite._blendAmount); // 21 writeStream->writeByte(sprite._thickness); // 22 writeStream->writeByte(sprite._flags); // 23 writeStream->writeByte(sprite._fgColorG); // 24 writeStream->writeByte(sprite._bgColorG); // 25 writeStream->writeByte(sprite._fgColorB); // 26 writeStream->writeByte(sprite._bgColorB); // 27 writeStream->writeUint32BE(sprite._angleRot); // 28 writeStream->writeUint32BE(sprite._angleSkew); // 32 writePadding(writeStream, 12); // 36-47 } /************************** * * Utility Functions * **************************/ Common::String Frame::formatChannelInfo() { Common::String result; result += Common::String::format("TMPO: tempo: %d, skipFrameFlag: %d, blend: %d\n", _mainChannels.tempo, _mainChannels.skipFrameFlag, _mainChannels.blend); if (_mainChannels.palette.paletteId.isNull()) { result += Common::String::format("PAL: paletteId: %s, firstColor: %d, lastColor: %d, flags: %d, cycleCount: %d, speed: %d, frameCount: %d, fade: %d, delay: %d, style: %d\n", _mainChannels.palette.paletteId.asString().c_str(), _mainChannels.palette.firstColor, _mainChannels.palette.lastColor, _mainChannels.palette.flags, _mainChannels.palette.cycleCount, _mainChannels.palette.speed, _mainChannels.palette.frameCount, _mainChannels.palette.fade, _mainChannels.palette.delay, _mainChannels.palette.style); } else { result += Common::String::format("PAL: paletteId: 000\n"); } result += Common::String::format("TRAN: transType: %d, transDuration: %d, transChunkSize: %d\n", _mainChannels.transType, _mainChannels.transDuration, _mainChannels.transChunkSize); result += Common::String::format("SND: 1 sound1: %d, soundType1: %d\n", _mainChannels.sound1.member, _mainChannels.soundType1); result += Common::String::format("SND: 2 sound2: %d, soundType2: %d\n", _mainChannels.sound2.member, _mainChannels.soundType2); result += Common::String::format("LSCR: actionId: %s\n", _mainChannels.actionId.asString().c_str()); for (int i = 0; i < _numChannels; i++) { Sprite &sprite = *_sprites[i + 1]; if (sprite._castId.member) { result += Common::String::format("CH: %-3d castId: %s, [inkData: 0x%02x [ink: %d, trails: %d, stretch: %d, line: %d], %dx%d@%d,%d type: %d (%s) fg: %d bg: %d], script: %s, colorcode: 0x%x, blendAmount: 0x%x, unk3: 0x%x\n", i + 1, sprite._castId.asString().c_str(), sprite._inkData, sprite._ink, sprite._trails, sprite._stretch, sprite._thickness, sprite._width, sprite._height, sprite._startPoint.x, sprite._startPoint.y, sprite._spriteType, spriteType2str(sprite._spriteType), sprite._foreColor, sprite._backColor, sprite._scriptId.asString().c_str(), sprite._colorcode, sprite._blendAmount, sprite._unk3); } else { result += Common::String::format("CH: %-3d castId: 000\n", i + 1); } } return result; } } // End of namespace Director