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 /* String and parser handling */
24 
25 #include "sci/resource/resource.h"
26 #include "sci/engine/state.h"
27 #include "sci/engine/selector.h"
28 #include "sci/engine/message.h"
29 #include "sci/engine/kernel.h"
30 
31 //#define DEBUG_PARSER
32 
33 namespace Sci {
34 
35 /*************************************************************/
36 /* Parser */
37 /**********/
38 
39 
kSaid(EngineState * s,int argc,reg_t * argv)40 reg_t kSaid(EngineState *s, int argc, reg_t *argv) {
41 	reg_t heap_said_block = argv[0];
42 	byte *said_block;
43 	int new_lastmatch;
44 	Vocabulary *voc = g_sci->getVocabulary();
45 #ifdef DEBUG_PARSER
46 	const int debug_parser = 1;
47 #else
48 	const int debug_parser = 0;
49 #endif
50 
51 	if (!heap_said_block.getSegment())
52 		return NULL_REG;
53 
54 	said_block = (byte *)s->_segMan->derefBulkPtr(heap_said_block, 0);
55 
56 	if (!said_block) {
57 		warning("Said on non-string, pointer %04x:%04x", PRINT_REG(heap_said_block));
58 		return NULL_REG;
59 	}
60 
61 #ifdef DEBUG_PARSER
62 		debugN("Said block: ");
63 		g_sci->getVocabulary()->debugDecipherSaidBlock(said_block);
64 #endif
65 
66 	if (voc->parser_event.isNull() || (readSelectorValue(s->_segMan, voc->parser_event, SELECTOR(claimed)))) {
67 		return NULL_REG;
68 	}
69 
70 	new_lastmatch = said(said_block, debug_parser);
71 	if (new_lastmatch  != SAID_NO_MATCH) { /* Build and possibly display a parse tree */
72 
73 #ifdef DEBUG_PARSER
74 		debugN("kSaid: Match.\n");
75 #endif
76 
77 		s->r_acc = make_reg(0, 1);
78 
79 		if (new_lastmatch != SAID_PARTIAL_MATCH)
80 			writeSelectorValue(s->_segMan, voc->parser_event, SELECTOR(claimed), 1);
81 
82 	} else {
83 		return NULL_REG;
84 	}
85 	return s->r_acc;
86 }
87 
kParse(EngineState * s,int argc,reg_t * argv)88 reg_t kParse(EngineState *s, int argc, reg_t *argv) {
89 	SegManager *segMan = s->_segMan;
90 	reg_t stringpos = argv[0];
91 	Common::String string = s->_segMan->getString(stringpos);
92 	char *error;
93 	reg_t event = argv[1];
94 	g_sci->checkVocabularySwitch();
95 	Vocabulary *voc = g_sci->getVocabulary();
96 	voc->parser_event = event;
97 	reg_t params[2] = { s->_segMan->getParserPtr(), stringpos };
98 
99 	ResultWordListList words;
100 	bool res = voc->tokenizeString(words, string.c_str(), &error);
101 	voc->parserIsValid = false; /* not valid */
102 
103 	if (res && !words.empty()) {
104 		voc->synonymizeTokens(words);
105 
106 		s->r_acc = make_reg(0, 1);
107 
108 #ifdef DEBUG_PARSER
109 		debugC(kDebugLevelParser, "Parsed to the following blocks:");
110 
111 		for (ResultWordListList::const_iterator i = words.begin(); i != words.end(); ++i) {
112 			debugCN(2, kDebugLevelParser, "   ");
113 			for (ResultWordList::const_iterator j = i->begin(); j != i->end(); ++j) {
114 				debugCN(2, kDebugLevelParser, "%sType[%04x] Group[%04x]", j == i->begin() ? "" : " / ", j->_class, j->_group);
115 			}
116 			debugCN(2, kDebugLevelParser, "\n");
117 		}
118 #endif
119 
120 		voc->replacePronouns(words);
121 
122 		int syntax_fail = voc->parseGNF(words);
123 
124 		if (syntax_fail) {
125 			s->r_acc = make_reg(0, 1);
126 			writeSelectorValue(segMan, event, SELECTOR(claimed), 1);
127 
128 			invokeSelector(s, g_sci->getGameObject(), SELECTOR(syntaxFail), argc, argv, 2, params);
129 			/* Issue warning */
130 
131 			debugC(kDebugLevelParser, "Tree building failed");
132 
133 		} else {
134 			voc->parserIsValid = true;
135 			voc->storePronounReference();
136 			writeSelectorValue(segMan, event, SELECTOR(claimed), 0);
137 
138 #ifdef DEBUG_PARSER
139 			voc->dumpParseTree();
140 #endif
141 		}
142 
143 	} else {
144 
145 		s->r_acc = make_reg(0, 0);
146 		writeSelectorValue(segMan, event, SELECTOR(claimed), 1);
147 
148 		if (error) {
149 			s->_segMan->strcpy(s->_segMan->getParserPtr(), error);
150 			debugC(kDebugLevelParser, "Word unknown: %s", error);
151 			/* Issue warning: */
152 
153 			invokeSelector(s, g_sci->getGameObject(), SELECTOR(wordFail), argc, argv, 2, params);
154 			free(error);
155 			return make_reg(0, 1); /* Tell them that it didn't work */
156 		}
157 	}
158 
159 	return s->r_acc;
160 }
161 
kSetSynonyms(EngineState * s,int argc,reg_t * argv)162 reg_t kSetSynonyms(EngineState *s, int argc, reg_t *argv) {
163 	SegManager *segMan = s->_segMan;
164 	reg_t object = argv[0];
165 	List *list;
166 	Node *node;
167 	int script;
168 	int numSynonyms = 0;
169 	Vocabulary *voc = g_sci->getVocabulary();
170 
171 	// Only SCI0-SCI1 EGA games had a parser. In newer versions, this is a stub
172 	if (!g_sci->hasParser())
173 		return s->r_acc;
174 
175 	voc->clearSynonyms();
176 
177 	list = s->_segMan->lookupList(readSelector(segMan, object, SELECTOR(elements)));
178 	node = s->_segMan->lookupNode(list->first);
179 
180 	while (node) {
181 		reg_t objpos = node->value;
182 		int seg;
183 
184 		script = readSelectorValue(segMan, objpos, SELECTOR(number));
185 		seg = s->_segMan->getScriptSegment(script);
186 
187 		if (seg > 0)
188 			numSynonyms = s->_segMan->getScript(seg)->getSynonymsNr();
189 
190 		if (numSynonyms) {
191 			const SciSpan<const byte> &synonyms = s->_segMan->getScript(seg)->getSynonyms();
192 
193 			if (synonyms) {
194 				debugC(kDebugLevelParser, "Setting %d synonyms for script.%d",
195 				          numSynonyms, script);
196 
197 				if (numSynonyms > 16384) {
198 					error("Segtable corruption: script.%03d has %d synonyms",
199 					         script, numSynonyms);
200 					/* We used to reset the corrupted value here. I really don't think it's appropriate.
201 					 * Lars */
202 				} else
203 					for (int i = 0; i < numSynonyms; i++) {
204 						synonym_t tmp;
205 						tmp.replaceant = synonyms.getUint16LEAt(i * 4);
206 						tmp.replacement = synonyms.getUint16LEAt(i * 4 + 2);
207 						voc->addSynonym(tmp);
208 					}
209 			} else
210 				warning("Synonyms of script.%03d were requested, but script is not available", script);
211 
212 		}
213 
214 		node = s->_segMan->lookupNode(node->succ);
215 	}
216 
217 	debugC(kDebugLevelParser, "A total of %d synonyms are active now.", numSynonyms);
218 
219 	return s->r_acc;
220 }
221 
222 } // End of namespace Sci
223