diff options
-rw-r--r-- | engines/sci/console.h | 1 | ||||
-rw-r--r-- | engines/sci/engine/memobj.cpp | 399 | ||||
-rw-r--r-- | engines/sci/engine/memobj.h | 556 | ||||
-rw-r--r-- | engines/sci/engine/seg_manager.cpp | 360 | ||||
-rw-r--r-- | engines/sci/engine/seg_manager.h | 1 | ||||
-rw-r--r-- | engines/sci/engine/vm.h | 517 | ||||
-rw-r--r-- | engines/sci/module.mk | 1 |
7 files changed, 959 insertions, 876 deletions
diff --git a/engines/sci/console.h b/engines/sci/console.h index f0975837df..b97b14f354 100644 --- a/engines/sci/console.h +++ b/engines/sci/console.h @@ -33,6 +33,7 @@ namespace Sci { class SciEngine; +struct List; // Refer to the "addresses" command on how to pass address parameters int parse_reg_t(EngineState *s, const char *str, reg_t *dest); diff --git a/engines/sci/engine/memobj.cpp b/engines/sci/engine/memobj.cpp new file mode 100644 index 0000000000..ef48270b41 --- /dev/null +++ b/engines/sci/engine/memobj.cpp @@ -0,0 +1,399 @@ +/* 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$ + * + */ + +#include "common/endian.h" + +#include "sci/sci.h" +#include "sci/engine/memobj.h" +#include "sci/engine/intmap.h" +#include "sci/engine/seg_manager.h" +#include "sci/engine/state.h" +#include "sci/tools.h" + +namespace Sci { + +MemObject *MemObject::createMemObject(MemObjectType type) { + MemObject *mem = 0; + switch (type) { + case MEM_OBJ_SCRIPT: + mem = new Script(); + break; + case MEM_OBJ_CLONES: + mem = new CloneTable(); + break; + case MEM_OBJ_LOCALS: + mem = new LocalVariables(); + break; + case MEM_OBJ_SYS_STRINGS: + mem = new SystemStrings(); + break; + case MEM_OBJ_STACK: + mem = new DataStack(); + break; + case MEM_OBJ_HUNK: + mem = new HunkTable(); + break; + case MEM_OBJ_STRING_FRAG: + mem = new StringFrag(); + break; + case MEM_OBJ_LISTS: + mem = new ListTable(); + break; + case MEM_OBJ_NODES: + mem = new NodeTable(); + break; + case MEM_OBJ_DYNMEM: + mem = new DynMem(); + break; + default: + error("Unknown MemObject type %d", type); + break; + } + + assert(mem); + mem->_type = type; + return mem; +} + +void Script::freeScript() { + free(buf); + buf = NULL; + buf_size = 0; + + _objects.clear(); + + delete obj_indices; + obj_indices = 0; + _codeBlocks.clear(); +} + +// memory operations + +void Script::mcpyInOut(int dst, const void *src, size_t n) { + if (buf) { + assert(dst + n <= buf_size); + memcpy(buf + dst, src, n); + } +} + +int16 Script::getHeap(uint16 offset) const { + assert(offset + 1 < (int)buf_size); + return READ_LE_UINT16(buf + offset); +// return (buf[offset] | (buf[offset+1]) << 8); +} + +void Script::incrementLockers() { + lockers++; +} + +void Script::decrementLockers() { + if (lockers > 0) + lockers--; +} + +int Script::getLockers() const { + return lockers; +} + +void Script::setLockers(int lockers_) { + lockers = lockers_; +} + +void Script::setExportTableOffset(int offset) { + if (offset) { + export_table = (uint16 *)(buf + offset + 2); + exports_nr = READ_LE_UINT16((byte *)(export_table - 1)); + } else { + export_table = NULL; + exports_nr = 0; + } +} + +void Script::setSynonymsOffset(int offset) { + synonyms = buf + offset; +} + +byte *Script::getSynonyms() const { + return synonyms; +} + +void Script::setSynonymsNr(int n) { + synonyms_nr = n; +} + +int Script::getSynonymsNr() const { + return synonyms_nr; +} + +byte *MemObject::dereference(reg_t pointer, int *size) { + error("Error: Trying to dereference pointer %04x:%04x to inappropriate segment", + PRINT_REG(pointer)); + return NULL; +} + +byte *Script::dereference(reg_t pointer, int *size) { + if (pointer.offset > buf_size) { + sciprintf("Error: Attempt to dereference invalid pointer %04x:%04x into script segment (script size=%d)\n", + PRINT_REG(pointer), (uint)buf_size); + return NULL; + } + if (size) + *size = buf_size - pointer.offset; + return (byte *)(buf + pointer.offset); +} + +byte *LocalVariables::dereference(reg_t pointer, int *size) { + // FIXME: The following doesn't seem to be endian safe. + // To fix this, we'd have to always treat the reg_t + // values stored here as in the little endian format. + int count = _locals.size() * sizeof(reg_t); + byte *base = (byte *)&_locals[0]; + + if (size) + *size = count; + + return base + pointer.offset; +} + +byte *DataStack::dereference(reg_t pointer, int *size) { + int count = nr * sizeof(reg_t); + byte *base = (byte *)entries; + + if (size) + *size = count; + + return base + pointer.offset; +} + +byte *DynMem::dereference(reg_t pointer, int *size) { + int count = _size; + byte *base = (byte *)_buf; + + if (size) + *size = count; + + return base + pointer.offset; +} + +byte *SystemStrings::dereference(reg_t pointer, int *size) { + if (size) + *size = strings[pointer.offset].max_size; + if (pointer.offset < SYS_STRINGS_MAX && strings[pointer.offset].name) + return (byte *)(strings[pointer.offset].value); + + // This occurs in KQ5CD when interacting with certain objects + warning("Attempt to dereference invalid pointer %04x:%04x", PRINT_REG(pointer)); + return NULL; +} + + +//-------------------- script -------------------- +reg_t Script::findCanonicAddress(SegManager *segmgr, reg_t addr) { + addr.offset = 0; + return addr; +} + +void Script::freeAtAddress(SegManager *segmgr, reg_t addr) { + /* + sciprintf("[GC] Freeing script %04x:%04x\n", PRINT_REG(addr)); + if (locals_segment) + sciprintf("[GC] Freeing locals %04x:0000\n", locals_segment); + */ + + if (_markedAsDeleted) + segmgr->deallocateScript(nr); +} + +void Script::listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) { + (*note)(param, make_reg(segId, 0)); +} + +void Script::listAllOutgoingReferences(EngineState *s, reg_t addr, void *param, NoteCallback note) { + Script *script = this; + + if (addr.offset <= script->buf_size && addr.offset >= -SCRIPT_OBJECT_MAGIC_OFFSET && RAW_IS_OBJECT(script->buf + addr.offset)) { + int idx = RAW_GET_CLASS_INDEX(script, addr); + if (idx >= 0 && (uint)idx < script->_objects.size()) { + // Note all local variables, if we have a local variable environment + if (script->locals_segment) + (*note)(param, make_reg(script->locals_segment, 0)); + + Object &obj = script->_objects[idx]; + for (uint i = 0; i < obj._variables.size(); i++) + (*note)(param, obj._variables[i]); + } else { + warning("Request for outgoing script-object reference at %04x:%04x yielded invalid index %d", PRINT_REG(addr), idx); + } + } else { + /* warning("Unexpected request for outgoing script-object references at %04x:%04x", PRINT_REG(addr));*/ + /* Happens e.g. when we're looking into strings */ + } +} + + +//-------------------- clones -------------------- + +template<typename T> +void Table<T>::listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) { + for (uint i = 0; i < _table.size(); i++) + if (isValidEntry(i)) + (*note)(param, make_reg(segId, i)); +} + +void CloneTable::listAllOutgoingReferences(EngineState *s, reg_t addr, void *param, NoteCallback note) { + CloneTable *clone_table = this; + Clone *clone; + +// assert(addr.segment == _segId); + + if (!clone_table->isValidEntry(addr.offset)) { + warning("Unexpected request for outgoing references from clone at %04x:%04x", PRINT_REG(addr)); +// BREAKPOINT(); + return; + } + + clone = &(clone_table->_table[addr.offset]); + + // Emit all member variables (including references to the 'super' delegate) + for (uint i = 0; i < clone->_variables.size(); i++) + (*note)(param, clone->_variables[i]); + + // Note that this also includes the 'base' object, which is part of the script and therefore also emits the locals. + (*note)(param, clone->pos); + //sciprintf("[GC] Reporting clone-pos %04x:%04x\n", PRINT_REG(clone->pos)); +} + +void CloneTable::freeAtAddress(SegManager *segmgr, reg_t addr) { + CloneTable *clone_table = this; + Object *victim_obj; + +// assert(addr.segment == _segId); + + victim_obj = &(clone_table->_table[addr.offset]); + +#ifdef GC_DEBUG + if (!(victim_obj->flags & OBJECT_FLAG_FREED)) + sciprintf("[GC] Warning: Clone %04x:%04x not reachable and not freed (freeing now)\n", PRINT_REG(addr)); +#ifdef GC_DEBUG_VERBOSE + else + sciprintf("[GC-DEBUG] Clone %04x:%04x: Freeing\n", PRINT_REG(addr)); +#endif +#endif + /* + sciprintf("[GC] Clone %04x:%04x: Freeing\n", PRINT_REG(addr)); + sciprintf("[GC] Clone had pos %04x:%04x\n", PRINT_REG(victim_obj->pos)); + */ + clone_table->freeEntry(addr.offset); +} + + +//-------------------- locals -------------------- +reg_t LocalVariables::findCanonicAddress(SegManager *segmgr, reg_t addr) { + // Reference the owning script + SegmentId owner_seg = segmgr->segGet(script_id); + + assert(owner_seg >= 0); + + return make_reg(owner_seg, 0); +} + +void LocalVariables::listAllOutgoingReferences(EngineState *s, reg_t addr, void *param, NoteCallback note) { +// assert(addr.segment == _segId); + + for (uint i = 0; i < _locals.size(); i++) + (*note)(param, _locals[i]); +} + + +//-------------------- stack -------------------- +reg_t DataStack::findCanonicAddress(SegManager *segmgr, reg_t addr) { + addr.offset = 0; + return addr; +} + +void DataStack::listAllOutgoingReferences(EngineState *s, reg_t addr, void *param, NoteCallback note) { + fprintf(stderr, "Emitting %d stack entries\n", nr); + for (int i = 0; i < nr; i++) + (*note)(param, entries[i]); + fprintf(stderr, "DONE"); +} + + +//-------------------- lists -------------------- +void ListTable::freeAtAddress(SegManager *segmgr, reg_t sub_addr) { + freeEntry(sub_addr.offset); +} + +void ListTable::listAllOutgoingReferences(EngineState *s, reg_t addr, void *param, NoteCallback note) { + if (!isValidEntry(addr.offset)) { + warning("Invalid list referenced for outgoing references: %04x:%04x", PRINT_REG(addr)); + return; + } + + List *list = &(_table[addr.offset]); + + note(param, list->first); + note(param, list->last); + // We could probably get away with just one of them, but + // let's be conservative here. +} + + +//-------------------- nodes -------------------- +void NodeTable::freeAtAddress(SegManager *segmgr, reg_t sub_addr) { + freeEntry(sub_addr.offset); +} + +void NodeTable::listAllOutgoingReferences(EngineState *s, reg_t addr, void *param, NoteCallback note) { + if (!isValidEntry(addr.offset)) { + warning("Invalid node referenced for outgoing references: %04x:%04x", PRINT_REG(addr)); + return; + } + Node *node = &(_table[addr.offset]); + + // We need all four here. Can't just stick with 'pred' OR 'succ' because node operations allow us + // to walk around from any given node + note(param, node->pred); + note(param, node->succ); + note(param, node->key); + note(param, node->value); +} + + +//-------------------- hunk -------------------- + +//-------------------- dynamic memory -------------------- + +reg_t DynMem::findCanonicAddress(SegManager *segmgr, reg_t addr) { + addr.offset = 0; + return addr; +} + +void DynMem::listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) { + (*note)(param, make_reg(segId, 0)); +} + + +} // End of namespace Sci diff --git a/engines/sci/engine/memobj.h b/engines/sci/engine/memobj.h new file mode 100644 index 0000000000..a871d2dcb6 --- /dev/null +++ b/engines/sci/engine/memobj.h @@ -0,0 +1,556 @@ +/* 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$ + * + */ + +#ifndef SCI_ENGINE_MEMOBJ_H +#define SCI_ENGINE_MEMOBJ_H + +#include "common/serializer.h" +#include "sci/engine/vm_types.h" // for reg_t + +//#include "common/util.h" + +namespace Sci { + +enum MemObjectType { + MEM_OBJ_INVALID = 0, + MEM_OBJ_SCRIPT = 1, + MEM_OBJ_CLONES = 2, + MEM_OBJ_LOCALS = 3, + MEM_OBJ_STACK = 4, + MEM_OBJ_SYS_STRINGS = 5, + MEM_OBJ_LISTS = 6, + MEM_OBJ_NODES = 7, + MEM_OBJ_HUNK = 8, + MEM_OBJ_DYNMEM = 9, + MEM_OBJ_STRING_FRAG = 10, + + MEM_OBJ_MAX // For sanity checking +}; + +struct MemObject : public Common::Serializable { + MemObjectType _type; + int _segmgrId; /**< Internal value used by the seg_manager's hash map */ + + typedef void (*NoteCallback)(void *param, reg_t addr); // FIXME: Bad choice of name + +public: + static MemObject *createMemObject(MemObjectType type); + +public: + virtual ~MemObject() {} + + inline MemObjectType getType() const { return _type; } + inline int getSegMgrId() const { return _segmgrId; } + + /** + * Dereferences a raw memory pointer. + * @param reg reference to dereference + * @param size if not NULL, set to the theoretical maximum size of the referenced data block + * @return the data block referenced + */ + virtual byte *dereference(reg_t pointer, int *size); + + /** + * Finds the canonic address associated with sub_reg. + * + * For each valid address a, there exists a canonic address c(a) such that c(a) = c(c(a)). + * This address "governs" a in the sense that deallocating c(a) will deallocate a. + * + * @param sub_addr base address whose canonic address is to be found + */ + virtual reg_t findCanonicAddress(SegManager *segmgr, reg_t sub_addr) { return sub_addr; } + + /** + * Deallocates all memory associated with the specified address. + * @param sub_addr address (within the given segment) to deallocate + */ + virtual void freeAtAddress(SegManager *segmgr, reg_t sub_addr) {} + + /** + * Iterates over and reports all addresses within the current segment. + * @param note Invoked for each address on which free_at_address() makes sense + * @param param parameter passed to 'note' + */ + virtual void listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) {} + + /** + * Iterates over all references reachable from the specified object. + * @param object object (within the current segment) to analyse + * @param param parameter passed to 'note' + * @param note Invoked for each outgoing reference within the object + * Note: This function may also choose to report numbers (segment 0) as adresses + */ + virtual void listAllOutgoingReferences(EngineState *s, reg_t object, void *param, NoteCallback note) {} +}; + + +// TODO: Implement the following class +struct StringFrag : public MemObject { + virtual void saveLoadWithSerializer(Common::Serializer &ser); +}; + +struct IntMapper; + +enum { + SYS_STRINGS_MAX = 4, + + SYS_STRING_SAVEDIR = 0, + SYS_STRING_PARSER_BASE = 1, + + MAX_PARSER_BASE = 64 +}; + +struct SystemString { + char *name; + int max_size; + reg_t *value; +}; + +struct SystemStrings : public MemObject { + SystemString strings[SYS_STRINGS_MAX]; + +public: + SystemStrings() { + memset(strings, 0, sizeof(strings)); + } + ~SystemStrings() { + for (int i = 0; i < SYS_STRINGS_MAX; i++) { + SystemString *str = &strings[i]; + if (str->name) { + free(str->name); + str->name = NULL; + + free(str->value); + str->value = NULL; + + str->max_size = 0; + } + } + } + + virtual byte *dereference(reg_t pointer, int *size); + + virtual void saveLoadWithSerializer(Common::Serializer &ser); +}; + +/** This struct is used to buffer the list of send calls in send_selector() */ +struct CallsStruct { + union { + reg_t func; + reg_t *var; + } address; + StackPtr argp; + int argc; + Selector selector; + StackPtr sp; /**< Stack pointer */ + int type; /**< Same as ExecStack.type */ +}; + +struct LocalVariables : public MemObject { + int script_id; /**< Script ID this local variable block belongs to */ + Common::Array<reg_t> _locals; + +public: + LocalVariables() { + script_id = 0; + } + + virtual byte *dereference(reg_t pointer, int *size); + virtual reg_t findCanonicAddress(SegManager *segmgr, reg_t sub_addr); + virtual void listAllOutgoingReferences(EngineState *s, reg_t object, void *param, NoteCallback note); + + virtual void saveLoadWithSerializer(Common::Serializer &ser); +}; + +/** Clone has been marked as 'freed' */ +#define OBJECT_FLAG_FREED (0x1 << 0) + +struct Object { + int flags; + reg_t pos; /**< Object offset within its script; for clones, this is their base */ + int variable_names_nr; /**< Number of variable names, may be less than variables_nr */ + int methods_nr; + byte *base; /**< Points to a buffer all relative references (code, strings) point to */ + byte *base_obj; /**< base + object offset within base */ + uint16 *base_method; /**< Pointer to the method selector area for this object */ + uint16 *base_vars; /**< Pointer to the varselector area for this object */ + Common::Array<reg_t> _variables; +}; + +struct CodeBlock { + reg_t pos; + int size; +}; + +#define VM_OBJECT_GET_VARSELECTOR(obj, i) \ + (s->version < SCI_VERSION_1_1 ? \ + READ_LE_UINT16(obj->base_obj + obj->_variables.size() * 2 + i*2) : \ + *(obj->base_vars + i)) +#define VM_OBJECT_READ_PROPERTY(obj, i) (obj->_variables[i]) +#define VM_OBJECT_GET_FUNCSELECTOR(obj, i) \ + (s->version < SCI_VERSION_1_1 ? \ + READ_LE_UINT16((byte *) (obj->base_method + i)) : \ + READ_LE_UINT16((byte *) (obj->base_method + i*2 + 1))) +#define VM_OBJECT_READ_FUNCTION(obj, i) \ + (s->version < SCI_VERSION_1_1 ? \ + make_reg(obj->pos.segment, \ + READ_LE_UINT16((byte *) (obj->base_method \ + + obj->methods_nr + 1 \ + + i))) : \ + make_reg(obj->pos.segment, \ + READ_LE_UINT16((byte *) (obj->base_method \ + + i * 2 + 2)))) + + + + +struct Script : public MemObject { + int nr; /**< Script number */ + byte *buf; /**< Static data buffer, or NULL if not used */ + size_t buf_size; + size_t script_size; + size_t heap_size; + + byte *synonyms; /**< Synonyms block or 0 if not present*/ + byte *heap_start; /**< Start of heap if SCI1.1, NULL otherwise */ + uint16 *export_table; /**< Abs. offset of the export table or 0 if not present */ + + IntMapper *obj_indices; + + int exports_nr; /**< Number of entries in the exports table */ + int synonyms_nr; /**< Number of entries in the synonyms block */ + int lockers; /**< Number of classes and objects that require this script */ + + /** + * Table for objects, contains property variables. + * Indexed by the value stored at SCRIPT_LOCALVARPTR_OFFSET, + * see VM_OBJECT_[GS]ET_INDEX() + */ + Common::Array<Object> _objects; + + int locals_offset; + int locals_segment; /**< The local variable segment */ + LocalVariables *locals_block; + + Common::Array<CodeBlock> _codeBlocks; + int relocated; + bool _markedAsDeleted; + +public: + Script() { + nr = 0; + buf = NULL; + buf_size = 0; + script_size = 0; + heap_size = 0; + + synonyms = NULL; + heap_start = NULL; + export_table = NULL; + + obj_indices = NULL; + + locals_offset = 0; + locals_segment = 0; + locals_block = NULL; + + relocated = 0; + _markedAsDeleted = 0; + } + + ~Script() { + freeScript(); + } + + void freeScript(); + + virtual byte *dereference(reg_t pointer, int *size); + virtual reg_t findCanonicAddress(SegManager *segmgr, reg_t sub_addr); + virtual void freeAtAddress(SegManager *segmgr, reg_t sub_addr); + virtual void listAllDeallocatable(SegmentId segId, void *param, NoteCallback note); + virtual void listAllOutgoingReferences(EngineState *s, reg_t object, void *param, NoteCallback note); + + virtual void saveLoadWithSerializer(Common::Serializer &ser); + + // 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 synonyms associated with this script + * @return pointer to the synonyms, in non-parsed format. + */ + byte *getSynonyms() const; + + /** + * Retrieves the number of synonyms associated with this script. + * @return the number of synonyms associated with this script + */ + int getSynonymsNr() const; + + + /** + * Sets the script-relative offset of the exports table. + * @param offset script-relative exports table offset + */ + void setExportTableOffset(int offset); + + /** + * Sets the script-relative offset of the synonyms associated with this script. + * @param offset script-relative offset of the synonyms block + */ + void setSynonymsOffset(int offset); + + /** + * Sets the number of synonyms associated with this script, + * @param nr number of synonyms, as to be stored within the script + */ + void setSynonymsNr(int nr); + + + /** + * 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); + + + /** + * 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; + } + + /** + * Marks the script as not deleted. + */ + void unmarkDeleted() { + _markedAsDeleted = false; + } + + /** + * Determines whether the script is marked as being deleted. + */ + bool isMarkedAsDeleted() const { + return _markedAsDeleted; + } + + /** + * Retrieves a 16 bit value from within a script's heap representation. + * @param offset offset to read from + * @return the value read from the specified location + */ + int16 getHeap(uint16 offset) const; + +}; + +/** Data stack */ +struct DataStack : MemObject { + int nr; /**< Number of stack entries */ + reg_t *entries; + +public: + DataStack() { + nr = 0; + entries = NULL; + } + ~DataStack() { + free(entries); + entries = NULL; + } + + virtual byte *dereference(reg_t pointer, int *size); + virtual reg_t findCanonicAddress(SegManager *segmgr, reg_t sub_addr); + virtual void listAllOutgoingReferences(EngineState *s, reg_t object, void *param, NoteCallback note); + + virtual void saveLoadWithSerializer(Common::Serializer &ser); +}; + +#define CLONE_USED -1 +#define CLONE_NONE -1 + +typedef Object Clone; + +struct Node { + reg_t pred; /**< Predecessor node */ + reg_t succ; /**< Successor node */ + reg_t key; + reg_t value; +}; /* List nodes */ + +struct List { + reg_t first; + reg_t last; +}; + +struct Hunk { + void *mem; + unsigned int size; + const char *type; +}; + +template<typename T> +struct Table : public MemObject { + typedef T value_type; + struct Entry : public T { + int next_free; /* Only used for free entries */ + }; + enum { HEAPENTRY_INVALID = -1 }; + + + int first_free; /**< Beginning of a singly linked list for entries */ + int entries_used; /**< Statistical information */ + + Common::Array<Entry> _table; + +public: + Table() { + initTable(); + } + + void initTable() { + entries_used = 0; + first_free = HEAPENTRY_INVALID; + _table.clear(); + } + + int allocEntry() { + entries_used++; + if (first_free != HEAPENTRY_INVALID) { + int oldff = first_free; + first_free = _table[oldff].next_free; + + _table[oldff].next_free = oldff; + return oldff; + } else { + uint newIdx = _table.size(); + _table.push_back(Entry()); + _table[newIdx].next_free = newIdx; // Tag as 'valid' + return newIdx; + } + } + + bool isValidEntry(int idx) { + return idx >= 0 && (uint)idx < _table.size() && _table[idx].next_free == idx; + } + + virtual void freeEntry(int idx) { + if (idx < 0 || (uint)idx >= _table.size()) + ::error("Table::freeEntry: Attempt to release invalid table index %d", idx); + + _table[idx].next_free = first_free; + first_free = idx; + entries_used--; + } + + virtual void listAllDeallocatable(SegmentId segId, void *param, NoteCallback note); +}; + + +/* CloneTable */ +struct CloneTable : public Table<Clone> { + virtual void freeAtAddress(SegManager *segmgr, reg_t sub_addr); + virtual void listAllOutgoingReferences(EngineState *s, reg_t object, void *param, NoteCallback note); + + virtual void saveLoadWithSerializer(Common::Serializer &ser); +}; + + +/* NodeTable */ +struct NodeTable : public Table<Node> { + virtual void freeAtAddress(SegManager *segmgr, reg_t sub_addr); + virtual void listAllOutgoingReferences(EngineState *s, reg_t object, void *param, NoteCallback note); + + virtual void saveLoadWithSerializer(Common::Serializer &ser); +}; + + +/* ListTable */ +struct ListTable : public Table<List> { + virtual void freeAtAddress(SegManager *segmgr, reg_t sub_addr); + virtual void listAllOutgoingReferences(EngineState *s, reg_t object, void *param, NoteCallback note); + + virtual void saveLoadWithSerializer(Common::Serializer &ser); +}; + + +/* HunkTable */ +struct HunkTable : public Table<Hunk> { + virtual void freeEntry(int idx) { + Table<Hunk>::freeEntry(idx); + + free(_table[idx].mem); + } + + virtual void saveLoadWithSerializer(Common::Serializer &ser); +}; + + +// Free-style memory +struct DynMem : public MemObject { + int _size; + char *_description; + byte *_buf; + +public: + DynMem() : _size(0), _description(0), _buf(0) {} + ~DynMem() { + free(_description); + _description = NULL; + free(_buf); + _buf = NULL; + } + + virtual byte *dereference(reg_t pointer, int *size); + virtual reg_t findCanonicAddress(SegManager *segmgr, reg_t sub_addr); + virtual void listAllDeallocatable(SegmentId segId, void *param, NoteCallback note); + + virtual void saveLoadWithSerializer(Common::Serializer &ser); +}; + + +} // End of namespace Sci + +#endif // SCI_ENGINE_VM_H diff --git a/engines/sci/engine/seg_manager.cpp b/engines/sci/engine/seg_manager.cpp index 69a939dae9..840c9bc525 100644 --- a/engines/sci/engine/seg_manager.cpp +++ b/engines/sci/engine/seg_manager.cpp @@ -232,49 +232,6 @@ int SegManager::deallocateScript(int script_nr) { return 1; } -MemObject *MemObject::createMemObject(MemObjectType type) { - MemObject *mem = 0; - switch (type) { - case MEM_OBJ_SCRIPT: - mem = new Script(); - break; - case MEM_OBJ_CLONES: - mem = new CloneTable(); - break; - case MEM_OBJ_LOCALS: - mem = new LocalVariables(); - break; - case MEM_OBJ_SYS_STRINGS: - mem = new SystemStrings(); - break; - case MEM_OBJ_STACK: - mem = new DataStack(); - break; - case MEM_OBJ_HUNK: - mem = new HunkTable(); - break; - case MEM_OBJ_STRING_FRAG: - mem = new StringFrag(); - break; - case MEM_OBJ_LISTS: - mem = new ListTable(); - break; - case MEM_OBJ_NODES: - mem = new NodeTable(); - break; - case MEM_OBJ_DYNMEM: - mem = new DynMem(); - break; - default: - error("Unknown MemObject type %d", type); - break; - } - - assert(mem); - mem->_type = type; - return mem; -} - MemObject *SegManager::memObjAllocate(SegmentId segid, int hash_id, MemObjectType type) { MemObject *mem = MemObject::createMemObject(type); if (!mem) { @@ -294,33 +251,6 @@ MemObject *SegManager::memObjAllocate(SegmentId segid, int hash_id, MemObjectTyp return mem; } -void Script::freeScript() { - free(buf); - buf = NULL; - buf_size = 0; - - _objects.clear(); - - delete obj_indices; - obj_indices = 0; - _codeBlocks.clear(); -} - -// memory operations - -void Script::mcpyInOut(int dst, const void *src, size_t n) { - if (buf) { - assert(dst + n <= buf_size); - memcpy(buf + dst, src, n); - } -} - -int16 Script::getHeap(uint16 offset) const { - VERIFY(offset + 1 < (int)buf_size, "invalid offset\n"); - return READ_LE_UINT16(buf + offset); -// return (buf[offset] | (buf[offset+1]) << 8); -} - // return the seg if script_id is valid and in the map, else -1 SegmentId SegManager::segGet(int script_id) const { return id_seg_map->lookupKey(script_id); @@ -367,53 +297,10 @@ bool SegManager::scriptIsLoaded(SegmentId seg) { return getScriptIfLoaded(seg) != 0; } -void Script::incrementLockers() { - lockers++; -} - -void Script::decrementLockers() { - if (lockers > 0) - lockers--; -} - -int Script::getLockers() const { - return lockers; -} - -void Script::setLockers(int lockers_) { - lockers = lockers_; -} - -void Script::setExportTableOffset(int offset) { - if (offset) { - export_table = (uint16 *)(buf + offset + 2); - exports_nr = READ_LE_UINT16((byte *)(export_table - 1)); - } else { - export_table = NULL; - exports_nr = 0; - } -} - void SegManager::setExportWidth(int flag) { exports_wide = flag; } -void Script::setSynonymsOffset(int offset) { - synonyms = buf + offset; -} - -byte *Script::getSynonyms() const { - return synonyms; -} - -void Script::setSynonymsNr(int n) { - synonyms_nr = n; -} - -int Script::getSynonymsNr() const { - return synonyms_nr; -} - int SegManager::relocateBlock(Common::Array<reg_t> &block, int block_location, SegmentId segment, int location) { int rel = location - block_location; @@ -925,67 +812,6 @@ Hunk *SegManager::alloc_Hunk(reg_t *addr) { return &(table->_table[offset]); } -byte *MemObject::dereference(reg_t pointer, int *size) { - error("Error: Trying to dereference pointer %04x:%04x to inappropriate segment", - PRINT_REG(pointer)); - return NULL; -} - -byte *Script::dereference(reg_t pointer, int *size) { - if (pointer.offset > buf_size) { - sciprintf("Error: Attempt to dereference invalid pointer %04x:%04x into script segment (script size=%d)\n", - PRINT_REG(pointer), (uint)buf_size); - return NULL; - } - if (size) - *size = buf_size - pointer.offset; - return (byte *)(buf + pointer.offset); -} - -byte *LocalVariables::dereference(reg_t pointer, int *size) { - // FIXME: The following doesn't seem to be endian safe. - // To fix this, we'd have to always treat the reg_t - // values stored here as in the little endian format. - int count = _locals.size() * sizeof(reg_t); - byte *base = (byte *)&_locals[0]; - - if (size) - *size = count; - - return base + pointer.offset; -} - -byte *DataStack::dereference(reg_t pointer, int *size) { - int count = nr * sizeof(reg_t); - byte *base = (byte *)entries; - - if (size) - *size = count; - - return base + pointer.offset; -} - -byte *DynMem::dereference(reg_t pointer, int *size) { - int count = _size; - byte *base = (byte *)_buf; - - if (size) - *size = count; - - return base + pointer.offset; -} - -byte *SystemStrings::dereference(reg_t pointer, int *size) { - if (size) - *size = strings[pointer.offset].max_size; - if (pointer.offset < SYS_STRINGS_MAX && strings[pointer.offset].name) - return (byte *)(strings[pointer.offset].value); - - // This occurs in KQ5CD when interacting with certain objects - warning("Attempt to dereference invalid pointer %04x:%04x", PRINT_REG(pointer)); - return NULL; -} - byte *SegManager::dereference(reg_t pointer, int *size) { if (!pointer.segment || (pointer.segment >= _heap.size()) || !_heap[pointer.segment]) { // This occurs in KQ5CD when interacting with certain objects @@ -1048,191 +874,5 @@ void SegManager::dbgPrint(const char* msg, void *i) { } -//-------------------- script -------------------- -reg_t Script::findCanonicAddress(SegManager *segmgr, reg_t addr) { - addr.offset = 0; - return addr; -} - -void Script::freeAtAddress(SegManager *segmgr, reg_t addr) { - /* - sciprintf("[GC] Freeing script %04x:%04x\n", PRINT_REG(addr)); - if (locals_segment) - sciprintf("[GC] Freeing locals %04x:0000\n", locals_segment); - */ - - if (_markedAsDeleted) - segmgr->deallocateScript(nr); -} - -void Script::listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) { - (*note)(param, make_reg(segId, 0)); -} - -void Script::listAllOutgoingReferences(EngineState *s, reg_t addr, void *param, NoteCallback note) { - Script *script = this; - - if (addr.offset <= script->buf_size && addr.offset >= -SCRIPT_OBJECT_MAGIC_OFFSET && RAW_IS_OBJECT(script->buf + addr.offset)) { - int idx = RAW_GET_CLASS_INDEX(script, addr); - if (idx >= 0 && (uint)idx < script->_objects.size()) { - // Note all local variables, if we have a local variable environment - if (script->locals_segment) - (*note)(param, make_reg(script->locals_segment, 0)); - - Object &obj = script->_objects[idx]; - for (uint i = 0; i < obj._variables.size(); i++) - (*note)(param, obj._variables[i]); - } else { - warning("Request for outgoing script-object reference at %04x:%04x yielded invalid index %d", PRINT_REG(addr), idx); - } - } else { - /* warning("Unexpected request for outgoing script-object references at %04x:%04x", PRINT_REG(addr));*/ - /* Happens e.g. when we're looking into strings */ - } -} - - -//-------------------- clones -------------------- - -template<typename T> -void Table<T>::listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) { - for (uint i = 0; i < _table.size(); i++) - if (isValidEntry(i)) - (*note)(param, make_reg(segId, i)); -} - -void CloneTable::listAllOutgoingReferences(EngineState *s, reg_t addr, void *param, NoteCallback note) { - CloneTable *clone_table = this; - Clone *clone; - -// assert(addr.segment == _segId); - - if (!clone_table->isValidEntry(addr.offset)) { - warning("Unexpected request for outgoing references from clone at %04x:%04x", PRINT_REG(addr)); -// BREAKPOINT(); - return; - } - - clone = &(clone_table->_table[addr.offset]); - - // Emit all member variables (including references to the 'super' delegate) - for (uint i = 0; i < clone->_variables.size(); i++) - (*note)(param, clone->_variables[i]); - - // Note that this also includes the 'base' object, which is part of the script and therefore also emits the locals. - (*note)(param, clone->pos); - //sciprintf("[GC] Reporting clone-pos %04x:%04x\n", PRINT_REG(clone->pos)); -} - -void CloneTable::freeAtAddress(SegManager *segmgr, reg_t addr) { - CloneTable *clone_table = this; - Object *victim_obj; - -// assert(addr.segment == _segId); - - victim_obj = &(clone_table->_table[addr.offset]); - -#ifdef GC_DEBUG - if (!(victim_obj->flags & OBJECT_FLAG_FREED)) - sciprintf("[GC] Warning: Clone %04x:%04x not reachable and not freed (freeing now)\n", PRINT_REG(addr)); -#ifdef GC_DEBUG_VERBOSE - else - sciprintf("[GC-DEBUG] Clone %04x:%04x: Freeing\n", PRINT_REG(addr)); -#endif -#endif - /* - sciprintf("[GC] Clone %04x:%04x: Freeing\n", PRINT_REG(addr)); - sciprintf("[GC] Clone had pos %04x:%04x\n", PRINT_REG(victim_obj->pos)); - */ - clone_table->freeEntry(addr.offset); -} - - -//-------------------- locals -------------------- -reg_t LocalVariables::findCanonicAddress(SegManager *segmgr, reg_t addr) { - // Reference the owning script - SegmentId owner_seg = segmgr->segGet(script_id); - - assert(owner_seg >= 0); - - return make_reg(owner_seg, 0); -} - -void LocalVariables::listAllOutgoingReferences(EngineState *s, reg_t addr, void *param, NoteCallback note) { -// assert(addr.segment == _segId); - - for (uint i = 0; i < _locals.size(); i++) - (*note)(param, _locals[i]); -} - - -//-------------------- stack -------------------- -reg_t DataStack::findCanonicAddress(SegManager *segmgr, reg_t addr) { - addr.offset = 0; - return addr; -} - -void DataStack::listAllOutgoingReferences(EngineState *s, reg_t addr, void *param, NoteCallback note) { - fprintf(stderr, "Emitting %d stack entries\n", nr); - for (int i = 0; i < nr; i++) - (*note)(param, entries[i]); - fprintf(stderr, "DONE"); -} - - -//-------------------- lists -------------------- -void ListTable::freeAtAddress(SegManager *segmgr, reg_t sub_addr) { - freeEntry(sub_addr.offset); -} - -void ListTable::listAllOutgoingReferences(EngineState *s, reg_t addr, void *param, NoteCallback note) { - if (!isValidEntry(addr.offset)) { - warning("Invalid list referenced for outgoing references: %04x:%04x", PRINT_REG(addr)); - return; - } - - List *list = &(_table[addr.offset]); - - note(param, list->first); - note(param, list->last); - // We could probably get away with just one of them, but - // let's be conservative here. -} - - -//-------------------- nodes -------------------- -void NodeTable::freeAtAddress(SegManager *segmgr, reg_t sub_addr) { - freeEntry(sub_addr.offset); -} - -void NodeTable::listAllOutgoingReferences(EngineState *s, reg_t addr, void *param, NoteCallback note) { - if (!isValidEntry(addr.offset)) { - warning("Invalid node referenced for outgoing references: %04x:%04x", PRINT_REG(addr)); - return; - } - Node *node = &(_table[addr.offset]); - - // We need all four here. Can't just stick with 'pred' OR 'succ' because node operations allow us - // to walk around from any given node - note(param, node->pred); - note(param, node->succ); - note(param, node->key); - note(param, node->value); -} - - -//-------------------- hunk -------------------- - -//-------------------- dynamic memory -------------------- - -reg_t DynMem::findCanonicAddress(SegManager *segmgr, reg_t addr) { - addr.offset = 0; - return addr; -} - -void DynMem::listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) { - (*note)(param, make_reg(segId, 0)); -} - } // End of namespace Sci diff --git a/engines/sci/engine/seg_manager.h b/engines/sci/engine/seg_manager.h index 643385fd74..dc91d60e69 100644 --- a/engines/sci/engine/seg_manager.h +++ b/engines/sci/engine/seg_manager.h @@ -29,6 +29,7 @@ #include "common/scummsys.h" #include "common/serializer.h" #include "sci/engine/vm.h" +#include "sci/engine/memobj.h" namespace Sci { diff --git a/engines/sci/engine/vm.h b/engines/sci/engine/vm.h index 70802bb377..39ffbb2760 100644 --- a/engines/sci/engine/vm.h +++ b/engines/sci/engine/vm.h @@ -28,7 +28,6 @@ /* VM and kernel declarations */ -#include "common/serializer.h" #include "sci/engine/vm_types.h" // for reg_t #include "common/util.h" @@ -38,128 +37,8 @@ namespace Sci { class SegManager; struct EngineState; typedef int sci_version_t; - -enum MemObjectType { - MEM_OBJ_INVALID = 0, - MEM_OBJ_SCRIPT = 1, - MEM_OBJ_CLONES = 2, - MEM_OBJ_LOCALS = 3, - MEM_OBJ_STACK = 4, - MEM_OBJ_SYS_STRINGS = 5, - MEM_OBJ_LISTS = 6, - MEM_OBJ_NODES = 7, - MEM_OBJ_HUNK = 8, - MEM_OBJ_DYNMEM = 9, - MEM_OBJ_STRING_FRAG = 10, - - MEM_OBJ_MAX // For sanity checking -}; - -struct MemObject : public Common::Serializable { - MemObjectType _type; - int _segmgrId; /**< Internal value used by the seg_manager's hash map */ - - typedef void (*NoteCallback)(void *param, reg_t addr); // FIXME: Bad choice of name - -public: - static MemObject *createMemObject(MemObjectType type); - -public: - virtual ~MemObject() {} - - inline MemObjectType getType() const { return _type; } - inline int getSegMgrId() const { return _segmgrId; } - - /** - * Dereferences a raw memory pointer. - * @param reg reference to dereference - * @param size if not NULL, set to the theoretical maximum size of the referenced data block - * @return the data block referenced - */ - virtual byte *dereference(reg_t pointer, int *size); - - /** - * Finds the canonic address associated with sub_reg. - * - * For each valid address a, there exists a canonic address c(a) such that c(a) = c(c(a)). - * This address "governs" a in the sense that deallocating c(a) will deallocate a. - * - * @param sub_addr base address whose canonic address is to be found - */ - virtual reg_t findCanonicAddress(SegManager *segmgr, reg_t sub_addr) { return sub_addr; } - - /** - * Deallocates all memory associated with the specified address. - * @param sub_addr address (within the given segment) to deallocate - */ - virtual void freeAtAddress(SegManager *segmgr, reg_t sub_addr) {} - - /** - * Iterates over and reports all addresses within the current segment. - * @param note Invoked for each address on which free_at_address() makes sense - * @param param parameter passed to 'note' - */ - virtual void listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) {} - - /** - * Iterates over all references reachable from the specified object. - * @param object object (within the current segment) to analyse - * @param param parameter passed to 'note' - * @param note Invoked for each outgoing reference within the object - * Note: This function may also choose to report numbers (segment 0) as adresses - */ - virtual void listAllOutgoingReferences(EngineState *s, reg_t object, void *param, NoteCallback note) {} -}; - - -// TODO: Implement the following class -struct StringFrag : public MemObject { - virtual void saveLoadWithSerializer(Common::Serializer &ser); -}; - struct IntMapper; - -enum { - SYS_STRINGS_MAX = 4, - - SYS_STRING_SAVEDIR = 0, - SYS_STRING_PARSER_BASE = 1, - - MAX_PARSER_BASE = 64 -}; - -struct SystemString { - char *name; - int max_size; - reg_t *value; -}; - -struct SystemStrings : public MemObject { - SystemString strings[SYS_STRINGS_MAX]; - -public: - SystemStrings() { - memset(strings, 0, sizeof(strings)); - } - ~SystemStrings() { - for (int i = 0; i < SYS_STRINGS_MAX; i++) { - SystemString *str = &strings[i]; - if (str->name) { - free(str->name); - str->name = NULL; - - free(str->value); - str->value = NULL; - - str->max_size = 0; - } - } - } - - virtual byte *dereference(reg_t pointer, int *size); - - virtual void saveLoadWithSerializer(Common::Serializer &ser); -}; +struct Object; /** Number of bytes to be allocated for the stack */ #define VM_STACK_SIZE 0x1000 @@ -243,400 +122,6 @@ struct Class { #define IS_CLASS(obj) (obj->_variables[SCRIPT_INFO_SELECTOR].offset & SCRIPT_INFO_CLASS) -/** This struct is used to buffer the list of send calls in send_selector() */ -struct CallsStruct { - union { - reg_t func; - reg_t *var; - } address; - StackPtr argp; - int argc; - Selector selector; - StackPtr sp; /**< Stack pointer */ - int type; /**< Same as ExecStack.type */ -}; - -struct LocalVariables : public MemObject { - int script_id; /**< Script ID this local variable block belongs to */ - Common::Array<reg_t> _locals; - -public: - LocalVariables() { - script_id = 0; - } - - virtual byte *dereference(reg_t pointer, int *size); - virtual reg_t findCanonicAddress(SegManager *segmgr, reg_t sub_addr); - virtual void listAllOutgoingReferences(EngineState *s, reg_t object, void *param, NoteCallback note); - - virtual void saveLoadWithSerializer(Common::Serializer &ser); -}; - -/** Clone has been marked as 'freed' */ -#define OBJECT_FLAG_FREED (0x1 << 0) - -struct Object { - int flags; - reg_t pos; /**< Object offset within its script; for clones, this is their base */ - int variable_names_nr; /**< Number of variable names, may be less than variables_nr */ - int methods_nr; - byte *base; /**< Points to a buffer all relative references (code, strings) point to */ - byte *base_obj; /**< base + object offset within base */ - uint16 *base_method; /**< Pointer to the method selector area for this object */ - uint16 *base_vars; /**< Pointer to the varselector area for this object */ - Common::Array<reg_t> _variables; -}; - -struct CodeBlock { - reg_t pos; - int size; -}; - -#define VM_OBJECT_GET_VARSELECTOR(obj, i) \ - (s->version < SCI_VERSION_1_1 ? \ - READ_LE_UINT16(obj->base_obj + obj->_variables.size() * 2 + i*2) : \ - *(obj->base_vars + i)) -#define VM_OBJECT_READ_PROPERTY(obj, i) (obj->_variables[i]) -#define VM_OBJECT_GET_FUNCSELECTOR(obj, i) \ - (s->version < SCI_VERSION_1_1 ? \ - READ_LE_UINT16((byte *) (obj->base_method + i)) : \ - READ_LE_UINT16((byte *) (obj->base_method + i*2 + 1))) -#define VM_OBJECT_READ_FUNCTION(obj, i) \ - (s->version < SCI_VERSION_1_1 ? \ - make_reg(obj->pos.segment, \ - READ_LE_UINT16((byte *) (obj->base_method \ - + obj->methods_nr + 1 \ - + i))) : \ - make_reg(obj->pos.segment, \ - READ_LE_UINT16((byte *) (obj->base_method \ - + i * 2 + 2)))) - - - - -struct Script : public MemObject { - int nr; /**< Script number */ - byte *buf; /**< Static data buffer, or NULL if not used */ - size_t buf_size; - size_t script_size; - size_t heap_size; - - byte *synonyms; /**< Synonyms block or 0 if not present*/ - byte *heap_start; /**< Start of heap if SCI1.1, NULL otherwise */ - uint16 *export_table; /**< Abs. offset of the export table or 0 if not present */ - - IntMapper *obj_indices; - - int exports_nr; /**< Number of entries in the exports table */ - int synonyms_nr; /**< Number of entries in the synonyms block */ - int lockers; /**< Number of classes and objects that require this script */ - - /** - * Table for objects, contains property variables. - * Indexed by the value stored at SCRIPT_LOCALVARPTR_OFFSET, - * see VM_OBJECT_[GS]ET_INDEX() - */ - Common::Array<Object> _objects; - - int locals_offset; - int locals_segment; /**< The local variable segment */ - LocalVariables *locals_block; - - Common::Array<CodeBlock> _codeBlocks; - int relocated; - bool _markedAsDeleted; - -public: - Script() { - nr = 0; - buf = NULL; - buf_size = 0; - script_size = 0; - heap_size = 0; - - synonyms = NULL; - heap_start = NULL; - export_table = NULL; - - obj_indices = NULL; - - locals_offset = 0; - locals_segment = 0; - locals_block = NULL; - - relocated = 0; - _markedAsDeleted = 0; - } - - ~Script() { - freeScript(); - } - - void freeScript(); - - virtual byte *dereference(reg_t pointer, int *size); - virtual reg_t findCanonicAddress(SegManager *segmgr, reg_t sub_addr); - virtual void freeAtAddress(SegManager *segmgr, reg_t sub_addr); - virtual void listAllDeallocatable(SegmentId segId, void *param, NoteCallback note); - virtual void listAllOutgoingReferences(EngineState *s, reg_t object, void *param, NoteCallback note); - - virtual void saveLoadWithSerializer(Common::Serializer &ser); - - // 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 synonyms associated with this script - * @return pointer to the synonyms, in non-parsed format. - */ - byte *getSynonyms() const; - - /** - * Retrieves the number of synonyms associated with this script. - * @return the number of synonyms associated with this script - */ - int getSynonymsNr() const; - - - /** - * Sets the script-relative offset of the exports table. - * @param offset script-relative exports table offset - */ - void setExportTableOffset(int offset); - - /** - * Sets the script-relative offset of the synonyms associated with this script. - * @param offset script-relative offset of the synonyms block - */ - void setSynonymsOffset(int offset); - - /** - * Sets the number of synonyms associated with this script, - * @param nr number of synonyms, as to be stored within the script - */ - void setSynonymsNr(int nr); - - - /** - * 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); - - - /** - * 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; - } - - /** - * Marks the script as not deleted. - */ - void unmarkDeleted() { - _markedAsDeleted = false; - } - - /** - * Determines whether the script is marked as being deleted. - */ - bool isMarkedAsDeleted() const { - return _markedAsDeleted; - } - - /** - * Retrieves a 16 bit value from within a script's heap representation. - * @param offset offset to read from - * @return the value read from the specified location - */ - int16 getHeap(uint16 offset) const; - -}; - -/** Data stack */ -struct DataStack : MemObject { - int nr; /**< Number of stack entries */ - reg_t *entries; - -public: - DataStack() { - nr = 0; - entries = NULL; - } - ~DataStack() { - free(entries); - entries = NULL; - } - - virtual byte *dereference(reg_t pointer, int *size); - virtual reg_t findCanonicAddress(SegManager *segmgr, reg_t sub_addr); - virtual void listAllOutgoingReferences(EngineState *s, reg_t object, void *param, NoteCallback note); - - virtual void saveLoadWithSerializer(Common::Serializer &ser); -}; - -#define CLONE_USED -1 -#define CLONE_NONE -1 - -typedef Object Clone; - -struct Node { - reg_t pred; /**< Predecessor node */ - reg_t succ; /**< Successor node */ - reg_t key; - reg_t value; -}; /* List nodes */ - -struct List { - reg_t first; - reg_t last; -}; - -struct Hunk { - void *mem; - unsigned int size; - const char *type; -}; - -template<typename T> -struct Table : public MemObject { - typedef T value_type; - struct Entry : public T { - int next_free; /* Only used for free entries */ - }; - enum { HEAPENTRY_INVALID = -1 }; - - - int first_free; /**< Beginning of a singly linked list for entries */ - int entries_used; /**< Statistical information */ - - Common::Array<Entry> _table; - -public: - Table() { - initTable(); - } - - void initTable() { - entries_used = 0; - first_free = HEAPENTRY_INVALID; - _table.clear(); - } - - int allocEntry() { - entries_used++; - if (first_free != HEAPENTRY_INVALID) { - int oldff = first_free; - first_free = _table[oldff].next_free; - - _table[oldff].next_free = oldff; - return oldff; - } else { - uint newIdx = _table.size(); - _table.push_back(Entry()); - _table[newIdx].next_free = newIdx; // Tag as 'valid' - return newIdx; - } - } - - bool isValidEntry(int idx) { - return idx >= 0 && (uint)idx < _table.size() && _table[idx].next_free == idx; - } - - virtual void freeEntry(int idx) { - if (idx < 0 || (uint)idx >= _table.size()) - ::error("Table::freeEntry: Attempt to release invalid table index %d", idx); - - _table[idx].next_free = first_free; - first_free = idx; - entries_used--; - } - - virtual void listAllDeallocatable(SegmentId segId, void *param, NoteCallback note); -}; - - -/* CloneTable */ -struct CloneTable : public Table<Clone> { - virtual void freeAtAddress(SegManager *segmgr, reg_t sub_addr); - virtual void listAllOutgoingReferences(EngineState *s, reg_t object, void *param, NoteCallback note); - - virtual void saveLoadWithSerializer(Common::Serializer &ser); -}; - - -/* NodeTable */ -struct NodeTable : public Table<Node> { - virtual void freeAtAddress(SegManager *segmgr, reg_t sub_addr); - virtual void listAllOutgoingReferences(EngineState *s, reg_t object, void *param, NoteCallback note); - - virtual void saveLoadWithSerializer(Common::Serializer &ser); -}; - - -/* ListTable */ -struct ListTable : public Table<List> { - virtual void freeAtAddress(SegManager *segmgr, reg_t sub_addr); - virtual void listAllOutgoingReferences(EngineState *s, reg_t object, void *param, NoteCallback note); - - virtual void saveLoadWithSerializer(Common::Serializer &ser); -}; - - -/* HunkTable */ -struct HunkTable : public Table<Hunk> { - virtual void freeEntry(int idx) { - Table<Hunk>::freeEntry(idx); - - free(_table[idx].mem); - } - - virtual void saveLoadWithSerializer(Common::Serializer &ser); -}; - - -// Free-style memory -struct DynMem : public MemObject { - int _size; - char *_description; - byte *_buf; - -public: - DynMem() : _size(0), _description(0), _buf(0) {} - ~DynMem() { - free(_description); - _description = NULL; - free(_buf); - _buf = NULL; - } - - virtual byte *dereference(reg_t pointer, int *size); - virtual reg_t findCanonicAddress(SegManager *segmgr, reg_t sub_addr); - virtual void listAllDeallocatable(SegmentId segId, void *param, NoteCallback note); - - virtual void saveLoadWithSerializer(Common::Serializer &ser); -}; /** Contains selector IDs for a few selected selectors */ struct selector_map_t { diff --git a/engines/sci/module.mk b/engines/sci/module.mk index f67d81e6e9..f2c58bcd8d 100644 --- a/engines/sci/module.mk +++ b/engines/sci/module.mk @@ -27,6 +27,7 @@ MODULE_OBJS = \ engine/ksound.o \ engine/kstring.o \ engine/message.o \ + engine/memobj.o \ engine/said.o \ engine/savegame.o \ engine/script.o \ |