void DECFile::load(Common::SeekableSubReadStreamEndian &dec, const Common::String &fileName) { dec.skip(2); // Unused int16 backdropCount = dec.readUint16(); int16 layerCount = dec.readUint16(); // Sanity checks if (backdropCount > 1) warning("DECFile::load(): More than one backdrop (%d) in file \"%s\"", backdropCount, fileName.c_str()); if (layerCount < 1) warning("DECFile::load(): Less than one layer (%d) in file \"%s\"", layerCount, fileName.c_str()); // Load the backdrop if (backdropCount > 0) { loadBackdrop(dec); // We only support one backdrop, skip the rest dec.skip((backdropCount - 1) * (13 + (_hasPadding ? 1 : 0))); } // Load the layers _layers.reserve(MAX(0, layerCount - 1)); for (int i = 0; i < layerCount - 1; i++) _layers.push_back(loadLayer(dec)); // Load the backdrop parts if (backdropCount > 0) loadParts(dec); }
Common::Array<NodePtr> Database::loadRoomScripts(RoomData *room) { Common::Array<NodePtr> nodes; Common::SeekableSubReadStreamEndian *file = openDatabaseFile(); // Load the node scripts if (room->scriptsOffset) { file->seek(room->scriptsOffset); loadRoomNodeScripts(file, nodes); } // Load the ambient sound scripts, if any if (room->ambSoundsOffset) { file->seek(room->ambSoundsOffset); loadRoomSoundScripts(file, nodes, false); } if (room->unkOffset) { file->seek(room->unkOffset); loadRoomSoundScripts(file, nodes, true); } delete file; return nodes; }
void Frame::readPaletteInfo(Common::SeekableSubReadStreamEndian &stream) { _palette->firstColor = stream.readByte(); _palette->lastColor = stream.readByte(); _palette->flags = stream.readByte(); _palette->speed = stream.readByte(); _palette->frameCount = stream.readUint16(); stream.skip(8); //unknown }
void DECFile::loadPart(Part &part, Common::SeekableSubReadStreamEndian &dec) { part.layer = dec.readByte() - 1; part.part = dec.readByte(); dec.skip(1); // Unknown part.x = dec.readUint16(); part.y = dec.readUint16(); part.transp = dec.readByte() != 0; }
void DECFile::loadParts(Common::SeekableSubReadStreamEndian &dec) { dec.skip(13); // Name if (_hasPadding) dec.skip(1); dec.skip(13); // File? if (_hasPadding) dec.skip(1); uint16 partCount = dec.readUint16(); _parts.resize(partCount); for (PartArray::iterator p = _parts.begin(); p != _parts.end(); ++p) loadPart(*p, dec); }
Database::Database(Myst3Engine *vm) : _vm(vm), _currentRoomID(0), _executableVersion(0), _currentRoomData(0) { _executableVersion = _vm->getExecutableVersion(); if (_executableVersion != 0) { debug("Initializing database from %s (Platform: %s) (%s)", _executableVersion->executable, getPlatformDescription(_vm->getPlatform()), _executableVersion->description); } else { error("Could not find any executable to load"); } // Load the ages and rooms description Common::SeekableSubReadStreamEndian *file = openDatabaseFile(); file->seek(_executableVersion->ageTableOffset); _ages = loadAges(*file); for (uint i = 0; i < _ages.size(); i++) { file->seek(_ages[i].roomsOffset); // Read the room offset table Common::Array<uint32> roomsOffsets; for (uint j = 0; j < _ages[i].roomCount; j++) { uint32 offset = file->readUint32() - _executableVersion->baseOffset; roomsOffsets.push_back(offset); } // Load the rooms for (uint j = 0; j < roomsOffsets.size(); j++) { file->seek(roomsOffsets[j]); _ages[i].rooms.push_back(loadRoomDescription(*file)); } } file->seek(_executableVersion->nodeInitScriptOffset); _nodeInitScript = loadOpcodes(*file); file->seek(_executableVersion->soundNamesOffset); loadSoundNames(file); // TODO: Remove once the offset table is complete if (!_executableVersion->ambientCuesOffset) { error("The description for this executable (%s, %s) does not contain the ambient cues offset. Please contact the ResidualVM team.", _executableVersion->executable, _executableVersion->description); } file->seek(_executableVersion->ambientCuesOffset); loadAmbientCues(file); preloadCommonRooms(file); initializeZipBitIndexTable(file); delete file; }
void DECFile::loadBackdrop(Common::SeekableSubReadStreamEndian &dec) { // Interestingly, DEC files reference "FOO.LBM" instead of "FOO.CMP" Common::String file = Util::setExtension(Util::readString(dec, 13), ""); if (_hasPadding) dec.skip(1); _backdrop = new CMPFile(_vm, file, _width, _height, _bpp); }
void TalkTable_GFF::readString05(Common::SeekableSubReadStreamEndian &huffTree, Common::SeekableSubReadStreamEndian &bitStream, Entry &entry) const { /* Read a string encoded in a Huffman'd bitstream. * * The Huffman tree itself is made up of signed 32bit nodes: * - Positive values are internal nodes, encoding a child index * - Negative values are leaf nodes, encoding an UTF-16 character value * * Kudos to Rick (gibbed) (<http://gib.me/>). */ std::vector<uint16> utf16Str; const uint32 startOffset = entry.strct->getUint(kGFF4HuffTalkStringBitOffset); uint32 index = startOffset >> 5; uint32 shift = startOffset & 0x1F; do { ptrdiff_t e = (huffTree.size() / 8) - 1; while (e >= 0) { bitStream.seek(index * 4); const ptrdiff_t offset = (bitStream.readUint32() >> shift) & 1; huffTree.seek(((e * 2) + offset) * 4); e = huffTree.readSint32(); shift++; index += (shift >> 5); shift %= 32; } utf16Str.push_back(TO_LE_16(0xFFFF - e)); } while (utf16Str.back() != 0); const byte *data = reinterpret_cast<const byte *>(&utf16Str[0]); const size_t size = utf16Str.size() * 2; entry.text = Common::readString(data, size, Common::kEncodingUTF16LE); }
CondScript Database::loadCondScript(Common::SeekableSubReadStreamEndian &s) { CondScript script; script.condition = s.readUint16(); if(!script.condition) return script; // WORKAROUND: Original data bug in MATO 32765 // The script data for node MATO 32765 is missing its first two bytes // of data, resulting in incorrect opcodes being read // Original disassembly: // init 0 > c[v565 != 0] // op 115, ifVarInRange ( ) // op 45, inventoryAddBack ( ) // op 53, varSetValue ( vSunspotColor 4090 ) // op 53, varSetValue ( vSunspotRadius 40 ) // op 33, waterEffectSetWave ( 100 80 ) // op 32, waterEffectSetAttenuation ( 359 ) // op 31, waterEffectSetSpeed ( 15 ) // Fixed disassembly // init 0 > c[v1 != 0] // op 53, varSetValue ( vSunspotIntensity 45 ) // op 53, varSetValue ( vSunspotColor 4090 ) // op 53, varSetValue ( vSunspotRadius 40 ) // op 33, waterEffectSetWave ( 100 80 ) // op 32, waterEffectSetAttenuation ( 359 ) // op 31, waterEffectSetSpeed ( 15 ) if (script.condition == 565) { script.condition = 1; s.seek(-2, SEEK_CUR); } // END WORKAROUND script.script = loadOpcodes(s); return script; }
void LBGraphics::setPalette(uint16 id) { // Old Living Books games use the old CTBL-style palette format while newer // games use the better tPAL format which can store partial palettes. if (_vm->isPreMohawk()) { Common::SeekableSubReadStreamEndian *ctblStream = _vm->wrapStreamEndian(ID_CTBL, id); uint16 colorCount = ctblStream->readUint16(); byte *palette = new byte[colorCount * 3]; for (uint16 i = 0; i < colorCount; i++) { palette[i * 3 + 0] = ctblStream->readByte(); palette[i * 3 + 1] = ctblStream->readByte(); palette[i * 3 + 2] = ctblStream->readByte(); ctblStream->readByte(); } delete ctblStream; _vm->_system->getPaletteManager()->setPalette(palette, 0, colorCount); delete[] palette; } else { GraphicsManager::setPalette(id); } }
Database::Database(Myst3Engine *vm) : _vm(vm), _currentRoomID(0), _executableVersion(0), _currentRoomData(0) { _executableVersion = _vm->getExecutableVersion(); if (_executableVersion != 0) { debug("Initializing database from %s (Platform: %s) (%s)", _executableVersion->executable, getPlatformDescription(_vm->getPlatform()), _executableVersion->description); } else { error("Could not find any executable to load"); } // Load the ages and rooms description Common::SeekableSubReadStreamEndian *file = openDatabaseFile(); file->seek(_executableVersion->ageTableOffset); _ages = loadAges(*file); for (uint i = 0; i < _ages.size(); i++) { file->seek(_ages[i].roomsOffset); // Read the room offset table Common::Array<uint32> roomsOffsets; for (uint j = 0; j < _ages[i].roomCount; j++) { uint32 offset = file->readUint32() - _executableVersion->baseOffset; roomsOffsets.push_back(offset); } // Load the rooms for (uint j = 0; j < roomsOffsets.size(); j++) { file->seek(roomsOffsets[j]); _ages[i].rooms.push_back(loadRoomDescription(*file)); } } file->seek(_executableVersion->nodeInitScriptOffset); _nodeInitScript = loadOpcodes(*file); file->seek(_executableVersion->soundNamesOffset); loadSoundNames(file); delete file; preloadCommonRooms(); }
void Frame::renderText(Graphics::ManagedSurface &surface, uint16 spriteID) { uint16 castID = _sprites[spriteID]->_castId; TextCast *textCast = static_cast<TextCast *>(_vm->_currentScore->_casts[castID]); Common::SeekableSubReadStreamEndian *textStream; if (_vm->_currentScore->_movieArchive->hasResource(MKTAG('S','T','X','T'), castID + 1024)) { textStream = _vm->_currentScore->_movieArchive->getResource(MKTAG('S','T','X','T'), castID + 1024); } else { textStream = _vm->getSharedSTXT()->getVal(spriteID + 1024); } /*uint32 unk1 = */ textStream->readUint32(); uint32 strLen = textStream->readUint32(); /*uin32 dataLen = */ textStream->readUint32(); Common::String text; for (uint32 i = 0; i < strLen; i++) { byte ch = textStream->readByte(); if (ch == 0x0d) { ch = '\n'; } text += ch; } uint32 rectLeft = static_cast<TextCast *>(_sprites[spriteID]->_cast)->initialRect.left; uint32 rectTop = static_cast<TextCast *>(_sprites[spriteID]->_cast)->initialRect.top; int x = _sprites[spriteID]->_startPoint.x + rectLeft; int y = _sprites[spriteID]->_startPoint.y + rectTop; int height = _sprites[spriteID]->_height; int width = _sprites[spriteID]->_width; const char *fontName; if (_vm->_currentScore->_fontMap.contains(textCast->fontId)) { fontName = _vm->_currentScore->_fontMap[textCast->fontId].c_str(); } else if ((fontName = _vm->_wm->getFontName(textCast->fontId, textCast->fontSize)) == NULL) { warning("Unknown font id %d, falling back to default", textCast->fontId); fontName = _vm->_wm->getFontName(0, 12); } const Graphics::Font *font = _vm->_wm->getFont(fontName, Graphics::FontManager::kBigGUIFont); font->drawString(&surface, text, x, y, width, 0); if (textCast->borderSize != kSizeNone) { uint16 size = textCast->borderSize; //Indent from borders, measured in d4 x -= 1; y -= 4; height += 4; width += 1; while (size) { surface.frameRect(Common::Rect(x, y, x + height, y + width), 0); x--; y--; height += 2; width += 2; size--; } } if (textCast->gutterSize != kSizeNone) { x -= 1; y -= 4; height += 4; width += 1; uint16 size = textCast->gutterSize; surface.frameRect(Common::Rect(x, y, x + height, y + width), 0); while (size) { surface.drawLine(x + width, y, x + width, y + height, 0); surface.drawLine(x, y + height, x + width, y + height, 0); x++; y++; size--; } } }
void Frame::readSprite(Common::SeekableSubReadStreamEndian &stream, uint16 offset, uint16 size) { uint16 spritePosition = (offset - 32) / 16; uint16 spriteStart = spritePosition * 16 + 32; uint16 fieldPosition = offset - spriteStart; uint16 finishPosition = fieldPosition + size; Sprite &sprite = *_sprites[spritePosition]; while (fieldPosition < finishPosition) { switch (fieldPosition) { case kSpritePositionUnk1: /*byte x1 = */ stream.readByte(); fieldPosition++; break; case kSpritePositionEnabled: sprite._enabled = (stream.readByte() != 0); fieldPosition++; break; case kSpritePositionUnk2: /*byte x2 = */ stream.readUint16(); fieldPosition += 2; break; case kSpritePositionFlags: sprite._flags = stream.readUint16(); sprite._ink = static_cast<InkType>(sprite._flags & 0x3f); if (sprite._flags & 0x40) sprite._trails = 1; else sprite._trails = 0; fieldPosition += 2; break; case kSpritePositionCastId: sprite._castId = stream.readUint16(); fieldPosition += 2; break; case kSpritePositionY: sprite._startPoint.y = stream.readUint16(); fieldPosition += 2; break; case kSpritePositionX: sprite._startPoint.x = stream.readUint16(); fieldPosition += 2; break; case kSpritePositionWidth: sprite._width = stream.readUint16(); fieldPosition += 2; break; case kSpritePositionHeight: sprite._height = stream.readUint16(); fieldPosition += 2; break; default: //end cycle, go to next sprite channel readSprite(stream, spriteStart + 16, finishPosition - fieldPosition); fieldPosition = finishPosition; break; } } }
void Frame::readMainChannels(Common::SeekableSubReadStreamEndian &stream, uint16 offset, uint16 size) { uint16 finishPosition = offset + size; while (offset < finishPosition) { switch(offset) { case kScriptIdPosition: _actionId = stream.readByte(); offset++; break; case kSoundType1Position: _soundType1 = stream.readByte(); offset++; break; case kTransFlagsPosition: { uint8 transFlags = stream.readByte(); if (transFlags & 0x80) _transArea = 1; else _transArea = 0; _transDuration = transFlags & 0x7f; offset++; } break; case kTransChunkSizePosition: _transChunkSize = stream.readByte(); offset++; break; case kTempoPosition: _tempo = stream.readByte(); offset++; break; case kTransTypePosition: _transType = static_cast<TransitionType>(stream.readByte()); offset++; break; case kSound1Position: _sound1 = stream.readUint16(); offset+=2; break; case kSkipFrameFlagsPosition: _skipFrameFlag = stream.readByte(); offset++; break; case kBlendPosition: _blend = stream.readByte(); offset++; break; case kSound2Position: _sound2 = stream.readUint16(); offset += 2; break; case kSound2TypePosition: _soundType2 = stream.readByte(); offset += 1; break; case kPaletePosition: if (stream.readUint16()) readPaletteInfo(stream); offset += 16; break; default: offset++; stream.readByte(); debugC(kDebugLoading, "Frame::readMainChannels: Field Position %d, Finish Position %d", offset, finishPosition); break; } } }
Stxt::Stxt(Common::SeekableSubReadStreamEndian &textStream) { // TODO: Side effects on textStream make this a little hard to understand in context? uint32 unk1 = textStream.readUint32(); uint32 strLen = textStream.readUint32(); uint32 dataLen = textStream.readUint32(); Common::String text; for (uint32 i = 0; i < strLen; i++) { byte ch = textStream.readByte(); if (ch == 0x0d) { ch = '\n'; } text += ch; } debugC(3, kDebugText, "Stxt init: unk1: %d strLen: %d dataLen: %d textlen: %u", unk1, strLen, dataLen, text.size()); if (strLen < 200) debugC(3, kDebugText, "text: '%s'", text.c_str()); uint16 formattingCount = textStream.readUint16(); uint32 prevPos = 0; while (formattingCount) { uint32 formatStartOffset = textStream.readUint32(); uint16 unk1f = textStream.readUint16(); uint16 unk2f = textStream.readUint16(); _fontId = textStream.readUint16(); _textSlant = textStream.readByte(); byte unk3f = textStream.readByte(); _fontSize = textStream.readUint16(); _palinfo1 = textStream.readUint16(); _palinfo2 = textStream.readUint16(); _palinfo3 = textStream.readUint16(); debugC(3, kDebugText, "Stxt init: formattingCount: %u, formatStartOffset: %d, unk1: %d unk2: %d, fontId: %d, textSlant: %d", formattingCount, formatStartOffset, unk1f, unk2f, _fontId, _textSlant); debugC(3, kDebugText, " unk3: %d, fontSize: %d, p0: %x p1: %x p2: %x", unk3f, _fontSize, _palinfo1, _palinfo2, _palinfo3); assert(prevPos <= formatStartOffset); // If this is triggered, we have to implement sorting while (prevPos != formatStartOffset) { char f = text.firstChar(); _ftext += text.firstChar(); text.deleteChar(0); if (f == '\001') // Insert two \001s as a replacement _ftext += '\001'; prevPos++; debugCN(4, kDebugText, "%c", f); } debugCN(4, kDebugText, "*"); _ftext += Common::String::format("\001\015%c%c%c%c%c%c%c%c%c%c%c%c", (_fontId >> 8) & 0xff, _fontId & 0xff, _textSlant & 0xff, unk3f & 0xff, (_fontSize >> 8) & 0xff, _fontSize & 0xff, (_palinfo1 >> 8) & 0xff, _palinfo1 & 0xff, (_palinfo2 >> 8) & 0xff, _palinfo2 & 0xff, (_palinfo3 >> 8) & 0xff, _palinfo3 & 0xff); formattingCount--; } debugC(4, kDebugText, "%s", text.c_str()); _ftext += text; }