diff options
Diffstat (limited to 'common/installshield_cab.cpp')
| -rw-r--r-- | common/installshield_cab.cpp | 212 | 
1 files changed, 212 insertions, 0 deletions
| diff --git a/common/installshield_cab.cpp b/common/installshield_cab.cpp new file mode 100644 index 0000000000..e25d14741a --- /dev/null +++ b/common/installshield_cab.cpp @@ -0,0 +1,212 @@ +/* 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 <twogood@users.sourceforge.net> +// +// 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 "common/archive.h" +#include "common/debug.h" +#include "common/hash-str.h" +#include "common/installshield_cab.h" +#include "common/memstream.h" +#include "common/zlib.h" + +namespace Common { + +class InstallShieldCabinet : public Archive { +public: +	InstallShieldCabinet(SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse); +	~InstallShieldCabinet(); + +	// Archive API implementation +	bool hasFile(const String &name) const; +	int listMembers(ArchiveMemberList &list) const; +	const ArchiveMemberPtr getMember(const String &name) const; +	SeekableReadStream *createReadStreamForMember(const String &name) const; + +private: +	struct FileEntry { +		uint32 uncompressedSize; +		uint32 compressedSize; +		uint32 offset; +		uint16 flags; +	}; + +	typedef HashMap<String, FileEntry, IgnoreCase_Hash, IgnoreCase_EqualTo> FileMap; +	FileMap _map; +	Common::SeekableReadStream *_stream; +	DisposeAfterUse::Flag _disposeAfterUse; +}; + +InstallShieldCabinet::~InstallShieldCabinet() { +	_map.clear(); + +	if (_disposeAfterUse == DisposeAfterUse::YES) +		delete _stream; +} + +InstallShieldCabinet::InstallShieldCabinet(SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) : _stream(stream), _disposeAfterUse(disposeAfterUse) { +	// 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) { +		warning("InstallShieldCabinet::InstallShieldCabinet(): Magic ID doesn't match"); +		return; +	} + +	uint32 version = _stream->readUint32LE(); + +	if (version != 0x01000004) { +		warning("Unsupported CAB version %08x", version); +		return; +	} + +	/* 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); +		String fileName; + +		char c = _stream->readByte(); +		while (c) { +			fileName += c; +			c = _stream->readByte(); +		} +		_map[fileName] = entry; +	} + +	delete[] fileTableOffsets; +} + +bool InstallShieldCabinet::hasFile(const String &name) const { +	return _map.contains(name); +} + +int InstallShieldCabinet::listMembers(ArchiveMemberList &list) const { +	for (FileMap::const_iterator it = _map.begin(); it != _map.end(); it++) +		list.push_back(getMember(it->_key)); + +	return _map.size(); +} + +const ArchiveMemberPtr InstallShieldCabinet::getMember(const String &name) const { +	return ArchiveMemberPtr(new GenericArchiveMember(name, this)); +} + +SeekableReadStream *InstallShieldCabinet::createReadStreamForMember(const String &name) const { +	if (!_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 = inflateZlibInstallShield(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 MemoryReadStream(dst, entry.uncompressedSize, DisposeAfterUse::YES); +#else +	warning("zlib required to extract compressed CAB file '%s'", name.c_str()); +	return 0; +#endif +} + +Archive *makeInstallShieldArchive(SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) { +	return new InstallShieldCabinet(stream, disposeAfterUse); +} + +} // End of namespace AGOS | 
