2009-02-17 15:09:09 +00:00
|
|
|
/* 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.
|
|
|
|
*
|
|
|
|
* $URL$
|
|
|
|
* $Id$
|
|
|
|
*
|
|
|
|
*/
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-05-13 16:52:41 +00:00
|
|
|
#include "sci/sci.h"
|
2009-05-15 14:07:45 +00:00
|
|
|
#include "sci/resource.h"
|
2010-02-13 17:44:58 +00:00
|
|
|
#include "sci/engine/features.h"
|
2009-02-27 02:23:40 +00:00
|
|
|
#include "sci/engine/state.h"
|
2010-01-29 11:05:06 +00:00
|
|
|
#include "sci/engine/kernel.h"
|
|
|
|
#include "sci/engine/script.h"
|
|
|
|
|
2009-02-16 00:34:40 +00:00
|
|
|
#include "common/util.h"
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-21 10:23:36 +00:00
|
|
|
namespace Sci {
|
|
|
|
|
2009-02-15 06:10:59 +00:00
|
|
|
#define END Script_None
|
|
|
|
|
2009-04-20 19:28:33 +00:00
|
|
|
opcode_format g_opcode_formats[128][4] = {
|
2009-02-15 22:28:50 +00:00
|
|
|
/*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*/
|
2010-01-28 19:22:58 +00:00
|
|
|
{Script_None}, {Script_None}, {Script_None}, {Script_Word},
|
2009-02-15 22:28:50 +00:00
|
|
|
/*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}
|
2009-02-15 06:10:59 +00:00
|
|
|
};
|
|
|
|
#undef END
|
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
// TODO: script_adjust_opcode_formats should probably be part of the
|
|
|
|
// constructor (?) of a VirtualMachine or a ScriptManager class.
|
2009-08-30 14:53:58 +00:00
|
|
|
void script_adjust_opcode_formats(EngineState *s) {
|
2009-08-16 19:18:19 +00:00
|
|
|
// TODO: Check that this is correct
|
2010-02-13 17:44:58 +00:00
|
|
|
if (g_sci->_features->detectLofsType() != SCI_VERSION_0_EARLY) {
|
2009-04-20 19:28:33 +00:00
|
|
|
g_opcode_formats[op_lofsa][0] = Script_Offset;
|
|
|
|
g_opcode_formats[op_lofss][0] = Script_Offset;
|
2009-02-15 06:10:59 +00:00
|
|
|
}
|
2009-08-30 14:53:58 +00:00
|
|
|
|
2009-08-26 22:11:31 +00:00
|
|
|
#ifdef ENABLE_SCI32
|
|
|
|
// In SCI32, some arguments are now words instead of bytes
|
2009-09-23 10:55:35 +00:00
|
|
|
if (getSciVersion() >= SCI_VERSION_2) {
|
2009-08-26 22:11:31 +00:00
|
|
|
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
|
2009-02-15 06:10:59 +00:00
|
|
|
}
|
2009-02-15 22:28:50 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
#define INST_LOOKUP_CLASS(id) ((id == 0xffff)? NULL_REG : segMan->getClassAddress(id, SCRIPT_GET_LOCK, reg))
|
2009-08-26 18:13:11 +00:00
|
|
|
|
2010-02-03 01:34:15 +00:00
|
|
|
|
|
|
|
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_LE_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;"
|
|
|
|
" Entering debugger.", 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SegManager::scriptInitialiseLocalsZero(SegmentId seg, int count) {
|
|
|
|
Script *scr = getScript(seg);
|
|
|
|
|
|
|
|
scr->_localsOffset = -count * 2; // Make sure it's invalid
|
|
|
|
|
2010-04-24 14:10:52 +00:00
|
|
|
LocalVariables *locals = allocLocalsSegment(scr, count);
|
|
|
|
if (locals) {
|
|
|
|
for (int i = 0; i < count; i++)
|
|
|
|
locals->_locals[i] = NULL_REG;
|
|
|
|
}
|
2010-02-03 01:34:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void SegManager::scriptInitialiseLocals(reg_t location) {
|
|
|
|
Script *scr = getScript(location.segment);
|
|
|
|
unsigned int count;
|
|
|
|
|
|
|
|
VERIFY(location.offset + 1 < (uint16)scr->_bufSize, "Locals beyond end of script\n");
|
|
|
|
|
|
|
|
if (getSciVersion() >= SCI_VERSION_1_1)
|
|
|
|
count = READ_LE_UINT16(scr->_buf + location.offset - 2);
|
|
|
|
else
|
|
|
|
count = (READ_LE_UINT16(scr->_buf + location.offset - 2) - 4) >> 1;
|
|
|
|
// half block size
|
|
|
|
|
|
|
|
scr->_localsOffset = location.offset;
|
|
|
|
|
|
|
|
if (!(location.offset + count * 2 + 1 < scr->_bufSize)) {
|
|
|
|
warning("Locals extend beyond end of script: offset %04x, count %x vs size %x", location.offset, count, (uint)scr->_bufSize);
|
|
|
|
count = (scr->_bufSize - location.offset) >> 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
LocalVariables *locals = allocLocalsSegment(scr, count);
|
|
|
|
if (locals) {
|
|
|
|
uint i;
|
|
|
|
byte *base = (byte *)(scr->_buf + location.offset);
|
|
|
|
|
|
|
|
for (i = 0; i < count; i++)
|
2010-04-24 14:10:52 +00:00
|
|
|
locals->_locals[i] = make_reg(0, READ_LE_UINT16(base + i * 2));
|
2010-02-03 01:34:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SegManager::scriptRelocateExportsSci11(SegmentId seg) {
|
|
|
|
Script *scr = getScript(seg);
|
|
|
|
for (int i = 0; i < scr->_numExports; i++) {
|
|
|
|
/* We are forced to use an ugly heuristic here to distinguish function
|
|
|
|
exports from object/class exports. The former kind points into the
|
|
|
|
script resource, the latter into the heap resource. */
|
|
|
|
uint16 location = READ_LE_UINT16((byte *)(scr->_exportTable + i));
|
|
|
|
if ((location < scr->_heapSize - 1) && (READ_LE_UINT16(scr->_heapStart + location) == SCRIPT_OBJECT_MAGIC_NUMBER)) {
|
|
|
|
WRITE_LE_UINT16((byte *)(scr->_exportTable + i), location + scr->_heapStart - scr->_buf);
|
|
|
|
} else {
|
|
|
|
// Otherwise it's probably a function export,
|
|
|
|
// and we don't need to do anything.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SegManager::scriptInitialiseObjectsSci11(SegmentId seg) {
|
|
|
|
Script *scr = getScript(seg);
|
|
|
|
byte *seeker = scr->_heapStart + 4 + READ_LE_UINT16(scr->_heapStart + 2) * 2;
|
|
|
|
|
|
|
|
while (READ_LE_UINT16(seeker) == SCRIPT_OBJECT_MAGIC_NUMBER) {
|
|
|
|
if (READ_LE_UINT16(seeker + 14) & SCRIPT_INFO_CLASS) {
|
|
|
|
int classpos = seeker - scr->_buf;
|
|
|
|
int species = READ_LE_UINT16(seeker + 10);
|
|
|
|
|
|
|
|
if (species < 0 || species >= (int)_classtable.size()) {
|
|
|
|
error("Invalid species %d(0x%x) not in interval [0,%d) while instantiating script %d",
|
|
|
|
species, species, _classtable.size(), scr->_nr);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
_classtable[species].reg.segment = seg;
|
|
|
|
_classtable[species].reg.offset = classpos;
|
|
|
|
}
|
|
|
|
seeker += READ_LE_UINT16(seeker + 2) * 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
seeker = scr->_heapStart + 4 + READ_LE_UINT16(scr->_heapStart + 2) * 2;
|
|
|
|
while (READ_LE_UINT16(seeker) == SCRIPT_OBJECT_MAGIC_NUMBER) {
|
|
|
|
reg_t reg;
|
|
|
|
Object *obj;
|
|
|
|
|
|
|
|
reg.segment = seg;
|
|
|
|
reg.offset = seeker - scr->_buf;
|
|
|
|
obj = scr->scriptObjInit(reg);
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
if (obj->_variables[5].offset != 0xffff) {
|
|
|
|
obj->_variables[5] = INST_LOOKUP_CLASS(obj->_variables[5].offset);
|
|
|
|
baseObj = getObject(obj->_variables[5]);
|
|
|
|
obj->variable_names_nr = baseObj->variables_nr;
|
|
|
|
obj->_baseObj = baseObj->_baseObj;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Copy base from species class, as we need its selector IDs
|
|
|
|
obj->setSuperClassSelector(
|
|
|
|
getClassAddress(obj->getSuperClassSelector().offset, SCRIPT_GET_LOCK, NULL_REG));
|
|
|
|
|
|
|
|
// Set the -classScript- selector to the script number.
|
|
|
|
// FIXME: As this selector is filled in at run-time, it is likely
|
|
|
|
// that it is supposed to hold a pointer. The Obj::isKindOf method
|
|
|
|
// uses this selector together with -propDict- to compare classes.
|
|
|
|
// For the purpose of Obj::isKindOf, using the script number appears
|
|
|
|
// to be sufficient.
|
|
|
|
obj->setClassScriptSelector(make_reg(0, scr->_nr));
|
|
|
|
|
|
|
|
seeker += READ_LE_UINT16(seeker + 2) * 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
int script_instantiate_common(ResourceManager *resMan, SegManager *segMan, int script_nr, Resource **script, Resource **heap, int *was_new) {
|
|
|
|
*was_new = 1;
|
2009-08-26 18:13:11 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
*script = resMan->findResource(ResourceId(kResourceTypeScript, script_nr), 0);
|
|
|
|
if (getSciVersion() >= SCI_VERSION_1_1)
|
|
|
|
*heap = resMan->findResource(ResourceId(kResourceTypeHeap, script_nr), 0);
|
2009-08-26 18:13:11 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
if (!*script || (getSciVersion() >= SCI_VERSION_1_1 && !heap)) {
|
|
|
|
warning("Script 0x%x requested but not found", script_nr);
|
|
|
|
if (getSciVersion() >= SCI_VERSION_1_1) {
|
|
|
|
if (*heap)
|
|
|
|
warning("Inconsistency: heap resource WAS found");
|
|
|
|
else if (*script)
|
|
|
|
warning("Inconsistency: script resource WAS found");
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2010-01-25 01:39:44 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
SegmentId seg_id = segMan->getScriptSegment(script_nr);
|
|
|
|
Script *scr = segMan->getScriptIfLoaded(seg_id);
|
|
|
|
if (scr) {
|
|
|
|
if (!scr->isMarkedAsDeleted()) {
|
|
|
|
scr->incrementLockers();
|
|
|
|
return seg_id;
|
|
|
|
} else {
|
|
|
|
scr->freeScript();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
scr = segMan->allocateScript(script_nr, &seg_id);
|
|
|
|
if (!scr) { // ALL YOUR SCRIPT BASE ARE BELONG TO US
|
|
|
|
error("Not enough heap space for script size 0x%x of script 0x%x (Should this happen?)", (*script)->size, script_nr);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
2009-08-26 18:13:11 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
scr->init(script_nr, resMan);
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
reg_t reg;
|
|
|
|
reg.segment = seg_id;
|
|
|
|
reg.offset = 0;
|
2009-12-21 14:32:54 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
// Set heap position (beyond the size word)
|
|
|
|
scr->setLockers(1);
|
|
|
|
scr->setExportTableOffset(0);
|
|
|
|
scr->setSynonymsOffset(0);
|
|
|
|
scr->setSynonymsNr(0);
|
|
|
|
|
|
|
|
*was_new = 0;
|
|
|
|
|
|
|
|
return seg_id;
|
2009-02-15 06:10:59 +00:00
|
|
|
}
|
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
int script_instantiate_sci0(ResourceManager *resMan, SegManager *segMan, int script_nr) {
|
|
|
|
int objtype;
|
|
|
|
unsigned int objlength;
|
|
|
|
int relocation = -1;
|
|
|
|
int magic_pos_adder; // Usually 0; 2 for older SCI versions
|
|
|
|
Resource *script;
|
|
|
|
int was_new;
|
|
|
|
bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY);
|
|
|
|
|
|
|
|
const int seg_id = script_instantiate_common(resMan, segMan, script_nr, &script, NULL, &was_new);
|
|
|
|
|
|
|
|
if (was_new)
|
|
|
|
return seg_id;
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
Script *scr = segMan->getScript(seg_id);
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
if (oldScriptHeader) {
|
|
|
|
//
|
|
|
|
int locals_nr = READ_LE_UINT16(script->data);
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
// 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.
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
scr->mcpyInOut(0, script->data, script->size);
|
|
|
|
magic_pos_adder = 2; // Step over the funny prefix
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
if (locals_nr)
|
|
|
|
segMan->scriptInitialiseLocalsZero(seg_id, locals_nr);
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
} else {
|
|
|
|
scr->mcpyInOut(0, script->data, script->size);
|
|
|
|
magic_pos_adder = 0;
|
2009-02-15 22:28:50 +00:00
|
|
|
}
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
// Now do a first pass through the script objects to find the
|
|
|
|
// export table and local variable block
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
reg_t reg;
|
|
|
|
reg.segment = seg_id;
|
|
|
|
reg.offset = magic_pos_adder;
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
objlength = 0;
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
do {
|
|
|
|
reg_t data_base;
|
|
|
|
reg_t addr;
|
|
|
|
reg.offset += objlength; // Step over the last checked object
|
|
|
|
objtype = scr->getHeap(reg.offset);
|
|
|
|
if (!objtype)
|
|
|
|
break;
|
|
|
|
|
|
|
|
objlength = scr->getHeap(reg.offset + 2);
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
// This happens in some demos (e.g. the EcoQuest 1 demo). Not sure what is the
|
|
|
|
// actual cause of it, but the scripts of these demos can't be loaded properly
|
|
|
|
// and we're stuck forever in this loop, as objlength never changes
|
|
|
|
if (!objlength) {
|
|
|
|
warning("script_instantiate_sci0: objlength is 0, unable to parse script");
|
|
|
|
return 0;
|
2009-02-15 22:28:50 +00:00
|
|
|
}
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
data_base = reg;
|
|
|
|
data_base.offset += 4;
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
addr = data_base;
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
switch (objtype) {
|
|
|
|
case SCI_OBJ_EXPORTS: {
|
|
|
|
scr->setExportTableOffset(data_base.offset);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SCI_OBJ_SYNONYMS:
|
|
|
|
scr->setSynonymsOffset(addr.offset); // +4 is to step over the header
|
|
|
|
scr->setSynonymsNr((objlength) / 4);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SCI_OBJ_LOCALVARS:
|
|
|
|
segMan->scriptInitialiseLocals(data_base);
|
|
|
|
break;
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
case SCI_OBJ_CLASS: {
|
|
|
|
int classpos = addr.offset - SCRIPT_OBJECT_MAGIC_OFFSET;
|
|
|
|
int species;
|
|
|
|
species = scr->getHeap(addr.offset - SCRIPT_OBJECT_MAGIC_OFFSET + SCRIPT_SPECIES_OFFSET);
|
|
|
|
if (species < 0 || species >= (int)segMan->_classtable.size()) {
|
|
|
|
if (species == (int)segMan->_classtable.size()) {
|
|
|
|
// Happens in the LSL2 demo
|
|
|
|
warning("Applying workaround for an off-by-one invalid species access");
|
|
|
|
segMan->_classtable.resize(segMan->_classtable.size() + 1);
|
|
|
|
} else {
|
|
|
|
warning("Invalid species %d(0x%x) not in interval "
|
|
|
|
"[0,%d) while instantiating script %d\n",
|
|
|
|
species, species, segMan->_classtable.size(),
|
|
|
|
script_nr);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
segMan->_classtable[species].reg = addr;
|
|
|
|
segMan->_classtable[species].reg.offset = classpos;
|
|
|
|
// Set technical class position-- into the block allocated for it
|
|
|
|
}
|
|
|
|
break;
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} while (objtype != 0);
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
// And now a second pass to adjust objects and class pointers, and the general pointers
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
objlength = 0;
|
|
|
|
reg.offset = magic_pos_adder; // Reset counter
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
do {
|
|
|
|
reg_t addr;
|
|
|
|
reg.offset += objlength; // Step over the last checked object
|
|
|
|
objtype = scr->getHeap(reg.offset);
|
|
|
|
if (!objtype)
|
|
|
|
break;
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
objlength = scr->getHeap(reg.offset + 2);
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
addr = reg;
|
|
|
|
addr.offset += 4; // Step over header
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
switch (objtype) {
|
|
|
|
case SCI_OBJ_CODE:
|
|
|
|
scr->scriptAddCodeBlock(addr);
|
|
|
|
break;
|
|
|
|
case SCI_OBJ_OBJECT:
|
|
|
|
case SCI_OBJ_CLASS: { // object or class?
|
|
|
|
Object *obj = scr->scriptObjInit(addr);
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
// Instantiate the superclass, if neccessary
|
|
|
|
obj->setSpeciesSelector(INST_LOOKUP_CLASS(obj->getSpeciesSelector().offset));
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
Object *baseObj = segMan->getObject(obj->getSpeciesSelector());
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
if (baseObj) {
|
|
|
|
obj->setVarCount(baseObj->getVarCount());
|
|
|
|
// Copy base from species class, as we need its selector IDs
|
|
|
|
obj->_baseObj = baseObj->_baseObj;
|
2009-02-15 22:28:50 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
obj->setSuperClassSelector(INST_LOOKUP_CLASS(obj->getSuperClassSelector().offset));
|
|
|
|
} else {
|
|
|
|
warning("Failed to locate base object for object at %04X:%04X; skipping", PRINT_REG(addr));
|
2009-02-15 22:28:50 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
scr->scriptObjRemove(addr);
|
|
|
|
}
|
|
|
|
} // if object or class
|
|
|
|
break;
|
|
|
|
case SCI_OBJ_POINTERS: // A relocation table
|
|
|
|
relocation = addr.offset;
|
|
|
|
break;
|
2009-02-15 22:28:50 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
default:
|
|
|
|
break;
|
2009-02-15 22:28:50 +00:00
|
|
|
}
|
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
} while (objtype != 0 && reg.offset < script->size - 2);
|
|
|
|
|
|
|
|
if (relocation >= 0)
|
|
|
|
scr->scriptRelocate(make_reg(seg_id, relocation));
|
2009-02-15 22:28:50 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
return reg.segment; // instantiation successful
|
|
|
|
}
|
2009-02-15 22:28:50 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
int script_instantiate_sci11(ResourceManager *resMan, SegManager *segMan, int script_nr) {
|
|
|
|
Resource *script, *heap;
|
|
|
|
int _heapStart;
|
|
|
|
reg_t reg;
|
|
|
|
int was_new;
|
2009-02-15 22:28:50 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
const int seg_id = script_instantiate_common(resMan, segMan, script_nr, &script, &heap, &was_new);
|
2009-02-15 22:28:50 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
if (was_new)
|
|
|
|
return seg_id;
|
2009-02-15 22:28:50 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
Script *scr = segMan->getScript(seg_id);
|
2009-02-15 22:28:50 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
_heapStart = script->size;
|
|
|
|
if (script->size & 2)
|
|
|
|
_heapStart ++;
|
2009-02-15 22:28:50 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
scr->mcpyInOut(0, script->data, script->size);
|
|
|
|
scr->mcpyInOut(_heapStart, heap->data, heap->size);
|
2009-02-15 22:28:50 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
if (READ_LE_UINT16(script->data + 6) > 0)
|
|
|
|
scr->setExportTableOffset(6);
|
2009-02-15 22:28:50 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
reg.segment = seg_id;
|
|
|
|
reg.offset = _heapStart + 4;
|
|
|
|
segMan->scriptInitialiseLocals(reg);
|
|
|
|
|
|
|
|
segMan->scriptRelocateExportsSci11(seg_id);
|
|
|
|
segMan->scriptInitialiseObjectsSci11(seg_id);
|
|
|
|
|
|
|
|
reg.offset = READ_LE_UINT16(heap->data);
|
|
|
|
scr->heapRelocate(reg);
|
2009-02-15 22:28:50 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
return seg_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
int script_instantiate(ResourceManager *resMan, SegManager *segMan, int script_nr) {
|
|
|
|
if (getSciVersion() >= SCI_VERSION_1_1)
|
|
|
|
return script_instantiate_sci11(resMan, segMan, script_nr);
|
|
|
|
else
|
|
|
|
return script_instantiate_sci0(resMan, segMan, script_nr);
|
|
|
|
}
|
|
|
|
|
|
|
|
void script_uninstantiate_sci0(SegManager *segMan, int script_nr, SegmentId seg) {
|
|
|
|
bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY);
|
|
|
|
reg_t reg = make_reg(seg, oldScriptHeader ? 2 : 0);
|
|
|
|
int objtype, objlength;
|
|
|
|
Script *scr = segMan->getScript(seg);
|
|
|
|
|
|
|
|
// Make a pass over the object in order uninstantiate all superclasses
|
|
|
|
objlength = 0;
|
|
|
|
|
|
|
|
do {
|
|
|
|
reg.offset += objlength; // Step over the last checked object
|
|
|
|
|
|
|
|
objtype = scr->getHeap(reg.offset);
|
|
|
|
if (!objtype)
|
2009-02-15 22:28:50 +00:00
|
|
|
break;
|
2010-02-02 22:50:32 +00:00
|
|
|
objlength = scr->getHeap(reg.offset + 2); // use SEG_UGET_HEAP ??
|
2009-02-15 22:28:50 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
reg.offset += 4; // Step over header
|
2009-02-15 22:28:50 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
if ((objtype == SCI_OBJ_OBJECT) || (objtype == SCI_OBJ_CLASS)) { // object or class?
|
|
|
|
int superclass;
|
2009-02-15 22:28:50 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
reg.offset -= SCRIPT_OBJECT_MAGIC_OFFSET;
|
2009-02-15 22:28:50 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
superclass = scr->getHeap(reg.offset + SCRIPT_SUPERCLASS_OFFSET); // Get superclass...
|
2009-02-15 22:28:50 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
if (superclass >= 0) {
|
|
|
|
int superclass_script = segMan->_classtable[superclass].script;
|
|
|
|
|
|
|
|
if (superclass_script == script_nr) {
|
|
|
|
if (scr->getLockers())
|
|
|
|
scr->decrementLockers(); // Decrease lockers if this is us ourselves
|
|
|
|
} else
|
|
|
|
script_uninstantiate(segMan, superclass_script);
|
|
|
|
// Recurse to assure that the superclass lockers number gets decreased
|
|
|
|
}
|
|
|
|
|
|
|
|
reg.offset += SCRIPT_OBJECT_MAGIC_OFFSET;
|
|
|
|
} // if object or class
|
|
|
|
|
|
|
|
reg.offset -= 4; // Step back on header
|
|
|
|
|
|
|
|
} while (objtype != 0);
|
|
|
|
}
|
2009-02-15 22:28:50 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
void script_uninstantiate(SegManager *segMan, int script_nr) {
|
|
|
|
SegmentId segment = segMan->getScriptSegment(script_nr);
|
|
|
|
Script *scr = segMan->getScriptIfLoaded(segment);
|
|
|
|
|
|
|
|
if (!scr) { // Is it already loaded?
|
|
|
|
//warning("unloading script 0x%x requested although not loaded", script_nr);
|
|
|
|
// This is perfectly valid SCI behaviour
|
|
|
|
return;
|
2009-02-15 22:28:50 +00:00
|
|
|
}
|
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
scr->decrementLockers(); // One less locker
|
|
|
|
|
|
|
|
if (scr->getLockers() > 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Free all classtable references to this script
|
|
|
|
for (uint i = 0; i < segMan->_classtable.size(); i++)
|
|
|
|
if (segMan->_classtable[i].reg.segment == segment)
|
|
|
|
segMan->_classtable[i].reg = NULL_REG;
|
|
|
|
|
|
|
|
if (getSciVersion() < SCI_VERSION_1_1)
|
|
|
|
script_uninstantiate_sci0(segMan, script_nr, segment);
|
|
|
|
// FIXME: Add proper script uninstantiation for SCI 1.1
|
|
|
|
|
|
|
|
if (scr->getLockers())
|
|
|
|
return; // if xxx.lockers > 0
|
|
|
|
|
|
|
|
// Otherwise unload it completely
|
|
|
|
// Explanation: I'm starting to believe that this work is done by SCI itself.
|
|
|
|
scr->markDeleted();
|
|
|
|
|
|
|
|
debugC(kDebugLevelScripts, "Unloaded script 0x%x.", script_nr);
|
|
|
|
|
|
|
|
return;
|
2009-02-15 06:10:59 +00:00
|
|
|
}
|
2009-02-21 10:23:36 +00:00
|
|
|
|
2010-02-02 22:50:32 +00:00
|
|
|
|
2009-02-21 10:23:36 +00:00
|
|
|
} // End of namespace Sci
|