SCI: Moved all the script-related code inside script.cpp/.h, and all script opcode-related code inside vm.cpp/.h
svn-id: r50396
This commit is contained in:
parent
0436f2823d
commit
bb992b0b93
9 changed files with 817 additions and 790 deletions
|
@ -25,6 +25,8 @@
|
||||||
|
|
||||||
#include "sci/sci.h"
|
#include "sci/sci.h"
|
||||||
#include "sci/resource.h"
|
#include "sci/resource.h"
|
||||||
|
#include "sci/engine/seg_manager.h"
|
||||||
|
#include "sci/engine/script.h"
|
||||||
#include "sci/engine/state.h"
|
#include "sci/engine/state.h"
|
||||||
#include "sci/engine/selector.h"
|
#include "sci/engine/selector.h"
|
||||||
#include "sci/engine/kernel.h"
|
#include "sci/engine/kernel.h"
|
||||||
|
|
|
@ -35,127 +35,353 @@
|
||||||
|
|
||||||
namespace Sci {
|
namespace Sci {
|
||||||
|
|
||||||
#define END Script_None
|
Script::Script() : SegmentObj(SEG_TYPE_SCRIPT) {
|
||||||
|
_nr = 0;
|
||||||
|
_buf = NULL;
|
||||||
|
_bufSize = 0;
|
||||||
|
_scriptSize = 0;
|
||||||
|
_heapSize = 0;
|
||||||
|
|
||||||
opcode_format g_opcode_formats[128][4] = {
|
_synonyms = NULL;
|
||||||
/*00*/
|
_heapStart = NULL;
|
||||||
{Script_None}, {Script_None}, {Script_None}, {Script_None},
|
_exportTable = NULL;
|
||||||
/*04*/
|
|
||||||
{Script_None}, {Script_None}, {Script_None}, {Script_None},
|
|
||||||
/*08*/
|
|
||||||
{Script_None}, {Script_None}, {Script_None}, {Script_None},
|
|
||||||
/*0C*/
|
|
||||||
{Script_None}, {Script_None}, {Script_None}, {Script_None},
|
|
||||||
/*10*/
|
|
||||||
{Script_None}, {Script_None}, {Script_None}, {Script_None},
|
|
||||||
/*14*/
|
|
||||||
{Script_None}, {Script_None}, {Script_None}, {Script_SRelative, END},
|
|
||||||
/*18*/
|
|
||||||
{Script_SRelative, END}, {Script_SRelative, END}, {Script_SVariable, END}, {Script_None},
|
|
||||||
/*1C*/
|
|
||||||
{Script_SVariable, END}, {Script_None}, {Script_None}, {Script_Variable, END},
|
|
||||||
/*20*/
|
|
||||||
{Script_SRelative, Script_Byte, END}, {Script_Variable, Script_Byte, END}, {Script_Variable, Script_Byte, END}, {Script_Variable, Script_SVariable, Script_Byte, END},
|
|
||||||
/*24 (24=ret)*/
|
|
||||||
{Script_End}, {Script_Byte, END}, {Script_Invalid}, {Script_Invalid},
|
|
||||||
/*28*/
|
|
||||||
{Script_Variable, END}, {Script_Invalid}, {Script_Byte, END}, {Script_Variable, Script_Byte, END},
|
|
||||||
/*2C*/
|
|
||||||
{Script_SVariable, END}, {Script_SVariable, Script_Variable, END}, {Script_None}, {Script_Invalid},
|
|
||||||
/*30*/
|
|
||||||
{Script_None}, {Script_Property, END}, {Script_Property, END}, {Script_Property, END},
|
|
||||||
/*34*/
|
|
||||||
{Script_Property, END}, {Script_Property, END}, {Script_Property, END}, {Script_Property, END},
|
|
||||||
/*38*/
|
|
||||||
{Script_Property, END}, {Script_SRelative, END}, {Script_SRelative, END}, {Script_None},
|
|
||||||
/*3C*/
|
|
||||||
{Script_None}, {Script_None}, {Script_None}, {Script_Word},
|
|
||||||
/*40-4F*/
|
|
||||||
{Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
|
|
||||||
{Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
|
|
||||||
{Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
|
|
||||||
{Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
|
|
||||||
/*50-5F*/
|
|
||||||
{Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
|
|
||||||
{Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
|
|
||||||
{Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
|
|
||||||
{Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
|
|
||||||
/*60-6F*/
|
|
||||||
{Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
|
|
||||||
{Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
|
|
||||||
{Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
|
|
||||||
{Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
|
|
||||||
/*70-7F*/
|
|
||||||
{Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
|
|
||||||
{Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
|
|
||||||
{Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
|
|
||||||
{Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}
|
|
||||||
};
|
|
||||||
#undef END
|
|
||||||
|
|
||||||
// TODO: script_adjust_opcode_formats should probably be part of the
|
_localsOffset = 0;
|
||||||
// constructor (?) of a VirtualMachine or a ScriptManager class.
|
_localsSegment = 0;
|
||||||
void script_adjust_opcode_formats() {
|
_localsBlock = NULL;
|
||||||
if (g_sci->_features->detectLofsType() != SCI_VERSION_0_EARLY) {
|
_localsCount = 0;
|
||||||
g_opcode_formats[op_lofsa][0] = Script_Offset;
|
|
||||||
g_opcode_formats[op_lofss][0] = Script_Offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef ENABLE_SCI32
|
_markedAsDeleted = false;
|
||||||
// In SCI32, some arguments are now words instead of bytes
|
|
||||||
if (getSciVersion() >= SCI_VERSION_2) {
|
|
||||||
g_opcode_formats[op_calle][2] = Script_Word;
|
|
||||||
g_opcode_formats[op_callk][1] = Script_Word;
|
|
||||||
g_opcode_formats[op_super][1] = Script_Word;
|
|
||||||
g_opcode_formats[op_send][0] = Script_Word;
|
|
||||||
g_opcode_formats[op_self][0] = Script_Word;
|
|
||||||
g_opcode_formats[op_call][1] = Script_Word;
|
|
||||||
g_opcode_formats[op_callb][1] = Script_Word;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SegManager::createClassTable() {
|
Script::~Script() {
|
||||||
Resource *vocab996 = _resMan->findResource(ResourceId(kResourceTypeVocab, 996), 1);
|
freeScript();
|
||||||
|
|
||||||
if (!vocab996)
|
|
||||||
error("SegManager: failed to open vocab 996");
|
|
||||||
|
|
||||||
int totalClasses = vocab996->size >> 2;
|
|
||||||
_classTable.resize(totalClasses);
|
|
||||||
|
|
||||||
for (uint16 classNr = 0; classNr < totalClasses; classNr++) {
|
|
||||||
uint16 scriptNr = READ_SCI11ENDIAN_UINT16(vocab996->data + classNr * 4 + 2);
|
|
||||||
|
|
||||||
_classTable[classNr].reg = NULL_REG;
|
|
||||||
_classTable[classNr].script = scriptNr;
|
|
||||||
}
|
|
||||||
|
|
||||||
_resMan->unlockResource(vocab996);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
reg_t SegManager::getClassAddress(int classnr, ScriptLoadType lock, reg_t caller) {
|
void Script::freeScript() {
|
||||||
if (classnr == 0xffff)
|
free(_buf);
|
||||||
return NULL_REG;
|
_buf = NULL;
|
||||||
|
_bufSize = 0;
|
||||||
|
|
||||||
if (classnr < 0 || (int)_classTable.size() <= classnr || _classTable[classnr].script < 0) {
|
_objects.clear();
|
||||||
error("[VM] Attempt to dereference class %x, which doesn't exist (max %x)", classnr, _classTable.size());
|
}
|
||||||
return NULL_REG;
|
|
||||||
|
void Script::init(int script_nr, ResourceManager *resMan) {
|
||||||
|
Resource *script = resMan->findResource(ResourceId(kResourceTypeScript, script_nr), 0);
|
||||||
|
|
||||||
|
_localsOffset = 0;
|
||||||
|
_localsBlock = NULL;
|
||||||
|
_localsCount = 0;
|
||||||
|
|
||||||
|
_markedAsDeleted = false;
|
||||||
|
|
||||||
|
_nr = script_nr;
|
||||||
|
_buf = 0;
|
||||||
|
_heapStart = 0;
|
||||||
|
|
||||||
|
_scriptSize = script->size;
|
||||||
|
_bufSize = script->size;
|
||||||
|
_heapSize = 0;
|
||||||
|
|
||||||
|
_lockers = 1;
|
||||||
|
|
||||||
|
if (getSciVersion() == SCI_VERSION_0_EARLY) {
|
||||||
|
_bufSize += READ_LE_UINT16(script->data) * 2;
|
||||||
|
} else if (getSciVersion() >= SCI_VERSION_1_1) {
|
||||||
|
/**
|
||||||
|
* In SCI11, the heap was in a separate space from the script.
|
||||||
|
* We append it to the end of the script, and adjust addressing accordingly.
|
||||||
|
* However, since we address the heap with a 16-bit pointer, the combined
|
||||||
|
* size of the stack and the heap must be 64KB. So far this has worked
|
||||||
|
* for SCI11, SCI2 and SCI21 games. SCI3 games use a different script format,
|
||||||
|
* and theoretically they can exceed the 64KB boundary using relocation.
|
||||||
|
*/
|
||||||
|
Resource *heap = resMan->findResource(ResourceId(kResourceTypeHeap, script_nr), 0);
|
||||||
|
_bufSize += heap->size;
|
||||||
|
_heapSize = heap->size;
|
||||||
|
|
||||||
|
// Ensure that the start of the heap resource can be word-aligned.
|
||||||
|
if (script->size & 2) {
|
||||||
|
_bufSize++;
|
||||||
|
_scriptSize++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// As mentioned above, the script and the heap together should not exceed 64KB
|
||||||
|
if (script->size + heap->size > 65535)
|
||||||
|
error("Script and heap sizes combined exceed 64K. This means a fundamental "
|
||||||
|
"design bug was made regarding SCI1.1 and newer games.\nPlease "
|
||||||
|
"report this error to the ScummVM team");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Script::load(ResourceManager *resMan) {
|
||||||
|
Resource *script = resMan->findResource(ResourceId(kResourceTypeScript, _nr), 0);
|
||||||
|
assert(script != 0);
|
||||||
|
|
||||||
|
_buf = (byte *)malloc(_bufSize);
|
||||||
|
assert(_buf);
|
||||||
|
|
||||||
|
assert(_bufSize >= script->size);
|
||||||
|
memcpy(_buf, script->data, script->size);
|
||||||
|
|
||||||
|
if (getSciVersion() >= SCI_VERSION_1_1) {
|
||||||
|
Resource *heap = resMan->findResource(ResourceId(kResourceTypeHeap, _nr), 0);
|
||||||
|
assert(heap != 0);
|
||||||
|
|
||||||
|
_heapStart = _buf + _scriptSize;
|
||||||
|
|
||||||
|
assert(_bufSize - _scriptSize <= heap->size);
|
||||||
|
memcpy(_heapStart, heap->data, heap->size);
|
||||||
|
}
|
||||||
|
|
||||||
|
_exportTable = 0;
|
||||||
|
_numExports = 0;
|
||||||
|
_synonyms = 0;
|
||||||
|
_numSynonyms = 0;
|
||||||
|
|
||||||
|
if (getSciVersion() >= SCI_VERSION_1_1) {
|
||||||
|
if (READ_LE_UINT16(_buf + 1 + 5) > 0) { // does the script have an export table?
|
||||||
|
_exportTable = (const uint16 *)(_buf + 1 + 5 + 2);
|
||||||
|
_numExports = READ_SCI11ENDIAN_UINT16(_exportTable - 1);
|
||||||
|
}
|
||||||
|
_localsOffset = _scriptSize + 4;
|
||||||
|
_localsCount = READ_SCI11ENDIAN_UINT16(_buf + _localsOffset - 2);
|
||||||
} else {
|
} else {
|
||||||
Class *the_class = &_classTable[classnr];
|
_exportTable = (const uint16 *)findBlock(SCI_OBJ_EXPORTS);
|
||||||
if (!the_class->reg.segment) {
|
if (_exportTable) {
|
||||||
getScriptSegment(the_class->script, lock);
|
_numExports = READ_SCI11ENDIAN_UINT16(_exportTable + 1);
|
||||||
|
_exportTable += 3; // skip header plus 2 bytes (_exportTable is a uint16 pointer)
|
||||||
if (!the_class->reg.segment) {
|
}
|
||||||
error("[VM] Trying to instantiate class %x by instantiating script 0x%x (%03d) failed;", classnr, the_class->script, the_class->script);
|
_synonyms = findBlock(SCI_OBJ_SYNONYMS);
|
||||||
return NULL_REG;
|
if (_synonyms) {
|
||||||
}
|
_numSynonyms = READ_SCI11ENDIAN_UINT16(_synonyms + 2) / 4;
|
||||||
} else
|
_synonyms += 4; // skip header
|
||||||
if (caller.segment != the_class->reg.segment)
|
}
|
||||||
getScript(the_class->reg.segment)->incrementLockers();
|
const byte* localsBlock = findBlock(SCI_OBJ_LOCALVARS);
|
||||||
|
if (localsBlock) {
|
||||||
return the_class->reg;
|
_localsOffset = localsBlock - _buf + 4;
|
||||||
|
_localsCount = (READ_LE_UINT16(_buf + _localsOffset - 2) - 4) >> 1; // half block size
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (getSciVersion() > SCI_VERSION_0_EARLY) {
|
||||||
|
// Does the script actually have locals? If not, set the locals offset to 0
|
||||||
|
if (!_localsCount)
|
||||||
|
_localsOffset = 0;
|
||||||
|
|
||||||
|
if (_localsOffset + _localsCount * 2 + 1 >= (int)_bufSize) {
|
||||||
|
error("Locals extend beyond end of script: offset %04x, count %d vs size %d", _localsOffset, _localsCount, _bufSize);
|
||||||
|
_localsCount = (_bufSize - _localsOffset) >> 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Old script block. There won't be a localvar block in this case.
|
||||||
|
// Instead, the script starts with a 16 bit int specifying the
|
||||||
|
// number of locals we need; these are then allocated and zeroed.
|
||||||
|
_localsCount = READ_LE_UINT16(_buf);
|
||||||
|
_localsOffset = -_localsCount * 2; // Make sure it's invalid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Object *Script::allocateObject(uint16 offset) {
|
||||||
|
return &_objects[offset];
|
||||||
|
}
|
||||||
|
|
||||||
|
Object *Script::getObject(uint16 offset) {
|
||||||
|
if (_objects.contains(offset))
|
||||||
|
return &_objects[offset];
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Object *Script::getObject(uint16 offset) const {
|
||||||
|
if (_objects.contains(offset))
|
||||||
|
return &_objects[offset];
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object *Script::scriptObjInit(reg_t obj_pos, bool fullObjectInit) {
|
||||||
|
Object *obj;
|
||||||
|
|
||||||
|
if (getSciVersion() < SCI_VERSION_1_1 && fullObjectInit)
|
||||||
|
obj_pos.offset += 8; // magic offset (SCRIPT_OBJECT_MAGIC_OFFSET)
|
||||||
|
|
||||||
|
VERIFY(obj_pos.offset < _bufSize, "Attempt to initialize object beyond end of script\n");
|
||||||
|
|
||||||
|
obj = allocateObject(obj_pos.offset);
|
||||||
|
|
||||||
|
VERIFY(obj_pos.offset + kOffsetFunctionArea < (int)_bufSize, "Function area pointer stored beyond end of script\n");
|
||||||
|
|
||||||
|
obj->init(_buf, obj_pos, fullObjectInit);
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Script::scriptObjRemove(reg_t obj_pos) {
|
||||||
|
if (getSciVersion() < SCI_VERSION_1_1)
|
||||||
|
obj_pos.offset += 8;
|
||||||
|
|
||||||
|
_objects.erase(obj_pos.toUint16());
|
||||||
|
}
|
||||||
|
|
||||||
|
// This helper function is used by Script::relocateLocal and Object::relocate
|
||||||
|
// Duplicate in segment.cpp and script.cpp
|
||||||
|
static bool relocateBlock(Common::Array<reg_t> &block, int block_location, SegmentId segment, int location, size_t scriptSize) {
|
||||||
|
int rel = location - block_location;
|
||||||
|
|
||||||
|
if (rel < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
uint idx = rel >> 1;
|
||||||
|
|
||||||
|
if (idx >= block.size())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (rel & 1) {
|
||||||
|
error("Attempt to relocate odd variable #%d.5e (relative to %04x)\n", idx, block_location);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
block[idx].segment = segment; // Perform relocation
|
||||||
|
if (getSciVersion() >= SCI_VERSION_1_1)
|
||||||
|
block[idx].offset += scriptSize;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Script::relocateLocal(SegmentId segment, int location) {
|
||||||
|
if (_localsBlock)
|
||||||
|
return relocateBlock(_localsBlock->_locals, _localsOffset, segment, location, _scriptSize);
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Script::relocate(reg_t block) {
|
||||||
|
byte *heap = _buf;
|
||||||
|
uint16 heapSize = (uint16)_bufSize;
|
||||||
|
uint16 heapOffset = 0;
|
||||||
|
|
||||||
|
if (getSciVersion() >= SCI_VERSION_1_1) {
|
||||||
|
heap = _heapStart;
|
||||||
|
heapSize = (uint16)_heapSize;
|
||||||
|
heapOffset = _scriptSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
VERIFY(block.offset < (uint16)heapSize && READ_SCI11ENDIAN_UINT16(heap + block.offset) * 2 + block.offset < (uint16)heapSize,
|
||||||
|
"Relocation block outside of script\n");
|
||||||
|
|
||||||
|
int count = READ_SCI11ENDIAN_UINT16(heap + block.offset);
|
||||||
|
int exportIndex = 0;
|
||||||
|
int pos = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
pos = READ_SCI11ENDIAN_UINT16(heap + block.offset + 2 + (exportIndex * 2)) + heapOffset;
|
||||||
|
// This occurs in SCI01/SCI1 games where usually one export value
|
||||||
|
// is zero. It seems that in this situation, we should skip the
|
||||||
|
// export and move to the next one, though the total count of valid
|
||||||
|
// exports remains the same
|
||||||
|
if (!pos) {
|
||||||
|
exportIndex++;
|
||||||
|
pos = READ_SCI11ENDIAN_UINT16(heap + block.offset + 2 + (exportIndex * 2)) + heapOffset;
|
||||||
|
if (!pos)
|
||||||
|
error("Script::relocate(): Consecutive zero exports found");
|
||||||
|
}
|
||||||
|
|
||||||
|
// In SCI0-SCI1, script local variables, objects and code are relocated. We only relocate
|
||||||
|
// locals and objects here, and ignore relocation of code blocks. In SCI1.1 and newer
|
||||||
|
// versions, only locals and objects are relocated.
|
||||||
|
if (!relocateLocal(block.segment, pos)) {
|
||||||
|
// Not a local? It's probably an object or code block. If it's an object, relocate it.
|
||||||
|
const ObjMap::iterator end = _objects.end();
|
||||||
|
for (ObjMap::iterator it = _objects.begin(); it != end; ++it)
|
||||||
|
if (it->_value.relocate(block.segment, pos, _scriptSize))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
exportIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Script::incrementLockers() {
|
||||||
|
_lockers++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Script::decrementLockers() {
|
||||||
|
if (_lockers > 0)
|
||||||
|
_lockers--;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Script::getLockers() const {
|
||||||
|
return _lockers;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Script::setLockers(int lockers) {
|
||||||
|
_lockers = lockers;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16 Script::validateExportFunc(int pubfunct) {
|
||||||
|
bool exportsAreWide = (g_sci->_features->detectLofsType() == SCI_VERSION_1_MIDDLE);
|
||||||
|
|
||||||
|
if (_numExports <= pubfunct) {
|
||||||
|
error("validateExportFunc(): pubfunct is invalid");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exportsAreWide)
|
||||||
|
pubfunct *= 2;
|
||||||
|
uint16 offset = READ_SCI11ENDIAN_UINT16(_exportTable + pubfunct);
|
||||||
|
VERIFY(offset < _bufSize, "invalid export function pointer");
|
||||||
|
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte *Script::findBlock(int type) {
|
||||||
|
byte *buf = _buf;
|
||||||
|
bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY);
|
||||||
|
|
||||||
|
if (oldScriptHeader)
|
||||||
|
buf += 2;
|
||||||
|
|
||||||
|
do {
|
||||||
|
int seekerType = READ_LE_UINT16(buf);
|
||||||
|
|
||||||
|
if (seekerType == 0)
|
||||||
|
break;
|
||||||
|
if (seekerType == type)
|
||||||
|
return buf;
|
||||||
|
|
||||||
|
int seekerSize = READ_LE_UINT16(buf + 2);
|
||||||
|
assert(seekerSize > 0);
|
||||||
|
buf += seekerSize;
|
||||||
|
} while (1);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// memory operations
|
||||||
|
|
||||||
|
void Script::mcpyInOut(int dst, const void *src, size_t n) {
|
||||||
|
if (_buf) {
|
||||||
|
assert(dst + n <= _bufSize);
|
||||||
|
memcpy(_buf + dst, src, n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Script::isValidOffset(uint16 offset) const {
|
||||||
|
return offset < _bufSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
SegmentRef Script::dereference(reg_t pointer) {
|
||||||
|
if (pointer.offset > _bufSize) {
|
||||||
|
error("Script::dereference(): Attempt to dereference invalid pointer %04x:%04x into script segment (script size=%d)",
|
||||||
|
PRINT_REG(pointer), (uint)_bufSize);
|
||||||
|
return SegmentRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
SegmentRef ret;
|
||||||
|
ret.isRaw = true;
|
||||||
|
ret.maxSize = _bufSize - pointer.offset;
|
||||||
|
ret.raw = _buf + pointer.offset;
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SegManager::scriptInitialiseLocals(SegmentId segmentId) {
|
void SegManager::scriptInitialiseLocals(SegmentId segmentId) {
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#define SCI_ENGINE_SCRIPT_H
|
#define SCI_ENGINE_SCRIPT_H
|
||||||
|
|
||||||
#include "common/str.h"
|
#include "common/str.h"
|
||||||
|
#include "sci/engine/segment.h"
|
||||||
|
|
||||||
namespace Sci {
|
namespace Sci {
|
||||||
|
|
||||||
|
@ -49,160 +50,205 @@ enum ScriptObjectTypes {
|
||||||
SCI_OBJ_LOCALVARS
|
SCI_OBJ_LOCALVARS
|
||||||
};
|
};
|
||||||
|
|
||||||
// Opcode formats
|
typedef Common::HashMap<uint16, Object> ObjMap;
|
||||||
enum opcode_format {
|
|
||||||
Script_Invalid = -1,
|
class Script : public SegmentObj {
|
||||||
Script_None = 0,
|
public:
|
||||||
Script_Byte,
|
int _nr; /**< Script number */
|
||||||
Script_SByte,
|
byte *_buf; /**< Static data buffer, or NULL if not used */
|
||||||
Script_Word,
|
byte *_heapStart; /**< Start of heap if SCI1.1, NULL otherwise */
|
||||||
Script_SWord,
|
|
||||||
Script_Variable,
|
uint32 getScriptSize() { return _scriptSize; }
|
||||||
Script_SVariable,
|
uint32 getHeapSize() { return _heapSize; }
|
||||||
Script_SRelative,
|
uint32 getBufSize() { return _bufSize; }
|
||||||
Script_Property,
|
|
||||||
Script_Global,
|
protected:
|
||||||
Script_Local,
|
int _lockers; /**< Number of classes and objects that require this script */
|
||||||
Script_Temp,
|
|
||||||
Script_Param,
|
private:
|
||||||
Script_Offset,
|
size_t _scriptSize;
|
||||||
Script_End
|
size_t _heapSize;
|
||||||
|
uint16 _bufSize;
|
||||||
|
|
||||||
|
const uint16 *_exportTable; /**< Abs. offset of the export table or 0 if not present */
|
||||||
|
uint16 _numExports; /**< Number of entries in the exports table */
|
||||||
|
|
||||||
|
const byte *_synonyms; /**< Synonyms block or 0 if not present*/
|
||||||
|
uint16 _numSynonyms; /**< Number of entries in the synonyms block */
|
||||||
|
|
||||||
|
int _localsOffset;
|
||||||
|
uint16 _localsCount;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Table for objects, contains property variables.
|
||||||
|
* Indexed by the TODO offset.
|
||||||
|
*/
|
||||||
|
ObjMap _objects;
|
||||||
|
|
||||||
|
int getLocalsOffset() const { return _localsOffset; }
|
||||||
|
uint16 getLocalsCount() const { return _localsCount; }
|
||||||
|
SegmentId _localsSegment; /**< The local variable segment */
|
||||||
|
LocalVariables *_localsBlock;
|
||||||
|
|
||||||
|
bool _markedAsDeleted;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Script();
|
||||||
|
~Script();
|
||||||
|
|
||||||
|
void freeScript();
|
||||||
|
void init(int script_nr, ResourceManager *resMan);
|
||||||
|
void load(ResourceManager *resMan);
|
||||||
|
|
||||||
|
virtual bool isValidOffset(uint16 offset) const;
|
||||||
|
virtual SegmentRef dereference(reg_t pointer);
|
||||||
|
virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr) const;
|
||||||
|
virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr);
|
||||||
|
virtual void listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) const;
|
||||||
|
virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note) const;
|
||||||
|
|
||||||
|
virtual void saveLoadWithSerializer(Common::Serializer &ser);
|
||||||
|
|
||||||
|
Object *allocateObject(uint16 offset);
|
||||||
|
Object *getObject(uint16 offset);
|
||||||
|
const Object *getObject(uint16 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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a script object
|
||||||
|
* @param obj_pos Location (segment, offset) of the object.
|
||||||
|
*/
|
||||||
|
void scriptObjRemove(reg_t obj_pos);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes a relocation block witin a script
|
||||||
|
* This function is idempotent, but it must only be called after all
|
||||||
|
* objects have been instantiated, or a run-time error will occur.
|
||||||
|
* @param obj_pos Location (segment, offset) of the block
|
||||||
|
* @return Location of the relocation block
|
||||||
|
*/
|
||||||
|
void relocate(reg_t block);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool relocateLocal(SegmentId segment, int location);
|
||||||
|
|
||||||
|
public:
|
||||||
|
// 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
|
||||||
|
*/
|
||||||
|
int getLockers() const;
|
||||||
|
|
||||||
|
/** Sets the number of locks held on this script. */
|
||||||
|
void setLockers(int lockers);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a pointer to the exports of this script
|
||||||
|
* @return pointer to the exports.
|
||||||
|
*/
|
||||||
|
const uint16 *getExportTable() const { return _exportTable; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 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
|
||||||
|
* @return NULL if the public function is invalid, its
|
||||||
|
* offset into the script's segment otherwise
|
||||||
|
*/
|
||||||
|
uint16 validateExportFunc(int pubfunct);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies a byte string into a script's heap representation.
|
||||||
|
* @param dst script-relative offset of the destination area
|
||||||
|
* @param src pointer to the data source location
|
||||||
|
* @param n number of bytes to copy
|
||||||
|
*/
|
||||||
|
void mcpyInOut(int dst, const void *src, size_t n);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the pointer where a block of a specific type starts from
|
||||||
|
*/
|
||||||
|
byte *findBlock(int type);
|
||||||
};
|
};
|
||||||
|
|
||||||
enum sci_opcodes {
|
/**
|
||||||
op_bnot = 0x00, // 000
|
* Makes sure that a script and its superclasses get loaded to the heap.
|
||||||
op_add = 0x01, // 001
|
* If the script already has been loaded, only the number of lockers is
|
||||||
op_sub = 0x02, // 002
|
* increased. All scripts containing superclasses of this script are loaded
|
||||||
op_mul = 0x03, // 003
|
* recursively as well, unless 'recursive' is set to zero. The
|
||||||
op_div = 0x04, // 004
|
* complementary function is "script_uninstantiate()" below.
|
||||||
op_mod = 0x05, // 005
|
* @param[in] resMan The resource manager
|
||||||
op_shr = 0x06, // 006
|
* @param[in] segMan The segment manager
|
||||||
op_shl = 0x07, // 007
|
* @param[in] script_nr The script number to load
|
||||||
op_xor = 0x08, // 008
|
* @return The script's segment ID or 0 if out of heap
|
||||||
op_and = 0x09, // 009
|
*/
|
||||||
op_or = 0x0a, // 010
|
int script_instantiate(ResourceManager *resMan, SegManager *segMan, int script_nr);
|
||||||
op_neg = 0x0b, // 011
|
|
||||||
op_not = 0x0c, // 012
|
|
||||||
op_eq_ = 0x0d, // 013
|
|
||||||
op_ne_ = 0x0e, // 014
|
|
||||||
op_gt_ = 0x0f, // 015
|
|
||||||
op_ge_ = 0x10, // 016
|
|
||||||
op_lt_ = 0x11, // 017
|
|
||||||
op_le_ = 0x12, // 018
|
|
||||||
op_ugt_ = 0x13, // 019
|
|
||||||
op_uge_ = 0x14, // 020
|
|
||||||
op_ult_ = 0x15, // 021
|
|
||||||
op_ule_ = 0x16, // 022
|
|
||||||
op_bt = 0x17, // 023
|
|
||||||
op_bnt = 0x18, // 024
|
|
||||||
op_jmp = 0x19, // 025
|
|
||||||
op_ldi = 0x1a, // 026
|
|
||||||
op_push = 0x1b, // 027
|
|
||||||
op_pushi = 0x1c, // 028
|
|
||||||
op_toss = 0x1d, // 029
|
|
||||||
op_dup = 0x1e, // 030
|
|
||||||
op_link = 0x1f, // 031
|
|
||||||
op_call = 0x20, // 032
|
|
||||||
op_callk = 0x21, // 033
|
|
||||||
op_callb = 0x22, // 034
|
|
||||||
op_calle = 0x23, // 035
|
|
||||||
op_ret = 0x24, // 036
|
|
||||||
op_send = 0x25, // 037
|
|
||||||
// dummy 0x26, // 038
|
|
||||||
// dummy 0x27, // 039
|
|
||||||
op_class = 0x28, // 040
|
|
||||||
// dummy 0x29, // 041
|
|
||||||
op_self = 0x2a, // 042
|
|
||||||
op_super = 0x2b, // 043
|
|
||||||
op_rest = 0x2c, // 044
|
|
||||||
op_lea = 0x2d, // 045
|
|
||||||
op_selfID = 0x2e, // 046
|
|
||||||
// dummy 0x2f // 047
|
|
||||||
op_pprev = 0x30, // 048
|
|
||||||
op_pToa = 0x31, // 049
|
|
||||||
op_aTop = 0x32, // 050
|
|
||||||
op_pTos = 0x33, // 051
|
|
||||||
op_sTop = 0x34, // 052
|
|
||||||
op_ipToa = 0x35, // 053
|
|
||||||
op_dpToa = 0x36, // 054
|
|
||||||
op_ipTos = 0x37, // 055
|
|
||||||
op_dpTos = 0x38, // 056
|
|
||||||
op_lofsa = 0x39, // 057
|
|
||||||
op_lofss = 0x3a, // 058
|
|
||||||
op_push0 = 0x3b, // 059
|
|
||||||
op_push1 = 0x3c, // 060
|
|
||||||
op_push2 = 0x3d, // 061
|
|
||||||
op_pushSelf = 0x3e, // 062
|
|
||||||
op_line = 0x3f, // 063
|
|
||||||
op_lag = 0x40, // 064
|
|
||||||
op_lal = 0x41, // 065
|
|
||||||
op_lat = 0x42, // 066
|
|
||||||
op_lap = 0x43, // 067
|
|
||||||
op_lsg = 0x44, // 068
|
|
||||||
op_lsl = 0x45, // 069
|
|
||||||
op_lst = 0x46, // 070
|
|
||||||
op_lsp = 0x47, // 071
|
|
||||||
op_lagi = 0x48, // 072
|
|
||||||
op_lali = 0x49, // 073
|
|
||||||
op_lati = 0x4a, // 074
|
|
||||||
op_lapi = 0x4b, // 075
|
|
||||||
op_lsgi = 0x4c, // 076
|
|
||||||
op_lsli = 0x4d, // 077
|
|
||||||
op_lsti = 0x4e, // 078
|
|
||||||
op_lspi = 0x4f, // 079
|
|
||||||
op_sag = 0x50, // 080
|
|
||||||
op_sal = 0x51, // 081
|
|
||||||
op_sat = 0x52, // 082
|
|
||||||
op_sap = 0x53, // 083
|
|
||||||
op_ssg = 0x54, // 084
|
|
||||||
op_ssl = 0x55, // 085
|
|
||||||
op_sst = 0x56, // 086
|
|
||||||
op_ssp = 0x57, // 087
|
|
||||||
op_sagi = 0x58, // 088
|
|
||||||
op_sali = 0x59, // 089
|
|
||||||
op_sati = 0x5a, // 090
|
|
||||||
op_sapi = 0x5b, // 091
|
|
||||||
op_ssgi = 0x5c, // 092
|
|
||||||
op_ssli = 0x5d, // 093
|
|
||||||
op_ssti = 0x5e, // 094
|
|
||||||
op_sspi = 0x5f, // 095
|
|
||||||
op_plusag = 0x60, // 096
|
|
||||||
op_plusal = 0x61, // 097
|
|
||||||
op_plusat = 0x62, // 098
|
|
||||||
op_plusap = 0x63, // 099
|
|
||||||
op_plussg = 0x64, // 100
|
|
||||||
op_plussl = 0x65, // 101
|
|
||||||
op_plusst = 0x66, // 102
|
|
||||||
op_plussp = 0x67, // 103
|
|
||||||
op_plusagi = 0x68, // 104
|
|
||||||
op_plusali = 0x69, // 105
|
|
||||||
op_plusati = 0x6a, // 106
|
|
||||||
op_plusapi = 0x6b, // 107
|
|
||||||
op_plussgi = 0x6c, // 108
|
|
||||||
op_plussli = 0x6d, // 109
|
|
||||||
op_plussti = 0x6e, // 110
|
|
||||||
op_plusspi = 0x6f, // 111
|
|
||||||
op_minusag = 0x70, // 112
|
|
||||||
op_minusal = 0x71, // 113
|
|
||||||
op_minusat = 0x72, // 114
|
|
||||||
op_minusap = 0x73, // 115
|
|
||||||
op_minussg = 0x74, // 116
|
|
||||||
op_minussl = 0x75, // 117
|
|
||||||
op_minusst = 0x76, // 118
|
|
||||||
op_minussp = 0x77, // 119
|
|
||||||
op_minusagi = 0x78, // 120
|
|
||||||
op_minusali = 0x79, // 121
|
|
||||||
op_minusati = 0x7a, // 122
|
|
||||||
op_minusapi = 0x7b, // 123
|
|
||||||
op_minussgi = 0x7c, // 124
|
|
||||||
op_minussli = 0x7d, // 125
|
|
||||||
op_minussti = 0x7e, // 126
|
|
||||||
op_minusspi = 0x7f // 127
|
|
||||||
};
|
|
||||||
|
|
||||||
extern opcode_format g_opcode_formats[128][4];
|
/**
|
||||||
|
* Decreases the numer of lockers of a script and unloads it if that number
|
||||||
void script_adjust_opcode_formats();
|
* reaches zero.
|
||||||
|
* This function will recursively unload scripts containing its
|
||||||
|
* superclasses, if those aren't locked by other scripts as well.
|
||||||
|
* @param[in] segMan The segment manager
|
||||||
|
* @param[in] version The SCI version to use
|
||||||
|
* @param[in] script_nr The script number that is requestet to be unloaded
|
||||||
|
*/
|
||||||
|
void script_uninstantiate(SegManager *segMan, int script_nr);
|
||||||
|
|
||||||
} // End of namespace Sci
|
} // End of namespace Sci
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#include "sci/sci.h"
|
#include "sci/sci.h"
|
||||||
#include "sci/engine/seg_manager.h"
|
#include "sci/engine/seg_manager.h"
|
||||||
#include "sci/engine/state.h"
|
#include "sci/engine/state.h"
|
||||||
|
#include "sci/engine/script.h"
|
||||||
|
|
||||||
namespace Sci {
|
namespace Sci {
|
||||||
|
|
||||||
|
@ -837,7 +838,6 @@ Common::String SegManager::getString(reg_t pointer, int entries) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
byte *SegManager::allocDynmem(int size, const char *descr, reg_t *addr) {
|
byte *SegManager::allocDynmem(int size, const char *descr, reg_t *addr) {
|
||||||
SegmentId seg;
|
SegmentId seg;
|
||||||
SegmentObj *mobj = allocSegment(new DynMem(), &seg);
|
SegmentObj *mobj = allocSegment(new DynMem(), &seg);
|
||||||
|
@ -949,4 +949,46 @@ void SegManager::freeString(reg_t addr) {
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void SegManager::createClassTable() {
|
||||||
|
Resource *vocab996 = _resMan->findResource(ResourceId(kResourceTypeVocab, 996), 1);
|
||||||
|
|
||||||
|
if (!vocab996)
|
||||||
|
error("SegManager: failed to open vocab 996");
|
||||||
|
|
||||||
|
int totalClasses = vocab996->size >> 2;
|
||||||
|
_classTable.resize(totalClasses);
|
||||||
|
|
||||||
|
for (uint16 classNr = 0; classNr < totalClasses; classNr++) {
|
||||||
|
uint16 scriptNr = READ_SCI11ENDIAN_UINT16(vocab996->data + classNr * 4 + 2);
|
||||||
|
|
||||||
|
_classTable[classNr].reg = NULL_REG;
|
||||||
|
_classTable[classNr].script = scriptNr;
|
||||||
|
}
|
||||||
|
|
||||||
|
_resMan->unlockResource(vocab996);
|
||||||
|
}
|
||||||
|
|
||||||
|
reg_t SegManager::getClassAddress(int classnr, ScriptLoadType lock, reg_t caller) {
|
||||||
|
if (classnr == 0xffff)
|
||||||
|
return NULL_REG;
|
||||||
|
|
||||||
|
if (classnr < 0 || (int)_classTable.size() <= classnr || _classTable[classnr].script < 0) {
|
||||||
|
error("[VM] Attempt to dereference class %x, which doesn't exist (max %x)", classnr, _classTable.size());
|
||||||
|
return NULL_REG;
|
||||||
|
} else {
|
||||||
|
Class *the_class = &_classTable[classnr];
|
||||||
|
if (!the_class->reg.segment) {
|
||||||
|
getScriptSegment(the_class->script, lock);
|
||||||
|
|
||||||
|
if (!the_class->reg.segment) {
|
||||||
|
error("[VM] Trying to instantiate class %x by instantiating script 0x%x (%03d) failed;", classnr, the_class->script, the_class->script);
|
||||||
|
return NULL_REG;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
if (caller.segment != the_class->reg.segment)
|
||||||
|
getScript(the_class->reg.segment)->incrementLockers();
|
||||||
|
|
||||||
|
return the_class->reg;
|
||||||
|
}
|
||||||
|
}
|
||||||
} // End of namespace Sci
|
} // End of namespace Sci
|
||||||
|
|
|
@ -28,7 +28,9 @@
|
||||||
|
|
||||||
#include "common/scummsys.h"
|
#include "common/scummsys.h"
|
||||||
#include "common/serializer.h"
|
#include "common/serializer.h"
|
||||||
|
#include "sci/engine/script.h"
|
||||||
#include "sci/engine/vm.h"
|
#include "sci/engine/vm.h"
|
||||||
|
#include "sci/engine/vm_types.h"
|
||||||
#include "sci/engine/segment.h"
|
#include "sci/engine/segment.h"
|
||||||
|
|
||||||
namespace Sci {
|
namespace Sci {
|
||||||
|
@ -42,8 +44,6 @@ namespace Sci {
|
||||||
error("%s, line, %d, %s", __FILE__, __LINE__, msg); \
|
error("%s, line, %d, %s", __FILE__, __LINE__, msg); \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parameters for getScriptSegment().
|
* Parameters for getScriptSegment().
|
||||||
*/
|
*/
|
||||||
|
@ -53,6 +53,7 @@ enum ScriptLoadType {
|
||||||
SCRIPT_GET_LOCK = 3 /**< Load, if neccessary, and lock */
|
SCRIPT_GET_LOCK = 3 /**< Load, if neccessary, and lock */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class Script;
|
||||||
|
|
||||||
class SegManager : public Common::Serializable {
|
class SegManager : public Common::Serializable {
|
||||||
friend class Console;
|
friend class Console;
|
||||||
|
|
|
@ -86,196 +86,8 @@ SegmentObj *SegmentObj::createSegmentObj(SegmentType type) {
|
||||||
return mem;
|
return mem;
|
||||||
}
|
}
|
||||||
|
|
||||||
Script::Script() : SegmentObj(SEG_TYPE_SCRIPT) {
|
|
||||||
_nr = 0;
|
|
||||||
_buf = NULL;
|
|
||||||
_bufSize = 0;
|
|
||||||
_scriptSize = 0;
|
|
||||||
_heapSize = 0;
|
|
||||||
|
|
||||||
_synonyms = NULL;
|
|
||||||
_heapStart = NULL;
|
|
||||||
_exportTable = NULL;
|
|
||||||
|
|
||||||
_localsOffset = 0;
|
|
||||||
_localsSegment = 0;
|
|
||||||
_localsBlock = NULL;
|
|
||||||
_localsCount = 0;
|
|
||||||
|
|
||||||
_markedAsDeleted = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Script::~Script() {
|
|
||||||
freeScript();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Script::freeScript() {
|
|
||||||
free(_buf);
|
|
||||||
_buf = NULL;
|
|
||||||
_bufSize = 0;
|
|
||||||
|
|
||||||
_objects.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Script::init(int script_nr, ResourceManager *resMan) {
|
|
||||||
Resource *script = resMan->findResource(ResourceId(kResourceTypeScript, script_nr), 0);
|
|
||||||
|
|
||||||
_localsOffset = 0;
|
|
||||||
_localsBlock = NULL;
|
|
||||||
_localsCount = 0;
|
|
||||||
|
|
||||||
_markedAsDeleted = false;
|
|
||||||
|
|
||||||
_nr = script_nr;
|
|
||||||
_buf = 0;
|
|
||||||
_heapStart = 0;
|
|
||||||
|
|
||||||
_scriptSize = script->size;
|
|
||||||
_bufSize = script->size;
|
|
||||||
_heapSize = 0;
|
|
||||||
|
|
||||||
_lockers = 1;
|
|
||||||
|
|
||||||
if (getSciVersion() == SCI_VERSION_0_EARLY) {
|
|
||||||
_bufSize += READ_LE_UINT16(script->data) * 2;
|
|
||||||
} else if (getSciVersion() >= SCI_VERSION_1_1) {
|
|
||||||
/**
|
|
||||||
* In SCI11, the heap was in a separate space from the script.
|
|
||||||
* We append it to the end of the script, and adjust addressing accordingly.
|
|
||||||
* However, since we address the heap with a 16-bit pointer, the combined
|
|
||||||
* size of the stack and the heap must be 64KB. So far this has worked
|
|
||||||
* for SCI11, SCI2 and SCI21 games. SCI3 games use a different script format,
|
|
||||||
* and theoretically they can exceed the 64KB boundary using relocation.
|
|
||||||
*/
|
|
||||||
Resource *heap = resMan->findResource(ResourceId(kResourceTypeHeap, script_nr), 0);
|
|
||||||
_bufSize += heap->size;
|
|
||||||
_heapSize = heap->size;
|
|
||||||
|
|
||||||
// Ensure that the start of the heap resource can be word-aligned.
|
|
||||||
if (script->size & 2) {
|
|
||||||
_bufSize++;
|
|
||||||
_scriptSize++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// As mentioned above, the script and the heap together should not exceed 64KB
|
|
||||||
if (script->size + heap->size > 65535)
|
|
||||||
error("Script and heap sizes combined exceed 64K. This means a fundamental "
|
|
||||||
"design bug was made regarding SCI1.1 and newer games.\nPlease "
|
|
||||||
"report this error to the ScummVM team");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Script::load(ResourceManager *resMan) {
|
|
||||||
Resource *script = resMan->findResource(ResourceId(kResourceTypeScript, _nr), 0);
|
|
||||||
assert(script != 0);
|
|
||||||
|
|
||||||
_buf = (byte *)malloc(_bufSize);
|
|
||||||
assert(_buf);
|
|
||||||
|
|
||||||
assert(_bufSize >= script->size);
|
|
||||||
memcpy(_buf, script->data, script->size);
|
|
||||||
|
|
||||||
if (getSciVersion() >= SCI_VERSION_1_1) {
|
|
||||||
Resource *heap = resMan->findResource(ResourceId(kResourceTypeHeap, _nr), 0);
|
|
||||||
assert(heap != 0);
|
|
||||||
|
|
||||||
_heapStart = _buf + _scriptSize;
|
|
||||||
|
|
||||||
assert(_bufSize - _scriptSize <= heap->size);
|
|
||||||
memcpy(_heapStart, heap->data, heap->size);
|
|
||||||
}
|
|
||||||
|
|
||||||
_exportTable = 0;
|
|
||||||
_numExports = 0;
|
|
||||||
_synonyms = 0;
|
|
||||||
_numSynonyms = 0;
|
|
||||||
|
|
||||||
if (getSciVersion() >= SCI_VERSION_1_1) {
|
|
||||||
if (READ_LE_UINT16(_buf + 1 + 5) > 0) { // does the script have an export table?
|
|
||||||
_exportTable = (const uint16 *)(_buf + 1 + 5 + 2);
|
|
||||||
_numExports = READ_SCI11ENDIAN_UINT16(_exportTable - 1);
|
|
||||||
}
|
|
||||||
_localsOffset = _scriptSize + 4;
|
|
||||||
_localsCount = READ_SCI11ENDIAN_UINT16(_buf + _localsOffset - 2);
|
|
||||||
} else {
|
|
||||||
_exportTable = (const uint16 *)findBlock(SCI_OBJ_EXPORTS);
|
|
||||||
if (_exportTable) {
|
|
||||||
_numExports = READ_SCI11ENDIAN_UINT16(_exportTable + 1);
|
|
||||||
_exportTable += 3; // skip header plus 2 bytes (_exportTable is a uint16 pointer)
|
|
||||||
}
|
|
||||||
_synonyms = findBlock(SCI_OBJ_SYNONYMS);
|
|
||||||
if (_synonyms) {
|
|
||||||
_numSynonyms = READ_SCI11ENDIAN_UINT16(_synonyms + 2) / 4;
|
|
||||||
_synonyms += 4; // skip header
|
|
||||||
}
|
|
||||||
const byte* localsBlock = findBlock(SCI_OBJ_LOCALVARS);
|
|
||||||
if (localsBlock) {
|
|
||||||
_localsOffset = localsBlock - _buf + 4;
|
|
||||||
_localsCount = (READ_LE_UINT16(_buf + _localsOffset - 2) - 4) >> 1; // half block size
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (getSciVersion() > SCI_VERSION_0_EARLY) {
|
|
||||||
// Does the script actually have locals? If not, set the locals offset to 0
|
|
||||||
if (!_localsCount)
|
|
||||||
_localsOffset = 0;
|
|
||||||
|
|
||||||
if (_localsOffset + _localsCount * 2 + 1 >= (int)_bufSize) {
|
|
||||||
error("Locals extend beyond end of script: offset %04x, count %d vs size %d", _localsOffset, _localsCount, _bufSize);
|
|
||||||
_localsCount = (_bufSize - _localsOffset) >> 1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Old script block. There won't be a localvar block in this case.
|
|
||||||
// Instead, the script starts with a 16 bit int specifying the
|
|
||||||
// number of locals we need; these are then allocated and zeroed.
|
|
||||||
_localsCount = READ_LE_UINT16(_buf);
|
|
||||||
_localsOffset = -_localsCount * 2; // Make sure it's invalid
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Object *Script::allocateObject(uint16 offset) {
|
|
||||||
return &_objects[offset];
|
|
||||||
}
|
|
||||||
|
|
||||||
Object *Script::getObject(uint16 offset) {
|
|
||||||
if (_objects.contains(offset))
|
|
||||||
return &_objects[offset];
|
|
||||||
else
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Object *Script::getObject(uint16 offset) const {
|
|
||||||
if (_objects.contains(offset))
|
|
||||||
return &_objects[offset];
|
|
||||||
else
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
Object *Script::scriptObjInit(reg_t obj_pos, bool fullObjectInit) {
|
|
||||||
Object *obj;
|
|
||||||
|
|
||||||
if (getSciVersion() < SCI_VERSION_1_1 && fullObjectInit)
|
|
||||||
obj_pos.offset += 8; // magic offset (SCRIPT_OBJECT_MAGIC_OFFSET)
|
|
||||||
|
|
||||||
VERIFY(obj_pos.offset < _bufSize, "Attempt to initialize object beyond end of script\n");
|
|
||||||
|
|
||||||
obj = allocateObject(obj_pos.offset);
|
|
||||||
|
|
||||||
VERIFY(obj_pos.offset + kOffsetFunctionArea < (int)_bufSize, "Function area pointer stored beyond end of script\n");
|
|
||||||
|
|
||||||
obj->init(_buf, obj_pos, fullObjectInit);
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Script::scriptObjRemove(reg_t obj_pos) {
|
|
||||||
if (getSciVersion() < SCI_VERSION_1_1)
|
|
||||||
obj_pos.offset += 8;
|
|
||||||
|
|
||||||
_objects.erase(obj_pos.toUint16());
|
|
||||||
}
|
|
||||||
|
|
||||||
// This helper function is used by Script::relocateLocal and Object::relocate
|
// This helper function is used by Script::relocateLocal and Object::relocate
|
||||||
|
// Duplicate in segment.cpp and script.cpp
|
||||||
static bool relocateBlock(Common::Array<reg_t> &block, int block_location, SegmentId segment, int location, size_t scriptSize) {
|
static bool relocateBlock(Common::Array<reg_t> &block, int block_location, SegmentId segment, int location, size_t scriptSize) {
|
||||||
int rel = location - block_location;
|
int rel = location - block_location;
|
||||||
|
|
||||||
|
@ -298,148 +110,12 @@ static bool relocateBlock(Common::Array<reg_t> &block, int block_location, Segme
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Script::relocateLocal(SegmentId segment, int location) {
|
|
||||||
if (_localsBlock)
|
|
||||||
return relocateBlock(_localsBlock->_locals, _localsOffset, segment, location, _scriptSize);
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Script::relocate(reg_t block) {
|
|
||||||
byte *heap = _buf;
|
|
||||||
uint16 heapSize = (uint16)_bufSize;
|
|
||||||
uint16 heapOffset = 0;
|
|
||||||
|
|
||||||
if (getSciVersion() >= SCI_VERSION_1_1) {
|
|
||||||
heap = _heapStart;
|
|
||||||
heapSize = (uint16)_heapSize;
|
|
||||||
heapOffset = _scriptSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
VERIFY(block.offset < (uint16)heapSize && READ_SCI11ENDIAN_UINT16(heap + block.offset) * 2 + block.offset < (uint16)heapSize,
|
|
||||||
"Relocation block outside of script\n");
|
|
||||||
|
|
||||||
int count = READ_SCI11ENDIAN_UINT16(heap + block.offset);
|
|
||||||
int exportIndex = 0;
|
|
||||||
int pos = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
pos = READ_SCI11ENDIAN_UINT16(heap + block.offset + 2 + (exportIndex * 2)) + heapOffset;
|
|
||||||
// This occurs in SCI01/SCI1 games where usually one export value
|
|
||||||
// is zero. It seems that in this situation, we should skip the
|
|
||||||
// export and move to the next one, though the total count of valid
|
|
||||||
// exports remains the same
|
|
||||||
if (!pos) {
|
|
||||||
exportIndex++;
|
|
||||||
pos = READ_SCI11ENDIAN_UINT16(heap + block.offset + 2 + (exportIndex * 2)) + heapOffset;
|
|
||||||
if (!pos)
|
|
||||||
error("Script::relocate(): Consecutive zero exports found");
|
|
||||||
}
|
|
||||||
|
|
||||||
// In SCI0-SCI1, script local variables, objects and code are relocated. We only relocate
|
|
||||||
// locals and objects here, and ignore relocation of code blocks. In SCI1.1 and newer
|
|
||||||
// versions, only locals and objects are relocated.
|
|
||||||
if (!relocateLocal(block.segment, pos)) {
|
|
||||||
// Not a local? It's probably an object or code block. If it's an object, relocate it.
|
|
||||||
const ObjMap::iterator end = _objects.end();
|
|
||||||
for (ObjMap::iterator it = _objects.begin(); it != end; ++it)
|
|
||||||
if (it->_value.relocate(block.segment, pos, _scriptSize))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
exportIndex++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Script::incrementLockers() {
|
|
||||||
_lockers++;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Script::decrementLockers() {
|
|
||||||
if (_lockers > 0)
|
|
||||||
_lockers--;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Script::getLockers() const {
|
|
||||||
return _lockers;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Script::setLockers(int lockers) {
|
|
||||||
_lockers = lockers;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16 Script::validateExportFunc(int pubfunct) {
|
|
||||||
bool exportsAreWide = (g_sci->_features->detectLofsType() == SCI_VERSION_1_MIDDLE);
|
|
||||||
|
|
||||||
if (_numExports <= pubfunct) {
|
|
||||||
error("validateExportFunc(): pubfunct is invalid");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (exportsAreWide)
|
|
||||||
pubfunct *= 2;
|
|
||||||
uint16 offset = READ_SCI11ENDIAN_UINT16(_exportTable + pubfunct);
|
|
||||||
VERIFY(offset < _bufSize, "invalid export function pointer");
|
|
||||||
|
|
||||||
return offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
byte *Script::findBlock(int type) {
|
|
||||||
byte *buf = _buf;
|
|
||||||
bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY);
|
|
||||||
|
|
||||||
if (oldScriptHeader)
|
|
||||||
buf += 2;
|
|
||||||
|
|
||||||
do {
|
|
||||||
int seekerType = READ_LE_UINT16(buf);
|
|
||||||
|
|
||||||
if (seekerType == 0)
|
|
||||||
break;
|
|
||||||
if (seekerType == type)
|
|
||||||
return buf;
|
|
||||||
|
|
||||||
int seekerSize = READ_LE_UINT16(buf + 2);
|
|
||||||
assert(seekerSize > 0);
|
|
||||||
buf += seekerSize;
|
|
||||||
} while (1);
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// memory operations
|
|
||||||
|
|
||||||
void Script::mcpyInOut(int dst, const void *src, size_t n) {
|
|
||||||
if (_buf) {
|
|
||||||
assert(dst + n <= _bufSize);
|
|
||||||
memcpy(_buf + dst, src, n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SegmentRef SegmentObj::dereference(reg_t pointer) {
|
SegmentRef SegmentObj::dereference(reg_t pointer) {
|
||||||
error("Error: Trying to dereference pointer %04x:%04x to inappropriate segment",
|
error("Error: Trying to dereference pointer %04x:%04x to inappropriate segment",
|
||||||
PRINT_REG(pointer));
|
PRINT_REG(pointer));
|
||||||
return SegmentRef();
|
return SegmentRef();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Script::isValidOffset(uint16 offset) const {
|
|
||||||
return offset < _bufSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
SegmentRef Script::dereference(reg_t pointer) {
|
|
||||||
if (pointer.offset > _bufSize) {
|
|
||||||
error("Script::dereference(): Attempt to dereference invalid pointer %04x:%04x into script segment (script size=%d)",
|
|
||||||
PRINT_REG(pointer), (uint)_bufSize);
|
|
||||||
return SegmentRef();
|
|
||||||
}
|
|
||||||
|
|
||||||
SegmentRef ret;
|
|
||||||
ret.isRaw = true;
|
|
||||||
ret.maxSize = _bufSize - pointer.offset;
|
|
||||||
ret.raw = _buf + pointer.offset;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool LocalVariables::isValidOffset(uint16 offset) const {
|
bool LocalVariables::isValidOffset(uint16 offset) const {
|
||||||
return offset < _locals.size() * 2;
|
return offset < _locals.size() * 2;
|
||||||
|
|
|
@ -331,182 +331,6 @@ private:
|
||||||
reg_t _pos; /**< Object offset within its script; for clones, this is their base */
|
reg_t _pos; /**< Object offset within its script; for clones, this is their base */
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef Common::HashMap<uint16, Object> ObjMap;
|
|
||||||
|
|
||||||
class Script : public SegmentObj {
|
|
||||||
public:
|
|
||||||
int _nr; /**< Script number */
|
|
||||||
byte *_buf; /**< Static data buffer, or NULL if not used */
|
|
||||||
byte *_heapStart; /**< Start of heap if SCI1.1, NULL otherwise */
|
|
||||||
|
|
||||||
uint32 getScriptSize() { return _scriptSize; }
|
|
||||||
uint32 getHeapSize() { return _heapSize; }
|
|
||||||
uint32 getBufSize() { return _bufSize; }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
int _lockers; /**< Number of classes and objects that require this script */
|
|
||||||
|
|
||||||
private:
|
|
||||||
size_t _scriptSize;
|
|
||||||
size_t _heapSize;
|
|
||||||
uint16 _bufSize;
|
|
||||||
|
|
||||||
const uint16 *_exportTable; /**< Abs. offset of the export table or 0 if not present */
|
|
||||||
uint16 _numExports; /**< Number of entries in the exports table */
|
|
||||||
|
|
||||||
const byte *_synonyms; /**< Synonyms block or 0 if not present*/
|
|
||||||
uint16 _numSynonyms; /**< Number of entries in the synonyms block */
|
|
||||||
|
|
||||||
int _localsOffset;
|
|
||||||
uint16 _localsCount;
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* Table for objects, contains property variables.
|
|
||||||
* Indexed by the TODO offset.
|
|
||||||
*/
|
|
||||||
ObjMap _objects;
|
|
||||||
|
|
||||||
int getLocalsOffset() const { return _localsOffset; }
|
|
||||||
uint16 getLocalsCount() const { return _localsCount; }
|
|
||||||
SegmentId _localsSegment; /**< The local variable segment */
|
|
||||||
LocalVariables *_localsBlock;
|
|
||||||
|
|
||||||
bool _markedAsDeleted;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Script();
|
|
||||||
~Script();
|
|
||||||
|
|
||||||
void freeScript();
|
|
||||||
void init(int script_nr, ResourceManager *resMan);
|
|
||||||
void load(ResourceManager *resMan);
|
|
||||||
|
|
||||||
virtual bool isValidOffset(uint16 offset) const;
|
|
||||||
virtual SegmentRef dereference(reg_t pointer);
|
|
||||||
virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr) const;
|
|
||||||
virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr);
|
|
||||||
virtual void listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) const;
|
|
||||||
virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note) const;
|
|
||||||
|
|
||||||
virtual void saveLoadWithSerializer(Common::Serializer &ser);
|
|
||||||
|
|
||||||
Object *allocateObject(uint16 offset);
|
|
||||||
Object *getObject(uint16 offset);
|
|
||||||
const Object *getObject(uint16 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);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes a script object
|
|
||||||
* @param obj_pos Location (segment, offset) of the object.
|
|
||||||
*/
|
|
||||||
void scriptObjRemove(reg_t obj_pos);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Processes a relocation block witin a script
|
|
||||||
* This function is idempotent, but it must only be called after all
|
|
||||||
* objects have been instantiated, or a run-time error will occur.
|
|
||||||
* @param obj_pos Location (segment, offset) of the block
|
|
||||||
* @return Location of the relocation block
|
|
||||||
*/
|
|
||||||
void relocate(reg_t block);
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool relocateLocal(SegmentId segment, int location);
|
|
||||||
|
|
||||||
public:
|
|
||||||
// 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
|
|
||||||
*/
|
|
||||||
int getLockers() const;
|
|
||||||
|
|
||||||
/** Sets the number of locks held on this script. */
|
|
||||||
void setLockers(int lockers);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves a pointer to the exports of this script
|
|
||||||
* @return pointer to the exports.
|
|
||||||
*/
|
|
||||||
const uint16 *getExportTable() const { return _exportTable; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 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
|
|
||||||
* @return NULL if the public function is invalid, its
|
|
||||||
* offset into the script's segment otherwise
|
|
||||||
*/
|
|
||||||
uint16 validateExportFunc(int pubfunct);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Copies a byte string into a script's heap representation.
|
|
||||||
* @param dst script-relative offset of the destination area
|
|
||||||
* @param src pointer to the data source location
|
|
||||||
* @param n number of bytes to copy
|
|
||||||
*/
|
|
||||||
void mcpyInOut(int dst, const void *src, size_t n);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Finds the pointer where a block of a specific type starts from
|
|
||||||
*/
|
|
||||||
byte *findBlock(int type);
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Data stack */
|
/** Data stack */
|
||||||
struct DataStack : SegmentObj {
|
struct DataStack : SegmentObj {
|
||||||
int _capacity; /**< Number of stack entries */
|
int _capacity; /**< Number of stack entries */
|
||||||
|
|
|
@ -49,6 +49,86 @@ const reg_t SIGNAL_REG = {0, SIGNAL_OFFSET};
|
||||||
|
|
||||||
#define SCI_XS_CALLEE_LOCALS ((SegmentId)-1)
|
#define SCI_XS_CALLEE_LOCALS ((SegmentId)-1)
|
||||||
|
|
||||||
|
#define END Script_None
|
||||||
|
|
||||||
|
opcode_format g_opcode_formats[128][4] = {
|
||||||
|
/*00*/
|
||||||
|
{Script_None}, {Script_None}, {Script_None}, {Script_None},
|
||||||
|
/*04*/
|
||||||
|
{Script_None}, {Script_None}, {Script_None}, {Script_None},
|
||||||
|
/*08*/
|
||||||
|
{Script_None}, {Script_None}, {Script_None}, {Script_None},
|
||||||
|
/*0C*/
|
||||||
|
{Script_None}, {Script_None}, {Script_None}, {Script_None},
|
||||||
|
/*10*/
|
||||||
|
{Script_None}, {Script_None}, {Script_None}, {Script_None},
|
||||||
|
/*14*/
|
||||||
|
{Script_None}, {Script_None}, {Script_None}, {Script_SRelative, END},
|
||||||
|
/*18*/
|
||||||
|
{Script_SRelative, END}, {Script_SRelative, END}, {Script_SVariable, END}, {Script_None},
|
||||||
|
/*1C*/
|
||||||
|
{Script_SVariable, END}, {Script_None}, {Script_None}, {Script_Variable, END},
|
||||||
|
/*20*/
|
||||||
|
{Script_SRelative, Script_Byte, END}, {Script_Variable, Script_Byte, END}, {Script_Variable, Script_Byte, END}, {Script_Variable, Script_SVariable, Script_Byte, END},
|
||||||
|
/*24 (24=ret)*/
|
||||||
|
{Script_End}, {Script_Byte, END}, {Script_Invalid}, {Script_Invalid},
|
||||||
|
/*28*/
|
||||||
|
{Script_Variable, END}, {Script_Invalid}, {Script_Byte, END}, {Script_Variable, Script_Byte, END},
|
||||||
|
/*2C*/
|
||||||
|
{Script_SVariable, END}, {Script_SVariable, Script_Variable, END}, {Script_None}, {Script_Invalid},
|
||||||
|
/*30*/
|
||||||
|
{Script_None}, {Script_Property, END}, {Script_Property, END}, {Script_Property, END},
|
||||||
|
/*34*/
|
||||||
|
{Script_Property, END}, {Script_Property, END}, {Script_Property, END}, {Script_Property, END},
|
||||||
|
/*38*/
|
||||||
|
{Script_Property, END}, {Script_SRelative, END}, {Script_SRelative, END}, {Script_None},
|
||||||
|
/*3C*/
|
||||||
|
{Script_None}, {Script_None}, {Script_None}, {Script_Word},
|
||||||
|
/*40-4F*/
|
||||||
|
{Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
|
||||||
|
{Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
|
||||||
|
{Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
|
||||||
|
{Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
|
||||||
|
/*50-5F*/
|
||||||
|
{Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
|
||||||
|
{Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
|
||||||
|
{Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
|
||||||
|
{Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
|
||||||
|
/*60-6F*/
|
||||||
|
{Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
|
||||||
|
{Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
|
||||||
|
{Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
|
||||||
|
{Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
|
||||||
|
/*70-7F*/
|
||||||
|
{Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
|
||||||
|
{Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
|
||||||
|
{Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
|
||||||
|
{Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}
|
||||||
|
};
|
||||||
|
#undef END
|
||||||
|
|
||||||
|
// TODO: script_adjust_opcode_formats should probably be part of the
|
||||||
|
// constructor (?) of a VirtualMachine or a ScriptManager class.
|
||||||
|
void script_adjust_opcode_formats() {
|
||||||
|
if (g_sci->_features->detectLofsType() != SCI_VERSION_0_EARLY) {
|
||||||
|
g_opcode_formats[op_lofsa][0] = Script_Offset;
|
||||||
|
g_opcode_formats[op_lofss][0] = Script_Offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef ENABLE_SCI32
|
||||||
|
// In SCI32, some arguments are now words instead of bytes
|
||||||
|
if (getSciVersion() >= SCI_VERSION_2) {
|
||||||
|
g_opcode_formats[op_calle][2] = Script_Word;
|
||||||
|
g_opcode_formats[op_callk][1] = Script_Word;
|
||||||
|
g_opcode_formats[op_super][1] = Script_Word;
|
||||||
|
g_opcode_formats[op_send][0] = Script_Word;
|
||||||
|
g_opcode_formats[op_self][0] = Script_Word;
|
||||||
|
g_opcode_formats[op_call][1] = Script_Word;
|
||||||
|
g_opcode_formats[op_callb][1] = Script_Word;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds an entry to the top of the execution stack.
|
* Adds an entry to the top of the execution stack.
|
||||||
*
|
*
|
||||||
|
|
|
@ -118,6 +118,160 @@ enum {
|
||||||
GC_INTERVAL = 32768
|
GC_INTERVAL = 32768
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Opcode formats
|
||||||
|
enum opcode_format {
|
||||||
|
Script_Invalid = -1,
|
||||||
|
Script_None = 0,
|
||||||
|
Script_Byte,
|
||||||
|
Script_SByte,
|
||||||
|
Script_Word,
|
||||||
|
Script_SWord,
|
||||||
|
Script_Variable,
|
||||||
|
Script_SVariable,
|
||||||
|
Script_SRelative,
|
||||||
|
Script_Property,
|
||||||
|
Script_Global,
|
||||||
|
Script_Local,
|
||||||
|
Script_Temp,
|
||||||
|
Script_Param,
|
||||||
|
Script_Offset,
|
||||||
|
Script_End
|
||||||
|
};
|
||||||
|
|
||||||
|
enum sci_opcodes {
|
||||||
|
op_bnot = 0x00, // 000
|
||||||
|
op_add = 0x01, // 001
|
||||||
|
op_sub = 0x02, // 002
|
||||||
|
op_mul = 0x03, // 003
|
||||||
|
op_div = 0x04, // 004
|
||||||
|
op_mod = 0x05, // 005
|
||||||
|
op_shr = 0x06, // 006
|
||||||
|
op_shl = 0x07, // 007
|
||||||
|
op_xor = 0x08, // 008
|
||||||
|
op_and = 0x09, // 009
|
||||||
|
op_or = 0x0a, // 010
|
||||||
|
op_neg = 0x0b, // 011
|
||||||
|
op_not = 0x0c, // 012
|
||||||
|
op_eq_ = 0x0d, // 013
|
||||||
|
op_ne_ = 0x0e, // 014
|
||||||
|
op_gt_ = 0x0f, // 015
|
||||||
|
op_ge_ = 0x10, // 016
|
||||||
|
op_lt_ = 0x11, // 017
|
||||||
|
op_le_ = 0x12, // 018
|
||||||
|
op_ugt_ = 0x13, // 019
|
||||||
|
op_uge_ = 0x14, // 020
|
||||||
|
op_ult_ = 0x15, // 021
|
||||||
|
op_ule_ = 0x16, // 022
|
||||||
|
op_bt = 0x17, // 023
|
||||||
|
op_bnt = 0x18, // 024
|
||||||
|
op_jmp = 0x19, // 025
|
||||||
|
op_ldi = 0x1a, // 026
|
||||||
|
op_push = 0x1b, // 027
|
||||||
|
op_pushi = 0x1c, // 028
|
||||||
|
op_toss = 0x1d, // 029
|
||||||
|
op_dup = 0x1e, // 030
|
||||||
|
op_link = 0x1f, // 031
|
||||||
|
op_call = 0x20, // 032
|
||||||
|
op_callk = 0x21, // 033
|
||||||
|
op_callb = 0x22, // 034
|
||||||
|
op_calle = 0x23, // 035
|
||||||
|
op_ret = 0x24, // 036
|
||||||
|
op_send = 0x25, // 037
|
||||||
|
// dummy 0x26, // 038
|
||||||
|
// dummy 0x27, // 039
|
||||||
|
op_class = 0x28, // 040
|
||||||
|
// dummy 0x29, // 041
|
||||||
|
op_self = 0x2a, // 042
|
||||||
|
op_super = 0x2b, // 043
|
||||||
|
op_rest = 0x2c, // 044
|
||||||
|
op_lea = 0x2d, // 045
|
||||||
|
op_selfID = 0x2e, // 046
|
||||||
|
// dummy 0x2f // 047
|
||||||
|
op_pprev = 0x30, // 048
|
||||||
|
op_pToa = 0x31, // 049
|
||||||
|
op_aTop = 0x32, // 050
|
||||||
|
op_pTos = 0x33, // 051
|
||||||
|
op_sTop = 0x34, // 052
|
||||||
|
op_ipToa = 0x35, // 053
|
||||||
|
op_dpToa = 0x36, // 054
|
||||||
|
op_ipTos = 0x37, // 055
|
||||||
|
op_dpTos = 0x38, // 056
|
||||||
|
op_lofsa = 0x39, // 057
|
||||||
|
op_lofss = 0x3a, // 058
|
||||||
|
op_push0 = 0x3b, // 059
|
||||||
|
op_push1 = 0x3c, // 060
|
||||||
|
op_push2 = 0x3d, // 061
|
||||||
|
op_pushSelf = 0x3e, // 062
|
||||||
|
op_line = 0x3f, // 063
|
||||||
|
op_lag = 0x40, // 064
|
||||||
|
op_lal = 0x41, // 065
|
||||||
|
op_lat = 0x42, // 066
|
||||||
|
op_lap = 0x43, // 067
|
||||||
|
op_lsg = 0x44, // 068
|
||||||
|
op_lsl = 0x45, // 069
|
||||||
|
op_lst = 0x46, // 070
|
||||||
|
op_lsp = 0x47, // 071
|
||||||
|
op_lagi = 0x48, // 072
|
||||||
|
op_lali = 0x49, // 073
|
||||||
|
op_lati = 0x4a, // 074
|
||||||
|
op_lapi = 0x4b, // 075
|
||||||
|
op_lsgi = 0x4c, // 076
|
||||||
|
op_lsli = 0x4d, // 077
|
||||||
|
op_lsti = 0x4e, // 078
|
||||||
|
op_lspi = 0x4f, // 079
|
||||||
|
op_sag = 0x50, // 080
|
||||||
|
op_sal = 0x51, // 081
|
||||||
|
op_sat = 0x52, // 082
|
||||||
|
op_sap = 0x53, // 083
|
||||||
|
op_ssg = 0x54, // 084
|
||||||
|
op_ssl = 0x55, // 085
|
||||||
|
op_sst = 0x56, // 086
|
||||||
|
op_ssp = 0x57, // 087
|
||||||
|
op_sagi = 0x58, // 088
|
||||||
|
op_sali = 0x59, // 089
|
||||||
|
op_sati = 0x5a, // 090
|
||||||
|
op_sapi = 0x5b, // 091
|
||||||
|
op_ssgi = 0x5c, // 092
|
||||||
|
op_ssli = 0x5d, // 093
|
||||||
|
op_ssti = 0x5e, // 094
|
||||||
|
op_sspi = 0x5f, // 095
|
||||||
|
op_plusag = 0x60, // 096
|
||||||
|
op_plusal = 0x61, // 097
|
||||||
|
op_plusat = 0x62, // 098
|
||||||
|
op_plusap = 0x63, // 099
|
||||||
|
op_plussg = 0x64, // 100
|
||||||
|
op_plussl = 0x65, // 101
|
||||||
|
op_plusst = 0x66, // 102
|
||||||
|
op_plussp = 0x67, // 103
|
||||||
|
op_plusagi = 0x68, // 104
|
||||||
|
op_plusali = 0x69, // 105
|
||||||
|
op_plusati = 0x6a, // 106
|
||||||
|
op_plusapi = 0x6b, // 107
|
||||||
|
op_plussgi = 0x6c, // 108
|
||||||
|
op_plussli = 0x6d, // 109
|
||||||
|
op_plussti = 0x6e, // 110
|
||||||
|
op_plusspi = 0x6f, // 111
|
||||||
|
op_minusag = 0x70, // 112
|
||||||
|
op_minusal = 0x71, // 113
|
||||||
|
op_minusat = 0x72, // 114
|
||||||
|
op_minusap = 0x73, // 115
|
||||||
|
op_minussg = 0x74, // 116
|
||||||
|
op_minussl = 0x75, // 117
|
||||||
|
op_minusst = 0x76, // 118
|
||||||
|
op_minussp = 0x77, // 119
|
||||||
|
op_minusagi = 0x78, // 120
|
||||||
|
op_minusali = 0x79, // 121
|
||||||
|
op_minusati = 0x7a, // 122
|
||||||
|
op_minusapi = 0x7b, // 123
|
||||||
|
op_minussgi = 0x7c, // 124
|
||||||
|
op_minussli = 0x7d, // 125
|
||||||
|
op_minussti = 0x7e, // 126
|
||||||
|
op_minusspi = 0x7f // 127
|
||||||
|
};
|
||||||
|
|
||||||
|
extern opcode_format g_opcode_formats[128][4];
|
||||||
|
|
||||||
|
void script_adjust_opcode_formats();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executes function pubfunct of the specified script.
|
* Executes function pubfunct of the specified script.
|
||||||
|
@ -194,30 +348,6 @@ void script_debug(EngineState *s);
|
||||||
SelectorType lookupSelector(SegManager *segMan, reg_t obj, Selector selectorid,
|
SelectorType lookupSelector(SegManager *segMan, reg_t obj, Selector selectorid,
|
||||||
ObjVarRef *varp, reg_t *fptr);
|
ObjVarRef *varp, reg_t *fptr);
|
||||||
|
|
||||||
/**
|
|
||||||
* Makes sure that a script and its superclasses get loaded to the heap.
|
|
||||||
* If the script already has been loaded, only the number of lockers is
|
|
||||||
* increased. All scripts containing superclasses of this script are loaded
|
|
||||||
* recursively as well, unless 'recursive' is set to zero. The
|
|
||||||
* complementary function is "script_uninstantiate()" below.
|
|
||||||
* @param[in] resMan The resource manager
|
|
||||||
* @param[in] segMan The segment manager
|
|
||||||
* @param[in] script_nr The script number to load
|
|
||||||
* @return The script's segment ID or 0 if out of heap
|
|
||||||
*/
|
|
||||||
int script_instantiate(ResourceManager *resMan, SegManager *segMan, int script_nr);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decreases the numer of lockers of a script and unloads it if that number
|
|
||||||
* reaches zero.
|
|
||||||
* This function will recursively unload scripts containing its
|
|
||||||
* superclasses, if those aren't locked by other scripts as well.
|
|
||||||
* @param[in] segMan The segment manager
|
|
||||||
* @param[in] version The SCI version to use
|
|
||||||
* @param[in] script_nr The script number that is requestet to be unloaded
|
|
||||||
*/
|
|
||||||
void script_uninstantiate(SegManager *segMan, int script_nr);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read a PMachine instruction from a memory buffer and return its length.
|
* Read a PMachine instruction from a memory buffer and return its length.
|
||||||
*
|
*
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue