Initial commit
This commit is contained in:
103
engines/ultima/ultima8/filesys/archive.cpp
Normal file
103
engines/ultima/ultima8/filesys/archive.cpp
Normal file
@@ -0,0 +1,103 @@
|
||||
/* 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 "ultima/ultima8/misc/debugger.h"
|
||||
#include "ultima/ultima8/filesys/archive.h"
|
||||
#include "ultima/ultima8/filesys/flex_file.h"
|
||||
#include "ultima/ultima8/filesys/u8_save_file.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima8 {
|
||||
|
||||
Archive::Archive() {
|
||||
_count = 0;
|
||||
}
|
||||
|
||||
Archive::~Archive() {
|
||||
for (unsigned int i = 0; i < _sources.size(); ++i)
|
||||
delete _sources[i];
|
||||
_sources.clear();
|
||||
}
|
||||
|
||||
Archive::Archive(Common::SeekableReadStream *rs) : _count(0) {
|
||||
addSource(rs);
|
||||
}
|
||||
|
||||
bool Archive::addSource(FlexFile *af) {
|
||||
_sources.push_back(af);
|
||||
|
||||
uint32 indexcount = af->getCount();
|
||||
if (indexcount > _count)
|
||||
_count = indexcount;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Archive::addSource(Common::SeekableReadStream *rs) {
|
||||
if (!rs)
|
||||
return false;
|
||||
|
||||
FlexFile *s = new FlexFile(rs);
|
||||
if (!s->isValid()) {
|
||||
delete s;
|
||||
return false;
|
||||
}
|
||||
|
||||
return addSource(s);
|
||||
}
|
||||
|
||||
void Archive::cache() {
|
||||
for (unsigned int i = 0; i < _count; ++i)
|
||||
cache(i);
|
||||
}
|
||||
|
||||
void Archive::uncache() {
|
||||
for (unsigned int i = 0; i < _count; ++i)
|
||||
uncache(i);
|
||||
}
|
||||
|
||||
uint8 *Archive::getRawObject(uint32 index, uint32 *sizep) {
|
||||
FlexFile *f = findArchiveFile(index);
|
||||
if (!f)
|
||||
return nullptr;
|
||||
|
||||
return f->getObject(index, sizep);
|
||||
}
|
||||
|
||||
uint32 Archive::getRawSize(uint32 index) const {
|
||||
FlexFile *f = findArchiveFile(index);
|
||||
if (!f) return 0;
|
||||
|
||||
return f->getSize(index);
|
||||
}
|
||||
|
||||
FlexFile *Archive::findArchiveFile(uint32 index) const {
|
||||
unsigned int n = _sources.size();
|
||||
for (unsigned int i = 1; i <= n; ++i) {
|
||||
if (_sources[n - i]->exists(index))
|
||||
return _sources[n - i];
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // End of namespace Ultima8
|
||||
} // End of namespace Ultima
|
||||
94
engines/ultima/ultima8/filesys/archive.h
Normal file
94
engines/ultima/ultima8/filesys/archive.h
Normal file
@@ -0,0 +1,94 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ULTIMA8_FILESYS_ARCHIVE_H
|
||||
#define ULTIMA8_FILESYS_ARCHIVE_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "ultima/shared/std/containers.h"
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima8 {
|
||||
|
||||
class FlexFile;
|
||||
|
||||
class Archive {
|
||||
public:
|
||||
//! create Archive without any input sources
|
||||
Archive();
|
||||
|
||||
//! create Archive with a single input source, autodetecting the type
|
||||
//! Will create FlexFile; ids will be deleted.
|
||||
explicit Archive(Common::SeekableReadStream *rs);
|
||||
|
||||
virtual ~Archive();
|
||||
|
||||
//! add input source.
|
||||
//! FlexFile will be deleted on destruction
|
||||
//! Input sources are used in the reversed order they are added.
|
||||
//! Effect of adding sources after having accessed objects is undef.
|
||||
bool addSource(FlexFile *af);
|
||||
|
||||
//! add input source, autodetecting the type (as the constructor)
|
||||
bool addSource(Common::SeekableReadStream *rs);
|
||||
|
||||
//! Cache all objects
|
||||
void cache();
|
||||
|
||||
//! Cache a single object
|
||||
virtual void cache(uint32 index) = 0;
|
||||
|
||||
//! Uncache all objects
|
||||
//! Potentially dangerous: all stored objects will be deleted; make sure
|
||||
//! they are no longer in use.
|
||||
void uncache();
|
||||
|
||||
//! Uncache a single object
|
||||
//! Potentially dangerous. See uncache()
|
||||
virtual void uncache(uint32 index) = 0;
|
||||
|
||||
//! Check if an object is cached
|
||||
virtual bool isCached(uint32 index) const = 0;
|
||||
|
||||
uint32 getCount() const {
|
||||
return _count;
|
||||
}
|
||||
|
||||
protected:
|
||||
uint32 _count;
|
||||
|
||||
uint8 *getRawObject(uint32 index, uint32 *sizep = 0);
|
||||
uint32 getRawSize(uint32 index) const;
|
||||
|
||||
private:
|
||||
Std::vector<FlexFile *> _sources;
|
||||
|
||||
FlexFile *findArchiveFile(uint32 index) const;
|
||||
};
|
||||
|
||||
} // End of namespace Ultima8
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
132
engines/ultima/ultima8/filesys/flex_file.cpp
Normal file
132
engines/ultima/ultima8/filesys/flex_file.cpp
Normal file
@@ -0,0 +1,132 @@
|
||||
/* 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 "common/memstream.h"
|
||||
|
||||
#include "ultima/ultima8/misc/debugger.h"
|
||||
#include "ultima/ultima8/filesys/flex_file.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima8 {
|
||||
|
||||
static const int FLEX_TABLE_OFFSET = 0x80;
|
||||
static const int FLEX_HDR_SIZE = 0x52;
|
||||
static const char FLEX_HDR_PAD = 0x1A;
|
||||
|
||||
FlexFile::FlexFile(Common::SeekableReadStream *rs) : _rs(rs) {
|
||||
_valid = isFlexFile(_rs);
|
||||
|
||||
if (_valid)
|
||||
_valid = readMetadata();
|
||||
}
|
||||
|
||||
FlexFile::~FlexFile() {
|
||||
delete _rs;
|
||||
}
|
||||
|
||||
//static
|
||||
bool FlexFile::isFlexFile(Common::SeekableReadStream *rs) {
|
||||
rs->seek(0);
|
||||
int i;
|
||||
char buf[FLEX_HDR_SIZE];
|
||||
rs->read(buf, FLEX_HDR_SIZE);
|
||||
|
||||
for (i = 0; i < FLEX_HDR_SIZE; ++i) {
|
||||
if (buf[i] == FLEX_HDR_PAD) break;
|
||||
}
|
||||
|
||||
if (i < FLEX_HDR_SIZE) {
|
||||
for (++i; i < FLEX_HDR_SIZE; ++i) {
|
||||
if (buf[i] != FLEX_HDR_PAD) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FlexFile::readMetadata() {
|
||||
_rs->seek(FLEX_HDR_SIZE + 2);
|
||||
uint32 count = _rs->readUint32LE();
|
||||
|
||||
if (count > 4095) {
|
||||
// In practice the largest flex in either Crusader or U8 games has
|
||||
// 3074 entries, so this seems invalid.
|
||||
warning("Flex invalid: improbable number of entries %d", count);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_rs->size() < FLEX_TABLE_OFFSET + 8 * count) {
|
||||
warning("Flex invalid: stream not long enough for offset table");
|
||||
return false;
|
||||
}
|
||||
|
||||
_entries.reserve(count);
|
||||
_rs->seek(FLEX_TABLE_OFFSET);
|
||||
for (unsigned int i = 0; i < count; ++i) {
|
||||
FileEntry fe;
|
||||
fe._offset = _rs->readUint32LE();
|
||||
fe._size = _rs->readUint32LE();
|
||||
|
||||
_entries.push_back(fe);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *FlexFile::getDataSource(uint32 index, bool is_text) {
|
||||
uint32 size;
|
||||
uint8 *buf = getObject(index, &size);
|
||||
|
||||
if (!buf)
|
||||
return nullptr;
|
||||
|
||||
return new Common::MemoryReadStream(buf, size, DisposeAfterUse::YES);
|
||||
}
|
||||
|
||||
uint8 *FlexFile::getObject(uint32 index, uint32 *sizep) {
|
||||
if (index >= _entries.size())
|
||||
return nullptr;
|
||||
|
||||
uint32 size = _entries[index]._size;
|
||||
if (size == 0)
|
||||
return nullptr;
|
||||
|
||||
uint8 *object = new uint8[size];
|
||||
uint32 offset = _entries[index]._offset;
|
||||
|
||||
_rs->seek(offset);
|
||||
_rs->read(object, size);
|
||||
|
||||
if (sizep)
|
||||
*sizep = size;
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
uint32 FlexFile::getSize(uint32 index) const {
|
||||
if (index >= _entries.size())
|
||||
return 0;
|
||||
|
||||
return _entries[index]._size;
|
||||
}
|
||||
|
||||
} // End of namespace Ultima8
|
||||
} // End of namespace Ultima
|
||||
90
engines/ultima/ultima8/filesys/flex_file.h
Normal file
90
engines/ultima/ultima8/filesys/flex_file.h
Normal file
@@ -0,0 +1,90 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ULTIMA8_FILESYS_FLEXFILE_H
|
||||
#define ULTIMA8_FILESYS_FLEXFILE_H
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima8 {
|
||||
|
||||
class FlexFile {
|
||||
public:
|
||||
//! create FlexFile from datasource; FlexFile takes ownership of ds
|
||||
//! and deletes it when destructed
|
||||
explicit FlexFile(Common::SeekableReadStream *rs);
|
||||
~FlexFile();
|
||||
|
||||
//! Check if constructed object is indeed a valid archive
|
||||
bool isValid() const {
|
||||
return _valid;
|
||||
}
|
||||
|
||||
//! Check if numbered object exists
|
||||
//! \param index index of object to check for
|
||||
bool exists(uint32 index) {
|
||||
return getSize(index) > 0;
|
||||
}
|
||||
|
||||
//! Get object as a Common::SeekableReadStream
|
||||
//! Delete the SeekableReadStream afterwards; that will delete the data as well
|
||||
Common::SeekableReadStream *getDataSource(uint32 index, bool is_text = false);
|
||||
|
||||
//! Get object from file; returns NULL if index is invalid.
|
||||
//! Must delete the returned buffer afterwards.
|
||||
//! See also exists(uint32 index)
|
||||
//! \param index index of object to fetch
|
||||
//! \param size if non-NULL, size of object is stored in *size
|
||||
uint8 *getObject(uint32 index, uint32 *size = nullptr);
|
||||
|
||||
//! Get size of object; returns zero if index is invalid.
|
||||
//! See also exists(uint32 index)
|
||||
//! \param index index of object to get size of
|
||||
uint32 getSize(uint32 index) const;
|
||||
|
||||
//! Get upper bound for number of objects.
|
||||
//! In an indexed file this is (probably) the highest index plus one,
|
||||
//! while in a named file it's (probably) the actual count
|
||||
uint32 getCount() const {
|
||||
return _entries.size();
|
||||
}
|
||||
|
||||
static bool isFlexFile(Common::SeekableReadStream *rs);
|
||||
|
||||
protected:
|
||||
Common::SeekableReadStream *_rs;
|
||||
bool _valid;
|
||||
|
||||
struct FileEntry {
|
||||
uint32 _offset;
|
||||
uint32 _size;
|
||||
FileEntry() : _offset(0), _size(0) {}
|
||||
};
|
||||
|
||||
Common::Array<FileEntry> _entries;
|
||||
|
||||
private:
|
||||
bool readMetadata();
|
||||
};
|
||||
|
||||
} // End of namespace Ultima8
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
101
engines/ultima/ultima8/filesys/raw_archive.cpp
Normal file
101
engines/ultima/ultima8/filesys/raw_archive.cpp
Normal file
@@ -0,0 +1,101 @@
|
||||
/* 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 "common/memstream.h"
|
||||
#include "ultima/ultima8/misc/debugger.h"
|
||||
#include "ultima/ultima8/filesys/raw_archive.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima8 {
|
||||
|
||||
RawArchive::~RawArchive() {
|
||||
Archive::uncache();
|
||||
}
|
||||
|
||||
void RawArchive::cache(uint32 index) {
|
||||
if (index >= _count) return;
|
||||
if (_objects.empty()) _objects.resize(_count);
|
||||
|
||||
if (_objects[index]) return;
|
||||
|
||||
_objects[index] = getRawObject(index);
|
||||
}
|
||||
|
||||
void RawArchive::uncache(uint32 index) {
|
||||
if (index >= _count) return;
|
||||
if (_objects.empty()) return;
|
||||
|
||||
if (_objects[index]) {
|
||||
delete[] _objects[index];
|
||||
_objects[index] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool RawArchive::isCached(uint32 index) const {
|
||||
if (index >= _count) return false;
|
||||
if (_objects.empty()) return false;
|
||||
|
||||
return (_objects[index] != nullptr);
|
||||
}
|
||||
|
||||
const uint8 *RawArchive::get_object_nodel(uint32 index) {
|
||||
if (index >= _count)
|
||||
return nullptr;
|
||||
cache(index);
|
||||
return _objects[index];
|
||||
}
|
||||
|
||||
uint8 *RawArchive::get_object(uint32 index) {
|
||||
if (index >= _count)
|
||||
return nullptr;
|
||||
|
||||
if (index < _objects.size() && _objects[index]) {
|
||||
// already cached
|
||||
uint32 size = getRawSize(index);
|
||||
if (size == 0)
|
||||
return nullptr;
|
||||
uint8 *object = new uint8[size];
|
||||
memcpy(object, _objects[index], size);
|
||||
return object;
|
||||
}
|
||||
|
||||
return getRawObject(index);
|
||||
}
|
||||
|
||||
uint32 RawArchive::get_size(uint32 index) const {
|
||||
if (index >= _count)
|
||||
return 0;
|
||||
return getRawSize(index);
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *RawArchive::get_datasource(uint32 index) {
|
||||
if (index >= _count)
|
||||
return nullptr;
|
||||
cache(index);
|
||||
|
||||
if (!_objects[index])
|
||||
return nullptr;
|
||||
|
||||
return new Common::MemoryReadStream(_objects[index], getRawSize(index));
|
||||
}
|
||||
|
||||
} // End of namespace Ultima8
|
||||
} // End of namespace Ultima
|
||||
64
engines/ultima/ultima8/filesys/raw_archive.h
Normal file
64
engines/ultima/ultima8/filesys/raw_archive.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ULTIMA8_FILESYS_RAWARCHIVE_H
|
||||
#define ULTIMA8_FILESYS_RAWARCHIVE_H
|
||||
|
||||
#include "ultima/ultima8/filesys/archive.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima8 {
|
||||
|
||||
class ArchiveFile;
|
||||
class IDataSource;
|
||||
|
||||
class RawArchive : public Archive {
|
||||
public:
|
||||
RawArchive() : Archive() { }
|
||||
explicit RawArchive(Common::SeekableReadStream *rs) : Archive(rs) { }
|
||||
|
||||
~RawArchive() override;
|
||||
|
||||
void cache(uint32 index) override;
|
||||
void uncache(uint32 index) override;
|
||||
bool isCached(uint32 index) const override;
|
||||
|
||||
//! return object. DON'T delete or modify!
|
||||
virtual const uint8 *get_object_nodel(uint32 index);
|
||||
|
||||
//! return object. delete afterwards. This will not cache the object
|
||||
virtual uint8 *get_object(uint32 index);
|
||||
|
||||
//! get size of object
|
||||
virtual uint32 get_size(uint32 index) const;
|
||||
|
||||
//! return object as SeekableReadStream. Delete the SeekableReadStream afterwards,
|
||||
//! but DON'T delete/modify the buffer it points to.
|
||||
virtual Common::SeekableReadStream *get_datasource(uint32 index);
|
||||
|
||||
protected:
|
||||
Std::vector<uint8 *> _objects;
|
||||
};
|
||||
|
||||
} // End of namespace Ultima8
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
227
engines/ultima/ultima8/filesys/savegame.cpp
Normal file
227
engines/ultima/ultima8/filesys/savegame.cpp
Normal file
@@ -0,0 +1,227 @@
|
||||
/* 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 "ultima/ultima8/filesys/savegame.h"
|
||||
#include "common/bufferedstream.h"
|
||||
#include "common/compression/unzip.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima8 {
|
||||
|
||||
#define SAVEGAME_IDENT MKTAG('V', 'M', 'U', '8')
|
||||
#define PKZIP_IDENT MKTAG('P', 'K', 3, 4)
|
||||
#define SAVEGAME_VERSION 6
|
||||
#define SAVEGAME_MIN_VERSION 2
|
||||
|
||||
class FileEntryArchive : public Common::Archive {
|
||||
struct FileEntry {
|
||||
uint _offset;
|
||||
uint _size;
|
||||
FileEntry() : _offset(0), _size(0) {}
|
||||
};
|
||||
private:
|
||||
typedef Common::HashMap<Common::Path, FileEntry, Common::Path::IgnoreCase_Hash, Common::Path::IgnoreCase_EqualTo> IndexMap;
|
||||
IndexMap _index;
|
||||
Common::SeekableReadStream *_file;
|
||||
|
||||
public:
|
||||
FileEntryArchive(Common::SeekableReadStream *rs);
|
||||
~FileEntryArchive() override;
|
||||
|
||||
// Common::Archive API implementation
|
||||
bool hasFile(const Common::Path &path) const override;
|
||||
int listMembers(Common::ArchiveMemberList &list) const override;
|
||||
const Common::ArchiveMemberPtr getMember(const Common::Path &path) const override;
|
||||
Common::SeekableReadStream *createReadStreamForMember(const Common::Path &path) const override;
|
||||
};
|
||||
|
||||
FileEntryArchive::FileEntryArchive(Common::SeekableReadStream *rs) : _file(rs) {
|
||||
// Load the index
|
||||
uint count = _file->readUint16LE();
|
||||
|
||||
for (uint idx = 0; idx < count; ++idx) {
|
||||
char name[12];
|
||||
_file->read(name, 12);
|
||||
name[11] = '\0';
|
||||
|
||||
FileEntry fe;
|
||||
fe._size = _file->readUint32LE();
|
||||
fe._offset = _file->pos();
|
||||
|
||||
_index[Common::Path(name, Common::Path::kNoSeparator)] = fe;
|
||||
_file->skip(fe._size);
|
||||
}
|
||||
}
|
||||
|
||||
FileEntryArchive::~FileEntryArchive() {
|
||||
}
|
||||
|
||||
bool FileEntryArchive::hasFile(const Common::Path &path) const {
|
||||
return _index.contains(path);
|
||||
}
|
||||
|
||||
int FileEntryArchive::listMembers(Common::ArchiveMemberList &list) const {
|
||||
list.clear();
|
||||
for (const auto &member : _index)
|
||||
list.push_back(Common::ArchiveMemberPtr(new Common::GenericArchiveMember(member._key, *this)));
|
||||
|
||||
return list.size();
|
||||
}
|
||||
|
||||
const Common::ArchiveMemberPtr FileEntryArchive::getMember(const Common::Path &path) const {
|
||||
if (!hasFile(path))
|
||||
return nullptr;
|
||||
|
||||
return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(path, *this));
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *FileEntryArchive::createReadStreamForMember(const Common::Path &path) const {
|
||||
assert(_index.contains(path));
|
||||
|
||||
const FileEntry &fe = _index[path];
|
||||
uint8 *data = (uint8 *)malloc(fe._size);
|
||||
_file->seek(fe._offset);
|
||||
_file->read(data, fe._size);
|
||||
|
||||
return new Common::MemoryReadStream(data, fe._size, DisposeAfterUse::YES);
|
||||
}
|
||||
|
||||
SavegameReader::SavegameReader(Common::SeekableReadStream *rs, bool metadataOnly) : _archive(nullptr), _version(0) {
|
||||
// Validate the identifier for a valid savegame
|
||||
uint32 ident = rs->readUint32LE();
|
||||
if (ident == SAVEGAME_IDENT) {
|
||||
_version = rs->readUint32LE();
|
||||
|
||||
if (!MetaEngine::readSavegameHeader(rs, &_header))
|
||||
return;
|
||||
|
||||
if (metadataOnly)
|
||||
return;
|
||||
|
||||
_archive = new FileEntryArchive(rs);
|
||||
} else if (SWAP_BYTES_32(ident) == PKZIP_IDENT) {
|
||||
// Note: Pentagram save description is the zip global comment
|
||||
_header.description = "Pentagram Save";
|
||||
|
||||
// Hack to pull the comment if length < 255
|
||||
char data[256];
|
||||
uint16 size = sizeof(data);
|
||||
rs->seek(-size, SEEK_END);
|
||||
rs->read(data, size);
|
||||
for (uint16 i = size; i >= 2; i--) {
|
||||
uint16 length = size - i;
|
||||
if (data[i - 2] == length && data[i - 1] == 0) {
|
||||
if (length > 0)
|
||||
_header.description = Common::String(data + i, length);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *stream = wrapBufferedSeekableReadStream(rs, 4096, DisposeAfterUse::NO);
|
||||
_archive = Common::makeZipArchive(stream);
|
||||
if (!_archive)
|
||||
return;
|
||||
|
||||
Common::ArchiveMemberPtr member = _archive->getMember("VERSION");
|
||||
if (member) {
|
||||
_version = member->createReadStream()->readUint32LE();
|
||||
_header.version = _version;
|
||||
}
|
||||
|
||||
if (metadataOnly) {
|
||||
delete _archive;
|
||||
_archive = nullptr;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SavegameReader::~SavegameReader() {
|
||||
if (_archive)
|
||||
delete _archive;
|
||||
}
|
||||
|
||||
SavegameReader::State SavegameReader::isValid() const {
|
||||
if (_version == 0)
|
||||
return SAVE_CORRUPT;
|
||||
else if (_version < SAVEGAME_MIN_VERSION)
|
||||
return SAVE_OUT_OF_DATE;
|
||||
else if (_version > SAVEGAME_VERSION)
|
||||
return SAVE_TOO_RECENT;
|
||||
|
||||
return SAVE_VALID;
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *SavegameReader::getDataSource(const Common::Path &name) {
|
||||
assert(_archive);
|
||||
|
||||
return _archive->createReadStreamForMember(name);
|
||||
}
|
||||
|
||||
|
||||
SavegameWriter::SavegameWriter(Common::WriteStream *ws) : _file(ws) {
|
||||
assert(_file);
|
||||
}
|
||||
|
||||
SavegameWriter::~SavegameWriter() {
|
||||
}
|
||||
|
||||
bool SavegameWriter::finish() {
|
||||
// Write ident and savegame version
|
||||
_file->writeUint32LE(SAVEGAME_IDENT);
|
||||
_file->writeUint32LE(SAVEGAME_VERSION);
|
||||
|
||||
// Iterate through writing out the files
|
||||
_file->writeUint16LE(_index.size());
|
||||
for (uint idx = 0; idx < _index.size(); ++idx) {
|
||||
// Set up a 12 byte space containing the resource name
|
||||
FileEntry &fe = _index[idx];
|
||||
char name[12];
|
||||
Common::fill(&name[0], &name[12], '\0');
|
||||
strncpy(name, fe._name.c_str(), 11);
|
||||
|
||||
// Write out name, size, and data
|
||||
_file->write(name, 12);
|
||||
_file->writeUint32LE(fe.size());
|
||||
_file->write(&fe[0], fe.size());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SavegameWriter::writeFile(const Std::string &name, const uint8 *data, uint32 size) {
|
||||
assert(name.size() <= 11);
|
||||
_index.push_back(FileEntry());
|
||||
|
||||
FileEntry &fe = _index.back();
|
||||
fe._name = name;
|
||||
fe.resize(size);
|
||||
Common::copy(data, data + size, &fe[0]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SavegameWriter::writeFile(const Std::string &name, Common::MemoryWriteStreamDynamic *buf) {
|
||||
return writeFile(name, buf->getData(), buf->pos());
|
||||
}
|
||||
|
||||
} // End of namespace Ultima8
|
||||
} // End of namespace Ultima
|
||||
90
engines/ultima/ultima8/filesys/savegame.h
Normal file
90
engines/ultima/ultima8/filesys/savegame.h
Normal file
@@ -0,0 +1,90 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ULTIMA8_FILESYS_SAVEGAME_H
|
||||
#define ULTIMA8_FILESYS_SAVEGAME_H
|
||||
|
||||
#include "ultima/shared/std/string.h"
|
||||
#include "common/hashmap.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/memstream.h"
|
||||
#include "engines/metaengine.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima8 {
|
||||
|
||||
class ZipFile;
|
||||
class IDataSource;
|
||||
|
||||
class SavegameReader {
|
||||
private:
|
||||
ExtendedSavegameHeader _header;
|
||||
Common::Archive *_archive;
|
||||
uint32 _version;
|
||||
public:
|
||||
explicit SavegameReader(Common::SeekableReadStream *rs, bool metadataOnly = false);
|
||||
~SavegameReader();
|
||||
|
||||
enum State { SAVE_CORRUPT, SAVE_VALID, SAVE_OUT_OF_DATE, SAVE_TOO_RECENT };
|
||||
State isValid() const;
|
||||
|
||||
uint32 getVersion() const { return _version; }
|
||||
|
||||
Std::string getDescription() const { return _header.description; }
|
||||
|
||||
/**
|
||||
* Get an entry/section within the save
|
||||
*/
|
||||
Common::SeekableReadStream *getDataSource(const Common::Path &name);
|
||||
};
|
||||
|
||||
class SavegameWriter {
|
||||
class FileEntry : public Common::Array<byte> {
|
||||
public:
|
||||
Std::string _name;
|
||||
FileEntry() : Common::Array<byte>() {}
|
||||
};
|
||||
private:
|
||||
Common::WriteStream *_file;
|
||||
Common::Array<FileEntry> _index;
|
||||
public:
|
||||
explicit SavegameWriter(Common::WriteStream *ws);
|
||||
virtual ~SavegameWriter();
|
||||
|
||||
//! write a file to the savegame
|
||||
//! \param name name of the file
|
||||
//! \param data the data
|
||||
//! \param size (in bytes) of data
|
||||
bool writeFile(const Std::string &name, const uint8 *data, uint32 size);
|
||||
|
||||
//! write a file to the savegame from a memory stream
|
||||
//! \param name name of the file
|
||||
//! \param buf the MemoryWriteStreamDynamic to save
|
||||
bool writeFile(const Std::string &name, Common::MemoryWriteStreamDynamic *buf);
|
||||
|
||||
//! finish savegame
|
||||
bool finish();
|
||||
};
|
||||
|
||||
} // End of namespace Ultima8
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
106
engines/ultima/ultima8/filesys/u8_save_file.cpp
Normal file
106
engines/ultima/ultima8/filesys/u8_save_file.cpp
Normal file
@@ -0,0 +1,106 @@
|
||||
/* 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 "common/memstream.h"
|
||||
#include "ultima/ultima8/misc/debugger.h"
|
||||
|
||||
#include "ultima/ultima8/filesys/u8_save_file.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima8 {
|
||||
|
||||
U8SaveFile::U8SaveFile(Common::SeekableReadStream *rs) : _rs(rs) {
|
||||
_valid = isU8SaveFile(_rs);
|
||||
|
||||
if (_valid)
|
||||
_valid = readMetadata();
|
||||
}
|
||||
|
||||
U8SaveFile::~U8SaveFile() {
|
||||
delete _rs;
|
||||
}
|
||||
|
||||
//static
|
||||
bool U8SaveFile::isU8SaveFile(Common::SeekableReadStream *rs) {
|
||||
rs->seek(0);
|
||||
char buf[24];
|
||||
rs->read(buf, 23);
|
||||
buf[23] = '\0';
|
||||
|
||||
return (strncmp(buf, "Ultima 8 SaveGame File.", 23) == 0);
|
||||
}
|
||||
|
||||
bool U8SaveFile::readMetadata() {
|
||||
_rs->seek(0x18);
|
||||
uint16 count = _rs->readUint16LE();
|
||||
|
||||
for (unsigned int i = 0; i < count; ++i) {
|
||||
uint32 namelen = _rs->readUint32LE();
|
||||
char *name = new char[namelen];
|
||||
_rs->read(name, namelen);
|
||||
|
||||
FileEntry fe;
|
||||
fe._size = _rs->readUint32LE();
|
||||
fe._offset = _rs->pos();
|
||||
|
||||
_map[Common::String(name)] = fe;
|
||||
delete[] name;
|
||||
_rs->skip(fe._size); // skip data
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool U8SaveFile::hasFile(const Common::Path &path) const {
|
||||
return _map.contains(path.toString());
|
||||
}
|
||||
|
||||
int U8SaveFile::listMembers(Common::ArchiveMemberList& list) const {
|
||||
list.clear();
|
||||
for (const auto &member : _map) {
|
||||
list.push_back(Common::ArchiveMemberPtr(new Common::GenericArchiveMember(member._key, *this)));
|
||||
}
|
||||
|
||||
return list.size();
|
||||
}
|
||||
|
||||
const Common::ArchiveMemberPtr U8SaveFile::getMember(const Common::Path& path) const {
|
||||
if (!hasFile(path))
|
||||
return nullptr;
|
||||
|
||||
Common::String name = path.toString();
|
||||
return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(name, *this));
|
||||
}
|
||||
|
||||
Common::SeekableReadStream* U8SaveFile::createReadStreamForMember(const Common::Path& path) const {
|
||||
if (!hasFile(path))
|
||||
return nullptr;
|
||||
|
||||
const FileEntry &fe = _map[path.toString()];
|
||||
uint8 *data = (uint8 *)malloc(fe._size);
|
||||
_rs->seek(fe._offset);
|
||||
_rs->read(data, fe._size);
|
||||
|
||||
return new Common::MemoryReadStream(data, fe._size, DisposeAfterUse::YES);
|
||||
}
|
||||
|
||||
} // End of namespace Ultima8
|
||||
} // End of namespace Ultima
|
||||
73
engines/ultima/ultima8/filesys/u8_save_file.h
Normal file
73
engines/ultima/ultima8/filesys/u8_save_file.h
Normal file
@@ -0,0 +1,73 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ULTIMA8_FILESYS_U8SAVEFILE_H
|
||||
#define ULTIMA8_FILESYS_U8SAVEFILE_H
|
||||
|
||||
#include "common/archive.h"
|
||||
|
||||
#include "ultima/shared/std/containers.h"
|
||||
#include "ultima/shared/std/string.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima8 {
|
||||
|
||||
class U8SaveFile : public Common::Archive {
|
||||
public:
|
||||
//! create U8SaveFile from datasource; U8SaveFile takes ownership of ds
|
||||
//! and deletes it when destructed
|
||||
explicit U8SaveFile(Common::SeekableReadStream *rs);
|
||||
~U8SaveFile() override;
|
||||
|
||||
//! Check if constructed object is indeed a valid archive
|
||||
bool isValid() const {
|
||||
return _valid;
|
||||
}
|
||||
|
||||
// Common::Archive API implementation
|
||||
bool hasFile(const Common::Path &path) const override;
|
||||
int listMembers(Common::ArchiveMemberList &list) const override;
|
||||
const Common::ArchiveMemberPtr getMember(const Common::Path &path) const override;
|
||||
Common::SeekableReadStream *createReadStreamForMember(const Common::Path &path) const override;
|
||||
|
||||
static bool isU8SaveFile(Common::SeekableReadStream *rs);
|
||||
|
||||
protected:
|
||||
Common::SeekableReadStream *_rs;
|
||||
bool _valid;
|
||||
|
||||
struct FileEntry {
|
||||
uint32 _offset;
|
||||
uint32 _size;
|
||||
FileEntry() : _offset(0), _size(0) {}
|
||||
};
|
||||
|
||||
typedef Common::HashMap<Common::String, FileEntry, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> U8SaveFileMap;
|
||||
U8SaveFileMap _map;
|
||||
|
||||
private:
|
||||
bool readMetadata();
|
||||
};
|
||||
|
||||
} // End of namespace Ultima8
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user