263 lines
8.0 KiB
C++
263 lines
8.0 KiB
C++
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "agds/resourceManager.h"
|
|
#include "common/algorithm.h"
|
|
#include "common/debug.h"
|
|
#include "common/file.h"
|
|
#include "common/memstream.h"
|
|
#include "common/path.h"
|
|
#include "common/ptr.h"
|
|
#include "graphics/surface.h"
|
|
#include "image/bmp.h"
|
|
#include "image/pcx.h"
|
|
#include "video/flic_decoder.h"
|
|
|
|
namespace AGDS {
|
|
ResourceManager::ResourceManager(int version) : _version(version) {}
|
|
|
|
ResourceManager::~ResourceManager() {}
|
|
|
|
void ResourceManager::decrypt(uint8 *data, unsigned size) {
|
|
static const char *kKey = "Vyvojovy tym AGDS varuje: Hackerovani skodi obchodu!";
|
|
const char *ptr = kKey;
|
|
while (size--) {
|
|
*data++ ^= 0xff ^ *ptr++;
|
|
if (*ptr == 0)
|
|
ptr = kKey;
|
|
}
|
|
}
|
|
|
|
bool ResourceManager::GrpFile::load(const Common::Path &grpPath) {
|
|
static const char *kSignature = "AGDS group file\x1a";
|
|
static const uint32 kMagic = 0x1a03c9e6;
|
|
static const uint32 kVersion1 = 44;
|
|
static const uint32 kVersion2 = 2;
|
|
|
|
debug("adding path %s", grpPath.toString().c_str());
|
|
if (!_file.open(grpPath)) {
|
|
warning("failing opening grp file %s", grpPath.toString().c_str());
|
|
return false;
|
|
}
|
|
uint8 header[0x2c];
|
|
if (_file.read(header, sizeof(header)) != sizeof(header)) {
|
|
warning("short read from header");
|
|
return false;
|
|
}
|
|
|
|
if (strncmp(reinterpret_cast<const char *>(header), kSignature, 0x10) != 0) {
|
|
decrypt(header, 0x10);
|
|
if (strncmp(reinterpret_cast<const char *>(header), kSignature, 0x10) != 0) {
|
|
warning("invalid signature");
|
|
return false;
|
|
}
|
|
debug("load grp file %s, encrypted", grpPath.toString().c_str());
|
|
_encrypted = true;
|
|
} else {
|
|
debug("load grp file %s, unencrypted", grpPath.toString().c_str());
|
|
_encrypted = false;
|
|
}
|
|
|
|
Common::MemoryReadStreamEndian reader(header + 0x10, sizeof(header) - 0x10, false);
|
|
uint32 version1 = reader.readUint32();
|
|
if (version1 != kVersion1) {
|
|
warning("invalid version 1 (%d)", version1);
|
|
return false;
|
|
}
|
|
|
|
uint32 magic = reader.readUint32();
|
|
if (magic != kMagic) {
|
|
warning("invalid magic (0x%08x)", magic);
|
|
return false;
|
|
}
|
|
|
|
uint32 version2 = reader.readUint32();
|
|
if (version2 != kVersion2) {
|
|
warning("invalid version 2 (%d)", version2);
|
|
return false;
|
|
}
|
|
|
|
unsigned dirCount = reader.readUint32();
|
|
if (!reader.skip(3 * 4))
|
|
return false;
|
|
|
|
// debug("+%u files in index", dirCount);
|
|
while (dirCount--) {
|
|
uint8 dirData[0x31];
|
|
uint8 *dirDataEnd = dirData + sizeof(dirData);
|
|
|
|
if (_file.read(dirData, sizeof(dirData)) != sizeof(dirData)) {
|
|
warning("short read, corrupted file");
|
|
return false;
|
|
}
|
|
|
|
uint8 *nameEnd = Common::find(dirData, dirDataEnd, 0);
|
|
if (nameEnd == dirDataEnd) {
|
|
warning("corrupted entry at %d", (int)_file.pos() - 0x31);
|
|
continue;
|
|
}
|
|
|
|
unsigned nameLength = nameEnd - dirData;
|
|
if (_encrypted)
|
|
decrypt(dirData, nameLength);
|
|
Common::String name(reinterpret_cast<char *>(dirData), nameLength);
|
|
|
|
Common::MemoryReadStreamEndian dirReader(dirData + 0x21, 8, false);
|
|
|
|
uint32 offset = dirReader.readSint32();
|
|
uint32 size = dirReader.readSint32();
|
|
// debug("\t\tfile %s %u %u", name.c_str(), offset, size);
|
|
ArchiveMemberPtr resource(new ArchiveMember(this, name, offset, size));
|
|
_members.setVal(name, resource);
|
|
}
|
|
|
|
debug("%s: %u files in index", grpPath.toString().c_str(), _members.size());
|
|
return true;
|
|
}
|
|
|
|
int ResourceManager::GrpFile::listMembers(Common::ArchiveMemberList &list) const {
|
|
int size = 0;
|
|
for (MembersType::const_iterator i = _members.begin(); i != _members.end(); ++i, ++size)
|
|
list.push_back(i->_value);
|
|
return size;
|
|
}
|
|
|
|
bool ResourceManager::GrpFile::hasFile(const Common::Path &name) const {
|
|
return _members.find(name.toString()) != _members.end();
|
|
}
|
|
|
|
const Common::ArchiveMemberPtr ResourceManager::GrpFile::getMember(const Common::Path &name) const {
|
|
Common::ArchiveMemberPtr member;
|
|
MembersType::const_iterator i = _members.find(name.toString());
|
|
if (i != _members.end())
|
|
member = i->_value;
|
|
return member;
|
|
}
|
|
|
|
Common::SeekableReadStream *ResourceManager::GrpFile::createReadStreamForMember(const Common::Path &name) const {
|
|
Common::ArchiveMemberPtr member = getMember(name);
|
|
return member ? member->createReadStream() : NULL;
|
|
}
|
|
|
|
bool ResourceManager::addPath(const Common::Path &grpFilename) {
|
|
Common::ScopedPtr<GrpFile> grpFile(new GrpFile());
|
|
if (!grpFile->load(grpFilename)) {
|
|
return false;
|
|
}
|
|
|
|
SearchMan.add(grpFilename.toString(), grpFile.release(), 0, true);
|
|
return true;
|
|
}
|
|
|
|
Common::SeekableReadStream *ResourceManager::ArchiveMember::createReadStream() const {
|
|
Common::SeekableReadStream &file = _parent->getArchiveStream();
|
|
file.seek(_offset);
|
|
return file.readStream(_size);
|
|
}
|
|
|
|
Common::SeekableReadStream *ResourceManager::getResource(const Common::String &name) const {
|
|
Common::File file;
|
|
return (file.open(Common::Path{name})) ? file.readStream(file.size()) : NULL;
|
|
}
|
|
|
|
bool ResourceManager::IsBMP(Common::SeekableReadStream &stream) {
|
|
auto b0 = stream.readByte();
|
|
auto b1 = stream.readByte();
|
|
stream.seek(-2, SEEK_CUR);
|
|
return b0 == 'B' && b1 == 'M';
|
|
}
|
|
|
|
Graphics::Surface *ResourceManager::loadPicture(const Common::String &name, const Graphics::PixelFormat &format) {
|
|
Common::SeekableReadStream *stream = getResource(name);
|
|
if (!stream)
|
|
return NULL;
|
|
|
|
auto is_bmp = IsBMP(*stream);
|
|
|
|
Common::String lname = name;
|
|
lname.toLowercase();
|
|
|
|
if (lname.hasSuffix(".bmp") || is_bmp) {
|
|
Image::BitmapDecoder bmp;
|
|
return bmp.loadStream(*stream) ? bmp.getSurface()->convertTo(format) : NULL;
|
|
} else if (lname.hasSuffix(".pcx")) {
|
|
Image::PCXDecoder pcx;
|
|
return pcx.loadStream(*stream) ? pcx.getSurface()->convertTo(format) : NULL;
|
|
} else if (lname.hasSuffix(".flc")) {
|
|
Video::FlicDecoder flic;
|
|
if (!flic.loadStream(stream)) {
|
|
warning("flic decoder failed to load %s", name.c_str());
|
|
return NULL;
|
|
}
|
|
const Graphics::Surface *surface = flic.decodeNextFrame();
|
|
return surface ? surface->convertTo(format, flic.getPalette()) : NULL;
|
|
} else
|
|
warning("unknown extensions for resource %s", name.c_str());
|
|
return NULL;
|
|
}
|
|
|
|
Common::String ResourceManager::loadText(Common::SeekableReadStream &stream) const {
|
|
Common::Array<char> text(stream.size());
|
|
if (stream.read(text.data(), text.size()) != text.size())
|
|
error("short read from text resource");
|
|
|
|
if (text.empty())
|
|
return Common::String();
|
|
|
|
char *begin = text.data();
|
|
char *end = begin + text.size();
|
|
while (begin != end && end[-1] == 0)
|
|
--end;
|
|
|
|
if (_version != 0 || (text[0] < ' ' || text[0] >= 0x7f))
|
|
decrypt(reinterpret_cast<uint8 *>(text.data()), end - begin);
|
|
|
|
while (begin != end && end[-1] == 0)
|
|
--end;
|
|
|
|
return Common::String(begin, end);
|
|
}
|
|
|
|
Common::String ResourceManager::loadText(const Common::String &name) const {
|
|
Common::ScopedPtr<Common::SeekableReadStream> stream(getResource(name));
|
|
if (!stream)
|
|
error("no text resource %s", name.c_str());
|
|
return loadText(*stream);
|
|
}
|
|
|
|
Common::String readString(Common::ReadStream &stream, uint size) {
|
|
Common::Array<char> text(size);
|
|
if (stream.read(text.data(), text.size()) != text.size())
|
|
error("readString: short read");
|
|
|
|
return Common::String(text.data(), strlen(text.data()));
|
|
}
|
|
|
|
void writeString(Common::WriteStream &stream, const Common::String &string, uint size) {
|
|
Common::Array<char> text(size);
|
|
memcpy(text.data(), string.c_str(), MIN(string.size(), size));
|
|
|
|
if (stream.write(text.data(), text.size()) != text.size())
|
|
error("writeString: short write");
|
|
}
|
|
|
|
} // End of namespace AGDS
|