Beispiel #1
0
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) {
Beispiel #2
0
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) {
Beispiel #3
0
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));
}
Beispiel #4
0
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();
}
Beispiel #5
0
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) {