Colin Snover 051366a48a SCI: Fix up Object::_baseMethod implementation
1. In SCI0/1, selectors and offsets in the method block are
   stored contiguously (all selectors, then all offsets), with a
   null separator between the two runs. All the later versions of
   SCI instead interleave selectors & offsets. Since these values
   are already being copied into a new array anyway, code for
   reading method selectors/offsets is now simplified by
   interleaving this data when it is written into _baseMethod
   for SCI0/1, so the same equation for retrieving method
   selectors/offsets can be used across all SCI versions.

2. In SCI1.1-2.1 branch, the method count was being copied into
   the first entry of the array, which meant that SCI1.1-2.1 had
   extra code for dealing with the fact that the first entry was
   not an entry. This has been fixed, and the extra code removed.

3. Data was being overread into _baseMethod in all games SCI0-2.1.
   (SCI0/1 had an extra magic value of 2, and SCI1.1-2.1 had an
   extra magic value of 3). Reviewing history, it's not clear why
   this happened, other than that it appears to have been
   introduced at 7b0760f1bc5c28abcede041a6e3930f84ff3d319. My best
   guess is that this was a confusion between byte count and record
   count, where the intent was to read an extra 2 bytes for the
   null separator in SCI0/1, but it actually read 2 records
   instead. (I do not have a guess on why SCI1.1 ended up with a
   3.) This overreading has been removed.
2017-07-15 20:10:38 -05:00

387 lines
10 KiB
C++

