mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-31 07:53:36 +00:00
COMMON: Move InstallShield code to common
The code also now works for both data compressed with sync bytes and without
This commit is contained in:
parent
f7e515a361
commit
ab45e72e67
@ -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();
|
||||
|
||||
if (_disposeAfterUse == DisposeAfterUse::YES)
|
||||
delete _stream;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
@ -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
|
@ -16,6 +16,7 @@ MODULE_OBJS := \
|
||||
gui_options.o \
|
||||
hashmap.o \
|
||||
iff_container.o \
|
||||
installshield_cab.o \
|
||||
language.o \
|
||||
localization.o \
|
||||
macresman.o \
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
||||
/**
|
||||
|
@ -51,7 +51,6 @@ ifdef ENABLE_AGOS2
|
||||
MODULE_OBJS += \
|
||||
animation.o \
|
||||
feeble.o \
|
||||
installshield_cab.o \
|
||||
oracle.o \
|
||||
script_dp.o \
|
||||
script_ff.o \
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user