370 lines
12 KiB
C++
370 lines
12 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 3 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, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#ifndef SCI_ENGINE_SCRIPT_H
|
|
#define SCI_ENGINE_SCRIPT_H
|
|
|
|
#include "common/str.h"
|
|
#include "sci/util.h"
|
|
#include "sci/engine/segment.h"
|
|
#include "sci/engine/script_patches.h"
|
|
|
|
namespace Sci {
|
|
|
|
struct EngineState;
|
|
class ResourceManager;
|
|
struct SciScriptPatcherEntry;
|
|
|
|
enum ScriptObjectTypes {
|
|
SCI_OBJ_TERMINATOR,
|
|
SCI_OBJ_OBJECT,
|
|
SCI_OBJ_CODE,
|
|
SCI_OBJ_SYNONYMS,
|
|
SCI_OBJ_SAID,
|
|
SCI_OBJ_STRINGS,
|
|
SCI_OBJ_CLASS,
|
|
SCI_OBJ_EXPORTS,
|
|
SCI_OBJ_POINTERS,
|
|
SCI_OBJ_PRELOAD_TEXT, /* This is really just a flag. */
|
|
SCI_OBJ_LOCALVARS
|
|
};
|
|
|
|
typedef Common::HashMap<uint32, Object> ObjMap;
|
|
|
|
enum ScriptOffsetEntryTypes {
|
|
SCI_SCR_OFFSET_TYPE_OBJECT = 0, // classes are handled by this type as well
|
|
SCI_SCR_OFFSET_TYPE_STRING,
|
|
SCI_SCR_OFFSET_TYPE_SAID
|
|
};
|
|
|
|
enum : uint {
|
|
kNoRelocation = 0xFFFFFFFF
|
|
};
|
|
|
|
struct offsetLookupArrayEntry {
|
|
uint16 type; // type of entry
|
|
uint16 id; // id of this type, first item inside script data is 1, second item is 2, etc.
|
|
uint32 offset; // offset of entry within script resource data
|
|
uint16 stringSize; // size of string, including terminating [NUL]
|
|
};
|
|
|
|
typedef Common::Array<offsetLookupArrayEntry> offsetLookupArrayType;
|
|
|
|
class Script : public SegmentObj {
|
|
private:
|
|
int _nr; /**< Script number */
|
|
Common::SpanOwner<SciSpan<byte> > _buf; /**< Static data buffer, or NULL if not used */
|
|
SciSpan<byte> _script; /**< Script size includes alignment byte */
|
|
SciSpan<byte> _heap; /**< Start of heap if SCI1.1, NULL otherwise */
|
|
|
|
uint _lockers; /**< Number of classes and objects that require this script */
|
|
|
|
SciSpan<const uint16> _exports; /**< Exports block or 0 if not present */
|
|
uint16 _numExports; /**< Number of export entries */
|
|
SciSpan<const byte> _synonyms; /**< Synonyms block or 0 if not present */
|
|
uint16 _numSynonyms; /**< Number of synonym entries */
|
|
|
|
int _codeOffset; /**< The absolute offset of the VM code block */
|
|
|
|
int _localsOffset;
|
|
uint16 _localsCount;
|
|
|
|
bool _markedAsDeleted;
|
|
SegmentId _localsSegment; /**< The local variable segment */
|
|
LocalVariables *_localsBlock;
|
|
|
|
ObjMap _objects; /**< Table for objects, contains property variables */
|
|
|
|
protected:
|
|
offsetLookupArrayType _offsetLookupArray; // Table of all elements of currently loaded script, that may get pointed to
|
|
|
|
private:
|
|
uint16 _offsetLookupObjectCount;
|
|
uint16 _offsetLookupStringCount;
|
|
uint16 _offsetLookupSaidCount;
|
|
|
|
public:
|
|
int getLocalsOffset() const { return _localsOffset; }
|
|
uint16 getLocalsCount() const { return _localsCount; }
|
|
|
|
uint32 getScriptSize() const { return _script.size(); }
|
|
uint32 getHeapSize() const { return _heap.size(); }
|
|
uint32 getBufSize() const { return _buf->size(); }
|
|
inline uint32 getHeapOffset() const {
|
|
if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1_LATE) {
|
|
return _script.size();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
const byte *getBuf(uint offset = 0) const { return _buf->getUnsafeDataAt(offset); }
|
|
SciSpan<const byte> getSpan(uint offset) const { return _buf->subspan(offset); }
|
|
|
|
int getScriptNumber() const { return _nr; }
|
|
SegmentId getLocalsSegment() const { return _localsSegment; }
|
|
reg_t *getLocalsBegin() { return _localsBlock ? _localsBlock->_locals.begin() : NULL; }
|
|
void syncLocalsBlock(SegManager *segMan);
|
|
ObjMap &getObjectMap() { return _objects; }
|
|
const ObjMap &getObjectMap() const { return _objects; }
|
|
bool offsetIsObject(uint32 offset) const;
|
|
|
|
public:
|
|
Script();
|
|
~Script() override;
|
|
|
|
void freeScript(const bool keepLocalsSegment = false);
|
|
void load(int script_nr, ResourceManager *resMan, ScriptPatcher *scriptPatcher, bool applyScriptPatches = true);
|
|
|
|
bool isValidOffset(uint32 offset) const override;
|
|
SegmentRef dereference(reg_t pointer) override;
|
|
reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr) const override;
|
|
void freeAtAddress(SegManager *segMan, reg_t sub_addr) override;
|
|
Common::Array<reg_t> listAllDeallocatable(SegmentId segId) const override;
|
|
Common::Array<reg_t> listAllOutgoingReferences(reg_t object) const override;
|
|
|
|
/**
|
|
* Return a list of all references to objects in this script
|
|
* (and also to the locals segment, if any).
|
|
* Used by the garbage collector.
|
|
* @return a list of outgoing references within the object
|
|
*/
|
|
Common::Array<reg_t> listObjectReferences() const;
|
|
|
|
void saveLoadWithSerializer(Common::Serializer &ser) override;
|
|
|
|
Object *getObject(uint32 offset);
|
|
const Object *getObject(uint32 offset) const;
|
|
|
|
/**
|
|
* Initializes an object within the segment manager
|
|
* @param obj_pos Location (segment, offset) of the object. It must
|
|
* point to the beginning of the script/class block
|
|
* (as opposed to what the VM considers to be the
|
|
* object location)
|
|
* @returns A newly created Object describing the object,
|
|
* stored within the relevant script
|
|
*/
|
|
Object *scriptObjInit(reg_t obj_pos, bool fullObjectInit = true);
|
|
|
|
/**
|
|
* Initializes the script's local variables
|
|
* @param segMan A reference to the segment manager
|
|
*/
|
|
void initializeLocals(SegManager *segMan);
|
|
|
|
/**
|
|
* Adds the script's classes to the segment manager's class table
|
|
* @param segMan A reference to the segment manager
|
|
*/
|
|
void initializeClasses(SegManager *segMan);
|
|
|
|
/**
|
|
* Initializes the script's objects (SCI0)
|
|
* @param segMan A reference to the segment manager
|
|
* @param segmentId The script's segment id
|
|
* @param applyScriptPatches Apply patches for the script, if available
|
|
*/
|
|
void initializeObjects(SegManager *segMan, SegmentId segmentId, bool applyScriptPatches);
|
|
|
|
// script lock operations
|
|
|
|
/** Increments the number of lockers of this script by one. */
|
|
void incrementLockers();
|
|
|
|
/** Decrements the number of lockers of this script by one. */
|
|
void decrementLockers();
|
|
|
|
/**
|
|
* Retrieves the number of locks held on this script.
|
|
* @return the number of locks held on the previously identified script
|
|
*/
|
|
uint getLockers() const;
|
|
|
|
/** Sets the number of locks held on this script. */
|
|
void setLockers(uint lockers);
|
|
|
|
/**
|
|
* Retrieves the offset of the export table in the script
|
|
* @return the exports offset.
|
|
*/
|
|
uint getExportsOffset() const { return _exports.sourceByteOffset(); }
|
|
|
|
/**
|
|
* Retrieves the number of exports of script.
|
|
* @return the number of exports of this script
|
|
*/
|
|
uint16 getExportsNr() const { return _numExports; }
|
|
|
|
/**
|
|
* Retrieves a pointer to the synonyms associated with this script
|
|
* @return pointer to the synonyms, in non-parsed format.
|
|
*/
|
|
const SciSpan<const byte> &getSynonyms() const { return _synonyms; }
|
|
|
|
/**
|
|
* Retrieves the number of synonyms associated with this script.
|
|
* @return the number of synonyms associated with this script
|
|
*/
|
|
uint16 getSynonymsNr() const { return _numSynonyms; }
|
|
|
|
/**
|
|
* Validate whether the specified public function is exported by
|
|
* the script in the specified segment.
|
|
* @param pubfunct Index of the function to validate
|
|
* @param relocSci3 Decide whether to relocate this SCI3 public function or not
|
|
* @return NULL if the public function is invalid, its
|
|
* offset into the script's segment otherwise
|
|
*/
|
|
uint32 validateExportFunc(int pubfunct, bool relocSci3);
|
|
|
|
/**
|
|
* Marks the script as deleted.
|
|
* This will not actually delete the script. If references remain present on the
|
|
* heap or the stack, the script will stay in memory in a quasi-deleted state until
|
|
* either unreachable (resulting in its eventual deletion) or reloaded (resulting
|
|
* in its data being updated).
|
|
*/
|
|
void markDeleted() {
|
|
_markedAsDeleted = true;
|
|
}
|
|
|
|
/**
|
|
* Determines whether the script is marked as being deleted.
|
|
*/
|
|
bool isMarkedAsDeleted() const {
|
|
return _markedAsDeleted;
|
|
}
|
|
|
|
/**
|
|
* Finds the pointer where a block of a specific type starts from,
|
|
* in SCI0 - SCI1 games
|
|
*/
|
|
SciSpan<const byte> findBlockSCI0(ScriptObjectTypes type, bool findLastBlock = false) const;
|
|
|
|
/**
|
|
* Syncs the string heap of a script. Used when saving/loading.
|
|
*/
|
|
void syncStringHeap(Common::Serializer &ser);
|
|
|
|
#ifdef ENABLE_SCI32
|
|
/**
|
|
* Resolve a relocation in an SCI3 script
|
|
* @param offset The offset to relocate from
|
|
*/
|
|
int relocateOffsetSci3(uint32 offset) const;
|
|
#endif
|
|
|
|
/**
|
|
* Gets an offset to the beginning of the code block in a SCI1.1 or later
|
|
* script
|
|
*/
|
|
int getCodeBlockOffset() { return _codeOffset; }
|
|
|
|
/**
|
|
* Get the offset array
|
|
*/
|
|
const offsetLookupArrayType *getOffsetArray() { return &_offsetLookupArray; };
|
|
uint16 getOffsetObjectCount() { return _offsetLookupObjectCount; };
|
|
uint16 getOffsetStringCount() { return _offsetLookupStringCount; };
|
|
uint16 getOffsetSaidCount() { return _offsetLookupSaidCount; };
|
|
|
|
/**
|
|
* @returns kNoRelocation if no relocation exists for the given offset,
|
|
* otherwise returns a delta for the offset to its relocated position.
|
|
*/
|
|
uint32 getRelocationOffset(const uint32 offset) const;
|
|
|
|
private:
|
|
/**
|
|
* Returns a Span containing the relocation table for a SCI0-SCI2.1 script.
|
|
* (The SCI0-SCI2.1 relocation table is simply a list of all of the
|
|
* offsets in the script heap whose values should be treated as pointers to
|
|
* objects (vs just being numbers).)
|
|
*/
|
|
const SciSpan<const uint16> getRelocationTableSci0Sci21() const;
|
|
|
|
/**
|
|
* Processes a relocation block within a SCI0-SCI2.1 script
|
|
* This function is idempotent, but it must only be called after all
|
|
* objects have been instantiated, or a run-time error will occur.
|
|
*/
|
|
void relocateSci0Sci21(const SegmentId segmentId);
|
|
|
|
#ifdef ENABLE_SCI32
|
|
/**
|
|
* Processes a relocation block within a SCI3 script
|
|
* This function is idempotent, but it must only be called after all
|
|
* objects have been instantiated, or a run-time error will occur.
|
|
*/
|
|
void relocateSci3(const SegmentId segmentId);
|
|
#endif
|
|
|
|
bool relocateLocal(SegmentId segment, int location, uint32 offset);
|
|
|
|
#ifdef ENABLE_SCI32
|
|
/**
|
|
* Gets a pointer to the beginning of the objects in a SCI3 script
|
|
*/
|
|
SciSpan<const byte> getSci3ObjectsPointer();
|
|
#endif
|
|
|
|
/**
|
|
* Initializes the script's objects (SCI0)
|
|
* @param segMan A reference to the segment manager
|
|
* @param segmentId The script's segment id
|
|
* @applyScriptPatches Apply patches for the script, if available
|
|
*/
|
|
void initializeObjectsSci0(SegManager *segMan, SegmentId segmentId, bool applyScriptPatches);
|
|
|
|
/**
|
|
* Initializes the script's objects (SCI1.1 - SCI2.1)
|
|
* @param segMan A reference to the segment manager
|
|
* @param segmentId The script's segment id
|
|
* @applyScriptPatches Apply patches for the script, if available
|
|
*/
|
|
void initializeObjectsSci11(SegManager *segMan, SegmentId segmentId, bool applyScriptPatches);
|
|
|
|
#ifdef ENABLE_SCI32
|
|
/**
|
|
* Initializes the script's objects (SCI3)
|
|
* @param segMan A reference to the segment manager
|
|
* @param segmentId The script's segment id
|
|
*/
|
|
void initializeObjectsSci3(SegManager *segMan, SegmentId segmentId, bool applyScriptPatches);
|
|
#endif
|
|
|
|
LocalVariables *allocLocalsSegment(SegManager *segMan);
|
|
|
|
/**
|
|
* Identifies certain offsets within script data and set up lookup-table
|
|
*/
|
|
void identifyOffsets();
|
|
|
|
/**
|
|
* Apply workarounds to known broken Said strings
|
|
*/
|
|
void applySaidWorkarounds();
|
|
};
|
|
|
|
} // End of namespace Sci
|
|
|
|
#endif // SCI_ENGINE_SCRIPT_H
|