Skip to content

Commit

Permalink
SCI: Find and store the original static names of objects
Browse files Browse the repository at this point in the history
See code comment in Object::init for more details.

Fixes Trac#9780.
  • Loading branch information
csnover committed May 21, 2017
1 parent 1f29e6f commit 4917330
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 34 deletions.
36 changes: 33 additions & 3 deletions engines/sci/engine/object.cpp
Expand Up @@ -23,6 +23,7 @@

#include "sci/engine/kernel.h"
#include "sci/engine/object.h"
#include "sci/engine/script.h"
#include "sci/engine/seg_manager.h"
#ifdef ENABLE_SCI32
#include "sci/engine/features.h"
Expand All @@ -32,9 +33,9 @@ namespace Sci {

extern bool relocateBlock(Common::Array<reg_t> &block, int block_location, SegmentId segment, int location, uint32 heapOffset);


void Object::init(const SciSpan<const byte> &buf, reg_t obj_pos, bool initVariables) {
const SciSpan<const byte> data = buf.subspan(obj_pos.getOffset());
void Object::init(const Script &owner, reg_t obj_pos, bool initVariables) {
const SciSpan<const byte> buf = owner.getSpan(0);
const SciSpan<const byte> data = owner.getSpan(obj_pos.getOffset());
_baseObj = data;
_pos = obj_pos;

Expand Down Expand Up @@ -81,6 +82,35 @@ void Object::init(const SciSpan<const byte> &buf, reg_t obj_pos, bool initVariab
#endif
}

// Some objects, like the unnamed LarryTalker instance in LSL6hires script
// 610, and the File class in Torin script 64993, have a `name` property
// that is assigned dynamically by game scripts, overriding the static name
// value that is normally created by the SC compiler. When this happens, the
// value can be set to anything: in LSL6hires it becomes a Str object; in
// Torin, it becomes a dynamically allocated string that is disposed before
// the corresponding File instance is disposed.
// To ensure `SegManager::getObjectName` works consistently and correctly,
// without hacks to bypass unexpected/invalid types of dynamic `name` data,
// the reg_t pointer to the original static name value for the object is
// stored here, ensuring that it is constant and guaranteed to be either a
// valid dereferenceable string or NULL_REG.
if (getSciVersion() != SCI_VERSION_3) {
const uint32 heapOffset = owner.getHeapOffset();
const uint32 nameOffset = (obj_pos.getOffset() - heapOffset) + (_offset + 3) * sizeof(uint16);
const uint32 relocOffset = owner.getRelocationOffset(nameOffset);
if (relocOffset != kNoRelocation) {
_name = make_reg(obj_pos.getSegment(), relocOffset + _baseObj.getUint16SEAt((_offset + 3) * sizeof(uint16)));
}
#ifdef ENABLE_SCI32
} else if (_propertyOffsetsSci3.size()) {
const uint32 nameOffset = _propertyOffsetsSci3[0];
const uint32 relocOffset = owner.getRelocationOffset(nameOffset);
if (relocOffset != kNoRelocation) {
_name = make_reg(obj_pos.getSegment(), relocOffset + buf.getUint16SEAt(nameOffset));
}
#endif
}

if (initVariables) {
#ifdef ENABLE_SCI32
if (getSciVersion() == SCI_VERSION_3) {
Expand Down
18 changes: 11 additions & 7 deletions engines/sci/engine/object.h
Expand Up @@ -34,6 +34,7 @@
namespace Sci {

class SegManager;
class Script;

enum infoSelectorFlags {
kInfoFlagClone = 0x0001,
Expand Down Expand Up @@ -69,6 +70,7 @@ enum ObjectOffsets {
class Object {
public:
Object() :
_name(NULL_REG),
_offset(getSciVersion() < SCI_VERSION_1_1 ? 0 : 5),
_isFreed(false),
_baseObj(),
Expand All @@ -81,6 +83,7 @@ class Object {
{}

Object &operator=(const Object &other) {
_name = other._name;
_baseObj = other._baseObj;
_baseMethod = other._baseMethod;
_variables = other._variables;
Expand Down Expand Up @@ -181,12 +184,7 @@ class Object {
#endif

reg_t getNameSelector() const {
#ifdef ENABLE_SCI32
if (getSciVersion() == SCI_VERSION_3)
return _variables.size() ? _variables[0] : NULL_REG;
else
#endif
return _offset + 3 < (uint16)_variables.size() ? _variables[_offset + 3] : NULL_REG;
return _name;
}

// No setter for the name selector
Expand Down Expand Up @@ -278,7 +276,7 @@ class Object {

uint getVarCount() const { return _variables.size(); }

void init(const SciSpan<const byte> &buf, reg_t obj_pos, bool initVariables = true);
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]; }
Expand All @@ -289,6 +287,7 @@ class Object {
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>();
Expand Down Expand Up @@ -319,6 +318,11 @@ class Object {
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.
*/
Expand Down
2 changes: 1 addition & 1 deletion engines/sci/engine/script.cpp
Expand Up @@ -656,7 +656,7 @@ Object *Script::scriptObjInit(reg_t obj_pos, bool fullObjectInit) {
// Get the object at the specified position and init it. This will
// automatically "allocate" space for it in the _objects map if necessary.
Object *obj = &_objects[obj_pos.getOffset()];
obj->init(*_buf, obj_pos, fullObjectInit);
obj->init(*this, obj_pos, fullObjectInit);

return obj;
}
Expand Down
24 changes: 2 additions & 22 deletions engines/sci/engine/seg_manager.cpp
Expand Up @@ -275,30 +275,10 @@ const char *SegManager::getObjectName(reg_t pos) {
if (nameReg.isNull())
return "<no name>";

const char *name = nullptr;

if (nameReg.getSegment()) {
#ifdef ENABLE_SCI32
// At least Torin script 64000 creates objects with names that are
// pointed to dynamically generated strings which are freed before the
// objects themselves are freed. This causes a crash when using
// `findObjectByName`, since the name of the object is no longer valid
if (nameReg.getSegment() != _arraysSegId ||
_heap[_arraysSegId]->isValidOffset(nameReg.getOffset())) {
#endif
name = derefString(nameReg);
#ifdef ENABLE_SCI32
}
#endif
}
const char *name = derefString(nameReg);

if (!name) {
// Crazy Nick Laura Bow is missing some object names needed for the static
// selector vocabulary
if (g_sci->getGameId() == GID_CNICK_LAURABOW && pos == make_reg(1, 0x2267))
return "Character";
else
return "<invalid name>";
return "<invalid name>";
}

return name;
Expand Down
2 changes: 1 addition & 1 deletion engines/sci/engine/workarounds.cpp
Expand Up @@ -257,7 +257,7 @@ const SciWorkaroundEntry uninitializedReadWorkarounds[] = {
{ GID_CNICK_KQ, -1, 700, 0, "gcWindow", "open", NULL, -1, { WORKAROUND_FAKE, 0 } }, // when entering the control menu, like in hoyle 3
{ GID_CNICK_KQ, 300, 303, 0, "theDoubleCube", "<noname520>", NULL, 5, { WORKAROUND_FAKE, 0 } }, // while playing backgammon with doubling enabled - bug #6426 (same as the theDoubleCube::make workaround for Hoyle 3)
{ GID_CNICK_KQ, 300, 303, 0, "theDoubleCube", "<noname519>", NULL, 9, { WORKAROUND_FAKE, 0 } }, // when accepting a double, while playing backgammon with doubling enabled (same as the theDoubleCube::accept workaround for Hoyle 3)
{ GID_CNICK_LAURABOW, -1, 0, 1, "Character", "say", NULL, -1, { WORKAROUND_FAKE, 0 } }, // Yatch, like in hoyle 3 - temps 504 and 505 - bug #6424
{ GID_CNICK_LAURABOW,500, 0, 1, "<no name>", "<noname446>", NULL, -1, { WORKAROUND_FAKE, 0 } }, // Yacht, like in hoyle 3 - temps 504 and 505 - bug #6424
{ GID_CNICK_LAURABOW, -1, 700, 0, NULL, "open", NULL, -1, { WORKAROUND_FAKE, 0 } }, // when entering control menu - bug #6423 (same as the gcWindow workaround for Hoyle 3)
{ GID_CNICK_LAURABOW,100, 100, 0, NULL, "<noname144>", NULL, 1, { WORKAROUND_FAKE, 0 } }, // while playing domino - bug #6429 (same as the dominoHand2 workaround for Hoyle 3)
{ GID_CNICK_LAURABOW,100, 110, 0, NULL, "doit", NULL, -1, { WORKAROUND_FAKE, 0 } }, // when changing the "Dominoes per hand" setting - bug #6430
Expand Down

0 comments on commit 4917330

Please sign in to comment.