https://github.com/scummvm/scummvm
Raw File
Tip revision: 5dbefa955166e4b22207d117104181d044f5590a authored by Lothar Serra Mari on 16 July 2022, 20:03:12 UTC
DISTS: Generated Code::Blocks and MSVC project files
Tip revision: 5dbefa9
room.cpp
/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */


#include "common/system.h"
#include "scumm/actor.h"
#include "scumm/boxes.h"
#ifdef ENABLE_HE
#include "scumm/he/intern_he.h"
#endif
#include "scumm/object.h"
#include "scumm/resource.h"
#include "scumm/scumm_v3.h"
#include "scumm/sound.h"
#include "scumm/util.h"

namespace Scumm {

/**
 * Start a 'scene' by loading the specified room with the given main actor.
 * The actor is placed next to the object indicated by objectNr.
 */
void ScummEngine::startScene(int room, Actor *a, int objectNr) {
	int i, where;

	debugC(DEBUG_GENERAL, "Loading room %d", room);

	stopTalk();

	fadeOut(_switchRoomEffect2);
	_newEffect = _switchRoomEffect;

	ScriptSlot *ss = &vm.slot[_currentScript];

	if (_currentScript != 0xFF) {
		if (ss->where == WIO_ROOM || ss->where == WIO_FLOBJECT) {
			if (ss->cutsceneOverride && _game.version >= 5)
				error("Object %d stopped with active cutscene/override in exit", ss->number);

			nukeArrays(_currentScript);
			_currentScript = 0xFF;
		} else if (ss->where == WIO_LOCAL) {
			if (ss->cutsceneOverride && _game.version >= 5)
				error("Script %d stopped with active cutscene/override in exit", ss->number);

			nukeArrays(_currentScript);
			_currentScript = 0xFF;
		}
	}

	if (VAR_NEW_ROOM != 0xFF)
		VAR(VAR_NEW_ROOM) = room;

	runExitScript();

	killScriptsAndResources();
	if (_game.version >= 4 && _game.heversion <= 62)
		stopCycle(0);

	if (_game.id == GID_SAMNMAX) {
		// WORKAROUND bug #1132 SAM: Overlapping music at Bigfoot convention
		// Added sound queue processing between execution of exit
		// script and entry script. In the case of this bug, the
		// entry script required that the iMuse state be fully up
		// to date, including last-moment changes from the previous
		// exit script.
		_sound->processSound();
	}

	clearDrawQueues();

	// For HE80+ games
	for (i = 0; i < _numRoomVariables; i++)
		_roomVars[i] = 0;
	nukeArrays(0xFF);

	// I don't know if this also belongs into v0, so I limit it to v1/2.
	// I do suspect that v0 should have it, since the other use cases in
	// o_loadRoomWithEgo/o2_loadRoomWithEgo and o_cutscene/o2_cutscene
	// are also the same.
	if (_game.version >= 1 && _game.version <= 2)
		resetSentence();

	for (i = 1; i < _numActors; i++) {
		_actors[i]->hideActor();
	}

	if (_game.version >= 7) {
		// Set the shadow palette(s) to all black. This fixes
		// bug #1196, and actually makes some sense (after all,
		// shadows tend to be rather black, don't they? ;-)
		memset(_shadowPalette, 0, NUM_SHADOW_PALETTE * 256);
	} else {
		for (i = 0; i < 256; i++) {
			_roomPalette[i] = i;
			if (_shadowPalette)
				_shadowPalette[i] = i;
		}
		if (_game.features & GF_SMALL_HEADER)
			setDirtyColors(0, 255);
	}

	VAR(VAR_ROOM) = room;
	_fullRedraw = true;

	_res->increaseResourceCounters();

	_currentRoom = room;
	VAR(VAR_ROOM) = room;

	if (room >= 0x80 && _game.version < 7 && _game.heversion <= 71)
		_roomResource = _resourceMapper[room & 0x7F];
	else
		_roomResource = room;

	if (VAR_ROOM_RESOURCE != 0xFF)
		VAR(VAR_ROOM_RESOURCE) = _roomResource;

	if (room != 0)
		ensureResourceLoaded(rtRoom, room);

	clearRoomObjects();

	if (_currentRoom == 0) {
		_ENCD_offs = _EXCD_offs = 0;
		_numObjectsInRoom = 0;
		return;
	}

	setupRoomSubBlocks();
	resetRoomSubBlocks();

	initBGBuffers(_roomHeight);

	resetRoomObjects();

	if (VAR_ROOM_WIDTH != 0xFF && VAR_ROOM_HEIGHT != 0xFF) {
		VAR(VAR_ROOM_WIDTH) = _roomWidth;
		VAR(VAR_ROOM_HEIGHT) = _roomHeight;
	}

	if (VAR_CAMERA_MIN_X != 0xFF)
		VAR(VAR_CAMERA_MIN_X) = _screenWidth / 2;
	if (VAR_CAMERA_MAX_X != 0xFF)
		VAR(VAR_CAMERA_MAX_X) = _roomWidth - (_screenWidth / 2);

	if (_game.version >= 7) {
		VAR(VAR_CAMERA_MIN_Y) = _screenHeight / 2;
		VAR(VAR_CAMERA_MAX_Y) = _roomHeight - (_screenHeight / 2);
		setCameraAt(_screenWidth / 2, _screenHeight / 2);
	} else {
		camera._mode = kNormalCameraMode;
		if (_game.version > 2)
			camera._cur.x = camera._dest.x = _screenWidth / 2;
		camera._cur.y = camera._dest.y = _screenHeight / 2;
	}

	if (_roomResource == 0)
		return;

	memset(gfxUsageBits, 0, sizeof(gfxUsageBits));

	if (_game.version >= 5 && a) {
		where = whereIsObject(objectNr);
		if (where != WIO_ROOM && where != WIO_FLOBJECT)
			error("startScene: Object %d is not in room %d", objectNr,
					_currentRoom);
		int x, y, dir;
		getObjectXYPos(objectNr, x, y, dir);
		a->putActor(x, y, _currentRoom);
		a->setDirection(dir + 180);
		a->stopActorMoving();
		if (_game.id == GID_SAMNMAX) {
			camera._cur.x = camera._dest.x = a->getPos().x;
			setCameraAt(a->getPos().x, a->getPos().y);
		}
	}

	showActors();

	_egoPositioned = false;

#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
	towns_resetPalCycleFields();
#endif

	runEntryScript();
	if (_game.version >= 1 && _game.version <= 2) {
		runScript(5, 0, 0, nullptr);
	} else if (_game.version >= 5 && _game.version <= 6) {
		if (a && !_egoPositioned) {
			int x, y;
			getObjectXYPos(objectNr, x, y);
			a->putActor(x, y, _currentRoom);
			a->_moving = 0;
		}
	} else if (_game.version >= 7) {
		if (camera._follows) {
			a = derefActor(camera._follows, "startScene: follows");
			setCameraAt(a->getPos().x, a->getPos().y);
		}
	}

	_doEffect = true;

	// Hint the backend about the virtual keyboard during copy protection screens
	if (_game.id == GID_MONKEY2) {
		bool hasCopyProtectionScreen = true;

		// The Macintosh version skips the copy protection screen with
		// a boot param, unless you ask it not to.
		if (_game.platform == Common::kPlatformMacintosh && _bootParam == -7873)
			hasCopyProtectionScreen = false;

		// The unofficial talkie never shows any copy protection screen.
		if (strcmp(_game.variant, "SE Talkie") == 0)
			hasCopyProtectionScreen = false;

		if (hasCopyProtectionScreen) {
			if (_system->getFeatureState(OSystem::kFeatureVirtualKeyboard)) {
				if (room != 108)
					_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
			} else if (room == 108)
				_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, true);
		}
	} else if (_game.id == GID_MONKEY_EGA) {	// this is my estimation that the room code is 90 (untested)
		if (_system->getFeatureState(OSystem::kFeatureVirtualKeyboard)) {
			if (room != 90)
				_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
		} else if (room == 90)
			_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, true);
	}

}