/* 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.
*
*/
#ifndef SCI_ENGINE_OBJECT_H
#define SCI_ENGINE_OBJECT_H
#include "common/array.h"
#include "common/serializer.h"
#include "common/textconsole.h"
#include "sci/sci.h" // for the SCI versions
#include "sci/engine/vm_types.h" // for reg_t
#include "sci/util.h"
namespace Sci {
class SegManager;
class Script;
enum infoSelectorFlags {
kInfoFlagClone = 0x0001,
#ifdef ENABLE_SCI32
/**
* When set, indicates to game scripts that a screen
* item can be updated.
*/
kInfoFlagViewVisible = 0x0008,
/**
* When set, the VM object has an associated ScreenItem in
* the rendering tree.
*/
kInfoFlagViewInserted = 0x0010,
#endif
kInfoFlagClass = 0x8000
};
enum ObjectOffsets {
kOffsetHeaderSize = 6,
kOffsetHeaderLocalVariables = 0,
kOffsetHeaderFunctionArea = 2,
kOffsetHeaderSelectorCounter = 4,
kOffsetSelectorSegment = 0,
kOffsetInfoSelectorSci0 = 4,
kOffsetNamePointerSci0 = 6,
kOffsetInfoSelectorSci11 = 14,
kOffsetNamePointerSci11 = 16
};
class Object : public Common::Serializable {
public:
Object() :
_name(NULL_REG),
_offset(getSciVersion() < SCI_VERSION_1_1 ? 0 : 5),
_isFreed(false),
_baseObj(),
_baseVars(),
_methodCount(0)
#ifdef ENABLE_SCI32
,
_propertyOffsetsSci3()
#endif
{}
Object &operator=(const Object &other) {
_name = other._name;
_baseObj = other._baseObj;
_baseMethod = other._baseMethod;
_variables = other._variables;
_methodCount = other._methodCount;
_isFreed = other._isFreed;
_offset = other._offset;
_pos = other._pos;
_baseVars = other._baseVars;
#ifdef ENABLE_SCI32
if (getSciVersion() == SCI_VERSION_3) {
_propertyOffsetsSci3 = other._propertyOffsetsSci3;
_superClassPosSci3 = other._superClassPosSci3;
_speciesSelectorSci3 = other._speciesSelectorSci3;
_infoSelectorSci3 = other._infoSelectorSci3;
_mustSetViewVisible = other._mustSetViewVisible;
}
#endif
return *this;
}
reg_t getSpeciesSelector() const {
#ifdef ENABLE_SCI32
if (getSciVersion() == SCI_VERSION_3)
return _speciesSelectorSci3;
else
#endif
return _variables[_offset];
}
void setSpeciesSelector(reg_t value) {
#ifdef ENABLE_SCI32
if (getSciVersion() == SCI_VERSION_3)
_speciesSelectorSci3 = value;
else
#endif
_variables[_offset] = value;
}
reg_t getSuperClassSelector() const {
#ifdef ENABLE_SCI32
if (getSciVersion() == SCI_VERSION_3)
return _superClassPosSci3;
else
#endif
return _variables[_offset + 1];
}
void setSuperClassSelector(reg_t value) {
#ifdef ENABLE_SCI32
if (getSciVersion() == SCI_VERSION_3)
_superClassPosSci3 = value;
else
#endif
_variables[_offset + 1] = value;
}
reg_t getInfoSelector() const {
#ifdef ENABLE_SCI32
if (getSciVersion() == SCI_VERSION_3)
return _infoSelectorSci3;
else
#endif
return _variables[_offset + 2];
}
void setInfoSelector(reg_t info) {
#ifdef ENABLE_SCI32
if (getSciVersion() == SCI_VERSION_3)
_infoSelectorSci3 = info;
else
#endif
_variables[_offset + 2] = info;
}
#ifdef ENABLE_SCI32
void setInfoSelectorFlag(infoSelectorFlags flag) {
if (getSciVersion() == SCI_VERSION_3) {
_infoSelectorSci3 |= flag;
} else {
_variables[_offset + 2] |= flag;
}
}
// NOTE: In real engine, -info- is treated as byte size
void clearInfoSelectorFlag(infoSelectorFlags flag) {
if (getSciVersion() == SCI_VERSION_3) {
_infoSelectorSci3 &= ~flag;
} else {
_variables[_offset + 2] &= ~flag;
}
}
bool isInserted() const {
return getInfoSelector().toUint16() & kInfoFlagViewInserted;
}
#endif
reg_t getNameSelector() const {
return _name;
}
// No setter for the name selector
reg_t getPropDictSelector() const {
#ifdef ENABLE_SCI32
if (getSciVersion() == SCI_VERSION_3)
// This should never occur, this is called from a SCI1.1 - SCI2.1 only function
error("getPropDictSelector called for SCI3");
else
#endif
return _variables[2];
}
void setPropDictSelector(reg_t value) {
#ifdef ENABLE_SCI32
if (getSciVersion() == SCI_VERSION_3)
// This should never occur, this is called from a SCI1.1 - SCI2.1 only function
error("setPropDictSelector called for SCI3");
else
#endif
_variables[2] = value;
}
reg_t getClassScriptSelector() const {
#ifdef ENABLE_SCI32
if (getSciVersion() == SCI_VERSION_3)
return make_reg(0, _baseObj.getUint16SEAt(6));
else
#endif
return _variables[4];
}
void setClassScriptSelector(reg_t value) {
#ifdef ENABLE_SCI32
if (getSciVersion() == SCI_VERSION_3)
// This should never occur, this is called from a SCI1.1 - SCI2.1 only function
error("setClassScriptSelector called for SCI3");
else
#endif
_variables[4] = value;
}
Selector getVarSelector(uint16 i) const { return _baseVars[i]; }
/**
* @returns A pointer to the code for the method at the given index.
*/
reg_t getFunction(const uint16 index) const {
reg_t addr;
addr.setSegment(_pos.getSegment());
addr.setOffset(_baseMethod[index * 2 + 1]);
return addr;
}
/**
* @returns The selector for the method at the given index.
*/
Selector getFuncSelector(const uint16 index) const {
return _baseMethod[index * 2];
}
/**
* Determines if this object is a class and explicitly defines the
* selector as a funcselector. Does NOT say anything about the object's
* superclasses, i.e. failure may be returned even if one of the
* superclasses defines the funcselector
*/
int funcSelectorPosition(Selector sel) const {
for (uint i = 0; i < _methodCount; i++)
if (getFuncSelector(i) == sel)
return i;
return -1;
}
/**
* Determines if the object explicitly defines slc as a varselector.
* Returns -1 if not found.
*/
int locateVarSelector(SegManager *segMan, Selector slc) const;
bool isClass() const { return (getInfoSelector().getOffset() & kInfoFlagClass); }
const Object *getClass(SegManager *segMan) const;
void markAsFreed() { _isFreed = true; }
bool isFreed() const { return _isFreed; }
uint getVarCount() const { return _variables.size(); }
void init(const Script &owner, reg_t obj_pos, bool initVariables = true);
reg_t getVariable(uint var) const { return _variables[var]; }
reg_t &getVariableRef(uint var) { return _variables[var]; }
uint16 getMethodCount() const { return _methodCount; }
reg_t getPos() const { return _pos; }
void saveLoadWithSerializer(Common::Serializer &ser);
void cloneFromObject(const Object *obj) {
_name = obj ? obj->_name : NULL_REG;
_baseObj = obj ? obj->_baseObj : SciSpan<const byte>();
_baseMethod = obj ? obj->_baseMethod : Common::Array<uint32>();
_baseVars = obj ? obj->_baseVars : Common::Array<uint16>();
#ifdef ENABLE_SCI32
if (getSciVersion() == SCI_VERSION_3) {
_mustSetViewVisible = obj ? obj->_mustSetViewVisible : Common::Array<bool>();
}
#endif
}
bool relocateSci0Sci21(SegmentId segment, int location, uint32 heapOffset);
#ifdef ENABLE_SCI32
bool relocateSci3(SegmentId segment, uint32 location, int offset, uint32 scriptSize);
#endif
int propertyOffsetToId(SegManager *segMan, int propertyOffset) const;
void initSpecies(SegManager *segMan, reg_t addr);
void initSuperClass(SegManager *segMan, reg_t addr);
bool initBaseObject(SegManager *segMan, reg_t addr, bool doInitSuperClass = true);
#ifdef ENABLE_SCI32
bool mustSetViewVisible(const int index, const bool fromPropertyOp) const;
#endif
private:
#ifdef ENABLE_SCI32
void initSelectorsSci3(const SciSpan<const byte> &buf, const bool initVariables);
#endif
/**
* The name of the object.
*/
reg_t _name;
/**
* A pointer to the raw object data within the object's owner script.
*/
SciSpan<const byte> _baseObj;
/**
* A lookup table from a property index to its corresponding selector
* number.
*/
Common::Array<uint16> _baseVars;
/**
* A lookup table from a method index to its corresponding selector number
* or offset to code. The table contains selector + offset in pairs.
*/
Common::Array<uint32> _baseMethod;
/**
* A lookup table from a property index to the property's current value.
*/
Common::Array<reg_t> _variables;
#ifdef ENABLE_SCI32
/**
* A lookup table from a property index to the property's original absolute
* offset within the raw script data. This absolute offset is coded into the
* script's relocation table, and is used to look up 32-bit values for
* properties in the relocation table (which is used to break the 16-bit
* barrier, since the script format still only holds 16-bit values inline).
*/
Common::Array<uint32> _propertyOffsetsSci3;
#endif
/**
* The number of methods on the object.
*/
uint16 _methodCount;
/**
* Whether or not a clone object has been marked as 'freed'.
*/
bool _isFreed;
/**
* For SCI0 through SCI2.1, an extra index offset used when looking up
* special object properties -species-, -super-, -info-, and name.
*/
uint16 _offset;
reg_t _pos; /**< Object offset within its script; for clones, this is their base */
#ifdef ENABLE_SCI32
reg_t _superClassPosSci3; /**< reg_t pointing to superclass for SCI3 */
reg_t _speciesSelectorSci3; /**< reg_t containing species "selector" for SCI3 */
reg_t _infoSelectorSci3; /**< reg_t containing info "selector" for SCI3 */
Common::Array<bool> _mustSetViewVisible; /** cached bit of info to make lookup fast, SCI3 only */
#endif
};
} // End of namespace Sci
#endif // SCI_ENGINE_OBJECT_H