diff options
author | Max Horn | 2011-04-09 23:47:35 +0200 |
---|---|---|
committer | Max Horn | 2011-04-09 23:47:35 +0200 |
commit | 6cf1de87acdb878e3a3e4ef7cc33d45adee4a592 (patch) | |
tree | d20295fc02d514a62ee4f22a5a34136316d0916c /devtools/create_lure | |
parent | ae49865e9e48b8569922d2ea1792541fb23b4a64 (diff) | |
download | scummvm-rg350-6cf1de87acdb878e3a3e4ef7cc33d45adee4a592.tar.gz scummvm-rg350-6cf1de87acdb878e3a3e4ef7cc33d45adee4a592.tar.bz2 scummvm-rg350-6cf1de87acdb878e3a3e4ef7cc33d45adee4a592.zip |
DEVTOOLS: Renamed 'tools' directory to 'devtools'
Diffstat (limited to 'devtools/create_lure')
-rw-r--r-- | devtools/create_lure/Makefile | 4 | ||||
-rw-r--r-- | devtools/create_lure/create_lure_dat.cpp | 1964 | ||||
-rw-r--r-- | devtools/create_lure/create_lure_dat.h | 432 | ||||
-rw-r--r-- | devtools/create_lure/dists/msvc8/create_lure.sln | 20 | ||||
-rw-r--r-- | devtools/create_lure/dists/msvc8/create_lure.vcproj | 222 | ||||
-rw-r--r-- | devtools/create_lure/dists/msvc8_to_msvc9.bat | 32 | ||||
-rw-r--r-- | devtools/create_lure/dists/msvc9/create_lure.sln | 20 | ||||
-rw-r--r-- | devtools/create_lure/dists/msvc9/create_lure.vcproj | 223 | ||||
-rw-r--r-- | devtools/create_lure/dists/msvc9_to_msvc8.bat | 33 | ||||
-rw-r--r-- | devtools/create_lure/module.mk | 14 | ||||
-rw-r--r-- | devtools/create_lure/process_actions.cpp | 544 |
11 files changed, 3508 insertions, 0 deletions
diff --git a/devtools/create_lure/Makefile b/devtools/create_lure/Makefile new file mode 100644 index 0000000000..1ed33b594c --- /dev/null +++ b/devtools/create_lure/Makefile @@ -0,0 +1,4 @@ +# $Id$ + +all: + g++ -I../.. create_lure_dat.cpp process_actions.cpp -o create_lure diff --git a/devtools/create_lure/create_lure_dat.cpp b/devtools/create_lure/create_lure_dat.cpp new file mode 100644 index 0000000000..ad883b808f --- /dev/null +++ b/devtools/create_lure/create_lure_dat.cpp @@ -0,0 +1,1964 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 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 different language + * version of the Lure of the Temptress lure.exe executable files into a new file + * lure.dat - this file is required for the ScummVM Lure of the Temptress module + * to work properly + */ + +// Disable symbol overrides so that we can use system headers. +#define FORBIDDEN_SYMBOL_ALLOW_ALL + +// HACK to allow building with the SDL backend on MinGW +// see bug #1800764 "TOOLS: MinGW tools building broken" +#ifdef main +#undef main +#endif // main + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "create_lure_dat.h" + +using namespace Common; + +File outputFile, lureExe; +Common::Language language; +uint16 dataSegment; + +#define NUM_BYTES_VALIDATE 1024 +#define ENGLISH_FILE_CHECKSUM 0xFD70 +#define ITALIAN_FILE_CHECKSUM 0x109AD +#define FRENCH_FILE_CHECKSUM 0xD38C +#define GERMAN_FILE_CHECKSUM 0xD143 +#define SPANISH_FILE_CHECKSUM 0xFFDA + +Common::Language processedLanguages[100]; +int langIndex = 0; +uint16 animOffsets[MAX_NUM_ANIM_RECORDS]; +int animIndex = 0; +uint16 actionOffsets[MAX_NUM_ACTION_RECORDS]; +int actionIndex = 0; + +#define TALK_NUM_ENTRIES 28 +#define MAX_TALK_LISTS 300 + +uint16 talkOffsets[MAX_TALK_LISTS]; +int talkOffsetIndex = 0; + +#define NUM_LANGUAGES 5 +struct AnimListRecord { + uint16 languages[NUM_LANGUAGES]; +}; + +AnimListRecord animDataList[] = { + {{0x1830, 0x1830, 0x1830, 0x1830, 0x1830}}, // Copy protection header + {{0x1839, 0x1839, 0x1839, 0x1839, 0x1839}}, // Copy protection wording header + {{0x1842, 0x1842, 0x1842, 0x1842, 0x1842}}, // Copy protection numbers + {{0x184B, 0x184B, 0x184B, 0x184B, 0x184B}}, // Restart/Restore buttons + {{0x55C0, 0x5680, 0x56A0, 0x56D0, 0x56a0}}, // Player midswing animation + {{0x55C9, 0x5689, 0x56A9, 0x56D9, 0x56a9}}, // Player mid-level defend + {{0x55D2, 0x5692, 0x56B2, 0x56E2, 0x56b2}}, // Player high-level strike + {{0x55DB, 0x569B, 0x56BB, 0x56EB, 0x56bb}}, // Player high-level defend + {{0x55E4, 0x56A4, 0x56C4, 0x56F4, 0x56c4}}, // Player low-level strike + {{0x55ED, 0x56AD, 0x56CD, 0x56FD, 0x56cd}}, // Player low-level defend + {{0x55F6, 0x56B6, 0x56D6, 0x5706, 0x56d6}}, // Player fight animation + {{0x55FF, 0x56BF, 0x56DF, 0x570F, 0x56df}}, // Pig fight animation + {{0x5608, 0x56C8, 0x56E8, 0x5718, 0x56e8}}, // Pig fight animation + {{0x5611, 0x56D1, 0x56F1, 0x5721, 0x56f1}}, // Player mid-level strike + {{0x561A, 0x56DA, 0x56FA, 0x572A, 0x56FA}}, // Pig upper block + {{0x5623, 0x56E3, 0x5703, 0x5733, 0x5703}}, // Pig fight animation + {{0x562C, 0x56EC, 0x570C, 0x573C, 0x570c}}, // Misc fight animation + {{0x5635, 0x56F5, 0x5715, 0x5745, 0x5715}}, // Pig fight animation + {{0x563E, 0x56FE, 0x571E, 0x574E, 0x571e}}, // Player recoiling from hit + {{0x5647, 0x5707, 0x5727, 0x5757, 0x5727}}, // Pig recoiling from hit + {{0x5650, 0x5710, 0x5730, 0x5760, 0x5730}}, // Pig dies + {{0x5810, 0x58D0, 0x58F0, 0x5920, 0x58f0}}, // Voice bubble + {{0x5915, 0x59D5, 0x59F5, 0x5a25, 0x59f5}}, // Blacksmith hammering + {{0x59E4, 0x5AA4, 0x5AC4, 0x5af4, 0x5ac4}}, // Ewan's standard animation + {{0x59ED, 0x5AAD, 0x5ACD, 0x5afd, 0x5acd}}, // Ewan's alternate animation + {{0x59FF, 0x5ABF, 0x5ADF, 0x5b0f, 0x5adf}}, // Dragon breathing fire + {{0x5A08, 0x5AC8, 0x5AE8, 0x5b18, 0x5af1}}, // Dragon breathing fire 2 + {{0x5A11, 0x5AD1, 0x5AF1, 0x5b21, 0x5af1}}, // Dragon breathing fire 3 + {{0x5A1A, 0x5ADA, 0x5AFA, 0x5b2a, 0x5afa}}, // Player turning winch in room #48 + {{0x5A59, 0x5B19, 0x5B39, 0x5b69, 0x5b39}}, // Player pulling lever in room #48 + {{0x5A62, 0x5B22, 0x5B42, 0x5b72, 0x5b42}}, // Minnow pulling lever in room #48 + {{0x5AAA, 0x5B6A, 0x5B8A, 0x5bba, 0x5b8a}}, // Goewin mixing potion + {{0x5C80, 0x5D40, 0x5D60, 0x5d90, 0x5d60}}, // Player standard animation + {{0x5C95, 0x5D55, 0x5D75, 0x5da5, 0x5d75}}, // Player operating rack + {{0x5CAA, 0x5D6A, 0x5D8A, 0x5dba, 0x5d8a}}, // Selena animation + {{0x5CE9, 0x5DA9, 0x5DC9, 0x5df9, 0x5dc9}}, // Blacksmith default + {{0x5D28, 0x5DE8, 0x5E08, 0x5e38, 0x5e08}}, // Goewin animation + {{0, 0, 0, 0}} +}; + +void errorExit(const char *msg) { + printf("%s\n", msg); + exit(1); +} + +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); + } + + if (animIndex == 0) { + // First call to the method, so add the fixed list for the current + // language in at the start, so they'll be at fixed indexes + int index = 0; + if (language == IT_ITA) index = 1; + else if (language == FR_FRA) index = 2; + else if (language == DE_DEU) index = 3; + else if (language == ES_ESP) index = 4; + else if (language != EN_ANY) errorExit("add_anim_record: Unknown language"); + + AnimListRecord *p = &animDataList[0]; + while (p->languages[index] != 0) { + animOffsets[animIndex++] = p->languages[index]; + ++p; + } + } + + 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; + uint32 segmentStart = 0xC0A7; + if (language == IT_ITA) segmentStart = 0xC107; + else if (language == FR_FRA) segmentStart = 0xC4B7; + else if (language == DE_DEU) segmentStart = 0xC517; + else if (language == ES_ESP) segmentStart = 0xC177; + else if (language != EN_ANY) errorExit("read_basic_palette: Unknown language"); + + lureExe.seek(segmentStart); + data = (byte *) malloc(totalSize); + lureExe.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); + + lureExe.seek(dataSegment + ALT_PALETTE_1); + lureExe.read(data, ALT_PALETTE_1_SIZE); + lureExe.seek(dataSegment + ALT_PALETTE_2); + lureExe.read(data + ALT_PALETTE_1_SIZE, ALT_PALETTE_2_SIZE); +} + +void read_dialog_data(byte *&data, uint16 &totalSize) { + uint32 segmentStart = 0x1dcb0; + if (language == IT_ITA) segmentStart = 0x1ddd0; + else if (language == FR_FRA) segmentStart = 0x1e1a0; + else if (language == DE_DEU) segmentStart = 0x1e230; + else if (language == ES_ESP) segmentStart = 0x1de60; + else if (language != EN_ANY) errorExit("read_dialog_data: Unknown language"); + + totalSize = DIALOG_SIZE; + lureExe.seek(segmentStart); + data = (byte *) malloc(totalSize); + lureExe.read(data, totalSize); +} + +void read_talk_dialog_data(byte *&data, uint16 &totalSize) { + uint32 segmentStart = 0x1de00; + if (language == IT_ITA) segmentStart = 0x1df20; + else if (language == FR_FRA) segmentStart = 0x1e2f0; + else if (language == DE_DEU) segmentStart = 0x1e380; + else if (language == ES_ESP) segmentStart = 0x1dfb0; + else if (language != EN_ANY) errorExit("read_talk_dialog_data: Unknown language"); + + totalSize = TALK_DIALOG_SIZE; + lureExe.seek(segmentStart); + data = (byte *) malloc(totalSize); + lureExe.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; + RoomHeaderEntry headerEntry; + RoomRectIn bounds; + + uint16 dataStart = 0xbf40; + uint16 walkAreaOffset = 0x2eb1; + switch (language) { + case EN_ANY: + break; + case IT_ITA: + dataStart = 0xc000; + walkAreaOffset = 0x2ec0; + break; + case FR_FRA: + dataStart = 0xc020; + walkAreaOffset = 0x2ed0; + break; + case DE_DEU: + dataStart = 0xc050; + walkAreaOffset = 0x2edf; + break; + case ES_ESP: + dataStart = 0xc020; + walkAreaOffset = 0x2ecb; + break; + default: + errorExit("read_room_data: Unknown language"); + } + + for (int index = 0; index < ROOM_NUM_ENTRIES; ++index) { + + lureExe.seek(dataSegment + dataStart + index * 9); + lureExe.read(&headerEntry, sizeof(RoomHeaderEntry)); + + if ((FROM_LE_16(headerEntry.offset) != 0) && + (FROM_LE_16(headerEntry.offset) != 0xffff) && + (FROM_LE_16(headerEntry.roomNumber) != 0)) { + // Store offset of room entry + *offsetPtr++ = TO_LE_16(offset); + + // Copy over basic room details + lureExe.seek(dataSegment + FROM_LE_16(headerEntry.offset)); + lureExe.read(&buffer, sizeof(RoomResource)); + RoomResourceOutput *rec = (RoomResourceOutput *) (data + offset); + rec->hdrFlags = headerEntry.hdrFlags; + rec->actions = FROM_LE_32(buffer.actions); + rec->roomNumber = index; + 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->exitTime = FROM_LE_32(buffer.exitTime); + rec->areaFlag = buffer.areaFlag; + rec->numExits = 0; + + offset += sizeof(RoomResourceOutput); + + // Copy over room exits + for (int foo = 0; ; ++foo) { + RoomResourceExit1 *p = (RoomResourceExit1 *) (data + offset); + lureExe.read(p, sizeof(RoomResourceExit1)); + if (FROM_LE_16(p->xs) == 0xffff) break; + + if (++rec->numExits == 255) + errorExit("Too many rooms read in"); + + 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) { + lureExe.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 + + lureExe.seek(dataSegment + walkAreaOffset + + buffer.walkBoundsIndex * sizeof(RoomRectIn)); + lureExe.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) { + lureExe.seek(dataSegment + FROM_LE_16(buffer.pixelListOffset)); + pixelOffset = lureExe.readWord(); + while (pixelOffset != 0) { + add_anim_record(pixelOffset); + pixelOffset = lureExe.readWord(); + } + } + } + } + + WRITE_LE_UINT16(offsetPtr, 0xffff); + totalSize = offset; +} + +uint16 englishTickProcOffsets[] = { + 0x41BD, 0x4f82, 0x5e44, 0x625e, 0x6571, 0x7207, 0x7c14, 0x7c24, 0x7efa, 0x7f02, + 0x7F37, 0x7f3a, 0x7f54, 0x7f69, 0x7fa1, 0x8009, 0x80c6, 0x813f, 0x8180, 0x81b3, + 0x81f3, 0x820e, 0x8241, 0x82a0, 0x85ce, 0x862d, 0x865A, 0x86FA, 0x86FF, 0x871E, + 0x873D, 0x8742, 0x8747, 0x87B3, 0x87EC, 0x882A, 0x8ABD, 0x982D, 0x98B6, + 0xffff +}; + +uint16 italianTickProcOffsets[] = { + 0x4205, 0x4fca, 0x5e8c, 0x62a6, 0x65b9, 0x724f, 0x7c5c, 0x7c6c, 0x7f58, 0x7f60, + 0x7f95, 0x7f98, 0x7fb2, 0x7fc7, 0x7fff, 0x8067, 0x8124, 0x819d, 0x81de, 0x8211, + 0x8251, 0x826c, 0x829f, 0x82fe, 0x862c, 0x868b, 0x86b8, 0x8758, 0x875D, 0x877C, + 0x879B, 0x87a0, 0x87a5, 0x8811, 0x884a, 0x8888, 0x8b20, 0x988f, 0x9918, + 0xffff +}; + +uint16 frenchTickProcOffsets[] = { + 0x457b, 0x5340, 0x6202, 0x661c, 0x692f, 0x75fb, 0x8008, 0x8018, 0x82f8, 0x8300, + 0x8335, 0x8338, 0x8352, 0x8367, 0x839f, 0x8407, 0x84c4, 0x853d, 0x857e, 0x85b1, + 0x85f1, 0x860c, 0x863f, 0x869e, 0x89cf, 0x8a2e, 0x8a5b, 0x8afb, 0x8b00, 0x8b1f, + 0x8b3e, 0x8b43, 0x8b48, 0x8bb4, 0x8bed, 0x8c26, 0x8ebe, 0x9c41, 0x9cca, + 0xffff +}; + +uint16 germanTickProcOffsets[] = { + 0x4543, 0x5308, 0x61ca, 0x65e4, 0x68fa, 0x7591, 0x7f9e, 0x7fae, 0x8358, 0x8360, + 0x8395, 0x8398, 0x83b2, 0x83c7, 0x83ff, 0x8467, 0x8524, 0x859d, 0x85de, 0x8611, + 0x8651, 0x866c, 0x869f, 0x86fe, 0x8a2c, 0x8a8b, 0x8ab8, 0x8b58, 0x8b5d, 0x8b7c, + 0x8b9b, 0x8ba0, 0x8ba5, 0x8c11, 0x8c4a, 0x8c83, 0x8f1a, 0x9c9b, 0x9d24, + 0xffff +}; + +uint16 spanishTickProcOffsets[] = { + 0x4247, 0x500c, 0x5ece, 0x62e8, 0x65fb, 0x7291, 0x7c9e, 0x7cae, 0x7fbe, 0x7fc6, + 0x7ffb, 0x7ffe, 0x8018, 0x802d, 0x8065, 0x80cd, 0x818a, 0x8203, 0x8244, 0x8277, + 0x82b7, 0x82d2, 0x8305, 0x8364, 0x8692, 0x86f1, 0x871e, 0x87be, 0x87c3, 0x87e2, + 0x8801, 0x8806, 0x880b, 0x8877, 0x88b0, 0x88ee, 0x8b86, 0x98f5, 0x997e, + 0xffff +}; + + +uint16 englishOffsets[4] = {0x5d98, 0x5eb8, 0x623e, 0x63b1}; +uint16 italianOffsets[4] = {0x5e58, 0x5f78, 0x62fe, 0x6471}; +uint16 frenchOffsets[4] = {0x5e78, 0x5f98, 0x631e, 0x6491}; +uint16 germanOffsets[4] = {0x5ea8, 0x5fc8, 0x634e, 0x64c1}; +uint16 spanishOffsets[4] = {0x5e78, 0x5f98, 0x631e, 0x6491}; + +uint16 englishLoadOffsets[] = {0x3afe, 0x41BD, 0x7167, 0x7172, 0x8617, 0x88ac, 0}; +uint16 italianLoadOffsets[] = {0x3b46, 0x4205, 0x71af, 0x71ba, 0x8675, 0x890a, 0}; +uint16 frenchLoadOffsets[] = {0x3ebc, 0x457B, 0x755b, 0x7566, 0x8a18, 0x8ca8, 0}; +uint16 germanLoadOffsets[] = {0x3e84, 0x4543, 0x74f1, 0x74fc, 0x8a75, 0x8d05, 0}; +uint16 spanishLoadOffsets[] = {0x3b88, 0x4247, 0x71f1, 0x71fc, 0x86db, 0x8970, 0}; + +void read_hotspot_data(byte *&data, uint16 &totalSize) { + 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; + + // Set up list pointers for various languages + uint16 *offsets = &englishOffsets[0]; + uint16 *procList = &englishTickProcOffsets[0]; + uint16 *loadOffsets = &englishLoadOffsets[0]; + uint16 walkToOffset = 0xBC4B; + switch (language) { + case EN_ANY: + break; + case IT_ITA: + offsets = &italianOffsets[0]; + procList = &italianTickProcOffsets[0]; + walkToOffset = 0xBD0B; + loadOffsets = &italianLoadOffsets[0]; + break; + case FR_FRA: + offsets = &frenchOffsets[0]; + procList = &frenchTickProcOffsets[0]; + walkToOffset = 0xBD2B; + loadOffsets = &frenchLoadOffsets[0]; + break; + case DE_DEU: + offsets = &germanOffsets[0]; + procList = &germanTickProcOffsets[0]; + walkToOffset = 0xBD5B; + loadOffsets = &germanLoadOffsets[0]; + break; + case ES_ESP: + offsets = &spanishOffsets[0]; + procList = &spanishTickProcOffsets[0]; + walkToOffset = 0xBD2B; + loadOffsets = &spanishLoadOffsets[0]; + break; + default: + errorExit("read_hotspot_data: Unknown language"); + break; + } + + // Allocate enough space for output hotspot list + data = (byte *) malloc(MAX_HOTSPOTS * sizeof(HotspotResourceOutput)); + + // Determine number of hotspot walk to entries + + lureExe.seek(dataSegment + walkToOffset); + do { + ++walkNumEntries; + lureExe.read(&rec, sizeof(HotspotWalkToRecord)); + } while (TO_LE_16(rec.hotspotId) != 0); + --walkNumEntries; + + dataSize = walkNumEntries * sizeof(HotspotWalkToRecord); + walkList = (HotspotWalkToRecord *) malloc(dataSize); + lureExe.seek(dataSegment + walkToOffset); + lureExe.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 (;;) { + uint16 currentHotspotId = startId[tableNum] + hotspotIndex; + + lureExe.seek(dataSegment + offsets[tableNum] + hotspotIndex * 9); + lureExe.read(&entryHeader, sizeof(HotspotHeaderEntry)); + if (FROM_LE_16(entryHeader.offset) == 0xffff) break; + if (FROM_LE_16(entryHeader.offset) == 0) { + ++hotspotIndex; + continue; + } + + memset(r, 0, sizeof(HotspotResourceOutput)); + r->hotspotId = TO_LE_16(startId[tableNum] + hotspotIndex); + r->nameId = entryHeader.resourceId; + r->descId = entryHeader.descId; + r->descId2 = entryHeader.descId2; + r->hdrFlags = entryHeader.hdrFlags; + + // Get the hotspot data + lureExe.seek(dataSegment + entryHeader.offset); + lureExe.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->colourOffset = entry.colourOffset; + r->hotspotScriptOffset = entry.hotspotScriptOffset; + r->talkScriptOffset = entry.talkScriptOffset; + r->flags = entry.flags; + + // Handle any necessary translation of script load offsets + + r->scriptLoadFlag = entry.scriptLoadFlag; + if (r->scriptLoadFlag || (tableNum == 3)) + // Load offset is in script segment, so leave as is + r->loadOffset = entry.loadOffset; + else { + // Translate load offset to an index + int loadIndex = 0; + while ((loadOffsets[loadIndex] != FROM_LE_16(entry.loadOffset)) && + (loadOffsets[loadIndex] != 0)) + ++loadIndex; + + if (loadOffsets[loadIndex] == 0) { + printf("Unknown load offset encountered for hotspot %xh offset %xh\n", + startId[tableNum] + hotspotIndex, + FROM_LE_16(entry.loadOffset)); + exit(1); + } + + r->loadOffset = TO_LE_16(loadIndex + 1); + } + + if (tableNum == 3) { + r->tickProcId = 0; + } else { + // Scan through the proc list for the correct offset + int procIndex = 0; + while ((procList[procIndex] != FROM_LE_16(entry.tickProcOffset)) && + (procList[procIndex] != 0xffff)) + ++procIndex; + + if (procList[procIndex] == 0xffff) { + if ((FROM_LE_16(entry.tickProcOffset) != 0xe00) && + (FROM_LE_16(entry.tickProcOffset) != 2)) +// printf("Could not find a tick proc handler for hotspot %xh offset %xh\n", +// startId[tableNum] + hotspotIndex, +printf("%xh,\n", + FROM_LE_16(entry.tickProcOffset)); + r->tickProcId = 0; + } + else + r->tickProcId = TO_LE_16(procIndex + 1); + } + + // WORKAROUND: Special check for the tinderbox hotspot to set it's room number correctly - the original + // game used this as a backup against people trying to hack the copy protection + if (currentHotspotId == 0x271C) + r->roomNumber = TO_LE_16(28); + + // WORKAROUND: Sets a null handler for a hotspot that has an invalid tick proc offset + if (currentHotspotId == 0x46b) r->tickProcId = 1; + + // Find the walk-to coordinates for the hotspot + uint16 findId = FROM_LE_16(r->hotspotId); + walkCtr = 0; + while (walkCtr < walkNumEntries) { + uint16 id = FROM_LE_16(walkList[walkCtr].hotspotId); + if (id == findId) + break; + ++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); + + // WORKAROUND: Edwina's walk-to position is actually inside the table, which meant that walking over + // to her could fail, depending on your start position. This increments it into the clear + int tempY = (int16) (y & 0x7fff) - 0x80; + if (currentHotspotId == 0x442) + tempY += 8; + r->walkY = TO_LE_16((y & 0x8000) | (uint16) tempY); + } + + // 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 + lureExe.seek(dataSegment + entryHeader.offset + 0x63); + lureExe.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) +{ + lureExe.seek(dataSegment + HOTSPOT_OVERRIDE_OFFSET); + int numOverrides = 0; + HotspotOverride rec; + + // Determine number of hotspot overrides + do { + ++numOverrides; + lureExe.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); + lureExe.seek(dataSegment + HOTSPOT_OVERRIDE_OFFSET); + lureExe.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); + + uint16 dataStart = 0x2F61; + if (language == IT_ITA) dataStart = 0x2f70; + else if (language == FR_FRA) dataStart = 0x2f80; + else if (language == DE_DEU) dataStart = 0x2f8f; + else if (language == ES_ESP) dataStart = 0x2f7b; + else if (language != EN_ANY) errorExit("read_room_exits: Unknown language"); + + lureExe.seek(dataSegment + dataStart); + for (roomCtr = 0; roomCtr < NUM_ROOM_EXITS; ++roomCtr) + offsets[roomCtr] = lureExe.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 + lureExe.seek(dataSegment + offsets[roomCtr]); + lureExe.read(&rec, sizeof(RoomExitHotspotRecord)); + while (FROM_LE_16(rec.xs) != 0) { + totalSize += sizeof(RoomExitHotspotOutputRecord); + numEntries[roomCtr]++; + lureExe.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); + + lureExe.seek(dataSegment + offsets[roomCtr]); + + for (entryCtr = 0; entryCtr < numEntries[roomCtr]; ++entryCtr, ++destP) { + lureExe.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; + int numRecords = 0; + uint32 unused; + + uint16 dataStart = 0xce30; + if (language == IT_ITA) dataStart = 0xcef0; + else if (language == FR_FRA) dataStart = 0xcf10; + else if (language == DE_DEU) dataStart = 0xcf40; + else if (language == ES_ESP) dataStart = 0xcf10; + else if (language != EN_ANY) errorExit("read_room_exit_joins: Unknown language"); + + lureExe.seek(dataSegment + dataStart); + + do { + lureExe.read(&rec, sizeof(RoomExitHotspotJoinRecord)); + lureExe.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); + lureExe.seek(dataSegment + dataStart); + + p = (RoomExitHotspotJoinRecord *) data; + for (int recordCtr = 0; recordCtr < numRecords; ++recordCtr) + { + lureExe.read(p, sizeof(RoomExitHotspotJoinRecord)); + lureExe.read(&unused, sizeof(uint32)); + ++p; + } + + WRITE_LE_UINT16(p, 0xffff); +} + +// This next method reads in the animation and movement data + +void read_anim_data(byte *&data, uint16 &totalSize) { + 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) { + lureExe.seek(dataSegment + animOffsets[ctr]); + lureExe.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) { + lureExe.seek(dataSegment + offset); + lureExe.read(&move, sizeof(MovementRecord)); + + while (FROM_LE_16(move.frameNumber) != 0xffff) { + movementSize += sizeof(MovementRecord); + lureExe.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) { + lureExe.seek(dataSegment + animOffsets[ctr]); + lureExe.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; + + lureExe.seek(dataSegment + offset); + lureExe.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; + lureExe.read(&move, sizeof(MovementRecord)); + } + + destMove->frameNumber = TO_LE_16(0xffff); + moveOffset += 2; + } + + WRITE_LE_UINT16(outDirs[dirCtr], startOffset); + } + } + } + + rec->animRecordId = TO_LE_16(0xffff); + free(includeAnim); +} + +void read_script_data(byte *&data, uint16 &totalSize) { + uint32 segmentOffset = 0x1df00; + if (language == IT_ITA) segmentOffset = 0x1e020; + else if (language == FR_FRA) segmentOffset = 0x1e3f0; + else if (language == DE_DEU) segmentOffset = 0x1e480; + else if (language == ES_ESP) segmentOffset = 0x1e0b0; + else if (language != EN_ANY) errorExit("read_script_data: Unknown language"); + lureExe.seek(segmentOffset); + + totalSize = SCRIPT_SEGMENT_SIZE; + data = (byte *) malloc(totalSize); + lureExe.read(data, totalSize); +} + +void read_script2_data(byte *&data, uint16 &totalSize) { + uint32 segmentOffset = 0x19c70; + if (language == IT_ITA) segmentOffset = 0x19D90; + else if (language == FR_FRA) segmentOffset = 0x1a160; + else if (language == DE_DEU) segmentOffset = 0x1a1f0; + else if (language == ES_ESP) segmentOffset = 0x19e20; + else if (language != EN_ANY) errorExit("read_script2_data: Unknown language"); + lureExe.seek(segmentOffset); + + totalSize = SCRIPT2_SEGMENT_SIZE; + data = (byte *) malloc(totalSize); + lureExe.read(data, totalSize); +} + +void read_hotspot_script_offsets(byte *&data, uint16 &totalSize) { + uint16 dataStart = 0x57e0; + if (language == IT_ITA) dataStart = 0x58a0; + else if (language == FR_FRA) dataStart = 0x58c0; + else if (language == DE_DEU) dataStart = 0x58f0; + else if (language == ES_ESP) dataStart = 0x58c0; + else if (language != EN_ANY) errorExit("read_hotspot_script_offsets: Unknown language"); + + lureExe.seek(dataSegment + dataStart); + totalSize = HOTSPOT_SCRIPT_SIZE; + data = (byte *) malloc(totalSize); + lureExe.read(data, totalSize); +} + +void read_messages_segment(byte *&data, uint16 &totalSize) { + uint32 segmentOffset = 0x20b60; + if (language == IT_ITA) segmentOffset = 0x20c80; + else if (language == FR_FRA) segmentOffset = 0x21050; + else if (language == DE_DEU) segmentOffset = 0x210E0; + else if (language == ES_ESP) segmentOffset = 0x20d10; + else if (language != EN_ANY) errorExit("read_messages_segment: Unknown language"); + + lureExe.seek(segmentOffset); + totalSize = MESSAGES_SEGMENT_SIZE; + data = (byte *) malloc(totalSize); + lureExe.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; + + lureExe.seek(dataSegment + actionOffsets[ctr]); + uint16 *numItems = (uint16 *) (data + offset); + lureExe.read(numItems, sizeof(uint16)); + offset += 2; + + if (READ_UINT16(numItems) > 0) { + lureExe.read(data + offset, READ_UINT16(numItems) * 3); + offset += READ_UINT16(numItems) * 3; + } + } + header->recordId = TO_LE_16(0xffff); +} + +// Reads in the talk data + +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; + + uint16 dataStart = 0x505c; + if (language == IT_ITA) dataStart = 0x511C; + else if (language == FR_FRA) dataStart = 0x513c; + else if (language == DE_DEU) dataStart = 0x515c; + else if (language == ES_ESP) dataStart = 0x512c; + else if (language != EN_ANY) errorExit("read_talk_headers: Unknown language"); + + lureExe.seek(dataSegment + dataStart); + lureExe.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; + } + + // Assume that the last talk header will have the same number of entries across language versions, + // so create an end address based on the start of the last entry using start/end from English version + sortedOffsets[entryCtr] = sortedOffsets[entryCtr - 1] + (0x5540 - 0x5504); + + data = (byte *) malloc(MAX_DATA_SIZE); + TalkEntry *entry = (TalkEntry *) data; + uint16 offset = TALK_NUM_ENTRIES * sizeof(TalkEntry) + sizeof(uint16); + + for (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 + lureExe.seek(dataSegment + startOffset); + int size = nextOffset - startOffset; + uint16 *talkOffset = (uint16 *) (data + offset); + lureExe.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; +}; + +#define NUM_GIVE_TALK_IDS 6 +uint16 englishGiveTalkIds[7] = {0xCF5E, 0xCF14, 0xCF90, 0xCFAA, 0xCFD0, 0xCFF6, 0xf010}; +uint16 italianGiveTalkIds[7] = {0xD01E, 0xCFD4, 0xD050, 0xD06A, 0xD090, 0xD0B6, 0xf0d0}; +uint16 frenchGiveTalkIds[7] = {0xD03E, 0xCFF4, 0xD070, 0xD08A, 0xD0B0, 0xD0D6, 0xf0f0}; +uint16 germanGiveTalkIds[7] = {0xD06E, 0xD024, 0xD0A0, 0xD0BA, 0xD0E0, 0xD106, 0xf120}; +uint16 spanishGiveTalkIds[7] = {0xD03E, 0xCFF4, 0xD070, 0xD08A, 0xD0B0, 0xD0D6, 0xf0f0}; + +void read_talk_data(byte *&data, uint16 &totalSize) { + uint16 responseOffset; + int talkCtr, subentryCtr; + uint16 size; + + uint16 *giveTalkIds = &englishGiveTalkIds[0]; + if (language == IT_ITA) giveTalkIds = &italianGiveTalkIds[0]; + else if (language == FR_FRA) giveTalkIds = &frenchGiveTalkIds[0]; + else if (language == DE_DEU) giveTalkIds = &germanGiveTalkIds[0]; + else if (language == ES_ESP) giveTalkIds = &spanishGiveTalkIds[0]; + else if (language != EN_ANY) errorExit("read_talk_data: Unknown language"); + + data = (byte *) malloc(MAX_DATA_SIZE); + uint16 *v = (uint16 *) data; + + for (talkCtr = 0; talkCtr < NUM_GIVE_TALK_IDS; ++talkCtr) { + add_talk_offset(giveTalkIds[talkCtr]); + *v++ = TO_LE_16(giveTalkIds[talkCtr]); + } + + byte *dataStart = (byte *) v; + TalkRecord *header = (TalkRecord *) dataStart; + 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] = giveTalkIds[6]; + 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; + + lureExe.seek(dataSegment + startOffset); + responseOffset = lureExe.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: size=%d\n", size); + exit(1); + } + + // Read in the list of talk entries + lureExe.read(dataStart + offset, size); + offset += size; + memset(dataStart + 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: no response found\n"); + exit(1); + } + + size = nextOffset - responseOffset; + if ((size % 6) != 0) size -= (size % 6); + + if ((size % 6) != 0) { + printf("Failure reading talk data: newSize=%d\n", size); + exit(1); + } + + lureExe.read(dataStart + offset, size); + offset += size; + WRITE_LE_UINT16(dataStart + offset, 0xffff); + offset += 2; + + ++header; + } + + header->recordId = TO_LE_16(0xffff); + totalSize = offset + NUM_GIVE_TALK_IDS * sizeof(uint16); + free(sortedList); +} + +void read_room_pathfinding_data(byte *&data, uint16 &totalSize) { + uint16 dataStart = 0x984A; + if (language == IT_ITA) dataStart = 0x990A; + else if (language == FR_FRA) dataStart = 0x992A; + else if (language == DE_DEU) dataStart = 0x995A; + else if (language == ES_ESP) dataStart = 0x992A; + else if (language != EN_ANY) errorExit("read_room_pathfinding_data: Unknown language"); + lureExe.seek(dataSegment + dataStart); + + totalSize = PATHFIND_SIZE; + data = (byte *) malloc(totalSize); + lureExe.read(data, totalSize); +} + +void read_room_exit_coordinate_data(byte *&data, uint16 &totalSize) +{ + // Read in the exit coordinates list + int roomNum, entryNum; + uint16 x, y; + RoomExitCoordinateEntryInputResource dataIn; + + totalSize = EXIT_COORDINATES_NUM_ROOMS * sizeof(RoomExitCoordinateEntryOutputResource) + 2; + data = (byte *) malloc(totalSize); + lureExe.seek(dataSegment + EXIT_COORDINATES_OFFSET); + WRITE_LE_UINT16(data + totalSize - 2, 0xffff); + + // Post process the list to adjust data + RoomExitCoordinateEntryOutputResource *rec = (RoomExitCoordinateEntryOutputResource *) data; + for (roomNum = 1; roomNum <= EXIT_COORDINATES_NUM_ROOMS; ++roomNum, ++rec) { + lureExe.read(&dataIn, sizeof(RoomExitCoordinateEntryInputResource)); + + for (entryNum = 0; entryNum < ROOM_EXIT_COORDINATES_NUM_ENTRIES; ++entryNum) { + x = FROM_LE_16(dataIn.entries[entryNum].x); + y = FROM_LE_16(dataIn.entries[entryNum].y); + if ((x != 0) || (y != 0)) { + x -= 0x80; + y = ((y & 0xfff) - 0x80) | (y & 0xf000); + } + + RoomExitCoordinateResource *p = &rec->entries[entryNum]; + p->x = TO_LE_16(x); + p->y = TO_LE_16(y); + p->roomNumber = dataIn.entries[entryNum].roomNumber; + } + + for (entryNum = 0; entryNum < ROOM_EXIT_COORDINATES_ENTRY_NUM_ROOMS; ++entryNum) { + rec->roomIndex[entryNum] = TO_LE_16(FROM_LE_16(dataIn.roomIndex[entryNum]) / 6); + } + + // WORKAROUND: Bugfix for the original game data to get to room #27 via rooms #10 or #11 + if ((roomNum == 10) || (roomNum == 11)) + rec->roomIndex[26] = TO_LE_16(1); + } +} + +void read_room_exit_hotspots_data(byte *&data, uint16 &totalSize) { + totalSize = 0; + data = (byte *) malloc(MAX_DATA_SIZE); + + RoomExitIndexedHotspotResource *rec = (RoomExitIndexedHotspotResource *) data; + + uint16 dataStart = 0x2E57; + if (language == IT_ITA) dataStart = 0x2E66; + else if (language == FR_FRA) dataStart = 0x2e76; + else if (language == DE_DEU) dataStart = 0x2e85; + else if (language == ES_ESP) dataStart = 0x2e71; + else if (language != EN_ANY) errorExit("read_room_exit_hotspots_data: Unknown language"); + lureExe.seek(dataSegment + dataStart); + + lureExe.read(rec, sizeof(RoomExitIndexedHotspotResource)); + while (FROM_LE_16(rec->roomNumber) != 0) { + ++rec; + totalSize += sizeof(RoomExitIndexedHotspotResource); + lureExe.read(rec, sizeof(RoomExitIndexedHotspotResource)); + } + + WRITE_LE_UINT16(rec, 0xffff); + totalSize += sizeof(uint16); +} + +void save_fight_segment(byte *&data, uint16 &totalSize) { + uint32 fightSegment = 0x1C400; + if (language == IT_ITA) fightSegment = 0x1c520; + else if (language == FR_FRA) fightSegment = 0x1c8f0; + else if (language == DE_DEU) fightSegment = 0x1c980; + else if (language == ES_ESP) fightSegment = 0x1c5b0; + else if (language != EN_ANY) errorExit("save_fight_segment: Unknown language"); + lureExe.seek(fightSegment); + + totalSize = FIGHT_SEGMENT_SIZE; + data = (byte *) malloc(totalSize); + lureExe.read(data, totalSize); +} + +#define NUM_TEXT_ENTRIES 56 +const char *englishTextStrings[NUM_TEXT_ENTRIES] = { + "Get", NULL, "Push", "Pull", "Operate", "Open", "Close", "Lock", "Unlock", "Use", + "Give", "Talk to", "Tell", "Buy", "Look", "Look at", "Look through", "Ask", NULL, + "Drink", "Status", "Go to", "Return", "Bribe", "Examine", + "Credits", "Restart game", "Save game", "Restore game", "Quit", "Fast Text\x8B", + "Slow Text\x8B", "Sound on", "Sound off", "(nothing)", " for ", " to ", " on ", + "and then", "finish", "Are you sure (y/n)?", + "You are carrying ", "nothing", "You have ", "groat", "groats", + NULL, "the ", "a ", "a ", "an ", "an ", "an ", "an ", NULL, NULL +}; + +const char *italianTextStrings[NUM_TEXT_ENTRIES] = { + "Prendi", NULL, "Spingi", "Tira", "Aziona", "Apri", "Chiudi", "Blocca", + "Sblocca", "Usa", "Dai", "Parla con", "Ordina a", "Compra", "Guarda", "Osserva", + "Guarda attraverso", "Chiedi", NULL, "Bevi", "Stato", "Vai a", "Ritorna", + "Corrompi", "Esamina", + "Crediti", "Ricomincia", "Salva gioco", "Ripristina", "Abbandona", "Testo lento\x8B", + "Testo veloce\x8B", "Sonoro acceso", "Sonoro spento", + "(niente)", " per ", " a ", " su ", + "e poi", "finito", "Sei sicuro (s/n)?", + "Stai portando ", "niente", "Hai ", "soldo", "soldi", + NULL, "l' ", "la ", NULL, "le ", "i ", "il ", NULL, NULL, NULL +}; + +const char frenchUnlockStr[] = {'D', '\x7f', 'v', 'e', 'r', 'r', 'o', 'u', 'i', 'l', 'l', 'e', '\0'}; +const char frenchCreditsStr[] = {'C', 'r', '\x7f', 'd', 'i', 't', 's', '\0'}; + +const char *frenchTextStrings[NUM_TEXT_ENTRIES] = { + "Prends", NULL, "Pousse", "Tire", "Actionne", "Ouvre", "Ferme", "Verrouille", + frenchUnlockStr, "Utilise", "Donne", "Parle \0x81", "Dis \x81", NULL, + "Regarde", "Observe", "Regarde par", "Demande \x81", NULL, "Bois", "Statut", + "Va \x81", "Reviens", "Corromps", "Examine", + frenchCreditsStr, "Recommencer", "Sauvegarder", "Restituer", "Quitter", + "Texte rapide\x8b", "Texte lent \x8b", "Avec son", "Sans son", + "(rien)", " avec ", " \x81 ", " sur ", "et puis", "fin", + "Vous \x83tes s\x89r (o/n)?", + "Vous avez ", "rien", "et vous avez ", "sou", "sous", + NULL, "les", "l'", "l'", "le", "le", "la", "la", NULL, NULL +}; + +const char *germanTextStrings[NUM_TEXT_ENTRIES] = { + "Nimm", NULL, "Schiebe", "Ziehe", "Bet\x8dtige", "\x90" "ffne", "Schlie\x92h" "e", + "Sperre", "Steck Schl\x8cssel in", "Benutze", "Gib", "Sprich mit", "Befehl an", + NULL, "Betrachte", "Betrachte", "Schau durch", "Verlange", NULL, "Trink", + "Status", "Geh zu", "Zur\x8c" "ck", "Besteche", "Untersuche", + "Mitwirkende", "Spiel neu starten", "Spiel sichern", "Gesichertes Spiel laden", + "Abbrechen", "Schneller Text\x8b", + "Langsamer Text\x8b", "Sound an", "Sound aus", "(nichts)", " nach ", " an ", " f\x8cr ", + "und dann", "Schlu\x92", "Sicher (j/n)?", + "Du tr\x8dgst", "niets", "und du hast ", "silberm\x8cnzen", "silberm\x8cnzen" + "keinen ", "keine ", "kein ", "der ", "die ", "das ", "den ", "einen ", "eine ", "ein " +}; + +const char *spanishTextStrings[NUM_TEXT_ENTRIES] = { + "Coge", NULL, "Empuja", "Tira de", "Opera", "Abre", "Cierra", "Atranca", "Desatranca", "Usa", + "Dale", "Habla con", "Dile a", "Compra", "Obsevar", "Observe", "Mirar por", "P\x97" "dele", NULL, + "Bebe", "Estado", "Ve a", "Regresa", "Sobornar", "Examinar", + "Cr\x7f" "ditos", "Reiniciar", "Salvar juego", "Recuperar jue", "Abandonar", "Texto r\x98pido\x8b", + "Texto lento \x8b", "Sonido activado ", "Sonido desactivado ", "(nada)", " con ", " a ", " con ", + "y luego", "eso es todo", "\x94" "Est\x98s seguro? (S/N)", + "Llevas ", "nada", "y tienes ", "moneda", "monedas" + "el ", "la ", "los ", "las ", "este ", "esta ", "estos ", "estas ", NULL, NULL +}; + +void save_text_strings(byte *&data, uint16 &totalSize) { + int index; + + const char **textStrings = &englishTextStrings[0]; + if (language == IT_ITA) textStrings = &italianTextStrings[0]; + else if (language == FR_FRA) textStrings = &frenchTextStrings[0]; + else if (language == DE_DEU) textStrings = &germanTextStrings[0]; + else if (language == ES_ESP) textStrings = &spanishTextStrings[0]; + else if (language != EN_ANY) errorExit("save_text_strings: Unknown language"); + + // Calculate the total needed space + totalSize = sizeof(uint16); + for (index = 0; index < NUM_TEXT_ENTRIES; ++index) { + if (textStrings[index] != NULL) + totalSize += strlen(textStrings[index]); + ++totalSize; + } + + // Duplicate the text strings list into a data buffer + data = (byte *) malloc(totalSize); + *((uint16 *) data) = TO_LE_16(NUM_TEXT_ENTRIES); + char *p = (char *) data + sizeof(uint16); + + for (index = 0; index < NUM_TEXT_ENTRIES; ++index) { + if (textStrings[index] == NULL) + *p++ = '\0'; + else { + strcpy(p, textStrings[index]); + p += strlen(p) + 1; + } + } +} + +void save_sound_desc_data(byte *&data, uint16 &totalSize) { + uint16 dataStart = 0x5671; + if (language == IT_ITA) dataStart = 0x5731; + else if (language == FR_FRA) dataStart = 0x5751; + else if (language == DE_DEU) dataStart = 0x5781; + else if (language == ES_ESP) dataStart = 0x5751; + else if (language != EN_ANY) errorExit("save_sound_desc_data: Unknown language"); + lureExe.seek(dataSegment + dataStart); + + totalSize = SOUND_DESCS_SIZE; + data = (byte *) malloc(totalSize); + lureExe.read(data, totalSize); +} + +struct DecoderEntry { + const char *sequence; + char character; +}; + +const DecoderEntry englishDecoders[] = { + {"00", ' '}, {"0100", 'e'}, {"0101", 'o'}, {"0110", 't'}, {"01110", 'a'}, + {"01111", 'n'}, {"1000", 's'}, {"1001", 'i'}, {"1010", 'r'}, {"10110", 'h'}, + {"101110", 'u'}, {"1011110", 'l'}, {"1011111", 'd'}, {"11000", 'y'}, + {"110010", 'g'}, {"110011", '\0'}, {"110100", 'w'}, {"110101", 'c'}, + {"110110", 'f'}, {"1101110", '.'}, {"1101111", 'm'}, {"111000", 'p'}, + {"111001", 'b'}, {"1110100", ','}, {"1110101", 'k'}, {"1110110", '\''}, + {"11101110", 'I'}, {"11101111", 'v'}, {"1111000", '!'}, {"1111001", '\xb4'}, + {"11110100", 'T'}, {"11110101", '\xb5'}, {"11110110", '?'}, {"111101110", '\xb2'}, + {"111101111", '\xb3'}, {"11111000", 'W'}, {"111110010", 'H'}, {"111110011", 'A'}, + {"111110100", '\xb1'}, {"111110101", 'S'}, {"111110110", 'Y'}, {"1111101110", 'G'}, + {"11111011110", 'M'}, {"11111011111", 'N'}, {"111111000", 'O'}, {"1111110010", 'E'}, + {"1111110011", 'L'}, {"1111110100", '-'}, {"1111110101", 'R'}, {"1111110110", 'B'}, + {"11111101110", 'D'}, {"11111101111", '\xa6'}, {"1111111000", 'C'}, + {"11111110010", 'x'}, {"11111110011", 'j'}, {"1111111010", '\xac'}, + {"11111110110", '\xa3'}, {"111111101110", 'P'}, {"111111101111", 'U'}, + {"11111111000", 'q'}, {"11111111001", '\xad'}, {"111111110100", 'F'}, + {"111111110101", '1'}, {"111111110110", '\xaf'}, {"1111111101110", ';'}, + {"1111111101111", 'z'}, {"111111111000", '\xa5'}, {"1111111110010", '2'}, + {"1111111110011", '\xb0'}, {"111111111010", 'K'}, {"1111111110110", '%'}, + {"11111111101110", '\xa2'}, {"11111111101111", '5'}, {"1111111111000", ':'}, + {"1111111111001", 'J'}, {"1111111111010", 'V'}, {"11111111110110", '6'}, + {"11111111110111", '3'}, {"1111111111100", '\xab'}, {"11111111111010", '\xae'}, + {"111111111110110", '0'}, {"111111111110111", '4'}, {"11111111111100", '7'}, + {"111111111111010", '9'}, {"111111111111011", '"'}, {"111111111111100", '8'}, + {"111111111111101", '\xa7'}, {"1111111111111100", '/'}, {"1111111111111101", 'Q'}, + {"11111111111111100", '\xa8'}, {"11111111111111101", '('}, {"111111111111111100", ')'}, + {"111111111111111101", '\x99'}, {"11111111111111111", '\xa9'}, + {NULL, '\0'} +}; + +const DecoderEntry italianDecoders[] = { + {"00", ' '}, {"010", (char) 0x69}, {"0110", (char) 0x6F}, {"01110", (char) 0x61}, {"01111", (char) 0x65}, + {"1000", (char) 0x72}, {"1001", (char) 0x6E}, {"1010", (char) 0x74}, {"10110", (char) 0x73}, {"101110", (char) 0x6C}, + {"101111", (char) 0x63}, {"11000", (char) 0x75}, {"110010", (char) 0x70}, {"110011", (char) 0x64}, {"110100", 0}, + {"110101", (char) 0x6D}, {"110110", (char) 0x67}, {"1101110", (char) 0x2E}, {"1101111", (char) 0x76}, + {"111000", (char) 0x68}, {"1110010", (char) 0x2C}, {"1110011", (char) 0x62}, {"1110100", (char) 0x66}, + {"1110101", (char) 0x21}, {"1110110", (char) 0xB5}, {"11101110", (char) 0xB1}, {"111011110", (char) 0xB3}, + {"111011111", (char) 0x7A}, {"1111000", (char) 0xB4}, {"11110010", (char) 0x27}, {"111100110", (char) 0x4E}, + {"111100111", (char) 0x4C}, {"11110100", (char) 0x3F}, {"111101010", (char) 0x85}, {"111101011", (char) 0x53}, + {"11110110", (char) 0x43}, {"111101110", (char) 0x4D}, {"1111011110", (char) 0xAC}, {"1111011111", (char) 0x49}, + {"11111000", (char) 0x45}, {"111110010", (char) 0x41}, {"1111100110", (char) 0x54}, {"1111100111", (char) 0xB2}, + {"111110100", (char) 0x71}, {"111110101", (char) 0x4F}, {"111110110", (char) 0x47}, {"1111101110", (char) 0xAB}, + {"11111011110", (char) 0x50}, {"11111011111", (char) 0x44}, {"111111000", (char) 0x81}, + {"1111110010", (char) 0x55}, {"11111100110", (char) 0xAE}, {"11111100111", (char) 0x52}, + {"1111110100", (char) 0xA6}, {"1111110101", (char) 0x56}, {"1111110110", (char) 0xA8}, + {"11111101110", (char) 0x42}, {"111111011110", (char) 0x51}, {"111111011111", (char) 0xB0}, + {"1111111000", (char) 0x95}, {"11111110010", (char) 0x48}, {"11111110011", (char) 0x2D}, + {"11111110100", (char) 0xA9}, {"11111110101", (char) 0x8A}, {"11111110110", (char) 0xA3}, + {"111111101110", (char) 0x46}, {"111111101111", (char) 0xA7}, {"11111111000", (char) 0x8D}, + {"11111111001", (char) 0x77}, {"11111111010", (char) 0x79}, {"111111110110", (char) 0x7F}, + {"1111111101110", (char) 0x6B}, {"1111111101111", (char) 0x31}, {"111111111000", (char) 0x3B}, + {"111111111001", (char) 0xA5}, {"111111111010", (char) 0x57}, {"1111111110110", (char) 0x32}, + {"11111111101110", (char) 0xAF}, {"11111111101111", (char) 0x35}, {"1111111111000", (char) 0xA2}, + {"1111111111001", (char) 0xAD}, {"1111111111010", (char) 0x25}, {"11111111110110", (char) 0x36}, + {"11111111110111", (char) 0x3A}, {"1111111111100", (char) 0x5A}, {"11111111111010", (char) 0x33}, + {"11111111111011", (char) 0x30}, {"11111111111100", (char) 0x34}, {"111111111111010", (char) 0x39}, + {"111111111111011", (char) 0x37}, {"111111111111100", (char) 0x38}, {"111111111111101", (char) 0x2F}, + {"1111111111111100", (char) 0x4B}, {"1111111111111101", (char) 0x22}, {"111111111111111000", (char) 0x09}, + {"111111111111111001", (char) 0x28}, {"11111111111111101", (char) 0x29}, {"111111111111111100", (char) 0x4A}, + {"111111111111111101", (char) 0x59}, {"11111111111111111", (char) 0x78}, + {NULL, '\0'} +}; + +const DecoderEntry frenchDecoders[] = { + {"00", (char) 0x20}, {"0100", (char) 0x65}, {"0101", (char) 0x73}, {"0110", (char) 0x61}, {"01110", (char) 0x69}, + {"011110", (char) 0x6E}, {"011111", (char) 0x72}, {"1000", (char) 0x6F}, {"10010", (char) 0x74}, + {"100110", (char) 0x75}, {"100111", (char) 0x70}, {"10100", (char) 0x6C}, {"10101", (char) 0x6D}, + {"101100", (char) 0x63}, {"101101", (char) 0x76}, {"101110", (char) 0x00}, {"1011110", (char) 0x2E}, + {"1011111", (char) 0x7F}, {"110000", (char) 0xAA}, {"110001", (char) 0x27}, {"110010", (char) 0x64}, + {"1100110", (char) 0xB8}, {"1100111", (char) 0x2C}, {"110100", (char) 0x62}, {"1101010", (char) 0x68}, + {"11010110", (char) 0x66}, {"11010111", (char) 0xBE}, {"1101100", (char) 0x21}, {"1101101", (char) 0xB0}, + {"1101110", (char) 0x67}, {"11011110", (char) 0xF2}, {"11011111", (char) 0xC5}, {"1110000", (char) 0xC0}, + {"11100010", (char) 0x6A}, {"11100011", (char) 0xA5}, {"1110010", (char) 0xB1}, {"11100110", (char) 0x4C}, + {"111001110", (char) 0x2D}, {"111001111", (char) 0x4A}, {"1110100", (char) 0xBD}, {"11101010", (char) 0x3F}, + {"111010110", (char) 0x71}, {"111010111", (char) 0xB5}, {"11101100", (char) 0x81}, {"111011010", (char) 0x43}, + {"111011011", (char) 0x45}, {"11101110", (char) 0x85}, {"111011110", (char) 0xC4}, {"1110111110", (char) 0x4D}, + {"1110111111", (char) 0x79}, {"11110000", (char) 0xBC}, {"111100010", (char) 0xBA}, {"111100011", (char) 0xAF}, + {"11110010", (char) 0x83}, {"111100110", (char) 0x53}, {"1111001110", (char) 0x56}, {"1111001111", (char) 0xA7}, + {"11110100", (char) 0xA6}, {"111101010", (char) 0xB2}, {"1111010110", (char) 0xB7}, {"1111010111", (char) 0xD9}, + {"111101100", (char) 0x41}, {"111101101", (char) 0xA3}, {"111101110", (char) 0x4E}, {"1111011110", (char) 0xB4}, + {"11110111110", (char) 0x54}, {"11110111111", (char) 0x49}, {"111110000", (char) 0x4F}, {"111110001", (char) 0xBF}, + {"111110010", (char) 0x47}, {"1111100110", (char) 0x50}, {"11111001110", (char) 0x7A}, {"11111001111", (char) 0x52}, + {"111110100", (char) 0x44}, {"1111101010", (char) 0x55}, {"1111101011", (char) 0x51}, {"1111101100", (char) 0xE0}, + {"1111101101", (char) 0xAC}, {"1111101110", (char) 0x78}, {"11111011110", (char) 0xC8}, {"11111011111", (char) 0x42}, + {"1111110000", (char) 0xEE}, {"1111110001", (char) 0xC2}, {"1111110010", (char) 0x82}, {"11111100110", (char) 0x87}, + {"11111100111", (char) 0x77}, {"1111110100", (char) 0xC7}, {"11111101010", (char) 0xEF}, {"11111101011", (char) 0x80}, + {"1111110110", (char) 0xE4}, {"11111101110", (char) 0x89}, {"111111011110", (char) 0xE8}, {"111111011111", (char) 0xA9}, + {"11111110000", (char) 0x46}, {"11111110001", (char) 0x48}, {"11111110010", (char) 0xDC}, + {"111111100110", (char) 0xAE}, {"111111100111", (char) 0x88}, {"11111110100", (char) 0x6B}, + {"111111101010", (char) 0x7E}, {"111111101011", (char) 0x31}, {"11111110110", (char) 0xC3}, + {"111111101110", (char) 0xD8}, {"1111111011110", (char) 0x3B}, {"1111111011111", (char) 0xBB}, + {"11111111000", (char) 0xCD}, {"111111110010", (char) 0x8A}, {"1111111100110", (char) 0xB3}, + {"1111111100111", (char) 0x32}, {"111111110100", (char) 0xC1}, {"111111110101", (char) 0xD0}, + {"111111110110", (char) 0x57}, {"1111111101110", (char) 0xB6}, {"1111111101111", (char) 0x25}, + {"111111111000", (char) 0xA1}, {"1111111110010", (char) 0xDF}, {"1111111110011", (char) 0xEA}, + {"111111111010", (char) 0x35}, {"1111111110110", (char) 0x3A}, {"11111111101110", (char) 0x36}, + {"11111111101111", (char) 0x33}, {"1111111111000", (char) 0x39}, {"1111111111001", (char) 0xDB}, + {"1111111111010", (char) 0xE6}, {"11111111110110", (char) 0x30}, {"111111111101110", (char) 0x22}, + {"111111111101111", (char) 0x34}, {"11111111111000", (char) 0xE3}, {"11111111111001", (char) 0x37}, + {"11111111111010", (char) 0x38}, {"111111111110110", (char) 0xD7}, {"111111111110111", (char) 0xCC}, + {"11111111111100", (char) 0xD5}, {"111111111111010", (char) 0xE5}, {"1111111111110110", (char) 0x2F}, + {"1111111111110111", (char) 0x4B}, {"111111111111100", (char) 0xE9}, {"1111111111111010", (char) 0x59}, + {"1111111111111011", (char) 0x28}, {"1111111111111100", (char) 0x29}, {"1111111111111101", (char) 0x5A}, + {"11111111111111100", (char) 0x86}, {"11111111111111101", (char) 0x58}, {"11111111111111110", (char) 0x84}, + {"11111111111111111", (char) 0xA2}, + {NULL, '\0'} +}; + +const DecoderEntry germanDecoders[] = { + {"000", (char) 0x20}, {"001", (char) 0x65}, {"0100", (char) 0x6E}, {"0101", (char) 0x61}, {"0110", (char) 0x72}, + {"01110", (char) 0x69}, {"011110", (char) 0x74}, {"011111", (char) 0x73}, {"10000", (char) 0x68}, + {"10001", (char) 0x6C}, {"10010", (char) 0x75}, {"100110", (char) 0x6D}, {"100111", (char) 0xB0}, + {"10100", (char) 0x67}, {"101010", (char) 0x00}, {"101011", (char) 0x62}, {"101100", (char) 0x63}, + {"101101", (char) 0x2C}, {"101110", (char) 0x6F}, {"1011110", (char) 0xAA}, {"1011111", (char) 0x77}, + {"110000", (char) 0x64}, {"110001", (char) 0x2E}, {"110010", (char) 0x6B}, {"1100110", (char) 0x66}, + {"1100111", (char) 0xDB}, {"110100", (char) 0x21}, {"1101010", (char) 0x7A}, {"11010110", (char) 0xC5}, + {"11010111", (char) 0x8C}, {"1101100", (char) 0x49}, {"11011010", (char) 0xCD}, {"11011011", (char) 0xCC}, + {"1101110", (char) 0x53}, {"11011110", (char) 0x45}, {"110111110", (char) 0x57}, {"110111111", (char) 0x8D}, + {"1110000", (char) 0xB8}, {"11100010", (char) 0x47}, {"11100011", (char) 0x76}, {"1110010", (char) 0x92}, + {"11100110", (char) 0xED}, {"111001110", (char) 0x44}, {"111001111", (char) 0x4D}, {"1110100", (char) 0x70}, + {"11101010", (char) 0x48}, {"111010110", (char) 0xD0}, {"111010111", (char) 0xDC}, {"11101100", (char) 0xE8}, + {"11101101", (char) 0x3F}, {"11101110", (char) 0x41}, {"111011110", (char) 0xDD}, {"1110111110", (char) 0x8F}, + {"1110111111", (char) 0xF2}, {"11110000", (char) 0x42}, {"11110001", (char) 0xEE}, {"11110010", (char) 0x46}, + {"111100110", (char) 0x4E}, {"1111001110", (char) 0x4C}, {"1111001111", (char) 0xE9}, {"11110100", (char) 0xEF}, + {"111101010", (char) 0x54}, {"111101011", (char) 0xEB}, {"111101100", (char) 0x4B}, {"111101101", (char) 0xD1}, + {"111101110", (char) 0xD9}, {"1111011110", (char) 0xD8}, {"1111011111", (char) 0x52}, {"111110000", (char) 0xEC}, + {"111110001", (char) 0xE4}, {"111110010", (char) 0xD7}, {"1111100110", (char) 0x55}, {"1111100111", (char) 0xEA}, + {"111110100", (char) 0xDF}, {"1111101010", (char) 0xE6}, {"1111101011", (char) 0x5A}, {"111110110", (char) 0x56}, + {"1111101110", (char) 0xD5}, {"11111011110", (char) 0xD6}, {"11111011111", (char) 0xE0}, {"111111000", (char) 0x6A}, + {"1111110010", (char) 0xA3}, {"1111110011", (char) 0xB1}, {"1111110100", (char) 0x2D}, {"1111110101", (char) 0xC8}, + {"1111110110", (char) 0x50}, {"11111101110", (char) 0xE7}, {"111111011110", (char) 0x27}, + {"111111011111", (char) 0xD4}, {"1111111000", (char) 0xCE}, {"11111110010", (char) 0x4F}, + {"11111110011", (char) 0x79}, {"1111111010", (char) 0xC9}, {"11111110110", (char) 0xAF}, + {"111111101110", (char) 0x4A}, {"111111101111", (char) 0xC2}, {"11111111000", (char) 0xC7}, + {"111111110010", (char) 0x31}, {"111111110011", (char) 0xDA}, {"111111110100", (char) 0xA1}, + {"111111110101", (char) 0x32}, {"111111110110", (char) 0xC4}, {"1111111101110", (char) 0xB5}, + {"11111111011110", (char) 0x91}, {"11111111011111", (char) 0xE5}, {"111111111000", (char) 0x25}, + {"1111111110010", (char) 0x35}, {"1111111110011", (char) 0x3A}, {"1111111110100", (char) 0x43}, + {"1111111110101", (char) 0xE3}, {"1111111110110", (char) 0x36}, {"11111111101110", (char) 0x78}, + {"11111111101111", (char) 0x90}, {"1111111111000", (char) 0xE2}, {"11111111110010", (char) 0x33}, + {"11111111110011", (char) 0x8E}, {"1111111111010", (char) 0xA7}, {"11111111110110", (char) 0x34}, + {"111111111101110", (char) 0x39}, {"111111111101111", (char) 0x30}, {"11111111111000", (char) 0x37}, + {"11111111111001", (char) 0x38}, {"11111111111010", (char) 0x71}, {"111111111110110", (char) 0xB7}, + {"111111111110111", (char) 0xBA}, {"11111111111100", (char) 0xB2}, {"111111111111010", (char) 0x3B}, + {"1111111111110110", (char) 0xAE}, {"1111111111110111", (char) 0x22}, {"111111111111100", (char) 0x2F}, + {"1111111111111010", (char) 0x51}, {"1111111111111011", (char) 0xA2}, {"1111111111111100", (char) 0xC3}, + {"11111111111111010", (char) 0xBC}, {"11111111111111011", (char) 0x28}, {"111111111111111000", (char) 0x29}, + {"111111111111111001", (char) 0x59}, {"11111111111111101", (char) 0xBB}, {"11111111111111110", (char) 0xBD}, + {"11111111111111111", (char) 0xC0}, + {NULL, '\0'} +}; + +const DecoderEntry spanishDecoders[] = { + {"00", (char) 0x20}, {"010", (char) 0x61}, {"0110", (char) 0x6F}, {"01110", (char) 0x65}, + {"01111", (char) 0x72}, {"1000", (char) 0x73}, {"1001", (char) 0x6E}, {"10100", (char) 0x69}, + {"10101", (char) 0x75}, {"10110", (char) 0x63}, {"101110", (char) 0x74}, {"1011110", (char) 0x6C}, + {"1011111", (char) 0x64}, {"11000", (char) 0x70}, {"110010", (char) 0x2E}, {"110011", (char) 0x6D}, + {"110100", (char) 0x00}, {"110101", (char) 0xB3}, {"1101100", (char) 0x62}, {"1101101", (char) 0x2C}, + {"1101110", (char) 0x68}, {"11011110", (char) 0x67}, {"11011111", (char) 0xB1}, {"1110000", (char) 0x76}, + {"1110001", (char) 0xB5}, {"1110010", (char) 0x79}, {"11100110", (char) 0xAB}, {"11100111", (char) 0xB4}, + {"1110100", (char) 0x97}, {"11101010", (char) 0xB2}, {"11101011", (char) 0x98}, {"11101100", (char) 0x45}, + {"11101101", (char) 0x66}, {"11101110", (char) 0x95}, {"111011110", (char) 0x21}, {"111011111", (char) 0x6A}, + {"11110000", (char) 0x4C}, {"11110001", (char) 0x4E}, {"11110010", (char) 0x7F}, {"111100110", (char) 0x96}, + {"111100111", (char) 0x3F}, {"11110100", (char) 0xAC}, {"11110101", (char) 0x94}, {"11110110", (char) 0x7A}, + {"111101110", (char) 0x41}, {"1111011110", (char) 0x53}, {"1111011111", (char) 0x71}, {"11111000", (char) 0x54}, + {"111110010", (char) 0x93}, {"1111100110", (char) 0xAF}, {"1111100111", (char) 0x43}, {"111110100", (char) 0x50}, + {"111110101", (char) 0x4D}, {"111110110", (char) 0x44}, {"1111101110", (char) 0xAE}, {"11111011110", (char) 0xAD}, + {"11111011111", (char) 0xB0}, {"111111000", (char) 0x48}, {"1111110010", (char) 0x51}, {"1111110011", (char) 0x55}, + {"1111110100", (char) 0x4F}, {"1111110101", (char) 0x47}, {"1111110110", (char) 0x99}, + {"11111101110", (char) 0xA8}, {"11111101111", (char) 0x22}, {"1111111000", (char) 0xA9}, + {"11111110010", (char) 0xA6}, {"11111110011", (char) 0x52}, {"1111111010", (char) 0x59}, + {"11111110110", (char) 0xA3}, {"111111101110", (char) 0x78}, {"111111101111", (char) 0x56}, + {"11111111000", (char) 0x77}, {"11111111001", (char) 0x42}, {"111111110100", (char) 0x6B}, + {"111111110101", (char) 0x49}, {"111111110110", (char) 0x31}, {"1111111101110", (char) 0xAA}, + {"1111111101111", (char) 0x2D}, {"111111111000", (char) 0x46}, {"111111111001", (char) 0xA5}, + {"1111111110100", (char) 0x32}, {"1111111110101", (char) 0x3B}, {"1111111110110", (char) 0xA7}, + {"11111111101110", (char) 0x35}, {"11111111101111", (char) 0x25}, {"1111111111000", (char) 0x3A}, + {"1111111111001", (char) 0xA2}, {"1111111111010", (char) 0x57}, {"11111111110110", (char) 0x36}, + {"11111111110111", (char) 0x33}, {"1111111111100", (char) 0x4A}, {"11111111111010", (char) 0x30}, + {"11111111111011", (char) 0x34}, {"11111111111100", (char) 0x37}, {"111111111111010", (char) 0x39}, + {"111111111111011", (char) 0x38}, {"111111111111100", (char) 0xA4}, {"111111111111101", (char) 0x2F}, + {"1111111111111100", (char) 0x5A}, {"1111111111111101", (char) 0x8C}, {"11111111111111100", (char) 0x28}, + {"11111111111111101", (char) 0x29}, {"11111111111111110", (char) 0x4B}, {"111111111111111110", (char) 0x58}, + {"111111111111111111", (char) 0x91}, + {NULL, '\0'} +}; + +void save_string_decoder_data(byte *&data, uint16 &totalSize) { + const DecoderEntry *list = &englishDecoders[0]; + if (language == IT_ITA) list = &italianDecoders[0]; + else if (language == FR_FRA) list = &frenchDecoders[0]; + else if (language == DE_DEU) list = &germanDecoders[0]; + else if (language == ES_ESP) list = &spanishDecoders[0]; + else if (language != EN_ANY) errorExit("save_string_decoder_data: Unknown language"); + + totalSize = 1; + const DecoderEntry *pSrc = list; + while (pSrc->sequence != NULL) { + totalSize += strlen(pSrc->sequence) + 2; + ++pSrc; + } + + data = (byte *) malloc(totalSize); + char *pDest = (char *)data; + + pSrc = list; + while (pSrc->sequence != NULL) { + *pDest++ = pSrc->character; + strcpy(pDest, pSrc->sequence); + pDest += strlen(pSrc->sequence) + 1; + + ++pSrc; + } + + *pDest = (char) 0xff; +} + +void getEntry(uint8 entryIndex, uint16 &resourceId, byte *&data, uint16 &size) { + resourceId = 0x3f01 + entryIndex; + printf("Get resource #%d\n", 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; + + case 21: + // Save the fight segment data + save_fight_segment(data, size); + break; + + case 22: + // Set up the list of text strings used by the game + save_text_strings(data, size); + break; + + case 23: + // Save the sound header desc data + save_sound_desc_data(data, size); + break; + + case 24: + // Save the decoder sequence list + save_string_decoder_data(data, size); + break; + + default: + data = NULL; + size = 0; + resourceId = 0xffff; + break; + } +} + +void openOutputFile(const char *outFilename) { + outputFile.open(outFilename, kFileWriteMode); + + // Write header + outputFile.write("lure", 4); + outputFile.writeWord(0); + + outputFile.seek(0xBF * 8); + FileEntry fileVersion; + memset(&fileVersion, 0xff, sizeof(FileEntry)); + fileVersion.unused = VERSION_MAJOR; + fileVersion.sizeExtension = VERSION_MINOR; + outputFile.write(&fileVersion, sizeof(FileEntry)); +} + +void closeOutputFile() { + outputFile.seek(6 + 5 * langIndex); + outputFile.writeByte(0xff); + outputFile.close(); +} + +void createFile(const char *outFilename) { + FileEntry rec; + uint32 startOffset, numBytes; + uint32 outputStart; + uint16 resourceId; + uint16 resourceSize; + byte *resourceData; + bool resourceFlag; + byte tempBuffer[32]; + + memset(tempBuffer, 0, 32); + + // Reset list counters + outputStart = ((outputFile.pos() + 0xff) / 0x100) * 0x100; + startOffset = 0x600; + animIndex = 0; + actionIndex = 0; + talkOffsetIndex = 0; + + // Write out the position of the next language set + outputFile.seek(6 + 5 * (langIndex - 1)); + + switch (language) { + case IT_ITA: + outputFile.writeByte(LANG_IT_ITA); + break; + case FR_FRA: + outputFile.writeByte(LANG_FR_FRA); + break; + case DE_DEU: + outputFile.writeByte(LANG_DE_DEU); + break; + case ES_ESP: + outputFile.writeByte(LANG_ES_ESP); + break; + case EN_ANY: + outputFile.writeByte(LANG_EN_ANY); + break; + default: + printf("Unknown language encountered\n"); + exit(1); + } + + outputFile.writeLong(outputStart); + + // Write out start header + outputFile.seek(outputStart); + outputFile.write("heywow", 6); + outputFile.writeWord(0); + + 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 + outputFile.seek(outputStart + (resIndex + 1) * 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 = 0; //(uint8) ((resourceSize >> 16) & 0xff); --never needed + rec.size = TO_LE_16(resourceSize & 0xffff); + rec.unused = 0xff; + } + + outputFile.write(&rec, sizeof(FileEntry)); + + // Write out the resource + if (resourceFlag) { + outputFile.seek(outputStart + startOffset); + outputFile.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) + { + outputFile.write(tempBuffer, numBytes); + startOffset += numBytes; + } + } + } + + // Move to the end of the written file + outputFile.seek(0, SEEK_END); +} + +// 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 + +bool validate_executable() { + uint32 sumTotal = 0; + byte buffer[NUM_BYTES_VALIDATE]; + lureExe.read(buffer, NUM_BYTES_VALIDATE); + for (int ctr = 0; ctr < NUM_BYTES_VALIDATE; ++ctr) + sumTotal += buffer[ctr]; + + if (sumTotal == ENGLISH_FILE_CHECKSUM) { + language = EN_ANY; + dataSegment = 0xAC50; + printf("Detected English version\n"); + } else if (sumTotal == ITALIAN_FILE_CHECKSUM) { + language = IT_ITA; + dataSegment = 0xACB0; + printf("Detected Italian version\n"); + } else if (sumTotal == FRENCH_FILE_CHECKSUM) { + language = FR_FRA; + dataSegment = 0xB060; + printf("Detected French version\n"); + } else if (sumTotal == GERMAN_FILE_CHECKSUM) { + language = DE_DEU; + dataSegment = 0xB0C0; + printf("Detected German version\n"); + } else if (sumTotal == SPANISH_FILE_CHECKSUM) { + language = ES_ESP; + dataSegment = 0xAD20; + printf("Detected Spanish version\n"); + } else { + printf("Lure executable version not recognised. Checksum = %xh\n", sumTotal); + return false; + } + + // Double-check that the given language has not already been done + for (int index = 0; index < langIndex; ++index) { + if (processedLanguages[index] == language) { + printf("Identical language executable listed multiple times\n"); + return false; + } + } + + processedLanguages[langIndex++] = language; + return true; +} + + +int main(int argc, char *argv[]) { + const char /**inFilename,*/ *outFilename = 0; + + if (argc == 1) { + printf("Format: %s output_filename [lureExecutable ..]\n", argv[0]); + exit(0); + } + + openOutputFile(argv[1]); + + for (int argi = 2; argi < argc; ++argi) { + if (!lureExe.open(argv[argi])) + printf("Could not open file: %s\n", argv[argi]); + else { + if (validate_executable()) + createFile(outFilename); + lureExe.close(); + } + } + + closeOutputFile(); +} diff --git a/devtools/create_lure/create_lure_dat.h b/devtools/create_lure/create_lure_dat.h new file mode 100644 index 0000000000..4743f6ac3f --- /dev/null +++ b/devtools/create_lure/create_lure_dat.h @@ -0,0 +1,432 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 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 CREATE_LURE_DAT_H +#define CREATE_LURE_DAT_H + +#include "common/endian.h" +#include "common/util.h" + +#define VERSION_MAJOR 1 +#define VERSION_MINOR 29 + +#define DIALOG_SIZE 0x150 + +#define TALK_DIALOG_SIZE 0x30 + +#define PALETTE_OFFSET 0xc0a7 +#define PALETTE_SIZE 0x300 + +#define ROOM_NUM_ENTRIES 51 + +#define HOTSPOT_OVERRIDE_OFFSET 0x2A01 + +#define SCRIPT_SEGMENT_SIZE 0x2c57 +#define SCRIPT2_SEGMENT_SIZE 0x2800 +#define FIGHT_SEGMENT_SIZE 0x1850 + +#define HOTSPOT_SCRIPT_SIZE 0x30 + +#define MAX_NUM_ANIM_RECORDS 0x200 +#define MAX_NUM_ACTION_RECORDS 0x100 + +#define NUM_ROOM_EXITS 50 + +#define MESSAGES_SEGMENT_SIZE 0x490 + +#define MAX_HOTSPOTS 0x100 +#define MAX_DATA_SIZE 0x4000 + +#define PATHFIND_SIZE (120 * ROOM_NUM_ENTRIES) + +#define EXIT_COORDINATES_OFFSET 0x1929 +#define EXIT_COORDINATES_NUM_ROOMS 49 + +#define TABLED_ACTIONS_OFFSET 0x1380 +#define NUM_TABLED_ACTION_BLOCKS 33 +#define RANDOM_ROOM_NUM_ENTRIES 41 + +#define SOUND_DESCS_SIZE 265 + +#include "common/pack-start.h" // START STRUCT PACKING + +// FIXME: Add PACKED_STRUCT to all structs which actually need packing, +// for increased portability + +// 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; + uint16 unused; + byte hdrFlags; +}; + +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 hotspotScriptOffset; + byte unused1[7]; + uint16 talkScriptOffset; + byte unused2[6]; + int8 talkX; + int8 talkY; + byte unused3[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 hotspotScriptOffset; + uint16 talkScriptOffset; + uint16 tickProcId; + uint16 tickTimeout; + uint16 tickSequenceOffset; + uint16 npcSchedule; + uint16 characterMode; + uint16 delayCtr; + byte flags; + byte hdrFlags; +}; + +struct RoomResource { + uint32 actions; + uint16 unknown1; + uint16 pixelListOffset; + byte numLayers; + uint16 layers[4]; + uint16 sequenceOffset; + uint32 exitTime; + uint8 areaFlag; + 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; + uint8 hdrFlags; + uint8 unused; + uint32 actions; + uint16 descId; + uint16 numLayers; + uint16 layers[4]; + uint16 sequenceOffset; + int16 clippingXStart; + int16 clippingXEnd; + uint8 areaFlag; + uint8 numExits; + uint32 exitTime; + RoomRectOut walkBounds; +}; + +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 RoomExitCoordinateEntryInputResource { + RoomExitCoordinateResource entries[ROOM_EXIT_COORDINATES_NUM_ENTRIES]; + uint8 roomIndex[ROOM_EXIT_COORDINATES_ENTRY_NUM_ROOMS]; +}; + +struct RoomExitCoordinateEntryOutputResource { + uint8 roomIndex[ROOM_EXIT_COORDINATES_ENTRY_NUM_ROOMS]; + RoomExitCoordinateResource entries[ROOM_EXIT_COORDINATES_NUM_ENTRIES]; +}; + + +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); + +#include "common/pack-end.h" // END STRUCT PACKING + + + +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); + } + uint32 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)); + } + uint32 pos() { + return ftell(f); + } +}; + +extern File lureExe; +extern void add_talk_offset(uint16 offset); + +enum LureLanguage { + LANG_IT_ITA = 10, + LANG_FR_FRA = 6, + LANG_DE_DEU = 7, + LANG_ES_ESP = 17, + LANG_EN_ANY = 3, + LANG_UNKOWN = -1 +}; + +extern Common::Language language; +extern uint16 dataSegment; + +extern void errorExit(const char *msg); + +#endif diff --git a/devtools/create_lure/dists/msvc8/create_lure.sln b/devtools/create_lure/dists/msvc8/create_lure.sln new file mode 100644 index 0000000000..8d74988d8c --- /dev/null +++ b/devtools/create_lure/dists/msvc8/create_lure.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "create_lure", "create_lure.vcproj", "{63E18A70-17D2-11DE-8C30-0800200C9A66}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {63E18A70-17D2-11DE-8C30-0800200C9A66}.Debug|Win32.ActiveCfg = Debug|Win32 + {63E18A70-17D2-11DE-8C30-0800200C9A66}.Debug|Win32.Build.0 = Debug|Win32 + {63E18A70-17D2-11DE-8C30-0800200C9A66}.Release|Win32.ActiveCfg = Release|Win32 + {63E18A70-17D2-11DE-8C30-0800200C9A66}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/devtools/create_lure/dists/msvc8/create_lure.vcproj b/devtools/create_lure/dists/msvc8/create_lure.vcproj new file mode 100644 index 0000000000..177466a29f --- /dev/null +++ b/devtools/create_lure/dists/msvc8/create_lure.vcproj @@ -0,0 +1,222 @@ +<?xml version="1.0" encoding="windows-1252"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="8,00" + Name="create_lure" + ProjectGUID="{63E18A70-17D2-11DE-8C30-0800200C9A66}" + RootNamespace="create_lure" + Keyword="Win32Proj" + > + <Platforms> + <Platform + Name="Win32" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + OutputDirectory="Debug" + IntermediateDirectory="Debug" + ConfigurationType="1" + CharacterSet="2" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + AdditionalOptions="/wd4996" + Optimization="0" + AdditionalIncludeDirectories="../../../../" + PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="4" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + OutputFile="$(OutDir)/create_lure.exe" + LinkIncremental="2" + IgnoreDefaultLibraryNames="libc.lib;libcmt.lib" + GenerateDebugInformation="true" + ProgramDatabaseFile="$(OutDir)/create_lure.pdb" + SubSystem="1" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + <Configuration + Name="Release|Win32" + OutputDirectory="Release" + IntermediateDirectory="Release" + ConfigurationType="1" + CharacterSet="2" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + AdditionalOptions="/wd4996" + Optimization="3" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + OutputFile="$(OutDir)/create_lure.exe" + LinkIncremental="1" + IgnoreDefaultLibraryNames="libc.lib;libcmt.lib" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="common" + > + <File + RelativePath="..\..\..\..\common\hashmap.cpp" + > + </File> + <File + RelativePath="..\..\..\..\common\hashmap.h" + > + </File> + <File + RelativePath="..\..\..\..\common\memorypool.cpp" + > + </File> + <File + RelativePath="..\..\..\..\common\memorypool.h" + > + </File> + <File + RelativePath="..\..\..\..\common\scummsys.h" + > + </File> + <File + RelativePath="..\..\..\..\common\str.cpp" + > + </File> + <File + RelativePath="..\..\..\..\common\str.h" + > + </File> + <File + RelativePath="..\..\..\..\common\util.h" + > + </File> + </Filter> + <File + RelativePath="..\..\create_lure_dat.cpp" + > + </File> + <File + RelativePath="..\..\create_lure_dat.h" + > + </File> + <File + RelativePath="..\..\process_actions.cpp" + > + </File> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/devtools/create_lure/dists/msvc8_to_msvc9.bat b/devtools/create_lure/dists/msvc8_to_msvc9.bat new file mode 100644 index 0000000000..54820b34d0 --- /dev/null +++ b/devtools/create_lure/dists/msvc8_to_msvc9.bat @@ -0,0 +1,32 @@ +@echo off +rem This batch file is used to convert MSVC8 (Visual Studio 2005) project files to MSVC9 (Visual Studio 2008) ones +rem You need the Windows version of GNU rpl +rem Get it here: +rem http://gnuwin32.sourceforge.net/packages/rpl.htm +rem Place rpl.exe from the bin folder inside the archive in the folder where +rem this batch file resides + +if not exist rpl.exe goto no_rpl + +echo Creating MSVC9 project files from the MSVC8 ones +copy /y msvc8\*.vcproj msvc9\ +copy /y msvc8\*.sln msvc9\ +rpl -e -q "Version=\"8.00\"" "Version=\"9.00\"" msvc9\*.vcproj +rpl -e -q "Version=\"8,00\"" "Version=\"9,00\"" msvc9\*.vcproj +rpl -e -q "Keyword=\"Win32Proj\"" "Keyword=\"Win32Proj\"\n\tTargetFrameworkVersion=\"131072\"" msvc9\*.vcproj +rpl -e -q "EntryPointSymbol=\"WinMainCRTStartup\"" "EntryPointSymbol=\"WinMainCRTStartup\"\n\t\t\t\tRandomizedBaseAddress=\"1\"\n\t\t\t\tDataExecutionPrevention=\"0\"" msvc9\*.vcproj +rpl -e -q "Format Version 9.00" "Format Version 10.00" msvc9\*.sln +rpl -e -q "Format Version 9,00" "Format Version 10,00" msvc9\*.sln +rpl -e -q "# Visual C++ Express 2005" "# Visual C++ Express 2008" msvc9\*.sln +rpl -e -q "# Visual Studio 2005" "# Visual Studio 2008" msvc9\*.sln +goto the_end + +:no_rpl +echo You need the Windows version of GNU rpl +echo Get it here: +echo http://gnuwin32.sourceforge.net/packages/rpl.htm +echo Place rpl.exe from the bin folder inside the archive in the folder where +echo this batch file resides + +:the_end +pause diff --git a/devtools/create_lure/dists/msvc9/create_lure.sln b/devtools/create_lure/dists/msvc9/create_lure.sln new file mode 100644 index 0000000000..4f55924eea --- /dev/null +++ b/devtools/create_lure/dists/msvc9/create_lure.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "create_lure", "create_lure.vcproj", "{63E18A70-17D2-11DE-8C30-0800200C9A66}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {63E18A70-17D2-11DE-8C30-0800200C9A66}.Debug|Win32.ActiveCfg = Debug|Win32 + {63E18A70-17D2-11DE-8C30-0800200C9A66}.Debug|Win32.Build.0 = Debug|Win32 + {63E18A70-17D2-11DE-8C30-0800200C9A66}.Release|Win32.ActiveCfg = Release|Win32 + {63E18A70-17D2-11DE-8C30-0800200C9A66}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/devtools/create_lure/dists/msvc9/create_lure.vcproj b/devtools/create_lure/dists/msvc9/create_lure.vcproj new file mode 100644 index 0000000000..a611db31fa --- /dev/null +++ b/devtools/create_lure/dists/msvc9/create_lure.vcproj @@ -0,0 +1,223 @@ +<?xml version="1.0" encoding="windows-1252"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="9,00" + Name="create_lure" + ProjectGUID="{63E18A70-17D2-11DE-8C30-0800200C9A66}" + RootNamespace="create_lure" + Keyword="Win32Proj" + TargetFrameworkVersion="131072" + > + <Platforms> + <Platform + Name="Win32" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + OutputDirectory="Debug" + IntermediateDirectory="Debug" + ConfigurationType="1" + CharacterSet="2" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + AdditionalOptions="/wd4996" + Optimization="0" + AdditionalIncludeDirectories="../../../../" + PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="4" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + OutputFile="$(OutDir)/create_lure.exe" + LinkIncremental="2" + IgnoreDefaultLibraryNames="libc.lib;libcmt.lib" + GenerateDebugInformation="true" + ProgramDatabaseFile="$(OutDir)/create_lure.pdb" + SubSystem="1" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + <Configuration + Name="Release|Win32" + OutputDirectory="Release" + IntermediateDirectory="Release" + ConfigurationType="1" + CharacterSet="2" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + AdditionalOptions="/wd4996" + Optimization="3" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + UsePrecompiledHeader="0" + WarningLevel="3" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + OutputFile="$(OutDir)/create_lure.exe" + LinkIncremental="1" + IgnoreDefaultLibraryNames="libc.lib;libcmt.lib" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="common" + > + <File + RelativePath="..\..\..\..\common\hashmap.cpp" + > + </File> + <File + RelativePath="..\..\..\..\common\hashmap.h" + > + </File> + <File + RelativePath="..\..\..\..\common\memorypool.cpp" + > + </File> + <File + RelativePath="..\..\..\..\common\memorypool.h" + > + </File> + <File + RelativePath="..\..\..\..\common\scummsys.h" + > + </File> + <File + RelativePath="..\..\..\..\common\str.cpp" + > + </File> + <File + RelativePath="..\..\..\..\common\str.h" + > + </File> + <File + RelativePath="..\..\..\..\common\util.h" + > + </File> + </Filter> + <File + RelativePath="..\..\create_lure_dat.cpp" + > + </File> + <File + RelativePath="..\..\create_lure_dat.h" + > + </File> + <File + RelativePath="..\..\process_actions.cpp" + > + </File> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/devtools/create_lure/dists/msvc9_to_msvc8.bat b/devtools/create_lure/dists/msvc9_to_msvc8.bat new file mode 100644 index 0000000000..c87a524f77 --- /dev/null +++ b/devtools/create_lure/dists/msvc9_to_msvc8.bat @@ -0,0 +1,33 @@ +@echo off +rem This batch file is used to convert MSVC9 (Visual Studio 2008) project files to MSVC8 (Visual Studio 2005) ones +rem You need the Windows version of GNU rpl +rem Get it here: +rem http://gnuwin32.sourceforge.net/packages/rpl.htm +rem Place rpl.exe from the bin folder inside the archive in the folder where +rem this batch file resides + +if not exist rpl.exe goto no_rpl + +echo Creating MSVC8 project files from the MSVC9 ones +copy /y msvc9\*.vcproj msvc8\ +copy /y msvc9\*.sln msvc8\ +rpl -e -q "Version=\"9.00\"" "Version=\"8.00\"" msvc8\*.vcproj +rpl -e -q "Version=\"9,00\"" "Version=\"8,00\"" msvc8\*.vcproj +rpl -e -q "\tTargetFrameworkVersion=\"131072\"\n" "" msvc8\*.vcproj +rpl -e -q "\t\t\t\tRandomizedBaseAddress=\"1\"\n" "" msvc8\*.vcproj +rpl -e -q "\t\t\t\tDataExecutionPrevention=\"0\"\n" "" msvc8\*.vcproj +rpl -e -q "Format Version 10.00" "Format Version 9.00" msvc8\*.sln +rpl -e -q "Format Version 10,00" "Format Version 9,00" msvc8\*.sln +rpl -e -q "# Visual C++ Express 2008" "# Visual C++ Express 2005" msvc8\*.sln +rpl -e -q "# Visual Studio 2008" "# Visual Studio 2005" msvc8\*.sln +goto the_end + +:no_rpl +echo You need the Windows version of GNU rpl +echo Get it here: +echo http://gnuwin32.sourceforge.net/packages/rpl.htm +echo Place rpl.exe from the bin folder inside the archive in the folder where +echo this batch file resides + +:the_end +pause diff --git a/devtools/create_lure/module.mk b/devtools/create_lure/module.mk new file mode 100644 index 0000000000..5d8192cb7a --- /dev/null +++ b/devtools/create_lure/module.mk @@ -0,0 +1,14 @@ +# $URL$ +# $Id$ + +MODULE := devtools/create_lure + +MODULE_OBJS := \ + create_lure_dat.o \ + process_actions.o + +# Set the name of the executable +TOOL_EXECUTABLE := create_lure + +# Include common rules +include $(srcdir)/rules.mk diff --git a/devtools/create_lure/process_actions.cpp b/devtools/create_lure/process_actions.cpp new file mode 100644 index 0000000000..30e7eed9e4 --- /dev/null +++ b/devtools/create_lure/process_actions.cpp @@ -0,0 +1,544 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 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$ + * + */ + +// Disable symbol overrides so that we can use system headers. +#define FORBIDDEN_SYMBOL_ALLOW_ALL + +#include "common/scummsys.h" +#include "common/util.h" +#include "create_lure_dat.h" + +using namespace Common; + +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_HEY_SIR = 29, NPC_EXEC_SCRIPT = 30, + NPC_RESET_PAUSED_LIST = 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_TALK_NPC_TO_NPC = 37, NPC_PAUSE = 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, 1, 1, 0, 0, 1, 1, 2, 2, 5, 2, 2, 1}; + +#define NUM_JUMP_OFFSETS 2 + +struct JumpOffsetsRecord { + Common::Language language; + uint16 jumpOffsets[2]; +}; + +JumpOffsetsRecord jumpOffsets[] = { + {EN_ANY, {0x87be, 0x881c}}, + {IT_ITA, {0x881c, 0x887a}}, + {FR_FRA, {0x8bbf, 0x8c18}}, + {DE_DEU, {0x8c1c, 0x8c75}}, + {ES_ESP, {0x8882, 0x88e0}}, + {UNK_LANG, {0, 0}} +}; + +#define MAX_BUFFER_ENTRIES 63 +#define MAX_INSTRUCTION_ENTRIES 300 +#define SCHEDULE_DATA_OFFSET 0x80 + +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 (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; + + switch (language) { + case EN_ANY: + 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 == 0x76ec) { startOffset = 0x734a; maxOffset = 0x77a2; } + break; + case IT_ITA: + if (startOffset == 0x7e8b) { startOffset = 0x7e5d; maxOffset = 0x7eb5; } + if (startOffset == 0x7a68) { startOffset = 0x791c; maxOffset = 0x7a92; } + if (startOffset == 0x7308) { startOffset = 0x7308; maxOffset = 0x7362; } + if (startOffset == 0x7308) { startOffset = 0x728e; maxOffset = 0x7362; } + if (startOffset == 0x700f) { startOffset = 0x700f; maxOffset = 0x7083; } + if (startOffset == 0x700f) { startOffset = 0x6f1d; maxOffset = 0x70a5; } + if (startOffset == 0x7866) { startOffset = 0x740a; maxOffset = 0x7876; } + if (startOffset == 0x3600) { startOffset = 0x35c6; maxOffset = 0x3622; } + break; + case FR_FRA: + if (startOffset == 0x7eab) { startOffset = 0x7e7d; maxOffset = 0x7ed5; } + if (startOffset == 0x7a88) { startOffset = 0x793c; maxOffset = 0x7ab2; } + if (startOffset == 0x7328) { startOffset = 0x72ae; maxOffset = 0x7382; } + if (startOffset == 0x702f) { startOffset = 0x6f3d; maxOffset = 0x70a3; } + if (startOffset == 0x7886) { startOffset = 0x742a; maxOffset = 0x7896; } + case DE_DEU: + if (startOffset == 0x7edb) { startOffset = 0x7ead; maxOffset = 0x7f05; } + if (startOffset == 0x7ab8) { startOffset = 0x796c; maxOffset = 0x7ae2; } + if (startOffset == 0x7358) { startOffset = 0x72de; maxOffset = 0x73b2; } + if (startOffset == 0x705f) { startOffset = 0x6f6d; maxOffset = 0x70d3; } + if (startOffset == 0x78b6) { startOffset = 0x745a; maxOffset = 0x78c6; } + break; + default: + if (startOffset == 0x7eab) { startOffset = 0x7e7d; maxOffset = 0x7ed5; } + if (startOffset == 0x7a88) { startOffset = 0x793c; maxOffset = 0x7ab2; } + if (startOffset == 0x7328) { startOffset = 0x72ae; maxOffset = 0x7382; } + if (startOffset == 0x702f) { startOffset = 0x702f; maxOffset = 0x70a3; } + if (startOffset == 0x702f) { startOffset = 0x6f3d; maxOffset = 0x70c5; } + if (startOffset == 0x7886) { startOffset = 0x742a; maxOffset = 0x7896; } + break; + } + +//printf("Start=%xh max=%xh\n", startOffset, maxOffset); + 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; + JumpOffsetsRecord *jmpOffset; + + lureExe.seek(dataSegment + 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 = lureExe.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] = lureExe.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 + jmpOffset = &jumpOffsets[0]; + while (jmpOffset->language != language) ++jmpOffset; + index = 0; + while ((index < NUM_JUMP_OFFSETS) && (jmpOffset->jumpOffsets[index] != params[0])) + ++index; + + if (index != NUM_JUMP_OFFSETS) + // Replace code offset with an index + params[0] = index; + else { + printf("\nEncountered unrecognised NPC code jump point: %xh\n", params[0]); + exit(1); + } + break; + + case NPC_HEY_SIR: + // The 'Hey Sir' opcode causes the NPC to request your attention, and sets the active talk + // record to a designated offset. So any offset occurances need to be saved so that it can + // be included in the resource for talk records + add_talk_offset(params[0]); + break; + + default: + break; + } + + // Output parameters + for (paramIndex = 0; paramIndex < numParams[actionNum]; ++paramIndex) + { + *pOut++ = TO_LE_16(params[paramIndex]); +//printf(" %xh", TO_LE_16(params[paramIndex])); + } +//printf("\n"); + + // 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; + + ++numSupportEntries; + if (numSupportEntries == MAX_BUFFER_ENTRIES) { + printf("Ran out of buffer space in processing NPC schedules\n"); + exit(1); + } + +//printf("process_entry index=%d, offset=%xh\n", numSupportEntries, offset); + totalSize += process_action_sequence_entry(numSupportEntries - 1, + data + totalSize, MAX_DATA_SIZE - totalSize); + } +} + +struct RoomRandomActionEntry { + bool repeatable; + uint16 offset; +}; + +struct RoomRandomActionSet { + uint16 offset; + uint8 numEntries; + RoomRandomActionEntry *entries; +}; + +void read_action_sequence(byte *&data, uint16 &totalSize) { + uint16 hotspotIndex; + HotspotHeaderEntry entryHeader; + CurrentActionInput action; + uint16 *pHeader; + int index, roomIndex; + + // 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]; + lureExe.seek(dataSegment + TABLED_ACTIONS_OFFSET, SEEK_SET); + for (index = 0; index < NUM_TABLED_ACTION_BLOCKS; ++index) + offsetList[index] = lureExe.readWord(); + totalSize = sizeof(uint16) * (NUM_TABLED_ACTION_BLOCKS + 1); + + /* Process the list of random actions that your follower can do in each room */ + RoomRandomActionSet *randomActions = new RoomRandomActionSet[RANDOM_ROOM_NUM_ENTRIES]; + + // Get a list of the offsets for each room + uint16 raOffset = 0x4D10; + if (language == IT_ITA) raOffset = 0x4dc0; + else if (language == FR_FRA) raOffset = 0x4df0; + else if (language == DE_DEU) raOffset = 0x4de0; + else if (language == ES_ESP) raOffset = 0x4dc0; + else if (language != EN_ANY) errorExit("read_action_sequence: Unknown language"); + + lureExe.seek(dataSegment + raOffset, SEEK_SET); + for (roomIndex = 0; roomIndex < RANDOM_ROOM_NUM_ENTRIES; ++roomIndex) { + randomActions[roomIndex].offset = lureExe.readWord(); + randomActions[roomIndex].numEntries = 0; + randomActions[roomIndex].entries = NULL; + } + + // Next get the set of offsetes for the start of each sequence + for (roomIndex = 0; roomIndex < RANDOM_ROOM_NUM_ENTRIES; ++roomIndex) { + if (randomActions[roomIndex].offset == 0) + continue; + + lureExe.seek(dataSegment + randomActions[roomIndex].offset, SEEK_SET); + randomActions[roomIndex].numEntries = lureExe.readByte(); + assert(randomActions[roomIndex].numEntries <= 8); + randomActions[roomIndex].entries = new RoomRandomActionEntry[randomActions[roomIndex].numEntries]; + + // Loop through the entries + uint16 offset = randomActions[roomIndex].offset + 1; + for (uint8 entryCtr = 0; entryCtr < randomActions[roomIndex].numEntries; ++entryCtr) { + randomActions[roomIndex].entries[entryCtr].repeatable = lureExe.readWord() == 1; + offset += 2; + + uint16 firstCommand = lureExe.readWord(); + randomActions[roomIndex].entries[entryCtr].offset = + (firstCommand == 0xfffe) ? 0 : offset; + + offset += sizeof(uint16); + while (lureExe.readWord() != 0xffff) + offset += sizeof(uint16); + offset += sizeof(uint16); + } + + // Adjust the total size to accomodate random action data in the output + totalSize += sizeof(uint16) * randomActions[roomIndex].numEntries + + (sizeof(uint16) * 2); + } + + totalSize += sizeof(uint16) + MAX_BUFFER_ENTRIES * sizeof(uint16); + + 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 + switch (language) { + case EN_ANY: + process_entry(0x13c2, data, totalSize); // RETURN sequence + process_entry(0xbb95, data, totalSize); // Exit blocked sequence + process_entry(0x706c, data, totalSize); // Jump proc #2 - go to castle basement + process_entry(0x728a, data, totalSize); + process_entry(0x76ec, data, totalSize); + process_entry(0x4ebb, data, totalSize); // Goewin as a follower in cave + process_entry(0x7D9D, data, totalSize); // Goewin standard handler + break; + case IT_ITA: + process_entry(0x13c2, data, totalSize); + process_entry(0xbc55, data, totalSize); + process_entry(0x712c, data, totalSize); + break; + case FR_FRA: + process_entry(0x13c2, data, totalSize); + process_entry(0xbc75, data, totalSize); + process_entry(0x714c, data, totalSize); + break; + case DE_DEU: + process_entry(0x13c2, data, totalSize); + process_entry(0xbca5, data, totalSize); + process_entry(0x717c, data, totalSize); + break; + case ES_ESP: + process_entry(0x13c2, data, totalSize); + process_entry(0xbc75, data, totalSize); + process_entry(0x714c, data, totalSize); + break; + default: + errorExit("read_action_sequence: Unknown language"); + } + + // Process the script engine list + + for (index = 0; index < NUM_TABLED_ACTION_BLOCKS; ++index) + if (offsetList[index] != 0) + process_entry(offsetList[index], data, totalSize); + + // Next process each of the character hotspots + + uint16 hsOffset = 0x5d98; + if (language == IT_ITA) hsOffset = 0x5e58; + else if (language == FR_FRA) hsOffset = 0x5e78; + else if (language == DE_DEU) hsOffset = 0x5ea8; + else if (language == ES_ESP) hsOffset = 0x5e78; + else if (language != EN_ANY) errorExit("read_action_sequence: Unknown language"); + + hotspotIndex = 0; + for (;;) { + lureExe.seek(dataSegment + hsOffset + + hotspotIndex * sizeof(HotspotHeaderEntry)); + lureExe.read(&entryHeader, sizeof(HotspotHeaderEntry)); + if (FROM_LE_16(entryHeader.offset) == 0xffff) break; + ++hotspotIndex; + + // Move to the action sequence area of the hotspot + lureExe.seek(dataSegment + entryHeader.offset + 0x63); + lureExe.read(&action, sizeof(CurrentActionInput)); + if (FROM_LE_16(action.action) == 2) + process_entry(FROM_LE_16(action.dataOffset), data, totalSize); + } + + // Finally process each of the random room actions + + for (roomIndex = 0; roomIndex < RANDOM_ROOM_NUM_ENTRIES; ++roomIndex) { + for (index = 0; index < randomActions[roomIndex].numEntries; ++index) { + if (randomActions[roomIndex].entries[index].offset != 0xfffe) { +//printf("room=%d entry=%xh\n", roomIndex+1, randomActions[roomIndex].entries[index].offset); + process_entry(randomActions[roomIndex].entries[index].offset, data, totalSize); + } + } + } + + // Output the list used in the script engine + + pHeader = (uint16 *) data; + for (index = 0; index < NUM_TABLED_ACTION_BLOCKS; ++index) + if (offsetList[index] == 0) + *pHeader++ = 0; + else + *pHeader++ = TO_LE_16(get_sequence_index(offsetList[index])); + *pHeader++ = TO_LE_16(0xffff); + + // Output the data for the random room actions + + for (roomIndex = 0; roomIndex < RANDOM_ROOM_NUM_ENTRIES; ++roomIndex) { + if (randomActions[roomIndex].numEntries == 0) + continue; + + *pHeader++ = TO_LE_16(roomIndex + 1); // Save the room number + + // Create a word containing the number of available actions and a bit flag set + // specifying which of the actions are repeatable (as opposed to once only) + uint16 v = randomActions[roomIndex].numEntries; + for (int entryCtr = 0; entryCtr < randomActions[roomIndex].numEntries; ++entryCtr) + if (randomActions[roomIndex].entries[entryCtr].repeatable) + v |= (0x100 << entryCtr); + *pHeader++ = TO_LE_16(v); + + // Loop through the entries storing the action set to use + for (int entryCtr = 0; entryCtr < randomActions[roomIndex].numEntries; ++entryCtr) + if (randomActions[roomIndex].entries[entryCtr].offset == 0) + *pHeader++ = 0; + else + *pHeader++ = TO_LE_16(get_sequence_index(randomActions[roomIndex].entries[entryCtr].offset)); + } + *pHeader++ = TO_LE_16(0xffff); + + // Output the offsets of each action set + + for (index = 0; index < numSupportEntries; ++index) + *pHeader++ = TO_LE_16(supportList[index].resourceOffset); + *pHeader++ = TO_LE_16(0xffff); + + // Free up the random room action array + for (roomIndex = 0; roomIndex < 1; ++roomIndex) { + if (randomActions[roomIndex].entries != NULL) + delete[] randomActions[roomIndex].entries; + } + delete[] randomActions; +} |