/**
 * Init some static room data after a room has been loaded.
 * E.g. the room dimension, the offset to the graphics data, the room scripts,
 * the offset to the room palette and other things which won't be changed
 * late on.
 * So it is possible to call this after loading a savegame.
 */
void ScummEngine::setupRoomSubBlocks() {
	int i;
	const byte *ptr;
	byte *roomptr, *searchptr, *roomResPtr = nullptr;
	const RoomHeader *rmhd;

	_ENCD_offs = 0;
	_EXCD_offs = 0;
	_EPAL_offs = 0;
	_CLUT_offs = 0;
	_PALS_offs = 0;

	// Determine the room and room script base address
	roomResPtr = roomptr = getResourceAddress(rtRoom, _roomResource);
	if (_game.version == 8)
		roomResPtr = getResourceAddress(rtRoomScripts, _roomResource);
	if (!roomptr || !roomResPtr)
		error("Room %d: data not found (" __FILE__  ":%d)", _roomResource, __LINE__);

	//
	// Determine the room dimensions (width/height)
	//
	rmhd = (const RoomHeader *)findResourceData(MKTAG('R','M','H','D'), roomptr);

	if (_game.version == 8) {
		_roomWidth = READ_LE_UINT32(&(rmhd->v8.width));
		_roomHeight = READ_LE_UINT32(&(rmhd->v8.height));
		_numObjectsInRoom = (byte)READ_LE_UINT32(&(rmhd->v8.numObjects));
	} else if (_game.version == 7) {
		_roomWidth = READ_LE_UINT16(&(rmhd->v7.width));
		_roomHeight = READ_LE_UINT16(&(rmhd->v7.height));
		_numObjectsInRoom = (byte)READ_LE_UINT16(&(rmhd->v7.numObjects));
	} else {
		_roomWidth = READ_LE_UINT16(&(rmhd->old.width));
		_roomHeight = READ_LE_UINT16(&(rmhd->old.height));
		_numObjectsInRoom = (byte)READ_LE_UINT16(&(rmhd->old.numObjects));
	}

	//
	// Find the room image data
	//
	if (_game.version == 8) {
		_IM00_offs = getObjectImage(roomptr, 1) - roomptr;
	} else if (_game.features & GF_SMALL_HEADER) {
		_IM00_offs = findResourceData(MKTAG('I','M','0','0'), roomptr) - roomptr;
	} else if (_game.heversion >= 70) {
		byte *roomImagePtr = getResourceAddress(rtRoomImage, _roomResource);
		_IM00_offs = findResource(MKTAG('I','M','0','0'), roomImagePtr) - roomImagePtr;
	} else {
		_IM00_offs = findResource(MKTAG('I','M','0','0'), findResource(MKTAG('R','M','I','M'), roomptr)) - roomptr;
	}

	//
	// Look for an exit script
	//
	ptr = findResourceData(MKTAG('E','X','C','D'), roomResPtr);
	if (ptr)
		_EXCD_offs = ptr - roomResPtr;
	if (_dumpScripts && _EXCD_offs)
		dumpResource("exit-", _roomResource, roomResPtr + _EXCD_offs - _resourceHeaderSize, -1);

	//
	// Look for an entry script
	//
	ptr = findResourceData(MKTAG('E','N','C','D'), roomResPtr);
	if (ptr)
		_ENCD_offs = ptr - roomResPtr;
	if (_dumpScripts && _ENCD_offs)
		dumpResource("entry-", _roomResource, roomResPtr + _ENCD_offs - _resourceHeaderSize, -1);

	//
	// Setup local scripts
	//

	// Determine the room script base address
	roomResPtr = roomptr = getResourceAddress(rtRoom, _roomResource);
	if (_game.version == 8)
		roomResPtr = getResourceAddress(rtRoomScripts, _roomResource);
	searchptr = roomResPtr;

	memset(_localScriptOffsets, 0, sizeof(_localScriptOffsets));

	if (_game.features & GF_SMALL_HEADER) {
		ResourceIterator localScriptIterator(searchptr, true);
		while ((ptr = localScriptIterator.findNext(MKTAG('L','S','C','R'))) != nullptr) {
			int id = 0;
			ptr += _resourceHeaderSize;	/* skip tag & size */
			id = ptr[0];

			if (_dumpScripts) {
				char buf[32];
				sprintf(buf, "room-%d-", _roomResource);
				dumpResource(buf, id, ptr - _resourceHeaderSize);
			}

			_localScriptOffsets[id - _numGlobalScripts] = ptr + 1 - roomptr;
		}
	} else if (_game.heversion >= 90) {
		ResourceIterator localScriptIterator2(searchptr, false);
		while ((ptr = localScriptIterator2.findNext(MKTAG('L','S','C','2'))) != nullptr) {
			int id = 0;

			ptr += _resourceHeaderSize;	/* skip tag & size */

			id = READ_LE_UINT32(ptr);

			assertRange(_numGlobalScripts, id, _numLocalScripts + _numGlobalScripts, "local script");
			_localScriptOffsets[id - _numGlobalScripts] = ptr + 4 - roomResPtr;

			if (_dumpScripts) {
				char buf[32];
				sprintf(buf, "room-%d-", _roomResource);
				dumpResource(buf, id, ptr - _resourceHeaderSize);
			}
		}

		ResourceIterator localScriptIterator(searchptr, false);
		while ((ptr = localScriptIterator.findNext(MKTAG('L','S','C','R'))) != nullptr) {
			int id = 0;

			ptr += _resourceHeaderSize;	/* skip tag & size */

			id = ptr[0];
			_localScriptOffsets[id - _numGlobalScripts] = ptr + 1 - roomResPtr;

			if (_dumpScripts) {
				char buf[32];
				sprintf(buf, "room-%d-", _roomResource);
				dumpResource(buf, id, ptr - _resourceHeaderSize);
			}
		}

	} else {
		ResourceIterator localScriptIterator(searchptr, false);
		while ((ptr = localScriptIterator.findNext(MKTAG('L','S','C','R'))) != nullptr) {
			int id = 0;

			ptr += _resourceHeaderSize;	/* skip tag & size */

			if (_game.version == 8) {
				id = READ_LE_UINT32(ptr);
				assertRange(_numGlobalScripts, id, _numLocalScripts + _numGlobalScripts, "local script");
				_localScriptOffsets[id - _numGlobalScripts] = ptr + 4 - roomResPtr;
			} else if (_game.version == 7) {
				id = READ_LE_UINT16(ptr);
				assertRange(_numGlobalScripts, id, _numLocalScripts + _numGlobalScripts, "local script");
				_localScriptOffsets[id - _numGlobalScripts] = ptr + 2 - roomResPtr;
			} else {
				id = ptr[0];
				_localScriptOffsets[id - _numGlobalScripts] = ptr + 1 - roomResPtr;
			}

			if (_dumpScripts) {
				char buf[32];
				sprintf(buf, "room-%d-", _roomResource);
				dumpResource(buf, id, ptr - _resourceHeaderSize);
			}
		}
	}

	// Locate the EGA palette (currently unused).
	ptr = findResourceData(MKTAG('E','P','A','L'), roomptr);
	if (ptr)
		_EPAL_offs = ptr - roomptr;

	// Locate the standard room palette (for V3-V5 games).
	ptr = findResourceData(MKTAG('C','L','U','T'), roomptr);
	if (ptr)
		_CLUT_offs = ptr - roomptr;

	// Locate the standard room palettes (for V6+ games).
	if (_game.version >= 6) {
		ptr = findResource(MKTAG('P','A','L','S'), roomptr);
		if (ptr) {
			_PALS_offs = ptr - roomptr;
		}
	}

	// Transparent color
	byte trans;
	if (_game.version == 8)
		trans = (byte)READ_LE_UINT32(&(rmhd->v8.transparency));
	else {
		ptr = findResourceData(MKTAG('T','R','N','S'), roomptr);
		if (ptr)
			trans = ptr[0];
		else
			trans = 255;
	}

	// Actor Palette in HE 70 games
	if (_game.heversion == 70) {
		ptr = findResourceData(MKTAG('R','E','M','P'), roomptr);
		if (ptr) {
			for (i = 0; i < 256; i++)
				_HEV7ActorPalette[i] = *ptr++;
		} else {
			for (i = 0; i < 256; i++)
				_HEV7ActorPalette[i] = i;
		}
	}


	// WORKAROUND bug #1831: The dreaded DOTT "Can't get teeth" bug
	// makes it impossible to go on playing w/o cheating in some way.
	// It's not quite clear what causes it, but the effect is that object
	// 182, the teeth, are still in class 32 (kObjectClassUntouchable),
	// when they shouldn't be. Luckily, bitvar69 is set to 1 if and only if
	// the teeth are trapped and have not yet been taken by the player. So
	// we can make use of that fact to fix the object class of obj 182.
	if (_game.id == GID_TENTACLE && _roomResource == 26 && readVar(0x8000 + 69)
			&& getClass(182, kObjectClassUntouchable)) {
		putClass(182, kObjectClassUntouchable, 0);
	}

	_gdi->roomChanged(roomptr);
	_gdi->setTransparentColor(trans);
}

