From 4eba5032d817ea32e3f2f7be52826c2e40471852 Mon Sep 17 00:00:00 2001 From: Filippos Karapetis Date: Sun, 27 Apr 2014 21:14:26 +0300 Subject: MADS: Add initial support for V2 backgrounds and hotspots V2 games include Phantom and Dragonsphere. OUAF hasn't been tested yet --- engines/mads/scene_data.cpp | 267 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 215 insertions(+), 52 deletions(-) (limited to 'engines/mads/scene_data.cpp') diff --git a/engines/mads/scene_data.cpp b/engines/mads/scene_data.cpp index 9f4d7d99ec..646106ee9a 100644 --- a/engines/mads/scene_data.cpp +++ b/engines/mads/scene_data.cpp @@ -26,7 +26,9 @@ #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 { @@ -49,10 +51,12 @@ KernelMessage::KernelMessage() { /*------------------------------------------------------------------------*/ -void ARTHeader::load(Common::SeekableReadStream *f) { - // Read in dimensions of image - _width = f->readUint16LE(); - _height = f->readUint16LE(); +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(); @@ -91,11 +95,18 @@ void SceneInfo::SpriteInfo::load(Common::SeekableReadStream *f) { /*------------------------------------------------------------------------*/ SceneInfo *SceneInfo::init(MADSEngine *vm) { - if (vm->getGameID() == GType_RexNebular) { + switch (vm->getGameID()) { + case GType_RexNebular: return new Nebular::SceneInfoNebular(vm); - } else { - error("Unknown game"); + 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, @@ -116,12 +127,25 @@ void SceneInfo::load(int sceneId, int variant, const Common::String &resName, // Read in basic data Common::SeekableReadStream *infoStream = infoPack.getItemStream(0); - _sceneId = infoStream->readUint16LE(); + 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 == 0) { + _width = 320; + _height = 156; + } + infoStream->skip(24); int nodeCount = infoStream->readUint16LE(); @@ -157,12 +181,15 @@ void SceneInfo::load(int sceneId, int variant, const Common::String &resName, // Load in sprite draw information Common::Array spriteInfo; - for (int i = 0; i < 50; ++i) { - SpriteInfo info; - info.load(infoStream); - - if (i < spriteInfoCount) - spriteInfo.push_back(info); + // 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; @@ -179,16 +206,67 @@ void SceneInfo::load(int sceneId, int variant, const Common::String &resName, depthSurface.setSize(width, height); } - // Load the depth surface with the scene codes - Common::SeekableReadStream *depthStream = infoPack.getItemStream(variant + 1); - loadCodes(depthSurface, depthStream); - delete depthStream; + 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 spriteSets; + Common::Array usageList; + + if (flags & 1) { + 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); + + asset->drawScaled(asset->getCount(), depthSurface, bgSurface, + si._scale, si._depth, si._position); + } + + // Free the sprite sets + for (int i = (int)spriteSets.size() - 1; i >= 0; --i) { + warning("TODO: sub_201C8 SPRITE_SET.field_6"); + 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, ".ART"); + resourceName = Resources::formatName(RESPREFIX_RM, artFileNum, extension); } else { resourceName = "*" + Resources::formatResource(resName, resName); } @@ -196,10 +274,10 @@ void SceneInfo::load(int sceneId, int variant, const Common::String &resName, // Load in the ART header and palette File artFile(resourceName); MadsPack artResource(&artFile); - Common::SeekableReadStream *stream = artResource.getItemStream(0); + Common::SeekableReadStream *stream = artResource.getItemStream(paletteStream); ARTHeader artHeader; - artHeader.load(stream); + artHeader.load(stream, isV2); delete stream; // Copy out the palette animation data @@ -224,52 +302,137 @@ void SceneInfo::load(int sceneId, int variant, const Common::String &resName, } } - // 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); - 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(); +} - Common::Array spriteSets; - Common::Array usageList; - - if (flags & 1) { - 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); - } +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 > 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; } - _vm->_palette->_paletteUsage.updateUsage(usageList, _usageIndex); + delete tileDataUncomp; - for (uint i = 0; i < spriteInfo.size(); ++i) { - SpriteInfo &si = spriteInfo[i]; - SpriteAsset *asset = spriteSets[si._spriteSetIndex]; - assert(asset && _depthStyle != 2); + // -------------------------------------------------------------------------------- - asset->drawScaled(asset->getCount(), depthSurface, bgSurface, - si._scale, si._depth, si._position); - } + // Loop through the mapping data to place the tiles on the screen - // Free the sprite sets - for (int i = (int)spriteSets.size() - 1; i >= 0; --i) { - warning("TODO: sub_201C8 SPRITE_SET.field_6"); - delete spriteSets[i]; + 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(); } /*------------------------------------------------------------------------*/ -- cgit v1.2.3