diff options
| -rw-r--r-- | common/installshield_cab.cpp (renamed from engines/agos/installshield_cab.cpp) | 126 | ||||
| -rw-r--r-- | common/installshield_cab.h (renamed from engines/agos/installshield_cab.h) | 18 | ||||
| -rw-r--r-- | common/module.mk | 1 | ||||
| -rw-r--r-- | common/zlib.cpp | 54 | ||||
| -rw-r--r-- | common/zlib.h | 19 | ||||
| -rw-r--r-- | engines/agos/module.mk | 1 | ||||
| -rw-r--r-- | engines/agos/res.cpp | 8 | 
7 files changed, 149 insertions, 78 deletions
| diff --git a/engines/agos/installshield_cab.cpp b/common/installshield_cab.cpp index d4e636f7b3..e25d14741a 100644 --- a/engines/agos/installshield_cab.cpp +++ b/common/installshield_cab.cpp @@ -43,27 +43,25 @@  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE  // SOFTWARE. -#include "agos/installshield_cab.h" - +#include "common/archive.h"  #include "common/debug.h" -#include "common/file.h" +#include "common/hash-str.h" +#include "common/installshield_cab.h"  #include "common/memstream.h"  #include "common/zlib.h" -namespace AGOS { - -class InstallShieldCabinet : public Common::Archive { -	Common::String _installShieldFilename; +namespace Common { +class InstallShieldCabinet : public Archive {  public: -	InstallShieldCabinet(const Common::String &filename); +	InstallShieldCabinet(SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse);  	~InstallShieldCabinet(); -	// Common::Archive API implementation -	bool hasFile(const Common::String &name) const; -	int listMembers(Common::ArchiveMemberList &list) const; -	const Common::ArchiveMemberPtr getMember(const Common::String &name) const; -	Common::SeekableReadStream *createReadStreamForMember(const Common::String &name) const; +	// 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 { @@ -73,53 +71,51 @@ private:  		uint16 flags;  	}; -	typedef Common::HashMap<Common::String, FileEntry, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> FileMap; +	typedef HashMap<String, FileEntry, IgnoreCase_Hash, IgnoreCase_EqualTo> FileMap;  	FileMap _map; +	Common::SeekableReadStream *_stream; +	DisposeAfterUse::Flag _disposeAfterUse;  };  InstallShieldCabinet::~InstallShieldCabinet() {  	_map.clear(); -} - -InstallShieldCabinet::InstallShieldCabinet(const Common::String &filename) : _installShieldFilename(filename) { -	Common::File installShieldFile; -	if (!installShieldFile.open(_installShieldFilename)) { -		warning("InstallShieldCabinet::InstallShieldCabinet(): Could not find the archive file %s", _installShieldFilename.c_str()); -		return; -	} +	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 (installShieldFile.readUint32LE() != 0x28635349) { +	if (_stream->readUint32LE() != 0x28635349) {  		warning("InstallShieldCabinet::InstallShieldCabinet(): Magic ID doesn't match");  		return;  	} -	uint32 version = installShieldFile.readUint32LE(); +	uint32 version = _stream->readUint32LE();  	if (version != 0x01000004) {  		warning("Unsupported CAB version %08x", version);  		return;  	} -	/* uint32 volumeInfo = */ installShieldFile.readUint32LE(); -	uint32 cabDescriptorOffset = installShieldFile.readUint32LE(); -	/* uint32 cabDescriptorSize = */ installShieldFile.readUint32LE(); +	/* uint32 volumeInfo = */ _stream->readUint32LE(); +	uint32 cabDescriptorOffset = _stream->readUint32LE(); +	/* uint32 cabDescriptorSize = */ _stream->readUint32LE(); -	installShieldFile.seek(cabDescriptorOffset); +	_stream->seek(cabDescriptorOffset); -	installShieldFile.skip(12); -	uint32 fileTableOffset = installShieldFile.readUint32LE(); -	installShieldFile.skip(4); -	uint32 fileTableSize = installShieldFile.readUint32LE(); -	uint32 fileTableSize2 = installShieldFile.readUint32LE(); -	uint32 directoryCount = installShieldFile.readUint32LE(); -	installShieldFile.skip(8); -	uint32 fileCount = installShieldFile.readUint32LE(); +	_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"); @@ -127,33 +123,33 @@ InstallShieldCabinet::InstallShieldCabinet(const Common::String &filename) : _in  	// We're ignoring file groups and components since we  	// should not need them. Moving on to the files... -	installShieldFile.seek(cabDescriptorOffset + fileTableOffset); +	_stream->seek(cabDescriptorOffset + fileTableOffset);  	uint32 fileTableCount = directoryCount + fileCount;  	uint32 *fileTableOffsets = new uint32[fileTableCount];  	for (uint32 i = 0; i < fileTableCount; i++) -		fileTableOffsets[i] = installShieldFile.readUint32LE(); +		fileTableOffsets[i] = _stream->readUint32LE();  	for (uint32 i = directoryCount; i < fileCount + directoryCount; i++) { -		installShieldFile.seek(cabDescriptorOffset + fileTableOffset + fileTableOffsets[i]); -		uint32 nameOffset = installShieldFile.readUint32LE(); -		/* uint32 directoryIndex = */ installShieldFile.readUint32LE(); +		_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 = installShieldFile.readUint16LE(); -		entry.uncompressedSize = installShieldFile.readUint32LE(); -		entry.compressedSize = installShieldFile.readUint32LE(); -		installShieldFile.skip(20); -		entry.offset = installShieldFile.readUint32LE(); +		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 -		installShieldFile.seek(cabDescriptorOffset + fileTableOffset + nameOffset); -		Common::String fileName; +		_stream->seek(cabDescriptorOffset + fileTableOffset + nameOffset); +		String fileName; -		char c = installShieldFile.readByte(); +		char c = _stream->readByte();  		while (c) {  			fileName += c; -			c = installShieldFile.readByte(); +			c = _stream->readByte();  		}  		_map[fileName] = entry;  	} @@ -161,43 +157,39 @@ InstallShieldCabinet::InstallShieldCabinet(const Common::String &filename) : _in  	delete[] fileTableOffsets;  } -bool InstallShieldCabinet::hasFile(const Common::String &name) const { +bool InstallShieldCabinet::hasFile(const String &name) const {  	return _map.contains(name);  } -int InstallShieldCabinet::listMembers(Common::ArchiveMemberList &list) const { +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 Common::ArchiveMemberPtr InstallShieldCabinet::getMember(const Common::String &name) const { -	return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(name, this)); +const ArchiveMemberPtr InstallShieldCabinet::getMember(const String &name) const { +	return ArchiveMemberPtr(new GenericArchiveMember(name, this));  } -Common::SeekableReadStream *InstallShieldCabinet::createReadStreamForMember(const Common::String &name) const { +SeekableReadStream *InstallShieldCabinet::createReadStreamForMember(const String &name) const {  	if (!_map.contains(name))  		return 0;  	const FileEntry &entry = _map[name]; -	Common::File archiveFile; -	archiveFile.open(_installShieldFilename); -	archiveFile.seek(entry.offset); +	_stream->seek(entry.offset); -	if (!(entry.flags & 0x04)) { -		// Not compressed -		return archiveFile.readStream(entry.uncompressedSize); -	} +	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); -	archiveFile.read(src, entry.compressedSize); +	_stream->read(src, entry.compressedSize); -	bool result = Common::inflateZlibHeaderless(dst, entry.uncompressedSize, src, entry.compressedSize); +	bool result = inflateZlibInstallShield(dst, entry.uncompressedSize, src, entry.compressedSize);  	free(src);  	if (!result) { @@ -206,15 +198,15 @@ Common::SeekableReadStream *InstallShieldCabinet::createReadStreamForMember(cons  		return 0;  	} -	return new Common::MemoryReadStream(dst, entry.uncompressedSize, DisposeAfterUse::YES); +	return new MemoryReadStream(dst, entry.uncompressedSize, DisposeAfterUse::YES);  #else  	warning("zlib required to extract compressed CAB file '%s'", name.c_str());  	return 0;  #endif  } -Common::Archive *makeInstallShieldArchive(const Common::String &name) { -	return new InstallShieldCabinet(name); +Archive *makeInstallShieldArchive(SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) { +	return new InstallShieldCabinet(stream, disposeAfterUse);  }  } // End of namespace AGOS diff --git a/engines/agos/installshield_cab.h b/common/installshield_cab.h index f7e8bed277..7c4f294578 100644 --- a/engines/agos/installshield_cab.h +++ b/common/installshield_cab.h @@ -20,22 +20,24 @@   *   */ -#include "common/archive.h" -#include "common/str.h" +#ifndef COMMON_INSTALLSHIELD_CAB_H +#define COMMON_INSTALLSHIELD_CAB_H -#ifndef AGOS_INSTALLSHIELD_CAB_H -#define AGOS_INSTALLSHIELD_CAB_H +#include "common/types.h" -namespace AGOS { +namespace Common { + +class Archive; +class SeekableReadStream;  /**   * This factory method creates an Archive instance corresponding to the content - * of the InstallShield compressed file with the given name. + * of the InstallShield compressed stream.   *   * May return 0 in case of a failure.   */ -Common::Archive *makeInstallShieldArchive(const Common::String &name); +Archive *makeInstallShieldArchive(SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES); -} // End of namespace AGOS +} // End of namespace Common  #endif diff --git a/common/module.mk b/common/module.mk index 92279740e5..d96b11ee40 100644 --- a/common/module.mk +++ b/common/module.mk @@ -16,6 +16,7 @@ MODULE_OBJS := \  	gui_options.o \  	hashmap.o \  	iff_container.o \ +	installshield_cab.o \  	language.o \  	localization.o \  	macresman.o \ diff --git a/common/zlib.cpp b/common/zlib.cpp index 7d765fc539..98f319f351 100644 --- a/common/zlib.cpp +++ b/common/zlib.cpp @@ -85,6 +85,60 @@ bool inflateZlibHeaderless(byte *dst, uint dstLen, const byte *src, uint srcLen,  	return true;  } +enum { +	kTempBufSize = 65536 +}; + +bool inflateZlibInstallShield(byte *dst, uint dstLen, const byte *src, uint srcLen) { +	if (!dst || !dstLen || !src || !srcLen) +		return false; + +	// See if we have sync bytes. If so, just use our function for that. +	if (srcLen >= 4 && READ_BE_UINT32(src + srcLen - 4) == 0xFFFF) +		return inflateZlibHeaderless(dst, dstLen, src, srcLen); + +	// Otherwise, we have some custom code we get to use here. + +	byte *temp = (byte *)malloc(kTempBufSize); + +	uint32 bytesRead = 0, bytesProcessed = 0; +	while (bytesRead < srcLen) { +		uint16 chunkSize = READ_LE_UINT16(src + bytesRead); +		bytesRead += 2; + +		// Initialize zlib +		z_stream stream; +		stream.next_in = const_cast<byte *>(src + bytesRead); +		stream.avail_in = chunkSize; +		stream.next_out = temp; +		stream.avail_out = kTempBufSize; +		stream.zalloc = Z_NULL; +		stream.zfree = Z_NULL; +		stream.opaque = Z_NULL; + +		// Negative MAX_WBITS tells zlib there's no zlib header +		int err = inflateInit2(&stream, -MAX_WBITS); +		if (err != Z_OK) +			return false; + +		err = inflate(&stream, Z_FINISH); +		if (err != Z_OK && err != Z_STREAM_END) { +			inflateEnd(&stream); +			free(temp); +			return false; +		} + +		memcpy(dst + bytesProcessed, temp, stream.total_out); +		bytesProcessed += stream.total_out; + +		inflateEnd(&stream); +		bytesRead += chunkSize; +	} + +	free(temp); +	return true; +} +  /**   * A simple wrapper class which can be used to wrap around an arbitrary   * other SeekableReadStream and will then provide on-the-fly decompression support. diff --git a/common/zlib.h b/common/zlib.h index 61322c286a..8cfc5829ac 100644 --- a/common/zlib.h +++ b/common/zlib.h @@ -77,6 +77,25 @@ bool uncompress(byte *dst, unsigned long *dstLen, const byte *src, unsigned long   */  bool inflateZlibHeaderless(byte *dst, uint dstLen, const byte *src, uint srcLen, const byte *dict = 0, uint dictLen = 0); +/** + * Wrapper around zlib's inflate functions. This function will call the + * necessary inflate functions to uncompress data compressed for InstallShield + * cabinet files. + * + * Decompresses the src buffer into the dst buffer. + * srcLen is the byte length of the source buffer, dstLen is the byte + * length of the output buffer. + * It decompress as much data as possible, up to dstLen bytes. + * + * @param dst       the buffer to store into. + * @param dstLen    the size of the destination buffer. + * @param src       the data to be decompressed. + * @param dstLen    the size of the compressed data. + * + * @return true on success (Z_OK or Z_STREAM_END), false otherwise. + */ +bool inflateZlibInstallShield(byte *dst, uint dstLen, const byte *src, uint srcLen); +  #endif  /** diff --git a/engines/agos/module.mk b/engines/agos/module.mk index 7ae5e17bf2..7069d8005b 100644 --- a/engines/agos/module.mk +++ b/engines/agos/module.mk @@ -51,7 +51,6 @@ ifdef ENABLE_AGOS2  MODULE_OBJS += \  	animation.o \  	feeble.o \ -	installshield_cab.o \  	oracle.o \  	script_dp.o \  	script_ff.o \ diff --git a/engines/agos/res.cpp b/engines/agos/res.cpp index 0305879390..2e44a6575c 100644 --- a/engines/agos/res.cpp +++ b/engines/agos/res.cpp @@ -23,6 +23,8 @@  // Resource file routines for Simon1/Simon2 +#include "common/archive.h" +#include "common/installshield_cab.h"  #include "common/file.h"  #include "common/memstream.h"  #include "common/textconsole.h" @@ -31,7 +33,6 @@  #include "agos/agos.h"  #include "agos/intern.h"  #include "agos/sound.h" -#include "agos/installshield_cab.h"  #include "common/zlib.h" @@ -43,7 +44,10 @@ ArchiveMan::ArchiveMan() {  #ifdef ENABLE_AGOS2  void ArchiveMan::registerArchive(const Common::String &filename, int priority) { -	add(filename, makeInstallShieldArchive(filename), priority); +	Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(filename); + +	if (stream) +		add(filename, makeInstallShieldArchive(stream, DisposeAfterUse::YES), priority);  }  #endif | 
