1 /*  Sarien - A Sierra AGI resource interpreter engine
2  *  Copyright (C) 1999-2001 Stuart George and Claudio Matsuoka
3  *
4  *  $Id: op_test.c,v 1.34 2001/09/12 16:02:31 cmatsuoka Exp $
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; see docs/COPYING for further details.
9  */
10 
11 #include <stdio.h>
12 #include <string.h>
13 
14 #include "sarien.h"
15 #include "agi.h"
16 #include "keyboard.h"
17 #include "opcodes.h"
18 
19 static UINT8	test_obj_right	(UINT8, UINT8, UINT8, UINT8, UINT8);
20 static UINT8	test_obj_centre	(UINT8, UINT8, UINT8, UINT8, UINT8);
21 static UINT8	test_obj_in_box	(UINT8, UINT8, UINT8, UINT8, UINT8);
22 static UINT8	test_posn	(UINT8, UINT8, UINT8, UINT8, UINT8);
23 static UINT8	test_said	(UINT8, UINT8 *);
24 static UINT8	test_controller	(UINT8);
25 static UINT8	test_keypressed	(void);
26 
27 #define ip (game.logics[lognum].cIP)
28 #define code (game.logics[lognum].data)
29 
30 #define test_equal(v1,v2)	(getvar(v1) == (v2))
31 #define test_less(v1,v2)	(getvar(v1) < (v2))
32 #define test_greater(v1,v2)	(getvar(v1) > (v2))
33 #define test_isset(flag)	(getflag (flag))
34 #define test_has(obj)		(object_get_location (obj) == EGO_OWNED)
35 #define test_obj_in_room(obj,v)	(object_get_location (obj) == getvar (v))
36 #define test_compare_strings(s1,s2) (!strcmp(game.strings[s1], game.strings[s2]))
37 
38 extern int timer_hack;	/* For the timer loop in MH1 logic 153 */
39 
test_keypressed()40 static UINT8 test_keypressed ()
41 {
42 	int x = game.keypress;
43 
44 	game.keypress = 0;
45 	if (!x) {
46 		int mode = game.input_mode;
47 		game.input_mode = INPUT_NONE;
48 		main_cycle ();
49 		game.input_mode = mode;
50 	}
51 
52 	if (x) _D (_D_WARN "keypress = %02x", x);
53 
54 	return x;
55 }
56 
57 
test_controller(UINT8 cont)58 static UINT8 test_controller (UINT8 cont)
59 {
60 	int r;
61 
62 	r = game.ev_keyp[cont].occured | game.ev_scan[cont].occured;
63 	/*game.ev_keyp[cont].occured = game.ev_scan[cont].occured = FALSE;*/
64 
65 	return r;
66 }
67 
68 
test_posn(UINT8 n,UINT8 x1,UINT8 y1,UINT8 x2,UINT8 y2)69 static UINT8 test_posn (UINT8 n, UINT8 x1, UINT8 y1, UINT8 x2, UINT8 y2)
70 {
71 	struct vt_entry *v = &game.view_table[n];
72 	UINT8 r;
73 
74 	r = v->x_pos >= x1 && v->y_pos >= y1 &&
75 		v->x_pos <= x2 && v->y_pos <= y2;
76 
77 	/* _D ("(%d,%d) in (%d,%d,%d,%d): %s", v->x_pos, v->y_pos,
78 		x1, y1, x2, y2, r ? "TRUE" : "FALSE"); */
79 
80 	return r;
81 }
82 
83 
test_obj_in_box(UINT8 n,UINT8 x1,UINT8 y1,UINT8 x2,UINT8 y2)84 static UINT8 test_obj_in_box (UINT8 n, UINT8 x1, UINT8 y1, UINT8 x2, UINT8 y2)
85 {
86 	struct vt_entry *v = &game.view_table[n];
87 
88 	return v->x_pos >= x1 &&
89 		v->y_pos >= y1 &&
90 		v->x_pos + v->x_size - 1 <= x2 &&
91 		v->y_pos <= y2;
92 }
93 
94 
95 /* if n is in centre of box */
test_obj_centre(UINT8 n,UINT8 x1,UINT8 y1,UINT8 x2,UINT8 y2)96 static UINT8 test_obj_centre (UINT8 n, UINT8 x1, UINT8 y1, UINT8 x2, UINT8 y2)
97 {
98 	struct vt_entry *v = &game.view_table[n];
99 
100 	return v->x_pos + v->x_size / 2 >= x1 &&
101 		v->x_pos + v->x_size / 2 <= x2 &&
102 		v->y_pos >= y1 &&
103 		v->y_pos <= y2;
104 }
105 
106 
107 /* if nect N is in right corner */
test_obj_right(UINT8 n,UINT8 x1,UINT8 y1,UINT8 x2,UINT8 y2)108 static UINT8 test_obj_right(UINT8 n, UINT8 x1, UINT8 y1, UINT8 x2, UINT8 y2)
109 {
110 	struct vt_entry *v = &game.view_table[n];
111 
112 	return v->x_pos + v->x_size - 1 >= x1 &&
113 		v->x_pos + v->x_size - 1 <= x2 &&
114 		v->y_pos >= y1 &&
115 		v->y_pos <= y2;
116 }
117 
118 
119 /* When player has entered something, it is parsed elsewhere */
test_said(UINT8 nwords,UINT8 * cc)120 static UINT8 test_said (UINT8 nwords, UINT8 *cc)
121 {
122 	int c, n = game.num_ego_words;
123 	int z = 0;
124 
125 	if (getflag (F_said_accepted_input) || !getflag (F_entered_cli))
126 		return FALSE;
127 
128 	/* FR:
129 	 * I think the reason for the code below is to add some speed....
130 	 *
131 	 *	if (nwords != num_ego_words)
132 	 *		return FALSE;
133 	 *
134 	 * In the disco scene in Larry 1 when you type "examine blonde",
135 	 * inside the logic is expected ( said("examine", "blonde", "rol") )
136 	 * where word("rol") = 9999
137 	 *
138 	 * According to the interpreter code 9999 means that whatever the
139 	 * user typed should be correct, but it looks like code 9999 means that
140 	 * if the string is empty at this point, the entry is also correct...
141 	 *
142 	 * With the removal of this code, the behaviour of the scene was
143 	 * corrected
144 	 */
145 
146 	for (c = 0; nwords && n; c++, nwords--, n--) {
147 		z = lohi_getword (cc);
148 		cc += 2;
149 
150 		switch (z) {
151 		case 9999:	/* rest of line (empty string counts to...) */
152 			nwords = 1;
153 			break;
154 		case 1:			/* any word */
155 			break;
156 		default:
157 			if (game.ego_words[c].id != z)
158 				return FALSE;
159 				break;
160 			}
161 	}
162 
163 	/* The entry string should be entirely parsed, or last word = 9999 */
164 	if (n && z != 9999)
165 		return FALSE;
166 
167 	/* The interpreter string shouldn't be entirely parsed, but next
168 	 * word must be 9999.
169 	 */
170 	if (nwords != 0 && lohi_getword(cc) != 9999)
171 		return FALSE;
172 
173 	setflag (F_said_accepted_input, TRUE);
174 
175 	return TRUE;
176 }
177 
178 
test_if_code(int lognum)179 int test_if_code (int lognum)
180 {
181 	int ec			= TRUE;
182 	int retval		= TRUE;
183 	UINT8	op		= 0;
184 	UINT8	not_test	= FALSE;
185 	UINT8	or_test		= FALSE;
186 	UINT16	last_ip		= ip;
187 	UINT8	p[16]		= {0};
188 
189 	while (retval && !game.quit_prog_now) {
190 #ifdef USE_CONSOLE
191 		if (debug.enabled && (debug.logic0 || lognum))
192 			debug_console (lognum, lTEST_MODE, NULL);
193 #endif
194 
195 		last_ip = ip;
196 		op = *(code + ip++);
197 		memmove (p, (code + ip), 16);
198 
199 		switch(op) {
200 		case 0xFF:			/* END IF, TEST TRUE */
201 			goto end_test;
202 		case 0xFD:
203 			not_test = !not_test;
204 			continue;
205 		case 0xFC:				/* OR */
206 			/* if or_test is ON and we hit 0xFC, end of OR, then
207 			 * or is STILL false so break.
208 			 */
209 			if (or_test) {
210 				ec = FALSE;
211 				retval = FALSE;
212 				goto end_test;
213 			}
214 
215 			or_test = TRUE;
216 			continue;
217 
218 		case 0x00:
219 			/* return true? */
220 			goto end_test;
221 		case 0x01:
222 			ec = test_equal (p[0], p[1]);
223 			if (p[0] == 11) timer_hack++;
224 			break;
225 		case 0x02:
226 			ec = test_equal (p[0], getvar(p[1]));
227 			if (p[0] == 11 || p[1] == 11) timer_hack++;
228 			break;
229 		case 0x03:
230 			ec = test_less (p[0], p[1]);
231 			if (p[0] == 11) timer_hack++;
232 			break;
233 		case 0x04:
234 			ec = test_less(p[0], getvar(p[1]));
235 			if (p[0] == 11 || p[1] == 11) timer_hack++;
236 			break;
237 		case 0x05:
238 			ec = test_greater (p[0], p[1]);
239 			if (p[0] == 11) timer_hack++;
240 			break;
241 		case 0x06:
242 			ec = test_greater (p[0], getvar (p[1]));
243 			if (p[0] == 11 || p[1] == 11) timer_hack++;
244 			break;
245 		case 0x07:
246 			ec = test_isset (p[0]);
247 			break;
248 		case 0x08:
249 			ec = test_isset (getvar(p[0]));
250 			break;
251 		case 0x09:
252 			ec = test_has (p[0]);
253 			break;
254 		case 0x0A:
255 			ec = test_obj_in_room (p[0], p[1]);
256 			break;
257 		case 0x0B:
258 			ec = test_posn (p[0], p[1], p[2], p[3], p[4]);
259 			break;
260 		case 0x0C:
261 			ec = test_controller (p[0]);
262 			break;
263 		case 0x0D:
264 			ec = test_keypressed ();
265 			break;
266 		case 0x0E:
267 			ec = test_said (p[0], (UINT8*)code + (ip + 1));
268 			ip = last_ip;
269 			ip++;		/* skip opcode */
270 			ip += p[0] * 2;	/* skip num_words * 2 */
271 			ip++;		/* skip num_words opcode */
272 			break;
273 		case 0x0F:
274 			_D(_D_WARN "comparing [%s], [%s]",
275 				game.strings[p[0]], game.strings[p[1]]);
276 			ec = test_compare_strings (p[0], p[1]);
277 			break;
278 		case 0x10:
279 			ec = test_obj_in_box (p[0], p[1], p[2], p[3], p[4]);
280 			break;
281 		case 0x11:
282 			ec = test_obj_centre (p[0], p[1], p[2], p[3], p[4]);
283 			break;
284 		case 0x12:
285 			ec = test_obj_right (p[0], p[1], p[2], p[3], p[4]);
286 			break;
287 		default:
288 			ec = FALSE;
289 			goto end_test;
290 		}
291 
292 		if (op <= 0x12)
293 			ip += logic_names_test[op].num_args;
294 
295 		/* exchange ec value */
296 		if (not_test)
297 			ec = !ec;
298 
299 		/* not is only enabled for 1 test command */
300 		not_test = FALSE;
301 
302 		if (or_test && ec) {
303 			/* a TRUE inside an OR statement passes
304 			 * ENTIRE statement scan for end of OR
305 			 */
306 
307 		/* CM: test for opcode < 0xfc changed from 'op' to
308 	 	 *     '*(code+ip)', to avoid problem with the 0xfd (NOT)
309 		 *     opcode byte. Changed a bad ip += ... ip++ construct.
310 		 *     This should fix the crash with Larry's logic.0 code:
311 		 *
312 		 *     if ((isset(4) ||
313 		 *          !isset(2) ||
314 		 *          v30 == 2 ||
315                  *          v30 == 1)) {
316 		 *       goto Label1;
317                  *     }
318 		 *
319 		 *     The bytecode is:
320 		 *     ff fc 07 04 fd 07 02 01 1e 02 01 1e 01 fc ff
321 		 */
322 
323 			/* find end of OR */
324 			while (*(code+ip) != 0xFC) {
325 				if (*(code + ip) == 0x0E) {	/* said */
326 					ip++;
327 					/* cover count + ^words */
328 					ip += 1 + ((*(code + ip)) * 2);
329 					continue;
330 				}
331 
332 				if (*(code + ip) < 0xFC)
333 					ip += logic_names_test[*(code +
334 						ip)].num_args;
335 				ip++;
336 			}
337 			ip++;
338 
339 			or_test = FALSE;
340 			retval = TRUE;
341 		} else {
342 			retval = or_test ? retval || ec : retval && ec;
343 		}
344 	}
345 end_test:
346 
347 	/* if false, scan for end of IP? */
348 	if (retval)
349 		ip += 2;
350 	else {
351 		ip = last_ip;
352 		while (*(code + ip) != 0xff) {
353 			if (*(code + ip) == 0x0e) {
354 				ip++;
355 				ip += (*(code + ip)) * 2 + 1;
356 			} else if (*(code + ip) < 0xfc) {
357 				ip += logic_names_test[*(code + ip)].num_args;
358 				ip++;
359 			} else {
360 				ip++;
361 			}
362 		}
363 		ip++;	/* skip over 0xFF */
364 		ip += lohi_getword (code + ip) + 2;
365 	}
366 
367 #ifdef USE_CONSOLE
368 	if (debug.enabled && (debug.logic0 || lognum))
369 		debug_console (lognum, 0xFF, retval ? "=TRUE" : "=FALSE");
370 #endif
371 
372 	return retval;
373 }
374 
375