From f91f0f275660cfa3362036f583619b4ef7d343fd Mon Sep 17 00:00:00 2001 From: Matthew Hoops Date: Sat, 20 Aug 2011 16:42:09 -0400 Subject: AGOS: Add an archive class for handling InstallShield Cabinets --- engines/agos/installshield_cab.cpp | 201 +++++++++++++++++++++++++++++++++++++ engines/agos/installshield_cab.h | 67 +++++++++++++ engines/agos/module.mk | 1 + 3 files changed, 269 insertions(+) create mode 100644 engines/agos/installshield_cab.cpp create mode 100644 engines/agos/installshield_cab.h (limited to 'engines/agos') diff --git a/engines/agos/installshield_cab.cpp b/engines/agos/installshield_cab.cpp new file mode 100644 index 0000000000..a8b5d0fba2 --- /dev/null +++ b/engines/agos/installshield_cab.cpp @@ -0,0 +1,201 @@ +/* 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. + * + */ + +// The following code is based on unshield +// Original copyright: + +// Copyright (c) 2003 David Eriksson +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include "agos/installshield_cab.h" + +#include "common/debug.h" +#include "common/memstream.h" +#include "common/zlib.h" + +namespace AGOS { + +InstallShieldCabinet::InstallShieldCabinet() : Common::Archive() { + _stream = 0; +} + +InstallShieldCabinet::~InstallShieldCabinet() { + close(); +} + +bool InstallShieldCabinet::open(const Common::String &filename) { + close(); + + _stream = SearchMan.createReadStreamForMember(filename); + + if (!_stream) + return false; + + // Note that we only support a limited subset of cabinet files + // Only single cabinet files and ones without data shared between + // cabinets. + + // Check for the magic uint32 + if (_stream->readUint32LE() != 0x28635349) { + close(); + return false; + } + + uint32 version = _stream->readUint32LE(); + + if (version != 0x01000004) { + warning("Unsupported CAB version %08x", version); + close(); + return false; + } + + /* uint32 volumeInfo = */ _stream->readUint32LE(); + uint32 cabDescriptorOffset = _stream->readUint32LE(); + /* uint32 cabDescriptorSize = */ _stream->readUint32LE(); + + _stream->seek(cabDescriptorOffset); + + _stream->skip(12); + uint32 fileTableOffset = _stream->readUint32LE(); + _stream->skip(4); + uint32 fileTableSize = _stream->readUint32LE(); + uint32 fileTableSize2 = _stream->readUint32LE(); + uint32 directoryCount = _stream->readUint32LE(); + _stream->skip(8); + uint32 fileCount = _stream->readUint32LE(); + + if (fileTableSize != fileTableSize2) + warning("file table sizes do not match"); + + // We're ignoring file groups and components since we + // should not need them. Moving on to the files... + + _stream->seek(cabDescriptorOffset + fileTableOffset); + uint32 fileTableCount = directoryCount + fileCount; + uint32 *fileTableOffsets = new uint32[fileTableCount]; + for (uint32 i = 0; i < fileTableCount; i++) + fileTableOffsets[i] = _stream->readUint32LE(); + + for (uint32 i = directoryCount; i < fileCount + directoryCount; i++) { + _stream->seek(cabDescriptorOffset + fileTableOffset + fileTableOffsets[i]); + uint32 nameOffset = _stream->readUint32LE(); + /* uint32 directoryIndex = */ _stream->readUint32LE(); + + // First read in data needed by us to get at the file data + FileEntry entry; + entry.flags = _stream->readUint16LE(); + entry.uncompressedSize = _stream->readUint32LE(); + entry.compressedSize = _stream->readUint32LE(); + _stream->skip(20); + entry.offset = _stream->readUint32LE(); + + // Then let's get the string + _stream->seek(cabDescriptorOffset + fileTableOffset + nameOffset); + Common::String fileName; + + char c = _stream->readByte(); + while (c) { + fileName += c; + c = _stream->readByte(); + } + + _map[fileName] = entry; + } + + delete[] fileTableOffsets; + + return true; +} + +void InstallShieldCabinet::close() { + delete _stream; _stream = 0; + _map.clear(); +} + +bool InstallShieldCabinet::hasFile(const Common::String &name) { + return _map.contains(name); +} + +int InstallShieldCabinet::listMembers(Common::ArchiveMemberList &list) { + for (FileMap::const_iterator it = _map.begin(); it != _map.end(); it++) + list.push_back(getMember(it->_key)); + + return _map.size(); +} + +Common::ArchiveMemberPtr InstallShieldCabinet::getMember(const Common::String &name) { + return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(name, this)); +} + +Common::SeekableReadStream *InstallShieldCabinet::createReadStreamForMember(const Common::String &name) const { + if (!_stream || !_map.contains(name)) + return 0; + + const FileEntry &entry = _map[name]; + + _stream->seek(entry.offset); + + if (!(entry.flags & 0x04)) { + // Not compressed + return _stream->readStream(entry.uncompressedSize); + } + +#ifdef USE_ZLIB + byte *src = (byte *)malloc(entry.compressedSize); + byte *dst = (byte *)malloc(entry.uncompressedSize); + + _stream->read(src, entry.compressedSize); + + bool result = Common::inflateZlibHeaderless(dst, entry.uncompressedSize, src, entry.compressedSize); + free(src); + + if (!result) { + warning("failed to inflate CAB file '%s'", name.c_str()); + free(dst); + return 0; + } + + return new Common::MemoryReadStream(dst, entry.uncompressedSize, DisposeAfterUse::YES); +#else + warning("zlib required to extract compressed CAB file '%s'", name.c_str()); + return 0; +#endif +} + +} // End of namespace AGOS diff --git a/engines/agos/installshield_cab.h b/engines/agos/installshield_cab.h new file mode 100644 index 0000000000..3fb8e66b03 --- /dev/null +++ b/engines/agos/installshield_cab.h @@ -0,0 +1,67 @@ +/* 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/archive.h" +#include "common/scummsys.h" +#include "common/endian.h" +#include "common/file.h" +#include "common/hash-str.h" +#include "common/hashmap.h" +#include "common/str.h" + +#ifndef AGOS_INSTALLSHIELD_CAB_H +#define AGOS_INSTALLSHIELD_CAB_H + +namespace AGOS { + +class InstallShieldCabinet : public Common::Archive { +public: + InstallShieldCabinet(); + ~InstallShieldCabinet(); + + bool open(const Common::String &filename); + void close(); + bool isOpen() const { return _stream != 0; } + + // Common::Archive API implementation + bool hasFile(const Common::String &name); + int listMembers(Common::ArchiveMemberList &list); + Common::ArchiveMemberPtr getMember(const Common::String &name); + Common::SeekableReadStream *createReadStreamForMember(const Common::String &name) const; + +private: + struct FileEntry { + uint32 uncompressedSize; + uint32 compressedSize; + uint32 offset; + uint16 flags; + }; + + Common::SeekableReadStream *_stream; + + typedef Common::HashMap FileMap; + FileMap _map; +}; + +} // End of namespace AGOS + +#endif diff --git a/engines/agos/module.mk b/engines/agos/module.mk index 7069d8005b..7ae5e17bf2 100644 --- a/engines/agos/module.mk +++ b/engines/agos/module.mk @@ -51,6 +51,7 @@ ifdef ENABLE_AGOS2 MODULE_OBJS += \ animation.o \ feeble.o \ + installshield_cab.o \ oracle.o \ script_dp.o \ script_ff.o \ -- cgit v1.2.3