From 4fc3a88c5f0b053323aeaeac658dafb8e4606662 Mon Sep 17 00:00:00 2001 From: Sven Hesse Date: Thu, 28 Jun 2012 22:54:05 +0200 Subject: GOB: Add support for different methods of handling Endianness The Once Upon A Time games handle endianness different in ANI, DEC and RXY files than Geisha does. We need to support both approaches. --- engines/gob/anifile.cpp | 34 +++++++++++++++++++++------------- engines/gob/cmpfile.cpp | 9 ++++++++- engines/gob/decfile.cpp | 42 +++++++++++++++++++++++++----------------- engines/gob/gob.cpp | 8 ++++++++ engines/gob/gob.h | 10 ++++++++++ engines/gob/rxyfile.cpp | 19 +++++++++++++------ engines/gob/rxyfile.h | 4 +++- 7 files changed, 88 insertions(+), 38 deletions(-) (limited to 'engines') diff --git a/engines/gob/anifile.cpp b/engines/gob/anifile.cpp index 2671fe0405..e6bf30f4d7 100644 --- a/engines/gob/anifile.cpp +++ b/engines/gob/anifile.cpp @@ -37,30 +37,38 @@ ANIFile::ANIFile(GobEngine *vm, const Common::String &fileName, uint16 width, uint8 bpp) : _vm(vm), _width(width), _bpp(bpp), _hasPadding(false) { - Common::SeekableReadStream *ani = _vm->_dataIO->getFile(fileName); - if (ani) { - Common::SeekableSubReadStreamEndian sub(ani, 0, ani->size(), false, DisposeAfterUse::YES); + bool bigEndian = false; + Common::String endianFileName = fileName; - load(sub, fileName); - return; - } + if ((_vm->getEndiannessMethod() == kEndiannessMethodAltFile) && + !_vm->_dataIO->hasFile(fileName)) { + // If the game has alternate big-endian files, look if one exist + + Common::String alternateFileName = fileName; + alternateFileName.setChar('_', 0); - // File doesn't exist, try to open the big-endian'd alternate file - Common::String alternateFileName = fileName; - alternateFileName.setChar('_', 0); + if (_vm->_dataIO->hasFile(alternateFileName)) { + bigEndian = true; + endianFileName = alternateFileName; + } + } else if ((_vm->getEndiannessMethod() == kEndiannessMethodBE) || + ((_vm->getEndiannessMethod() == kEndiannessMethodSystem) && + (_vm->getEndianness() == kEndiannessBE))) + // Game always little endian or it follows the system and it is big endian + bigEndian = true; - ani = _vm->_dataIO->getFile(alternateFileName); + Common::SeekableReadStream *ani = _vm->_dataIO->getFile(endianFileName); if (ani) { - Common::SeekableSubReadStreamEndian sub(ani, 0, ani->size(), true, DisposeAfterUse::YES); + Common::SeekableSubReadStreamEndian sub(ani, 0, ani->size(), bigEndian, DisposeAfterUse::YES); // The big endian version pads a few fields to even size - _hasPadding = true; + _hasPadding = bigEndian; load(sub, fileName); return; } - warning("ANIFile::ANIFile(): No such file \"%s\"", fileName.c_str()); + warning("ANIFile::ANIFile(): No such file \"%s\" (\"%s\")", endianFileName.c_str(), fileName.c_str()); } ANIFile::~ANIFile() { diff --git a/engines/gob/cmpfile.cpp b/engines/gob/cmpfile.cpp index 7b21c4c835..1cd1375879 100644 --- a/engines/gob/cmpfile.cpp +++ b/engines/gob/cmpfile.cpp @@ -21,6 +21,7 @@ */ #include "common/stream.h" +#include "common/substream.h" #include "common/str.h" #include "gob/gob.h" @@ -143,7 +144,13 @@ void CMPFile::loadCMP(Common::SeekableReadStream &cmp) { } void CMPFile::loadRXY(Common::SeekableReadStream &rxy) { - _coordinates = new RXYFile(rxy); + bool bigEndian = (_vm->getEndiannessMethod() == kEndiannessMethodBE) || + ((_vm->getEndiannessMethod() == kEndiannessMethodSystem) && + (_vm->getEndianness() == kEndiannessBE)); + + Common::SeekableSubReadStreamEndian sub(&rxy, 0, rxy.size(), bigEndian, DisposeAfterUse::NO); + + _coordinates = new RXYFile(sub); for (uint i = 0; i < _coordinates->size(); i++) { const RXYFile::Coordinates &c = (*_coordinates)[i]; diff --git a/engines/gob/decfile.cpp b/engines/gob/decfile.cpp index fb67c52627..85b4c09ca3 100644 --- a/engines/gob/decfile.cpp +++ b/engines/gob/decfile.cpp @@ -38,30 +38,38 @@ DECFile::DECFile(GobEngine *vm, const Common::String &fileName, uint16 width, uint16 height, uint8 bpp) : _vm(vm), _width(width), _height(height), _bpp(bpp), _hasPadding(false), _backdrop(0) { - Common::SeekableReadStream *dec = _vm->_dataIO->getFile(fileName); - if (dec) { - Common::SeekableSubReadStreamEndian sub(dec, 0, dec->size(), false, DisposeAfterUse::YES); - - load(sub, fileName); - return; - } - - // File doesn't exist, try to open the big-endian'd alternate file - Common::String alternateFileName = fileName; - alternateFileName.setChar('_', 0); - - dec = _vm->_dataIO->getFile(alternateFileName); - if (dec) { - Common::SeekableSubReadStreamEndian sub(dec, 0, dec->size(), true, DisposeAfterUse::YES); + bool bigEndian = false; + Common::String endianFileName = fileName; + + if ((_vm->getEndiannessMethod() == kEndiannessMethodAltFile) && + !_vm->_dataIO->hasFile(fileName)) { + // If the game has alternate big-endian files, look if one exist + + Common::String alternateFileName = fileName; + alternateFileName.setChar('_', 0); + + if (_vm->_dataIO->hasFile(alternateFileName)) { + bigEndian = true; + endianFileName = alternateFileName; + } + } else if ((_vm->getEndiannessMethod() == kEndiannessMethodBE) || + ((_vm->getEndiannessMethod() == kEndiannessMethodSystem) && + (_vm->getEndianness() == kEndiannessBE))) + // Game always little endian or it follows the system and it is big endian + bigEndian = true; + + Common::SeekableReadStream *ani = _vm->_dataIO->getFile(endianFileName); + if (ani) { + Common::SeekableSubReadStreamEndian sub(ani, 0, ani->size(), bigEndian, DisposeAfterUse::YES); // The big endian version pads a few fields to even size - _hasPadding = true; + _hasPadding = bigEndian; load(sub, fileName); return; } - warning("DECFile::DECFile(): No such file \"%s\"", fileName.c_str()); + warning("DECFile::DECFile(): No such file \"%s\" (\"%s\")", endianFileName.c_str(), fileName.c_str()); } DECFile::~DECFile() { diff --git a/engines/gob/gob.cpp b/engines/gob/gob.cpp index 02aea63377..fcf98f0355 100644 --- a/engines/gob/gob.cpp +++ b/engines/gob/gob.cpp @@ -184,6 +184,10 @@ void GobEngine::validateVideoMode(int16 videoMode) { error("Video mode 0x%X is not supported", videoMode); } +EndiannessMethod GobEngine::getEndiannessMethod() const { + return _endiannessMethod; +} + Endianness GobEngine::getEndianness() const { if ((getPlatform() == Common::kPlatformAmiga) || (getPlatform() == Common::kPlatformMacintosh) || @@ -403,6 +407,8 @@ Common::Error GobEngine::initGameParts() { // just detect some devices some of which will be always there if the music is not disabled _noMusic = MidiDriver::getMusicType(MidiDriver::detectDevice(MDT_PCSPK | MDT_MIDI | MDT_ADLIB)) == MT_NULL ? true : false; + _endiannessMethod = kEndiannessMethodSystem; + _global = new Global(this); _util = new Util(this); _dataIO = new DataIO(); @@ -433,6 +439,8 @@ Common::Error GobEngine::initGameParts() { _goblin = new Goblin_v1(this); _scenery = new Scenery_v1(this); _saveLoad = new SaveLoad_Geisha(this, _targetName.c_str()); + + _endiannessMethod = kEndiannessMethodAltFile; break; case kGameTypeFascination: diff --git a/engines/gob/gob.h b/engines/gob/gob.h index 9b919098d6..df73404596 100644 --- a/engines/gob/gob.h +++ b/engines/gob/gob.h @@ -149,6 +149,13 @@ enum Features { kFeaturesTrueColor = 1 << 7 }; +enum EndiannessMethod { + kEndiannessMethodLE, ///< Always little endian. + kEndiannessMethodBE, ///< Always big endian. + kEndiannessMethodSystem, ///< Follows system endianness. + kEndiannessMethodAltFile ///< Different endianness in alternate file. +}; + enum { kDebugFuncOp = 1 << 0, kDebugDrawOp = 1 << 1, @@ -172,6 +179,8 @@ private: int32 _features; Common::Platform _platform; + EndiannessMethod _endiannessMethod; + uint32 _pauseStart; // Engine APIs @@ -232,6 +241,7 @@ public: void pauseGame(); + EndiannessMethod getEndiannessMethod() const; Endianness getEndianness() const; Common::Platform getPlatform() const; GameType getGameType() const; diff --git a/engines/gob/rxyfile.cpp b/engines/gob/rxyfile.cpp index 9702dc8c7f..2ff8c121cd 100644 --- a/engines/gob/rxyfile.cpp +++ b/engines/gob/rxyfile.cpp @@ -21,12 +21,19 @@ */ #include "common/stream.h" +#include "common/substream.h" #include "gob/rxyfile.h" namespace Gob { RXYFile::RXYFile(Common::SeekableReadStream &rxy) : _width(0), _height(0) { + Common::SeekableSubReadStreamEndian sub(&rxy, 0, rxy.size(), false, DisposeAfterUse::NO); + + load(sub); +} + +RXYFile::RXYFile(Common::SeekableSubReadStreamEndian &rxy) : _width(0), _height(0) { load(rxy); } @@ -64,22 +71,22 @@ const RXYFile::Coordinates &RXYFile::operator[](uint i) const { return _coords[i]; } -void RXYFile::load(Common::SeekableReadStream &rxy) { +void RXYFile::load(Common::SeekableSubReadStreamEndian &rxy) { if (rxy.size() < 2) return; rxy.seek(0); - _realCount = rxy.readUint16LE(); + _realCount = rxy.readUint16(); uint16 count = (rxy.size() - 2) / 8; _coords.resize(count); for (CoordArray::iterator c = _coords.begin(); c != _coords.end(); ++c) { - c->left = rxy.readUint16LE(); - c->right = rxy.readUint16LE(); - c->top = rxy.readUint16LE(); - c->bottom = rxy.readUint16LE(); + c->left = rxy.readUint16(); + c->right = rxy.readUint16(); + c->top = rxy.readUint16(); + c->bottom = rxy.readUint16(); if (c->left != 0xFFFF) { _width = MAX(_width , c->right + 1); diff --git a/engines/gob/rxyfile.h b/engines/gob/rxyfile.h index bc9600b5b0..4fd46c5e40 100644 --- a/engines/gob/rxyfile.h +++ b/engines/gob/rxyfile.h @@ -28,6 +28,7 @@ namespace Common { class SeekableReadStream; + class SeekableSubReadStreamEndian; } namespace Gob { @@ -46,6 +47,7 @@ public: }; RXYFile(Common::SeekableReadStream &rxy); + RXYFile(Common::SeekableSubReadStreamEndian &rxy); RXYFile(uint16 width, uint16 height); ~RXYFile(); @@ -71,7 +73,7 @@ private: uint16 _height; - void load(Common::SeekableReadStream &rxy); + void load(Common::SeekableSubReadStreamEndian &rxy); }; } // End of namespace Gob -- cgit v1.2.3