diff options
Diffstat (limited to 'tools/create_lure/process_actions.cpp')
-rw-r--r-- | tools/create_lure/process_actions.cpp | 341 |
1 files changed, 341 insertions, 0 deletions
diff --git a/tools/create_lure/process_actions.cpp b/tools/create_lure/process_actions.cpp new file mode 100644 index 0000000000..681d43283f --- /dev/null +++ b/tools/create_lure/process_actions.cpp @@ -0,0 +1,341 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2005-2006 The ScummVM project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#include "common/stdafx.h" +#include "common/scummsys.h" +#include "create_lure_dat.h" + +enum Action { + GET = 1, PUSH = 3, PULL = 4, OPERATE = 5, OPEN = 6, CLOSE = 7, LOCK = 8, + UNLOCK = 9, USE = 10, GIVE = 11, TALK_TO = 12, TELL = 13, BUY = 14, + LOOK = 15, LOOK_AT = 16, LOOK_THROUGH = 17, ASK = 18, DRINK = 20, + STATUS = 21, GO_TO = 22, RETURN = 23, BRIBE = 24, EXAMINE = 25, + NPC_SET_ROOM_AND_BLOCKED_OFFSET = 28, NPC_UNKNOWN1 = 29, NPC_EXEC_SCRIPT = 30, + NPC_UNKNOWN2 = 31, NPC_SET_RAND_DEST = 32, NPC_WALKING_CHECK = 33, + NPC_SET_SUPPORT_OFFSET = 34, NPC_SUPPORT_OFFSET_COND = 35, + NPC_DISPATCH_ACTION = 36, NPC_UNKNOWN3 = 37, NPC_UNKNOWN4 = 38, + NPC_START_TALKING = 39, NPC_JUMP_ADDRESS = 40, + NONE = 0 +}; + +struct CurrentActionOutput { + uint8 action; + uint8 hsAction; + uint16 roomNumber; + uint16 hotspotId; + uint16 usedId; +}; + +int numParams[NPC_JUMP_ADDRESS+1] = {0, + 1, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 2, 0, 1, + 0, 1, 1, 1, 1, 0, 0, 2, 0, 1, 0, 0, 1, 1, 2, 2, 5, 2, 2, 1}; + +#ifdef ENGLISH_LURE +#define NUM_JUMP_OFFSETS 2 +uint16 jumpOffsets[2] = {0x87be, 0x881c}; +#endif + +#define MAX_BUFFER_ENTRIES 50 +#define MAX_INSTRUCTION_ENTRIES 300 +#define SCHEDULE_DATA_OFFSET 0x60 + +struct SupportStructure { + uint16 offset; + int numInstructions; + uint16 instructionOffsets[MAX_INSTRUCTION_ENTRIES]; + uint16 resourceOffset; +}; + +SupportStructure supportList[MAX_BUFFER_ENTRIES]; +uint16 numSupportEntries = 0; + +#define FORWARD_JUMP_ALLOWANCE 0x30 + + +uint16 get_sequence_index(uint16 offset, int supportIndex) { + int index; + + if (supportIndex != -1) { + // Within a sequence, so if an offset is within it, it's a local jump + for (index = 0; index < supportList[supportIndex].numInstructions; ++index) { + if (supportList[supportIndex].instructionOffsets[index] == offset) + return index; + } + } + + for (int index = 0; index <= numSupportEntries; ++index) { + SupportStructure &rec = supportList[index]; + + if ((rec.numInstructions > 0) && + (offset >= rec.instructionOffsets[0]) && + (offset <= rec.instructionOffsets[rec.numInstructions - 1])) { + // Scan through the entry's insruction list + for (int iIndex = 0; iIndex < rec.numInstructions; ++iIndex) { + if (rec.instructionOffsets[iIndex] == offset) { + return ((index + 1) << 10) | iIndex; + } + } + } + } + + return 0xffff; +} + +struct SymbolTableEntry { + uint16 *p; + bool globalNeeded; +}; + +uint16 process_action_sequence_entry(int supportIndex, byte *data, uint16 remainingSize) { + SupportStructure &rec = supportList[supportIndex]; + uint16 startOffset = rec.offset; + uint16 maxOffset = 0; +#ifdef ENGLISH_LURE + if (startOffset == 0x7dcb) { startOffset = 0x7d9d; maxOffset = 0x7dcb; } + if (startOffset == 0x7248) { startOffset = 0x71ce; maxOffset = 0x7248; } + if (startOffset == 0x79a8) { startOffset = 0x785c; maxOffset = 0x79a8; } + if (startOffset == 0x6f4f) { startOffset = 0x6e5d; maxOffset = 0x6fe5; } + if (startOffset == 0x734a) maxOffset = 0x77a2; +#endif + + SymbolTableEntry symbolTable[MAX_INSTRUCTION_ENTRIES]; + uint16 numSymbols = 0; + uint16 offset = startOffset; + uint16 totalSize = 0; + uint16 actionNum = 0; + uint16 paramIndex; + uint16 params[5]; + uint16 index; + uint16 *pOut = (uint16 *) data; + + lure_exe.seek(DATA_SEGMENT + startOffset); + rec.numInstructions = 0; + + for (;;) { + if (remainingSize < 10) { + printf("Ran out of space to process NPC action sequences\n"); + exit(1); + } + + // Check for end of sequence set with prior instruction + if ((actionNum == NPC_SET_SUPPORT_OFFSET) && ((maxOffset == 0) || + (offset > maxOffset))) + break; + + // Mark the offset of the next instruction + rec.instructionOffsets[rec.numInstructions++] = offset; + if (rec.numInstructions == MAX_INSTRUCTION_ENTRIES) { + printf("A method exceeded the maximum allowable number of instructions\n"); + exit(1); + } + + // Get in the next action + actionNum = lure_exe.readWord(); + +// printf("%xh - action=%d", offset, actionNum); + + if (actionNum == 0) { + // At end of script block +// printf("\n"); + break; + } + else if (actionNum > NPC_JUMP_ADDRESS) { + // Unknown action code - halt execution + printf("%xh - unknown action %d\n", offset, actionNum); + exit(1); + } + + *pOut++ = TO_LE_16(actionNum); + + // Read in any action parameters + for (int paramCtr = 0; paramCtr < numParams[actionNum]; ++paramCtr) + params[paramCtr] = lure_exe.readWord(); + + switch(actionNum) { + case NPC_SET_ROOM_AND_BLOCKED_OFFSET: + case NPC_SET_SUPPORT_OFFSET: + case NPC_SUPPORT_OFFSET_COND: + case NPC_DISPATCH_ACTION: + // These instructions have a support record parameter. Store the + // offset the parameter will be in the output data so we can come + // back at the end and resolve it + paramIndex = (actionNum == NPC_SET_SUPPORT_OFFSET) ? 0 : 1; + symbolTable[numSymbols].globalNeeded = actionNum == NPC_SET_ROOM_AND_BLOCKED_OFFSET; + symbolTable[numSymbols].p = pOut + paramIndex; + ++numSymbols; + + // Special check for forward references - it's considered to be in + // the same block if it's forward within 100h blocks + if ((params[paramIndex] > offset) && + (params[paramIndex] < offset + FORWARD_JUMP_ALLOWANCE) && + (params[paramIndex] > maxOffset)) { + maxOffset = params[paramIndex]; + } + break; + + case NPC_JUMP_ADDRESS: + // Make sure the address is in the known list + index = 0; + while ((index < NUM_JUMP_OFFSETS) && (jumpOffsets[index] != params[0])) + ++index; + + if (index != NUM_JUMP_OFFSETS) + // Replace code offset with an index + params[0] = index; + else { + printf("Encountered unrecognised NPC code jump point: %xh\n", params[0]); + exit(1); + } + break; + + default: + break; + } + + // Output parameters + for (paramIndex = 0; paramIndex < numParams[actionNum]; ++paramIndex) + *pOut++ = TO_LE_16(params[paramIndex]); + + // Increase size + totalSize += (numParams[actionNum] + 1) * sizeof(uint16); + offset = startOffset + totalSize; + remainingSize -= (numParams[actionNum] + 1) * sizeof(uint16); + } + + // Flag an end of the sequence + *pOut++ = 0; + totalSize += sizeof(uint16); + + // handle post-processing of the symbol list + + for (int symbolCtr = 0; symbolCtr < numSymbols; ++symbolCtr) { + if (READ_LE_UINT16(symbolTable[symbolCtr].p) == 0) + // No Id special constant + WRITE_LE_UINT16(symbolTable[symbolCtr].p, 0xffff); + else { + // Handle resolving the constant + index = get_sequence_index(READ_LE_UINT16(symbolTable[symbolCtr].p), + symbolTable[symbolCtr].globalNeeded ? -1 : supportIndex); +//printf("Symbol %xh => %xh\n", *symbolTable[symbolCtr].p, index); + if (index != 0xffff) { + // Jump found - so replace symbol entry with it + WRITE_LE_UINT16(symbolTable[symbolCtr].p, index); + } else { + printf("Sequence contained unknown offset %xh\n", + READ_LE_UINT16(symbolTable[symbolCtr].p)); + exit(1); + } + } + } + + return totalSize; +} + +void process_entry(uint16 offset, byte *data, uint16 &totalSize) { + if (get_sequence_index(offset) == 0xffff) { + // Process the next entry + supportList[numSupportEntries].offset = offset; + supportList[numSupportEntries].numInstructions = 0; + supportList[numSupportEntries].resourceOffset = totalSize; +//printf("process_entry index=%d, offset=%xh\n", numSupportEntries + 1, offset); + totalSize += process_action_sequence_entry(numSupportEntries, + data + totalSize, MAX_DATA_SIZE - totalSize); + + ++numSupportEntries; + if (numSupportEntries == MAX_BUFFER_ENTRIES) { + printf("Ran out of buffer space in processing NPC schedules\n"); + exit(1); + } + } +} + +void read_action_sequence(byte *&data, uint16 &totalSize) +{ + const uint16 hsOffset = 0x5d98; + const uint16 hsStartId = 0x3e8; + uint16 hotspotIndex; + HotspotHeaderEntry entryHeader; + CurrentActionInput action; + uint16 *pHeader; + int index; + + // Allocate enough space for output sequence list + data = (byte *) malloc(MAX_DATA_SIZE); + + // Get a list of offsets used in the script engine + uint16 offsetList[NUM_TABLED_ACTION_BLOCKS]; + lure_exe.seek(DATA_SEGMENT + TABLED_ACTIONS_OFFSET, SEEK_SET); + for (index = 0; index < NUM_TABLED_ACTION_BLOCKS; ++index) + offsetList[index] = lure_exe.readWord(); + + totalSize = SCHEDULE_DATA_OFFSET; + numSupportEntries = 0; + + // Handle required initial entries - the Lure engine refers to them directly by + // index, so they need to be first, and in that order +#ifdef ENGLISH_LURE + process_entry(0x13c2, data, totalSize); + process_entry(0xbb95, data, totalSize); + process_entry(0x7060, data, totalSize); + process_entry(0x728a, data, totalSize); +#endif + + // Process the script engine list + + for (index = 0; index < NUM_TABLED_ACTION_BLOCKS; ++index) + process_entry(offsetList[index], data, totalSize); + + // Next process each of the character hotspots + + hotspotIndex = 0; + for (;;) { + lure_exe.seek(DATA_SEGMENT + hsOffset + + hotspotIndex * sizeof(HotspotHeaderEntry)); + lure_exe.read(&entryHeader, sizeof(HotspotHeaderEntry)); + if (FROM_LE_16(entryHeader.offset) == 0xffff) break; + ++hotspotIndex; + + // Move to the action sequence area of the hotspot + lure_exe.seek(DATA_SEGMENT + entryHeader.offset + 0x63); + lure_exe.read(&action, sizeof(CurrentActionInput)); + if (FROM_LE_16(action.action) == 2) + process_entry(FROM_LE_16(action.dataOffset), data, totalSize); + } + + // Output the list used in the script engine + + pHeader = (uint16 *) data; + for (index = 0; index < NUM_TABLED_ACTION_BLOCKS; ++index) + *pHeader++ = TO_LE_16(get_sequence_index(offsetList[index])); + *pHeader++ = 0xffff; + + // Output the offsets of each action set + + for (index = 0; index < numSupportEntries; ++index) + *pHeader++ = TO_LE_16(supportList[index].resourceOffset); + *pHeader++ = 0xffff; + + if ((int) ((byte *) pHeader - data) > SCHEDULE_DATA_OFFSET) { + printf("SCHEDULE_DATA_OFFSET was not high enough\n"); + exit(1); + } +} |