diff options
| -rw-r--r-- | tools/create_lure/Makefile | 4 | ||||
| -rw-r--r-- | tools/create_lure/create_lure_dat.cpp | 1147 | ||||
| -rw-r--r-- | tools/create_lure/create_lure_dat.h | 401 | ||||
| -rw-r--r-- | tools/create_lure/process_actions.cpp | 341 | 
4 files changed, 1893 insertions, 0 deletions
diff --git a/tools/create_lure/Makefile b/tools/create_lure/Makefile new file mode 100644 index 0000000000..1ed33b594c --- /dev/null +++ b/tools/create_lure/Makefile @@ -0,0 +1,4 @@ +# $Id$ + +all: +	g++ -I../.. create_lure_dat.cpp process_actions.cpp -o create_lure diff --git a/tools/create_lure/create_lure_dat.cpp b/tools/create_lure/create_lure_dat.cpp new file mode 100644 index 0000000000..42b06d5432 --- /dev/null +++ b/tools/create_lure/create_lure_dat.cpp @@ -0,0 +1,1147 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2005-2006 The ScummVM project + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + * This is a utility for extracting needed resource data from the Lure of the Temptress + * lure.exe file into a new file lure.dat - this file is required for the ScummVM + * Lure of the Temptress module to work properly + * + * TODO:  + * Some of the field values, such as hotspot tick proc offsets, will vary with + * different language versions. These will need to be remapped to a language independant + * value once I've fully implemented the English version. + * Some areas of the data segment are still to be decoded + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "create_lure_dat.h" + +File lure_exe; + +uint16 animOffsets[MAX_NUM_ANIM_RECORDS]; +int animIndex = 0; +uint16 actionOffsets[MAX_NUM_ACTION_RECORDS]; +int actionIndex = 0; + + +void add_anim_record(uint16 offset) { +	for (int ctr = 0; ctr < animIndex; ++ctr) +		if (animOffsets[ctr] == offset) return; +	if (animIndex == MAX_NUM_ANIM_RECORDS) { +		printf("Animation record offset table size exceeded\n"); +		exit(1); +	} +	animOffsets[animIndex++] = offset; +} + +void add_action_list(uint16 offset) { +	for (int ctr = 0; ctr < actionIndex; ++ctr) +		if (actionOffsets[ctr] == offset) return; +	if (actionIndex == MAX_NUM_ACTION_RECORDS) { +		printf("Action record offset table size exceeded\n"); +		exit(1); +	} +	actionOffsets[actionIndex++] = offset; +} + +void read_basic_palette(byte *&data, uint16 &totalSize) { +	totalSize = PALETTE_SIZE; +	lure_exe.seek(PALETTE_OFFSET); +	data = (byte *) malloc(PALETTE_SIZE); +	lure_exe.read(data, totalSize); +} + +#define ALT_PALETTE_1 0x1757 +#define ALT_PALETTE_1_SIZE 180 +#define ALT_PALETTE_2 0x180B +#define ALT_PALETTE_2_SIZE 24 + +void read_replacement_palette(byte *&data, uint16 &totalSize) { +	totalSize = ALT_PALETTE_1_SIZE + ALT_PALETTE_2_SIZE; +	data = (byte *) malloc(totalSize); + +	lure_exe.seek(DATA_SEGMENT + ALT_PALETTE_1); +	lure_exe.read(data, ALT_PALETTE_1_SIZE); +	lure_exe.seek(DATA_SEGMENT + ALT_PALETTE_2); +	lure_exe.read(data + ALT_PALETTE_1_SIZE, ALT_PALETTE_2_SIZE); +} + +void read_dialog_data(byte *&data, uint16 &totalSize) { +	totalSize = DIALOG_SIZE; +	lure_exe.seek(DIALOG_OFFSET); +	data = (byte *) malloc(DIALOG_SIZE); +	lure_exe.read(data, totalSize); +} + +void read_talk_dialog_data(byte *&data, uint16 &totalSize) { +	totalSize = TALK_DIALOG_SIZE; +	lure_exe.seek(TALK_DIALOG_OFFSET, SEEK_SET); +	data = (byte *) malloc(TALK_DIALOG_SIZE); +	lure_exe.read(data, totalSize); +} + +void read_room_data(byte *&data, uint16 &totalSize)  +{ +	data = (byte *) malloc(MAX_DATA_SIZE); +	memset(data, 0, MAX_DATA_SIZE); + +	uint16 *offsetPtr = (uint16 *) data; +	uint16 offset = (ROOM_NUM_ENTRIES + 1) * sizeof(uint16); +	uint16 pixelOffset; +	RoomResource buffer; +	int outputIndex = 0; +	RoomHeaderEntry headerEntry; +	RoomRectIn bounds; + +	for (int index = 0; index < ROOM_NUM_ENTRIES; ++index) { + +		lure_exe.seek(DATA_SEGMENT + ROOM_TABLE + index * 9); +		lure_exe.read(&headerEntry, sizeof(RoomHeaderEntry)); + +		if ((FROM_LE_16(headerEntry.offset) != 0) &&  +			(FROM_LE_16(headerEntry.offset) != 0xffff) && +			(FROM_LE_16(headerEntry.roomNumber) != 0xc09) &&  +			(FROM_LE_16(headerEntry.roomNumber) != 0)) { +			// Store offset of room entry +			*offsetPtr++ = TO_LE_16(offset); + +			// Copy over basic room details +			lure_exe.seek(DATA_SEGMENT + headerEntry.offset); +			lure_exe.read(&buffer, sizeof(RoomResource)); + +			RoomResourceOutput *rec = (RoomResourceOutput *) (data + offset); +			rec->roomNumber = headerEntry.roomNumber; +			rec->descId = headerEntry.descId; +			rec->numLayers = buffer.numLayers; +			memcpy(rec->layers, buffer.layers, 8); +			rec->sequenceOffset = buffer.sequenceOffset;			 +			rec->clippingXStart = TO_LE_16(FROM_LE_16(buffer.clippingXStart) - 0x80); +			rec->clippingXEnd = (FROM_LE_16(buffer.clippingXEnd) == 0) ? 0 :  +			  TO_LE_16(FROM_LE_16(buffer.clippingXEnd) - 0x80); +			rec->numExits = 0; + +			offset += sizeof(RoomResourceOutput); + +			// Copy over room exits +			for (;;) { +				RoomResourceExit1 *p = (RoomResourceExit1 *) (data + offset); +				lure_exe.read(p, sizeof(RoomResourceExit1)); +				if (FROM_LE_16(p->xs) == 0xffff) break; + +				rec->numExits = TO_LE_16(FROM_LE_16(rec->numExits) + 1); +				p->xs = TO_LE_16(FROM_LE_16(p->xs) - 0x80); +				p->ys = TO_LE_16(FROM_LE_16(p->ys) - 0x80); +				p->xe = TO_LE_16(FROM_LE_16(p->xe) - 0x80); +				p->ye = TO_LE_16(FROM_LE_16(p->ye) - 0x80); +				offset += sizeof(RoomResourceExit1); +				RoomResourceExit2 *p2 = (RoomResourceExit2 *) (data + offset); + +				if (FROM_LE_16(p->sequenceOffset) == 0xffff) { +					lure_exe.read(p2, sizeof(RoomResourceExit2)); +					p2->newRoomX = TO_LE_16(FROM_LE_16(p2->newRoomX) - 0x80); +					p2->newRoomY = TO_LE_16(FROM_LE_16(p2->newRoomY) - 0x80); +				} else { +					p2->newRoom = 0; +					p2->direction = 0; +					p2->newRoomX = 0; +					p2->newRoomY = 0; +				} + +				offset += sizeof(RoomResourceExit2); +			} + +			// Handle the random destination walk bounds for the room +			lure_exe.seek(DATA_SEGMENT + WALK_AREAS_OFFSET +  +				buffer.walkBoundsIndex * sizeof(RoomRectIn)); +			lure_exe.read(&bounds, sizeof(RoomRectIn)); +			rec->walkBounds.xs = TO_LE_16(FROM_LE_16(bounds.xs) - 0x80); +			rec->walkBounds.xe = TO_LE_16(FROM_LE_16(bounds.xe) - 0x80); +			rec->walkBounds.ys = TO_LE_16(FROM_LE_16(bounds.ys) - 0x80); +			rec->walkBounds.ye = TO_LE_16(FROM_LE_16(bounds.ye) - 0x80); + +			// If the room has a default pixel blocks list, add the references +			if (buffer.pixelListOffset != 0) { +				lure_exe.seek(DATA_SEGMENT + FROM_LE_16(buffer.pixelListOffset)); +				pixelOffset = lure_exe.readWord(); +				while (pixelOffset != 0) { +					add_anim_record(pixelOffset); +					pixelOffset = lure_exe.readWord(); +				} +			} +		} +	} + +	WRITE_LE_UINT16(offsetPtr, 0xffff); +	totalSize = offset; +} + +void read_hotspot_data(byte *&data, uint16 &totalSize)  +{ +	uint16 offsets[4] = {0x5d98, 0x5eb8, 0x623e, 0x63b1}; +	uint16 startId[4] = {0x3e8, 0x408, 0x2710, 0x7530}; +	int walkNumEntries = 0; +	int walkCtr; +	int numEntries; +	HotspotWalkToRecord rec; +	HotspotWalkToRecord *walkList; +	HotspotHeaderEntry entryHeader; +	HotspotResource entry; +	uint16 dataSize; +	HotspotResourceOutput *r; +	CurrentActionInput action; + +	// Allocate enough space for output hotspot list +	data = (byte *) malloc(MAX_HOTSPOTS * sizeof(HotspotResourceOutput)); + +	// Determine number of hotspot walk to entries +	lure_exe.seek(DATA_SEGMENT + HOTSPOT_WALK_TO_OFFSET); +	do { +		++walkNumEntries; +		lure_exe.read(&rec, sizeof(HotspotWalkToRecord)); +	} while (TO_LE_16(rec.hotspotId) != 0); +	--walkNumEntries; + +	dataSize = walkNumEntries * sizeof(HotspotWalkToRecord); +	walkList = (HotspotWalkToRecord *) malloc(dataSize); +	lure_exe.seek(DATA_SEGMENT + HOTSPOT_WALK_TO_OFFSET); +	lure_exe.read(walkList, sizeof(HotspotWalkToRecord) * walkNumEntries); + +	// Main code for creating the hotspot list + +	r = (HotspotResourceOutput *) data; +	numEntries = 0; + +	for (int tableNum = 0; tableNum < 4; ++tableNum) { +		uint16 hotspotIndex = 0; +		for (;;) { +			lure_exe.seek(DATA_SEGMENT + offsets[tableNum] +  hotspotIndex * 9); +			lure_exe.read(&entryHeader, sizeof(HotspotHeaderEntry)); +			if (FROM_LE_16(entryHeader.offset) == 0xffff) break; + +			memset(r, 0, sizeof(HotspotResourceOutput)); +			r->hotspotId = TO_LE_16(startId[tableNum] + hotspotIndex); +			r->nameId = TO_LE_16(FROM_LE_16(entryHeader.resourceId) & 0x1fff); +			r->descId = TO_LE_16(FROM_LE_16(entryHeader.descId) & 0x1fff); +			r->descId2 = TO_LE_16(FROM_LE_16(entryHeader.descId2) & 0x1fff); +			r->hdrFlags = entryHeader.hdrFlags; + +			// Get the hotspot data +			lure_exe.seek(DATA_SEGMENT + entryHeader.offset); +			lure_exe.read(&entry, sizeof(HotspotResource)); +			 +			r->actions = entry.actions; +			r->roomNumber = entry.roomNumber; +			r->startX = TO_LE_16(FROM_LE_16(entry.startX) - 0x80); +			r->startY = TO_LE_16(FROM_LE_16(entry.startY) - 0x80); + +			r->width = entry.width; +			r->height = entry.height; +			r->widthCopy = entry.widthCopy; +			r->heightCopy = entry.heightCopy; +			r->yCorrection = entry.yCorrection; +			r->talkX = entry.talkX; +			r->talkY = entry.talkY; +			r->characterMode = entry.characterMode; +			r->delayCtr = entry.delayCtr; +			r->tickSequenceOffset = entry.tickSequenceOffset; + +			r->layer = entry.layer; +			r->scriptLoadFlag = entry.scriptLoadFlag; +			r->loadOffset = entry.loadOffset; +			r->colourOffset = entry.colourOffset; +			r->sequenceOffset = entry.sequenceOffset; +			r->tickProcOffset = entry.tickProcOffset; +			r->flags = entry.flags; + +			// Find the walk-to coordinates for the hotspot +			walkCtr = 0; +			while ((walkCtr < walkNumEntries) && +				   (FROM_LE_16(walkList[walkCtr].hotspotId) != FROM_LE_16(r->hotspotId))) +				++walkCtr; +			if (walkCtr == walkNumEntries) { +				r->walkX = 0; +				r->walkY = 0; +			} else { +				r->walkX = TO_LE_16(FROM_LE_16(walkList[walkCtr].x) - 0x80); +				uint16 y = FROM_LE_16(walkList[walkCtr].y); +				r->walkY = TO_LE_16((y & 0x8000) | (uint16) ((int16) (y & 0x7fff) - 0x80)); +			} + +			// Use the offset of the animation data as a dummy Id for the data +			r->animRecordId = entry.animOffset; +			r->tickTimeout = entry.tickTimeout; +			add_anim_record(FROM_LE_16(entry.animOffset)); + +			// Add in the actions offset table +			r->actionsOffset = entry.actionsOffset; +			if (FROM_LE_16(entry.actionsOffset) != 0) +				add_action_list(FROM_LE_16(entry.actionsOffset)); + +			if (FROM_LE_16(r->hotspotId) >= 0x408) { +				// Hotspot is not an NPC +				r->npcSchedule = 0; +			} else { +				// Check for an NPC schedule +				lure_exe.seek(DATA_SEGMENT + entryHeader.offset + 0x63); +				lure_exe.read(&action, sizeof(CurrentActionInput)); + +				if (action.action != 2)  +					r->npcSchedule = 0; +				else { +					r->npcSchedule = get_sequence_index(FROM_LE_16(action.dataOffset)); +				} +			} + +			++hotspotIndex; +			++r; +			++numEntries; + +			if (numEntries == MAX_HOTSPOTS) { +				printf("Ran out of stack spaces for hotspot copying\n"); +				exit(1); +			} +		} +	} + +	r->hotspotId = TO_LE_16(0xffff); +	totalSize = numEntries * sizeof(HotspotResourceOutput) + 2; + +	// Dispose of hotspot walk-to co-ordinate list +	free(walkList); +} + +void read_hotspot_override_data(byte *&data, uint16 &totalSize)  +{ +	lure_exe.seek(DATA_SEGMENT + HOTSPOT_OVERRIDE_OFFSET); +	int numOverrides = 0; +	HotspotOverride rec; + +	// Determine number of hotspot overrides +	do { +		++numOverrides; +		lure_exe.read(&rec, sizeof(HotspotOverride)); +	} while (FROM_LE_16(rec.hotspotId) != 0); +	--numOverrides; + +	// Prepare output data and read in all entries at once +	totalSize = numOverrides * sizeof(HotspotOverride) + 2; +	data = (byte *) malloc(totalSize); +	lure_exe.seek(DATA_SEGMENT + HOTSPOT_OVERRIDE_OFFSET); +	lure_exe.read(data, totalSize - 2); +	WRITE_LE_UINT16(data + totalSize - 2, 0xffff); + +	// Post-process the coordinates +	HotspotOverride *p = (HotspotOverride *) data; +	for (int overrideCtr = 0; overrideCtr < numOverrides; ++overrideCtr, ++p) { +		p->xs = TO_LE_16(FROM_LE_16(p->xs) - 0x80); +		p->xe = TO_LE_16(FROM_LE_16(p->xe) - 0x80); +		p->ys = TO_LE_16(FROM_LE_16(p->ys) - 0x80); +		p->ye = TO_LE_16(FROM_LE_16(p->ye) - 0x80); +	} +} + +void read_room_exits(byte *&data, uint16 &totalSize) { +	RoomExitHotspotRecord rec; +	uint16 offsets[NUM_ROOM_EXITS]; +	uint16 numEntries[NUM_ROOM_EXITS]; +	int roomCtr; +	totalSize = (NUM_ROOM_EXITS + 1) * sizeof(uint16); + +	lure_exe.seek(DATA_SEGMENT + ROOM_EXITS_OFFSET); +	for (roomCtr = 0; roomCtr < NUM_ROOM_EXITS; ++roomCtr) +		offsets[roomCtr] = lure_exe.readWord(); + +	// First loop to find total of room exit records there are +	for (roomCtr = 0; roomCtr < NUM_ROOM_EXITS; ++roomCtr) { +		numEntries[roomCtr] = 0; +		if (offsets[roomCtr] == 0) continue; + +		// Get number of exits for the room +		lure_exe.seek(DATA_SEGMENT + offsets[roomCtr]); +		lure_exe.read(&rec, sizeof(RoomExitHotspotRecord)); +		while (FROM_LE_16(rec.xs) != 0) { +			totalSize += sizeof(RoomExitHotspotOutputRecord); +			numEntries[roomCtr]++; +			lure_exe.read(&rec, sizeof(RoomExitHotspotRecord)); +		} +		totalSize += sizeof(uint16); // save room for room list end flag +	} + +	// Alloacte the total needed space +	data = (byte *) malloc(totalSize); +	uint16 *offset = (uint16 *) data; +	uint16 destIndex = (NUM_ROOM_EXITS + 1) * sizeof(uint16); +	uint16 entryCtr; +	 +	// Loop to build up the result table + +	for (roomCtr = 0; roomCtr < NUM_ROOM_EXITS; ++roomCtr) { +		if (offsets[roomCtr] == 0) { +			*offset++ = 0;		// No entries +		} else { +			// Read in the entries for the room +			*offset++ = TO_LE_16(destIndex); + +			RoomExitHotspotOutputRecord *destP = (RoomExitHotspotOutputRecord *)  +				(data + destIndex); + +			lure_exe.seek(DATA_SEGMENT + offsets[roomCtr]); + +			for (entryCtr = 0; entryCtr < numEntries[roomCtr]; ++entryCtr, ++destP) { +				lure_exe.read(&rec, sizeof(RoomExitHotspotRecord)); + +				// Copy over the record +				destP->xs = TO_LE_16(FROM_LE_16(rec.xs) - 0x80); +				destP->xe = TO_LE_16(FROM_LE_16(rec.xe) - 0x80); +				destP->ys = TO_LE_16(FROM_LE_16(rec.ys) - 0x80); +				destP->ye = TO_LE_16(FROM_LE_16(rec.ye) - 0x80); +				destP->hotspotId = rec.hotspotId; +				destP->cursorNum = rec.cursorNum; +				destP->destRoomNumber = rec.destRoomNumber; +			} +			 +			destIndex += numEntries[roomCtr] * sizeof(RoomExitHotspotOutputRecord); +			WRITE_LE_UINT16(data + destIndex, 0xffff); +			destIndex += sizeof(uint16); +		} +	} +	WRITE_LE_UINT16(offset, 0xffff); +} + +void read_room_exit_joins(byte *&data, uint16 &totalSize) { +	RoomExitHotspotJoinRecord rec, *p; +	lure_exe.seek(DATA_SEGMENT + ROOM_EXIT_JOINS_OFFSET); +	int numRecords = 0; +	uint32 unused; + +	lure_exe.seek(DATA_SEGMENT + ROOM_EXIT_JOINS_OFFSET); +	do { +		lure_exe.read(&rec, sizeof(RoomExitHotspotJoinRecord)); +		lure_exe.read(&unused, sizeof(uint32)); +		++numRecords; +	} while (FROM_LE_16(rec.hotspot1Id) != 0); +	--numRecords; + +	// Allocate the data and read in all the records +	totalSize = (numRecords * sizeof(RoomExitHotspotJoinRecord)) + 2; +	data = (byte *) malloc(totalSize); +	lure_exe.seek(DATA_SEGMENT + ROOM_EXIT_JOINS_OFFSET); +	 +	p = (RoomExitHotspotJoinRecord *) data; +	for (int recordCtr = 0; recordCtr < numRecords; ++recordCtr) +	{ +		lure_exe.read(p, sizeof(RoomExitHotspotJoinRecord)); +		lure_exe.read(&unused, sizeof(uint32)); +		++p; +	} +	WRITE_LE_UINT16(p, 0xffff); +} + +// This next method reads in the animation and movement data. At the moment I +// figure out which animations have valid movement record sets by finding +// animations whose four direction offsets are near each other. There's  +// probably a better method than this, but it'll do for now + +void read_anim_data(byte *&data, uint16 &totalSize) { +	// Add special pixel records +	add_anim_record(0x5c95); + +	// Get the animation data records +	AnimRecord inRec; +	MovementRecord move; +	MovementRecord *destMove; +	uint16 offset, moveOffset; +	uint16 startOffset; +	int ctr, dirCtr; +	int movementSize = 0; +	bool *includeAnim = (bool *) malloc(animIndex); + +	// Loop to figure out the total number of movement records there are +	for (ctr = 0; ctr < animIndex; ++ctr) { +		lure_exe.seek(DATA_SEGMENT + animOffsets[ctr]); +		lure_exe.read(&inRec, sizeof(AnimRecord)); + +		if ((FROM_LE_16(inRec.leftOffset) < 0x5000) ||  +			(FROM_LE_16(inRec.rightOffset) < 0x5000) || +			(abs(FROM_LE_16(inRec.leftOffset)-FROM_LE_16(inRec.rightOffset)) > 0x800) || +			(abs(FROM_LE_16(inRec.rightOffset)-FROM_LE_16(inRec.upOffset)) > 0x800) || +			(abs(FROM_LE_16(inRec.upOffset)-FROM_LE_16(inRec.downOffset)) > 0x800)) { +			// Animation doesn't have valid movement data +			includeAnim[ctr] = false; +		} else { +			includeAnim[ctr] = true; +			for (dirCtr=0; dirCtr<4; ++dirCtr) { +				switch (dirCtr) { +				case 0: +					offset = FROM_LE_16(inRec.leftOffset); +					break; +				case 1: +					offset = FROM_LE_16(inRec.rightOffset); +					break; +				case 2: +					offset = FROM_LE_16(inRec.upOffset); +					break; +				default: +					offset = FROM_LE_16(inRec.downOffset); +				} + +				if (offset != 0) { +					lure_exe.seek(DATA_SEGMENT + offset); +					lure_exe.read(&move, sizeof(MovementRecord)); + +					while (FROM_LE_16(move.frameNumber) != 0xffff) { +						movementSize += sizeof(MovementRecord); +						lure_exe.read(&move, sizeof(MovementRecord)); +					} +					movementSize += 2; +				} +			} +		} +	} + +	totalSize = animIndex * sizeof(AnimRecordOutput) + 2 + movementSize; +	AnimRecordOutput *rec = (AnimRecordOutput *) malloc(totalSize); +	data = (byte *) rec; +	moveOffset = animIndex * sizeof(AnimRecordOutput) + 2; + +	// Loop to get in the animation records +	for (ctr = 0; ctr < animIndex; ++ctr, ++rec) { +		lure_exe.seek(DATA_SEGMENT + animOffsets[ctr]); +		lure_exe.read(&inRec, sizeof(AnimRecord)); + +		rec->animRecordId = animOffsets[ctr]; +		rec->animId = inRec.animId; +		rec->flags = TO_LE_16(inRec.flags); + +		rec->leftOffset = 0;  +		rec->rightOffset = 0; +		rec->upOffset = 0; +		rec->downOffset = 0; + +		rec->upFrame = inRec.upFrame; +		rec->downFrame = inRec.downFrame; +		rec->leftFrame = inRec.leftFrame; +		rec->rightFrame = inRec.rightFrame; + +		if (includeAnim[ctr]) { +			// Loop to get movement records +			uint16 *inDirs[4] = {&inRec.leftOffset, &inRec.rightOffset, +				&inRec.upOffset, &inRec.downOffset}; +			uint16 *outDirs[4] = {&rec->leftOffset, &rec->rightOffset, +				&rec->upOffset, &rec->downOffset}; + +			for (dirCtr=0; dirCtr<4; ++dirCtr) { +				offset = READ_LE_UINT16(inDirs[dirCtr]); + +				if (offset == 0) { +					startOffset = 0; +				} else { +					startOffset = moveOffset; + +					lure_exe.seek(DATA_SEGMENT + offset); +					lure_exe.read(&move, sizeof(MovementRecord)); +					destMove = (MovementRecord *) (data + moveOffset); + +					while (FROM_LE_16(move.frameNumber) != 0xffff) { +						destMove->frameNumber = move.frameNumber; +						destMove->xChange = move.xChange; +						destMove->yChange = move.yChange; + +						moveOffset += sizeof(MovementRecord); +						++destMove; +						lure_exe.read(&move, sizeof(MovementRecord)); +					} +					 +					destMove->frameNumber = TO_LE_16(0xffff); +					moveOffset += 2; +				} + +				WRITE_LE_UINT16(outDirs[dirCtr], startOffset); +			} +		} +	} + +	rec->animRecordId = TO_LE_16(0xffff); +	delete includeAnim; +} + +void read_script_data(byte *&data, uint16 &totalSize) { +	lure_exe.seek(SCRIPT_SEGMENT); +	 +	totalSize = SCRIPT_SEGMENT_SIZE; +	data = (byte *) malloc(totalSize); +	lure_exe.read(data, totalSize); +} + +void read_script2_data(byte *&data, uint16 &totalSize) { +	lure_exe.seek(SCRIPT2_SEGMENT); +	 +	totalSize = SCRIPT2_SEGMENT_SIZE; +	data = (byte *) malloc(totalSize); +	lure_exe.read(data, totalSize); +} + +void read_hotspot_script_offsets(byte *&data, uint16 &totalSize) { +	lure_exe.seek(DATA_SEGMENT + HOTSPOT_SCRIPT_LIST); +	 +	totalSize = HOTSPOT_SCRIPT_SIZE; +	data = (byte *) malloc(totalSize); +	lure_exe.read(data, totalSize); +} + +void read_messages_segment(byte *&data, uint16 &totalSize) { +	lure_exe.seek(MESSAGES_SEGMENT); +	totalSize = MESSAGES_SEGMENT_SIZE; +	data = (byte *) malloc(totalSize); +	lure_exe.read(data, totalSize); +} + +// Reads in the list of actions used + +void read_actions_list(byte *&data, uint16 &totalSize) { +	// Allocate enough space for output action list +	data = (byte *) malloc(MAX_DATA_SIZE); +	HotspotActionsRecord *header = (HotspotActionsRecord *) data; +	uint16 offset = actionIndex * sizeof(HotspotActionsRecord) + sizeof(uint16); + +	for (int ctr = 0; ctr < actionIndex; ++ctr) { +		header->recordId = actionOffsets[ctr]; +		header->offset = offset; +		++header; + +		lure_exe.seek(DATA_SEGMENT + actionOffsets[ctr]); +		uint16 *numItems = (uint16 *) (data + offset); +		lure_exe.read(numItems, sizeof(uint16)); +		offset += 2; + +		if (READ_UINT16(numItems) > 0) { +			lure_exe.read(data + offset, READ_UINT16(numItems) * 3); +			offset += READ_UINT16(numItems) * 3; +		} +	} +	header->recordId = TO_LE_16(0xffff); +} + +// Reads in the talk data  + +#define TALK_OFFSET 0x505c +#define TALK_NUM_ENTRIES 28 +#define MAX_TALK_LISTS 300 + +uint16 talkOffsets[MAX_TALK_LISTS]; +int talkOffsetIndex = 0; + +void add_talk_offset(uint16 offset) { +	for (int ctr = 0; ctr < talkOffsetIndex; ++ctr)  +		if (talkOffsets[ctr] == offset) return; +	if (talkOffsetIndex == MAX_TALK_LISTS) { +		printf("Exceeded maximum talk offset list size\n"); +		exit(1); +	} + +	talkOffsets[talkOffsetIndex++] = offset; +} + +struct TalkEntry { +	uint16 hotspotId; +	uint16 offset; +}; + +void read_talk_headers(byte *&data, uint16 &totalSize) { +	TalkEntry entries[TALK_NUM_ENTRIES]; +	uint16 sortedOffsets[TALK_NUM_ENTRIES+1]; +	int entryCtr, subentryCtr; + +	lure_exe.seek(DATA_SEGMENT + TALK_OFFSET); +	lure_exe.read(&entries[0], sizeof(TalkEntry) * TALK_NUM_ENTRIES); + +	// Sort the entry offsets into a list - this is used to figure out each entry's size +	int currVal, prevVal = 0; +	for (entryCtr = 0; entryCtr < TALK_NUM_ENTRIES; ++entryCtr) { +		currVal = 0xffff; +		for (subentryCtr = 0; subentryCtr < TALK_NUM_ENTRIES; ++subentryCtr) { +			if ((FROM_LE_16(entries[subentryCtr].offset) < currVal) && +				(FROM_LE_16(entries[subentryCtr].offset) > prevVal))  +				currVal = FROM_LE_16(entries[subentryCtr].offset); +		} +		if (currVal == 0xffff) break; + +		sortedOffsets[entryCtr] = currVal; +		prevVal = currVal; +	} +	sortedOffsets[entryCtr] = 0x5540; // end for end record + +	data = (byte *) malloc(MAX_DATA_SIZE); +	TalkEntry *entry = (TalkEntry *) data; +	uint16 offset = TALK_NUM_ENTRIES * sizeof(TalkEntry) + sizeof(uint16); + +	for (int entryCtr = 0; entryCtr < TALK_NUM_ENTRIES; ++entryCtr) { +		entry->hotspotId = entries[entryCtr].hotspotId; +		entry->offset = TO_LE_16(offset); +		++entry; + +		// Find the following offset in a sorted list +		int startOffset = FROM_LE_16(entries[entryCtr].offset); +		int nextOffset = 0; +		for (subentryCtr = 0; subentryCtr < TALK_NUM_ENTRIES; ++subentryCtr) { +			if (sortedOffsets[subentryCtr] == startOffset) { +				nextOffset = sortedOffsets[subentryCtr+1]; +				break; +			} +		} +		if (nextOffset == 0)  +			exit(1); + +		// Read in line entries into the data +		lure_exe.seek(DATA_SEGMENT + startOffset); +		int size = nextOffset - startOffset; +		uint16 *talkOffset = (uint16 *) (data + offset); +		lure_exe.read(talkOffset, size); + +		while (size > 0) { +			if (READ_UINT16(talkOffset) != 0)  +				add_talk_offset(READ_UINT16(talkOffset)); +			size -= sizeof(uint16); +			offset += sizeof(uint16); +			talkOffset++; +		} + +		WRITE_LE_UINT16(talkOffset, 0xffff); +		offset += 2; +	} + +	add_talk_offset(0xffff); +	entry->hotspotId = TO_LE_16(0xffff); +	totalSize = offset + 2; +} + +// Reads in the contents of the previously loaded talk lists + +struct TalkRecord { +	uint16 recordId; +	uint16 listOffset; +	uint16 responsesOffset; +}; + +uint16 giveTalkIds[6] = {0xCF5E, 0xCF14, 0xCF90, 0xCFAA, 0xCFD0, 0xCFF6}; + +void read_talk_data(byte *&data, uint16 &totalSize) { +	uint16 responseOffset; +	int talkCtr, subentryCtr; +	uint16 size; + +	for (talkCtr = 0; talkCtr < 6; ++talkCtr) +		add_talk_offset(giveTalkIds[talkCtr]); + +	data = (byte *) malloc(MAX_DATA_SIZE); +	TalkRecord *header = (TalkRecord *) data; +	uint16 offset = talkOffsetIndex * sizeof(TalkRecord) + sizeof(uint16); + +	uint16 *sortedList = (uint16 *) malloc((talkOffsetIndex+1) * sizeof(uint16)); +	memset(sortedList, 0, (talkOffsetIndex+1) * sizeof(uint16)); + +	// Sort the entry offsets into a list - this is used to figure out each entry's size +	int currVal, prevVal = 0; +	for (talkCtr = 0; talkCtr < talkOffsetIndex; ++talkCtr) { +		currVal = 0xffff; +		for (subentryCtr = 0; subentryCtr < talkOffsetIndex; ++subentryCtr) { +			if ((talkOffsets[subentryCtr] < currVal) && +				(talkOffsets[subentryCtr] > prevVal))  +				currVal = talkOffsets[subentryCtr]; +		} +		if (currVal == 0xffff) break; + +		sortedList[talkCtr] = currVal; +		prevVal = currVal; +	} +	sortedList[talkCtr] = 0xf010; +	int numTalks = talkCtr; + +	// Loop through the talk list + +	for (talkCtr = 0; talkCtr < numTalks; ++talkCtr) { +		uint16 startOffset = sortedList[talkCtr]; +		uint16 nextOffset = sortedList[talkCtr+1]; + +		header->recordId = startOffset; +		header->listOffset = offset; +		 +		lure_exe.seek(DATA_SEGMENT + startOffset); +		responseOffset = lure_exe.readWord(); +		startOffset += 2; + +		// Special handling for entry at 0d930h +		if (responseOffset == 0x8000) continue; + +		// Calculate talk data size - if response is within record range, +		// use simple calculation size. Otherwise, read in full data until +		// end of record +		if ((responseOffset < startOffset) || (responseOffset >= nextOffset)) +			size = nextOffset - startOffset; +		else +			size = responseOffset - startOffset; +		if ((size % 6) == 2) size -= 2; +		if ((size % 6) != 0) { +			printf("Failure reading talk data\n"); +			exit(1); +		} + +		// Read in the list of talk entries +		lure_exe.read(data + offset, size); +		offset += size; +		memset(data + offset, 0xff, 2); +		offset += 2; + +		// Handle the response data +		header->responsesOffset = offset; + +		// Scan through the list of record offsets and find the offset of +		// the following record. This is done because although the talk +		// records and responses are normally sequential, it can also +		// point into another record's talk responses + +		nextOffset = 0; +		for (subentryCtr = 0; subentryCtr < numTalks; ++subentryCtr) { +			if ((responseOffset >= sortedList[subentryCtr]) && +				(responseOffset < sortedList[subentryCtr+1])) { +				// Found a record				 +				nextOffset = sortedList[subentryCtr+1]; +				break; +			} +		} +		if (nextOffset < responseOffset) { +			printf("Failure reading talk data\n"); +			exit(1); +		} +		 +		size = nextOffset - responseOffset; +		if ((size % 6) != 0) size -= (size % 6); + +		if ((size % 6) != 0) { +			printf("Failure reading talk data\n"); +			exit(1); +		} + +		lure_exe.read(data + offset, size); +		offset += size; +		WRITE_LE_UINT16(data + offset, 0xffff); +		offset += 2; + +		++header; +	} + +	header->recordId = TO_LE_16(0xffff); +	totalSize = offset; +	free(sortedList); +} + +void read_room_pathfinding_data(byte *&data, uint16 &totalSize) { +	lure_exe.seek(DATA_SEGMENT + PATHFIND_OFFSET); +	 +	totalSize = PATHFIND_SIZE; +	data = (byte *) malloc(totalSize); +	lure_exe.read(data, totalSize); +} + +void read_room_exit_coordinate_data(byte *&data, uint16 &totalSize)  +{ +	// Read in the exit coordinates list	 +	int roomNum, entryNum; + +	totalSize = EXIT_COORDINATES_NUM_ROOMS * sizeof(RoomExitCoordinateEntryResource) + 2;  +	data = (byte *) malloc(totalSize); +	lure_exe.seek(DATA_SEGMENT + EXIT_COORDINATES_OFFSET); +	lure_exe.read(data, totalSize - 2); +	WRITE_LE_UINT16(data + totalSize - 2, 0xffff); + +	// Post process the list to adjust data +	RoomExitCoordinateEntryResource *rec = (RoomExitCoordinateEntryResource *) data; +	for (roomNum = 0; roomNum < EXIT_COORDINATES_NUM_ROOMS; ++roomNum, ++rec) { +		for (entryNum = 0; entryNum < ROOM_EXIT_COORDINATES_NUM_ENTRIES; ++entryNum) { +			if ((rec->entries[entryNum].x != 0) || (rec->entries[entryNum].y != 0)) { +				rec->entries[entryNum].x = TO_LE_16(FROM_LE_16(rec->entries[entryNum].x) - 0x80); +				uint16 tempY = FROM_LE_16(rec->entries[entryNum].y); +				rec->entries[entryNum].y = TO_LE_16( +					((tempY & 0xfff) - 0x80) | (tempY & 0xf000)); +			} +		} + +		for (entryNum = 0; entryNum < ROOM_EXIT_COORDINATES_ENTRY_NUM_ROOMS; ++entryNum) { +			rec->roomIndex[entryNum] = TO_LE_16(FROM_LE_16(rec->roomIndex[entryNum]) / 6); +		} +	} +} + +void read_room_exit_hotspots_data(byte *&data, uint16 &totalSize) { +	totalSize = 0; +	data = (byte *) malloc(MAX_DATA_SIZE); + +	RoomExitIndexedHotspotResource *rec = (RoomExitIndexedHotspotResource *) data; +	lure_exe.seek(DATA_SEGMENT + EXIT_HOTSPOTS_OFFSET); + +	lure_exe.read(rec, sizeof(RoomExitIndexedHotspotResource)); +	while (FROM_LE_16(rec->roomNumber) != 0) { +		++rec; +		totalSize += sizeof(RoomExitIndexedHotspotResource); +		lure_exe.read(rec, sizeof(RoomExitIndexedHotspotResource)); +	} + +	WRITE_LE_UINT16(rec, 0xffff); +	totalSize += sizeof(uint16); +} + +void getEntry(uint8 entryIndex, uint16 &resourceId, byte *&data, uint16 &size) +{ +	resourceId = 0x3f01 + entryIndex; + +	switch (entryIndex)  +	{ +	case 0: +		// Copy the default palette to file +		read_basic_palette(data, size); +		break; + +	case 1: +		// Copy the replacement palette fragments to file +		read_replacement_palette(data, size); +		break; + +	case 2: +		// Copy the dialog segment data into the new vga file +		read_dialog_data(data, size); +		break; + +	case 3: +		// Copy the talk dialog segment data into the new vga file +		read_talk_dialog_data(data, size); +		break; + +	case 4: +		// Get the room info data +		read_room_data(data, size); +		break; + +	case 5: +		// Get the action sequence set for NPC characters +		read_action_sequence(data, size); +		break; + +	case 6: +		// Get the hotspot info data +		read_hotspot_data(data, size); +		break; + +	case 7: +		// Get the hotspot override info data +		read_hotspot_override_data(data, size); +		break; + +	case 8: +		// Get the list of room exits +		read_room_exits(data, size); +		break; + +	case 9: +		// Get the list of room exit joins +		read_room_exit_joins(data, size); +		break; + +	case 10: +		// Get the hotspot animation record data +		read_anim_data(data, size); +		break; + +	case 11: +		// Get the script segment data +		read_script_data(data, size); +		break; + +	case 12: +		// Get the second script segment data +		read_script2_data(data, size); +		break; + +	case 13: +		// Get a list of hotspot script offsets +		read_hotspot_script_offsets(data, size); +		break; + +	case 14: +		// Get the messages segment  +		read_messages_segment(data, size); +		break; + +	case 15: +		// Get the actions list +		read_actions_list(data, size); +		break; + +	case 16: +		// Get the talk header information +		read_talk_headers(data, size); +		break; + +	case 17: +		// Get the talk data +		read_talk_data(data, size); +		break; + +	case 18: +		// Get the pathfinding data +		read_room_pathfinding_data(data, size); +		break; + +	case 19: +		// Get the room exit coordinate list +		read_room_exit_coordinate_data(data, size); +		break; + +	case 20: +		// Read the room exit hotspot list +		read_room_exit_hotspots_data(data, size); +		break; + +	default: +		data = NULL; +		size = 0; +		resourceId = 0xffff; +		break; +	} +} + +void createFile(const char *outFilename) +{ +	FileEntry rec; +	uint32 startOffset, numBytes; +	uint16 resourceId; +	uint16 resourceSize; +	byte *resourceData; +	bool resourceFlag; +	byte tempBuffer[32]; + +	File f; +	f.open(outFilename, kFileWriteMode); +	memset(tempBuffer, 0, 32); + +	// Write header +	f.write("heywow", 6); +	f.writeWord(0); + +	startOffset = 0x600; +	resourceFlag = true; +	for (int resIndex=0; resIndex<0xBE; ++resIndex) +	{ +		resourceData = NULL; + +		// Get next data entry +		if (resourceFlag) +			// Get resource details +			getEntry(resIndex, resourceId, resourceData, resourceSize);  + +		// Write out the next header entry +		f.seek(8 + resIndex * 8); +		if (resourceSize == 0)  +		{ +			// Unused entry +			memset(&rec, 0xff, sizeof(FileEntry)); +			resourceFlag = false; +		} +		else +		{ +			rec.id = TO_LE_16(resourceId); +			rec.offset = TO_LE_16(startOffset >> 5); +			rec.sizeExtension = (uint8) ((resourceSize >> 16) & 0xff); +			rec.size = TO_LE_16(resourceSize & 0xffff); +			rec.unused = 0xff; +		} + +		f.write(&rec, sizeof(FileEntry)); + +		// Write out the resource +		if (resourceFlag) +		{ +			f.seek(startOffset); +			f.write(resourceData, resourceSize); +			startOffset += resourceSize; +			free(resourceData);		// Free the data block + +			// Write out enough bytes to move to the next 32 byte boundary +			numBytes = 0x20 * ((startOffset + 0x1f) / 0x20) - startOffset; +			if (numBytes != 0)  +			{ +				f.write(tempBuffer, numBytes); +				startOffset += numBytes; +			} +		} +	} + +	// Store file version in final file slot +	f.seek(0xBF * 8); +	FileEntry fileVersion; +	memset(&fileVersion, 0xff, sizeof(FileEntry)); +	fileVersion.unused = VERSION_MAJOR; +	fileVersion.sizeExtension = VERSION_MINOR; +	f.write(&fileVersion, sizeof(FileEntry)); + +	f.close(); +} + +// validate_executable +// Validates that the correct executable is being used to generate the +// resource file. Eventually the resource file creator will need to work +// with the other language executables, but for now just make  + +#define NUM_BYTES_VALIDATE 1024 +#define FILE_CHECKSUM 64880 + +void validate_executable() { +	uint32 sumTotal = 0; +	byte buffer[NUM_BYTES_VALIDATE]; +	lure_exe.read(buffer, NUM_BYTES_VALIDATE); +	for (int ctr = 0; ctr < NUM_BYTES_VALIDATE; ++ctr)  +		sumTotal += buffer[ctr]; + +	if (sumTotal != FILE_CHECKSUM) { +		printf("Lure executable not correct English version\n"); +		exit(1); +	} +} + + +int main(int argc, char *argv[]) +{ +	const char *inFilename = (argc >= 2) ? argv[1] : "f:\\games\\lure\\lure.exe"; +	const char *outFilename = (argc == 3) ? argv[2] : "f:\\games\\lure\\lure.dat"; + +	if (!lure_exe.open(inFilename)) +	{ +		if (argc == 1)  +			printf("Format: %s input_exe_filename output_filename\n"); +		else +			printf("Could not open file: %s\n", inFilename); +	}  +	else  +	{ +		validate_executable(); +		createFile(outFilename);	 +		lure_exe.close(); +	} +} diff --git a/tools/create_lure/create_lure_dat.h b/tools/create_lure/create_lure_dat.h new file mode 100644 index 0000000000..ffdde22053 --- /dev/null +++ b/tools/create_lure/create_lure_dat.h @@ -0,0 +1,401 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2005-2006 The ScummVM project + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#ifndef __createlure_dat__ +#define __createlure_dat__ + +#include "common/stdafx.h" +#include "common/endian.h" + +#define VERSION_MAJOR 1 +#define VERSION_MINOR 10 +#define ENGLISH_LURE  + +#define DATA_SEGMENT 0xac50 + +#define DIALOG_OFFSET 0x1dcb0 +#define DIALOG_SIZE 0x150 + +#define TALK_DIALOG_OFFSET 0x1de00 +#define TALK_DIALOG_SIZE 0x30 + +#define PALETTE_OFFSET 0xc0a7 +#define PALETTE_SIZE 0x300 + +#define ROOM_TABLE 0xbf40 +#define ROOM_NUM_ENTRIES 51 + +#define HOTSPOT_OVERRIDE_OFFSET 0x2A01 + +#define SCRIPT_SEGMENT 0x1df00 +#define SCRIPT_SEGMENT_SIZE 0x2c57 +#define SCRIPT2_SEGMENT 0x19c70 +#define SCRIPT2_SEGMENT_SIZE 0x2800 + +#define HOTSPOT_SCRIPT_LIST 0x57e0 +#define HOTSPOT_SCRIPT_SIZE 0x30 + +#define MAX_NUM_ANIM_RECORDS 0x200 +#define MAX_NUM_ACTION_RECORDS 0x100 + +#define ROOM_EXITS_OFFSET 0x2f61 +#define NUM_ROOM_EXITS 50 +#define ROOM_EXIT_JOINS_OFFSET 0xce30 + +#define MESSAGES_SEGMENT 0x20b60 +#define MESSAGES_SEGMENT_SIZE 0x490 + +#define MAX_HOTSPOTS 0x100 +#define MAX_DATA_SIZE 0x4000 + +#define PATHFIND_OFFSET 0x984A +#define PATHFIND_SIZE (120 * ROOM_NUM_ENTRIES)  + +#define HOTSPOT_WALK_TO_OFFSET 0xBC4B +#define EXIT_COORDINATES_OFFSET 0x1929 +#define EXIT_COORDINATES_NUM_ROOMS 49 + +#define TABLED_ACTIONS_OFFSET 0x1380 +#define NUM_TABLED_ACTION_BLOCKS 18 + +#define WALK_AREAS_OFFSET 0x2EB1 + +#define EXIT_HOTSPOTS_OFFSET 0x2E57 + +#pragma pack(1) + +// Rect currently copied from common/rect.h - if I try directly including it, +// the link complains about an unresolved external token Common.String.__dtor + +struct Rect { +	int16 top, left;		//!< The point at the top left of the rectangle (part of the rect). +	int16 bottom, right;	//!< The point at the bottom right of the rectangle (not part of the rect). +}; + +struct FileEntry { +	uint16 id; +	byte unused; +	byte sizeExtension; +	uint16 size; +	uint16 offset; +}; + +struct RoomHeaderEntry { +	uint16 offset; +	uint16 roomNumber; +	uint16 descId; +	byte unused[3]; +}; + +struct HotspotHeaderEntry { +	uint16 offset; +	uint16 resourceId; +	uint16 descId, descId2; +	byte hdrFlags; +}; + +struct HotspotResource { +	uint32 actions; +	uint16 actionsOffset; +	uint16 roomNumber; +	byte scriptLoadFlag; +	uint16 loadOffset; +	uint16 unused; +	uint16 startX; +	uint16 startY; +	uint16 width; +	uint16 height; +	byte layer; +	byte flags; +	uint16 tickProcOffset; +	uint16 widthCopy; +	uint16 heightCopy; +	uint16 yCorrection; +	uint16 tickTimeout; +	uint16 animOffset; +	byte colourOffset; +	uint16 sequenceOffset; +	byte unknown4[15]; +	int8 talkX; +	int8 talkY; +	byte unused5[11]; +	uint16 delayCtr; +	uint8 characterMode; +	uint16 tickSequenceOffset; +}; + +struct CurrentActionInput { +	uint8 action; +	uint16 dataOffset; +	uint16 roomNumber; +}; + +struct HotspotResourceOutput { +	uint16 hotspotId; +	uint16 nameId; +	uint16 descId; +	uint16 descId2; +	uint32 actions; +	uint16 actionsOffset; +	uint16 roomNumber; +	byte layer; +	byte scriptLoadFlag; +	uint16 loadOffset; +	uint16 startX; +	uint16 startY; +	uint16 width; +	uint16 height; +	uint16 widthCopy; +	uint16 heightCopy; +	uint16 yCorrection; +	int16 walkX; +	uint16 walkY; +	int8 talkX; +	int8 talkY; +	uint16 colourOffset; +	uint16 animRecordId; +	uint16 sequenceOffset; +	uint16 tickProcOffset; +	uint16 tickTimeout; +	uint16 tickSequenceOffset; +	uint16 npcSchedule; +	uint16 characterMode; +	uint16 delayCtr; +	byte flags; +	byte hdrFlags; +}; + +struct RoomResource { +	byte unknown1[6]; +	uint16 pixelListOffset; +	byte numLayers; +	uint16 layers[4]; +	uint16 sequenceOffset; +	byte unknown3[5]; +	uint8 walkBoundsIndex; +	int16 clippingXStart; +	int16 clippingXEnd; +}; + +struct RoomRectIn { +	uint16 xs, xe; +	uint16 ys, ye; +}; + +struct RoomRectOut { +	int16 xs, xe; +	int16 ys, ye; +}; + +struct RoomResourceOutput { +	uint16 roomNumber; +	uint16 descId; +	uint16 numLayers; +	uint16 layers[4]; +	uint16 sequenceOffset; +	int16 clippingXStart; +	int16 clippingXEnd; +	RoomRectOut walkBounds; +	uint16 numExits; +}; + +struct RoomResourceExit1 { +	int16 xs, xe, ys, ye; +	uint16 sequenceOffset; +}; + +struct RoomResourceExit2 { +	uint8 newRoom; +	uint8 direction; +	int16 newRoomX, newRoomY; +}; + +struct HotspotOverride { +	uint16 hotspotId; +	uint16 xs, xe, ys, ye; +}; + +struct AnimRecord { +	uint16 animId; +	uint8 flags; +	uint8 unused[6]; +	uint16 upOffset; +	uint16 downOffset; +	uint16 leftOffset; +	uint16 rightOffset; +	uint8 upFrame; +	uint8 downFrame; +	uint8 leftFrame; +	uint8 rightFrame; +}; + +struct AnimRecordOutput { +	uint16 animRecordId; +	uint16 animId; +	uint16 flags; +	uint16 upOffset; +	uint16 downOffset; +	uint16 leftOffset; +	uint16 rightOffset; +	uint8 upFrame; +	uint8 downFrame; +	uint8 leftFrame; +	uint8 rightFrame; +}; + +struct MovementRecord { +	uint16 frameNumber; +	int16 xChange; +	int16 yChange; +}; + +struct RoomExitHotspotRecord { +	int16 xs, xe; +	int16 ys, ye; +	uint16 cursorNum; +	uint16 hotspotId; +	uint16 destRoomNumber; +}; + +struct RoomExitHotspotOutputRecord { +	uint16 hotspotId; +	int16 xs, xe; +	int16 ys, ye; +	uint16 cursorNum; +	uint16 destRoomNumber; +}; + +struct RoomExitHotspotJoinRecord { +	uint16 hotspot1Id; +	byte h1CurrentFrame; +	byte h1DestFrame; +	byte h1OpenSound; +	byte h1CloseSound; +	uint16 hotspot2Id; +	byte h2CurrentFrame; +	byte h2DestFrame; +	byte h2OpenSound; +	byte h2CloseSound; +	byte blocked; +}; + +struct HotspotActionSequenceRecord { +	byte actionNumber; +	uint16 sequenceOffset; +}; + +struct HotspotActionsRecord { +	uint16 recordId; +	uint16 offset; +}; + +struct RoomExitCoordinateResource { +	int16 x; +	int16 y; +	uint16 roomNumber; +}; + +struct HotspotWalkToRecord { +	uint16 hotspotId; +	int16 x; +	uint16 y; +}; + +struct RoomExitIndexedHotspotResource { +	uint8 roomNumber; +	uint8 hotspotIndex; +	uint16 hotspotId; +}; + + +#define ROOM_EXIT_COORDINATES_NUM_ENTRIES 6 +#define ROOM_EXIT_COORDINATES_ENTRY_NUM_ROOMS 52 + +struct RoomExitCoordinateEntryResource { +	RoomExitCoordinateResource entries[ROOM_EXIT_COORDINATES_NUM_ENTRIES]; +	uint8 roomIndex[ROOM_EXIT_COORDINATES_ENTRY_NUM_ROOMS]; +}; + +enum CurrentAction {NO_ACTION, START_WALKING, DISPATCH_ACTION, EXEC_HOTSPOT_SCRIPT,  +	PROCESSING_PATH, WALKING}; + +extern void read_action_sequence(byte *&data, uint16 &totalSize); + +extern uint16 get_sequence_index(uint16 offset, int supportIndex = -1); + +enum AccessMode { +	kFileReadMode = 1, +	kFileWriteMode = 2 +}; + +class File { +private: +	FILE *f; +public: +	bool open(const char *filename, AccessMode mode = kFileReadMode) { +		f = fopen(filename, (mode == kFileReadMode) ? "rb" : "wb"); +		return (f != NULL); +	} +	void close() { +		fclose(f); +		f = NULL; +	} +	int seek(int32 offset, int whence = SEEK_SET) { +		return fseek(f, offset, whence); +	} +	long read(void *buffer, int len) { +		return fread(buffer, 1, len, f); +	} +	void write(const void *buffer, int len) { +		fwrite(buffer, 1, len, f); +	} +	byte readByte() {  +		byte v; +		read(&v, sizeof(byte)); +		return v; +	} +	uint16 readWord() { +		uint16 v; +		read(&v, sizeof(uint16)); +		return FROM_LE_16(v); +	} +	uint16 readLong() { +		uint32 v; +		read(&v, sizeof(uint32)); +		return FROM_LE_32(v); +	} +	void writeByte(byte v) {  +		write(&v, sizeof(byte)); +	} +	void writeWord(uint16 v) {  +		uint16 vTemp = TO_LE_16(v); +		write(&vTemp, sizeof(uint16)); +	} +	void writeLong(uint32 v) {  +		uint32 vTemp = TO_LE_32(v); +		write(&vTemp, sizeof(uint32)); +	} +}; + +extern File lure_exe; + +#endif diff --git a/tools/create_lure/process_actions.cpp b/tools/create_lure/process_actions.cpp new file mode 100644 index 0000000000..681d43283f --- /dev/null +++ b/tools/create_lure/process_actions.cpp @@ -0,0 +1,341 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2005-2006 The ScummVM project + * + * 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 2 + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#include "common/stdafx.h" +#include "common/scummsys.h" +#include "create_lure_dat.h" + +enum Action { +	GET = 1, PUSH = 3, PULL = 4, OPERATE = 5, OPEN = 6,	CLOSE = 7, LOCK = 8, +	UNLOCK = 9,	USE = 10, GIVE = 11, TALK_TO = 12, TELL = 13, BUY = 14, +	LOOK = 15, LOOK_AT = 16, LOOK_THROUGH = 17,	ASK = 18, DRINK = 20, +	STATUS = 21, GO_TO = 22, RETURN = 23, BRIBE = 24, EXAMINE = 25, +	NPC_SET_ROOM_AND_BLOCKED_OFFSET = 28, NPC_UNKNOWN1 = 29, NPC_EXEC_SCRIPT = 30,  +	NPC_UNKNOWN2 = 31, NPC_SET_RAND_DEST = 32, NPC_WALKING_CHECK = 33,  +	NPC_SET_SUPPORT_OFFSET = 34, NPC_SUPPORT_OFFSET_COND = 35,  +	NPC_DISPATCH_ACTION = 36, NPC_UNKNOWN3 = 37, NPC_UNKNOWN4 = 38,  +	NPC_START_TALKING = 39, NPC_JUMP_ADDRESS = 40, +	NONE = 0 +}; + +struct CurrentActionOutput { +	uint8 action; +	uint8 hsAction; +	uint16 roomNumber; +	uint16 hotspotId; +	uint16 usedId; +}; + +int numParams[NPC_JUMP_ADDRESS+1] = {0,  +	1, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 2, 0, 1, +	0, 1, 1, 1, 1, 0, 0, 2, 0, 1, 0, 0, 1, 1, 2, 2, 5, 2, 2, 1}; + +#ifdef ENGLISH_LURE +#define NUM_JUMP_OFFSETS 2 +uint16 jumpOffsets[2] = {0x87be, 0x881c}; +#endif + +#define MAX_BUFFER_ENTRIES 50 +#define MAX_INSTRUCTION_ENTRIES 300 +#define SCHEDULE_DATA_OFFSET 0x60 + +struct SupportStructure { +	uint16 offset; +	int numInstructions; +	uint16 instructionOffsets[MAX_INSTRUCTION_ENTRIES];	 +	uint16 resourceOffset; +}; + +SupportStructure supportList[MAX_BUFFER_ENTRIES]; +uint16 numSupportEntries = 0; + +#define FORWARD_JUMP_ALLOWANCE 0x30 + + +uint16 get_sequence_index(uint16 offset, int supportIndex) { +	int index; + +	if (supportIndex != -1) { +		// Within a sequence, so if an offset is within it, it's a local jump +		for (index = 0; index < supportList[supportIndex].numInstructions; ++index) { +			if (supportList[supportIndex].instructionOffsets[index] == offset) +				return index; +		} +	} + +	for (int index = 0; index <= numSupportEntries; ++index) { +		SupportStructure &rec = supportList[index]; + +		if ((rec.numInstructions > 0) && +			(offset >= rec.instructionOffsets[0]) &&  +			(offset <= rec.instructionOffsets[rec.numInstructions - 1])) { +			// Scan through the entry's insruction list +			for (int iIndex = 0; iIndex < rec.numInstructions; ++iIndex) { +				if (rec.instructionOffsets[iIndex] == offset) { +					return ((index + 1) << 10) | iIndex; +				} +			} +		} +	} + +	return 0xffff; +} + +struct SymbolTableEntry { +	uint16 *p; +	bool globalNeeded; +}; + +uint16 process_action_sequence_entry(int supportIndex, byte *data, uint16 remainingSize) { +	SupportStructure &rec = supportList[supportIndex]; +	uint16 startOffset = rec.offset; +	uint16 maxOffset = 0; +#ifdef ENGLISH_LURE +	if (startOffset == 0x7dcb) { startOffset = 0x7d9d; maxOffset = 0x7dcb; } +	if (startOffset == 0x7248) { startOffset = 0x71ce; maxOffset = 0x7248; } +	if (startOffset == 0x79a8) { startOffset = 0x785c; maxOffset = 0x79a8; } +	if (startOffset == 0x6f4f) { startOffset = 0x6e5d; maxOffset = 0x6fe5; } +	if (startOffset == 0x734a) maxOffset = 0x77a2; +#endif + +	SymbolTableEntry symbolTable[MAX_INSTRUCTION_ENTRIES]; +	uint16 numSymbols = 0; +	uint16 offset = startOffset; +	uint16 totalSize = 0; +	uint16 actionNum = 0; +	uint16 paramIndex; +	uint16 params[5]; +	uint16 index; +	uint16 *pOut = (uint16 *) data; + +	lure_exe.seek(DATA_SEGMENT + startOffset); +	rec.numInstructions = 0; + +	for (;;) { +		if (remainingSize < 10) { +			printf("Ran out of space to process NPC action sequences\n"); +			exit(1); +		} + +		// Check for end of sequence set with prior instruction +		if ((actionNum == NPC_SET_SUPPORT_OFFSET) && ((maxOffset == 0) || +			(offset > maxOffset))) +			break; + +		// Mark the offset of the next instruction +		rec.instructionOffsets[rec.numInstructions++] = offset; +		if (rec.numInstructions == MAX_INSTRUCTION_ENTRIES) { +			printf("A method exceeded the maximum allowable number of instructions\n"); +			exit(1); +		} + +		// Get in the next action +		actionNum = lure_exe.readWord(); + +//		printf("%xh - action=%d", offset, actionNum); + +		if (actionNum == 0) { +			// At end of script block +//			printf("\n"); +			break; +		} +		else if (actionNum > NPC_JUMP_ADDRESS) { +			// Unknown action code - halt execution +			printf("%xh - unknown action %d\n", offset, actionNum); +			exit(1); +		} + +		*pOut++ = TO_LE_16(actionNum); + +		// Read in any action parameters +		for (int paramCtr = 0; paramCtr < numParams[actionNum]; ++paramCtr) +			params[paramCtr] = lure_exe.readWord(); + +		switch(actionNum) { +		case NPC_SET_ROOM_AND_BLOCKED_OFFSET: +		case NPC_SET_SUPPORT_OFFSET: +		case NPC_SUPPORT_OFFSET_COND: +		case NPC_DISPATCH_ACTION: +			// These instructions have a support record parameter. Store the +			// offset the parameter will be in the output data so we can come +			// back at the end and resolve it +			paramIndex = (actionNum == NPC_SET_SUPPORT_OFFSET) ? 0 : 1; +			symbolTable[numSymbols].globalNeeded = actionNum == NPC_SET_ROOM_AND_BLOCKED_OFFSET; +			symbolTable[numSymbols].p = pOut + paramIndex; +			++numSymbols; + +			// Special check for forward references - it's considered to be in +			// the same block if it's forward within 100h blocks +			if ((params[paramIndex] > offset) &&  +				(params[paramIndex] < offset + FORWARD_JUMP_ALLOWANCE) && +				(params[paramIndex] > maxOffset)) { +				maxOffset = params[paramIndex]; +			}  +			break; + +		case NPC_JUMP_ADDRESS: +			// Make sure the address is in the known list +			index = 0; +			while ((index < NUM_JUMP_OFFSETS) && (jumpOffsets[index] != params[0])) +				++index; +			 +			if (index != NUM_JUMP_OFFSETS) +				// Replace code offset with an index			 +				params[0] = index; +			else { +				printf("Encountered unrecognised NPC code jump point: %xh\n", params[0]); +				exit(1); +			} +			break; + +		default: +			break; +		} + +		// Output parameters +		for (paramIndex = 0; paramIndex < numParams[actionNum]; ++paramIndex) +			*pOut++ = TO_LE_16(params[paramIndex]); + +		// Increase size +		totalSize += (numParams[actionNum] + 1) * sizeof(uint16);  +		offset = startOffset + totalSize; +		remainingSize -= (numParams[actionNum] + 1) * sizeof(uint16); +	} + +	// Flag an end of the sequence +	*pOut++ = 0; +	totalSize += sizeof(uint16); + +	// handle post-processing of the symbol list + +	for (int symbolCtr = 0; symbolCtr < numSymbols; ++symbolCtr) { +		if (READ_LE_UINT16(symbolTable[symbolCtr].p) == 0)  +			// No Id special constant +			WRITE_LE_UINT16(symbolTable[symbolCtr].p, 0xffff); +		else { +			// Handle resolving the constant +			index = get_sequence_index(READ_LE_UINT16(symbolTable[symbolCtr].p),  +				symbolTable[symbolCtr].globalNeeded ? -1 : supportIndex); +//printf("Symbol %xh => %xh\n", *symbolTable[symbolCtr].p, index); +			if (index != 0xffff) { +				// Jump found - so replace symbol entry with it  +				WRITE_LE_UINT16(symbolTable[symbolCtr].p, index); +			} else { +				printf("Sequence contained unknown offset %xh\n",  +					READ_LE_UINT16(symbolTable[symbolCtr].p)); +				exit(1); +			} +		} +	} + +	return totalSize; +} + +void process_entry(uint16 offset, byte *data, uint16 &totalSize) { +	if (get_sequence_index(offset) == 0xffff) { +		// Process the next entry +		supportList[numSupportEntries].offset = offset; +		supportList[numSupportEntries].numInstructions = 0; +		supportList[numSupportEntries].resourceOffset = totalSize; +//printf("process_entry index=%d, offset=%xh\n", numSupportEntries + 1, offset); +		totalSize += process_action_sequence_entry(numSupportEntries,  +			data + totalSize,  MAX_DATA_SIZE - totalSize); + +		++numSupportEntries; +		if (numSupportEntries == MAX_BUFFER_ENTRIES) { +			printf("Ran out of buffer space in processing NPC schedules\n"); +			exit(1); +		} +	} +} + +void read_action_sequence(byte *&data, uint16 &totalSize)  +{ +	const uint16 hsOffset = 0x5d98; +	const uint16 hsStartId = 0x3e8; +	uint16 hotspotIndex; +	HotspotHeaderEntry entryHeader; +	CurrentActionInput action; +	uint16 *pHeader; +	int index; + +	// Allocate enough space for output sequence list +	data = (byte *) malloc(MAX_DATA_SIZE); + +	// Get a list of offsets used in the script engine +	uint16 offsetList[NUM_TABLED_ACTION_BLOCKS]; +	lure_exe.seek(DATA_SEGMENT + TABLED_ACTIONS_OFFSET, SEEK_SET); +	for (index = 0; index < NUM_TABLED_ACTION_BLOCKS; ++index) +		offsetList[index] = lure_exe.readWord(); + +	totalSize = SCHEDULE_DATA_OFFSET; +	numSupportEntries = 0; + +	// Handle required initial entries - the Lure engine refers to them directly by +	// index, so they need to be first, and in that order +#ifdef ENGLISH_LURE +	process_entry(0x13c2, data, totalSize); +	process_entry(0xbb95, data, totalSize); +	process_entry(0x7060, data, totalSize); +	process_entry(0x728a, data, totalSize); +#endif + +	// Process the script engine list  +	 +	for (index = 0; index < NUM_TABLED_ACTION_BLOCKS; ++index)  +		process_entry(offsetList[index], data, totalSize); + +	// Next process each of the character hotspots + +	hotspotIndex = 0; +	for (;;) { +		lure_exe.seek(DATA_SEGMENT + hsOffset +  +			hotspotIndex * sizeof(HotspotHeaderEntry)); +		lure_exe.read(&entryHeader, sizeof(HotspotHeaderEntry)); +		if (FROM_LE_16(entryHeader.offset) == 0xffff) break; +		++hotspotIndex; + +		// Move to the action sequence area of the hotspot +		lure_exe.seek(DATA_SEGMENT + entryHeader.offset + 0x63); +		lure_exe.read(&action, sizeof(CurrentActionInput)); +		if (FROM_LE_16(action.action) == 2)  +			process_entry(FROM_LE_16(action.dataOffset), data, totalSize); +	} + +	// Output the list used in the script engine + +	pHeader = (uint16 *) data; +	for (index = 0; index < NUM_TABLED_ACTION_BLOCKS; ++index)  +		*pHeader++ = TO_LE_16(get_sequence_index(offsetList[index])); +	*pHeader++ = 0xffff; + +	// Output the offsets of each action set + +	for (index = 0; index < numSupportEntries; ++index)  +		*pHeader++ = TO_LE_16(supportList[index].resourceOffset); +	*pHeader++ = 0xffff; + +	if ((int) ((byte *) pHeader - data) > SCHEDULE_DATA_OFFSET) { +		printf("SCHEDULE_DATA_OFFSET was not high enough\n"); +		exit(1); +	} +}	  | 