/**
 * Init some dynamic room data after a room has been loaded.
 * E.g. the initial box data is loaded, the initial palette is set etc.
 * All of the things setup in here can be modified later on by scripts.
 * So it is not appropriate to call it after loading a savegame.
 */
void ScummEngine::resetRoomSubBlocks() {
	ResId i;
	const byte *ptr;
	byte *roomptr;

	// Determine the room and room script base address
	roomptr = getResourceAddress(rtRoom, _roomResource);
	if (!roomptr)
		error("Room %d: data not found (" __FILE__  ":%d)", _roomResource, __LINE__);

	//
	// Load box data
	//
	memset(_extraBoxFlags, 0, sizeof(_extraBoxFlags));

	_res->nukeResource(rtMatrix, 1);
	_res->nukeResource(rtMatrix, 2);
	if (_game.features & GF_SMALL_HEADER) {
		ptr = findResourceData(MKTAG('B','O','X','D'), roomptr);
		if (ptr) {
			byte numOfBoxes = *ptr;
			int size;
			if (_game.version == 3)
				size = numOfBoxes * SIZEOF_BOX_V3 + 1;
			else
				size = numOfBoxes * SIZEOF_BOX + 1;

			_res->createResource(rtMatrix, 2, size);
			memcpy(getResourceAddress(rtMatrix, 2), ptr, size);
			ptr += size;

			size = getResourceDataSize(ptr - size - _resourceHeaderSize) - size;
			if (size > 0) {					// do this :)
				_res->createResource(rtMatrix, 1, size);
				memcpy(getResourceAddress(rtMatrix, 1), ptr, size);
			}

		}
	} else {
		ptr = findResourceData(MKTAG('B','O','X','D'), roomptr);
		if (ptr) {
			int size = getResourceDataSize(ptr);
			_res->createResource(rtMatrix, 2, size);
			roomptr = getResourceAddress(rtRoom, _roomResource);
			ptr = findResourceData(MKTAG('B','O','X','D'), roomptr);
			memcpy(getResourceAddress(rtMatrix, 2), ptr, size);
		}

		ptr = findResourceData(MKTAG('B','O','X','M'), roomptr);
		if (ptr) {
			int size = getResourceDataSize(ptr);
			_res->createResource(rtMatrix, 1, size);
			roomptr = getResourceAddress(rtRoom, _roomResource);
			ptr = findResourceData(MKTAG('B','O','X','M'), roomptr);
			memcpy(getResourceAddress(rtMatrix, 1), ptr, size);
		}
	}

	//
	// Load scale data
	//
	for (i = 1; i < _res->_types[rtScaleTable].size(); i++)
		_res->nukeResource(rtScaleTable, i);

	ptr = findResourceData(MKTAG('S','C','A','L'), roomptr);
	if (ptr) {
		int s1, s2, y1, y2;
		if (_game.version == 8) {
			for (i = 1; i < _res->_types[rtScaleTable].size(); i++, ptr += 16) {
				s1 = READ_LE_UINT32(ptr);
				y1 = READ_LE_UINT32(ptr + 4);
				s2 = READ_LE_UINT32(ptr + 8);
				y2 = READ_LE_UINT32(ptr + 12);
				setScaleSlot(i, 0, y1, s1, 0, y2, s2);
			}
		} else {
			for (i = 1; i < _res->_types[rtScaleTable].size(); i++, ptr += 8) {
				s1 = READ_LE_UINT16(ptr);
				y1 = READ_LE_UINT16(ptr + 2);
				s2 = READ_LE_UINT16(ptr + 4);
				y2 = READ_LE_UINT16(ptr + 6);
				if (s1 || y1 || s2 || y2) {
					setScaleSlot(i, 0, y1, s1, 0, y2, s2);
				}
			}
		}
	}

	// We need to setup the current palette before initCycl for Indy4 Amiga.
	if (_PALS_offs || _CLUT_offs)
		setCurrentPalette(0);

	// Color cycling
	// HE 7.0 games load resources but don't use them.
	if (_game.version >= 4 && _game.heversion <= 62) {
		ptr = findResourceData(MKTAG('C','Y','C','L'), roomptr);
		if (ptr) {
			initCycl(ptr);
		}
	}

#ifdef ENABLE_HE
	// Polygons in HE 80+ games
	if (_game.heversion >= 80) {
		ptr = findResourceData(MKTAG('P','O','L','D'), roomptr);
		if (ptr) {
			((ScummEngine_v71he *)this)->_wiz->polygonLoad(ptr);
		}
	}
#endif
}


