diff options
Diffstat (limited to 'engines/mads/scene_data.cpp')
-rw-r--r-- | engines/mads/scene_data.cpp | 463 |
1 files changed, 463 insertions, 0 deletions
diff --git a/engines/mads/scene_data.cpp b/engines/mads/scene_data.cpp new file mode 100644 index 0000000000..1494f62d7a --- /dev/null +++ b/engines/mads/scene_data.cpp @@ -0,0 +1,463 @@ +/* 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/scummsys.h" +#include "mads/scene_data.h" +#include "mads/mads.h" +#include "mads/compression.h" +#include "mads/screen.h" +#include "mads/resources.h" +#include "mads/dragonsphere/dragonsphere_scenes.h" +#include "mads/nebular/nebular_scenes.h" +#include "mads/phantom/phantom_scenes.h" + +namespace MADS { + +KernelMessage::KernelMessage() { + _flags = 0; + _sequenceIndex = 0; + _color1 = 0; + _color2 = 0; + _msgOffset = 0; + _numTicks = 0; + _frameTimer2 = 0; + _frameTimer = 0; + _timeout = 0; + _trigger = 0; + _abortMode = SEQUENCE_TRIGGER_PARSER; + _actionDetails._verbId = VERB_NONE; + _actionDetails._objectNameId = 0; + _actionDetails._indirectObjectId = 0; + _textDisplayIndex = 0; +} + +/*------------------------------------------------------------------------*/ + +void ARTHeader::load(Common::SeekableReadStream *f, bool isV2) { + if (!isV2) { + // Read in dimensions of image + _width = f->readUint16LE(); + _height = f->readUint16LE(); + } + + // Read in palette information + int palCount = f->readUint16LE(); + for (int i = 0; i < palCount; ++i) { + RGB6 rgb; + rgb.load(f); + _palette.push_back(rgb); + } + f->skip(6 * (256 - palCount)); + + // Read palette animations + int cycleCount = f->readUint16LE(); + for (int i = 0; i < cycleCount; ++i) { + PaletteCycle cycle; + cycle._colorCount = f->readByte(); + cycle._firstListColor = f->readByte(); + cycle._firstColorIndex = f->readByte(); + cycle._ticks = f->readByte(); + + _paletteCycles.push_back(cycle); + } +} + +/*------------------------------------------------------------------------*/ + +void SceneInfo::SpriteInfo::load(Common::SeekableReadStream *f) { + f->skip(3); + _spriteSetIndex = f->readByte(); + f->skip(2); + _position.x = f->readSint16LE(); + _position.y = f->readSint16LE(); + _depth = f->readByte(); + _scale = f->readByte(); +} + +/*------------------------------------------------------------------------*/ + +SceneInfo::SceneInfo(MADSEngine *vm) : _vm(vm) { + _sceneId = 0; + _artFileNum = 0; + _depthStyle = 0; + _width = 0; + _height = 0; + _yBandsEnd = 0; + _yBandsStart = 0; + _maxScale = 0; + _minScale = 0; + _field4A = 0; + _usageIndex = 0; + for (int i = 0; i < 15; ++i) + _depthList[i] = 0; +} + +SceneInfo *SceneInfo::init(MADSEngine *vm) { + switch (vm->getGameID()) { + case GType_RexNebular: + return new Nebular::SceneInfoNebular(vm); + case GType_Dragonsphere: + return new Dragonsphere::SceneInfoDragonsphere(vm); + case GType_Phantom: + return new Phantom::SceneInfoPhantom(vm); + default: + error("SceneInfo: Unknown game"); + } + + return nullptr; +} + +void SceneInfo::load(int sceneId, int variant, const Common::String &resName, + int flags, DepthSurface &depthSurface, MSurface &bgSurface) { + bool sceneFlag = sceneId >= 0; + + // Figure out the resource to use + Common::String resourceName; + if (sceneFlag) { + resourceName = Resources::formatName(RESPREFIX_RM, sceneId, ".DAT"); + } else { + resourceName = "*" + Resources::formatResource(resName, resName); + } + + // Open the scene info resource for access + File infoFile(resourceName); + MadsPack infoPack(&infoFile); + + // Read in basic data + Common::SeekableReadStream *infoStream = infoPack.getItemStream(0); + if (_vm->getGameID() == GType_RexNebular) { + _sceneId = infoStream->readUint16LE(); + } else { + infoStream->skip(6); // actual scene ID (string) + _sceneId = sceneId; + } + + // TODO: The following isn't quite right for V2 games (it's all 0) + _artFileNum = infoStream->readUint16LE(); + _depthStyle = infoStream->readUint16LE(); + _width = infoStream->readUint16LE(); + _height = infoStream->readUint16LE(); + + // HACK for V2 games (for now) + if (_vm->getGameID() != GType_RexNebular) { + _width = 320; + _height = 156; + } + + infoStream->skip(24); + + int nodeCount = infoStream->readUint16LE(); + _yBandsEnd = infoStream->readUint16LE(); + _yBandsStart = infoStream->readUint16LE(); + _maxScale = infoStream->readUint16LE(); + _minScale = infoStream->readUint16LE(); + for (int i = 0; i < DEPTH_BANDS_SIZE; ++i) + _depthList[i] = infoStream->readUint16LE(); + _field4A = infoStream->readUint16LE(); + + // Load the set of objects that are associated with the scene + for (int i = 0; i < 20; ++i) { + WalkNode node; + node.load(infoStream); + + if (i < nodeCount) + _nodes.push_back(node); + } + + int spriteSetsCount = infoStream->readUint16LE(); + int spriteInfoCount = infoStream->readUint16LE(); + + // Load in sprite sets + Common::StringArray setNames; + for (int i = 0; i < 10; ++i) { + char name[64]; + infoStream->read(name, 64); + + if (i < spriteSetsCount) + setNames.push_back(Common::String(name)); + } + + // Load in sprite draw information + Common::Array<SpriteInfo> spriteInfo; + // TODO: The following isn't quite right for V2 games + if (_vm->getGameID() == GType_RexNebular) { + for (int i = 0; i < 50; ++i) { + SpriteInfo info; + info.load(infoStream); + + if (i < spriteInfoCount) + spriteInfo.push_back(info); + } + } + delete infoStream; + + int width = _width; + int height = _height; + + if (!bgSurface.getPixels()) { + bgSurface.setSize(width, height); + } + + if (_depthStyle == 2) + width >>= 2; + if (!depthSurface.getPixels()) { + depthSurface.setSize(width, height); + } + + if (_vm->getGameID() == GType_RexNebular) { + // Load the depth surface with the scene codes + Common::SeekableReadStream *depthStream = infoPack.getItemStream(variant + 1); + loadCodes(depthSurface, depthStream); + delete depthStream; + } + + infoFile.close(); + + if (_vm->getGameID() == GType_RexNebular) { + loadMadsV1Background(sceneId, resName, flags, bgSurface); + loadPalette(sceneId, _artFileNum, resName, flags, bgSurface); + } else { + loadMadsV2Background(sceneId, resName, flags, bgSurface); + loadPalette(sceneId, sceneId, resName, flags, bgSurface); + } + + Common::Array<SpriteAsset *> spriteSets; + Common::Array<int> usageList; + + // TODO: The following isn't quite right for V2 games + if (_vm->getGameID() == GType_RexNebular) { + for (uint i = 0; i < setNames.size(); ++i) { + Common::String setResName; + if (sceneFlag || resName.hasPrefix("*")) + setResName += "*"; + setResName += setNames[i]; + + SpriteAsset *sprites = new SpriteAsset(_vm, setResName, flags); + spriteSets.push_back(sprites); + usageList.push_back(sprites->_usageIndex); + } + } + + _vm->_palette->_paletteUsage.updateUsage(usageList, _usageIndex); + + for (uint i = 0; i < spriteInfo.size(); ++i) { + SpriteInfo &si = spriteInfo[i]; + SpriteAsset *asset = spriteSets[si._spriteSetIndex]; + assert(asset && _depthStyle != 2); + + MSprite *spr = asset->getFrame(asset->getCount() - 1); + bgSurface.copyFrom(spr, si._position, si._depth, &depthSurface, + si._scale, spr->getTransparencyIndex()); + } + + // Free the sprite sets + for (int i = (int)spriteSets.size() - 1; i >= 0; --i) { + _vm->_palette->_paletteUsage.resetPalFlags(spriteSets[i]->_usageIndex); + delete spriteSets[i]; + } +} + +void SceneInfo::loadPalette(int sceneId, int artFileNum, const Common::String &resName, int flags, MSurface &bgSurface) { + bool sceneFlag = sceneId >= 0; + Common::String resourceName; + bool isV2 = (_vm->getGameID() != GType_RexNebular); + Common::String extension = !isV2 ? ".ART" : ".TT"; + int paletteStream = !isV2 ? 0 : 2; + + // Get the ART resource + if (sceneFlag) { + resourceName = Resources::formatName(RESPREFIX_RM, artFileNum, extension); + } else { + resourceName = "*" + Resources::formatResource(resName, resName); + } + + // Load in the ART header and palette + File artFile(resourceName); + MadsPack artResource(&artFile); + Common::SeekableReadStream *stream = artResource.getItemStream(paletteStream); + + ARTHeader artHeader; + artHeader.load(stream, isV2); + delete stream; + + // Copy out the palette animation data + for (uint i = 0; i < artHeader._paletteCycles.size(); ++i) + _paletteCycles.push_back(artHeader._paletteCycles[i]); + + if (!(flags & 1)) { + if (!_vm->_palette->_paletteUsage.empty()) { + _vm->_palette->_paletteUsage.getKeyEntries(artHeader._palette); + _vm->_palette->_paletteUsage.prioritize(artHeader._palette); + } + + _usageIndex = _vm->_palette->_paletteUsage.process(artHeader._palette, + (flags & 0xF800) | 0x8000); + if (_usageIndex > 0) { + _vm->_palette->_paletteUsage.transform(artHeader._palette); + + for (uint i = 0; i < _paletteCycles.size(); ++i) { + byte listColor = _paletteCycles[i]._firstListColor; + _paletteCycles[i]._firstColorIndex = artHeader._palette[listColor]._palIndex; + } + } + } + + if (!(flags & 1)) { + // Translate the background to use the correct palette indexes + bgSurface.translate(artHeader._palette); + } +} + +void SceneInfo::loadMadsV1Background(int sceneId, const Common::String &resName, int flags, MSurface &bgSurface) { + bool sceneFlag = sceneId >= 0; + Common::String resourceName; + Common::SeekableReadStream *stream; + + // Get the ART resource + if (sceneFlag) { + resourceName = Resources::formatName(RESPREFIX_RM, _artFileNum, ".ART"); + } else { + resourceName = "*" + Resources::formatResource(resName, resName); + } + + // Load in the ART data + File artFile(resourceName); + MadsPack artResource(&artFile); + + // Read in the background surface data + assert(_width == bgSurface.w && _height == bgSurface.h); + stream = artResource.getItemStream(1); + stream->read(bgSurface.getPixels(), bgSurface.w * bgSurface.h); + + // Close the ART file + delete stream; + artFile.close(); +} + +void SceneInfo::loadMadsV2Background(int sceneId, const Common::String &resName, int flags, MSurface &bgSurface) { + Common::String tileMapResourceName = Resources::formatName(RESPREFIX_RM, sceneId, ".MM"); + File tileMapFile(tileMapResourceName); + MadsPack tileMapPack(&tileMapFile); + Common::SeekableReadStream *mapStream = tileMapPack.getItemStream(0); + + // Get the details of the tiles and map + mapStream->readUint32LE(); + int tileCountX = mapStream->readUint16LE(); + int tileCountY = mapStream->readUint16LE(); + int tileWidthMap = mapStream->readUint16LE(); + int tileHeightMap = mapStream->readUint16LE(); + int screenWidth = mapStream->readUint16LE(); + int screenHeight = mapStream->readUint16LE(); + int tileCountMap = tileCountX * tileCountY; + delete mapStream; + + // Obtain tile map information + typedef Common::List<Common::SharedPtr<MSurface> > TileSetList; + typedef TileSetList::iterator TileSetIterator; + TileSetList tileSet; + uint16 *tileMap = new uint16[tileCountMap]; + mapStream = tileMapPack.getItemStream(1); + for (int i = 0; i < tileCountMap; ++i) + tileMap[i] = mapStream->readUint16LE(); + delete mapStream; + tileMapFile.close(); + + // -------------------------------------------------------------------------------- + + // Tile data, which needs to be kept compressed, as the tile map offsets refer to + // the compressed data. Each tile is then uncompressed separately + Common::String tileDataResourceName = Resources::formatName(RESPREFIX_RM, sceneId, ".TT"); + File tileDataFile(tileDataResourceName); + MadsPack tileDataPack(&tileDataFile); + Common::SeekableReadStream *tileDataUncomp = tileDataPack.getItemStream(0); + + // Validate that the data matches between the tiles and tile map file and is valid + int tileCount = tileDataUncomp->readUint16LE(); + int tileWidth = tileDataUncomp->readUint16LE(); + int tileHeight = tileDataUncomp->readUint16LE(); + delete tileDataUncomp; + assert(tileCountMap == tileCount); + assert(tileWidth == tileWidthMap); + assert(tileHeight == tileHeightMap); + assert(screenWidth == _width); + assert(screenHeight <= _height); + + // -------------------------------------------------------------------------------- + + // Get tile data + + tileDataUncomp = tileDataPack.getItemStream(1); + FabDecompressor fab; + uint32 compressedTileDataSize = 0; + + for (int i = 0; i < tileCount; i++) { + tileDataUncomp->seek(i * 4, SEEK_SET); + uint32 tileOfs = tileDataUncomp->readUint32LE(); + MSurface* newTile = new MSurface(tileWidth, tileHeight); + + if (i == tileCount - 1) + compressedTileDataSize = tileDataFile.size() - tileOfs; + else + compressedTileDataSize = tileDataUncomp->readUint32LE() - tileOfs; + + //debugCN(kDebugGraphics, "Tile: %i, compressed size: %i\n", i, compressedTileDataSize); + + newTile->empty(); + + byte *compressedTileData = new byte[compressedTileDataSize]; + + tileDataFile.seek(tileDataPack.getDataOffset() + tileOfs, SEEK_SET); + tileDataFile.read(compressedTileData, compressedTileDataSize); + + fab.decompress(compressedTileData, compressedTileDataSize, (byte*)newTile->getPixels(), tileWidth * tileHeight); + tileSet.push_back(TileSetList::value_type(newTile)); + delete[] compressedTileData; + } + + delete tileDataUncomp; + + // -------------------------------------------------------------------------------- + + // Loop through the mapping data to place the tiles on the screen + + uint16 *tIndex = &tileMap[0]; + for (int y = 0; y < tileCountY; y++) { + for (int x = 0; x < tileCountX; x++) { + int tileIndex = *tIndex++; + assert(tileIndex < tileCount); + TileSetIterator tile = tileSet.begin(); + for (int i = 0; i < tileIndex; i++) + ++tile; + ((*tile).get())->copyTo(&bgSurface, Common::Point(x * tileWidth, y * tileHeight)); + } + } + tileSet.clear(); + tileDataFile.close(); +} + +/*------------------------------------------------------------------------*/ + +SceneLogic::SceneLogic(MADSEngine *vm) : _vm(vm) { + _scene = &_vm->_game->_scene; +} + +} // End of namespace MADS |