diff options
author | Max Horn | 2010-02-02 22:50:32 +0000 |
---|---|---|
committer | Max Horn | 2010-02-02 22:50:32 +0000 |
commit | 6c322506dd15b164028f8c560e55323f1b834711 (patch) | |
tree | 2e233495329c36bb5df7c38215f0a6d89ac07a67 | |
parent | 5d80990380a8438dc474cdd5778807b7a72e2501 (diff) | |
download | scummvm-rg350-6c322506dd15b164028f8c560e55323f1b834711.tar.gz scummvm-rg350-6c322506dd15b164028f8c560e55323f1b834711.tar.bz2 scummvm-rg350-6c322506dd15b164028f8c560e55323f1b834711.zip |
SCI: Move some code around (no code changes)
svn-id: r47823
-rw-r--r-- | engines/sci/engine/script.cpp | 602 | ||||
-rw-r--r-- | engines/sci/engine/scriptdebug.cpp | 237 | ||||
-rw-r--r-- | engines/sci/engine/selector.cpp | 139 | ||||
-rw-r--r-- | engines/sci/engine/vm.cpp | 349 |
4 files changed, 665 insertions, 662 deletions
diff --git a/engines/sci/engine/script.cpp b/engines/sci/engine/script.cpp index 1c898da4e5..604aff557c 100644 --- a/engines/sci/engine/script.cpp +++ b/engines/sci/engine/script.cpp @@ -91,6 +91,8 @@ opcode_format g_opcode_formats[128][4] = { }; #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(EngineState *s) { // TODO: Check that this is correct if (s->detectLofsType() != SCI_VERSION_0_EARLY) { @@ -112,380 +114,354 @@ void script_adjust_opcode_formats(EngineState *s) { #endif } -#if 1 +#define INST_LOOKUP_CLASS(id) ((id == 0xffff)? NULL_REG : segMan->getClassAddress(id, SCRIPT_GET_LOCK, reg)) -#define FIND_SELECTOR(_slc_) _selectorCache._slc_ = findSelector(#_slc_) -#define FIND_SELECTOR2(_slc_, _slcstr_) _selectorCache._slc_ = findSelector(_slcstr_) +int script_instantiate_common(ResourceManager *resMan, SegManager *segMan, int script_nr, Resource **script, Resource **heap, int *was_new) { + *was_new = 1; -#else + *script = resMan->findResource(ResourceId(kResourceTypeScript, script_nr), 0); + if (getSciVersion() >= SCI_VERSION_1_1) + *heap = resMan->findResource(ResourceId(kResourceTypeHeap, script_nr), 0); -// The defines below can be used to construct static selector tables for games which don't have -// a vocab.997 resource, by dumping the selector table from other similar versions or games -#define FIND_SELECTOR(_slc_) _selectorCache._slc_ = findSelector(#_slc_); \ - printf("\t{ \"%s\", %d },\n", #_slc_, _selectorCache._slc_) + if (!*script || (getSciVersion() >= SCI_VERSION_1_1 && !heap)) { + warning("Script 0x%x requested but not found", script_nr); + if (getSciVersion() >= SCI_VERSION_1_1) { + if (*heap) + warning("Inconsistency: heap resource WAS found"); + else if (*script) + warning("Inconsistency: script resource WAS found"); + } + return 0; + } -#define FIND_SELECTOR2(_slc_, _slcstr_) _selectorCache._slc_ = findSelector(_slcstr_); \ - printf("\t{ \"%s\", %d },\n", _slcstr_, _selectorCache._slc_) + SegmentId seg_id = segMan->getScriptSegment(script_nr); + Script *scr = segMan->getScriptIfLoaded(seg_id); + if (scr) { + if (!scr->isMarkedAsDeleted()) { + scr->incrementLockers(); + return seg_id; + } else { + scr->freeScript(); + } + } else { + scr = segMan->allocateScript(script_nr, &seg_id); + if (!scr) { // ALL YOUR SCRIPT BASE ARE BELONG TO US + error("Not enough heap space for script size 0x%x of script 0x%x (Should this happen?)", (*script)->size, script_nr); + return 0; + } + } -#endif + scr->init(script_nr, resMan); -void Kernel::mapSelectors() { - // species - // superClass - // -info- - FIND_SELECTOR(y); - FIND_SELECTOR(x); - FIND_SELECTOR(view); - FIND_SELECTOR(loop); - FIND_SELECTOR(cel); - FIND_SELECTOR(underBits); - FIND_SELECTOR(nsTop); - FIND_SELECTOR(nsLeft); - FIND_SELECTOR(nsBottom); - FIND_SELECTOR(lsTop); - FIND_SELECTOR(lsLeft); - FIND_SELECTOR(lsBottom); - FIND_SELECTOR(lsRight); - FIND_SELECTOR(nsRight); - FIND_SELECTOR(signal); - FIND_SELECTOR(illegalBits); - FIND_SELECTOR(brTop); - FIND_SELECTOR(brLeft); - FIND_SELECTOR(brBottom); - FIND_SELECTOR(brRight); - // name - // key - // time - FIND_SELECTOR(text); - FIND_SELECTOR(elements); - // color - // back - FIND_SELECTOR(mode); - // style - FIND_SELECTOR(state); - FIND_SELECTOR(font); - FIND_SELECTOR(type); - // window - FIND_SELECTOR(cursor); - FIND_SELECTOR(max); - // mark - // who - FIND_SELECTOR(message); - // edit - FIND_SELECTOR(play); - FIND_SELECTOR(number); - FIND_SELECTOR(handle); // nodePtr - FIND_SELECTOR(client); - FIND_SELECTOR(dx); - FIND_SELECTOR(dy); - FIND_SELECTOR2(b_movCnt, "b-moveCnt"); - FIND_SELECTOR2(b_i1, "b-i1"); - FIND_SELECTOR2(b_i2, "b-i2"); - FIND_SELECTOR2(b_di, "b-di"); - FIND_SELECTOR2(b_xAxis, "b-xAxis"); - FIND_SELECTOR2(b_incr, "b-incr"); - FIND_SELECTOR(xStep); - FIND_SELECTOR(yStep); - FIND_SELECTOR(moveSpeed); - FIND_SELECTOR(canBeHere); // cantBeHere - FIND_SELECTOR(heading); - FIND_SELECTOR(mover); - FIND_SELECTOR(doit); - FIND_SELECTOR(isBlocked); - FIND_SELECTOR(looper); - FIND_SELECTOR(priority); - FIND_SELECTOR(modifiers); - FIND_SELECTOR(replay); - // setPri - // at - // next - // done - // width - FIND_SELECTOR(wordFail); - FIND_SELECTOR(syntaxFail); - // semanticFail - // pragmaFail - // said - FIND_SELECTOR(claimed); - // value - // save - // restore - // title - // button - // icon - // draw - FIND_SELECTOR2(delete_, "delete"); - FIND_SELECTOR(z); - // ----------------------------- - FIND_SELECTOR(size); - FIND_SELECTOR(moveDone); - FIND_SELECTOR(vol); - FIND_SELECTOR(pri); - FIND_SELECTOR(min); - FIND_SELECTOR(sec); - FIND_SELECTOR(frame); - FIND_SELECTOR(dataInc); - FIND_SELECTOR(palette); - FIND_SELECTOR(cantBeHere); - FIND_SELECTOR(nodePtr); - FIND_SELECTOR(flags); - FIND_SELECTOR(points); - FIND_SELECTOR(syncCue); - FIND_SELECTOR(syncTime); - FIND_SELECTOR(printLang); - FIND_SELECTOR(subtitleLang); - FIND_SELECTOR(parseLang); - FIND_SELECTOR(overlay); - FIND_SELECTOR(setCursor); - FIND_SELECTOR(topString); - FIND_SELECTOR(scaleSignal); - FIND_SELECTOR(scaleX); - FIND_SELECTOR(scaleY); + reg_t reg; + reg.segment = seg_id; + reg.offset = 0; -#ifdef ENABLE_SCI32 - FIND_SELECTOR(data); - FIND_SELECTOR(picture); - FIND_SELECTOR(plane); - FIND_SELECTOR(top); - FIND_SELECTOR(left); -#endif + // Set heap position (beyond the size word) + scr->setLockers(1); + scr->setExportTableOffset(0); + scr->setSynonymsOffset(0); + scr->setSynonymsNr(0); + + *was_new = 0; + + return seg_id; } -void Kernel::dumpScriptObject(char *data, int seeker, int objsize) { - int selectors, overloads, selectorsize; - int species = (int16)READ_LE_UINT16((unsigned char *) data + 8 + seeker); - int superclass = (int16)READ_LE_UINT16((unsigned char *) data + 10 + seeker); - int namepos = (int16)READ_LE_UINT16((unsigned char *) data + 14 + seeker); - int i = 0; +int script_instantiate_sci0(ResourceManager *resMan, SegManager *segMan, int script_nr) { + int objtype; + unsigned int objlength; + int relocation = -1; + int magic_pos_adder; // Usually 0; 2 for older SCI versions + Resource *script; + int was_new; + bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY); + + const int seg_id = script_instantiate_common(resMan, segMan, script_nr, &script, NULL, &was_new); + + if (was_new) + return seg_id; - printf("Object\n"); + Script *scr = segMan->getScript(seg_id); - Common::hexdump((unsigned char *) data + seeker, objsize - 4, 16, seeker); - //-4 because the size includes the two-word header + if (oldScriptHeader) { + // + int locals_nr = READ_LE_UINT16(script->data); - printf("Name: %s\n", namepos ? ((char *)(data + namepos)) : "<unknown>"); - printf("Superclass: %x\n", superclass); - printf("Species: %x\n", species); - printf("-info-:%x\n", (int16)READ_LE_UINT16((unsigned char *) data + 12 + seeker) & 0xffff); + // 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. - printf("Function area offset: %x\n", (int16)READ_LE_UINT16((unsigned char *) data + seeker + 4)); - printf("Selectors [%x]:\n", selectors = (selectorsize = (int16)READ_LE_UINT16((unsigned char *) data + seeker + 6))); + scr->mcpyInOut(0, script->data, script->size); + magic_pos_adder = 2; // Step over the funny prefix - seeker += 8; + if (locals_nr) + segMan->scriptInitialiseLocalsZero(seg_id, locals_nr); - while (selectors--) { - printf(" [#%03x] = 0x%x\n", i++, (int16)READ_LE_UINT16((unsigned char *)data + seeker) & 0xffff); - seeker += 2; + } else { + scr->mcpyInOut(0, script->data, script->size); + magic_pos_adder = 0; } - printf("Overridden functions: %x\n", selectors = overloads = (int16)READ_LE_UINT16((unsigned char *)data + seeker)); + // Now do a first pass through the script objects to find the + // export table and local variable block - seeker += 2; + reg_t reg; + reg.segment = seg_id; + reg.offset = magic_pos_adder; - if (overloads < 100) - while (overloads--) { - int selector = (int16)READ_LE_UINT16((unsigned char *) data + (seeker)); + objlength = 0; - printf(" [%03x] %s: @", selector & 0xffff, (selector >= 0 && selector < (int)_selectorNames.size()) ? _selectorNames[selector].c_str() : "<?>"); - printf("%04x\n", (int16)READ_LE_UINT16((unsigned char *)data + seeker + selectors*2 + 2) & 0xffff); + do { + reg_t data_base; + reg_t addr; + reg.offset += objlength; // Step over the last checked object + objtype = scr->getHeap(reg.offset); + if (!objtype) + break; + + objlength = scr->getHeap(reg.offset + 2); - seeker += 2; + // This happens in some demos (e.g. the EcoQuest 1 demo). Not sure what is the + // actual cause of it, but the scripts of these demos can't be loaded properly + // and we're stuck forever in this loop, as objlength never changes + if (!objlength) { + warning("script_instantiate_sci0: objlength is 0, unable to parse script"); + return 0; } -} -void Kernel::dumpScriptClass(char *data, int seeker, int objsize) { - int selectors, overloads, selectorsize; - int species = (int16)READ_LE_UINT16((unsigned char *) data + 8 + seeker); - int superclass = (int16)READ_LE_UINT16((unsigned char *) data + 10 + seeker); - int namepos = (int16)READ_LE_UINT16((unsigned char *) data + 14 + seeker); + data_base = reg; + data_base.offset += 4; - printf("Class\n"); + addr = data_base; - Common::hexdump((unsigned char *) data + seeker, objsize - 4, 16, seeker); + switch (objtype) { + case SCI_OBJ_EXPORTS: { + scr->setExportTableOffset(data_base.offset); + } + break; + + case SCI_OBJ_SYNONYMS: + scr->setSynonymsOffset(addr.offset); // +4 is to step over the header + scr->setSynonymsNr((objlength) / 4); + break; + + case SCI_OBJ_LOCALVARS: + segMan->scriptInitialiseLocals(data_base); + break; - printf("Name: %s\n", namepos ? ((char *)data + namepos) : "<unknown>"); - printf("Superclass: %x\n", superclass); - printf("Species: %x\n", species); - printf("-info-:%x\n", (int16)READ_LE_UINT16((unsigned char *)data + 12 + seeker) & 0xffff); + case SCI_OBJ_CLASS: { + int classpos = addr.offset - SCRIPT_OBJECT_MAGIC_OFFSET; + int species; + species = scr->getHeap(addr.offset - SCRIPT_OBJECT_MAGIC_OFFSET + SCRIPT_SPECIES_OFFSET); + if (species < 0 || species >= (int)segMan->_classtable.size()) { + if (species == (int)segMan->_classtable.size()) { + // Happens in the LSL2 demo + warning("Applying workaround for an off-by-one invalid species access"); + segMan->_classtable.resize(segMan->_classtable.size() + 1); + } else { + warning("Invalid species %d(0x%x) not in interval " + "[0,%d) while instantiating script %d\n", + species, species, segMan->_classtable.size(), + script_nr); + return 0; + } + } - printf("Function area offset: %x\n", (int16)READ_LE_UINT16((unsigned char *)data + seeker + 4)); - printf("Selectors [%x]:\n", selectors = (selectorsize = (int16)READ_LE_UINT16((unsigned char *)data + seeker + 6))); + segMan->_classtable[species].reg = addr; + segMan->_classtable[species].reg.offset = classpos; + // Set technical class position-- into the block allocated for it + } + break; - seeker += 8; - selectorsize <<= 1; + default: + break; + } + } while (objtype != 0); - while (selectors--) { - int selector = (int16)READ_LE_UINT16((unsigned char *) data + (seeker) + selectorsize); + // And now a second pass to adjust objects and class pointers, and the general pointers - printf(" [%03x] %s = 0x%x\n", 0xffff & selector, (selector >= 0 && selector < (int)_selectorNames.size()) ? _selectorNames[selector].c_str() : "<?>", - (int16)READ_LE_UINT16((unsigned char *)data + seeker) & 0xffff); + objlength = 0; + reg.offset = magic_pos_adder; // Reset counter - seeker += 2; - } + do { + reg_t addr; + reg.offset += objlength; // Step over the last checked object + objtype = scr->getHeap(reg.offset); + if (!objtype) + break; - seeker += selectorsize; + objlength = scr->getHeap(reg.offset + 2); - printf("Overloaded functions: %x\n", selectors = overloads = (int16)READ_LE_UINT16((unsigned char *)data + seeker)); + addr = reg; + addr.offset += 4; // Step over header - seeker += 2; + switch (objtype) { + case SCI_OBJ_CODE: + scr->scriptAddCodeBlock(addr); + break; + case SCI_OBJ_OBJECT: + case SCI_OBJ_CLASS: { // object or class? + Object *obj = scr->scriptObjInit(addr); - while (overloads--) { - int selector = (int16)READ_LE_UINT16((unsigned char *)data + (seeker)); - fprintf(stderr, "selector=%d; selectorNames.size() =%d\n", selector, _selectorNames.size()); - printf(" [%03x] %s: @", selector & 0xffff, (selector >= 0 && selector < (int)_selectorNames.size()) ? - _selectorNames[selector].c_str() : "<?>"); - printf("%04x\n", (int16)READ_LE_UINT16((unsigned char *)data + seeker + selectors * 2 + 2) & 0xffff); + // Instantiate the superclass, if neccessary + obj->setSpeciesSelector(INST_LOOKUP_CLASS(obj->getSpeciesSelector().offset)); - seeker += 2; - } -} + Object *baseObj = segMan->getObject(obj->getSpeciesSelector()); -void Kernel::dissectScript(int scriptNumber, Vocabulary *vocab) { - int objectctr[11] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - unsigned int _seeker = 0; - Resource *script = _resMan->findResource(ResourceId(kResourceTypeScript, scriptNumber), 0); + if (baseObj) { + obj->setVarCount(baseObj->getVarCount()); + // Copy base from species class, as we need its selector IDs + obj->_baseObj = baseObj->_baseObj; - if (!script) { - warning("dissectScript(): Script not found!\n"); - return; - } + obj->setSuperClassSelector(INST_LOOKUP_CLASS(obj->getSuperClassSelector().offset)); + } else { + warning("Failed to locate base object for object at %04X:%04X; skipping", PRINT_REG(addr)); - while (_seeker < script->size) { - int objtype = (int16)READ_LE_UINT16(script->data + _seeker); - int objsize; - unsigned int seeker = _seeker + 4; + scr->scriptObjRemove(addr); + } + } // if object or class + break; + case SCI_OBJ_POINTERS: // A relocation table + relocation = addr.offset; + break; - if (!objtype) { - printf("End of script object (#0) encountered.\n"); - printf("Classes: %i, Objects: %i, Export: %i,\n Var: %i (all base 10)", - objectctr[6], objectctr[1], objectctr[7], objectctr[10]); - return; + default: + break; } - printf("\n"); + } while (objtype != 0 && reg.offset < script->size - 2); + + if (relocation >= 0) + scr->scriptRelocate(make_reg(seg_id, relocation)); - objsize = (int16)READ_LE_UINT16(script->data + _seeker + 2); + return reg.segment; // instantiation successful +} - printf("Obj type #%x, size 0x%x: ", objtype, objsize); +int script_instantiate_sci11(ResourceManager *resMan, SegManager *segMan, int script_nr) { + Resource *script, *heap; + int _heapStart; + reg_t reg; + int was_new; - _seeker += objsize; + const int seg_id = script_instantiate_common(resMan, segMan, script_nr, &script, &heap, &was_new); - objectctr[objtype]++; + if (was_new) + return seg_id; - switch (objtype) { - case SCI_OBJ_OBJECT: - dumpScriptObject((char *)script->data, seeker, objsize); - break; + Script *scr = segMan->getScript(seg_id); - case SCI_OBJ_CODE: { - printf("Code\n"); - Common::hexdump(script->data + seeker, objsize - 4, 16, seeker); - }; - break; + _heapStart = script->size; + if (script->size & 2) + _heapStart ++; - case 3: { - printf("<unknown>\n"); - Common::hexdump(script->data + seeker, objsize - 4, 16, seeker); - }; - break; + scr->mcpyInOut(0, script->data, script->size); + scr->mcpyInOut(_heapStart, heap->data, heap->size); - case SCI_OBJ_SAID: { - printf("Said\n"); - Common::hexdump(script->data + seeker, objsize - 4, 16, seeker); - - printf("%04x: ", seeker); - while (seeker < _seeker) { - unsigned char nextitem = script->data [seeker++]; - if (nextitem == 0xFF) - printf("\n%04x: ", seeker); - else if (nextitem >= 0xF0) { - switch (nextitem) { - case 0xf0: - printf(", "); - break; - case 0xf1: - printf("& "); - break; - case 0xf2: - printf("/ "); - break; - case 0xf3: - printf("( "); - break; - case 0xf4: - printf(") "); - break; - case 0xf5: - printf("[ "); - break; - case 0xf6: - printf("] "); - break; - case 0xf7: - printf("# "); - break; - case 0xf8: - printf("< "); - break; - case 0xf9: - printf("> "); - break; - } - } else { - nextitem = nextitem << 8 | script->data [seeker++]; - printf("%s[%03x] ", vocab->getAnyWordFromGroup(nextitem), nextitem); - } - } - printf("\n"); - } - break; + if (READ_LE_UINT16(script->data + 6) > 0) + scr->setExportTableOffset(6); - case SCI_OBJ_STRINGS: { - printf("Strings\n"); - while (script->data [seeker]) { - printf("%04x: %s\n", seeker, script->data + seeker); - seeker += strlen((char *)script->data + seeker) + 1; - } - seeker++; // the ending zero byte - }; - break; + reg.segment = seg_id; + reg.offset = _heapStart + 4; + segMan->scriptInitialiseLocals(reg); + + segMan->scriptRelocateExportsSci11(seg_id); + segMan->scriptInitialiseObjectsSci11(seg_id); + + reg.offset = READ_LE_UINT16(heap->data); + scr->heapRelocate(reg); - case SCI_OBJ_CLASS: - dumpScriptClass((char *)script->data, seeker, objsize); + return seg_id; +} + +int script_instantiate(ResourceManager *resMan, SegManager *segMan, int script_nr) { + if (getSciVersion() >= SCI_VERSION_1_1) + return script_instantiate_sci11(resMan, segMan, script_nr); + else + return script_instantiate_sci0(resMan, segMan, script_nr); +} + +void script_uninstantiate_sci0(SegManager *segMan, int script_nr, SegmentId seg) { + bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY); + reg_t reg = make_reg(seg, oldScriptHeader ? 2 : 0); + int objtype, objlength; + Script *scr = segMan->getScript(seg); + + // Make a pass over the object in order uninstantiate all superclasses + objlength = 0; + + do { + reg.offset += objlength; // Step over the last checked object + + objtype = scr->getHeap(reg.offset); + if (!objtype) break; + objlength = scr->getHeap(reg.offset + 2); // use SEG_UGET_HEAP ?? - case SCI_OBJ_EXPORTS: { - printf("Exports\n"); - Common::hexdump((unsigned char *)script->data + seeker, objsize - 4, 16, seeker); - }; - break; + reg.offset += 4; // Step over header - case SCI_OBJ_POINTERS: { - printf("Pointers\n"); - Common::hexdump(script->data + seeker, objsize - 4, 16, seeker); - }; - break; + if ((objtype == SCI_OBJ_OBJECT) || (objtype == SCI_OBJ_CLASS)) { // object or class? + int superclass; - case 9: { - printf("<unknown>\n"); - Common::hexdump(script->data + seeker, objsize - 4, 16, seeker); - }; - break; + reg.offset -= SCRIPT_OBJECT_MAGIC_OFFSET; - case SCI_OBJ_LOCALVARS: { - printf("Local vars\n"); - Common::hexdump(script->data + seeker, objsize - 4, 16, seeker); - }; - break; + superclass = scr->getHeap(reg.offset + SCRIPT_SUPERCLASS_OFFSET); // Get superclass... - default: - printf("Unsupported!\n"); - return; - } + if (superclass >= 0) { + int superclass_script = segMan->_classtable[superclass].script; + + if (superclass_script == script_nr) { + if (scr->getLockers()) + scr->decrementLockers(); // Decrease lockers if this is us ourselves + } else + script_uninstantiate(segMan, superclass_script); + // Recurse to assure that the superclass lockers number gets decreased + } + + reg.offset += SCRIPT_OBJECT_MAGIC_OFFSET; + } // if object or class + + reg.offset -= 4; // Step back on header + + } while (objtype != 0); +} +void script_uninstantiate(SegManager *segMan, int script_nr) { + SegmentId segment = segMan->getScriptSegment(script_nr); + Script *scr = segMan->getScriptIfLoaded(segment); + + if (!scr) { // Is it already loaded? + //warning("unloading script 0x%x requested although not loaded", script_nr); + // This is perfectly valid SCI behaviour + return; } - printf("Script ends without terminator\n"); + scr->decrementLockers(); // One less locker + + if (scr->getLockers() > 0) + return; + + // Free all classtable references to this script + for (uint i = 0; i < segMan->_classtable.size(); i++) + if (segMan->_classtable[i].reg.segment == segment) + segMan->_classtable[i].reg = NULL_REG; + + if (getSciVersion() < SCI_VERSION_1_1) + script_uninstantiate_sci0(segMan, script_nr, segment); + // FIXME: Add proper script uninstantiation for SCI 1.1 + + if (scr->getLockers()) + return; // if xxx.lockers > 0 + + // Otherwise unload it completely + // Explanation: I'm starting to believe that this work is done by SCI itself. + scr->markDeleted(); + + debugC(kDebugLevelScripts, "Unloaded script 0x%x.", script_nr); + + return; } + } // End of namespace Sci diff --git a/engines/sci/engine/scriptdebug.cpp b/engines/sci/engine/scriptdebug.cpp index b54e9cd35a..f58ea869fb 100644 --- a/engines/sci/engine/scriptdebug.cpp +++ b/engines/sci/engine/scriptdebug.cpp @@ -428,4 +428,241 @@ void script_debug(EngineState *s, bool bp) { con->attach(); } +void Kernel::dumpScriptObject(char *data, int seeker, int objsize) { + int selectors, overloads, selectorsize; + int species = (int16)READ_LE_UINT16((unsigned char *) data + 8 + seeker); + int superclass = (int16)READ_LE_UINT16((unsigned char *) data + 10 + seeker); + int namepos = (int16)READ_LE_UINT16((unsigned char *) data + 14 + seeker); + int i = 0; + + printf("Object\n"); + + Common::hexdump((unsigned char *) data + seeker, objsize - 4, 16, seeker); + //-4 because the size includes the two-word header + + printf("Name: %s\n", namepos ? ((char *)(data + namepos)) : "<unknown>"); + printf("Superclass: %x\n", superclass); + printf("Species: %x\n", species); + printf("-info-:%x\n", (int16)READ_LE_UINT16((unsigned char *) data + 12 + seeker) & 0xffff); + + printf("Function area offset: %x\n", (int16)READ_LE_UINT16((unsigned char *) data + seeker + 4)); + printf("Selectors [%x]:\n", selectors = (selectorsize = (int16)READ_LE_UINT16((unsigned char *) data + seeker + 6))); + + seeker += 8; + + while (selectors--) { + printf(" [#%03x] = 0x%x\n", i++, (int16)READ_LE_UINT16((unsigned char *)data + seeker) & 0xffff); + seeker += 2; + } + + printf("Overridden functions: %x\n", selectors = overloads = (int16)READ_LE_UINT16((unsigned char *)data + seeker)); + + seeker += 2; + + if (overloads < 100) + while (overloads--) { + int selector = (int16)READ_LE_UINT16((unsigned char *) data + (seeker)); + + printf(" [%03x] %s: @", selector & 0xffff, (selector >= 0 && selector < (int)_selectorNames.size()) ? _selectorNames[selector].c_str() : "<?>"); + printf("%04x\n", (int16)READ_LE_UINT16((unsigned char *)data + seeker + selectors*2 + 2) & 0xffff); + + seeker += 2; + } +} + +void Kernel::dumpScriptClass(char *data, int seeker, int objsize) { + int selectors, overloads, selectorsize; + int species = (int16)READ_LE_UINT16((unsigned char *) data + 8 + seeker); + int superclass = (int16)READ_LE_UINT16((unsigned char *) data + 10 + seeker); + int namepos = (int16)READ_LE_UINT16((unsigned char *) data + 14 + seeker); + + printf("Class\n"); + + Common::hexdump((unsigned char *) data + seeker, objsize - 4, 16, seeker); + + printf("Name: %s\n", namepos ? ((char *)data + namepos) : "<unknown>"); + printf("Superclass: %x\n", superclass); + printf("Species: %x\n", species); + printf("-info-:%x\n", (int16)READ_LE_UINT16((unsigned char *)data + 12 + seeker) & 0xffff); + + printf("Function area offset: %x\n", (int16)READ_LE_UINT16((unsigned char *)data + seeker + 4)); + printf("Selectors [%x]:\n", selectors = (selectorsize = (int16)READ_LE_UINT16((unsigned char *)data + seeker + 6))); + + seeker += 8; + selectorsize <<= 1; + + while (selectors--) { + int selector = (int16)READ_LE_UINT16((unsigned char *) data + (seeker) + selectorsize); + + printf(" [%03x] %s = 0x%x\n", 0xffff & selector, (selector >= 0 && selector < (int)_selectorNames.size()) ? _selectorNames[selector].c_str() : "<?>", + (int16)READ_LE_UINT16((unsigned char *)data + seeker) & 0xffff); + + seeker += 2; + } + + seeker += selectorsize; + + printf("Overloaded functions: %x\n", selectors = overloads = (int16)READ_LE_UINT16((unsigned char *)data + seeker)); + + seeker += 2; + + while (overloads--) { + int selector = (int16)READ_LE_UINT16((unsigned char *)data + (seeker)); + fprintf(stderr, "selector=%d; selectorNames.size() =%d\n", selector, _selectorNames.size()); + printf(" [%03x] %s: @", selector & 0xffff, (selector >= 0 && selector < (int)_selectorNames.size()) ? + _selectorNames[selector].c_str() : "<?>"); + printf("%04x\n", (int16)READ_LE_UINT16((unsigned char *)data + seeker + selectors * 2 + 2) & 0xffff); + + seeker += 2; + } +} + +void Kernel::dissectScript(int scriptNumber, Vocabulary *vocab) { + int objectctr[11] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + unsigned int _seeker = 0; + Resource *script = _resMan->findResource(ResourceId(kResourceTypeScript, scriptNumber), 0); + + if (!script) { + warning("dissectScript(): Script not found!\n"); + return; + } + + while (_seeker < script->size) { + int objtype = (int16)READ_LE_UINT16(script->data + _seeker); + int objsize; + unsigned int seeker = _seeker + 4; + + if (!objtype) { + printf("End of script object (#0) encountered.\n"); + printf("Classes: %i, Objects: %i, Export: %i,\n Var: %i (all base 10)", + objectctr[6], objectctr[1], objectctr[7], objectctr[10]); + return; + } + + printf("\n"); + + objsize = (int16)READ_LE_UINT16(script->data + _seeker + 2); + + printf("Obj type #%x, size 0x%x: ", objtype, objsize); + + _seeker += objsize; + + objectctr[objtype]++; + + switch (objtype) { + case SCI_OBJ_OBJECT: + dumpScriptObject((char *)script->data, seeker, objsize); + break; + + case SCI_OBJ_CODE: { + printf("Code\n"); + Common::hexdump(script->data + seeker, objsize - 4, 16, seeker); + }; + break; + + case 3: { + printf("<unknown>\n"); + Common::hexdump(script->data + seeker, objsize - 4, 16, seeker); + }; + break; + + case SCI_OBJ_SAID: { + printf("Said\n"); + Common::hexdump(script->data + seeker, objsize - 4, 16, seeker); + + printf("%04x: ", seeker); + while (seeker < _seeker) { + unsigned char nextitem = script->data [seeker++]; + if (nextitem == 0xFF) + printf("\n%04x: ", seeker); + else if (nextitem >= 0xF0) { + switch (nextitem) { + case 0xf0: + printf(", "); + break; + case 0xf1: + printf("& "); + break; + case 0xf2: + printf("/ "); + break; + case 0xf3: + printf("( "); + break; + case 0xf4: + printf(") "); + break; + case 0xf5: + printf("[ "); + break; + case 0xf6: + printf("] "); + break; + case 0xf7: + printf("# "); + break; + case 0xf8: + printf("< "); + break; + case 0xf9: + printf("> "); + break; + } + } else { + nextitem = nextitem << 8 | script->data [seeker++]; + printf("%s[%03x] ", vocab->getAnyWordFromGroup(nextitem), nextitem); + } + } + printf("\n"); + } + break; + + case SCI_OBJ_STRINGS: { + printf("Strings\n"); + while (script->data [seeker]) { + printf("%04x: %s\n", seeker, script->data + seeker); + seeker += strlen((char *)script->data + seeker) + 1; + } + seeker++; // the ending zero byte + }; + break; + + case SCI_OBJ_CLASS: + dumpScriptClass((char *)script->data, seeker, objsize); + break; + + case SCI_OBJ_EXPORTS: { + printf("Exports\n"); + Common::hexdump((unsigned char *)script->data + seeker, objsize - 4, 16, seeker); + }; + break; + + case SCI_OBJ_POINTERS: { + printf("Pointers\n"); + Common::hexdump(script->data + seeker, objsize - 4, 16, seeker); + }; + break; + + case 9: { + printf("<unknown>\n"); + Common::hexdump(script->data + seeker, objsize - 4, 16, seeker); + }; + break; + + case SCI_OBJ_LOCALVARS: { + printf("Local vars\n"); + Common::hexdump(script->data + seeker, objsize - 4, 16, seeker); + }; + break; + + default: + printf("Unsupported!\n"); + return; + } + + } + + printf("Script ends without terminator\n"); +} + } // End of namespace Sci diff --git a/engines/sci/engine/selector.cpp b/engines/sci/engine/selector.cpp index 5e8b969edf..720c7546d8 100644 --- a/engines/sci/engine/selector.cpp +++ b/engines/sci/engine/selector.cpp @@ -29,6 +29,145 @@ namespace Sci { +#if 1 + +#define FIND_SELECTOR(_slc_) _selectorCache._slc_ = findSelector(#_slc_) +#define FIND_SELECTOR2(_slc_, _slcstr_) _selectorCache._slc_ = findSelector(_slcstr_) + +#else + +// The defines below can be used to construct static selector tables for games which don't have +// a vocab.997 resource, by dumping the selector table from other similar versions or games +#define FIND_SELECTOR(_slc_) _selectorCache._slc_ = findSelector(#_slc_); \ + printf("\t{ \"%s\", %d },\n", #_slc_, _selectorCache._slc_) + +#define FIND_SELECTOR2(_slc_, _slcstr_) _selectorCache._slc_ = findSelector(_slcstr_); \ + printf("\t{ \"%s\", %d },\n", _slcstr_, _selectorCache._slc_) + +#endif + +void Kernel::mapSelectors() { + // species + // superClass + // -info- + FIND_SELECTOR(y); + FIND_SELECTOR(x); + FIND_SELECTOR(view); + FIND_SELECTOR(loop); + FIND_SELECTOR(cel); + FIND_SELECTOR(underBits); + FIND_SELECTOR(nsTop); + FIND_SELECTOR(nsLeft); + FIND_SELECTOR(nsBottom); + FIND_SELECTOR(lsTop); + FIND_SELECTOR(lsLeft); + FIND_SELECTOR(lsBottom); + FIND_SELECTOR(lsRight); + FIND_SELECTOR(nsRight); + FIND_SELECTOR(signal); + FIND_SELECTOR(illegalBits); + FIND_SELECTOR(brTop); + FIND_SELECTOR(brLeft); + FIND_SELECTOR(brBottom); + FIND_SELECTOR(brRight); + // name + // key + // time + FIND_SELECTOR(text); + FIND_SELECTOR(elements); + // color + // back + FIND_SELECTOR(mode); + // style + FIND_SELECTOR(state); + FIND_SELECTOR(font); + FIND_SELECTOR(type); + // window + FIND_SELECTOR(cursor); + FIND_SELECTOR(max); + // mark + // who + FIND_SELECTOR(message); + // edit + FIND_SELECTOR(play); + FIND_SELECTOR(number); + FIND_SELECTOR(handle); // nodePtr + FIND_SELECTOR(client); + FIND_SELECTOR(dx); + FIND_SELECTOR(dy); + FIND_SELECTOR2(b_movCnt, "b-moveCnt"); + FIND_SELECTOR2(b_i1, "b-i1"); + FIND_SELECTOR2(b_i2, "b-i2"); + FIND_SELECTOR2(b_di, "b-di"); + FIND_SELECTOR2(b_xAxis, "b-xAxis"); + FIND_SELECTOR2(b_incr, "b-incr"); + FIND_SELECTOR(xStep); + FIND_SELECTOR(yStep); + FIND_SELECTOR(moveSpeed); + FIND_SELECTOR(canBeHere); // cantBeHere + FIND_SELECTOR(heading); + FIND_SELECTOR(mover); + FIND_SELECTOR(doit); + FIND_SELECTOR(isBlocked); + FIND_SELECTOR(looper); + FIND_SELECTOR(priority); + FIND_SELECTOR(modifiers); + FIND_SELECTOR(replay); + // setPri + // at + // next + // done + // width + FIND_SELECTOR(wordFail); + FIND_SELECTOR(syntaxFail); + // semanticFail + // pragmaFail + // said + FIND_SELECTOR(claimed); + // value + // save + // restore + // title + // button + // icon + // draw + FIND_SELECTOR2(delete_, "delete"); + FIND_SELECTOR(z); + // ----------------------------- + FIND_SELECTOR(size); + FIND_SELECTOR(moveDone); + FIND_SELECTOR(vol); + FIND_SELECTOR(pri); + FIND_SELECTOR(min); + FIND_SELECTOR(sec); + FIND_SELECTOR(frame); + FIND_SELECTOR(dataInc); + FIND_SELECTOR(palette); + FIND_SELECTOR(cantBeHere); + FIND_SELECTOR(nodePtr); + FIND_SELECTOR(flags); + FIND_SELECTOR(points); + FIND_SELECTOR(syncCue); + FIND_SELECTOR(syncTime); + FIND_SELECTOR(printLang); + FIND_SELECTOR(subtitleLang); + FIND_SELECTOR(parseLang); + FIND_SELECTOR(overlay); + FIND_SELECTOR(setCursor); + FIND_SELECTOR(topString); + FIND_SELECTOR(scaleSignal); + FIND_SELECTOR(scaleX); + FIND_SELECTOR(scaleY); + +#ifdef ENABLE_SCI32 + FIND_SELECTOR(data); + FIND_SELECTOR(picture); + FIND_SELECTOR(plane); + FIND_SELECTOR(top); + FIND_SELECTOR(left); +#endif +} + reg_t read_selector(SegManager *segMan, reg_t object, Selector selector_id) { ObjVarRef address; diff --git a/engines/sci/engine/vm.cpp b/engines/sci/engine/vm.cpp index 001f154974..859ebac0c5 100644 --- a/engines/sci/engine/vm.cpp +++ b/engines/sci/engine/vm.cpp @@ -1603,355 +1603,6 @@ void run_vm(EngineState *s, int restoring) { } } -#define INST_LOOKUP_CLASS(id) ((id == 0xffff)? NULL_REG : segMan->getClassAddress(id, SCRIPT_GET_LOCK, reg)) - -int script_instantiate_common(ResourceManager *resMan, SegManager *segMan, int script_nr, Resource **script, Resource **heap, int *was_new) { - *was_new = 1; - - *script = resMan->findResource(ResourceId(kResourceTypeScript, script_nr), 0); - if (getSciVersion() >= SCI_VERSION_1_1) - *heap = resMan->findResource(ResourceId(kResourceTypeHeap, script_nr), 0); - - if (!*script || (getSciVersion() >= SCI_VERSION_1_1 && !heap)) { - warning("Script 0x%x requested but not found", script_nr); - if (getSciVersion() >= SCI_VERSION_1_1) { - if (*heap) - warning("Inconsistency: heap resource WAS found"); - else if (*script) - warning("Inconsistency: script resource WAS found"); - } - return 0; - } - - SegmentId seg_id = segMan->getScriptSegment(script_nr); - Script *scr = segMan->getScriptIfLoaded(seg_id); - if (scr) { - if (!scr->isMarkedAsDeleted()) { - scr->incrementLockers(); - return seg_id; - } else { - scr->freeScript(); - } - } else { - scr = segMan->allocateScript(script_nr, &seg_id); - if (!scr) { // ALL YOUR SCRIPT BASE ARE BELONG TO US - error("Not enough heap space for script size 0x%x of script 0x%x (Should this happen?)", (*script)->size, script_nr); - return 0; - } - } - - scr->init(script_nr, resMan); - - reg_t reg; - reg.segment = seg_id; - reg.offset = 0; - - // Set heap position (beyond the size word) - scr->setLockers(1); - scr->setExportTableOffset(0); - scr->setSynonymsOffset(0); - scr->setSynonymsNr(0); - - *was_new = 0; - - return seg_id; -} - -int script_instantiate_sci0(ResourceManager *resMan, SegManager *segMan, int script_nr) { - int objtype; - unsigned int objlength; - int relocation = -1; - int magic_pos_adder; // Usually 0; 2 for older SCI versions - Resource *script; - int was_new; - bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY); - - const int seg_id = script_instantiate_common(resMan, segMan, script_nr, &script, NULL, &was_new); - - if (was_new) - return seg_id; - - Script *scr = segMan->getScript(seg_id); - - if (oldScriptHeader) { - // - int locals_nr = READ_LE_UINT16(script->data); - - // 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. - - scr->mcpyInOut(0, script->data, script->size); - magic_pos_adder = 2; // Step over the funny prefix - - if (locals_nr) - segMan->scriptInitialiseLocalsZero(seg_id, locals_nr); - - } else { - scr->mcpyInOut(0, script->data, script->size); - magic_pos_adder = 0; - } - - // Now do a first pass through the script objects to find the - // export table and local variable block - - reg_t reg; - reg.segment = seg_id; - reg.offset = magic_pos_adder; - - objlength = 0; - - do { - reg_t data_base; - reg_t addr; - reg.offset += objlength; // Step over the last checked object - objtype = scr->getHeap(reg.offset); - if (!objtype) - break; - - objlength = scr->getHeap(reg.offset + 2); - - // This happens in some demos (e.g. the EcoQuest 1 demo). Not sure what is the - // actual cause of it, but the scripts of these demos can't be loaded properly - // and we're stuck forever in this loop, as objlength never changes - if (!objlength) { - warning("script_instantiate_sci0: objlength is 0, unable to parse script"); - return 0; - } - - data_base = reg; - data_base.offset += 4; - - addr = data_base; - - switch (objtype) { - case SCI_OBJ_EXPORTS: { - scr->setExportTableOffset(data_base.offset); - } - break; - - case SCI_OBJ_SYNONYMS: - scr->setSynonymsOffset(addr.offset); // +4 is to step over the header - scr->setSynonymsNr((objlength) / 4); - break; - - case SCI_OBJ_LOCALVARS: - segMan->scriptInitialiseLocals(data_base); - break; - - case SCI_OBJ_CLASS: { - int classpos = addr.offset - SCRIPT_OBJECT_MAGIC_OFFSET; - int species; - species = scr->getHeap(addr.offset - SCRIPT_OBJECT_MAGIC_OFFSET + SCRIPT_SPECIES_OFFSET); - if (species < 0 || species >= (int)segMan->_classtable.size()) { - if (species == (int)segMan->_classtable.size()) { - // Happens in the LSL2 demo - warning("Applying workaround for an off-by-one invalid species access"); - segMan->_classtable.resize(segMan->_classtable.size() + 1); - } else { - warning("Invalid species %d(0x%x) not in interval " - "[0,%d) while instantiating script %d\n", - species, species, segMan->_classtable.size(), - script_nr); - return 0; - } - } - - segMan->_classtable[species].reg = addr; - segMan->_classtable[species].reg.offset = classpos; - // Set technical class position-- into the block allocated for it - } - break; - - default: - break; - } - } while (objtype != 0); - - // And now a second pass to adjust objects and class pointers, and the general pointers - - objlength = 0; - reg.offset = magic_pos_adder; // Reset counter - - do { - reg_t addr; - reg.offset += objlength; // Step over the last checked object - objtype = scr->getHeap(reg.offset); - if (!objtype) - break; - - objlength = scr->getHeap(reg.offset + 2); - - addr = reg; - addr.offset += 4; // Step over header - - switch (objtype) { - case SCI_OBJ_CODE: - scr->scriptAddCodeBlock(addr); - break; - case SCI_OBJ_OBJECT: - case SCI_OBJ_CLASS: { // object or class? - Object *obj = scr->scriptObjInit(addr); - - // Instantiate the superclass, if neccessary - obj->setSpeciesSelector(INST_LOOKUP_CLASS(obj->getSpeciesSelector().offset)); - - Object *baseObj = segMan->getObject(obj->getSpeciesSelector()); - - if (baseObj) { - obj->setVarCount(baseObj->getVarCount()); - // Copy base from species class, as we need its selector IDs - obj->_baseObj = baseObj->_baseObj; - - obj->setSuperClassSelector(INST_LOOKUP_CLASS(obj->getSuperClassSelector().offset)); - } else { - warning("Failed to locate base object for object at %04X:%04X; skipping", PRINT_REG(addr)); - - scr->scriptObjRemove(addr); - } - } // if object or class - break; - case SCI_OBJ_POINTERS: // A relocation table - relocation = addr.offset; - break; - - default: - break; - } - - } while (objtype != 0 && reg.offset < script->size - 2); - - if (relocation >= 0) - scr->scriptRelocate(make_reg(seg_id, relocation)); - - return reg.segment; // instantiation successful -} - -int script_instantiate_sci11(ResourceManager *resMan, SegManager *segMan, int script_nr) { - Resource *script, *heap; - int _heapStart; - reg_t reg; - int was_new; - - const int seg_id = script_instantiate_common(resMan, segMan, script_nr, &script, &heap, &was_new); - - if (was_new) - return seg_id; - - Script *scr = segMan->getScript(seg_id); - - _heapStart = script->size; - if (script->size & 2) - _heapStart ++; - - scr->mcpyInOut(0, script->data, script->size); - scr->mcpyInOut(_heapStart, heap->data, heap->size); - - if (READ_LE_UINT16(script->data + 6) > 0) - scr->setExportTableOffset(6); - - reg.segment = seg_id; - reg.offset = _heapStart + 4; - segMan->scriptInitialiseLocals(reg); - - segMan->scriptRelocateExportsSci11(seg_id); - segMan->scriptInitialiseObjectsSci11(seg_id); - - reg.offset = READ_LE_UINT16(heap->data); - scr->heapRelocate(reg); - - return seg_id; -} - -int script_instantiate(ResourceManager *resMan, SegManager *segMan, int script_nr) { - if (getSciVersion() >= SCI_VERSION_1_1) - return script_instantiate_sci11(resMan, segMan, script_nr); - else - return script_instantiate_sci0(resMan, segMan, script_nr); -} - -void script_uninstantiate_sci0(SegManager *segMan, int script_nr, SegmentId seg) { - bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY); - reg_t reg = make_reg(seg, oldScriptHeader ? 2 : 0); - int objtype, objlength; - Script *scr = segMan->getScript(seg); - - // Make a pass over the object in order uninstantiate all superclasses - objlength = 0; - - do { - reg.offset += objlength; // Step over the last checked object - - objtype = scr->getHeap(reg.offset); - if (!objtype) - break; - objlength = scr->getHeap(reg.offset + 2); // use SEG_UGET_HEAP ?? - - reg.offset += 4; // Step over header - - if ((objtype == SCI_OBJ_OBJECT) || (objtype == SCI_OBJ_CLASS)) { // object or class? - int superclass; - - reg.offset -= SCRIPT_OBJECT_MAGIC_OFFSET; - - superclass = scr->getHeap(reg.offset + SCRIPT_SUPERCLASS_OFFSET); // Get superclass... - - if (superclass >= 0) { - int superclass_script = segMan->_classtable[superclass].script; - - if (superclass_script == script_nr) { - if (scr->getLockers()) - scr->decrementLockers(); // Decrease lockers if this is us ourselves - } else - script_uninstantiate(segMan, superclass_script); - // Recurse to assure that the superclass lockers number gets decreased - } - - reg.offset += SCRIPT_OBJECT_MAGIC_OFFSET; - } // if object or class - - reg.offset -= 4; // Step back on header - - } while (objtype != 0); -} - -void script_uninstantiate(SegManager *segMan, int script_nr) { - SegmentId segment = segMan->getScriptSegment(script_nr); - Script *scr = segMan->getScriptIfLoaded(segment); - - if (!scr) { // Is it already loaded? - //warning("unloading script 0x%x requested although not loaded", script_nr); - // This is perfectly valid SCI behaviour - return; - } - - scr->decrementLockers(); // One less locker - - if (scr->getLockers() > 0) - return; - - // Free all classtable references to this script - for (uint i = 0; i < segMan->_classtable.size(); i++) - if (segMan->_classtable[i].reg.segment == segment) - segMan->_classtable[i].reg = NULL_REG; - - if (getSciVersion() < SCI_VERSION_1_1) - script_uninstantiate_sci0(segMan, script_nr, segment); - // FIXME: Add proper script uninstantiation for SCI 1.1 - - if (scr->getLockers()) - return; // if xxx.lockers > 0 - - // Otherwise unload it completely - // Explanation: I'm starting to believe that this work is done by SCI itself. - scr->markDeleted(); - - debugC(kDebugLevelScripts, "Unloaded script 0x%x.", script_nr); - - return; -} - static void _init_stack_base_with_selector(EngineState *s, Selector selector) { s->stack_base[0] = make_reg(0, (uint16)selector); s->stack_base[1] = NULL_REG; |