void ScummEngine_v3old::setupRoomSubBlocks() {
	const byte *ptr;
	byte *roomptr;
	const RoomHeader *rmhd;

	_ENCD_offs = 0;
	_EXCD_offs = 0;
	_EPAL_offs = 0;
	_CLUT_offs = 0;
	_PALS_offs = 0;

	// Determine the room and room script base address
	roomptr = getResourceAddress(rtRoom, _roomResource);
	if (!roomptr)
		error("Room %d: data not found (" __FILE__  ":%d)", _roomResource, __LINE__);

	//
	// Determine the room dimensions (width/height)
	//
	rmhd = (const RoomHeader *)(roomptr + 4);

	if (_game.version <= 1) {
		if (_game.platform == Common::kPlatformNES) {
			_roomWidth = READ_LE_UINT16(&(rmhd->old.width)) * 8;
			_roomHeight = READ_LE_UINT16(&(rmhd->old.height)) * 8;

			// HACK: To let our code work normal with narrow rooms we
			// adjust width. It will render garbage on right edge but we do
			// not render it anyway
			if (_roomWidth < 32 * 8)
				_roomWidth = 32 * 8;
		} else {
			_roomWidth = roomptr[4] * 8;
			_roomHeight = roomptr[5] * 8;
		}
	} else {
		_roomWidth = READ_LE_UINT16(&(rmhd->old.width));

		// WORKAROUND: Fix bad width value for room 64 (book of maps) in
		// Indy3. A specific version of this game (DOS/EGA v1.0, according to
		// scumm-md5.txt) has a wrong width of 1793 stored in the data files,
		// which causes a strange situation in which the book view may scroll
		// towards the right depending on Indy's position from the previous room.
		// Fixes bug #6679.
		if (_game.id == GID_INDY3 && _roomResource == 64 && _roomWidth == 1793)
			_roomWidth = 320;
		_roomHeight = READ_LE_UINT16(&(rmhd->old.height));
	}
	_numObjectsInRoom = roomptr[20];

	//
	// Find the room image data
	//
	if (_game.version <= 1) {
		_IM00_offs = 0;
	} else {
		_IM00_offs = READ_LE_UINT16(roomptr + 0x0A);
	}

	//
	// Look for an exit script
	//
	int EXCD_len = -1;
	if (_game.version <= 2) {
		_EXCD_offs = READ_LE_UINT16(roomptr + 0x18);
		EXCD_len = READ_LE_UINT16(roomptr + 0x1A) - _EXCD_offs + _resourceHeaderSize;	// HACK
	} else {
		_EXCD_offs = READ_LE_UINT16(roomptr + 0x19);
		EXCD_len = READ_LE_UINT16(roomptr + 0x1B) - _EXCD_offs + _resourceHeaderSize;	// HACK
	}
	if (_dumpScripts && _EXCD_offs)
		dumpResource("exit-", _roomResource, roomptr + _EXCD_offs - _resourceHeaderSize, EXCD_len);

	//
	// Look for an entry script
	//
	int ENCD_len = -1;
	if (_game.version <= 2) {
		_ENCD_offs = READ_LE_UINT16(roomptr + 0x1A);
		ENCD_len = READ_LE_UINT16(roomptr) - _ENCD_offs + _resourceHeaderSize; // HACK
	} else {
		_ENCD_offs = READ_LE_UINT16(roomptr + 0x1B);
		// FIXME - the following is a hack which assumes that immediately after
		// the entry script the first local script follows.
		int num_objects = *(roomptr + 20);
		int num_sounds = *(roomptr + 23);
		int num_scripts = *(roomptr + 24);
		ptr = roomptr + 29 + num_objects * 4 + num_sounds + num_scripts;
		ENCD_len = READ_LE_UINT16(ptr + 1) - _ENCD_offs + _resourceHeaderSize; // HACK
	}
	if (_dumpScripts && _ENCD_offs)
		dumpResource("entry-", _roomResource, roomptr + _ENCD_offs - _resourceHeaderSize, ENCD_len);

	//
	// Setup local scripts
	//

	// Determine the room script base address
	roomptr = getResourceAddress(rtRoom, _roomResource);

	memset(_localScriptOffsets, 0, sizeof(_localScriptOffsets));

	int num_objects = *(roomptr + 20);
	int num_sounds;
	int num_scripts;

	if (_game.version <= 2) {
		num_sounds = *(roomptr + 22);
		num_scripts = *(roomptr + 23);
		ptr = roomptr + 28 + num_objects * 4;
		while (num_sounds--)
			loadResource(rtSound, *ptr++);
		while (num_scripts--)
			loadResource(rtScript, *ptr++);
	} else /* if (_game.version == 3) */ {
		num_sounds = *(roomptr + 23);
		num_scripts = *(roomptr + 24);
		ptr = roomptr + 29 + num_objects * 4 + num_sounds + num_scripts;
		while (*ptr) {
			int id = *ptr;

			_localScriptOffsets[id - _numGlobalScripts] = READ_LE_UINT16(ptr + 1);
			ptr += 3;

			if (_dumpScripts) {
				char buf[32];
				sprintf(buf, "room-%d-", _roomResource);

				// HACK: to determine the sizes of the local scripts, we assume that
				// a) their order in the data file is the same as in the index
				// b) the last script at the same time is the last item in the room "header"
				int len = - (int)_localScriptOffsets[id - _numGlobalScripts] + _resourceHeaderSize;
				if (*ptr)
					len += READ_LE_UINT16(ptr + 1);
				else
					len += READ_LE_UINT16(roomptr);
				dumpResource(buf, id, roomptr + _localScriptOffsets[id - _numGlobalScripts] - _resourceHeaderSize, len);
			}
		}
	}

	_gdi->roomChanged(roomptr);
}

