From bb992b0b93f3ee9dfad6524fd0721a350f12085a Mon Sep 17 00:00:00 2001 From: Filippos Karapetis Date: Sun, 27 Jun 2010 20:38:43 +0000 Subject: SCI: Moved all the script-related code inside script.cpp/.h, and all script opcode-related code inside vm.cpp/.h svn-id: r50396 --- engines/sci/engine/kscripts.cpp | 2 + engines/sci/engine/script.cpp | 434 ++++++++++++++++++++++++++++--------- engines/sci/engine/script.h | 346 ++++++++++++++++------------- engines/sci/engine/seg_manager.cpp | 44 +++- engines/sci/engine/seg_manager.h | 5 +- engines/sci/engine/segment.cpp | 326 +--------------------------- engines/sci/engine/segment.h | 176 --------------- engines/sci/engine/vm.cpp | 80 +++++++ engines/sci/engine/vm.h | 178 +++++++++++++-- 9 files changed, 809 insertions(+), 782 deletions(-) (limited to 'engines/sci/engine') diff --git a/engines/sci/engine/kscripts.cpp b/engines/sci/engine/kscripts.cpp index 4e6be15760..8ea0ec2019 100644 --- a/engines/sci/engine/kscripts.cpp +++ b/engines/sci/engine/kscripts.cpp @@ -25,6 +25,8 @@ #include "sci/sci.h" #include "sci/resource.h" +#include "sci/engine/seg_manager.h" +#include "sci/engine/script.h" #include "sci/engine/state.h" #include "sci/engine/selector.h" #include "sci/engine/kernel.h" diff --git a/engines/sci/engine/script.cpp b/engines/sci/engine/script.cpp index 800460bfc2..748a8f9140 100644 --- a/engines/sci/engine/script.cpp +++ b/engines/sci/engine/script.cpp @@ -35,127 +35,353 @@ namespace Sci { -#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; +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 + } } -#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; + 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 } -#endif } -void SegManager::createClassTable() { - Resource *vocab996 = _resMan->findResource(ResourceId(kResourceTypeVocab, 996), 1); +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; +} - if (!vocab996) - error("SegManager: failed to open vocab 996"); +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()); +} - int totalClasses = vocab996->size >> 2; - _classTable.resize(totalClasses); +// This helper function is used by Script::relocateLocal and Object::relocate +// Duplicate in segment.cpp and script.cpp +static bool relocateBlock(Common::Array &block, int block_location, SegmentId segment, int location, size_t scriptSize) { + int rel = location - block_location; - for (uint16 classNr = 0; classNr < totalClasses; classNr++) { - uint16 scriptNr = READ_SCI11ENDIAN_UINT16(vocab996->data + classNr * 4 + 2); + if (rel < 0) + return false; - _classTable[classNr].reg = NULL_REG; - _classTable[classNr].script = scriptNr; + 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; - _resMan->unlockResource(vocab996); + return true; } -reg_t SegManager::getClassAddress(int classnr, ScriptLoadType lock, reg_t caller) { - if (classnr == 0xffff) - return NULL_REG; +bool Script::relocateLocal(SegmentId segment, int location) { + if (_localsBlock) + return relocateBlock(_localsBlock->_locals, _localsOffset, segment, location, _scriptSize); + else + return false; +} - 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); +void Script::relocate(reg_t block) { + byte *heap = _buf; + uint16 heapSize = (uint16)_bufSize; + uint16 heapOffset = 0; - 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(); + 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); - return the_class->reg; + 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) { diff --git a/engines/sci/engine/script.h b/engines/sci/engine/script.h index fe92dabe58..62d2228c9a 100644 --- a/engines/sci/engine/script.h +++ b/engines/sci/engine/script.h @@ -27,6 +27,7 @@ #define SCI_ENGINE_SCRIPT_H #include "common/str.h" +#include "sci/engine/segment.h" namespace Sci { @@ -49,160 +50,205 @@ enum ScriptObjectTypes { SCI_OBJ_LOCALVARS }; -// 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 -}; +typedef Common::HashMap 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); -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 + /** + * 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); }; -extern opcode_format g_opcode_formats[128][4]; +/** + * 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); -void script_adjust_opcode_formats(); +/** + * 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); } // End of namespace Sci diff --git a/engines/sci/engine/seg_manager.cpp b/engines/sci/engine/seg_manager.cpp index 9394e717ec..35ac0a5f69 100644 --- a/engines/sci/engine/seg_manager.cpp +++ b/engines/sci/engine/seg_manager.cpp @@ -26,6 +26,7 @@ #include "sci/sci.h" #include "sci/engine/seg_manager.h" #include "sci/engine/state.h" +#include "sci/engine/script.h" namespace Sci { @@ -837,7 +838,6 @@ Common::String SegManager::getString(reg_t pointer, int entries) { return ret; } - byte *SegManager::allocDynmem(int size, const char *descr, reg_t *addr) { SegmentId seg; SegmentObj *mobj = allocSegment(new DynMem(), &seg); @@ -949,4 +949,46 @@ void SegManager::freeString(reg_t addr) { #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 diff --git a/engines/sci/engine/seg_manager.h b/engines/sci/engine/seg_manager.h index 1ed01e2c6b..19fd06f596 100644 --- a/engines/sci/engine/seg_manager.h +++ b/engines/sci/engine/seg_manager.h @@ -28,7 +28,9 @@ #include "common/scummsys.h" #include "common/serializer.h" +#include "sci/engine/script.h" #include "sci/engine/vm.h" +#include "sci/engine/vm_types.h" #include "sci/engine/segment.h" namespace Sci { @@ -42,8 +44,6 @@ namespace Sci { error("%s, line, %d, %s", __FILE__, __LINE__, msg); \ } - - /** * Parameters for getScriptSegment(). */ @@ -53,6 +53,7 @@ enum ScriptLoadType { SCRIPT_GET_LOCK = 3 /**< Load, if neccessary, and lock */ }; +class Script; class SegManager : public Common::Serializable { friend class Console; diff --git a/engines/sci/engine/segment.cpp b/engines/sci/engine/segment.cpp index 13c7904726..2bbedf2dcf 100644 --- a/engines/sci/engine/segment.cpp +++ b/engines/sci/engine/segment.cpp @@ -86,196 +86,8 @@ SegmentObj *SegmentObj::createSegmentObj(SegmentType type) { 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 +// Duplicate in segment.cpp and script.cpp static bool relocateBlock(Common::Array &block, int block_location, SegmentId segment, int location, size_t scriptSize) { int rel = location - block_location; @@ -298,148 +110,12 @@ static bool relocateBlock(Common::Array &block, int block_location, Segme 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) { error("Error: Trying to dereference pointer %04x:%04x to inappropriate segment", PRINT_REG(pointer)); 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 { return offset < _locals.size() * 2; diff --git a/engines/sci/engine/segment.h b/engines/sci/engine/segment.h index 24d27e367f..b621e53412 100644 --- a/engines/sci/engine/segment.h +++ b/engines/sci/engine/segment.h @@ -331,182 +331,6 @@ private: reg_t _pos; /**< Object offset within its script; for clones, this is their base */ }; -typedef Common::HashMap 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 */ struct DataStack : SegmentObj { int _capacity; /**< Number of stack entries */ diff --git a/engines/sci/engine/vm.cpp b/engines/sci/engine/vm.cpp index 841d4ede64..9ab95c41bb 100644 --- a/engines/sci/engine/vm.cpp +++ b/engines/sci/engine/vm.cpp @@ -49,6 +49,86 @@ const reg_t SIGNAL_REG = {0, SIGNAL_OFFSET}; #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. * diff --git a/engines/sci/engine/vm.h b/engines/sci/engine/vm.h index 0938520525..ed4cc4a456 100644 --- a/engines/sci/engine/vm.h +++ b/engines/sci/engine/vm.h @@ -118,6 +118,160 @@ enum { 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. @@ -194,30 +348,6 @@ void script_debug(EngineState *s); SelectorType lookupSelector(SegManager *segMan, reg_t obj, Selector selectorid, 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. * -- cgit v1.2.3