aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--tools/create_lure/Makefile4
-rw-r--r--tools/create_lure/create_lure_dat.cpp1147
-rw-r--r--tools/create_lure/create_lure_dat.h401
-rw-r--r--tools/create_lure/process_actions.cpp341
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);
+ }
+}