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

installshield_cab.cpp

Go to the documentation of this file.
00001 /* ScummVM - Graphic Adventure Engine
00002  *
00003  * ScummVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
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 // The following code is based on unshield
00024 // Original copyright:
00025 
00026 // Copyright (c) 2003 David Eriksson <twogood@users.sourceforge.net>
00027 //
00028 // Permission is hereby granted, free of charge, to any person obtaining a copy of
00029 // this software and associated documentation files (the "Software"), to deal in
00030 // the Software without restriction, including without limitation the rights to
00031 // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
00032 // of the Software, and to permit persons to whom the Software is furnished to do
00033 // so, subject to the following conditions:
00034 //
00035 // The above copyright notice and this permission notice shall be included in all
00036 // copies or substantial portions of the Software.
00037 //
00038 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00039 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00040 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
00041 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00042 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00043 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
00044 // SOFTWARE.
00045 
00046 #include "common/archive.h"
00047 #include "common/debug.h"
00048 #include "common/hash-str.h"
00049 #include "common/installshield_cab.h"
00050 #include "common/memstream.h"
00051 #include "common/zlib.h"
00052 
00053 namespace Common {
00054 
00055 class InstallShieldCabinet : public Archive {
00056 public:
00057     InstallShieldCabinet(SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse);
00058     ~InstallShieldCabinet();
00059 
00060     // Archive API implementation
00061     bool hasFile(const String &name) const;
00062     int listMembers(ArchiveMemberList &list) const;
00063     const ArchiveMemberPtr getMember(const String &name) const;
00064     SeekableReadStream *createReadStreamForMember(const String &name) const;
00065 
00066 private:
00067     struct FileEntry {
00068         uint32 uncompressedSize;
00069         uint32 compressedSize;
00070         uint32 offset;
00071         uint16 flags;
00072     };
00073 
00074     typedef HashMap<String, FileEntry, IgnoreCase_Hash, IgnoreCase_EqualTo> FileMap;
00075     FileMap _map;
00076     Common::SeekableReadStream *_stream;
00077     DisposeAfterUse::Flag _disposeAfterUse;
00078 };
00079 
00080 InstallShieldCabinet::~InstallShieldCabinet() {
00081     _map.clear();
00082 
00083     if (_disposeAfterUse == DisposeAfterUse::YES)
00084         delete _stream;
00085 }
00086 
00087 InstallShieldCabinet::InstallShieldCabinet(SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) : _stream(stream), _disposeAfterUse(disposeAfterUse) {
00088     // Note that we only support a limited subset of cabinet files
00089     // Only single cabinet files and ones without data shared between
00090     // cabinets.
00091 
00092     // Check for the magic uint32
00093     if (_stream->readUint32LE() != 0x28635349) {
00094         warning("InstallShieldCabinet::InstallShieldCabinet(): Magic ID doesn't match");
00095         return;
00096     }
00097 
00098     uint32 version = _stream->readUint32LE();
00099 
00100     if (version != 0x01000004) {
00101         warning("Unsupported CAB version %08x", version);
00102         return;
00103     }
00104 
00105     /* uint32 volumeInfo = */ _stream->readUint32LE();
00106     uint32 cabDescriptorOffset = _stream->readUint32LE();
00107     /* uint32 cabDescriptorSize = */ _stream->readUint32LE();
00108 
00109     _stream->seek(cabDescriptorOffset);
00110 
00111     _stream->skip(12);
00112     uint32 fileTableOffset = _stream->readUint32LE();
00113     _stream->skip(4);
00114     uint32 fileTableSize = _stream->readUint32LE();
00115     uint32 fileTableSize2 = _stream->readUint32LE();
00116     uint32 directoryCount = _stream->readUint32LE();
00117     _stream->skip(8);
00118     uint32 fileCount = _stream->readUint32LE();
00119 
00120     if (fileTableSize != fileTableSize2)
00121         warning("file table sizes do not match");
00122 
00123     // We're ignoring file groups and components since we
00124     // should not need them. Moving on to the files...
00125 
00126     _stream->seek(cabDescriptorOffset + fileTableOffset);
00127     uint32 fileTableCount = directoryCount + fileCount;
00128     uint32 *fileTableOffsets = new uint32[fileTableCount];
00129     for (uint32 i = 0; i < fileTableCount; i++)
00130         fileTableOffsets[i] = _stream->readUint32LE();
00131 
00132     for (uint32 i = directoryCount; i < fileCount + directoryCount; i++) {
00133         _stream->seek(cabDescriptorOffset + fileTableOffset + fileTableOffsets[i]);
00134         uint32 nameOffset = _stream->readUint32LE();
00135         /* uint32 directoryIndex = */ _stream->readUint32LE();
00136 
00137         // First read in data needed by us to get at the file data
00138         FileEntry entry;
00139         entry.flags = _stream->readUint16LE();
00140         entry.uncompressedSize = _stream->readUint32LE();
00141         entry.compressedSize = _stream->readUint32LE();
00142         _stream->skip(20);
00143         entry.offset = _stream->readUint32LE();
00144 
00145         // Then let's get the string
00146         _stream->seek(cabDescriptorOffset + fileTableOffset + nameOffset);
00147         String fileName;
00148 
00149         char c = _stream->readByte();
00150         while (c) {
00151             fileName += c;
00152             c = _stream->readByte();
00153         }
00154         _map[fileName] = entry;
00155     }
00156 
00157     delete[] fileTableOffsets;
00158 }
00159 
00160 bool InstallShieldCabinet::hasFile(const String &name) const {
00161     return _map.contains(name);
00162 }
00163 
00164 int InstallShieldCabinet::listMembers(ArchiveMemberList &list) const {
00165     for (FileMap::const_iterator it = _map.begin(); it != _map.end(); it++)
00166         list.push_back(getMember(it->_key));
00167 
00168     return _map.size();
00169 }
00170 
00171 const ArchiveMemberPtr InstallShieldCabinet::getMember(const String &name) const {
00172     return ArchiveMemberPtr(new GenericArchiveMember(name, this));
00173 }
00174 
00175 SeekableReadStream *InstallShieldCabinet::createReadStreamForMember(const String &name) const {
00176     if (!_map.contains(name))
00177         return nullptr;
00178 
00179     const FileEntry &entry = _map[name];
00180 
00181     _stream->seek(entry.offset);
00182 
00183     if (!(entry.flags & 0x04)) // Not compressed
00184         return _stream->readStream(entry.uncompressedSize);
00185 
00186 #ifdef USE_ZLIB
00187     byte *src = (byte *)malloc(entry.compressedSize);
00188     byte *dst = (byte *)malloc(entry.uncompressedSize);
00189 
00190     _stream->read(src, entry.compressedSize);
00191 
00192     bool result = inflateZlibInstallShield(dst, entry.uncompressedSize, src, entry.compressedSize);
00193     free(src);
00194 
00195     if (!result) {
00196         warning("failed to inflate CAB file '%s'", name.c_str());
00197         free(dst);
00198         return nullptr;
00199     }
00200 
00201     return new MemoryReadStream(dst, entry.uncompressedSize, DisposeAfterUse::YES);
00202 #else
00203     warning("zlib required to extract compressed CAB file '%s'", name.c_str());
00204     return 0;
00205 #endif
00206 }
00207 
00208 Archive *makeInstallShieldArchive(SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) {
00209     return new InstallShieldCabinet(stream, disposeAfterUse);
00210 }
00211 
00212 } // End of namespace AGOS


Generated on Sat Mar 16 2019 05:01:38 for ResidualVM by doxygen 1.7.1
curved edge   curved edge