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); + } +} |