Beispiel #1
0
SciSpan<const byte> Script::findBlockSCI0(ScriptObjectTypes type, bool findLastBlock) const {
	SciSpan<const byte> foundBlock;

	bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY);

	SciSpan<const byte> buf = *_buf;
	if (oldScriptHeader)
		buf += 2;

	for (;;) {
		const int blockType = buf.getUint16LEAt(0);

		if (blockType == 0)
			break;

		// the size in the block header includes the size of the header itself
		const int blockSize = buf.getUint16LEAt(2);
		assert(blockSize > 0);

		if (blockType == type) {
			foundBlock = buf.subspan(0, blockSize, Common::String::format("%s, %s block", _buf->name().c_str(), sciObjectTypeNames[type]));

			if (!findLastBlock) {
				break;
			}
		}

		buf += blockSize;
	}

	return foundBlock;
}
Beispiel #2
0
void Script::initializeObjectsSci3(SegManager *segMan, SegmentId segmentId) {
	SciSpan<const byte> seeker = getSci3ObjectsPointer();

	while (seeker.getUint16SEAt(0) == SCRIPT_OBJECT_MAGIC_NUMBER) {
		Object *obj = scriptObjInit(make_reg32(segmentId, seeker - *_buf));
		obj->setSuperClassSelector(segMan->getClassAddress(obj->getSuperClassSelector().getOffset(), SCRIPT_GET_LOCK, 0));
		seeker += seeker.getUint16SEAt(2);
	}

	relocateSci3(segmentId);
}
Beispiel #3
0
void Script::initializeObjectsSci0(SegManager *segMan, SegmentId segmentId) {
	bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY);

	// We need to make two passes, as the objects in the script might be in the
	// wrong order (e.g. in the demo of Iceman) - refer to bug #3034713
	for (int pass = 1; pass <= 2; pass++) {
		SciSpan<const byte> seeker = _buf->subspan(oldScriptHeader ? 2 : 0);

		do {
			uint16 objType = seeker.getUint16SEAt(0);
			if (!objType)
				break;

			switch (objType) {
			case SCI_OBJ_OBJECT:
			case SCI_OBJ_CLASS:
				{
					reg_t addr = make_reg(segmentId, seeker - *_buf + 4 - SCRIPT_OBJECT_MAGIC_OFFSET);
					Object *obj;
					if (pass == 1) {
						obj = scriptObjInit(addr);
						obj->initSpecies(segMan, addr);
					} else {
						obj = getObject(addr.getOffset());
						if (!obj->initBaseObject(segMan, addr)) {
							if ((_nr == 202 || _nr == 764) && g_sci->getGameId() == GID_KQ5) {
								// WORKAROUND: Script 202 of KQ5 French and German
								// (perhaps Spanish too?) has an invalid object.
								// This is non-fatal. Refer to bugs #3035396 and
								// #3150767.
								// Same happens with script 764, it seems to
								// contain junk towards its end.
								_objects.erase(addr.toUint16() - SCRIPT_OBJECT_MAGIC_OFFSET);
							} else {
								error("Failed to locate base object for object at %04x:%04x in script %d", PRINT_REG(addr), _nr);
							}
						}
					}
				}
				break;

			default:
				break;
			}

			seeker += seeker.getUint16SEAt(2);
		} while ((uint32)(seeker - *_buf) < getScriptSize() - 2);
	}

	relocateSci0Sci21(segmentId);
}
Beispiel #4
0
int Script::relocateOffsetSci3(uint32 offset) const {
	int relocStart = _buf->getUint32LEAt(8);
	int relocCount = _buf->getUint16LEAt(18);
	SciSpan<const byte> seeker = _buf->subspan(relocStart);

	for (int i = 0; i < relocCount; ++i) {
		if (seeker.getUint32SEAt(0) == offset) {
			return _buf->getUint16SEAt(offset) + seeker.getUint32SEAt(4);
		}
		seeker += 10;
	}

	return -1;
}
Beispiel #5
0
void Script::initializeLocals(SegManager *segMan) {
	LocalVariables *locals = allocLocalsSegment(segMan);
	if (locals) {
		if (getSciVersion() > SCI_VERSION_0_EARLY) {
			const SciSpan<const byte> base = _buf->subspan(getLocalsOffset());

			for (uint16 i = 0; i < getLocalsCount(); i++)
				locals->_locals[i] = make_reg(0, base.getUint16SEAt(i * sizeof(uint16)));
		} else {
			// In SCI0 early, locals are set at run time, thus zero them all here
			for (uint16 i = 0; i < getLocalsCount(); i++)
				locals->_locals[i] = NULL_REG;
		}
	}
}
Beispiel #6
0
void Script::relocateSci3(const SegmentId segmentId) {
	SciSpan<const byte> relocEntry = _buf->subspan(_buf->getUint32SEAt(8));
	const uint relocCount = _buf->getUint16SEAt(18);

	for (uint i = 0; i < relocCount; ++i) {
		const uint location = relocEntry.getUint32SEAt(0);
		const uint offset = relocEntry.getUint32SEAt(4);

		if (!relocateLocal(segmentId, location, offset)) {
			const ObjMap::iterator end = _objects.end();
			for (ObjMap::iterator it = _objects.begin(); it != end; ++it) {
				if (it->_value.relocateSci3(segmentId, location, offset, _script.size())) {
					break;
				}
			}
		}

		relocEntry += 10;
	}
}
Beispiel #7
0
void Script::relocateSci0Sci21(const SegmentId segmentId) {
	const SciSpan<const uint16> relocEntries = getRelocationTableSci0Sci21();

	const uint32 heapOffset = getHeapOffset();

	for (uint i = 0; i < relocEntries.size(); ++i) {
		const uint pos = relocEntries.getUint16SEAt(i) + heapOffset;

		// In SCI0-SCI1, script local variables, objects and code are relocated.
		// We only relocate locals and objects here, and ignore relocation of
		// code blocks. In SCI1.1 and newer versions, only locals and objects
		// are relocated.
		if (!relocateLocal(segmentId, pos, getHeapOffset())) {
			// Not a local? It's probably an object or code block. If it's an
			// object, relocate it.
			const ObjMap::iterator end = _objects.end();
			for (ObjMap::iterator it = _objects.begin(); it != end; ++it)
				if (it->_value.relocateSci0Sci21(segmentId, pos, getHeapOffset()))
					break;
		}
	}
}
Beispiel #8
0
void HunkPalette::write(SciSpan<byte> &out, const Palette &palette) {
	const uint8 numPalettes = 1;
	const uint16 paletteOffset = kHunkPaletteHeaderSize + 2 * numPalettes;

	out[kNumPaletteEntriesOffset] = numPalettes;
	out[kHunkPaletteHeaderSize + 2] = paletteOffset;

	SciSpan<byte> entry = out.subspan(paletteOffset);
	entry[kEntryStartColorOffset] = 0;
	entry.setUint16SEAt(kEntryNumColorsOffset, ARRAYSIZE(palette.colors));
	entry[kEntryUsedOffset] = 1;
	entry[kEntrySharedUsedOffset] = 0;
	entry.setUint32SEAt(kEntryVersionOffset, 1);

	SciSpan<byte> paletteData = entry.subspan(kEntryHeaderSize);
	for (uint i = 0; i < ARRAYSIZE(palette.colors); ++i) {
		*paletteData++ = palette.colors[i].used;
		*paletteData++ = palette.colors[i].r;
		*paletteData++ = palette.colors[i].g;
		*paletteData++ = palette.colors[i].b;
	}
}
Beispiel #9
0
uint32 Script::getRelocationOffset(const uint32 offset) const {
	if (getSciVersion() == SCI_VERSION_3) {
		SciSpan<const byte> relocStart = _buf->subspan(_buf->getUint32SEAt(8));
		const uint relocCount = _buf->getUint16SEAt(18);

		for (uint i = 0; i < relocCount; ++i) {
			if (offset == relocStart.getUint32SEAt(0)) {
				return relocStart.getUint32SEAt(4);
			}
			relocStart += 10;
		}
	} else {
		const SciSpan<const uint16> relocTable = getRelocationTableSci0Sci21();
		for (uint i = 0; i < relocTable.size(); ++i) {
			if (relocTable.getUint16SEAt(i) == offset) {
				return getHeapOffset();
			}
		}
	}

	return kNoRelocation;
}
Beispiel #10
0
void Script::initializeClasses(SegManager *segMan) {
	SciSpan<const byte> seeker;
	uint16 mult = 0;

	if (getSciVersion() <= SCI_VERSION_1_LATE) {
		seeker = _script;
		mult = 1;

		// SCI0 early has an extra two bytes of header
		if (getSciVersion() == SCI_VERSION_0_EARLY) {
			seeker += 2;
		}
	} else if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1_LATE) {
		seeker = _heap.subspan(4 + _heap.getUint16SEAt(2) * 2);
		mult = 2;
#ifdef ENABLE_SCI32
	} else if (getSciVersion() == SCI_VERSION_3) {
		seeker = getSci3ObjectsPointer();
		mult = 1;
#endif
	}

	if (!seeker)
		return;

	uint16 marker;
	bool isClass = false;
	uint32 classpos;
	int16 species = 0;

	for (;;) {
		// In SCI0-SCI1, this is the segment type. In SCI11, it's a marker (0x1234)
		marker = seeker.getUint16SEAt(0);
		classpos = seeker - *_buf;

		if (getSciVersion() <= SCI_VERSION_1_LATE && !marker)
			break;

		if (getSciVersion() >= SCI_VERSION_1_1 && marker != SCRIPT_OBJECT_MAGIC_NUMBER)
			break;

		if (getSciVersion() <= SCI_VERSION_1_LATE) {
			isClass = (marker == SCI_OBJ_CLASS);
			if (isClass)
				species = seeker.getUint16SEAt(12);
			classpos += 12;
		} else if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1_LATE) {
			isClass = (seeker.getUint16SEAt(14) & kInfoFlagClass);	// -info- selector
			species = seeker.getUint16SEAt(10);
		} else if (getSciVersion() == SCI_VERSION_3) {
			isClass = (seeker.getUint16SEAt(10) & kInfoFlagClass);
			species = seeker.getUint16SEAt(4);
		}

		if (isClass) {
			// WORKAROUNDs for off-by-one script errors
			if (species == (int)segMan->classTableSize()) {
				if (g_sci->getGameId() == GID_LSL2 && g_sci->isDemo())
					segMan->resizeClassTable(species + 1);
				else if (g_sci->getGameId() == GID_LSL3 && !g_sci->isDemo() && _nr == 500)
					segMan->resizeClassTable(species + 1);
				else if (g_sci->getGameId() == GID_SQ3 && !g_sci->isDemo() && _nr == 93)
					segMan->resizeClassTable(species + 1);
				else if (g_sci->getGameId() == GID_SQ3 && !g_sci->isDemo() && _nr == 99)
					segMan->resizeClassTable(species + 1);
			}

			if (species < 0 || species >= (int)segMan->classTableSize())
				error("Invalid species %d(0x%x) unknown max %d(0x%x) while instantiating script %d",
						  species, species, segMan->classTableSize(), segMan->classTableSize(), _nr);

			segMan->setClassOffset(species, make_reg32(segMan->getScriptSegment(_nr), classpos));
		}

		seeker += seeker.getUint16SEAt(2) * mult;
	}
}
Beispiel #11
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 #12
0
void GfxPicture::drawVectorData(const SciSpan<const byte> &data) {
	byte pic_op;
	byte pic_color = _screen->getColorDefaultVectorData();
	byte pic_priority = 255, pic_control = 255;
	int16 x = 0, y = 0, oldx, oldy;
	byte EGApalettes[PIC_EGAPALETTE_TOTALSIZE] = {0};
	byte *EGApalette = &EGApalettes[_EGApaletteNo * PIC_EGAPALETTE_SIZE];
	byte EGApriority[PIC_EGAPRIORITY_SIZE] = {0};
	bool isEGA = false;
	uint curPos = 0;
	uint16 size;
	byte pixel;
	int i;
	Palette palette;
	int16 pattern_Code = 0, pattern_Texture = 0;
	bool icemanDrawFix = false;
	bool ignoreBrokenPriority = false;

	memset(&palette, 0, sizeof(palette));

	if (_EGApaletteNo >= PIC_EGAPALETTE_COUNT)
		_EGApaletteNo = 0;

	if (_resMan->getViewType() == kViewEga) {
		isEGA = true;
		// setup default mapping tables
		for (i = 0; i < PIC_EGAPALETTE_TOTALSIZE; i += PIC_EGAPALETTE_SIZE)
			memcpy(&EGApalettes[i], &vector_defaultEGApalette, sizeof(vector_defaultEGApalette));
		memcpy(&EGApriority, &vector_defaultEGApriority, sizeof(vector_defaultEGApriority));

		if (g_sci->getGameId() == GID_ICEMAN) {
			// WORKAROUND: we remove certain visual&priority lines in underwater
			// rooms of iceman, when not dithering the picture. Normally those
			// lines aren't shown, because they share the same color as the
			// dithered fill color combination. When not dithering, those lines
			// would appear and get distracting.
			if ((_screen->isUnditheringEnabled()) && ((_resourceId >= 53 && _resourceId <= 58) || (_resourceId == 61)))
				icemanDrawFix = true;
		}
	}

	// Drawing
	while (curPos < data.size()) {
#ifdef DEBUG_PICTURE_DRAW
		debug("Picture op: %X (%s) at %d", data[curPos], picOpcodeNames[data[curPos] - 0xF0], curPos);
		_screen->copyToScreen();
		g_system->updateScreen();
		g_system->delayMillis(400);
#endif
		switch (pic_op = data[curPos++]) {
		case PIC_OP_SET_COLOR:
			pic_color = data[curPos++];
			if (isEGA) {
				pic_color = EGApalette[pic_color];
				pic_color ^= pic_color << 4;
			}
			break;
		case PIC_OP_DISABLE_VISUAL:
			pic_color = 0xFF;
			break;

		case PIC_OP_SET_PRIORITY:
			pic_priority = data[curPos++] & 0x0F;
			if (isEGA)
				pic_priority = EGApriority[pic_priority];
			if (ignoreBrokenPriority)
				pic_priority = 255;
			break;
		case PIC_OP_DISABLE_PRIORITY:
			pic_priority = 255;
			break;

		case PIC_OP_SET_CONTROL:
			pic_control = data[curPos++] & 0x0F;
			break;
		case PIC_OP_DISABLE_CONTROL:
			pic_control = 255;
			break;

		case PIC_OP_SHORT_LINES: // short line
			vectorGetAbsCoords(data, curPos, x, y);
			while (vectorIsNonOpcode(data[curPos])) {
				oldx = x; oldy = y;
				vectorGetRelCoords(data, curPos, x, y);
				Common::Point startPoint(oldx, oldy);
				Common::Point endPoint(x, y);
				_ports->offsetLine(startPoint, endPoint);
				_screen->drawLine(startPoint, endPoint, pic_color, pic_priority, pic_control);
			}
			break;
		case PIC_OP_MEDIUM_LINES: // medium line
			vectorGetAbsCoords(data, curPos, x, y);
			if (icemanDrawFix) {
				// WORKAROUND: remove certain lines in iceman - see above
				if ((pic_color == 1) && (pic_priority == 14)) {
					if ((y < 100) || (!(y & 1))) {
						pic_color = 255;
						pic_priority = 255;
					}
				}
			}
			while (vectorIsNonOpcode(data[curPos])) {
				oldx = x; oldy = y;
				vectorGetRelCoordsMed(data, curPos, x, y);
				Common::Point startPoint(oldx, oldy);
				Common::Point endPoint(x, y);
				_ports->offsetLine(startPoint, endPoint);
				_screen->drawLine(startPoint, endPoint, pic_color, pic_priority, pic_control);
			}
			break;
		case PIC_OP_LONG_LINES: // long line
			vectorGetAbsCoords(data, curPos, x, y);
			while (vectorIsNonOpcode(data[curPos])) {
				oldx = x; oldy = y;
				vectorGetAbsCoords(data, curPos, x, y);
				Common::Point startPoint(oldx, oldy);
				Common::Point endPoint(x, y);
				_ports->offsetLine(startPoint, endPoint);
				_screen->drawLine(startPoint, endPoint, pic_color, pic_priority, pic_control);
			}
			break;

		case PIC_OP_FILL: //fill
			while (vectorIsNonOpcode(data[curPos])) {
				vectorGetAbsCoords(data, curPos, x, y);
				vectorFloodFill(x, y, pic_color, pic_priority, pic_control);
			}
			break;

		// Pattern opcodes are handled in sierra sci1.1+ as actual NOPs and
		// normally they definitely should not occur inside picture data for
		// such games.
		case PIC_OP_SET_PATTERN:
			if (_resourceType >= SCI_PICTURE_TYPE_SCI11) {
				if (g_sci->getGameId() == GID_SQ4) {
					// WORKAROUND: For SQ4 / for some pictures handle this like
					// a terminator. This picture includes garbage data, first a
					// set pattern w/o parameter and then short pattern. I guess
					// that garbage is a left over from the sq4-floppy (sci1) to
					// sq4-cd (sci1.1) conversion.
					switch (_resourceId) {
					case 35:
					case 381:
					case 376:
					//case 390:	// in the blacklisted NRS patch 1.2 (bug #3615060)
						return;
					default:
						break;
					}
				}
				error("pic-operation set pattern inside sci1.1+ vector data");
			}
			pattern_Code = data[curPos++];
			break;
		case PIC_OP_SHORT_PATTERNS:
			if (_resourceType >= SCI_PICTURE_TYPE_SCI11)
				error("pic-operation short pattern inside sci1.1+ vector data");
			vectorGetPatternTexture(data, curPos, pattern_Code, pattern_Texture);
			vectorGetAbsCoords(data, curPos, x, y);
			vectorPattern(x, y, pic_color, pic_priority, pic_control, pattern_Code, pattern_Texture);
			while (vectorIsNonOpcode(data[curPos])) {
				vectorGetPatternTexture(data, curPos, pattern_Code, pattern_Texture);
				vectorGetRelCoords(data, curPos, x, y);
				vectorPattern(x, y, pic_color, pic_priority, pic_control, pattern_Code, pattern_Texture);
			}
			break;
		case PIC_OP_MEDIUM_PATTERNS:
			if (_resourceType >= SCI_PICTURE_TYPE_SCI11)
				error("pic-operation medium pattern inside sci1.1+ vector data");
			vectorGetPatternTexture(data, curPos, pattern_Code, pattern_Texture);
			vectorGetAbsCoords(data, curPos, x, y);
			vectorPattern(x, y, pic_color, pic_priority, pic_control, pattern_Code, pattern_Texture);
			while (vectorIsNonOpcode(data[curPos])) {
				vectorGetPatternTexture(data, curPos, pattern_Code, pattern_Texture);
				vectorGetRelCoordsMed(data, curPos, x, y);
				vectorPattern(x, y, pic_color, pic_priority, pic_control, pattern_Code, pattern_Texture);
			}
			break;
		case PIC_OP_ABSOLUTE_PATTERN:
			if (_resourceType >= SCI_PICTURE_TYPE_SCI11)
				error("pic-operation absolute pattern inside sci1.1+ vector data");
			while (vectorIsNonOpcode(data[curPos])) {
				vectorGetPatternTexture(data, curPos, pattern_Code, pattern_Texture);
				vectorGetAbsCoords(data, curPos, x, y);
				vectorPattern(x, y, pic_color, pic_priority, pic_control, pattern_Code, pattern_Texture);
			}
			break;

		case PIC_OP_OPX: // Extended functions
			if (isEGA) {
#ifdef DEBUG_PICTURE_DRAW
				debug("* Picture ex op: %X (%s) at %d", data[curPos], picExOpcodeNamesEGA[data[curPos]], curPos);
#endif
				switch (pic_op = data[curPos++]) {
				case PIC_OPX_EGA_SET_PALETTE_ENTRIES:
					while (vectorIsNonOpcode(data[curPos])) {
						pixel = data[curPos++];
						if (pixel >= PIC_EGAPALETTE_TOTALSIZE) {
							error("picture trying to write to invalid EGA-palette");
						}
						EGApalettes[pixel] = data[curPos++];
					}
					break;
				case PIC_OPX_EGA_SET_PALETTE:
					pixel = data[curPos++];
					if (pixel >= PIC_EGAPALETTE_COUNT) {
						error("picture trying to write to invalid palette %d", (int)pixel);
					}
					pixel *= PIC_EGAPALETTE_SIZE;
					for (i = 0; i < PIC_EGAPALETTE_SIZE; i++) {
						EGApalettes[pixel + i] = data[curPos++];
					}
					break;
				case PIC_OPX_EGA_MONO0:
					curPos += 41;
					break;
				case PIC_OPX_EGA_MONO1:
				case PIC_OPX_EGA_MONO3:
					curPos++;
					break;
				case PIC_OPX_EGA_MONO2:
				case PIC_OPX_EGA_MONO4:
					break;
				case PIC_OPX_EGA_EMBEDDED_VIEW:
					vectorGetAbsCoordsNoMirror(data, curPos, x, y);
					size = data.getUint16LEAt(curPos); curPos += 2;
					// hardcoded in SSCI, 16 for SCI1early excluding Space Quest 4, 0 for anything else
					//  fixes sq4 pictures 546+547 (Vohaul's head and Roger Jr trapped). Bug #5250
					//  fixes sq4 picture 631 (SQ1 view from cockpit). Bug 5249
					if ((getSciVersion() <= SCI_VERSION_1_EARLY) && (g_sci->getGameId() != GID_SQ4)) {
						_priority = 16;
					} else {
						_priority = 0;
					}
					drawCelData(data, curPos, curPos + 8, 0, x, y, 0, 0, true);
					curPos += size;
					break;
				case PIC_OPX_EGA_SET_PRIORITY_TABLE:
					_ports->priorityBandsInit(data.subspan(curPos, 14));
					curPos += 14;
					break;
				default:
					error("Unsupported sci1 extended pic-operation %X", pic_op);
				}
			} else {
#ifdef DEBUG_PICTURE_DRAW
				debug("* Picture ex op: %X (%s) at %d", data[curPos], picExOpcodeNamesVGA[data[curPos]], curPos);
#endif
				switch (pic_op = data[curPos++]) {
				case PIC_OPX_VGA_SET_PALETTE_ENTRIES:
					while (vectorIsNonOpcode(data[curPos])) {
						curPos++; // skip commands
					}
					break;
				case PIC_OPX_VGA_SET_PALETTE:
					if (_resMan->getViewType() == kViewAmiga || _resMan->getViewType() == kViewAmiga64) {
						if ((data[curPos] == 0x00) && (data[curPos + 1] == 0x01) && ((data[curPos + 32] & 0xF0) != 0xF0)) {
							// Left-Over VGA palette, we simply ignore it
							curPos += 256 + 4 + 1024;
						} else {
							// Setting half of the Amiga palette
							_palette->modifyAmigaPalette(data.subspan(curPos));
							curPos += 32;
						}
					} else {
						curPos += 256 + 4; // Skip over mapping and timestamp
						for (i = 0; i < 256; i++) {
							palette.colors[i].used = data[curPos++];
							palette.colors[i].r = data[curPos++]; palette.colors[i].g = data[curPos++]; palette.colors[i].b = data[curPos++];
						}
						_palette->set(&palette, true);
					}
					break;
				case PIC_OPX_VGA_EMBEDDED_VIEW: // draw cel
					vectorGetAbsCoordsNoMirror(data, curPos, x, y);
					size = data.getUint16LEAt(curPos); curPos += 2;
					if (getSciVersion() <= SCI_VERSION_1_EARLY) {
						// During SCI1Early sierra always used 0 as priority for cels inside picture resources
						//  fixes Space Quest 4 orange ship lifting off (bug #6446)
						_priority = 0;
					} else {
						_priority = pic_priority; // set global priority so the cel gets drawn using current priority as well
					}
					drawCelData(data, curPos, curPos + 8, 0, x, y, 0, 0, false);
					curPos += size;
					break;
				case PIC_OPX_VGA_PRIORITY_TABLE_EQDIST:
					_ports->priorityBandsInit(-1, data.getUint16LEAt(curPos), data.getUint16LEAt(curPos + 2));
					curPos += 4;
					break;
				case PIC_OPX_VGA_PRIORITY_TABLE_EXPLICIT:
					_ports->priorityBandsInit(data.subspan(curPos, 14));
					curPos += 14;
					break;
				default:
					error("Unsupported sci1 extended pic-operation %X", pic_op);
				}
			}
			break;
		case PIC_OP_TERMINATE:
			_priority = pic_priority;
			// Dithering EGA pictures
			if (isEGA) {
				_screen->dither(_addToFlag);
				switch (g_sci->getGameId()) {
				case GID_SQ3:
					switch (_resourceId) {
					case 154: // SQ3: intro, ship gets sucked in
						_screen->ditherForceDitheredColor(0xD0);
						break;
					default:
						break;
					}
					break;
				default:
					break;
				}
			}
			return;
		default:
			error("Unsupported pic-operation %X", pic_op);
		}
		if ((_EGAdrawingVisualize) && (isEGA)) {
			_screen->copyToScreen();
			g_system->updateScreen();
			g_system->delayMillis(10);
		}
	}
	error("picture vector data without terminator");
}
Beispiel #13
0
void Script::identifyOffsets() {
	offsetLookupArrayEntry arrayEntry;
	SciSpan<const byte> scriptDataPtr;
	SciSpan<const byte> stringStartPtr;
	SciSpan<const byte> stringDataPtr;
	byte stringDataByte  = 0;
	uint16 typeObject_id = 0;
	uint16 typeString_id = 0;
	uint16 typeSaid_id   = 0;

	uint16 blockType = 0;
	uint16 blockSize = 0;

	_offsetLookupArray.clear();
	_offsetLookupObjectCount = 0;
	_offsetLookupStringCount = 0;
	_offsetLookupSaidCount = 0;
	_codeOffset = 0;

	if (getSciVersion() < SCI_VERSION_1_1) {
		// SCI0 + SCI1
		scriptDataPtr = *_buf;

		// Go through all blocks
		if (getSciVersion() == SCI_VERSION_0_EARLY) {
			if (scriptDataPtr.size() < 2)
				error("Script::identifyOffsets(): unexpected end of script %d", _nr);
			scriptDataPtr += 2;
		}

		for (;;) {
			if (scriptDataPtr.size() < 2)
				error("Script::identifyOffsets(): unexpected end of script %d", _nr);

			blockType = scriptDataPtr.getUint16LEAt(0);
			scriptDataPtr += 2;
			if (blockType == 0) // end of blocks detected
				break;

			if (scriptDataPtr.size() < 2)
				error("Script::identifyOffsets(): unexpected end of script %d", _nr);

			blockSize = scriptDataPtr.getUint16LEAt(0);
			if (blockSize < 4)
				error("Script::identifyOffsets(): invalid block size in script %d", _nr);
			blockSize     -= 4; // block size includes block-type UINT16 and block-size UINT16
			scriptDataPtr += 2;

			if (scriptDataPtr.size() < blockSize)
				error("Script::identifyOffsets(): invalid block size in script %d", _nr);

			switch (blockType) {
			case SCI_OBJ_OBJECT:
			case SCI_OBJ_CLASS:
				typeObject_id++;
				arrayEntry.type       = SCI_SCR_OFFSET_TYPE_OBJECT;
				arrayEntry.id         = typeObject_id;
				arrayEntry.offset     = scriptDataPtr - *_buf + 8; // Calculate offset inside script data (VM uses +8)
				arrayEntry.stringSize = 0;
				_offsetLookupArray.push_back(arrayEntry);
				_offsetLookupObjectCount++;
				break;

			case SCI_OBJ_STRINGS:
				// string block detected, we now grab all NUL terminated strings out of this block
				stringDataPtr = scriptDataPtr.subspan(0, blockSize);

				arrayEntry.type       = SCI_SCR_OFFSET_TYPE_STRING;

				for (;;) {
					if (stringDataPtr.size() < 1) // no more bytes left
						break;

					stringStartPtr = stringDataPtr;

					if (stringDataPtr.size() == 1) {
						// only 1 byte left and that byte is a [00], in that case we also exit
						stringDataByte = *stringStartPtr;
						if (stringDataByte == 0x00)
							break;
					}

					// now look for terminating [NUL]
					for (;;) {
						stringDataByte = *stringDataPtr;
						stringDataPtr++;
						if (!stringDataByte) // NUL found, exit this loop
							break;
						if (stringDataPtr.size() < 1) {
							// no more bytes left
							warning("Script::identifyOffsets(): string without terminating NUL in script %d", _nr);
							break;
						}
					}

					if (stringDataByte)
						break;

					typeString_id++;
					arrayEntry.id         = typeString_id;
					arrayEntry.offset     = stringStartPtr - *_buf; // Calculate offset inside script data
					arrayEntry.stringSize = stringDataPtr - stringStartPtr;
					_offsetLookupArray.push_back(arrayEntry);
					_offsetLookupStringCount++;
				}
				break;

			case SCI_OBJ_SAID:
				// said block detected, we now try to find every single said "string" inside this block
				// said strings are terminated with a 0xFF, the string itself may contain words (2 bytes), where
				//  the second byte of a word may also be a 0xFF.
				stringDataPtr  = scriptDataPtr.subspan(0, blockSize);

				arrayEntry.type       = SCI_SCR_OFFSET_TYPE_SAID;

				for (;;) {
					if (stringDataPtr.size() < 1) // no more bytes left
						break;

					stringStartPtr = stringDataPtr;
					if (stringDataPtr.size() == 1) {
						// only 1 byte left and that byte is a [00], in that case we also exit
						// happens in some scripts, for example Conquests of Camelot, script 997
						// may have been a bug in the compiler or just an intentional filler byte
						stringDataByte = *stringStartPtr;
						if (stringDataByte == 0x00)
							break;
					}

					// now look for terminating 0xFF
					for (;;) {
						stringDataByte = *stringDataPtr;
						stringDataPtr++;
						if (stringDataByte == 0xFF) // Terminator found, exit this loop
							break;
						if (stringDataPtr.size() < 1) // no more bytes left
							error("Script::identifyOffsets(): said-string without terminator in script %d", _nr);
						if (stringDataByte < 0xF0) {
							// Part of a word, skip second byte
							stringDataPtr++;
							if (stringDataPtr.size() < 1) // no more bytes left
								error("Script::identifyOffsets(): said-string without terminator in script %d", _nr);
						}
					}

					typeSaid_id++;
					arrayEntry.id         = typeSaid_id;
					arrayEntry.offset     = stringStartPtr - *_buf; // Calculate offset inside script data
					arrayEntry.stringSize = 0;
					_offsetLookupArray.push_back(arrayEntry);
					_offsetLookupSaidCount++;
				}
				break;

			default:
				break;
			}

			scriptDataPtr  += blockSize;
		}

	} else if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1_LATE) {
		// Strings in SCI1.1 up to SCI2 come after the object instances
		scriptDataPtr = _heap;

		enum {
			kExportSize = sizeof(uint16),
			kPropertySize = sizeof(uint16),
			kNumMethodsSize = sizeof(uint16),
			kPropDictEntrySize = 2,
			kMethDictEntrySize = 4
		};

		SciSpan<const byte> hunkPtr = _buf->subspan(kSci11ExportTableOffset + _numExports * kExportSize);

		if (scriptDataPtr.size() < 4)
			error("Script::identifyOffsets(): unexpected end of script in script %d", _nr);

		uint16 endOfStringOffset = scriptDataPtr.getUint16SEAt(0);
		uint16 objectStartOffset = scriptDataPtr.getUint16SEAt(2) * 2 + 4;

		if (scriptDataPtr.size() < objectStartOffset)
			error("Script::identifyOffsets(): object start is beyond heap size in script %d", _nr);
		if (scriptDataPtr.size() < endOfStringOffset)
			error("Script::identifyOffsets(): end of string is beyond heap size in script %d", _nr);

		SciSpan<const byte> endOfStringPtr = scriptDataPtr.subspan(endOfStringOffset);

		scriptDataPtr  += objectStartOffset;

		// go through all objects
		for (;;) {
			if (scriptDataPtr.size() < 2)
				error("Script::identifyOffsets(): unexpected end of script %d", _nr);

			blockType = scriptDataPtr.getUint16SEAt(0);
			scriptDataPtr  += 2;
			if (blockType != SCRIPT_OBJECT_MAGIC_NUMBER)
				break;

			// Object found, add offset of object
			typeObject_id++;
			arrayEntry.type       = SCI_SCR_OFFSET_TYPE_OBJECT;
			arrayEntry.id         = typeObject_id;
			arrayEntry.offset     = scriptDataPtr - *_buf - 2; // the VM uses a pointer to the Magic-Number
			arrayEntry.stringSize = 0;
			_offsetLookupArray.push_back(arrayEntry);
			_offsetLookupObjectCount++;

			if (scriptDataPtr.size() < 2)
				error("Script::identifyOffsets(): unexpected end of script in script %d", _nr);

			const uint16 numProperties = scriptDataPtr.getUint16SEAt(0);
			blockSize = numProperties * kPropertySize;
			if (blockSize < 4)
				error("Script::identifyOffsets(): invalid block size in script %d", _nr);
			scriptDataPtr  += 2;

			const uint16 scriptNum = scriptDataPtr.getUint16SEAt(6);

			if (scriptNum != 0xFFFF) {
				hunkPtr += numProperties * kPropDictEntrySize;
			}

			const uint16 numMethods = hunkPtr.getUint16SEAt(0);
			hunkPtr += kNumMethodsSize + numMethods * kMethDictEntrySize;

			blockSize -= 4; // blocksize contains UINT16 type and UINT16 size
			if (scriptDataPtr.size() < blockSize)
				error("Script::identifyOffsets(): invalid block size in script %d", _nr);

			scriptDataPtr  += blockSize;
		}

		_codeOffset = hunkPtr - *_buf;

		// now scriptDataPtr points to right at the start of the strings
		if (scriptDataPtr > endOfStringPtr)
			error("Script::identifyOffsets(): string block / end-of-string block mismatch in script %d", _nr);

		stringDataPtr = scriptDataPtr.subspan(0, endOfStringPtr - scriptDataPtr);

		arrayEntry.type       = SCI_SCR_OFFSET_TYPE_STRING;
		for (;;) {
			if (stringDataPtr.size() < 1) // no more bytes left
				break;

			stringStartPtr = stringDataPtr;
			// now look for terminating [NUL]
			for (;;) {
				stringDataByte = *stringDataPtr;
				stringDataPtr++;
				if (!stringDataByte) // NUL found, exit this loop
					break;
				if (stringDataPtr.size() < 1) {
				    // no more bytes left
					warning("Script::identifyOffsets(): string without terminating NUL in script %d", _nr);
					break;
				}
			}

			if (stringDataByte)
				break;

			typeString_id++;
			arrayEntry.id         = typeString_id;
			arrayEntry.offset     = stringStartPtr - *_buf; // Calculate offset inside script data
			arrayEntry.stringSize = stringDataPtr - stringStartPtr;
			_offsetLookupArray.push_back(arrayEntry);
			_offsetLookupStringCount++;
		}

#ifdef ENABLE_SCI32
	} else if (getSciVersion() == SCI_VERSION_3) {
		// SCI3
		uint32 sci3StringOffset = 0;
		uint32 sci3RelocationOffset = 0;
		uint32 sci3BoundaryOffset = 0;

		if (_buf->size() < 22)
			error("Script::identifyOffsets(): script %d smaller than expected SCI3-header", _nr);

		_codeOffset = _buf->getUint32LEAt(0);
		sci3StringOffset = _buf->getUint32LEAt(4);
		sci3RelocationOffset = _buf->getUint32LEAt(8);

		if (sci3RelocationOffset > _buf->size())
			error("Script::identifyOffsets(): relocation offset is beyond end of script %d", _nr);

		// First we get all the objects
		scriptDataPtr = getSci3ObjectsPointer();
		for (;;) {
			if (scriptDataPtr.size() < 2)
				error("Script::identifyOffsets(): unexpected end of script %d", _nr);

			blockType = scriptDataPtr.getUint16SEAt(0);
			scriptDataPtr += 2;
			if (blockType != SCRIPT_OBJECT_MAGIC_NUMBER)
				break;

			// Object found, add offset of object
			typeObject_id++;
			arrayEntry.type       = SCI_SCR_OFFSET_TYPE_OBJECT;
			arrayEntry.id         = typeObject_id;
			arrayEntry.offset     = scriptDataPtr - *_buf - 2; // the VM uses a pointer to the Magic-Number
			arrayEntry.stringSize = 0;
			_offsetLookupArray.push_back(arrayEntry);
			_offsetLookupObjectCount++;

			if (scriptDataPtr.size() < 2)
				error("Script::identifyOffsets(): unexpected end of script in script %d", _nr);

			blockSize = scriptDataPtr.getUint16SEAt(0);
			if (blockSize < 4)
				error("Script::identifyOffsets(): invalid block size in script %d", _nr);
			scriptDataPtr  += 2;
			blockSize -= 4; // blocksize contains UINT16 type and UINT16 size
			if (scriptDataPtr.size() < blockSize)
				error("Script::identifyOffsets(): invalid block size in script %d", _nr);

			scriptDataPtr  += blockSize;
		}

		// And now we get all the strings
		if (sci3StringOffset > 0) {
			// string offset set, we expect strings
			if (sci3StringOffset > _buf->size())
				error("Script::identifyOffsets(): string offset is beyond end of script %d", _nr);

			if (sci3RelocationOffset < sci3StringOffset)
				error("Script::identifyOffsets(): string offset points beyond relocation offset in script %d", _nr);

			stringDataPtr  = _buf->subspan(sci3StringOffset, sci3RelocationOffset - sci3StringOffset);

			arrayEntry.type       = SCI_SCR_OFFSET_TYPE_STRING;

			for (;;) {
				if (stringDataPtr.size() < 1) // no more bytes left
					break;

				stringStartPtr = stringDataPtr;

				if (stringDataPtr.size() == 1) {
					// only 1 byte left and that byte is a [00], in that case we also exit
					stringDataByte = *stringStartPtr;
					if (stringDataByte == 0x00)
						break;
				}

				// now look for terminating [NUL]
				for (;;) {
					stringDataByte = *stringDataPtr;
					stringDataPtr++;
					if (!stringDataByte) // NUL found, exit this loop
						break;
					if (stringDataPtr.size() < 1) {
						// no more bytes left
						warning("Script::identifyOffsets(): string without terminating NUL in script %d", _nr);
						break;
					}
				}

				if (stringDataByte)
					break;

				typeString_id++;
				arrayEntry.id         = typeString_id;
				arrayEntry.offset     = stringStartPtr - *_buf; // Calculate offset inside script data
				arrayEntry.stringSize = stringDataPtr - stringStartPtr;
				_offsetLookupArray.push_back(arrayEntry);
				_offsetLookupStringCount++;

				// SCI3 seems to have aligned all string on DWORD boundaries
				sci3BoundaryOffset = stringDataPtr - *_buf; // Calculate current offset inside script data
				sci3BoundaryOffset = sci3BoundaryOffset & 3; // Check boundary offset
				if (sci3BoundaryOffset) {
					// lower 2 bits are set? Then we have to adjust the offset
					sci3BoundaryOffset = 4 - sci3BoundaryOffset;
					if (stringDataPtr.size() < sci3BoundaryOffset)
						error("Script::identifyOffsets(): SCI3 string boundary adjustment goes beyond end of string block in script %d", _nr);
					stringDataPtr += sci3BoundaryOffset;
				}
			}
		}
#endif
	}
}
Beispiel #14
0
void Script::initializeObjectsSci11(SegManager *segMan, SegmentId segmentId) {
	SciSpan<const byte> seeker = _heap.subspan(4 + _heap.getUint16SEAt(2) * 2);
	Common::Array<reg_t> mismatchedVarCountObjects;

	while (seeker.getUint16SEAt(0) == SCRIPT_OBJECT_MAGIC_NUMBER) {
		reg_t reg = make_reg(segmentId, seeker - *_buf);
		Object *obj = scriptObjInit(reg);

		// Copy base from species class, as we need its selector IDs
		obj->setSuperClassSelector(
			segMan->getClassAddress(obj->getSuperClassSelector().getOffset(), SCRIPT_GET_LOCK, 0));

		// -propDict- is used by Obj::isMemberOf to determine if an object
		// is an instance of a class. For classes, we therefore relocate
		// -propDict- to the script's segment. For instances, we copy
		// -propDict- from its class.
		// Example test case - room 381 of sq4cd - if isMemberOf() doesn't work,
		// talk-clicks on the robot will act like clicking on ego.
		if (obj->isClass()) {
			reg_t propDict = obj->getPropDictSelector();
			propDict.setSegment(segmentId);
			obj->setPropDictSelector(propDict);
		} else {
			reg_t classObject = obj->getSuperClassSelector();
			const Object *classObj = segMan->getObject(classObject);
			obj->setPropDictSelector(classObj->getPropDictSelector());

			// At least some versions of Island of Dr Brain have a bMessager
			// instance in script 0 with a var count greater than that of its
			// class; warn about this here to see if it shows up in any other
			// games
			if (obj->getVarCount() != classObj->getVarCount()) {
				// Warnings have to be deferred until after relocation for the
				// object name to be resolvable
				mismatchedVarCountObjects.push_back(reg);
			}
		}

		// Set the -classScript- selector to the script number.
		// FIXME: As this selector is filled in at run-time, it is likely
		// that it is supposed to hold a pointer. The Obj::isKindOf method
		// uses this selector together with -propDict- to compare classes.
		// For the purpose of Obj::isKindOf, using the script number appears
		// to be sufficient.
		obj->setClassScriptSelector(make_reg(0, _nr));

		seeker += seeker.getUint16SEAt(2) * 2;
	}

	relocateSci0Sci21(segmentId);

	for (uint i = 0; i < mismatchedVarCountObjects.size(); ++i) {
		const reg_t pos = mismatchedVarCountObjects[i];
		const Object *obj = segMan->getObject(pos);
		reg_t classObject = obj->getSuperClassSelector();
		const Object *classObj = segMan->getObject(classObject);

		warning("Object %04x:%04x (%s) from %s declares %d variables, "
				"but its class declares %d variables",
				PRINT_REG(pos),
				segMan->getObjectName(pos),
				_buf->name().c_str(),
				obj->getVarCount(),
				classObj->getVarCount());
	}
}
Beispiel #15
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 #16
0
void GfxPalette::createFromData(const SciSpan<const byte> &data, Palette *paletteOut) const {
	int palFormat = 0;
	uint palOffset = 0;
	uint palColorStart = 0;
	uint palColorCount = 0;
	uint colorNo = 0;

	memset(paletteOut, 0, sizeof(Palette));

	// Setup 1:1 mapping
	for (colorNo = 0; colorNo < 256; colorNo++) {
		paletteOut->mapping[colorNo] = colorNo;
	}

	if (data.size() < 37) {
		// This happens when loading palette of picture 0 in sq5 - the resource is broken and doesn't contain a full
		//  palette
		debugC(kDebugLevelResMan, "GfxPalette::createFromData() - not enough bytes in resource (%u), expected palette header", data.size());
		return;
	}

	// palette formats in here are not really version exclusive, we can not use sci-version to differentiate between them
	//  they were just called that way, because they started appearing in sci1.1 for example
	if ((data[0] == 0 && data[1] == 1) || (data[0] == 0 && data[1] == 0 && data.getUint16SEAt(29) == 0)) {
		// SCI0/SCI1 palette
		palFormat = SCI_PAL_FORMAT_VARIABLE; // CONSTANT;
		palOffset = 260;
		palColorStart = 0; palColorCount = 256;
		//memcpy(&paletteOut->mapping, data, 256);
	} else {
		// SCI1.1 palette
		palFormat = data[32];
		palOffset = 37;
		palColorStart = data[25];
		palColorCount = data.getUint16SEAt(29);
	}

	switch (palFormat) {
		case SCI_PAL_FORMAT_CONSTANT:
			// Check, if enough bytes left
			if (data.size() < palOffset + (3 * palColorCount)) {
				warning("GfxPalette::createFromData() - not enough bytes in resource, expected palette colors");
				return;
			}

			for (colorNo = palColorStart; colorNo < palColorStart + palColorCount; colorNo++) {
				paletteOut->colors[colorNo].used = 1;
				paletteOut->colors[colorNo].r = data[palOffset++];
				paletteOut->colors[colorNo].g = data[palOffset++];
				paletteOut->colors[colorNo].b = data[palOffset++];
			}
			break;
		case SCI_PAL_FORMAT_VARIABLE:
			if (data.size() < palOffset + (4 * palColorCount)) {
				warning("GfxPalette::createFromData() - not enough bytes in resource, expected palette colors");
				return;
			}

			for (colorNo = palColorStart; colorNo < palColorStart + palColorCount; colorNo++) {
				paletteOut->colors[colorNo].used = data[palOffset++];
				paletteOut->colors[colorNo].r = data[palOffset++];
				paletteOut->colors[colorNo].g = data[palOffset++];
				paletteOut->colors[colorNo].b = data[palOffset++];
			}
			break;
	}
}
Beispiel #17
0
void GfxPicture::drawCelData(const SciSpan<const byte> &inbuffer, int headerPos, int rlePos, int literalPos, int16 drawX, int16 drawY, int16 pictureX, int16 pictureY, bool isEGA) {
	const SciSpan<const byte> headerPtr = inbuffer.subspan(headerPos);
	const SciSpan<const byte> rlePtr = inbuffer.subspan(rlePos);
	// displaceX, displaceY fields are ignored, and may contain garbage
	// (e.g. pic 261 in Dr. Brain 1 Spanish - bug #3614914)
	//int16 displaceX, displaceY;
	byte priority = _priority;
	byte clearColor;
	bool compression = true;
	byte curByte;
	int16 y, lastY, x, leftX, rightX;
	int pixelCount;
	uint16 width, height;

	// if the picture is not an overlay and we are also not in EGA mode, use priority 0
	if (!isEGA && !_addToFlag)
		priority = 0;

	// Width/height here are always LE, even in Mac versions
	width = headerPtr.getUint16LEAt(0);
	height = headerPtr.getUint16LEAt(2);
	//displaceX = (signed char)headerPtr[4];
	//displaceY = (unsigned char)headerPtr[5];
	if (_resourceType == SCI_PICTURE_TYPE_SCI11)
		// SCI1.1 uses hardcoded clearcolor for pictures, even if cel header specifies otherwise
		clearColor = _screen->getColorWhite();
	else
		clearColor = headerPtr[6];

	//if (displaceX || displaceY)
	//	error("unsupported embedded cel-data in picture");

	// We will unpack cel-data into a temporary buffer and then plot it to screen
	//  That needs to be done cause a mirrored picture may be requested
	pixelCount = width * height;
	Common::SpanOwner<SciSpan<byte> > celBitmap;
	celBitmap->allocate(pixelCount, _resource->name());

	if (g_sci->getPlatform() == Common::kPlatformMacintosh && getSciVersion() >= SCI_VERSION_2) {
		// See GfxView::unpackCel() for why this black/white swap is done
		// This picture swap is only needed in SCI32, not SCI1.1
		if (clearColor == 0)
			clearColor = 0xff;
		else if (clearColor == 0xff)
			clearColor = 0;
	}

	if (compression) {
		unpackCelData(inbuffer, *celBitmap, clearColor, rlePos, literalPos, _resMan->getViewType(), width, false);
	} else
		// No compression (some SCI32 pictures)
		memcpy(celBitmap->getUnsafeDataAt(0, pixelCount), rlePtr.getUnsafeDataAt(0, pixelCount), pixelCount);

	Common::Rect displayArea = _coordAdjuster->pictureGetDisplayArea();

	// Horizontal clipping
	uint16 skipCelBitmapPixels = 0;
	int16 displayWidth = width;
	if (pictureX) {
		// horizontal scroll position for picture active, we need to adjust drawX accordingly
		drawX -= pictureX;
		if (drawX < 0) {
			skipCelBitmapPixels = -drawX;
			displayWidth -= skipCelBitmapPixels;
			drawX = 0;
		}
	}

	// Vertical clipping
	uint16 skipCelBitmapLines = 0;
	int16 displayHeight = height;
	if (pictureY) {
		// vertical scroll position for picture active, we need to adjust drawY accordingly
		// TODO: Finish this
		/*drawY -= pictureY;
		if (drawY < 0) {
			skipCelBitmapLines = -drawY;
			displayHeight -= skipCelBitmapLines;
			drawY = 0;
		}*/
	}

	if (displayWidth > 0 && displayHeight > 0) {
		y = displayArea.top + drawY;
		lastY = MIN<int16>(height + y, displayArea.bottom);
		leftX = displayArea.left + drawX;
		rightX = MIN<int16>(displayWidth + leftX, displayArea.right);

		uint16 sourcePixelSkipPerRow = 0;
		if (width > rightX - leftX)
			sourcePixelSkipPerRow = width - (rightX - leftX);

		// Change clearcolor to white, if we dont add to an existing picture. That way we will paint everything on screen
		// but white and that won't matter because the screen is supposed to be already white. It seems that most (if not all)
		// SCI1.1 games use color 0 as transparency and SCI1 games use color 255 as transparency. Sierra SCI seems to paint
		// the whole data to screen and wont skip over transparent pixels. So this will actually make it work like Sierra.
		if (!_addToFlag)
			clearColor = _screen->getColorWhite();

		byte drawMask = priority > 15 ? GFX_SCREEN_MASK_VISUAL : GFX_SCREEN_MASK_VISUAL | GFX_SCREEN_MASK_PRIORITY;

		SciSpan<const byte> ptr = *celBitmap;
		ptr += skipCelBitmapPixels;
		ptr += skipCelBitmapLines * width;

		if ((!isEGA) || (priority < 16)) {
			// VGA + EGA, EGA only checks priority, when given priority is below 16
			if (!_mirroredFlag) {
				// Draw bitmap to screen
				x = leftX;
				while (y < lastY) {
					curByte = *ptr++;
					if ((curByte != clearColor) && (priority >= _screen->getPriority(x, y)))
						_screen->putPixel(x, y, drawMask, curByte, priority, 0);

					x++;

					if (x >= rightX) {
						ptr += sourcePixelSkipPerRow;
						x = leftX;
						y++;
					}
				}
			} else {
				// Draw bitmap to screen (mirrored)
				x = rightX - 1;
				while (y < lastY) {
					curByte = *ptr++;
					if ((curByte != clearColor) && (priority >= _screen->getPriority(x, y)))
						_screen->putPixel(x, y, drawMask, curByte, priority, 0);

					if (x == leftX) {
						ptr += sourcePixelSkipPerRow;
						x = rightX;
						y++;
					}

					x--;
				}
			}
		} else {
			// EGA, when priority is above 15
			//  we don't check priority and also won't set priority at all
			//  fixes picture 48 of kq5 (island overview). Bug #5182
			if (!_mirroredFlag) {
				// EGA+priority>15: Draw bitmap to screen
				x = leftX;
				while (y < lastY) {
					curByte = *ptr++;
					if (curByte != clearColor)
						_screen->putPixel(x, y, GFX_SCREEN_MASK_VISUAL, curByte, 0, 0);

					x++;

					if (x >= rightX) {
						ptr += sourcePixelSkipPerRow;
						x = leftX;
						y++;
					}
				}
			} else {
				// EGA+priority>15: Draw bitmap to screen (mirrored)
				x = rightX - 1;
				while (y < lastY) {
					curByte = *ptr++;
					if (curByte != clearColor)
						_screen->putPixel(x, y, GFX_SCREEN_MASK_VISUAL, curByte, 0, 0);

					if (x == leftX) {
						ptr += sourcePixelSkipPerRow;
						x = rightX;
						y++;
					}

					x--;
				}
			}
		}
	}
}