ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

archive.cpp

Go to the documentation of this file.
00001 /* ResidualVM - A 3D game interpreter
00002  *
00003  * ResidualVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the AUTHORS
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 #include "engines/myst3/archive.h"
00024 
00025 #include "common/debug.h"
00026 #include "common/memstream.h"
00027 #include "common/substream.h"
00028 
00029 namespace Myst3 {
00030 
00031 void Archive::decryptHeader(Common::SeekableReadStream &inStream, Common::WriteStream &outStream) {
00032     static const uint32 addKey = 0x3C6EF35F;
00033     static const uint32 multKey = 0x0019660D;
00034 
00035     inStream.seek(0);
00036     uint32 size = inStream.readUint32LE();
00037 
00038     bool encrypted = size > 1000000;
00039     
00040     inStream.seek(0);
00041 
00042     if (encrypted) {
00043         uint32 decryptedSize = size ^ addKey;
00044 
00045         uint32 currentKey = 0;
00046         for (uint i = 0; i < decryptedSize; i++) {
00047             currentKey += addKey;
00048             outStream.writeUint32LE(inStream.readUint32LE() ^ currentKey);
00049             currentKey *= multKey;
00050         }
00051     } else {
00052         for (uint i = 0; i < size; i++) {
00053             outStream.writeUint32LE(inStream.readUint32LE());
00054         }
00055     }
00056 }
00057 
00058 static Common::String readFixedString(Common::ReadStream &stream, uint32 length) {
00059     Common::String value;
00060 
00061     for (uint i = 0; i < length; i++) {
00062         value += stream.readByte();
00063     }
00064 
00065     return value;
00066 }
00067 
00068 static uint32 readUint24(Common::ReadStream &stream) {
00069     uint32 value = stream.readUint16LE();
00070     value |= stream.readByte() << 16;
00071     return value;
00072 }
00073 
00074 Archive::DirectorySubEntry Archive::readSubEntry(Common::ReadStream &stream) {
00075     DirectorySubEntry subEntry;
00076 
00077     subEntry.offset = stream.readUint32LE();
00078     subEntry.size = stream.readUint32LE();
00079     uint16 metadataSize = stream.readUint16LE();
00080     subEntry.face = stream.readByte();
00081     subEntry.type = static_cast<ResourceType>(stream.readByte());
00082 
00083     subEntry.metadata.resize(metadataSize);
00084     for (uint i = 0; i < metadataSize; i++) {
00085         subEntry.metadata[i] = stream.readUint32LE();
00086     }
00087 
00088     return subEntry;
00089 }
00090 
00091 Archive::DirectoryEntry Archive::readEntry(Common::ReadStream &stream) {
00092     DirectoryEntry entry;
00093     if (_roomName.empty()) {
00094         entry.roomName = readFixedString(stream, 4);
00095     } else {
00096         entry.roomName = _roomName;
00097     }
00098     entry.index = readUint24(stream);
00099 
00100     byte subItemCount = stream.readByte();
00101     entry.subentries.resize(subItemCount);
00102 
00103     for (uint i = 0; i < subItemCount; i++) {
00104         entry.subentries[i] = readSubEntry(stream);
00105     }
00106 
00107     return entry;
00108 }
00109 
00110 void Archive::readDirectory() {
00111     Common::MemoryWriteStreamDynamic buf(DisposeAfterUse::YES);
00112     decryptHeader(_file, buf);
00113 
00114     Common::MemoryReadStream directory(buf.getData(), buf.size());
00115     _directorySize = directory.readUint32LE();
00116 
00117     while (directory.pos() + 4 < directory.size()) {
00118         _directory.push_back(readEntry(directory));
00119     }
00120 }
00121 
00122 void Archive::visit(ArchiveVisitor &visitor) {
00123     visitor.visitArchive(*this);
00124 
00125     for (uint i = 0; i < _directory.size(); i++) {
00126         visitor.visitDirectoryEntry(_directory[i]);
00127 
00128         for (uint j = 0; j < _directory[i].subentries.size(); j++) {
00129             visitor.visitDirectorySubEntry(_directory[i].subentries[j]);
00130         }
00131     }
00132 }
00133 
00134 Common::SeekableReadStream *Archive::dumpToMemory(uint32 offset, uint32 size) {
00135     _file.seek(offset);
00136     return _file.readStream(size);
00137 }
00138 
00139 uint32 Archive::copyTo(uint32 offset, uint32 size, Common::WriteStream &out) {
00140     Common::SeekableSubReadStream subStream(&_file, offset, offset + size);
00141     subStream.seek(0);
00142     return out.writeStream(&subStream);
00143 }
00144 
00145 const Archive::DirectoryEntry *Archive::getEntry(const Common::String &room, uint32 index) const {
00146     for (uint i = 0; i < _directory.size(); i++) {
00147         const DirectoryEntry &entry = _directory[i];
00148         if (entry.index == index && entry.roomName == room) {
00149             return &entry;
00150         }
00151     }
00152 
00153     return nullptr;
00154 }
00155 
00156 ResourceDescription Archive::getDescription(const Common::String &room, uint32 index, uint16 face,
00157                                                  ResourceType type) {
00158     const DirectoryEntry *entry = getEntry(room, index);
00159     if (!entry) {
00160         return ResourceDescription();
00161     }
00162 
00163     for (uint i = 0; i < entry->subentries.size(); i++) {
00164         const DirectorySubEntry &subentry = entry->subentries[i];
00165         if (subentry.face == face && subentry.type == type) {
00166             return ResourceDescription(this, subentry);
00167         }
00168     }
00169 
00170     return ResourceDescription();
00171 }
00172 
00173 ResourceDescriptionArray Archive::listFilesMatching(const Common::String &room, uint32 index, uint16 face,
00174                                                  ResourceType type) {
00175     const DirectoryEntry *entry = getEntry(room, index);
00176     if (!entry) {
00177         return ResourceDescriptionArray();
00178     }
00179 
00180     ResourceDescriptionArray list;
00181     for (uint i = 0; i < entry->subentries.size(); i++) {
00182         const DirectorySubEntry &subentry = entry->subentries[i];
00183         if (subentry.face == face && subentry.type == type) {
00184             list.push_back(ResourceDescription(this, subentry));
00185         }
00186     }
00187 
00188     return list;
00189 }
00190 
00191 bool Archive::open(const char *fileName, const char *room) {
00192     // If the room name is not provided, it is assumed that
00193     // we are opening a multi-room archive
00194     if (room) {
00195         _roomName = room;
00196     }
00197 
00198     if (_file.open(fileName)) {
00199         readDirectory();
00200         return true;
00201     }
00202     
00203     return false;
00204 }
00205 
00206 void Archive::close() {
00207     _directorySize = 0;
00208     _roomName.clear();
00209     _directory.clear();
00210     _file.close();
00211 }
00212 
00213 ResourceDescription::ResourceDescription() :
00214         _archive(nullptr),
00215         _subentry(nullptr) {
00216 }
00217 
00218 ResourceDescription::ResourceDescription(Archive *archive, const Archive::DirectorySubEntry &subentry) :
00219         _archive(archive),
00220         _subentry(&subentry) {
00221 }
00222 
00223 Common::SeekableReadStream *ResourceDescription::getData() const {
00224     return _archive->dumpToMemory(_subentry->offset, _subentry->size);
00225 }
00226 
00227 ResourceDescription::SpotItemData ResourceDescription::getSpotItemData() const {
00228     assert(_subentry->type == Archive::kSpotItem || _subentry->type == Archive::kLocalizedSpotItem);
00229 
00230     SpotItemData spotItemData;
00231     spotItemData.u = _subentry->metadata[0];
00232     spotItemData.v = _subentry->metadata[1];
00233 
00234     return spotItemData;
00235 }
00236 
00237 ResourceDescription::VideoData ResourceDescription::getVideoData() const {
00238     VideoData videoData;
00239 
00240     if (_subentry->type == Archive::kMovie || _subentry->type == Archive::kMultitrackMovie) {
00241         videoData.v1.setValue(0, static_cast<int32>(_subentry->metadata[0]) * 0.000001f);
00242         videoData.v1.setValue(1, static_cast<int32>(_subentry->metadata[1]) * 0.000001f);
00243         videoData.v1.setValue(2, static_cast<int32>(_subentry->metadata[2]) * 0.000001f);
00244 
00245         videoData.v2.setValue(0, static_cast<int32>(_subentry->metadata[3]) * 0.000001f);
00246         videoData.v2.setValue(1, static_cast<int32>(_subentry->metadata[4]) * 0.000001f);
00247         videoData.v2.setValue(2, static_cast<int32>(_subentry->metadata[5]) * 0.000001f);
00248 
00249         videoData.u      = static_cast<int32>(_subentry->metadata[6]);
00250         videoData.v      = static_cast<int32>(_subentry->metadata[7]);
00251         videoData.width  = static_cast<int32>(_subentry->metadata[8]);
00252         videoData.height = static_cast<int32>(_subentry->metadata[9]);
00253     }
00254 
00255     return videoData;
00256 }
00257 
00258 uint32 ResourceDescription::getMiscData(uint index) const {
00259     assert(_subentry->type == Archive::kNumMetadata || _subentry->type == Archive::kTextMetadata);
00260 
00261     if (index == 0) {
00262         return _subentry->offset;
00263     } else if (index == 1) {
00264         return _subentry->size;
00265     } else {
00266         return _subentry->metadata[index - 2];
00267     }
00268 }
00269 
00270 Common::String ResourceDescription::getTextData(uint index) const {
00271     assert(_subentry->type == Archive::kTextMetadata);
00272 
00273     uint8 key = 35;
00274     uint8 cnt = 0;
00275     uint8 decrypted[89];
00276     memset(decrypted, 0, sizeof(decrypted));
00277 
00278     uint8 *out = &decrypted[0];
00279     while (cnt / 4 < (_subentry->metadata.size() + 2) && cnt < 89) {
00280         // XORed text stored in little endian 32 bit words
00281         *out++ = (getMiscData(cnt / 4) >> (8 * (3 - (cnt % 4)))) ^ key++;
00282         cnt++;
00283     }
00284 
00285     // decrypted contains a null separated string array
00286     // extract the wanted one
00287     cnt = 0;
00288     int i = 0;
00289     Common::String text;
00290     while (cnt <= index && i < 89) {
00291         if (cnt == index)
00292             text += decrypted[i];
00293 
00294         if (!decrypted[i])
00295             cnt++;
00296 
00297         i++;
00298     }
00299 
00300     return text;
00301 }
00302 
00303 ArchiveVisitor::~ArchiveVisitor() {
00304 }
00305 
00306 } // End of namespace Myst3


Generated on Sat Aug 8 2020 05:01:07 for ResidualVM by doxygen 1.7.1
curved edge   curved edge