void Script::load(ResourceManager *resMan) { Resource *script = resMan->findResource(ResourceId(kResourceTypeScript, _nr), 0); assert(script != 0); uint extraLocalsWorkaround = 0; if (g_sci->getGameId() == GID_FANMADE && _nr == 1 && script->size == 11140) { // WORKAROUND: Script 1 in Ocean Battle doesn't have enough locals to // fit the string showing how many shots are left (a nasty script bug, // corrupting heap memory). We add 10 more locals so that it has enough // space to use as the target for its kFormat operation. Fixes bug // #3059871. extraLocalsWorkaround = 10; } _bufSize += extraLocalsWorkaround * 2; _buf = (byte *)malloc(_bufSize); assert(_buf); assert(_bufSize >= script->size); memcpy(_buf, script->data, script->size); // Check scripts for matching signatures and patch those, if found matchSignatureAndPatch(_nr, _buf, script->size); if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1) { Resource *heap = resMan->findResource(ResourceId(kResourceTypeHeap, _nr), 0); assert(heap != 0); _heapStart = _buf + _scriptSize; assert(_bufSize - _scriptSize <= heap->size); memcpy(_heapStart, heap->data, heap->size); } _exportTable = 0; _numExports = 0; _synonyms = 0; _numSynonyms = 0; if (getSciVersion() <= SCI_VERSION_1_LATE) { _exportTable = (const uint16 *)findBlockSCI0(SCI_OBJ_EXPORTS); if (_exportTable) { _numExports = READ_SCI11ENDIAN_UINT16(_exportTable + 1); _exportTable += 3; // skip header plus 2 bytes (_exportTable is a uint16 pointer) } _synonyms = findBlockSCI0(SCI_OBJ_SYNONYMS); if (_synonyms) { _numSynonyms = READ_SCI11ENDIAN_UINT16(_synonyms + 2) / 4; _synonyms += 4; // skip header } const byte* localsBlock = findBlockSCI0(SCI_OBJ_LOCALVARS); if (localsBlock) { _localsOffset = localsBlock - _buf + 4; _localsCount = (READ_LE_UINT16(_buf + _localsOffset - 2) - 4) >> 1; // half block size } } else if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1) {
void Script::load(ResourceManager *resMan) { Resource *script = resMan->findResource(ResourceId(kResourceTypeScript, _nr), 0); assert(script != 0); _buf = (byte *)malloc(_bufSize); assert(_buf); assert(_bufSize >= script->size); memcpy(_buf, script->data, script->size); // Check scripts for matching signatures and patch those, if found matchSignatureAndPatch(_nr, _buf, script->size); if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1) { Resource *heap = resMan->findResource(ResourceId(kResourceTypeHeap, _nr), 0); assert(heap != 0); _heapStart = _buf + _scriptSize; assert(_bufSize - _scriptSize <= heap->size); memcpy(_heapStart, heap->data, heap->size); } _exportTable = 0; _numExports = 0; _synonyms = 0; _numSynonyms = 0; if (getSciVersion() <= SCI_VERSION_1_LATE) { _exportTable = (const uint16 *)findBlockSCI0(SCI_OBJ_EXPORTS); if (_exportTable) { _numExports = READ_SCI11ENDIAN_UINT16(_exportTable + 1); _exportTable += 3; // skip header plus 2 bytes (_exportTable is a uint16 pointer) } _synonyms = findBlockSCI0(SCI_OBJ_SYNONYMS); if (_synonyms) { _numSynonyms = READ_SCI11ENDIAN_UINT16(_synonyms + 2) / 4; _synonyms += 4; // skip header } const byte* localsBlock = findBlockSCI0(SCI_OBJ_LOCALVARS); if (localsBlock) { _localsOffset = localsBlock - _buf + 4; _localsCount = (READ_LE_UINT16(_buf + _localsOffset - 2) - 4) >> 1; // half block size } } else if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1) {
const SciSpan<const uint16> Script::getRelocationTableSci0Sci21() const { SciSpan<const byte> relocationBlock; uint16 numEntries; uint16 dataOffset; if (getSciVersion() < SCI_VERSION_1_1) { relocationBlock = findBlockSCI0(SCI_OBJ_POINTERS); if (!relocationBlock) { return SciSpan<const uint16>(); } if (relocationBlock != findBlockSCI0(SCI_OBJ_POINTERS, true)) { warning("Script %d has multiple relocation tables", _nr); } numEntries = relocationBlock.getUint16SEAt(4); if (!numEntries) { return SciSpan<const uint16>(); } dataOffset = 6; // Starting somewhere around SQ1, and continuing through the rest of // SCI1, the relocation table in scripts started including an extra // null entry at the beginning of the table, without a corresponding // increase in the entry count. While this change is consistent in // most of the SCI1mid+ games (all scripts in LSL1, Jones CD, // EQ floppy, SQ1, LSL5, and Ms Astro Chicken have the null entry), // a few games include scripts without the null entry (Castle of Dr // Brain 947 & 997, PQ3 997, KQ5 CD 975 & 997). Since 0 is never a // valid relocation offset, we just skip it if we see it const uint16 firstEntry = relocationBlock.getUint16SEAt(6); if (firstEntry == 0) { dataOffset += 2; } } else if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1_LATE) { relocationBlock = _heap.subspan(_heap.getUint16SEAt(0)); if (!relocationBlock) { return SciSpan<const uint16>(); } numEntries = relocationBlock.getUint16SEAt(0); if (!numEntries) { return SciSpan<const uint16>(); } dataOffset = 2; } else { error("Invalid engine version called Script::getRelocationTableSci0Sci21 on script %d", _nr); } // This check should work correctly even with SCI1.1+ because the relocation // table is always at the very end of the heap in these games if (dataOffset + numEntries * sizeof(uint16) != relocationBlock.size()) { warning("Script %d unexpected relocation table size %u", _nr, relocationBlock.size()); } return relocationBlock.subspan<const uint16>(dataOffset, numEntries * sizeof(uint16)); }
void Script::load(int script_nr, ResourceManager *resMan, ScriptPatcher *scriptPatcher) { freeScript(); Resource *script = resMan->findResource(ResourceId(kResourceTypeScript, script_nr), false); if (!script) error("Script %d not found", script_nr); _nr = script_nr; uint32 scriptSize = script->size(); uint32 bufSize = scriptSize; if (getSciVersion() == SCI_VERSION_0_EARLY) { bufSize += script->getUint16LEAt(0) * 2; } else if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1_LATE) { // In SCI1.1 - SCI2.1, the heap was in a separate space from the script. We append // it to the end of the script, and adjust addressing accordingly. // However, since we address the heap with a 16-bit pointer, the // combined size of the stack and the heap must be 64KB. So far this has // worked for SCI11, SCI2 and SCI21 games. SCI3 games use a different // script format, and they can exceed the 64KB boundary using relocation. Resource *heap = resMan->findResource(ResourceId(kResourceTypeHeap, script_nr), false); bufSize += heap->size(); // Ensure that the start of the heap resource can be word-aligned. if (script->size() & 2) { ++bufSize; ++scriptSize; } // As mentioned above, the script and the heap together should not exceed 64KB if (script->size() + heap->size() > 65535) error("Script and heap %d sizes combined exceed 64K. This means a fundamental " "design bug was made regarding SCI1.1 and newer games.\n" "Please report this error to the ScummVM team", script_nr); } else if (getSciVersion() == SCI_VERSION_3 && script->size() > 0x3FFFF) { error("Script %d size exceeds 256K (it is %u bytes).\n" "Please report this error to the ScummVM team", script_nr, script->size()); } uint extraLocalsWorkaround = 0; if (g_sci->getGameId() == GID_FANMADE && _nr == 1 && script->size() == 11140) { // WORKAROUND: Script 1 in Ocean Battle doesn't have enough locals to // fit the string showing how many shots are left (a nasty script bug, // corrupting heap memory). We add 10 more locals so that it has enough // space to use as the target for its kFormat operation. Fixes bug // #3059871. extraLocalsWorkaround = 10; } bufSize += extraLocalsWorkaround * 2; SciSpan<byte> outBuffer = _buf->allocate(bufSize, script->name() + " buffer"); script->copyDataTo(outBuffer); // The word-aligned script size is used here because other parts of the code // currently rely on finding the start of the heap by reading the script // size _script = _buf->subspan(0, scriptSize, script->name()); if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1_LATE) { Resource *heap = resMan->findResource(ResourceId(kResourceTypeHeap, _nr), false); assert(heap); SciSpan<byte> outHeap = outBuffer.subspan(scriptSize, heap->size(), heap->name(), 0); heap->copyDataTo(outHeap); _heap = outHeap; } // Check scripts (+ possibly SCI 1.1 heap) for matching signatures and patch those, if found scriptPatcher->processScript(_nr, outBuffer); if (getSciVersion() <= SCI_VERSION_1_LATE) { // Some buggy game scripts contain two export tables (e.g. script 912 // in Camelot and script 306 in KQ4); in these scripts, the first table // is broken, so we ignore it and use the last one instead // Fixes bugs #3039785 and #3037595. SciSpan<const uint16> exportTable = findBlockSCI0(SCI_OBJ_EXPORTS, true).subspan<const uint16>(0); if (exportTable) { // The export table is after the block header (4 bytes / 2 uint16s) // and the number of exports (2 bytes / 1 uint16). // The exports span does not need to be explicitly sized since the // maximum size was already determined by findBlockSCI0 _exports = exportTable.subspan(3); _numExports = exportTable.getUint16SEAt(2); } SciSpan<const byte> synonymTable = findBlockSCI0(SCI_OBJ_SYNONYMS); if (synonymTable) { // the synonyms table is after the block header (4 bytes), // and each synonym entry is 4 bytes _synonyms = synonymTable.subspan(4); _numSynonyms = _synonyms.size() / 4; } SciSpan<const byte> localsTable = findBlockSCI0(SCI_OBJ_LOCALVARS); if (localsTable) { // skip header (4 bytes) _localsOffset = localsTable - *_buf + 4; _localsCount = localsTable.size() / 2 - 2; } } else if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1_LATE) { _numExports = _buf->getUint16SEAt(kSci11NumExportsOffset); if (_numExports) { _exports = _buf->subspan<const uint16>(kSci11ExportTableOffset, _numExports * sizeof(uint16)); } _localsOffset = getHeapOffset() + 4; _localsCount = _buf->getUint16SEAt(_localsOffset - 2); } else if (getSciVersion() == SCI_VERSION_3) { _localsCount = _buf->getUint16LEAt(12); _numExports = _buf->getUint16LEAt(20); if (_numExports) { _exports = _buf->subspan<const uint16>(22, _numExports * sizeof(uint16)); } // SCI3 local variables always start dword-aligned if (_numExports % 2) _localsOffset = 22 + _numExports * sizeof(uint16); else _localsOffset = 24 + _numExports * sizeof(uint16); } // WORKAROUND: Increase locals, if needed (check above) _localsCount += extraLocalsWorkaround; if (getSciVersion() == SCI_VERSION_0_EARLY) { // SCI0 early // Old script block. There won't be a localvar block in this case. // Instead, the script starts with a 16 bit int specifying the // number of locals we need; these are then allocated and zeroed. _localsCount = _buf->getUint16LEAt(0); _localsOffset = -_localsCount * 2; // Make sure it's invalid } else { // SCI0 late and newer // Does the script actually have locals? If not, set the locals offset to 0 if (!_localsCount) _localsOffset = 0; if (_localsOffset + _localsCount * 2 + 1 >= (int)_buf->size()) { error("Locals extend beyond end of script %d: offset %04x, count %u vs size %u", _nr, _localsOffset, _localsCount, _buf->size()); } } // find all strings of this script identifyOffsets(); }
void Script::load(int script_nr, ResourceManager *resMan, ScriptPatcher *scriptPatcher) { freeScript(); Resource *script = resMan->findResource(ResourceId(kResourceTypeScript, script_nr), 0); if (!script) error("Script %d not found", script_nr); _nr = script_nr; _bufSize = _scriptSize = script->size; if (getSciVersion() == SCI_VERSION_0_EARLY) { _bufSize += READ_LE_UINT16(script->data) * 2; } else if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1) { // In SCI1.1 - SCI2.1, the heap was in a separate space from the script. We append // it to the end of the script, and adjust addressing accordingly. // However, since we address the heap with a 16-bit pointer, the // combined size of the stack and the heap must be 64KB. So far this has // worked for SCI11, SCI2 and SCI21 games. SCI3 games use a different // script format, and theoretically they can exceed the 64KB boundary // using relocation. Resource *heap = resMan->findResource(ResourceId(kResourceTypeHeap, script_nr), 0); _bufSize += heap->size; _heapSize = heap->size; // Ensure that the start of the heap resource can be word-aligned. if (script->size & 2) { _bufSize++; _scriptSize++; } // As mentioned above, the script and the heap together should not exceed 64KB if (script->size + heap->size > 65535) error("Script and heap sizes combined exceed 64K. This means a fundamental " "design bug was made regarding SCI1.1 and newer games.\n" "Please report this error to the ScummVM team"); } else if (getSciVersion() == SCI_VERSION_3) { // Check for scripts over 64KB. These won't work with the current 16-bit address // scheme. We need an overlaying mechanism, or a mechanism to split script parts // in different segments to handle these. For now, simply stop when such a script // is found. // // Known large SCI 3 scripts are: // Lighthouse: 9, 220, 270, 351, 360, 490, 760, 765, 800 // LSL7: 240, 511, 550 // Phantasmagoria 2: none (hooray!) // RAMA: 70 // // TODO: Remove this once such a mechanism is in place if (script->size > 65535) error("TODO: SCI script %d is over 64KB - it's %d bytes long. This can't " "be handled at the moment, thus stopping", script_nr, script->size); } uint extraLocalsWorkaround = 0; if (g_sci->getGameId() == GID_FANMADE && _nr == 1 && script->size == 11140) { // WORKAROUND: Script 1 in Ocean Battle doesn't have enough locals to // fit the string showing how many shots are left (a nasty script bug, // corrupting heap memory). We add 10 more locals so that it has enough // space to use as the target for its kFormat operation. Fixes bug // #3059871. extraLocalsWorkaround = 10; } _bufSize += extraLocalsWorkaround * 2; _buf = (byte *)malloc(_bufSize); assert(_buf); assert(_bufSize >= script->size); memcpy(_buf, script->data, script->size); if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1) { Resource *heap = resMan->findResource(ResourceId(kResourceTypeHeap, _nr), 0); assert(heap != 0); _heapStart = _buf + _scriptSize; assert(_bufSize - _scriptSize >= heap->size); memcpy(_heapStart, heap->data, heap->size); } // Check scripts (+ possibly SCI 1.1 heap) for matching signatures and patch those, if found scriptPatcher->processScript(_nr, _buf, _bufSize); if (getSciVersion() <= SCI_VERSION_1_LATE) { _exportTable = (const uint16 *)findBlockSCI0(SCI_OBJ_EXPORTS); if (_exportTable) { _numExports = READ_SCI11ENDIAN_UINT16(_exportTable + 1); _exportTable += 3; // skip header plus 2 bytes (_exportTable is a uint16 pointer) } _synonyms = findBlockSCI0(SCI_OBJ_SYNONYMS); if (_synonyms) { _numSynonyms = READ_SCI11ENDIAN_UINT16(_synonyms + 2) / 4; _synonyms += 4; // skip header } const byte* localsBlock = findBlockSCI0(SCI_OBJ_LOCALVARS); if (localsBlock) { _localsOffset = localsBlock - _buf + 4; _localsCount = (READ_LE_UINT16(_buf + _localsOffset - 2) - 4) >> 1; // half block size } } else if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1) {