1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "common/debug-channels.h"
24 #include "common/file.h"
25 #include "common/textconsole.h"
26 
27 #include "agos/agos.h"
28 #include "agos/intern.h"
29 #include "agos/sound.h"
30 
31 namespace AGOS {
32 
33 // Script opcodes to load into memory
34 static const char *const opcodeArgTable_elvira1[300] = {
35 	"I ", "I ", "I ", "I ", "I ", "I ", "I ", "I ",	 "II ",	"II ", "II ", "II ", "F ", "F ", "FN ",
36 	"FN ", "FN ", "FN ", "FF ", "FF ", "FF ", "FF ", "II ", "II ", "a ", "a ", "n ", "n ", "p ",
37 	"N ", "I ", "I ", "I ",	 "I ",	"IN ",	"IB ", "IB ", "II ", "IB ", "N ", " ", " ", " ", "I ",
38 	"I ","I ","I ", "I ","I ","I ",	"II ","II ","II ","II ","IBF ", "FIB ", "FF ", "N ", "NI ",
39 	"IF ", "F ", "F ", "IB ", "IB ", "FN ",	"FN ", "FN ", "FF ", "FF ", "FN ", "FN ", "FF ", "FF ",
40 	"FN ", "FF ", "FN ", "F ", "I ", "IN ", "IN ", "IB ", "IB ", "IB ", "IB ", "II ", "I ", "I ",
41 	"IN ", "T ", "F ", " ", "T ", "T ", "I ", "I ", " ", " ", "T ", " ", " ", " ", " ", " ", "T ",
42 	" ", "N ", "INN ", "II ", "II ", "ITN ", "ITIN ", "ITIN ", "I3 ", "IN ", "I ",	 "I ", "Ivnn ",
43 	"vnn ", "Ivnn ", "NN ",	"IT ", "INN ", " ", "N ", "N ", "N ", "T ", "v ", " ", " ", " ", " ",
44 	"FN ", "I ", "TN ", "IT ", "II ", "I ", " ", "N ", "I ", " ", "I ", "NI ", "I ", "I ", "T ",
45 	"I ", "I ", "N ", "N ", " ", "N ", "IF ", "IF ", "IF ", "IF ", "IF ", "IF ", "T ", "IB ",
46 	"IB ", "IB ", "I ", " ", "vnnN ", "Ivnn ", "T ", "T ", "T ", "IF ", " ", " ", " ", "Ivnn ",
47 	"IF ", "INI ", "INN ", "IN ", "II ", "IFF ", "IIF ", "I ", "II ", "I ", "I ", "IN ", "IN ",
48 	"II ", "II ", "II ", "II ", "IIN ", "IIN ", "IN ", "II ", "IN ", "IN ", "T ", "vanpan ",
49 	"vIpI ", "T ", "T ", " ", " ",	"IN ", "IN ", "IN ", "IN ", "N ", "INTTT ", "ITTT ",
50 	"ITTT ", "I ", "I ", "IN ", "I ", " ", "F ", "NN ", "INN ", "INN ", "INNN ", "TF ", "NN ",
51 	"N ", "NNNNN ", "N ", " ", "NNNNNNN ", "N ", " ", "N ",	"NN ", "N ", "NNNNNIN ", "N ", "N ",
52 	"N ", "NNN ", "NNNN ", "INNN ", "IN ", "IN ", "TT ", "I ", "I ", "I ", "TTT ", "IN ", "IN ",
53 	"FN ", "FN ", "FN ", "N ", "N ", "N ", "NI ", " ", " ",	 "N ", "I ", "INN ", "NN ", "N ",
54 	"N ", "Nan ", "NN ", " ", " ", " ", " ", " ", " ", " ", "IF ", "N ", " ", " ",	 " ", "II ",
55 	" ", "NI ","N ",
56 };
57 
58 static const char *const opcodeArgTable_elvira2[256] = {
59 	" ", "I ", "I ", "I ", "I ", "I ", "I ", "II ", "II ", "II ", "II ", "B ", "B ", "BN ", "BN ",
60 	"BN ", "BN ", "BB ", "BB ", "BB ", "BB ", "II ", "II ", "N ", "I ", "I ", "I ", "IN ", "IB ",
61 	"II ", "I ", "I ", "II ", "II ", "IBB ", "BIB ", "BB ", "B ", "BI ", "IB ", "B ", "B ", "BN ",
62 	"BN ", "BN ", "BB ", "BB ", "BN ", "BN ", "BB ", "BB ", "BN ", "BB ", "BN ", "B ", "I ", "IB ",
63 	"IB ", "II ", "I ", "I ", "IN ", "B ", "T ", "T ", "I  ", "I ", " ", "T ", " ", " ",
64 	"N ", "IBN ", "I ", "I ", "I ", "NN ", " ", " ", "IT ", "II ", "I ", "B ", " ", "IB ", "IBB ",
65 	"IIB ", "T ", "T ", "T ", "IB ", "IB ", "IB ", "B ", "BB ", "IBB ", "NB ", "N ", "NBNNN ", "N ",
66 	" ", "BNNNNNN ", "B ", " ", "B ", "B ", "N ", "NNNNNIN ", "N ", "N ", "N ", "NNN ", "NBNN ",
67 	"IBNN ", "IB ", "IB ", "IB ", "IB ", "N ", "N ", "N ", "BI ", " ", " ", "N ", "I ", "IBB ",
68 	"NN ", "N ", "N ", "Ban ", "BB ", " ", " ", " ", " ", "IB ", "B ", " ", "II ", " ", "BI ", "N ",
69 	"I ", "IB ", "IB ", "IB ", "IB ", "IB ", "IB ", "IB ", "BI ", "BB ", "B ", "B ", "B ",	"B ",
70 	"IBB ", "IBN ", "IB ", "B ", " ", "TB ", "TB ", "I ", "N ", "B ", "INB ", "INB ", "INB ", "INB ",
71 	"INB ", "INB ", "INB ", "N ", " ", "INBB ", "B ", "B ", "Ian ", "B ", "B ", "B ", "B ", "T ",
72 	"T ", "B ", " ", "I ", " ", " "
73 };
74 
75 static const char *const opcodeArgTable_waxworks[256] = {
76 	" ", "I ", "I ", "I ", "I ", "I ", "I ", "II ", "II ", "II ", "II ", "B ", "B ", "BN ", "BN ",
77 	"BN ", "BN ", "BB ", "BB ", "BB ", "BB ", "II ", "II ", "N ", "I ", "I ", "I ", "IN ", "IB ",
78 	"II ", "I ", "I ", "II ", "II ", "IBB ", "BIB ", "BB ", "B ", "BI ", "IB ", "B ", "B ", "BN ",
79 	"BN ", "BN ", "BB ", "BB ", "BN ", "BN ", "BB ", "BB ", "BN ", "BB ", "BN ", "B ", "I ", "IB ",
80 	"IB ", "II ", "I ", "I ", "IN ", "B ", "T ", "T ", "NNNNNB ", "BT ", "BT ", "T ", " ", "B ",
81 	"N ", "IBN ", "I ", "I ", "I ", "NN ", " ", " ", "IT ", "II ", "I ", "B ", " ", "IB ", "IBB ",
82 	"IIB ", "T ", "T ", "T ", "IB ", "IB ", "IB ", "B ", "BB ", "IBB ", "NB ", "N ", "NBNNN ", "N ",
83 	" ", "BNNNNNN ", "B ", " ", "B ", "B ", "BB ", "NNNNNIN ", "N ", "N ", "N ", "NNN ", "NBNN ",
84 	"IBNN ", "IB ", "IB ", "IB ", "IB ", "N ", "N ", "N ", "BI ", " ", " ", "N ", "I ", "IBB ",
85 	"NN ", "N ", "N ", "Ban ", "BB ", " ", " ", " ", " ", "IB ", "B ", " ", "II ", " ", "BI ", "N ",
86 	"I ", "IB ", "IB ", "IB ", "IB ", "IB ", "IB ", "IB ", "BI ", "BB ", "B ", "B ", "B ",	"B ",
87 	"IBB ", "IBN ", "IB ", "B ", " ", "TB ", "TB ", "I ", "N ", "B ", "INB ", "INB ", "INB ", "INB ",
88 	"INB ", "INB ", "INB ", "N ", " ", "INBB ", "B ", "B ", "Ian ", "B ", "B ", "B ", "B ", "T ",
89 	"T ", "B ", " ", "I ", " ", " "
90 };
91 
92 static const char *const opcodeArgTable_simon1talkie[256] = {
93 	" ", "I ", "I ", "I ", "I ", "I ", "I ", "II ", "II ", "II ", "II ", "B ", "B ", "BN ", "BN ",
94 	"BN ", "BN ", "BB ", "BB ", "BB ", "BB ", "II ", "II ", "N ", "I ", "I ", "I ", "IN ", "IB ",
95 	"II ", "I ", "I ", "II ", "II ", "IBB ", "BIB ", "BB ", "B ", "BI ", "IB ", "B ", "B ", "BN ",
96 	"BN ", "BN ", "BB ", "BB ", "BN ", "BN ", "BB ", "BB ", "BN ", "BB ", "BN ", "B ", "I ", "IB ",
97 	"IB ", "II ", "I ", "I ", "IN ", "B ", "T ", "T ", "NNNNNB ", "BT ", "BTS ", "T ", " ", "B ",
98 	"N ", "IBN ", "I ", "I ", "I ", "NN ", " ", " ", "IT ", "II ", "I ", "B ", " ", "IB ", "IBB ",
99 	"IIB ", "T ", " ", " ", "IB ", "IB ", "IB ", "B ", "BB ", "IBB ", "NB ", "N ", "NBNNN ", "N ",
100 	" ", "BNNNNNN ", "B ", " ", "B ", "B ", "BB ", "NNNNNIN ", "N ", "N ", "N ", "NNN ", "NBNN ",
101 	"IBNN ", "IB ", "IB ", "IB ", "IB ", "N ", "N ", "N ", "BI ", " ", " ", "N ", "I ", "IBB ",
102 	"NN ", "N ", "N ", "Ban ", "BB ", " ", " ", " ", " ", "IB ", "B ", " ", "II ", " ", "BI ", "N ",
103 	"I ", "IB ", "IB ", "IB ", "IB ", "IB ", "IB ", "IB ", "BI ", "BB ", "B ", "B ", "B ", "B ",
104 	"IBB ", "IBN ", "IB ", "B ", "BNBN ", "BBTS ", "N ", " ", "Ian ", "B ", "B ", "B ", "B ", "T ",
105 	"T ", "B ", " ", "I ", " ", " ", "BBI ", "NNBB ", "BBB ", " ", " ", " ", " ", "N ", "N ", " ",
106 	" ",
107 };
108 
109 static const char *const opcodeArgTable_simon1dos[256] = {
110 	" ", "I ", "I ", "I ", "I ", "I ", "I ", "II ", "II ", "II ", "II ", "B ", "B ", "BN ", "BN ",
111 	"BN ", "BN ", "BB ", "BB ", "BB ", "BB ", "II ", "II ", "N ", "I ", "I ", "I ", "IN ", "IB ",
112 	"II ", "I ", "I ", "II ", "II ", "IBB ", "BIB ", "BB ", "B ", "BI ", "IB ", "B ", "B ", "BN ",
113 	"BN ", "BN ", "BB ", "BB ", "BN ", "BN ", "BB ", "BB ", "BN ", "BB ", "BN ", "B ", "I ", "IB ",
114 	"IB ", "II ", "I ", "I ", "IN ", "B ", "T ", "T ", "NNNNNB ", "BT ", "BT ", "T ", " ", "B ",
115 	"N ", "IBN ", "I ", "I ", "I ", "NN ", " ", " ", "IT ", "II ", "I ", "B ", " ", "IB ", "IBB ",
116 	"IIB ", "T ", " ", " ", "IB ", "IB ", "IB ", "B ", "BB ", "IBB ", "NB ", "N ", "NBNNN ", "N ",
117 	" ", "BNNNNNN ", "B ", " ", "B ", "B ", "BB ", "NNNNNIN ", "N ", "N ", "N ", "NNN ", "NBNN ",
118 	"IBNN ", "IB ", "IB ", "IB ", "IB ", "N ", "N ", "N ", "BI ", " ", " ", "N ", "I ", "IBB ",
119 	"NN ", "N ", "N ", "Ban ", "BB ", " ", " ", " ", " ", "IB ", "B ", " ", "II ", " ", "BI ", "N ",
120 	"I ", "IB ", "IB ", "IB ", "IB ", "IB ", "IB ", "IB ", "BI ", "BB ", "B ", "B ", "B ", "B ",
121 	"IBB ", "IBN ", "IB ", "B ", "BNBN ", "BBT ", "N ", " ", "Ian ", "B ", "B ", "B ", "B ", "T ",
122 	"T ", "B ", " ", "I ", " ", " ", "BBI ", "NNBB ", "BBB ", " ", " ", " ", " ", "N ", "N ", " ",
123 	" ",
124 };
125 
126 static const char *const opcodeArgTable_simon2talkie[256] = {
127 	" ", "I ", "I ", "I ", "I ", "I ", "I ", "II ", "II ", "II ", "II ", "B ", "B ", "BN ", "BN ",
128 	"BN ", "BN ", "BB ", "BB ", "BB ", "BB ", "II ", "II ", "N ", "I ", "I ", "I ", "IN ", "IB ",
129 	"II ", "I ", "I ", "II ", "II ", "IBB ", "BIB ", "BB ", "B ", "BI ", "IB ", "B ", "B ", "BN ",
130 	"BN ", "BN ", "BB ", "BB ", "BN ", "BN ", "BB ", "BB ", "BN ", "BB ", "BN ", "B ", "I ", "IB ",
131 	"IB ", "II ", "I ", "I ", "IN ", "B ", "T ", "T ", "NNNNNB ", "BT ", "BTS ", "T ", " ", "B ",
132 	"N ", "IBN ", "I ", "I ", "I ", "NN ", " ", " ", "IT ", "II ", "I ", "B ", " ", "IB ", "IBB ",
133 	"IIB ", "T ", " ", " ", "IB ", "IB ", "IB ", "B ", "BB ", "IBB ", "NB ", "N ", "NNBNNN ", "NN ",
134 	" ", "BNNNNNN ", "B ", " ", "B ", "B ", "BB ", "NNNNNIN ", "N ", "N ", "N ", "NNN ", "NBNN ",
135 	"IBNN ", "IB ", "IB ", "IB ", "IB ", "N ", "N ", "N ", "BI ", " ", " ", "N ", "I ", "IBB ",
136 	"NNB ", "N ", "N ", "Ban ", "BB ", " ", " ", " ", " ", "IB ", "B ", " ", "II ", " ", "BI ",
137 	"N ", "I ", "IB ", "IB ", "IB ", "IB ", "IB ", "IB ", "IB ", "BI ", "BB ", "B ", "B ", "B ",
138 	"B ", "IBB ", "IBN ", "IB ", "B ", "BNBN ", "BBTS ", "N ", " ", "Ian ", "B ", "B ", "B ", "B ",
139 	"T ", "T ", "B ", " ", "I ", " ", " ", "BBI ", "NNBB ", "BBB ", " ", " ", " ", " ", "N ", "N ",
140 	" ", " ", "BT ", " ", "B "
141 };
142 
143 static const char *const opcodeArgTable_simon2dos[256] = {
144 	" ", "I ", "I ", "I ", "I ", "I ", "I ", "II ", "II ", "II ", "II ", "B ", "B ", "BN ", "BN ",
145 	"BN ", "BN ", "BB ", "BB ", "BB ", "BB ", "II ", "II ", "N ", "I ", "I ", "I ", "IN ", "IB ",
146 	"II ", "I ", "I ", "II ", "II ", "IBB ", "BIB ", "BB ", "B ", "BI ", "IB ", "B ", "B ", "BN ",
147 	"BN ", "BN ", "BB ", "BB ", "BN ", "BN ", "BB ", "BB ", "BN ", "BB ", "BN ", "B ", "I ", "IB ",
148 	"IB ", "II ", "I ", "I ", "IN ", "B ", "T ", "T ", "NNNNNB ", "BT ", "BT ", "T ", " ", "B ",
149 	"N ", "IBN ", "I ", "I ", "I ", "NN ", " ", " ", "IT ", "II ", "I ", "B ", " ", "IB ", "IBB ",
150 	"IIB ", "T ", " ", " ", "IB ", "IB ", "IB ", "B ", "BB ", "IBB ", "NB ", "N ", "NNBNNN ", "NN ",
151 	" ", "BNNNNNN ", "B ", " ", "B ", "B ", "BB ", "NNNNNIN ", "N ", "N ", "N ", "NNN ", "NBNN ",
152 	"IBNN ", "IB ", "IB ", "IB ", "IB ", "N ", "N ", "N ", "BI ", " ", " ", "N ", "I ", "IBB ",
153 	"NNB ", "N ", "N ", "Ban ", "BB ", " ", " ", " ", " ", "IB ", "B ", " ", "II ", " ", "BI ",
154 	"N ", "I ", "IB ", "IB ", "IB ", "IB ", "IB ", "IB ", "IB ", "BI ", "BB ", "B ", "B ", "B ",
155 	"B ", "IBB ", "IBN ", "IB ", "B ", "BNBN ", "BBT ", "N ", " ", "Ian ", "B ", "B ", "B ", "B ",
156 	"T ", "T ", "B ", " ", "I ", " ", " ", "BBI ", "NNBB ", "BBB ", " ", " ", " ", " ", "N ", "N ",
157 	" ", " ", "BT ", " ", "B "
158 };
159 
160 static const char *const opcodeArgTable_feeblefiles[256] = {
161 	" ", "I ", "I ", "I ", "I ", "I ", "I ", "II ", "II ", "II ", "II ", "B ", "B ", "BN ", "BN ",
162 	"BN ", "BN ", "BB ", "BB ", "BB ", "BB ", "II ", "II ", "N ", "I ", "I ", "I ", "IN ", "IB ",
163 	"II ", "I ", "I ", "II ", "II ", "IBB ", "BIB ", "BB ", "B ", "BI ", "IB ", "B ", "B ", "BN ",
164 	"BN ", "BN ", "BB ", "BB ", "BN ", "BN ", "BB ", "BB ", "BN ", "BB ", "BN ", "B ", "I ", "IB ",
165 	"IB ", "II ", "I ", "I ", "IN ", "B ", "T ", "T ", "NNNNNB ", "BT ", "BTS ", "T ", " ", "B ",
166 	"N ", "IBN ", "I ", "I ", "I ", "NN ", " ", " ", "IT ", "II ", "I ", "B ", " ", "IB ", "IBB ",
167 	"IIB ", "T ", " ", " ", "IB ", "IB ", "IB ", "B ", "BB ", "IBB ", "NB ", "N ", "NNBNNN ", "NN ",
168 	" ", "BNNNNNN ", "B ", " ", "B ", "B ", "BB ", "NNNNNIN ", "N ", "N ", "N ", "NNN ", "NBNN ",
169 	"IBNN ", "IB ", "IB ", "IB ", "IB ", "N ", "N ", "N ", "BI ", " ", " ", "N ", "I ", "IBB ",
170 	"NNB ", "N ", "N ", "Ban ", " ", " ", " ", " ", " ", "IB ", "B ", " ", "II ", " ", "BI ",
171 	"N ", "I ", "IB ", "IB ", "IB ", "IB ", "IB ", "IB ", "IB ", "BI ", "BB ", "B ", "B ", "B ",
172 	"B ", "IBB ", "IBN ", "IB ", "B ", "BNNN ", "BBTS ", "N ", " ", "Ian ", "B ", "B ", "B ", "B ",
173 	"T ", "N ", " ", " ", "I ", " ", " ", "BBI ", "NNBB ", "BBB ", " ", " ", "T ", " ", "N ", "N ",
174 	" ", " ", "BT ", " ", "B ", " ", "BBBB ", " ", " ", "BBBB ", "B ", "B ", "B ", "B "
175 };
176 
177 static const char *const opcodeArgTable_puzzlepack[256] = {
178 	" ", "I ", "I ", "I ", "I ", "I ", "I ", "II ", "II ", "II ", "II ", "N ", "N ", "NN ", "NN ",
179 	"NN ", "NN ", "NN ", "NN ", "NN ", "NN ", "II ", "II ", "N ", "I ", "I ", "I ", "IN ", "IB ",
180 	"II ", "I ", "I ", "II ", "II ", "IBN ", "NIB ", "NN ", "B ", "BI ", "IN ", "N ", "N ", "NN ",
181 	"NN ", "NN ", "NN ", "NN ", "NN ", "NN ", "NN ", "NN ", "NN ", "NN ", "NN ", "B ", "I ", "IB ",
182 	"IB ", "II ", "I ", "I ", "IN ", "N ", "T ", "T ", "NNNNNB ", "BTNN ", "BTS ", "T ", " ", "B ",
183 	"N ", "IBN ", "I ", "I ", "I ", "NN ", " ", " ", "IT ", "II ", "I ", "B ", " ", "IB ", "IBB ",
184 	"IIB ", "T ", " ", " ", "IB ", "IB ", "IB ", "B ", "BB ", "IBB ", "NB ", "N ", "NNBNNN ", "NN ",
185 	" ", "BNNNNNN ", "B ", " ", "B ", "B ", "BB ", "NNNNNIN ", "N ", "N ", "N ", "NNN ", "NBNN ",
186 	"IBNN ", "IB ", "IB ", "IB ", "IB ", "N ", "N ", "N ", "BI ", " ", " ", "N ", "I ", "IBB ",
187 	"NNB ", "N ", "N ", "Ban ", " ", " ", " ", " ", " ", "IN ", "B ", " ", "II ", " ", "BI ",
188 	"N ", "I ", "IB ", "IB ", "IB ", "IB ", "IB ", "IB ", "IB ", "BI ", "BB ", "N ", "N ", "N ",
189 	"N ", "IBN ", "IBN ", "IN ", "B ", "BNNN ", "BBTS ", "N ", " ", "Ian ", "B ", "B ", "B ", "B ",
190 	"T ", "N ", " ", " ", "I ", " ", " ", "BBI ", "NNBB ", "BBB ", " ", " ", "T ", " ", "N ", "N ",
191 	" ", " ", "BT ", " ", "B ", " ", "BBBB ", " ", " ", "BBBB ", "B ", "B ", "B ", "B "
192 };
193 
getSubroutineByID(uint subroutineId)194 Subroutine *AGOSEngine::getSubroutineByID(uint subroutineId) {
195 	Subroutine *cur;
196 
197 	for (cur = _subroutineList; cur; cur = cur->next) {
198 		if (cur->id == subroutineId)
199 			return cur;
200 	}
201 
202 	if (loadXTablesIntoMem(subroutineId)) {
203 		for (cur = _subroutineList; cur; cur = cur->next) {
204 			if (cur->id == subroutineId)
205 				return cur;
206 		}
207 	}
208 
209 	if (loadTablesIntoMem(subroutineId)) {
210 		for (cur = _subroutineList; cur; cur = cur->next) {
211 			if (cur->id == subroutineId)
212 				return cur;
213 		}
214 	}
215 
216 	debug(0,"getSubroutineByID: subroutine %d not found", subroutineId);
217 	return NULL;
218 }
219 
alignTableMem()220 void AGOSEngine::alignTableMem() {
221 	if (!IS_ALIGNED(_tablesHeapPtr, 4)) {
222 		_tablesHeapPtr += 2;
223 		_tablesHeapCurPos += 2;
224 	}
225 }
226 
allocateTable(uint size)227 void *AGOSEngine::allocateTable(uint size) {
228 	byte *org = _tablesHeapPtr;
229 
230 	size = (size + 1) & ~1;
231 
232 	_tablesHeapPtr += size;
233 	_tablesHeapCurPos += size;
234 
235 	if (_tablesHeapCurPos > _tablesHeapSize)
236 		error("Tablesheap overflow");
237 
238 	return org;
239 }
240 
allocTablesHeap()241 void AGOSEngine::allocTablesHeap() {
242 	_tablesHeapSize = _tableMemSize;
243 	_tablesHeapCurPos = 0;
244 	_tablesHeapPtr = (byte *)calloc(_tableMemSize, 1);
245 	if (!_tablesHeapPtr)
246 		error("Out Of Memory - Tables");
247 }
248 
endCutscene()249 void AGOSEngine::endCutscene() {
250 	Subroutine *sub;
251 
252 	_sound->stopVoice();
253 
254 	sub = getSubroutineByID(170);
255 	if (sub != NULL)
256 		startSubroutineEx(sub);
257 
258 	_runScriptReturn1 = true;
259 }
260 
openTablesFile(const char * filename)261 Common::SeekableReadStream *AGOSEngine::openTablesFile(const char *filename) {
262 	if (getFeatures() & GF_OLD_BUNDLE)
263 		return openTablesFile_simon1(filename);
264 	else
265 		return openTablesFile_gme(filename);
266 }
267 
openTablesFile_simon1(const char * filename)268 Common::SeekableReadStream *AGOSEngine::openTablesFile_simon1(const char *filename) {
269 	Common::File *in = new Common::File();
270 	if (!in->open(filename))
271 		error("openTablesFile: Can't open '%s'", filename);
272 	return in;
273 }
274 
openTablesFile_gme(const char * filename)275 Common::SeekableReadStream *AGOSEngine::openTablesFile_gme(const char *filename) {
276 	uint res;
277 	uint32 offs;
278 
279 	res = atoi(filename + 6) + _tableIndexBase - 1;
280 	offs = _gameOffsetsPtr[res];
281 
282 	_gameFile->seek(offs, SEEK_SET);
283 	return _gameFile;
284 }
285 
loadTablesIntoMem(uint16 subrId)286 bool AGOSEngine::loadTablesIntoMem(uint16 subrId) {
287 	byte *p;
288 	uint16 min_num, max_num, file_num;
289 	Common::SeekableReadStream *in;
290 	char filename[30];
291 
292 	if (_tblList == NULL)
293 		return 0;
294 
295 	p = _tblList + 32;
296 
297 	min_num = READ_BE_UINT16(p);
298 	max_num = READ_BE_UINT16(p + 2);
299 	file_num = *(p + 4);
300 	p += 6;
301 
302 	while (min_num) {
303 		if ((subrId >= min_num) && (subrId <= max_num)) {
304 			_subroutineList = _subroutineListOrg;
305 			_tablesHeapPtr = _tablesHeapPtrOrg;
306 			_tablesHeapCurPos = _tablesHeapCurPosOrg;
307 			_stringIdLocalMin = 1;
308 			_stringIdLocalMax = 0;
309 
310 			sprintf(filename, "TABLES%.2d", file_num);
311 			in = openTablesFile(filename);
312 			readSubroutineBlock(in);
313 			closeTablesFile(in);
314 
315 			alignTableMem();
316 
317 			_tablesheapPtrNew = _tablesHeapPtr;
318 			_tablesHeapCurPosNew = _tablesHeapCurPos;
319 
320 			if (_tablesHeapCurPos > _tablesHeapSize)
321 				error("loadTablesIntoMem: Out of table memory");
322 			return 1;
323 		}
324 
325 		min_num = READ_BE_UINT16(p);
326 		max_num = READ_BE_UINT16(p + 2);
327 		file_num = *(p + 4);
328 		p += 6;
329 	}
330 
331 	debug(1,"loadTablesIntoMem: didn't find %d", subrId);
332 	return 0;
333 }
334 
loadTablesIntoMem(uint16 subrId)335 bool AGOSEngine_Waxworks::loadTablesIntoMem(uint16 subrId) {
336 	byte *p;
337 	uint min_num, max_num;
338 	Common::SeekableReadStream *in;
339 
340 	p = _tblList;
341 	if (p == NULL)
342 		return 0;
343 
344 	while (*p) {
345 		Common::String filename;
346 		while (*p)
347 			filename += *p++;
348 		p++;
349 
350 		if (getPlatform() == Common::kPlatformAcorn) {
351 			filename += ".DAT";
352 		}
353 
354 		for (;;) {
355 			min_num = READ_BE_UINT16(p); p += 2;
356 			if (min_num == 0)
357 				break;
358 
359 			max_num = READ_BE_UINT16(p); p += 2;
360 
361 			if (subrId >= min_num && subrId <= max_num) {
362 				_subroutineList = _subroutineListOrg;
363 				_tablesHeapPtr = _tablesHeapPtrOrg;
364 				_tablesHeapCurPos = _tablesHeapCurPosOrg;
365 				_stringIdLocalMin = 1;
366 				_stringIdLocalMax = 0;
367 
368 				in = openTablesFile(filename.c_str());
369 				readSubroutineBlock(in);
370 				closeTablesFile(in);
371 				if (getGameType() == GType_SIMON2) {
372 					_sound->loadSfxTable(getFileName(GAME_GMEFILE), _gameOffsetsPtr[atoi(filename.c_str() + 6) - 1 + _soundIndexBase]);
373 				} else if (getGameType() == GType_SIMON1 && getPlatform() == Common::kPlatformWindows) {
374 					filename.setChar('S', 0);
375 					filename.setChar('F', 1);
376 					filename.setChar('X', 2);
377 					filename.setChar('X', 3);
378 					filename.setChar('X', 4);
379 					filename.setChar('X', 5);
380 					if (atoi(filename.c_str() + 6) != 1 && atoi(filename.c_str() + 6) != 30)
381 						_sound->readSfxFile(filename);
382 				}
383 
384 				alignTableMem();
385 
386 				_tablesheapPtrNew = _tablesHeapPtr;
387 				_tablesHeapCurPosNew = _tablesHeapCurPos;
388 
389 				if (_tablesHeapCurPos > _tablesHeapSize)
390 					error("loadTablesIntoMem: Out of table memory");
391 				return 1;
392 			}
393 		}
394 	}
395 
396 	debug(1,"loadTablesIntoMem: didn't find %d", subrId);
397 	return 0;
398 }
399 
loadXTablesIntoMem(uint16 subrId)400 bool AGOSEngine::loadXTablesIntoMem(uint16 subrId) {
401 	byte *p;
402 	int i;
403 	uint min_num, max_num;
404 	char filename[30];
405 	Common::SeekableReadStream *in;
406 
407 	p = _xtblList;
408 	if (p == NULL)
409 		return 0;
410 
411 	while (*p) {
412 		for (i = 0; *p; p++, i++)
413 			filename[i] = *p;
414 		filename[i] = 0;
415 		p++;
416 
417 		for (;;) {
418 			min_num = READ_BE_UINT16(p);
419 			p += 2;
420 
421 			if (min_num == 0)
422 				break;
423 
424 			max_num = READ_BE_UINT16(p);
425 			p += 2;
426 
427 			if (subrId >= min_num && subrId <= max_num) {
428 				_subroutineList = _xsubroutineListOrg;
429 				_tablesHeapPtr = _xtablesHeapPtrOrg;
430 				_tablesHeapCurPos = _xtablesHeapCurPosOrg;
431 				_stringIdLocalMin = 1;
432 				_stringIdLocalMax = 0;
433 
434 				in = openTablesFile(filename);
435 				readSubroutineBlock(in);
436 				closeTablesFile(in);
437 
438 				alignTableMem();
439 
440 				_subroutineListOrg = _subroutineList;
441 				_tablesHeapPtrOrg = _tablesHeapPtr;
442 				_tablesHeapCurPosOrg = _tablesHeapCurPos;
443 				_tablesheapPtrNew = _tablesHeapPtr;
444 				_tablesHeapCurPosNew = _tablesHeapCurPos;
445 
446 				return 1;
447 			}
448 		}
449 	}
450 
451 	debug(1,"loadXTablesIntoMem: didn't find %d", subrId);
452 	return 0;
453 }
454 
closeTablesFile(Common::SeekableReadStream * in)455 void AGOSEngine::closeTablesFile(Common::SeekableReadStream *in) {
456 	if (getFeatures() & GF_OLD_BUNDLE) {
457 		delete in;
458 	}
459 }
460 
createSubroutine(uint16 id)461 Subroutine *AGOSEngine::createSubroutine(uint16 id) {
462 	Subroutine *sub;
463 
464 	alignTableMem();
465 
466 	sub = (Subroutine *)allocateTable(sizeof(Subroutine));
467 	sub->id = id;
468 	sub->first = 0;
469 	sub->next = _subroutineList;
470 	_subroutineList = sub;
471 	return sub;
472 }
473 
createSubroutineLine(Subroutine * sub,int where)474 SubroutineLine *AGOSEngine::createSubroutineLine(Subroutine *sub, int where) {
475 	SubroutineLine *sl, *cur_sl = NULL, *last_sl = NULL;
476 
477 	if (sub->id == 0)
478 		sl = (SubroutineLine *)allocateTable(SUBROUTINE_LINE_BIG_SIZE);
479 	else
480 		sl = (SubroutineLine *)allocateTable(SUBROUTINE_LINE_SMALL_SIZE);
481 
482 	// where is what offset to insert the line at, locate the proper beginning line
483 	if (sub->first != 0) {
484 		cur_sl = (SubroutineLine *)((byte *)sub + sub->first);
485 		while (where) {
486 			last_sl = cur_sl;
487 			cur_sl = (SubroutineLine *)((byte *)sub + cur_sl->next);
488 			if ((byte *)cur_sl == (byte *)sub)
489 				break;
490 			where--;
491 		}
492 	}
493 
494 	if (last_sl != NULL) {
495 		// Insert the subroutine line in the middle of the link
496 		last_sl->next = (byte *)sl - (byte *)sub;
497 		sl->next = (byte *)cur_sl - (byte *)sub;
498 	} else {
499 		// Insert the subroutine line at the head of the link
500 		sl->next = sub->first;
501 		sub->first = (byte *)sl - (byte *)sub;
502 	}
503 
504 	return sl;
505 }
506 
runSubroutine101()507 void AGOSEngine::runSubroutine101() {
508 	Subroutine *sub;
509 
510 	sub = getSubroutineByID(101);
511 	if (sub != NULL)
512 		startSubroutineEx(sub);
513 
514 	permitInput();
515 }
516 
startSubroutine(Subroutine * sub)517 int AGOSEngine::startSubroutine(Subroutine *sub) {
518 	int result = -1;
519 	SubroutineLine *sl = (SubroutineLine *)((byte *)sub + sub->first);
520 
521 	const byte *old_code_ptr = _codePtr;
522 	Subroutine *old_currentTable = _currentTable;
523 	SubroutineLine *old_currentLine = _currentLine;
524 	SubroutineLine *old_classLine = _classLine;
525 	int16 old_classMask = _classMask;
526 	int16 old_classMode1 = _classMode1;
527 	int16 old_classMode2 = _classMode2;
528 
529 	_classLine = 0;
530 	_classMask = 0;
531 	_classMode1 = 0;
532 	_classMode2 = 0;
533 
534 	if (DebugMan.isDebugChannelEnabled(kDebugSubroutine))
535 		dumpSubroutine(sub);
536 
537 	if (++_recursionDepth > 40)
538 		error("Recursion error");
539 
540 	// WORKAROUND: If the game is saved, right after Simon is thrown in the dungeon of Sordid's Fortress of Doom,
541 	// the saved game fails to load correctly. When loading the saved game, the sequence of Simon waking is started,
542 	// before the scene is actually reloaded, due to a script bug. We manually add the extra script code from the
543 	// updated DOS CD release, which fixed this particular script bug.
544 	if (getGameType() == GType_SIMON2 && sub->id == 12101) {
545 		const byte bit = 228;
546 		if ((_bitArrayTwo[bit / 16] & (1 << (bit & 15))) != 0 && (int)readVariable(34) == -1) {
547 			_bitArrayTwo[228 / 16] &= ~(1 << (bit & 15));
548 			writeVariable(34, 1);
549 		}
550 	}
551 
552 	_currentTable = sub;
553 restart:
554 
555 	if (shouldQuit())
556 		return result;
557 
558 	while ((byte *)sl != (byte *)sub) {
559 		_currentLine = sl;
560 		if (checkIfToRunSubroutineLine(sl, sub)) {
561 			_codePtr = (byte *)sl;
562 			if (sub->id)
563 				_codePtr += 2;
564 			else
565 				_codePtr += 8;
566 
567 			debugC(kDebugOpcode, "; %d", sub->id);
568 			result = runScript();
569 			if (result != 0) {
570 				break;
571 			}
572 		}
573 		sl = (SubroutineLine *)((byte *)sub + sl->next);
574 	}
575 
576 	// WORKAROUND: Feeble walks in the incorrect direction, when looking at the Vent in the Research and Testing area of
577 	// the Company Central Command Compound. We manually add the extra script code from the updated English 2CD release,
578 	// which fixed this particular script bug.
579 	if (getGameType() == GType_FF && _language == Common::EN_ANY) {
580 		if (sub->id == 39125 && readVariable(84) == 2) {
581 			writeVariable(1, 1136);
582 			writeVariable(2, 346);
583 		}
584 		if (sub->id == 39126 && readVariable(84) == 2) {
585 			Subroutine *tmpSub = getSubroutineByID(80);
586 			if (tmpSub != NULL) {
587 				startSubroutine(tmpSub);
588 			}
589 		}
590 	}
591 
592 	if (_classMode1) {
593 		_subjectItem = nextInByClass(_subjectItem, _classMask);
594 		if (!_subjectItem) {
595 			_classMode1 = 0;
596 		} else {
597 			delay(0);
598 			sl = _classLine;	/* Rescanner */
599 			goto restart;
600 		}
601 	}
602 	if (_classMode2) {
603 		_objectItem = nextInByClass(_objectItem, _classMask);
604 		if (!_objectItem) {
605 			_classMode2 = 0;
606 		} else {
607 			delay(0);
608 			sl = _classLine;	/* Rescanner */
609 			goto restart;
610 		}
611 	}
612 
613 	/* result -10 means restart subroutine */
614 	if (result == -10) {
615 		delay(0);
616 		sl = (SubroutineLine *)((byte *)sub + sub->first);
617 		goto restart;
618 	}
619 
620 	_codePtr = old_code_ptr;
621 	_currentLine = old_currentLine;
622 	_currentTable = old_currentTable;
623 	_classLine = old_classLine;
624 	_classMask = old_classMask;
625 	_classMode1 = old_classMode2;
626 	_classMode2 = old_classMode1;
627 	_findNextPtr = 0;
628 
629 	_recursionDepth--;
630 	return result;
631 }
632 
startSubroutineEx(Subroutine * sub)633 int AGOSEngine::startSubroutineEx(Subroutine *sub) {
634 	return startSubroutine(sub);
635 }
636 
checkIfToRunSubroutineLine(SubroutineLine * sl,Subroutine * sub)637 bool AGOSEngine::checkIfToRunSubroutineLine(SubroutineLine *sl, Subroutine *sub) {
638 	if (sub->id)
639 		return true;
640 
641 	if (sl->verb != -1 && sl->verb != _scriptVerb &&
642 			(sl->verb != -2 || _scriptVerb != -1))
643 		return false;
644 
645 	if (sl->noun1 != -1 && sl->noun1 != _scriptNoun1 &&
646 			(sl->noun1 != -2 || _scriptNoun1 != -1))
647 		return false;
648 
649 	if (sl->noun2 != -1 && sl->noun2 != _scriptNoun2 &&
650 			(sl->noun2 != -2 || _scriptNoun2 != -1))
651 		return false;
652 
653 	return true;
654 }
655 
readSubroutineBlock(Common::SeekableReadStream * in)656 void AGOSEngine::readSubroutineBlock(Common::SeekableReadStream *in) {
657 	while (in->readUint16BE() == 0) {
658 		readSubroutine(in, createSubroutine(in->readUint16BE()));
659 	}
660 }
readSubroutine(Common::SeekableReadStream * in,Subroutine * sub)661 void AGOSEngine::readSubroutine(Common::SeekableReadStream *in, Subroutine *sub) {
662 	while (in->readUint16BE() == 0) {
663 		readSubroutineLine(in, createSubroutineLine(sub, 0xFFFF), sub);
664 	}
665 }
666 
readSubroutineLine(Common::SeekableReadStream * in,SubroutineLine * sl,Subroutine * sub)667 void AGOSEngine::readSubroutineLine(Common::SeekableReadStream *in, SubroutineLine *sl, Subroutine *sub) {
668 	byte line_buffer[2048], *q = line_buffer;
669 	int size;
670 
671 	if (sub->id == 0) {
672 		sl->verb = in->readUint16BE();
673 		sl->noun1 = in->readUint16BE();
674 		sl->noun2 = in->readUint16BE();
675 	} else if (getGameType() == GType_ELVIRA1) {
676 		in->readUint16BE();
677 		in->readUint16BE();
678 		in->readUint16BE();
679 	}
680 
681 	if (getGameType() == GType_ELVIRA1) {
682 		int16 tmp = in->readUint16BE();
683 		WRITE_BE_UINT16(q, tmp);
684 		while (tmp != 10000) {
685 			if (READ_BE_UINT16(q) == 198) {
686 				in->readUint16BE();
687 			} else {
688 				q = readSingleOpcode(in, q);
689 			}
690 
691 			tmp = in->readUint16BE();
692 			WRITE_BE_UINT16(q, tmp);
693 		}
694 	} else {
695 		while ((*q = in->readByte()) != 0xFF) {
696 			if (*q == 87) {
697 				in->readUint16BE();
698 			} else {
699 				q = readSingleOpcode(in, q);
700 			}
701 		}
702 	}
703 
704 	size = (q - line_buffer + 2);
705 	memcpy(allocateTable(size), line_buffer, size);
706 }
707 
readSingleOpcode(Common::SeekableReadStream * in,byte * ptr)708 byte *AGOSEngine::readSingleOpcode(Common::SeekableReadStream *in, byte *ptr) {
709 	int i, l;
710 	const char *stringPtr;
711 	uint16 opcode, val;
712 
713 	const char *const *table;
714 
715 	if (getGameType() == GType_PP)
716 		table = opcodeArgTable_puzzlepack;
717 	else if (getGameType() == GType_FF)
718 		table = opcodeArgTable_feeblefiles;
719 	else if (getGameType() == GType_SIMON2 && (getFeatures() & GF_TALKIE))
720 		table = opcodeArgTable_simon2talkie;
721 	else if (getGameType() == GType_SIMON2)
722 		table = opcodeArgTable_simon2dos;
723 	else if (getGameType() == GType_SIMON1 && (getFeatures() & GF_TALKIE))
724 		table = opcodeArgTable_simon1talkie;
725 	else if (getGameType() == GType_SIMON1)
726 		table = opcodeArgTable_simon1dos;
727 	else if (getGameType() == GType_WW)
728 		table = opcodeArgTable_waxworks;
729 	else if (getGameType() == GType_ELVIRA2)
730 		table = opcodeArgTable_elvira2;
731 	else
732 		table = opcodeArgTable_elvira1;
733 
734 	i = 0;
735 	if (getGameType() == GType_ELVIRA1) {
736 		opcode = READ_BE_UINT16(ptr);
737 		ptr += 2;
738 	} else {
739 		opcode = *ptr++;
740 	}
741 
742 	stringPtr = table[opcode];
743 	if (!stringPtr)
744 		error("Unable to locate opcode table. Perhaps you are using the wrong game target?");
745 
746 	for (;;) {
747 		if (stringPtr[i] == ' ')
748 			return ptr;
749 
750 		l = stringPtr[i++];
751 
752 		switch (l) {
753 		case 'F':
754 		case 'N':
755 		case 'S':
756 		case 'a':
757 		case 'n':
758 		case 'p':
759 		case 'v':
760 		case '3':
761 			val = in->readUint16BE();
762 			WRITE_BE_UINT16(ptr, val); ptr += 2;
763 			break;
764 
765 		case 'B':
766 			if (getGameType() == GType_ELVIRA1) {
767 				val = in->readUint16BE();
768 				WRITE_BE_UINT16(ptr, val); ptr += 2;
769 			} else {
770 				*ptr++ = in->readByte();
771 				if (ptr[-1] == 0xFF) {
772 					*ptr++ = in->readByte();
773 				}
774 			}
775 			break;
776 
777 		case 'I':
778 			val = in->readUint16BE();
779 			switch (val) {
780 			case 1:
781 				val = 0xFFFF;
782 				break;
783 			case 3:
784 				val = 0xFFFD;
785 				break;
786 			case 5:
787 				val = 0xFFFB;
788 				break;
789 			case 7:
790 				val = 0xFFF9;
791 				break;
792 			case 9:
793 				val = 0xFFF7;
794 				break;
795 			default:
796 				val = fileReadItemID(in);
797 				break;
798 			}
799 			WRITE_BE_UINT16(ptr, val); ptr += 2;
800 			break;
801 
802 		case 'T':
803 			val = in->readUint16BE();
804 			switch (val) {
805 			case 0:
806 				val = 0xFFFF;
807 				break;
808 			case 3:
809 				val = 0xFFFD;
810 				break;
811 			default:
812 				val = (uint16)in->readUint32BE();
813 				break;
814 			}
815 			WRITE_BE_UINT16(ptr, val); ptr += 2;
816 			break;
817 		default:
818 			error("readSingleOpcode: Bad cmd table entry %c", l);
819 		}
820 	}
821 }
822 
823 } // End of namespace AGOS
824