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