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.
This commit is contained in:
Sven Hesse 2012-06-28 22:54:05 +02:00
parent 83896dea3e
commit 4fc3a88c5f
7 changed files with 88 additions and 38 deletions

View File

@ -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);
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(), 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);
ani = _vm->_dataIO->getFile(alternateFileName);
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() {

View File

@ -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];

View File

@ -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);
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
// File doesn't exist, try to open the big-endian'd alternate file
Common::String alternateFileName = fileName;
alternateFileName.setChar('_', 0);
Common::String alternateFileName = fileName;
alternateFileName.setChar('_', 0);
dec = _vm->_dataIO->getFile(alternateFileName);
if (dec) {
Common::SeekableSubReadStreamEndian sub(dec, 0, dec->size(), true, DisposeAfterUse::YES);
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() {

View File

@ -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:

View File

@ -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;

View File

@ -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<uint16>(_width , c->right + 1);

View File

@ -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