/* 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 2 * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * $URL$ * $Id$ * */ #include "common/endian.h" #include "common/savefile.h" #include "gob/gob.h" #include "gob/saveload.h" #include "gob/draw.h" namespace Gob { TempSprite::TempSprite() { _sprite = 0; _width = _height = 0; _size = -1; memset(_palette, 0, 768); } TempSprite::~TempSprite() { delete[] _sprite; } int TempSprite::getSpriteIndex(int32 size) const { if (size < -1000) size += 1000; return -size - 1; } bool TempSprite::getSpritePalette(int32 size) const { return size < -1000; } bool TempSprite::getProperties(int16 dataVar, int32 size, int32 offset, int &index, bool &palette) const { if (size >= 0) { warning("Invalid index (%d)", size); return false; } index = getSpriteIndex(size); palette = getSpritePalette(size); if ((index < 0) || (index >= SPRITES_COUNT)) { warning("Index out of range (%d)", index); return false; } return true; } int32 TempSprite::getSize() const { return _size; } bool TempSprite::saveSprite(const SurfaceDesc &surfDesc) { delete[] _sprite; _width = surfDesc.getWidth(); _height = surfDesc.getHeight(); _size = _width * _height; _sprite = new byte[_size]; memcpy(_sprite, surfDesc.getVidMem(), _size); return true; } bool TempSprite::savePalette(const Video::Color *palette) { memcpy((byte *) _palette, (byte *) palette, 768); return true; } bool TempSprite::loadSprite(SurfaceDesc &surfDesc) { if (!_sprite) { warning("No sprite saved"); return false; } if (_size != (surfDesc.getWidth() * surfDesc.getHeight())) { warning("Dimensions don't match (%dx%d - %dx%d", _width, _height, surfDesc.getWidth(), surfDesc.getHeight()); return false; } memcpy(surfDesc.getVidMem(), _sprite, _size); return true; } bool TempSprite::loadPalette(Video::Color *palette) { memcpy((byte *) palette, (byte *) _palette, 768); return true; } bool TempSprite::toBuffer(byte *buffer, int32 size, bool palette) const { int32 haveSize = _size + (palette ? 768 : 0); if (size != haveSize) { warning("Sizes don't match (%d != %d)", size, haveSize); return false; } if (palette) { memcpy(buffer, (byte *) _palette, 768); buffer += 768; } memcpy(buffer, _sprite, _size); return true; } bool TempSprite::fromBuffer(const byte *buffer, int32 size, bool palette) { if (palette) { memcpy((byte *) _palette, buffer, 768); buffer += 768; size -= 768; } _size = size; delete[] _sprite; _sprite = new byte[_size]; memcpy(_sprite, buffer, _size); return true; } PlainSave::PlainSave(Endianness endianness) : _endianness(endianness) { } PlainSave::~PlainSave() { } bool PlainSave::save(int16 dataVar, int32 size, int32 offset, const char *name, const Variables *variables) { if ((size <= 0) || (offset != 0)) { warning("Invalid size (%d) or offset (%d)", size, offset); return false; } byte *vars = new byte[size]; byte *varSizes = new byte[size]; if (!variables->copyTo(dataVar, vars, varSizes, size)) { delete[] vars; delete[] varSizes; warning("dataVar (%d) or size (%d) out of range", dataVar, size); return false; } bool result = save(0, size, offset, name, vars, varSizes); delete[] vars; delete[] varSizes; return result; } bool PlainSave::load(int16 dataVar, int32 size, int32 offset, const char *name, Variables *variables) { if ((size <= 0) || (offset != 0)) { warning("Invalid size (%d) or offset (%d)", size, offset); return false; } byte *vars = new byte[size]; byte *varSizes = new byte[size]; bool result = load(0, size, offset, name, vars, varSizes); if (result && variables) { if (!variables->copyFrom(dataVar, vars, varSizes, size)) { delete[] vars; delete[] varSizes; warning("dataVar (%d) or size (%d) out of range", dataVar, size); return false; } } delete[] vars; delete[] varSizes; return result; } bool PlainSave::save(int16 dataVar, int32 size, int32 offset, const char *name, const byte *variables, const byte *variableSizes) const { if ((size <= 0) || (offset != 0)) { warning("Invalid size (%d) or offset (%d)", size, offset); return false; } Common::SaveFileManager *saveMan = g_system->getSavefileManager(); Common::OutSaveFile *out = saveMan->openForSaving(name); if (!out) { warning("Can't open file \"%s\" for writing", name); return false; } bool retVal; retVal = SaveLoad::saveDataEndian(*out, dataVar, size, variables, variableSizes, _endianness); out->finalize(); if (out->ioFailed()) { warning("Can't write to file \"%s\"", name); retVal = false; } delete out; return retVal; } bool PlainSave::load(int16 dataVar, int32 size, int32 offset, const char *name, byte *variables, byte *variableSizes) const { if ((size <= 0) || (offset != 0)) { warning("Invalid size (%d) or offset (%d)", size, offset); return false; } Common::SaveFileManager *saveMan = g_system->getSavefileManager(); Common::InSaveFile *in = saveMan->openForLoading(name); if (!in) { warning("Can't open file \"%s\" for reading", name); return false; } bool retVal = SaveLoad::loadDataEndian(*in, dataVar, size, variables, variableSizes, _endianness); delete in; return retVal; } StagedSave::StagedSave(Endianness endianness) : _endianness(endianness) { _mode = kModeNone; _name = 0; _loaded = false; } StagedSave::~StagedSave() { clear(); } void StagedSave::addStage(int32 size, bool endianed) { int32 offset = 0; if (!_stages.empty()) offset = _stages[_stages.size() - 1].offset + _stages[_stages.size() - 1].size; Stage stage(size, offset, endianed); _stages.push_back(stage); } int StagedSave::findStage(int16 dataVar, int32 size, int32 offset) const { for (uint i = 0; i < _stages.size(); i++) if ((_stages[i].size == size) && (_stages[i].offset == offset)) return i; return -1; } bool StagedSave::allSaved() const { for (uint i = 0; i < _stages.size(); i++) if (!_stages[i].bufVar) return false; return true; } uint32 StagedSave::getSize() const { uint32 size = 0; for (uint i = 0; i < _stages.size(); i++) { if (_stages[i].endianed) size += 2 * _stages[i].size; else size += _stages[i].size; } return size; } void StagedSave::clear() { for (uint i = 0; i < _stages.size(); i++) { delete[] _stages[i].bufVar; delete[] _stages[i].bufVarSizes; _stages[i].bufVar = 0; _stages[i].bufVarSizes = 0; } delete[] _name; _name = 0; _mode = kModeNone; _loaded = false; } void StagedSave::assertMode(Mode mode, const char *name) { if ((_mode != mode) || ((name[0] != '\0') && strcmp(_name, name))) { clear(); _mode = mode; _name = new char[strlen(name) + 1]; strcpy(_name, name); } } bool StagedSave::save(int16 dataVar, int32 size, int32 offset, const char *name, const Variables *variables) { if ((dataVar < 0) || (size <= 0) || (offset < 0)) { warning("Invalid dataVar (%d), size (%d) or offset (%d)", dataVar, size, offset); return false; } byte *vars = 0, *varSizes = 0; if (variables) { vars = new byte[size]; varSizes = new byte[size]; if (!variables->copyTo(dataVar, vars, varSizes, size)) { delete[] vars; delete[] varSizes; warning("dataVar (%d) or size (%d) out of range", dataVar, size); return false; } } bool result = save(0, size, offset, name, vars, varSizes); delete[] vars; delete[] varSizes; return result; } bool StagedSave::load(int16 dataVar, int32 size, int32 offset, const char *name, Variables *variables) { if ((dataVar < 0) || (size <= 0) || (offset < 0)) { warning("Invalid dataVar (%d), size (%d) or offset (%d)", dataVar, size, offset); return false; } byte *vars = new byte[size]; byte *varSizes = new byte[size]; bool result = load(0, size, offset, name, vars, varSizes); if (result && variables) { if (!variables->copyFrom(dataVar, vars, varSizes, size)) { delete[] vars; delete[] varSizes; warning("dataVar (%d) or size (%d) out of range", dataVar, size); return false; } } delete[] vars; delete[] varSizes; return result; } bool StagedSave::save(int16 dataVar, int32 size, int32 offset, const char *name, const byte *variables, const byte *variableSizes) { if ((dataVar < 0) || (size <= 0) || (offset < 0)) { warning("Invalid dataVar (%d), size (%d) or offset (%d)", dataVar, size, offset); return false; } int stage = findStage(dataVar, size, offset); if (stage == -1) { warning("Invalid saving procedure"); return false; } if (!variables || (_stages[stage].endianed && !variableSizes)) { warning("Missing data"); return false; } assertMode(kModeSave, name); _stages[stage].bufVar = new byte[size]; memcpy(_stages[stage].bufVar, variables + dataVar, size); if (_stages[stage].endianed) { _stages[stage].bufVarSizes = new byte[size]; memcpy(_stages[stage].bufVarSizes, variableSizes + dataVar, size); } if (allSaved()) { bool result = write(); clear(); return result; } return true; } bool StagedSave::load(int16 dataVar, int32 size, int32 offset, const char *name, byte *variables, byte *variableSizes) { if ((dataVar < 0) || (size <= 0) || (offset < 0)) { warning("Invalid dataVar (%d), size (%d) or offset (%d)", dataVar, size, offset); return false; } int stage = findStage(dataVar, size, offset); if (stage == -1) { warning("Invalid loading procedure"); return false; } assertMode(kModeLoad, name); if (!_loaded) { if (!read()) { clear(); return false; } } if (variables) memcpy(variables + dataVar, _stages[stage].bufVar, size); if (_stages[stage].endianed && variableSizes) memcpy(variableSizes + dataVar, _stages[stage].bufVarSizes, size); return true; } bool StagedSave::write() const { Common::SaveFileManager *saveMan = g_system->getSavefileManager(); Common::OutSaveFile *out = saveMan->openForSaving(_name); if (!out) { warning("Can't open file \"%s\" for writing", _name); return false; } bool result = true; for (uint i = 0; (i < _stages.size()) && result; i++) { if (!_stages[i].endianed) { uint32 written = out->write(_stages[i].bufVar, _stages[i].size); result = (written == ((uint32) _stages[i].size)); if (!result) warning("Can't write data: requested %d, wrote %d", _stages[i].size, written); } else result = SaveLoad::saveDataEndian(*out, 0, _stages[i].size, _stages[i].bufVar, _stages[i].bufVarSizes, _endianness); } if (result) { out->finalize(); if (out->ioFailed()) { warning("Can't write to file \"%s\"", _name); result = false; } } delete out; return result; } bool StagedSave::read() { Common::SaveFileManager *saveMan = g_system->getSavefileManager(); Common::InSaveFile *in = saveMan->openForLoading(_name); if (!in) { warning("Can't open file \"%s\" for reading", _name); return false; } int32 saveSize = getSize(); if (in->size() != saveSize) { warning("Wrong size (%d != %d)", in->size(), saveSize); return false; } bool result = true; for (uint i = 0; ((i < _stages.size()) && result); i++) { _stages[i].bufVar = new byte[_stages[i].size]; if (!_stages[i].endianed) { uint32 nRead = in->read(_stages[i].bufVar, _stages[i].size); result = (nRead == ((uint32) _stages[i].size)); if (!result) warning("Can't read data: requested %d, got %d", _stages[i].size, nRead); } else { _stages[i].bufVarSizes = new byte[_stages[i].size]; result = SaveLoad::loadDataEndian(*in, 0, _stages[i].size, _stages[i].bufVar, _stages[i].bufVarSizes, _endianness); } } if (result) _loaded = true; delete in; return result; } PagedBuffer::PagedBuffer(uint32 pageSize) { _size = 0; _pageSize = pageSize; } PagedBuffer::~PagedBuffer() { clear(); } bool PagedBuffer::empty() const { return _pages.empty(); } uint32 PagedBuffer::getSize() const { return _size; } void PagedBuffer::clear() { for (uint i = 0; i < _pages.size(); i++) delete[] _pages[i]; _pages.clear(); _size = 0; } bool PagedBuffer::write(const byte *buffer, uint32 size, uint32 offset) { grow(size, offset); uint page = offset / _pageSize; while (size > 0) { if (!_pages[page]) _pages[page] = new byte[_pageSize]; uint32 pStart = offset % _pageSize; uint32 n = MIN(size, _pageSize - pStart); memcpy(_pages[page] + pStart, buffer, n); buffer += n; offset += n; size -= n; page++; } return true; } bool PagedBuffer::read(byte *buffer, uint32 size, uint32 offset) const { uint page = offset / _pageSize; while (size > 0) { if (offset >= _size) { memset(buffer, 0, size); break; } uint32 pStart = offset % _pageSize; uint32 n = MIN(MIN(size, _pageSize - pStart), _size - offset); if (_pages[page]) memcpy(buffer, _pages[page] + pStart, n); else memset(buffer, 0, n); buffer += n; offset += n; size -= n; page++; } return true; } uint32 PagedBuffer::writeToStream(Common::WriteStream &out) const { for (uint i = 0; i < _pages.size(); i++) { if (!_pages[i]) { for (uint32 j = 0; j < _pageSize; j++) out.writeByte(0); } else out.write(_pages[i], _pageSize); } return _size; } uint32 PagedBuffer::readFromStream(Common::ReadStream &in) { clear(); while (!in.eos()) { byte *buffer = new byte[_pageSize]; _size += in.read(buffer, _pageSize); _pages.push_back(buffer); } return _size; } void PagedBuffer::grow(uint32 size, uint32 offset) { uint32 eSize = offset + size; while (_size < eSize) { _pages.push_back(0); _size += MIN(_pageSize, eSize - _size); } } SaveLoad::SaveLoad(GobEngine *vm, const char *targetName) : _vm(vm) { _targetName = new char[strlen(targetName) + 1]; strcpy(_targetName, targetName); } SaveLoad::~SaveLoad() { delete[] _targetName; } int32 SaveLoad::getSize(const char *fileName) { int type; type = getSaveType(stripPath(fileName)); if (type == -1) return -1; debugC(3, kDebugSaveLoad, "Requested size of save file \"%s\" (type %d)", fileName, type); return getSizeVersioned(type); } bool SaveLoad::load(const char *fileName, int16 dataVar, int32 size, int32 offset) { int type; type = getSaveType(stripPath(fileName)); if (type == -1) return false; debugC(3, kDebugSaveLoad, "Requested loading of save file \"%s\" (type %d) - %d, %d, %d", fileName, type, dataVar, size, offset); return loadVersioned(type, dataVar, size, offset); } bool SaveLoad::save(const char *fileName, int16 dataVar, int32 size, int32 offset) { int type; type = getSaveType(stripPath(fileName)); if (type == -1) return false; debugC(3, kDebugSaveLoad, "Requested saving of save file \"%s\" (type %d) - %d, %d, %d", fileName, type, dataVar, size, offset); return saveVersioned(type, dataVar, size, offset); } const char *SaveLoad::stripPath(const char *fileName) { const char *backSlash; if ((backSlash = strrchr(fileName, '\\'))) return backSlash + 1; return fileName; } char *SaveLoad::setCurrentSlot(char *destName, int slot) { char *slotBase = destName + strlen(destName) - 2; snprintf(slotBase, 3, "%02d", slot); return destName; } void SaveLoad::buildIndex(byte *buffer, char *name, int n, int32 size, int32 offset) { Common::SaveFileManager *saveMan = g_system->getSavefileManager(); Common::InSaveFile *in; for (int i = 0; i < n; i++, buffer += size) { in = saveMan->openForLoading(setCurrentSlot(name, i)); if (in) { in->seek(offset); in->read(buffer, size); delete in; } else memset(buffer, 0, size); } } bool SaveLoad::fromEndian(byte *buf, const byte *sizes, uint32 count, Endianness endianness) { bool LE = (endianness == kEndiannessLE); while (count-- > 0) { if (*sizes == 3) *((uint32 *) buf) = LE ? READ_LE_UINT32(buf) : READ_BE_UINT32(buf); else if (*sizes == 1) *((uint16 *) buf) = LE ? READ_LE_UINT16(buf) : READ_BE_UINT16(buf); else if (*sizes != 0) { warning("SaveLoad::fromEndian(): Corrupted variables sizes"); return false; } count -= *sizes; buf += *sizes + 1; sizes += *sizes + 1; } return true; } bool SaveLoad::toEndian(byte *buf, const byte *sizes, uint32 count, Endianness endianness) { while (count-- > 0) { if (*sizes == 3) { if (endianness == kEndiannessLE) WRITE_LE_UINT32(buf, *((uint32 *) buf)); else WRITE_BE_UINT32(buf, *((uint32 *) buf)); } else if (*sizes == 1) { if (endianness == kEndiannessLE) WRITE_LE_UINT16(buf, *((uint16 *) buf)); else WRITE_BE_UINT16(buf, *((uint16 *) buf)); } else if (*sizes != 0) { warning("SaveLoad::toEndian(): Corrupted variables sizes"); return false; } count -= *sizes; buf += *sizes + 1; sizes += *sizes + 1; } return true; } uint32 SaveLoad::read(Common::ReadStream &in, byte *buf, byte *sizes, uint32 count) { uint32 nRead; nRead = in.read(buf, count); if (nRead != count) { warning("Can't read data: requested %d, got %d", count, nRead); return 0; } nRead = in.read(sizes, count); if (nRead != count) { warning("Can't read data sizes: requested %d, got %d", count, nRead); return 0; } return count; } uint32 SaveLoad::write(Common::WriteStream &out, const byte *buf, const byte *sizes, uint32 count) { uint32 written; written = out.write(buf, count); if (written != count) { warning("Can't write data: requested %d, wrote %d", count, written); return 0; } written = out.write(sizes, count); if (written != count) { warning("Can't write data: requested %d, wrote %d", count, written); return 0; } return count; } bool SaveLoad::loadDataEndian(Common::ReadStream &in, int16 dataVar, uint32 size, byte *variables, byte *variableSizes, Endianness endianness) { bool retVal = false; byte *varBuf = new byte[size]; byte *sizeBuf = new byte[size]; assert(varBuf && sizeBuf); if (read(in, varBuf, sizeBuf, size) == size) { if (fromEndian(varBuf, sizeBuf, size, endianness)) { memcpy(variables + dataVar, varBuf, size); memcpy(variableSizes + dataVar, sizeBuf, size); retVal = true; } } delete[] varBuf; delete[] sizeBuf; return retVal; } bool SaveLoad::saveDataEndian(Common::WriteStream &out, int16 dataVar, uint32 size, const byte *variables, const byte *variableSizes, Endianness endianness) { bool retVal = false; byte *varBuf = new byte[size]; byte *sizeBuf = new byte[size]; assert(varBuf && sizeBuf); memcpy(varBuf, variables + dataVar, size); memcpy(sizeBuf, variableSizes + dataVar, size); if (toEndian(varBuf, sizeBuf, size, endianness)) if (write(out, varBuf, sizeBuf, size) == size) retVal = true; delete[] varBuf; delete[] sizeBuf; return retVal; } SaveLoad::SaveMode SaveLoad::getSaveMode(const char *fileName) { return kSaveModeNone; } int SaveLoad::getSaveType(const char *fileName) { return -1; } int32 SaveLoad::getSizeVersioned(int type) { return -1; } bool SaveLoad::loadVersioned(int type, int16 dataVar, int32 size, int32 offset) { return false; } bool SaveLoad::saveVersioned(int type, int16 dataVar, int32 size, int32 offset) { return false; } } // End of namespace Gob