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 "ultima/nuvie/core/nuvie_defs.h"
24 #include "ultima/nuvie/misc/u6_misc.h"
25 #include "ultima/nuvie/core/player.h"
26 #include "ultima/nuvie/core/party.h"
27 #include "ultima/nuvie/usecode/u6_usecode.h"
28 #include "ultima/nuvie/actors/actor_manager.h"
29 #include "ultima/nuvie/core/game_clock.h"
30 #include "ultima/nuvie/core/map.h"
31 #include "ultima/nuvie/gui/widgets/map_window.h"
32 #include "ultima/nuvie/core/effect.h"
33 #include "ultima/nuvie/script/script.h"
34 #include "ultima/nuvie/core/converse_interpret.h"
35 #include "ultima/nuvie/core/converse_speech.h"
36 
37 namespace Ultima {
38 namespace Nuvie {
39 
40 //#define CONVERSE_DEBUG
41 
ConverseInterpret(Converse * owner)42 ConverseInterpret::ConverseInterpret(Converse *owner) {
43 	converse = owner;
44 	b_frame = NULL;
45 	decl_v = decl_t = 0x00;
46 	in_start = 0;
47 
48 	unwait();
49 	answer_mode = ANSWER_NO;
50 	stopped = false;
51 	db_lvar = false;
52 	db_loc = 0;
53 	db_offset = 0;
54 }
55 
56 
~ConverseInterpret()57 ConverseInterpret::~ConverseInterpret() {
58 	leave_all(); // deletes b_frame when empty
59 }
60 
61 
62 /* Returns a value from the current location in the ConvScript, respecting any
63  * present data-size definition.
64  */
read_value()65 struct ConverseInterpret::in_val_s ConverseInterpret::read_value() {
66 	ConvScript *cs = converse->script;
67 	converse_value nval = cs->read();
68 	uint8 dtype = nval;
69 	if (nval == 0xd3)
70 		nval = cs->read();
71 	else if (nval == 0xd2)
72 		nval = cs->read4();
73 	else if (nval == 0xd4)
74 		nval = cs->read2();
75 	else
76 		dtype = 0;
77 	struct in_val_s ival;
78 	ival.v = nval;
79 	ival.d = dtype;
80 	return (ival);
81 }
82 
83 
84 /* Read a control statement from the script.
85  */
collect_input()86 void ConverseInterpret::collect_input() {
87 	ConvScript *cs = converse->script;
88 	converse_value cval = cs->peek();
89 	// special cases
90 	if (cval == U6OP_JUMP) { // 0xb0 (location)
91 		add_val(cs->read()); // OPC
92 		add_val(cs->read4());
93 		return;
94 	} else if (cval == U6OP_SIDENT) { // 0xff, get NPC identity
95 		add_val(cs->read()); // OPC
96 		add_val(cs->read()); // number
97 		add_text(); // name
98 		return;
99 	} else if (cval == U6OP_KEYWORDS // 0xef, get keyword list
100 	           || cval == U6OP_ASKC   // ...0xf8, or characters allowed as input
101 	           || cval == U6OP_SLOOK) { // ...0xf1, or description
102 		add_val(cs->read()); // OPC
103 		add_text();
104 		return;
105 	}
106 	// standard
107 	uint32 arg_num = 1; // which arg. of control statement will be evaluated
108 	do {
109 		if (is_print(cs->peek()) && (cs->overflow(1) || !is_valop(cs->peek(1))))
110 			break;
111 		if (cs->peek() == U6OP_EVAL) { // 0xa7
112 			cs->skip();
113 			// HACK forced by this input-collection design, don't eval if not
114 			// running (but this was supposed to be checked only in exec())
115 			if (!top_frame() || top_frame()->run) // "no frame" == run
116 				eval(arg_num++);
117 		} else {
118 			struct in_val_s nval = read_value();
119 			add_val(nval.v, nval.d);
120 		}
121 	} while (!cs->overflow() && !is_ctrl(cs->peek()));
122 }
123 
124 
125 /* Interpret control codes and text from the ConvScript.
126  */
step()127 void ConverseInterpret::step() {
128 	ConvScript *cs = converse->script;
129 
130 	flush();
131 
132 	while (!waiting() && !cs->overflow() && !converse->is_waiting_for_scroll()) {
133 		if (is_print(cs->peek())) {
134 			add_text(); // collect
135 			converse->set_output(get_text());
136 		} else if (is_ctrl(cs->peek())) {
137 			in_start = cs->pos();
138 			collect_input(); // get opcode + data (1 statement/instruction)
139 		} else {
140 #ifdef CONVERSE_DEBUG
141 			DEBUG(1, LEVEL_DEBUGGING, "Converse: skipped 0x%02x at %04x\n", cs->peek(), cs->pos());
142 #endif
143 			converse->print("[Tried to print a control char.]\n");
144 			cs->skip();
145 		}
146 		exec(); // run it
147 	}
148 
149 	if (cs->overflow() && !stopped) {
150 		converse->print("\n[EOF]\n");
151 		stop();
152 	}
153 }
154 
155 
156 /* Collect text from script and add to end of collected text statement, or add
157  * one character to text.
158  */
add_text(unsigned char c)159 void ConverseInterpret::add_text(unsigned char c) {
160 	ConvScript *cs = converse->script;
161 	do {
162 		text.append(1, (unsigned char)cs->read());
163 	} while (!cs->overflow() && is_print(cs->peek()));
164 }
165 
166 
167 /* Add some code or data to end of collected control statement.
168  */
add_val(converse_value c,uint8 d)169 void ConverseInterpret::add_val(converse_value c, uint8 d) {
170 	struct in_val_s ivs;
171 	ivs.v = c;
172 	ivs.d = d;
173 	in.push_back(ivs);
174 }
175 
176 
177 /* Delete the top-level frame. If no frames are left, run==true for all code.
178  */
leave()179 void ConverseInterpret::leave() {
180 	if (top_frame()) {
181 		struct convi_frame_s *fp = b_frame->top();
182 #ifdef CONVERSE_DEBUG
183 		DEBUG(1, LEVEL_DEBUGGING, "Converse: ...leave %02x...\n", fp->start_c);
184 #endif
185 		delete fp;
186 		fp = NULL;
187 		b_frame->pop();
188 		if (b_frame->empty()) {
189 			delete b_frame;
190 			b_frame = NULL;
191 		}
192 	}
193 }
194 
195 
196 /* Create new top-level frame for a code block starting at control code `c'.
197  */
enter(converse_value c)198 void ConverseInterpret::enter(converse_value c) {
199 	struct convi_frame_s *ef = new convi_frame_s;
200 	ef->start = in_start; // start location
201 	ef->run = top_frame() ? top_frame()->run : true; // run if parent is
202 	ef->break_c = 0x00;
203 	ef->start_c = c;
204 	if (!b_frame)
205 		b_frame = new stack<struct convi_frame_s *>;
206 	b_frame->push(ef);
207 #ifdef CONVERSE_DEBUG
208 	DEBUG(1, LEVEL_DEBUGGING, "Converse: ...enter %02x...\n", ef->start_c);
209 #endif
210 }
211 
212 
213 /* Leave the top-frame for end-code matching `c', and enter a new frame for
214  * other codes. Toggle run mode if `c' is break-code.
215  */
do_frame(converse_value c)216 void ConverseInterpret::do_frame(converse_value c) {
217 	switch (c) {
218 	case U6OP_IF: // 0xa1
219 		enter(U6OP_IF);
220 		break;
221 	case U6OP_ENDIF: // 0xa2
222 		leave(); // (0xa1)
223 		break;
224 	case U6OP_KEYWORDS: // 0xef
225 		if (!top_frame() || top_frame()->start_c != U6OP_KEYWORDS)
226 			enter(U6OP_KEYWORDS); // 0xef...0xef...0xee, can't be recursive
227 		break;
228 	case U6OP_ENDANSWER: // 0xee
229 		leave(); // (0xef)
230 		break;
231 	}
232 	if (c != 0x00 && c == get_break())
233 		set_run(!get_run());
234 }
235 
236 
237 /* Run (or skip) the stored control and text statements, and clear them.
238  */
exec()239 void ConverseInterpret::exec() {
240 	do_frame(get_val(0));
241 	if (!top_frame() || top_frame()->run) { // "no frame" == run
242 		if (val_count())
243 			do_ctrl();
244 		if (!converse->get_output().empty())
245 			do_text();
246 	}
247 #ifdef CONVERSE_DEBUG
248 	else if (get_val(0) != 0x00) // show stepped over ctrl code (not text)
249 		DEBUG(1, LEVEL_DEBUGGING, "Converse: %04x ----: %02x\n", in_start, get_val(0));
250 #endif
251 	flush();
252 	converse->set_output(""); // clear output
253 }
254 
255 
256 /* Print script text, resolving special symbols as they are encountered.
257  */
do_text()258 void ConverseInterpret::do_text() {
259 	string output = get_formatted_text(converse->get_output().c_str());
260 	converse->print(output.c_str());
261 }
262 
263 /* Print script text, resolving special symbols as they are encountered.
264  */
get_formatted_text(const char * c_str)265 string ConverseInterpret::get_formatted_text(const char *c_str) {
266 	unsigned int i = 0;
267 	char symbol[3] = { '\0', '\0', '\0' },
268 	                 intval[16];
269 	string output;
270 	const uint32 len = strlen(c_str);
271 	uint32 last_value = 0;
272 
273 	while (i < len) {
274 		switch (c_str[i]) {
275 
276 		case '$' :
277 		case '#' : // copy symbol
278 			strncpy(symbol, &c_str[i], 2);
279 			// evaluate
280 			if (!strcmp(symbol, "$G")) // gender title
281 				output.append(converse->player->get_gender_title());
282 			else if (!strcmp(symbol, "$N")) // npc name
283 				output.append(converse->_name.c_str());
284 			else if (!strcmp(symbol, "$P")) // player name
285 				output.append(converse->player->get_name());
286 			else if (!strcmp(symbol, "$T")) // time of day
287 				output.append(converse->clock->get_time_of_day_string());
288 			else if (!strcmp(symbol, "$Y")) // Y-string
289 				output.append(get_ystr());
290 			else if (!strcmp(symbol, "$Z")) // previous input
291 				output.append(converse->get_svar(U6TALK_VAR_INPUT));
292 			else if (symbol[0] == '$' // value of a string variable
293 			         && Common::isDigit(symbol[1]))
294 				output.append(converse->get_svar(strtol(&symbol[1], NULL, 10)));
295 			else if (symbol[0] == '#' // value of a variable
296 			         && Common::isDigit(symbol[1])) {
297 				last_value = converse->get_var(strtol(&symbol[1], NULL, 10));
298 				snprintf(intval, 16, "%u", last_value);
299 
300 				output.append((char *)intval);
301 				output.append("");
302 			} else
303 				output.append(symbol);
304 
305 			i += 2;
306 			break;
307 
308 		case '{' :
309 			i++;
310 			break; //FIXME what does this FM-Towns command do?
311 
312 		case '~' :
313 			if (i + 3 <= len) {
314 				i++;
315 				if (c_str[i] == 'P')
316 					converse->get_speech()->play_speech(converse->script_num, (int)strtol(&c_str[i + 1], NULL, 10));
317 
318 				for (i++; Common::isDigit(c_str[i]) && i < len;)
319 					i++;
320 			}
321 			break;
322 
323 		case '/' : // singular
324 		case '\\' : { // plural
325 			bool show = false;
326 			if ((last_value == 1 && c_str[i] == '/') ||
327 			        (last_value != 1 && c_str[i] == '\\')) show = true;
328 			++i;
329 			unsigned int count = 0;
330 			while (i + count < len &&
331 			        c_str[i + count] >= 'a' && c_str[i + count] <= 'z')
332 				// NOTE: the above check might not work for non-english
333 				count++;
334 			if (show) output.append(count, c_str[i]);
335 			i += count;
336 			break;
337 		}
338 
339 		default :
340 			//skip fm-towns keyword commands when in original ui mode.
341 			// They take the following form. '+actor_numberKeyword+' eg. '+3leave+'
342 			if (c_str[i] == '+' && !Game::get_game()->is_new_style()) {
343 				if (i + 3 <= len) {
344 					i++;
345 
346 					for (; c_str[i] != 0x2b && i < len;) //0x2b = '+'
347 						i++;
348 
349 					i++;
350 				}
351 			} else {
352 				output.append(1, c_str[i]);
353 				i += 1;
354 			}
355 			break;
356 		}
357 	}
358 
359 	return output;
360 }
361 
362 
363 /* Execute a control statement/instruction (control code + optional arguments
364  * which must have been evaluated already.)
365  */
do_ctrl()366 void ConverseInterpret::do_ctrl() {
367 	stack<converse_typed_value> st;
368 #ifdef CONVERSE_DEBUG
369 	DEBUG(1, LEVEL_DEBUGGING, "Converse: %04x INSTR:", in_start);
370 	for (uint32 v = 0; v < val_count(); v++)
371 		DEBUG(1, LEVEL_DEBUGGING, " 0x%02x", get_val(v));
372 	DEBUG(1, LEVEL_DEBUGGING, "\n");
373 #endif
374 
375 	converse_typed_value v = {U6OP_VAR, 0};
376 	while (val_count()) {
377 		v.val = pop_val();
378 		st.push(v);
379 	}
380 	op(st);
381 }
382 
383 
384 /* Returns last item value from input list (and removes it from the same.)
385  */
pop_val()386 converse_value ConverseInterpret::pop_val() {
387 	converse_value ret = 0;
388 	if (!in.empty()) {
389 		ret = get_val(val_count() - 1);
390 		in.resize(val_count() - 1);
391 	}
392 	return (ret);
393 }
394 
395 
396 /* Returns last item data-size/type from value list (and removes it from the
397  * same.)
398  */
pop_val_size()399 uint8 ConverseInterpret::pop_val_size() {
400 	converse_value ret = 0;
401 	if (!in.empty()) {
402 		ret = get_val_size(val_count() - 1);
403 		in.resize(val_count() - 1);
404 	}
405 	return (ret);
406 }
407 
408 
409 /* Returns any item value from the input list (and leaves the list alone.)
410  */
get_val(uint32 vi)411 converse_value ConverseInterpret::get_val(uint32 vi) {
412 	if (vi < in.size())
413 		return (in[vi].v);
414 	return (0);
415 }
416 
417 
418 /* Returns any item data-size/type from the input list (and leaves the list
419  * alone.)
420  */
get_val_size(uint32 vi)421 uint8 ConverseInterpret::get_val_size(uint32 vi) {
422 	if (vi < in.size())
423 		return (in[vi].d);
424 	return (0);
425 }
426 
set_ystr(const char * s)427 void ConverseInterpret::set_ystr(const char *s) {
428 	ystring = s ? s : "";
429 	converse->set_svar(U6TALK_VAR_YSTRING, ystring.c_str());
430 }
431 
432 /* Set the return string `sn' to `s'.
433  */
set_rstr(uint32 sn,const char * s)434 void ConverseInterpret::set_rstr(uint32 sn, const char *s) {
435 	if (sn >= rstrings.size())
436 		rstrings.resize(rstrings.size() + 1);
437 	rstrings[sn] = s ? s : "";
438 }
439 
440 
441 /* Add a return string.
442  * Returns the index into the list, which is used by an instruction.
443  */
add_rstr(const char * s)444 converse_value ConverseInterpret::add_rstr(const char *s) {
445 	rstrings.push_back(s ? s : "");
446 	return (rstrings.size() - 1);
447 }
448 
449 
450 /* Returns and removes top item from a value stack.
451  */
pop_arg(stack<converse_typed_value> & vs)452 converse_value ConverseInterpret::pop_arg(stack<converse_typed_value> &vs) {
453 	converse_value ret = 0;
454 	if (!vs.empty()) {
455 		converse_typed_value val = vs.top();
456 		ret = val.val;
457 		vs.pop();
458 	}
459 	return (ret);
460 }
461 
pop_typed_arg(stack<converse_typed_value> & vs)462 converse_typed_value ConverseInterpret::pop_typed_arg(stack<converse_typed_value> &vs) {
463 	converse_typed_value ret = {0, 0};
464 	if (!vs.empty()) {
465 		ret = vs.top();
466 		vs.pop();
467 	}
468 	return (ret);
469 }
470 
471 #if 0
472 bool MDTalkInterpret::op(stack<converse_value> &i) {
473 	bool success = true;
474 	converse_value v[4], in; // args
475 
476 	switch (in == pop_arg(i)) {
477 	default:
478 		i.push(in);
479 		success = ConverseInterpret::op(i);
480 	}
481 	return (success);
482 }
483 #endif
484 
op_create_new(stack<converse_typed_value> & i)485 bool ConverseInterpret::op_create_new(stack<converse_typed_value> &i) {
486 	converse_value v[4];
487 	Actor *cnpc = NULL;
488 
489 	v[0] = pop_arg(i); // npc
490 	v[1] = pop_arg(i); // obj
491 	v[2] = pop_arg(i); // qual
492 	v[3] = pop_arg(i); // quant
493 	cnpc = converse->actors->get_actor(npc_num(v[0]));
494 	if (cnpc) {
495 		if (Game::get_game()->get_game_type() == NUVIE_GAME_U6 && v[1] == 76) { // Amulet of Submission
496 			cnpc->remove_readied_object(ACTOR_NECK);
497 			Obj *cnpc_obj = cnpc->inventory_new_object(76, 1, 0);
498 			cnpc->add_readied_object(cnpc_obj);
499 		} else
500 			cnpc->inventory_new_object(v[1], v[3], v[2]);
501 	}
502 
503 	return true;
504 }
505 
op_create_new(stack<converse_typed_value> & i)506 bool WOUConverseInterpret::op_create_new(stack<converse_typed_value> &i) {
507 	converse_value v[4];
508 	Actor *cnpc = NULL;
509 
510 	v[0] = pop_arg(i); // npc
511 	v[1] = pop_arg(i); // obj
512 	v[2] = pop_arg(i); // qual
513 	v[3] = pop_arg(i); // quant
514 	cnpc = converse->actors->get_actor(npc_num(v[0]));
515 	if (cnpc) {
516 		if (cnpc->can_carry_object(v[1], v[3])) {
517 			cnpc->inventory_new_object(v[1], v[3], v[2]);
518 			converse->set_var(WOUTALK_VAR_ADD_TO_INVENTORY_FAILED, 0);
519 		} else {
520 			converse->set_var(WOUTALK_VAR_ADD_TO_INVENTORY_FAILED, 1); //This var is set to true when the object cannot be added to the actor's inventory
521 		}
522 	}
523 
524 	return true;
525 }
526 
527 /* Run control code with arguments/operands (arranged on a stack from
528  * top to bottom.)
529  */
op(stack<converse_typed_value> & i)530 bool ConverseInterpret::op(stack<converse_typed_value> &i) {
531 	bool success = true;
532 	converse_value v[4] = { 0, 0, 0, 0 }; // args
533 	converse_value inVal;
534 	ConvScript *cs = converse->script;
535 	Actor *cnpc = NULL;
536 	Obj *cnpc_obj = NULL;
537 	Player *player = converse->player;
538 //    converse_db_s *cdb;
539 
540 	inVal = pop_arg(i);
541 
542 #ifdef CONVERSE_DEBUG
543 	DEBUG(1, LEVEL_DEBUGGING, "op %s(%x)\n", op_str(inVal), inVal);
544 #endif
545 
546 	switch (inVal) {
547 	case U6OP_SLEEP: // 0x9e
548 		// Note: It's usually unecessary to wait for the effect, as it
549 		// pauses input and the user can't continue the conversation until
550 		// the effect is complete.
551 		new SleepEffect(5); // sleep until sunrise
552 		break;
553 	case U6OP_HORSE: // 0x9c
554 		// FIXME: probably need to do more real actor/object set-up here
555 		cnpc = converse->actors->get_actor(npc_num(pop_arg(i)));
556 		cnpc_obj = cnpc->make_obj();
557 		cnpc_obj->frame_n = 0; // FIX for actors orginal direction.
558 		cnpc_obj->obj_n = OBJ_U6_HORSE_WITH_RIDER; // mount up.
559 		cnpc->init_from_obj(cnpc_obj);
560 		delete_obj(cnpc_obj);
561 		break;
562 	case U6OP_IF: // 0xa1 (test val)
563 		set_break(U6OP_ELSE);
564 		set_run(pop_arg(i) ? true : false);
565 		break;
566 	case U6OP_ENDIF: // 0xa2
567 		break; // (frame only)
568 	case U6OP_ELSE: // 0xa3
569 		break; // (frame only_
570 	case U6OP_SETF: // 0xa4 (npc, flagnum)
571 		cnpc = converse->actors->get_actor(npc_num(pop_arg(i)));
572 		if (cnpc)
573 			cnpc->set_flag(pop_arg(i));
574 		break;
575 	case U6OP_CLEARF: // 0xa5 (npc, flagnum)
576 		cnpc = converse->actors->get_actor(npc_num(pop_arg(i)));
577 		if (cnpc)
578 			cnpc->clear_flag(pop_arg(i));
579 		break;
580 	case U6OP_DECL: // 0xa6
581 		db_lvar = false;
582 		v[0] = pop_arg(i); // variable
583 		v[1] = pop_arg(i); // type
584 
585 		if (!i.empty()) {
586 			converse_typed_value top = i.top();
587 			if ((top.val == 0xb2 || top.val == 0xb4)) {
588 				if (top.val == 0xb2) { // eg DB1[#2] = ...
589 					pop_arg(i); //0xb2
590 					pop_arg(i); //0xb4 DATA op.
591 					db_lvar = true;
592 					db_loc = v[0];
593 					db_offset = converse->get_var(v[1]);
594 				} else if (top.val == 0xb4) { // eg DB1[0] = ...
595 					pop_arg(i); //0xb4 DATA op.
596 					db_lvar = true;
597 					db_loc = v[0];
598 					db_offset = v[1];
599 				}
600 #ifdef CONVERSE_DEBUG
601 				DEBUG(1, LEVEL_DEBUGGING, "DB lvar found. location = %x, offset = %x", db_loc, db_offset);
602 #endif
603 			}
604 		}
605 		let(v[0], v[1]);
606 		break;
607 	case U6OP_ASSIGN: // 0xa8
608 	case U6OP_SETNAME: // 0xd8
609 		v[0] = pop_arg(i); // value
610 		if (inVal == U6OP_ASSIGN) { // 0xa8
611 			if (db_lvar) {
612 				set_db_integer(db_loc, db_offset, v[0]);
613 			} else if (decl_v <= U6TALK_VAR__LAST_) {
614 				if (decl_t == 0xb3)
615 					converse->set_svar(decl_v, get_rstr(v[0]));
616 				else if (decl_t == 0xb2)
617 					converse->set_var(decl_v, v[0]);
618 			} else {
619 				converse->print("[Illegal variable]\n");
620 				DEBUG(0, LEVEL_ERROR, "[Illegal variable] decl_v = %x decl_t = %x\n", decl_v, decl_t);
621 			}
622 		} else { // // 0xd8, assign name of npc `result' to ystr
623 			v[0] = npc_num(v[0]);
624 			// get name from party information
625 			if (player->get_party()->contains_actor(v[0])) {
626 				v[0] = player->get_party()->get_member_num(v[0]);
627 				set_ystr(player->get_party()->get_actor_name(v[0]));
628 			} else
629 				set_ystr(converse->npc_name(v[0])); // read from script
630 		}
631 		let(); // clear
632 		break;
633 	case U6OP_JUMP: // 0xb0
634 		v[0] = pop_arg(i);
635 #ifdef CONVERSE_DEBUG
636 		DEBUG(1, LEVEL_DEBUGGING, "Converse: JUMP 0x%04x\n", v[0]);
637 #endif
638 		cs->seek(v[0]);
639 		leave_all(); // always run
640 		break;
641 	case U6OP_DPRINT: { // 0xb5
642 		v[0] = pop_arg(i); // db location
643 		v[1] = pop_arg(i); // index
644 		char *dstring = get_db_string(v[0], v[1]);
645 		if (dstring) {
646 			converse->set_output(dstring); // data may have special symbols
647 //                converse->print(dstring); // data can have no special symbols -- wrong
648 //                converse->print("\n");
649 			free(dstring);
650 		}
651 		break;
652 	}
653 	case U6OP_BYE: // 0xb6
654 		stop();
655 		break;
656 	case U6OP_NEW: // 0xb9 (npc, obj, qual, quant)
657 		op_create_new(i);
658 		break;
659 	case U6OP_DELETE: // 0xba
660 //bool Actor::inventory_del_object(uint16 obj_n, uint8 qty, uint8 quality);
661 		v[0] = pop_arg(i); // npc
662 		v[1] = pop_arg(i); // obj
663 		v[2] = pop_arg(i); // qual
664 		v[3] = pop_arg(i); // quant
665 		cnpc = converse->actors->get_actor(npc_num(v[0]));
666 		if (cnpc)
667 			cnpc->inventory_del_object(v[1], v[3], v[2]);
668 		break;
669 	case U6OP_GIVE: // 0xc9
670 		v[0] = pop_arg(i); // obj
671 		v[1] = pop_arg(i); // qual
672 		v[2] = pop_arg(i); // from
673 		v[3] = pop_arg(i); // to
674 		cnpc = converse->actors->get_actor(npc_num(v[2]));
675 		if (!cnpc)
676 			break;
677 		cnpc->inventory_del_object(v[0], 1, v[1]);
678 		cnpc = converse->actors->get_actor(npc_num(v[3]));
679 		if (cnpc)
680 			cnpc->inventory_new_object(v[0], 1, v[1]);
681 		break;
682 	case U6OP_ADDKARMA: // 0xc4
683 		player->add_karma(pop_arg(i));
684 		break;
685 	case U6OP_SUBKARMA: // 0xc5
686 		player->subtract_karma(pop_arg(i));
687 		break;
688 	case MDOP_MISC_ACTION: // 0xd1
689 		v[0] = pop_arg(i); // action number
690 		Game::get_game()->get_script()->call_talk_script(v[0]);
691 		break;
692 	case U6OP_RESURRECT: { // 0xd6
693 		v[0] = npc_num(pop_arg(i));
694 		uint16 body = OBJ_U6_DEAD_BODY;
695 
696 		if (Game::get_game()->get_game_type() == NUVIE_GAME_SE)
697 			body = OBJ_SE_DEAD_BODY;
698 		else if (Game::get_game()->get_game_type() == NUVIE_GAME_MD)
699 			body = OBJ_MD_DEAD_BODY;
700 
701 		if (v[0] == 0) // Party
702 			cnpc = converse->player->get_party()->who_has_obj(body , 0, false);
703 		else
704 			cnpc = converse->actors->get_actor(v[0]);
705 
706 		cnpc_obj = cnpc->inventory_get_object(body, 0, false);
707 		if (Game::get_game()->get_game_type() == NUVIE_GAME_U6 && !cnpc_obj) {
708 			if (v[0] == 0)
709 				cnpc = converse->player->get_party()->who_has_obj(OBJ_U6_MOUSE, 0, false);
710 			cnpc_obj = cnpc->inventory_get_object(OBJ_U6_MOUSE, 0, false);
711 		}
712 		if (cnpc_obj != NULL) {
713 			if (converse->actors->resurrect_actor(cnpc_obj, converse->player->get_actor()->get_location())) {
714 				converse->objects->unlink_from_engine(cnpc_obj);
715 				delete_obj(cnpc_obj);
716 			}
717 		}
718 		break;
719 	}
720 	case U6OP_HEAL: // 0xd9
721 		cnpc = converse->actors->get_actor(npc_num(pop_arg(i)));
722 		if (cnpc)
723 			cnpc->heal();
724 		break;
725 	case U6OP_CURE: // 0xdb
726 		cnpc = converse->actors->get_actor(npc_num(pop_arg(i)));
727 		if (cnpc)
728 			cnpc->set_poisoned(false);
729 		break;
730 	case U6OP_WORKTYPE: // 0xcd
731 		v[0] = pop_arg(i); // npc
732 		v[1] = pop_arg(i); // worktype
733 		cnpc = converse->actors->get_actor(npc_num(v[0]));
734 		if (cnpc)
735 			cnpc->set_worktype(v[1]);
736 		break;
737 	case U6OP_INVENTORY: // 0xbe
738 		converse->print("!inventory\n");
739 		break;
740 	case U6OP_PORTRAIT: // 0xbf
741 		converse->show_portrait(npc_num(pop_arg(i)));
742 		break;
743 	case U6OP_WAIT: // 0xcb, set page-break on scroller and wait
744 		converse->print("*");
745 		wait();
746 		break;
747 	case U6OP_ENDANSWER: // 0xee
748 		break; // (frame only)
749 	case U6OP_KEYWORDS: // 0xef (text:keywords)
750 		if (answer_mode != ANSWER_DONE) { // havn't already answered
751 			answer_mode = ANSWER_NO;
752 			if (check_keywords(get_text(), converse->get_input()))
753 				answer_mode = ANSWER_YES;
754 		}
755 		break; // (frame only)
756 	case U6OP_SIDENT: // 0xff, arg 1 is id number, name follows
757 		v[0] = pop_arg(i);
758 		if (v[0] != converse->npc_num)
759 			DEBUG(0, LEVEL_WARNING,
760 			      "Converse: npc number inVal script (%d) does not"
761 			      " match actor number (%d)\n", v[0], converse->npc_num);
762 		converse->_name = get_text(); // collected
763 		break;
764 	case U6OP_SLOOK: // 0xf1, description follows
765 		converse->_desc = get_formatted_text(get_text().c_str()); // collected
766 		converse->print("\nYou see ");
767 		converse->print(converse->_desc.c_str());
768 		converse->print("\n");
769 		break;
770 	case U6OP_SCONVERSE: // 0xf2
771 		break;
772 	case U6OP_SPREFIX: // 0xf3, Unknown
773 		break;
774 	case U6OP_ANSWER: // 0xf6
775 		set_break(U6OP_KEYWORDS); // run or skip to next 0xef
776 		if (answer_mode == ANSWER_YES) { // depending on input comparison
777 			set_run(true);
778 			answer_mode = ANSWER_DONE; // don't do further comparisons
779 		} else
780 			set_run(false);
781 		break;
782 	case U6OP_ASK: // 0xf7
783 		answer_mode = ANSWER_NO; // reset answer switch
784 		converse->collect_input();
785 		break;
786 	case U6OP_ASKC: // 0xf8 (blocking, single character input)
787 		answer_mode = ANSWER_NO; // reset answer switch
788 		converse->poll_input(get_text().c_str(), false); // collected=allowed input
789 		break;
790 	case U6OP_INPUTSTR: // 0xf9 (string or integer)
791 		answer_mode = ANSWER_NO; // reset answer switch
792 		// fall through
793 	case U6OP_INPUT: // 0xfb (integer)
794 		v[0] = pop_arg(i); // var
795 		v[1] = pop_arg(i); // type (should be 0xb2)
796 		let(v[0], v[1]);
797 		converse->poll_input();
798 		break;
799 	case U6OP_INPUTNUM: // 0xfc
800 		v[0] = pop_arg(i); // var
801 		v[1] = pop_arg(i); // type
802 		let(v[0], v[1]);
803 		converse->poll_input("0123456789");
804 		break;
805 	default:
806 		converse->print("[Unknown command]\n");
807 		DEBUG(0, LEVEL_ERROR, "Converse: UNK OP=%02x\n", inVal);
808 		success = false;
809 	}
810 	return (success);
811 }
812 
813 
814 /* The other set of codes, these operate on the input values, so call them
815  * valops. Output goes back to the stack.
816  */
evop(stack<converse_typed_value> & i)817 bool ConverseInterpret::evop(stack<converse_typed_value> &i) {
818 	bool success = true;
819 	converse_value v[4]; // input
820 	converse_typed_value inVal;
821 	converse_typed_value out;
822 	Actor *cnpc = NULL;
823 	Obj *cnpc_obj = NULL;
824 //    converse_db_s *cdb;
825 	Player *player = converse->player;
826 
827 	out.type = U6OP_VAR;
828 	out.val = 0;
829 
830 	inVal.val = pop_arg(i);
831 
832 #ifdef CONVERSE_DEBUG
833 	DEBUG(1, LEVEL_DEBUGGING, "evop %s(%x)\n", evop_str(inVal.val), inVal.val);
834 #endif
835 
836 	switch (inVal.val) {
837 	case U6OP_GT: // 0x81
838 		v[1] = pop_arg(i);
839 		v[0] = pop_arg(i);
840 		if (v[0] > v[1])
841 			out.val = 1;
842 		break;
843 	case U6OP_GE: // 0x82
844 		v[1] = pop_arg(i);
845 		v[0] = pop_arg(i);
846 		if (v[0] >= v[1])
847 			out.val = 1;
848 		break;
849 	case U6OP_LT: // 0x83
850 		v[1] = pop_arg(i);
851 		v[0] = pop_arg(i);
852 		if (v[0] < v[1])
853 			out.val = 1;
854 		break;
855 	case U6OP_LE: // 0x84
856 		v[1] = pop_arg(i);
857 		v[0] = pop_arg(i);
858 		if (v[0] <= v[1])
859 			out.val = 1;
860 		break;
861 	case U6OP_NE: // 0x85
862 		if (pop_arg(i) != pop_arg(i))
863 			out.val = 1;
864 		break;
865 	case U6OP_EQ: // 0x86
866 		out.val = evop_eq(i);
867 		break;
868 	case U6OP_ADD: // 0x90
869 		out.val = pop_arg(i) + pop_arg(i);
870 		break;
871 	case U6OP_SUB: // 0x91
872 		v[1] = pop_arg(i);
873 		v[0] = pop_arg(i);
874 		out.val = v[0] - v[1];
875 		break;
876 	case U6OP_MUL: // 0x92
877 		out.val = pop_arg(i) * pop_arg(i);
878 		break;
879 	case U6OP_DIV: // 0x93
880 		v[1] = pop_arg(i);
881 		v[0] = pop_arg(i);
882 		if (v[1] == 0) {
883 			DEBUG(0, LEVEL_ERROR, "Converse: Divide error\n");
884 			success = false;
885 			stop();
886 		} else
887 			out.val = v[0] / v[1];
888 		break;
889 	case U6OP_LOR: // 0x94
890 		v[1] = pop_arg(i);
891 		v[0] = pop_arg(i);
892 		if (v[0] || v[1])
893 			out.val = 1;
894 		break;
895 	case U6OP_LAND: // 0x95
896 		v[1] = pop_arg(i);
897 		v[0] = pop_arg(i);
898 		if (v[0] && v[1])
899 			out.val = 1;
900 		break;
901 	case U6OP_CANCARRY: // 0x9a
902 		cnpc = converse->actors->get_actor(pop_arg(i));
903 		if (cnpc)
904 			out.val = (converse_value)((cnpc->inventory_get_max_weight()
905 			                            - cnpc->get_inventory_weight()) * 10);
906 		break;
907 	case U6OP_WEIGHT: { // 0x9b
908 		v[1] = pop_arg(i); // quantity
909 		v[0] = pop_arg(i); // object
910 		float weight = (float)converse->objects->get_obj_weight_unscaled(v[0]) * v[1]; //FIXME should this be scaled down by 10???
911 		if (converse->objects->has_reduced_weight(v[0]))
912 			weight /= 10;
913 		out.val = weight;
914 		break;
915 	}
916 	case U6OP_HORSED: // 0x9d
917 		cnpc = converse->actors->get_actor(npc_num(pop_arg(i)));
918 		cnpc_obj = cnpc->make_obj();
919 		if (cnpc_obj->obj_n == OBJ_U6_HORSE_WITH_RIDER)
920 			out.val = 1;
921 		delete_obj(cnpc_obj);
922 		break;
923 	case U6OP_HASOBJ: // 0x9f
924 		v[2] = pop_arg(i); // quality
925 		v[1] = pop_arg(i); // object
926 		v[0] = pop_arg(i); // npc
927 		cnpc = converse->actors->get_actor(npc_num(v[0]));
928 		if (cnpc && cnpc->inventory_has_object(v[1], v[2], (v[2] != 0 ? true : false))) //don't search quality if quality is 0
929 			out.val = 1;
930 		break;
931 	case U6OP_RAND: // 0xa0
932 		v[1] = pop_arg(i); // max.
933 		v[0] = pop_arg(i); // min.
934 		if (v[0] >= v[1])
935 			out.val = v[0];
936 		else
937 			out.val = v[0] + (NUVIE_RAND() % (v[1] - v[0] + 1));
938 		break;
939 	case U6OP_FLAG: // 0xab (npc, flag)
940 		v[1] = pop_arg(i);
941 		v[0] = pop_arg(i);
942 #ifdef CONVERSE_DEBUG
943 		DEBUG(1, LEVEL_DEBUGGING, "npc=%d bit=%d\n", v[0], v[1]);
944 #endif
945 		if (v[1] <= 7) {
946 			cnpc = converse->actors->get_actor(npc_num(v[0]));
947 			if (cnpc && (cnpc->get_talk_flags() & (1 << v[1])))
948 				out.val = 1;
949 		}
950 		break;
951 	case U6OP_VAR: // 0xb2
952 		out.val = converse->get_var(pop_arg(i));
953 		break;
954 	case U6OP_SVAR: // 0xb3 (using new rstring)
955 		out.val = add_rstr(converse->get_svar(pop_arg(i))); // rstr num
956 		out.type = U6OP_SVAR;
957 		break;
958 #if 0 /* old read style tries to detect if data is string or integer */
959 	case U6OP_DATA: // 0xb4
960 		v[1] = pop_arg(i); // index
961 		v[0] = pop_arg(i); // db location
962 		cdb = get_db(v[0], v[1]);
963 		if (!cdb)
964 			out.val = 0;
965 		else if (cdb->type == 0)
966 			out.val = add_rstr(cdb->s);
967 		else
968 			out.val = cdb->i;
969 		delete cdb;
970 		break;
971 #else /* new read assumes DATA may be assigned, and depend on variable type */
972 	case U6OP_DATA: // 0xb4
973 		v[1] = pop_arg(i); // index
974 		v[0] = pop_arg(i); // db location
975 
976 		if (decl_t == 0xb3) {
977 			char *dstring = get_db_string(v[0], v[1]);
978 			if (dstring) {
979 				out.val = add_rstr(dstring);
980 				free(dstring);
981 			} else
982 				out.val = add_rstr("");
983 
984 			out.type = U6OP_SVAR;
985 		} else // this will work for 0xB2 assignment and normal stack ops
986 			out.val = get_db_integer(v[0], v[1]);
987 		break;
988 #endif
989 	case U6OP_INDEXOF: // 0xb7
990 		v[1] = pop_arg(i); // string variable
991 		v[0] = pop_arg(i); // db location
992 		out.val = find_db_string(v[0], converse->get_svar(v[1]));
993 		break;
994 	case U6OP_OBJCOUNT: // 0xbb
995 		v[1] = pop_arg(i); // object
996 		v[0] = pop_arg(i); // npc
997 		cnpc = converse->actors->get_actor(npc_num(v[0])); //FIXME should this be party?
998 		if (cnpc)
999 			out.val = cnpc->inventory_count_object(v[1]);
1000 		break;
1001 	case U6OP_INPARTY: // 0xc6
1002 		if (player->get_party()->contains_actor(npc_num(pop_arg(i))))
1003 			out.val = 1;
1004 		break;
1005 	case U6OP_OBJINPARTY: { // 0xc7 ?? check if party has object
1006 		v[1] = pop_arg(i); // qual
1007 		v[0] = pop_arg(i); // obj
1008 		bool has_mouse = false; // resurrect others first
1009 		if (!player->get_party()->has_obj(v[0], v[1], false) && (v[0] != OBJ_U6_DEAD_BODY
1010 		        || Game::get_game()->get_game_type() != NUVIE_GAME_U6
1011 		        || !(has_mouse = player->get_party()->has_obj(OBJ_U6_MOUSE, v[1], false))))
1012 			out.val = 0x8001; // something OR'ed or u6val version of "no npc"?
1013 		else {
1014 			cnpc = player->get_party()->who_has_obj(has_mouse ? OBJ_U6_MOUSE : v[0], v[1], false);
1015 			assert(cnpc);
1016 			out.val = cnpc->get_actor_num(); // first NPC that has object (sometimes 0xFFFF?)
1017 		}
1018 		break;
1019 	}
1020 	case U6OP_JOIN: // 0xca
1021 		cnpc = converse->actors->get_actor(npc_num(pop_arg(i)));
1022 		if (cnpc) {
1023 			if (player->get_party()->contains_actor(cnpc))
1024 				out.val = 3; // 3: ALREADY IN PARTY
1025 			else if (player->get_party()->get_party_size() >=
1026 			         player->get_party()->get_party_max())
1027 				out.val = 2; // 2: PARTY TOO LARGE
1028 			else if (player->get_actor()->get_actor_num() == 0) // vehicle
1029 				out.val = 1; // 1: NOT ON LAND
1030 			else
1031 				player->get_party()->add_actor(cnpc);
1032 			// 0: SUCCESS
1033 		}
1034 		break;
1035 	case U6OP_LEAVE: // 0xcc
1036 		cnpc = converse->actors->get_actor(npc_num(pop_arg(i)));
1037 		if (cnpc) {
1038 			if (!player->get_party()->contains_actor(cnpc))
1039 				out.val = 2; // 2: NOT IN PARTY
1040 			else if (player->get_actor()->get_actor_num() == 0) // vehicle
1041 				out.val = 1; // 1: NOT ON LAND
1042 			else
1043 				player->get_party()->remove_actor(cnpc);
1044 			// 0: SUCCESS
1045 		}
1046 		break;
1047 	case U6OP_NPCNEARBY: // 0xd7
1048 		cnpc = converse->actors->get_actor(npc_num(pop_arg(i)));
1049 		if (cnpc)
1050 			out.val = converse->npc->is_nearby(cnpc) ? 1 : 0;
1051 		break;
1052 	case U6OP_WOUNDED: // 0xda
1053 		cnpc = converse->actors->get_actor(npc_num(pop_arg(i)));
1054 		if (cnpc)
1055 			if (cnpc->get_hp() < cnpc->get_maxhp())
1056 				out.val = 1;
1057 		break;
1058 	case U6OP_POISONED: // 0xdc
1059 		cnpc = converse->actors->get_actor(npc_num(pop_arg(i)));
1060 		if (cnpc)
1061 			out.val = cnpc->is_poisoned() ? 1 : 0;
1062 		break;
1063 	case U6OP_NPC: // 0xdd (val2=??)
1064 		v[1] = pop_arg(i);
1065 		v[0] = pop_arg(i); // 0+
1066 		cnpc = player->get_party()->get_actor(v[0]);
1067 		out.val = cnpc ? cnpc->get_actor_num() : 0;
1068 		break;
1069 	case U6OP_EXP: // 0xe0
1070 		v[1] = pop_arg(i); // add value
1071 		v[0] = pop_arg(i); // npc
1072 		cnpc = converse->actors->get_actor(npc_num(v[0]));
1073 		if (cnpc) {
1074 			out.val = cnpc->get_exp() + v[1];
1075 			cnpc->set_exp(out.val);
1076 		}
1077 		break;
1078 	case U6OP_LVL: // 0xe1
1079 		v[1] = pop_arg(i);
1080 		v[0] = pop_arg(i);
1081 		cnpc = converse->actors->get_actor(npc_num(v[0]));
1082 		if (cnpc) {
1083 			out.val = cnpc->get_level() + v[1];
1084 			cnpc->set_level(out.val);
1085 		}
1086 		break;
1087 	case U6OP_STR: // 0xe2
1088 		v[1] = pop_arg(i);
1089 		v[0] = pop_arg(i);
1090 		cnpc = converse->actors->get_actor(npc_num(v[0]));
1091 		if (cnpc) {
1092 			out.val = cnpc->get_strength() + v[1];
1093 			cnpc->set_strength(out.val);
1094 		}
1095 		break;
1096 	case U6OP_INT: // 0xe3
1097 		v[1] = pop_arg(i);
1098 		v[0] = pop_arg(i);
1099 		cnpc = converse->actors->get_actor(npc_num(v[0]));
1100 		if (cnpc) {
1101 			out.val = cnpc->get_intelligence() + v[1];
1102 			cnpc->set_intelligence(out.val);
1103 		}
1104 		break;
1105 	case U6OP_DEX: // 0xe4
1106 		v[1] = pop_arg(i);
1107 		v[0] = pop_arg(i);
1108 		cnpc = converse->actors->get_actor(npc_num(v[0]));
1109 		if (cnpc) {
1110 			out.val = cnpc->get_dexterity() + v[1];
1111 			cnpc->set_dexterity(out.val);
1112 		}
1113 		break;
1114 	default:
1115 		DEBUG(0, LEVEL_ERROR, "Converse: UNK EVOP=%02x\n", inVal);
1116 		success = false;
1117 	}
1118 	i.push(out);
1119 	return (success);
1120 }
1121 
evop_eq(stack<converse_typed_value> & vs)1122 converse_value ConverseInterpret::evop_eq(stack<converse_typed_value> &vs) {
1123 	converse_typed_value operand1 = pop_typed_arg(vs);
1124 	converse_typed_value operand2 = pop_typed_arg(vs);
1125 	converse_value out = 0;
1126 
1127 	if (operand1.type == U6OP_VAR) {
1128 		if (operand1.val == operand2.val)
1129 			out = 1;
1130 	} else {
1131 		if (scumm_stricmp(get_rstr(operand1.val), get_rstr(operand2.val)) == 0)
1132 			out = 1;
1133 	}
1134 
1135 	return out;
1136 }
1137 
1138 /* Take the values collected so far as input (starting with value `vi') and run
1139  * any assign/compare value ops that are mixed with the data. The result goes
1140  * back to the input list, and replaces everything after and including `vi'.
1141  */
eval(uint32 vi)1142 void ConverseInterpret::eval(uint32 vi) {
1143 	stack<converse_typed_value> op_stk, r_stk;
1144 	uint32 v = vi;
1145 #ifdef CONVERSE_DEBUG
1146 	DEBUG(1, LEVEL_DEBUGGING, "Converse: EVAL");
1147 	for (uint32 w = 0; w < val_count(); w++)
1148 		DEBUG(1, LEVEL_DEBUGGING, " %s0x%02x%s", (w == vi) ? "(" : "", get_val(w), (w == val_count() - 1) ? ")" : "");
1149 #endif
1150 
1151 	while (v < val_count()) {
1152 		converse_typed_value a = {U6OP_VAR, 0};
1153 		a.val = get_val(v);
1154 		uint8 ds = get_val_size(v++);
1155 		op_stk.push(a);
1156 		if (is_valop(a.val) && !ds)
1157 			evop(op_stk);
1158 	}
1159 	in.resize(vi);
1160 	if (op_stk.empty()) // took all parameters, no return
1161 		add_val(0x00);
1162 	else {
1163 		while (!op_stk.empty()) {
1164 			r_stk.push(op_stk.top()); // reverse
1165 			op_stk.pop();
1166 		}
1167 		while (!r_stk.empty())
1168 			add_val(pop_arg(r_stk)); // return(s)
1169 	}
1170 
1171 #ifdef CONVERSE_DEBUG
1172 	DEBUG(1, LEVEL_DEBUGGING, " ->");
1173 	if (val_count()) {
1174 		for (uint32 w = 0; w < val_count(); w++)
1175 			DEBUG(1, LEVEL_DEBUGGING, " 0x%02x", get_val(w));
1176 		DEBUG(1, LEVEL_DEBUGGING, "\n");
1177 	}
1178 #endif
1179 }
1180 
1181 
1182 /* Return `n' as-is or convert to number of NPC from Converse.
1183  */
npc_num(uint32 n)1184 uint8 ConverseInterpret::npc_num(uint32 n) {
1185 	return ((n != 0xeb) ? n : converse->npc_num);
1186 }
1187 
1188 
1189 /* Returns true if the keywords list contains the input string, or contains an
1190  * asterisk (matching any input).
1191  */
check_keywords(string keystr,string instr)1192 bool ConverseInterpret::check_keywords(string keystr, string instr) {
1193 	const char *strt_s = NULL;
1194 	char *tok_s = NULL, *cmp_s = NULL;
1195 	if (keystr == "*")
1196 		return (true);
1197 	// check each comma-separated keyword
1198 	strt_s = keystr.c_str();
1199 	for (uint32 c = 0; c < strlen(strt_s); c++) {
1200 		// check at start of string and each keyword
1201 		if (c == 0 || strt_s[c] == ',') {
1202 			// copy from keyword start to end of string/keyword
1203 			uint32 l;
1204 			tok_s = scumm_strdup(&strt_s[(c == 0) ? c : c + 1]);
1205 			for (l = 0; l < strlen(tok_s) && tok_s[l] != ','; l++);
1206 			tok_s[l] = '\0';
1207 			cmp_s = scumm_strdup(instr.c_str());
1208 			// trim input to keyword size
1209 			if (l < strlen(cmp_s))
1210 				cmp_s[l] = '\0';
1211 			// compare
1212 			if (!scumm_stricmp(tok_s, cmp_s)) {
1213 				free(cmp_s);
1214 				free(tok_s);
1215 				return (true);
1216 			}
1217 			free(cmp_s);
1218 			free(tok_s);
1219 		}
1220 	}
1221 	return (false);
1222 }
1223 
1224 
1225 /* Assign input from Converse to the declared variable.
1226  */
assign_input()1227 void ConverseInterpret::assign_input() {
1228 	// FIXME: Nuvie treats 0xF9-INPUTSTR & 0xFB-INPUT as identical, but in U6
1229 	//        0xFB-INPUT could not input strings.
1230 	if (decl_t == 0xb2)
1231 		converse->set_var(decl_v, strtol(converse->get_input().c_str(), NULL, 10));
1232 	if (decl_t == 0xb3)
1233 		converse->set_svar(decl_v, converse->get_input().c_str());
1234 }
1235 
1236 
1237 /* Collect data from section at `loc', index `i'.
1238  * Returns pointer to data, which can be 8bit integer or a character string.
1239  * FIXME: there is no checking for overflow beyond the initial location
1240  *        Deprecated function that guesses the data type, and reads incorrectly.
1241  */
1242 struct ConverseInterpret::converse_db_s *
get_db(uint32 loc,uint32 i)1243 ConverseInterpret::get_db(uint32 loc, uint32 i) {
1244 	convscript_buffer db = converse->script->get_buffer(loc);
1245 	struct converse_db_s *item = NULL;
1246 	uint32 d = 0, dbuf_len = 0, p = 0, e = 0;
1247 	if (!db)
1248 		return (NULL);
1249 
1250 //    item = (struct converse_db_s *)malloc(sizeof(struct converse_db_s));
1251 	item = new struct converse_db_s;
1252 	item->type = 0;
1253 	item->s = NULL;
1254 	item->i = 0;
1255 	while (e++ <= i) {
1256 		if (is_print(db[p]) && is_print(db[p + 1])) {
1257 			d = 0;
1258 			dbuf_len = 0;
1259 			item->type = 0;
1260 			do {
1261 				if ((d + 1) >= dbuf_len)
1262 					dbuf_len += 16;
1263 				item->s = (char *)realloc(item->s, dbuf_len);
1264 				item->s[d++] = (char)(db[p]);
1265 				item->s[d] = '\0';
1266 			} while (is_print(db[++p]));
1267 		} else {
1268 			free(item->s);
1269 			item->s = 0;
1270 			item->type = 1;
1271 			item->i = (uint8)(db[p++]);
1272 		}
1273 		++p; // skip 0
1274 	}
1275 	return (item);
1276 }
1277 
1278 
1279 /* Collect data from section at `loc', index `i', as a string.
1280  * Returns pointer to NEW data, or NULL if only integer data is found.
1281  */
get_db_string(uint32 loc,uint32 i)1282 char *ConverseInterpret::get_db_string(uint32 loc, uint32 i) {
1283 	convscript_buffer db = converse->script->get_buffer(loc);
1284 	char *item = NULL;
1285 	uint32 d = 0, dbuf_len = 0, /* string pointer & length */
1286 	       p = 0; /* pointer into db */
1287 	if (!db)
1288 		return (NULL);
1289 	/* skip to index */
1290 	uint32 e = 0;
1291 	while (e++ < i) {
1292 		if (db[p] == U6OP_ENDDATA)
1293 			return (NULL);
1294 		while (is_print(db[p++]));
1295 	}
1296 
1297 	d = 0;
1298 	dbuf_len = 0;
1299 	do {
1300 		if ((d + 1) >= dbuf_len) // resize buffer as needed
1301 			dbuf_len += 16;
1302 		item = (char *)nuvie_realloc(item, dbuf_len);
1303 		item[d++] = (char)(db[p]); // copy
1304 		item[d] = '\0';
1305 	} while (is_print(db[++p]));
1306 	return (item);
1307 }
1308 
1309 
1310 /* Collect data from section at `loc', index `i', as an integer.
1311  * Returns the two-byte integer value.
1312  */
get_db_integer(uint32 loc,uint32 i)1313 converse_value ConverseInterpret::get_db_integer(uint32 loc, uint32 i) {
1314 	uint16 item = 0;
1315 	uint32 p = 0; /* pointer into db */
1316 
1317 	/* skip to index */
1318 	uint32 e = 0;
1319 	while (e++ < i)
1320 		p += 2;
1321 
1322 	/* use ConvScript functions to check overflow and read data correctly */
1323 	uint32 old_pos = converse->script->pos();
1324 	converse->script->seek(loc + p);
1325 	if (!converse->script->overflow(+1))
1326 		item = converse->script->read2();
1327 	converse->script->seek(old_pos);
1328 
1329 	return ((converse_value)item);
1330 }
1331 
set_db_integer(uint32 loc,uint32 i,converse_value val)1332 void ConverseInterpret::set_db_integer(uint32 loc, uint32 i, converse_value val) {
1333 #ifdef CONVERSE_DEBUG
1334 	DEBUG(1, LEVEL_DEBUGGING, "set_db_integer(%x, %x, %d)\n", loc, i, val);
1335 #endif
1336 	uint32 p = 0; /* pointer into db */
1337 
1338 	/* skip to index */
1339 	uint32 e = 0;
1340 	while (e++ < i)
1341 		p += 2;
1342 
1343 	/* use ConvScript functions to check overflow and read data correctly */
1344 	uint32 old_pos = converse->script->pos();
1345 	converse->script->seek(loc + p);
1346 	if (!converse->script->overflow(+1))
1347 		converse->script->write2(val);
1348 	converse->script->seek(old_pos);
1349 
1350 	return;
1351 }
1352 
1353 /* Scan data section `loc' for `dstring'. Stop at ENDDATA.
1354  * Returns the index of the string or the index of the ENDDATA marker.
1355  */
find_db_string(uint32 loc,const char * dstring)1356 converse_value ConverseInterpret::find_db_string(uint32 loc, const char *dstring) {
1357 	convscript_buffer db = converse->script->get_buffer(loc);
1358 	char *item = NULL; /* item being checked */
1359 	uint32 d = 0, dbuf_len = 0, /* string pointer & length */
1360 	       p = 0, /* pointer into db */
1361 	       i = 0; /* item index */
1362 #ifdef CONVERSE_DEBUG
1363 	DEBUG(1, LEVEL_DEBUGGING, "\nConverse: find_db_string(0x%04x, \"%s\")\n", loc, dstring);
1364 #endif
1365 	while ((converse_value)(db[p]) != U6OP_ENDDATA) {
1366 		if (is_print(db[p])) {
1367 			item = NULL;
1368 			d = 0;
1369 			dbuf_len = 0;
1370 			do {
1371 				if ((d + 1) >= dbuf_len) // resize buffer as needed
1372 					dbuf_len += 16;
1373 				item = (char *)nuvie_realloc(item, dbuf_len);
1374 				item[d++] = (char)(db[p]); // copy
1375 				item[d] = '\0';
1376 			} while (is_print(db[++p]));
1377 			++p; // skip this unprintable now so it's not counted as an item
1378 			if (item) {
1379 				string item_str = item;
1380 				string find_str = dstring;
1381 				free(item);
1382 				// match keywords format: clamp item to 4 characters
1383 				if (item_str.size() > 4)
1384 					item_str.resize(4);
1385 				if (check_keywords(item_str, find_str))
1386 					return (i);
1387 			}
1388 		} else ++p;
1389 		++i;
1390 	}
1391 #ifdef CONVERSE_DEBUG
1392 	DEBUG(0, LEVEL_DEBUGGING, "\nConverse: find_db_string: not found; returning %d\n", i);
1393 #endif
1394 	return (i);
1395 }
1396 
evop_str(converse_value op)1397 const char *ConverseInterpret::evop_str(converse_value op) {
1398 	switch (op) {
1399 	case U6OP_GT:
1400 		return "U6OP_GT";
1401 	case U6OP_GE:
1402 		return "U6OP_GE";
1403 	case U6OP_LT:
1404 		return "U6OP_LT";
1405 	case U6OP_LE:
1406 		return "U6OP_LE";
1407 	case U6OP_NE:
1408 		return "U6OP_NE";
1409 	case U6OP_EQ:
1410 		return "U6OP_EQ";
1411 	case U6OP_ADD:
1412 		return "U6OP_ADD";
1413 	case U6OP_SUB:
1414 		return "U6OP_SUB";
1415 	case U6OP_MUL:
1416 		return "U6OP_MUL";
1417 	case U6OP_DIV:
1418 		return "U6OP_DIV";
1419 	case U6OP_LOR:
1420 		return "U6OP_LOR";
1421 	case U6OP_LAND:
1422 		return "U6OP_LAND";
1423 	case U6OP_CANCARRY:
1424 		return "U6OP_CANCARRY";
1425 	case U6OP_WEIGHT:
1426 		return "U6OP_WEIGHT";
1427 	case U6OP_HORSED:
1428 		return "U6OP_HORSED";
1429 	case U6OP_HASOBJ:
1430 		return "U6OP_HASOBJ";
1431 	case U6OP_RAND:
1432 		return "U6OP_RAND";
1433 	case U6OP_FLAG:
1434 		return "U6OP_FLAG";
1435 	case U6OP_VAR:
1436 		return "U6OP_VAR";
1437 	case U6OP_SVAR:
1438 		return "U6OP_SVAR";
1439 	case U6OP_DATA:
1440 		return "U6OP_DATA";
1441 	case U6OP_INDEXOF:
1442 		return "U6OP_INDEXOF";
1443 	case U6OP_OBJCOUNT:
1444 		return "U6OP_OBJCOUNT";
1445 	case U6OP_INPARTY:
1446 		return "U6OP_INPARTY";
1447 	case U6OP_OBJINPARTY:
1448 		return "U6OP_OBJINPARTY";
1449 	case U6OP_JOIN:
1450 		return "U6OP_JOIN";
1451 	case U6OP_LEAVE:
1452 		return "U6OP_LEAVE";
1453 	case U6OP_NPCNEARBY:
1454 		return "U6OP_NPCNEARBY";
1455 	case U6OP_WOUNDED:
1456 		return "U6OP_WOUNDED";
1457 	case U6OP_POISONED:
1458 		return "U6OP_POISONED";
1459 	case U6OP_NPC:
1460 		return "U6OP_NPC";
1461 	case U6OP_EXP:
1462 		return "U6OP_EXP";
1463 	case U6OP_LVL:
1464 		return "U6OP_LVL";
1465 	case U6OP_STR:
1466 		return "U6OP_STR";
1467 	case U6OP_INT:
1468 		return "U6OP_INT";
1469 	case U6OP_DEX:
1470 		return "U6OP_DEX";
1471 	default:
1472 		break;
1473 	}
1474 
1475 	return "U6OP_UNKNOWN";
1476 }
1477 
op_str(converse_value op)1478 const char *ConverseInterpret::op_str(converse_value op) {
1479 	switch (op) {
1480 	case U6OP_SLEEP:
1481 		return "U6OP_SLEEP";
1482 	case U6OP_HORSE:
1483 		return "U6OP_HORSE";
1484 	case U6OP_IF:
1485 		return "U6OP_IF";
1486 	case U6OP_ENDIF:
1487 		return "U6OP_ENDIF";
1488 	case U6OP_ELSE:
1489 		return "U6OP_ELSE";
1490 	case U6OP_SETF:
1491 		return "U6OP_SETF";
1492 	case U6OP_CLEARF:
1493 		return "U6OP_CLEARF";
1494 	case U6OP_DECL:
1495 		return "U6OP_DECL";
1496 	case U6OP_ASSIGN:
1497 		return "U6OP_ASSIGN";
1498 	case U6OP_SETNAME:
1499 		return "U6OP_SETNAME";
1500 	case U6OP_JUMP:
1501 		return "U6OP_JUMP";
1502 	case U6OP_DPRINT:
1503 		return "U6OP_DPRINT";
1504 	case U6OP_BYE:
1505 		return "U6OP_BYE";
1506 	case U6OP_NEW:
1507 		return "U6OP_NEW";
1508 	case U6OP_DELETE:
1509 		return "U6OP_DELETE";
1510 	case U6OP_GIVE:
1511 		return "U6OP_GIVE";
1512 	case U6OP_ADDKARMA:
1513 		return "U6OP_ADDKARMA";
1514 	case U6OP_SUBKARMA:
1515 		return "U6OP_SUBKARMA";
1516 	case U6OP_RESURRECT:
1517 		return "U6OP_RESURRECT";
1518 	case U6OP_HEAL:
1519 		return "U6OP_HEAL";
1520 	case U6OP_CURE:
1521 		return "U6OP_CURE";
1522 	case U6OP_WORKTYPE:
1523 		return "U6OP_WORKTYPE";
1524 	case U6OP_INVENTORY:
1525 		return "U6OP_INVENTORY";
1526 	case U6OP_PORTRAIT:
1527 		return "U6OP_PORTRAIT";
1528 	case U6OP_WAIT:
1529 		return "U6OP_WAIT";
1530 	case U6OP_ENDANSWER:
1531 		return "U6OP_ENDANSWER";
1532 	case U6OP_KEYWORDS:
1533 		return "U6OP_KEYWORDS";
1534 	case U6OP_SIDENT:
1535 		return "U6OP_SIDENT";
1536 	case U6OP_SLOOK:
1537 		return "U6OP_SLOOK";
1538 	case U6OP_SCONVERSE:
1539 		return "U6OP_SCONVERSE";
1540 	case U6OP_SPREFIX:
1541 		return "U6OP_SPREFIX";
1542 	case U6OP_ANSWER:
1543 		return "U6OP_ANSWER";
1544 	case U6OP_ASK:
1545 		return "U6OP_ASK";
1546 	case U6OP_ASKC:
1547 		return "U6OP_ASKC";
1548 	case U6OP_INPUTSTR:
1549 		return "U6OP_INPUTSTR";
1550 	case U6OP_INPUT:
1551 		return "U6OP_INPUT";
1552 	case U6OP_INPUTNUM:
1553 		return "U6OP_INPUTNUM";
1554 	default :
1555 		break;
1556 	}
1557 
1558 	return "U6OP_UNKNOWN";
1559 }
1560 
1561 } // End of namespace Nuvie
1562 } // End of namespace Ultima
1563