mirror of
https://github.com/libretro/scummvm.git
synced 2025-02-04 09:56:30 +00:00
GRIM: Add support for patches directly from the update executable
This commit is contained in:
parent
1ed60ed385
commit
0f969be811
@ -58,6 +58,10 @@ MODULE_OBJS := \
|
||||
emisound/vimatrack.o \
|
||||
emisound/track.o \
|
||||
emisound/emisound.o \
|
||||
update/packfile.o \
|
||||
update/mscab.o \
|
||||
update/lang_filter.o \
|
||||
update/update.o \
|
||||
actor.o \
|
||||
animation.o \
|
||||
bitmap.o \
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include "engines/grim/inputdialog.h"
|
||||
#include "engines/grim/debug.h"
|
||||
#include "engines/grim/patchr.h"
|
||||
#include "engines/grim/update/update.h"
|
||||
#include "common/algorithm.h"
|
||||
#include "common/zlib.h"
|
||||
#include "gui/message.h"
|
||||
@ -65,7 +66,7 @@ ResourceLoader::ResourceLoader() {
|
||||
_cacheMemorySize = 0;
|
||||
|
||||
Lab *l;
|
||||
Common::ArchiveMemberList files;
|
||||
Common::ArchiveMemberList files, updFiles;
|
||||
|
||||
if (g_grim->getGameType() == GType_GRIM) {
|
||||
if (g_grim->getGameFlags() & ADGF_DEMO) {
|
||||
@ -74,6 +75,15 @@ ResourceLoader::ResourceLoader() {
|
||||
SearchMan.listMatchingMembers(files, "sound001.lab");
|
||||
SearchMan.listMatchingMembers(files, "voice001.lab");
|
||||
} else {
|
||||
//Load the update from the executable
|
||||
Common::File *updStream = new Common::File();
|
||||
if (updStream && updStream->open("gfupd101.exe")) {
|
||||
Common::Archive *update = loadUpdateArchive(updStream);
|
||||
if (update)
|
||||
SearchMan.add("update", update, 1);
|
||||
} else
|
||||
delete updStream;
|
||||
|
||||
if (!SearchMan.hasFile("residualvm-grim-patch.lab"))
|
||||
error("residualvm-grim-patch.lab not found");
|
||||
|
||||
@ -107,6 +117,22 @@ ResourceLoader::ResourceLoader() {
|
||||
SearchMan.listMatchingMembers(files, "tile.lab");
|
||||
SearchMan.listMatchingMembers(files, "voice.lab");
|
||||
} else {
|
||||
if (g_grim->getGamePlatform() == Common::kPlatformWindows) {
|
||||
//Load the update from the executable
|
||||
SearchMan.listMatchingMembers(updFiles, "MonkeyUpdate.exe");
|
||||
SearchMan.listMatchingMembers(updFiles, "MonkeyUpdate_???.exe");
|
||||
for (Common::ArchiveMemberList::const_iterator x = updFiles.begin(); x != updFiles.end(); ++x) {
|
||||
Common::SeekableReadStream *updStream;
|
||||
updStream = (*x)->createReadStream();
|
||||
|
||||
Common::Archive *update = loadUpdateArchive(updStream);
|
||||
if (update)
|
||||
SearchMan.add("update", update, 1);
|
||||
}
|
||||
}
|
||||
|
||||
SearchMan.listMatchingMembers(files, "local.m4b");
|
||||
SearchMan.listMatchingMembers(files, "i9n.m4b");
|
||||
SearchMan.listMatchingMembers(files, "art???.m4b");
|
||||
SearchMan.listMatchingMembers(files, "lip.m4b");
|
||||
SearchMan.listMatchingMembers(files, "local.m4b");
|
||||
@ -118,11 +144,16 @@ ResourceLoader::ResourceLoader() {
|
||||
if (files.empty())
|
||||
error("Cannot find game data - check configuration file");
|
||||
|
||||
//load labs
|
||||
int priority = files.size();
|
||||
for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) {
|
||||
Common::String filename = (*x)->getName();
|
||||
filename.toLowercase();
|
||||
|
||||
//Avoid duplicates
|
||||
if (_files.hasArchive(filename))
|
||||
continue;
|
||||
|
||||
l = new Lab();
|
||||
if (l->open(filename))
|
||||
_files.add(filename, l, priority--, true);
|
||||
|
140
engines/grim/update/lang_filter.cpp
Normal file
140
engines/grim/update/lang_filter.cpp
Normal file
@ -0,0 +1,140 @@
|
||||
/* ResidualVM - A 3D game interpreter
|
||||
*
|
||||
* ResidualVM 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 library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include "engines/grim/update/lang_filter.h"
|
||||
|
||||
#include "common/file.h"
|
||||
#include "common/archive.h"
|
||||
#include "common/str.h"
|
||||
|
||||
namespace Grim {
|
||||
|
||||
const char *LangFilter::kLanguages1[7] = { "@@_", "US_", "FR_", "GE_", "IT_", "PT_", "SP_" };
|
||||
const char *LangFilter::kLanguages2[7] = { "Common/", "Eng/", "Fra/", "Deu/", "Ita/", "Brz/", "Esp/" };
|
||||
|
||||
LangFilter::LangFilter(Common::Archive *arc, Common::Language lang) : _arc(arc) {
|
||||
if (!_arc)
|
||||
return;
|
||||
|
||||
switch (lang) {
|
||||
case Common::EN_ANY:
|
||||
case Common::EN_GRB:
|
||||
case Common::EN_USA:
|
||||
_lang = kEnglish;
|
||||
break;
|
||||
case Common::FR_FRA:
|
||||
_lang = kFrench;
|
||||
break;
|
||||
case Common::DE_DEU:
|
||||
_lang = kGerman;
|
||||
break;
|
||||
case Common::IT_ITA:
|
||||
_lang = kItalian;
|
||||
break;
|
||||
case Common::PT_BRA:
|
||||
_lang = kPortuguese;
|
||||
break;
|
||||
case Common::ES_ESP:
|
||||
_lang = kSpanish;
|
||||
break;
|
||||
default:
|
||||
_lang = kCommon;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LangFilter::~LangFilter() {
|
||||
if (_arc)
|
||||
delete _arc;
|
||||
}
|
||||
|
||||
bool LangFilter::hasFile(const Common::String &name) const {
|
||||
if (!_arc)
|
||||
return false;
|
||||
|
||||
return (_arc->hasFile(kLanguages1[_lang] + name)) ||
|
||||
(_arc->hasFile(kLanguages1[kCommon] + name)) ||
|
||||
(_arc->hasFile(kLanguages2[_lang] + name)) ||
|
||||
(_arc->hasFile(kLanguages2[kCommon] + name));
|
||||
}
|
||||
|
||||
int LangFilter::listMembers(Common::ArchiveMemberList &list) const {
|
||||
if (!_arc)
|
||||
return false;
|
||||
|
||||
Common::ArchiveMemberList orgList;
|
||||
Common::String orgName, name;
|
||||
|
||||
_arc->listMembers(orgList);
|
||||
|
||||
int num = 0;
|
||||
//Search only files with the right language and create a list with their basenames
|
||||
for (Common::ArchiveMemberList::const_iterator it = orgList.begin(); it != orgList.end(); it++) {
|
||||
orgName = (*it)->getName();
|
||||
if (orgName.hasPrefix(kLanguages1[_lang]) ||orgName.hasPrefix(kLanguages1[kCommon]))
|
||||
name = Common::String(orgName.c_str() + 3);
|
||||
else if (orgName.hasPrefix(kLanguages2[_lang]) || orgName.hasPrefix(kLanguages2[kCommon])) {
|
||||
int i = 0;
|
||||
while (orgName[i++] != '/');
|
||||
name = Common::String(orgName.c_str() + i);
|
||||
|
||||
//If the file is a subfolder, reject it
|
||||
if (name.contains('/'))
|
||||
continue;
|
||||
} else
|
||||
continue;
|
||||
name.toLowercase();
|
||||
list.push_back(getMember(name));
|
||||
++num;
|
||||
}
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
const Common::ArchiveMemberPtr LangFilter::getMember(const Common::String &name) const {
|
||||
return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(name, this));
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *LangFilter::createReadStreamForMember(const Common::String &name) const {
|
||||
if (!_arc)
|
||||
return 0;
|
||||
|
||||
//Search the right file
|
||||
Common::String fullName;
|
||||
Common::List<Common::String> namesToTry;
|
||||
namesToTry.push_front(kLanguages1[_lang] + name);
|
||||
namesToTry.push_front(kLanguages1[kCommon] + name);
|
||||
namesToTry.push_front(kLanguages2[_lang] + name);
|
||||
namesToTry.push_front(kLanguages2[kCommon] + name);
|
||||
for (Common::List<Common::String>::const_iterator it = namesToTry.begin(); it != namesToTry.end(); it++)
|
||||
if (_arc->hasFile(*it)) {
|
||||
fullName = *it;
|
||||
break;
|
||||
}
|
||||
|
||||
if (fullName.empty())
|
||||
return 0;
|
||||
|
||||
return _arc->createReadStreamForMember(fullName);
|
||||
}
|
||||
|
||||
} // End of namespace Grim
|
60
engines/grim/update/lang_filter.h
Normal file
60
engines/grim/update/lang_filter.h
Normal file
@ -0,0 +1,60 @@
|
||||
/* ResidualVM - A 3D game interpreter
|
||||
*
|
||||
* ResidualVM 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 library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LANGFILTER_H
|
||||
#define LANGFILTER_H
|
||||
|
||||
#include "common/archive.h"
|
||||
#include "common/util.h"
|
||||
|
||||
namespace Grim {
|
||||
|
||||
class LangFilter : public Common::Archive {
|
||||
public:
|
||||
LangFilter(Common::Archive *arc, Common::Language lang);
|
||||
~LangFilter();
|
||||
|
||||
// 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;
|
||||
private:
|
||||
Common::Archive *_arc;
|
||||
|
||||
enum kLang {
|
||||
kCommon = 0,
|
||||
kEnglish,
|
||||
kFrench,
|
||||
kGerman,
|
||||
kItalian,
|
||||
kPortuguese,
|
||||
kSpanish
|
||||
};
|
||||
kLang _lang;
|
||||
static const char *kLanguages1[7];
|
||||
static const char *kLanguages2[7];
|
||||
};
|
||||
|
||||
} // end of namespace Grim
|
||||
|
||||
#endif
|
349
engines/grim/update/mscab.cpp
Normal file
349
engines/grim/update/mscab.cpp
Normal file
@ -0,0 +1,349 @@
|
||||
/* ResidualVM - A 3D game interpreter
|
||||
*
|
||||
* ResidualVM 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 library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*
|
||||
*/
|
||||
|
||||
/* Portions of this file are part of libmspack.
|
||||
* (C) 2003-2004 Stuart Caie.
|
||||
*
|
||||
* This source code is adapted and stripped for ResidualVM project.
|
||||
*
|
||||
* libmspack is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
|
||||
*
|
||||
* For further details, see the file COPYING.LIB distributed with libmspack
|
||||
*/
|
||||
|
||||
#include "engines/grim/update/mscab.h"
|
||||
|
||||
#include "common/file.h"
|
||||
#include "common/archive.h"
|
||||
#include "common/memstream.h"
|
||||
#include "common/zlib.h"
|
||||
#include "common/str.h"
|
||||
|
||||
namespace Grim {
|
||||
|
||||
MsCabinet::~MsCabinet() {
|
||||
for (CacheMap::iterator it = _cache.begin(); it != _cache.end(); it++)
|
||||
delete[] it->_value;
|
||||
|
||||
_folderMap.clear();
|
||||
_fileMap.clear();
|
||||
|
||||
if (_data)
|
||||
delete _data;
|
||||
|
||||
if (_decompressor)
|
||||
delete _decompressor;
|
||||
}
|
||||
|
||||
MsCabinet::MsCabinet(Common::SeekableReadStream* data) :
|
||||
_data(data), _decompressor(0) {
|
||||
if (!_data)
|
||||
return;
|
||||
|
||||
//CFHEADER PARSING
|
||||
|
||||
// Verify Head-signature
|
||||
uint32 tag = _data->readUint32BE();
|
||||
if (tag != MKTAG('M','S','C','F'))
|
||||
return;
|
||||
|
||||
/* uint32 reserved1 = */ _data->readUint32LE();
|
||||
uint32 length = _data->readUint32LE();
|
||||
if (length > uint32(_data->size()))
|
||||
return;
|
||||
|
||||
/* uint32 reserved2 = */ _data->readUint32LE();
|
||||
uint32 filesOffset = _data->readUint32LE();
|
||||
/* uint32 reserved3 = */ _data->readUint32LE();
|
||||
|
||||
byte versionMinor = _data->readByte();
|
||||
byte versionMajor = _data->readByte();
|
||||
if (versionMajor != 1 || versionMinor != 3)
|
||||
return;
|
||||
|
||||
uint16 numFolders = _data->readUint16LE();
|
||||
uint16 numFiles = _data->readUint16LE();
|
||||
if (numFolders == 0 || numFiles == 0)
|
||||
return;
|
||||
|
||||
//This implementation doesn't support multicabinet and reserved fields
|
||||
uint16 flags = _data->readUint16LE();
|
||||
if (flags != 0)
|
||||
return;
|
||||
|
||||
/* uint16 setId = */ _data->readUint16LE();
|
||||
/* uint16 iCab = */ _data->readUint16LE();
|
||||
|
||||
if (_data->err())
|
||||
return;
|
||||
|
||||
//CFFOLDERs PARSING
|
||||
for (uint16 i = 0; i < numFolders; ++i) {
|
||||
FolderEntry fEntry;
|
||||
|
||||
fEntry.offset = _data->readUint32LE();
|
||||
fEntry.num_blocks = _data->readUint16LE();
|
||||
fEntry.comp_type = _data->readUint16LE();
|
||||
|
||||
if (_data->err())
|
||||
return;
|
||||
|
||||
_folderMap[i] = fEntry;
|
||||
}
|
||||
|
||||
//CFFILEs PARSING
|
||||
_data->seek(filesOffset);
|
||||
if (_data->err())
|
||||
return;
|
||||
|
||||
for (uint16 i = 0; i < numFiles; ++i) {
|
||||
FileEntry fEntry;
|
||||
|
||||
fEntry.length = _data->readUint32LE();
|
||||
fEntry.folderOffset = _data->readUint32LE();
|
||||
uint16 iFolder = _data->readUint16LE();
|
||||
/* uint16 date = */ _data->readUint16LE();
|
||||
/* uint16 time = */ _data->readUint16LE();
|
||||
/* uint16 attribs = */ _data->readUint16LE();
|
||||
Common::String name = readString(_data);
|
||||
for (uint l = 0; l < name.size(); ++l)
|
||||
if (name[l] == '\\')
|
||||
name.setChar('/', l);
|
||||
|
||||
if (_data->err()) {
|
||||
_fileMap.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_folderMap.contains(iFolder))
|
||||
fEntry.folder = &_folderMap[iFolder];
|
||||
else {
|
||||
_fileMap.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
_fileMap[name] = fEntry;
|
||||
}
|
||||
}
|
||||
|
||||
/* read a null-terminated string from a stream
|
||||
Copied from ScummVm MohawkEngine_LivingBooks.*/
|
||||
Common::String MsCabinet::readString(Common::ReadStream *stream) {
|
||||
Common::String ret;
|
||||
while (!stream->eos()) {
|
||||
byte in = stream->readByte();
|
||||
if (!in)
|
||||
break;
|
||||
ret += in;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool MsCabinet::hasFile(const Common::String &name) const {
|
||||
return _fileMap.contains(name);
|
||||
}
|
||||
|
||||
int MsCabinet::listMembers(Common::ArchiveMemberList &list) const {
|
||||
for (FileMap::const_iterator it = _fileMap.begin(); it != _fileMap.end(); it++)
|
||||
list.push_back(getMember(it->_key));
|
||||
|
||||
return _fileMap.size();
|
||||
}
|
||||
|
||||
const Common::ArchiveMemberPtr MsCabinet::getMember(const Common::String &name) const {
|
||||
return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(name, this));
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *MsCabinet::createReadStreamForMember(const Common::String &name) const {
|
||||
byte *fileBuf;
|
||||
|
||||
if (!hasFile(name))
|
||||
return 0;
|
||||
|
||||
const FileEntry &entry = _fileMap[name];
|
||||
|
||||
//Check if the file has already been decompressed and it's in the cache,
|
||||
// otherwise decompress it and put it in the cache
|
||||
if (_cache.contains(name))
|
||||
fileBuf = _cache[name];
|
||||
else {
|
||||
//Check if the decompressor should be reinitialized
|
||||
if (!_decompressor || entry.folder != _decompressor->getFolder()) {
|
||||
if (_decompressor)
|
||||
delete _decompressor;
|
||||
|
||||
_decompressor = new Decompressor(entry.folder, _data);
|
||||
}
|
||||
|
||||
if (!_decompressor->decompressFile(fileBuf, entry))
|
||||
return 0;
|
||||
|
||||
_cache[name] = fileBuf;
|
||||
}
|
||||
|
||||
return new Common::MemoryReadStream(fileBuf, entry.length, DisposeAfterUse::NO);
|
||||
}
|
||||
|
||||
MsCabinet::Decompressor::Decompressor(const MsCabinet::FolderEntry *folder, Common::SeekableReadStream* data) :
|
||||
_curFolder(folder), _data(data), _curBlock(-1), _compressedBlock(0), _decompressedBlock(0), _fileBuf(0) {
|
||||
|
||||
//Alloc the decompression buffers
|
||||
_compressedBlock = new byte[kCabInputmax];
|
||||
_decompressedBlock = new byte[kCabBlockSize];
|
||||
}
|
||||
|
||||
MsCabinet::Decompressor::~Decompressor() {
|
||||
if (_decompressedBlock)
|
||||
delete[] _decompressedBlock;
|
||||
|
||||
if (_compressedBlock)
|
||||
delete[] _compressedBlock;
|
||||
|
||||
if (_fileBuf)
|
||||
delete[] _fileBuf;
|
||||
}
|
||||
|
||||
bool MsCabinet::Decompressor::decompressFile(byte *&fileBuf, const FileEntry &entry) {
|
||||
#ifdef USE_ZLIB
|
||||
// Ref: http://blogs.kde.org/node/3181
|
||||
uint32 cksum, cksum2;
|
||||
uint16 uncompressedLen, compressedLen;
|
||||
byte hdrS[4];
|
||||
byte *buf_tmp, *dict;
|
||||
bool decRes;
|
||||
|
||||
//Sanity checks
|
||||
if (!_compressedBlock || !_compressedBlock)
|
||||
return false;
|
||||
|
||||
if (entry.folder != _curFolder)
|
||||
return false;
|
||||
|
||||
_startBlock = entry.folderOffset / kCabBlockSize;
|
||||
_inBlockStart = entry.folderOffset % kCabBlockSize;
|
||||
_endBlock = (entry.folderOffset + entry.length) / kCabBlockSize;
|
||||
_inBlockEnd = (entry.folderOffset + entry.length) % kCabBlockSize;
|
||||
|
||||
//Check if the decompressor should be reinitialized
|
||||
if (_curBlock > _startBlock || _curBlock == -1) {
|
||||
_data->seek(entry.folder->offset);
|
||||
//Check the compression method (only mszip supported)
|
||||
if (entry.folder->comp_type != kMszipCompression)
|
||||
return false;
|
||||
|
||||
_curBlock = -1; //No block decompressed
|
||||
}
|
||||
|
||||
//Check if the file is contained in the folder
|
||||
if ((entry.length + entry.folderOffset) / kCabBlockSize > entry.folder->num_blocks)
|
||||
return false;
|
||||
|
||||
_fileBuf = new byte[entry.length];
|
||||
if (!_fileBuf)
|
||||
return false;
|
||||
|
||||
buf_tmp = _fileBuf;
|
||||
|
||||
//if a part of this file has been decompressed in the last block, make a copy of it
|
||||
copyBlock(buf_tmp);
|
||||
|
||||
while((_curBlock + 1) <= _endBlock) {
|
||||
// Read the CFDATA header
|
||||
cksum = _data->readUint32LE();
|
||||
_data->read(hdrS, 4);
|
||||
compressedLen = READ_LE_UINT16(hdrS);
|
||||
uncompressedLen = READ_LE_UINT16(hdrS + 2);
|
||||
|
||||
if (_data->err())
|
||||
return false;
|
||||
|
||||
if (compressedLen > kCabInputmax || uncompressedLen > kCabBlockSize)
|
||||
return false;
|
||||
|
||||
//Read the compressed block
|
||||
if (_data->read(_compressedBlock, compressedLen) != compressedLen)
|
||||
return false;
|
||||
|
||||
//Perform checksum test on the block (if one is stored)
|
||||
if (cksum != 0) {
|
||||
cksum2 = checksum(_compressedBlock, compressedLen, 0);
|
||||
if (checksum(hdrS, 4, cksum2) != cksum)
|
||||
return false;
|
||||
}
|
||||
|
||||
//Check the CK header
|
||||
if (_compressedBlock[0] != 'C' && _compressedBlock[1] == 'K')
|
||||
return false;
|
||||
|
||||
//Decompress the block. If it isn't the first, provide the previous block as dictonary
|
||||
dict = (_curBlock >= 0) ? _decompressedBlock : 0;
|
||||
decRes = Common::inflateZlibHeaderless(_decompressedBlock, uncompressedLen, _compressedBlock + 2, compressedLen - 2, dict, kCabBlockSize);
|
||||
if (!decRes)
|
||||
return false;
|
||||
|
||||
_curBlock++;
|
||||
|
||||
//Copy the decompressed data, if needed
|
||||
copyBlock(buf_tmp);
|
||||
}
|
||||
|
||||
fileBuf = _fileBuf;
|
||||
_fileBuf = 0;
|
||||
return true;
|
||||
#else
|
||||
warning("zlib required to extract MSCAB");
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void MsCabinet::Decompressor::copyBlock(byte *&data_ptr) const {
|
||||
uint16 start, end, size;
|
||||
|
||||
if(_startBlock <= _curBlock && _curBlock <= _endBlock) {
|
||||
start = (_startBlock == _curBlock) ? _inBlockStart: 0;
|
||||
end = (_endBlock == _curBlock) ? _inBlockEnd : uint16(kCabBlockSize);
|
||||
size = end - start;
|
||||
|
||||
memcpy(data_ptr, _decompressedBlock + start, size);
|
||||
data_ptr += size;
|
||||
}
|
||||
}
|
||||
|
||||
uint32 MsCabinet::Decompressor::checksum(byte *data, uint32 bytes, uint32 cksum) const {
|
||||
uint32 len, ul = 0;
|
||||
|
||||
for (len = bytes >> 2; len--; data += 4) {
|
||||
cksum ^= READ_LE_UINT32(data);
|
||||
}
|
||||
|
||||
switch (bytes & 3) {
|
||||
case 3: ul |= *data++ << 16;
|
||||
case 2: ul |= *data++ << 8;
|
||||
case 1: ul |= *data;
|
||||
}
|
||||
cksum ^= ul;
|
||||
|
||||
return cksum;
|
||||
}
|
||||
|
||||
} // End of namespace Grim
|
103
engines/grim/update/mscab.h
Normal file
103
engines/grim/update/mscab.h
Normal file
@ -0,0 +1,103 @@
|
||||
/* ResidualVM - A 3D game interpreter
|
||||
*
|
||||
* ResidualVM 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 library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MSCAB_H
|
||||
#define MSCAB_H
|
||||
|
||||
#include "common/hashmap.h"
|
||||
#include "common/hash-str.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/archive.h"
|
||||
#include "common/str.h"
|
||||
#include "common/util.h"
|
||||
|
||||
namespace Grim {
|
||||
|
||||
class MsCabinet : public Common::Archive {
|
||||
public:
|
||||
MsCabinet(Common::SeekableReadStream* data);
|
||||
~MsCabinet();
|
||||
|
||||
// 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;
|
||||
|
||||
private:
|
||||
Common::SeekableReadStream* _data;
|
||||
|
||||
struct FolderEntry {
|
||||
uint16 comp_type; //The compression type
|
||||
uint16 num_blocks; //The total number of data blocks used by this folder
|
||||
uint32 offset; //The cabinet offset of first datablock
|
||||
};
|
||||
|
||||
struct FileEntry {
|
||||
uint32 length; //Uncompressed size of the file in bytes
|
||||
FolderEntry *folder; //Folder holding this file
|
||||
uint32 folderOffset; //Uncompressed offset in the folder
|
||||
};
|
||||
|
||||
typedef Common::HashMap<Common::String, FileEntry, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> FileMap;
|
||||
FileMap _fileMap;
|
||||
|
||||
typedef Common::HashMap<uint16, FolderEntry> FolderMap;
|
||||
FolderMap _folderMap;
|
||||
|
||||
Common::String readString(Common::ReadStream *stream);
|
||||
|
||||
//Decompressor
|
||||
class Decompressor {
|
||||
public:
|
||||
Decompressor(const FolderEntry *folder, Common::SeekableReadStream* _data);
|
||||
~Decompressor();
|
||||
bool decompressFile(byte *&fileBuf, const FileEntry &entry);
|
||||
const FolderEntry *getFolder() const { return _curFolder; }
|
||||
private:
|
||||
Common::SeekableReadStream* _data;
|
||||
const FolderEntry *_curFolder;
|
||||
int16 _curBlock;
|
||||
byte *_compressedBlock, *_decompressedBlock;
|
||||
byte *_fileBuf;
|
||||
uint16 _startBlock, _inBlockStart, _endBlock, _inBlockEnd;
|
||||
|
||||
void copyBlock(byte *&data_ptr) const;
|
||||
uint32 checksum(byte *data, uint32 bytes, uint32 cksum) const;
|
||||
|
||||
enum {
|
||||
kMszipCompression = 1,
|
||||
kCabBlockSize = 0x8000,
|
||||
kCabInputmax = kCabBlockSize + 12
|
||||
};
|
||||
};
|
||||
|
||||
mutable Decompressor *_decompressor;
|
||||
|
||||
//Cache
|
||||
typedef Common::HashMap<Common::String, byte *, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> CacheMap;
|
||||
mutable CacheMap _cache;
|
||||
};
|
||||
|
||||
} // End of namespace Grim
|
||||
|
||||
#endif
|
138
engines/grim/update/packfile.cpp
Normal file
138
engines/grim/update/packfile.cpp
Normal file
@ -0,0 +1,138 @@
|
||||
/* ResidualVM - A 3D game interpreter
|
||||
*
|
||||
* ResidualVM 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 library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/endian.h"
|
||||
#include "common/stream.h"
|
||||
|
||||
#include "engines/grim/update/packfile.h"
|
||||
|
||||
namespace Grim {
|
||||
|
||||
const uint32 PackFile::_knownOffsets[] = { 0x1c000, 0x21000, 0x23000, 0x24000, NULL };
|
||||
|
||||
PackFile::PackFile(Common::SeekableReadStream *data):
|
||||
_codeTable(NULL), _orgStream(data), _offset(0), _kCodeTableSize(0x100) {
|
||||
|
||||
uint32 magicContainer, magicCabinet, key;
|
||||
|
||||
for (int i = 0; _knownOffsets[i] != 0; ++i) {
|
||||
if (_knownOffsets[i] > uint32(_orgStream->size()))
|
||||
continue;
|
||||
|
||||
_orgStream->seek(_knownOffsets[i]);
|
||||
|
||||
//Check for content signature
|
||||
magicContainer = _orgStream->readUint32BE();
|
||||
if (!err() && magicContainer == MKTAG('1','C','N','T')) {
|
||||
key = _orgStream->readUint32LE();
|
||||
createCodeTable(key);
|
||||
_offset = _orgStream->pos();
|
||||
|
||||
//Check for cabinet signature
|
||||
magicCabinet = readUint32BE();
|
||||
if (!err() && magicCabinet == MKTAG('M','S','C','F'))
|
||||
break;
|
||||
else {
|
||||
delete[] _codeTable;
|
||||
_codeTable = NULL;
|
||||
_offset = 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_size = _orgStream->size() - _offset;
|
||||
_orgStream->seek(_offset);
|
||||
}
|
||||
|
||||
|
||||
PackFile::~PackFile() {
|
||||
if (_codeTable)
|
||||
delete[] _codeTable;
|
||||
|
||||
delete _orgStream;
|
||||
}
|
||||
|
||||
bool PackFile::err() const {
|
||||
return _orgStream->err();
|
||||
}
|
||||
|
||||
void PackFile::clearErr() {
|
||||
_orgStream->clearErr();
|
||||
}
|
||||
|
||||
void PackFile::createCodeTable(uint32 key) {
|
||||
const uint32 kRandA = 0x343FD;
|
||||
const uint32 kRandB = 0x269EC3;
|
||||
uint32 value = key;
|
||||
|
||||
if (_codeTable)
|
||||
delete[] _codeTable;
|
||||
_codeTable = new uint16[_kCodeTableSize * 2];
|
||||
|
||||
for (uint i = 0; i < _kCodeTableSize; i++) {
|
||||
value = kRandA * value + kRandB;
|
||||
_codeTable[i] = uint16((value >> 16) & 0x7FFF);
|
||||
}
|
||||
}
|
||||
|
||||
void PackFile::decode(uint8 *data, uint32 dataSize, uint32 start_point) {
|
||||
for (uint32 i = 0; i < dataSize; i++) {
|
||||
data[i] ^= uint8(_codeTable[(i + start_point) % _kCodeTableSize]);
|
||||
data[i] -= uint8(_codeTable[(i + start_point) % _kCodeTableSize] >> 8);
|
||||
}
|
||||
}
|
||||
|
||||
uint32 PackFile::read(void *dataPtr, uint32 dataSize) {
|
||||
uint32 start_point, count;
|
||||
|
||||
start_point = uint32(pos());
|
||||
count = _orgStream->read(dataPtr, dataSize);
|
||||
|
||||
if (err() || count != dataSize)
|
||||
return 0;
|
||||
|
||||
if (_codeTable)
|
||||
decode((uint8*)dataPtr, count, start_point);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
bool PackFile::eos() const {
|
||||
return _orgStream->eos();
|
||||
}
|
||||
|
||||
int32 PackFile::pos() const {
|
||||
return _orgStream->pos() - _offset;
|
||||
}
|
||||
|
||||
int32 PackFile::size() const {
|
||||
return _size;
|
||||
}
|
||||
|
||||
bool PackFile::seek(int32 offset, int whence) {
|
||||
if (_codeTable && whence == SEEK_SET)
|
||||
offset += _offset;
|
||||
return _orgStream->seek(offset, whence);
|
||||
}
|
||||
|
||||
} // end of namespace Grim
|
60
engines/grim/update/packfile.h
Normal file
60
engines/grim/update/packfile.h
Normal file
@ -0,0 +1,60 @@
|
||||
/* ResidualVM - A 3D game interpreter
|
||||
*
|
||||
* ResidualVM 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 library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef PACKFILE_H
|
||||
#define PACKFILE_H
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace Grim {
|
||||
|
||||
class PackFile : public Common::SeekableReadStream {
|
||||
public:
|
||||
PackFile(Common::SeekableReadStream *data);
|
||||
~PackFile();
|
||||
|
||||
bool err() const;
|
||||
void clearErr();
|
||||
uint32 read(void *dataPtr, uint32 dataSize);
|
||||
bool eos() const;
|
||||
int32 pos() const;
|
||||
int32 size() const;
|
||||
bool seek(int32 offset, int whence = SEEK_SET);
|
||||
|
||||
private:
|
||||
Common::SeekableReadStream *_orgStream;
|
||||
int32 _offset, _size;
|
||||
|
||||
uint16 *_codeTable;
|
||||
const uint32 _kCodeTableSize;
|
||||
|
||||
void createCodeTable(uint32 key);
|
||||
void decode(uint8 *data, uint32 size, uint32 start_point);
|
||||
|
||||
static const uint32 _knownOffsets[5];
|
||||
};
|
||||
|
||||
} // end of namespace Grim
|
||||
|
||||
#endif
|
48
engines/grim/update/update.cpp
Normal file
48
engines/grim/update/update.cpp
Normal file
@ -0,0 +1,48 @@
|
||||
/* ResidualVM - A 3D game interpreter
|
||||
*
|
||||
* ResidualVM 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 library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/stream.h"
|
||||
#include "common/archive.h"
|
||||
|
||||
#include "engines/grim/update/packfile.h"
|
||||
#include "engines/grim/update/mscab.h"
|
||||
#include "engines/grim/update/lang_filter.h"
|
||||
#include "engines/grim/update/update.h"
|
||||
#include "engines/grim/grim.h"
|
||||
|
||||
namespace Grim {
|
||||
|
||||
Common::Archive *loadUpdateArchive(Common::SeekableReadStream *data) {
|
||||
Common::SeekableReadStream *updStream = new PackFile(data);
|
||||
Common::Archive *cab = new MsCabinet(updStream);
|
||||
Common::Archive *update = new LangFilter(cab, g_grim->getGameLanguage());
|
||||
|
||||
Common::ArchiveMemberList list;
|
||||
if (update->listMembers(list) == 0) {
|
||||
delete update;
|
||||
return 0;
|
||||
} else
|
||||
return update;
|
||||
|
||||
}
|
||||
|
||||
} // end of namespace Grim
|
41
engines/grim/update/update.h
Normal file
41
engines/grim/update/update.h
Normal file
@ -0,0 +1,41 @@
|
||||
/* ResidualVM - A 3D game interpreter
|
||||
*
|
||||
* ResidualVM 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 library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef UPDATE_H
|
||||
#define UPDATE_H
|
||||
|
||||
namespace Common {
|
||||
class Archive;
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace Grim {
|
||||
|
||||
/**
|
||||
* This method creates an Archive instance corresponding to the content
|
||||
* of a GF or EMI update executable, with the current game language.
|
||||
*/
|
||||
Common::Archive *loadUpdateArchive(Common::SeekableReadStream *data);
|
||||
|
||||
} // end of namespace Grim
|
||||
|
||||
#endif
|
Loading…
x
Reference in New Issue
Block a user