Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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