void ScummEngine_v3old::resetRoomSubBlocks() {
	const byte *ptr;
	byte *roomptr;

	// Determine the room and room script base address
	roomptr = getResourceAddress(rtRoom, _roomResource);
	if (!roomptr)
		error("Room %d: data not found (" __FILE__  ":%d)", _roomResource, __LINE__);

	// Reset room color for V1 zak
	if (_game.version <= 1)
		_roomPalette[0] = 0;

	//
	// Load box data
	//
	_res->nukeResource(rtMatrix, 1);
	_res->nukeResource(rtMatrix, 2);

	if (_game.version <= 2)
		ptr = roomptr + *(roomptr + 0x15);
	else
		ptr = roomptr + READ_LE_UINT16(roomptr + 0x15);
	if (ptr) {
		byte numOfBoxes = 0;
		int size;

		if (_game.version == 0) {
			// Count number of boxes
			while (*ptr != 0xFF) {
				numOfBoxes++;
				ptr += 5;
			}

			ptr = roomptr + *(roomptr + 0x15);
			size = numOfBoxes * SIZEOF_BOX_V0 + 1;

			_res->createResource(rtMatrix, 2, size + 1);
			getResourceAddress(rtMatrix, 2)[0] = numOfBoxes;
			memcpy(getResourceAddress(rtMatrix, 2) + 1, ptr, size);
		} else {
			numOfBoxes = *ptr;
			if (_game.version <= 2)
				size = numOfBoxes * SIZEOF_BOX_V2 + 1;
			else
				size = numOfBoxes * SIZEOF_BOX_V3 + 1;

			_res->createResource(rtMatrix, 2, size);
			memcpy(getResourceAddress(rtMatrix, 2), ptr, size);
		}

		ptr += size;
		if (_game.version == 0) {
			const byte *tmp = ptr;
			size = 0;

			// Compute matrix size
			for (int i = 0; i < numOfBoxes; i++) {
				while (*tmp != 0xFF) {
					size++;
					tmp++;
				}
				size++;
				tmp++;
			}
		} else if (_game.version <= 2) {
			size = numOfBoxes * (numOfBoxes + 1);
		} else {
			// FIXME. This is an evil HACK!!!
			size = (READ_LE_UINT16(roomptr + 0x0A) - READ_LE_UINT16(roomptr + 0x15)) - size;
		}

		if (size > 0) {					// do this :)
			_res->createResource(rtMatrix, 1, size);
			memcpy(getResourceAddress(rtMatrix, 1), ptr, size);
		}

	}

	//
	// No scale data in old bundle games
	//
	for (ResId id = 1; id < _res->_types[rtScaleTable].size(); id++)
		_res->nukeResource(rtScaleTable, id);

}

} // End of namespace Scumm
back to top