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 "glk/agt/agility.h"
24 
25 namespace Glk {
26 namespace AGT {
27 
28 /*
29   This is a mishmash of utilities and preinitialized arrays,
30   including the verblist, the metacommand token list,
31   and the dictionary routines.
32 */
33 
34 /* ------------------------------------------------------------------- */
35 /*  Preinitialized data structures                                     */
36 /*    Most of the preinitialized data structures used by all of the    */
37 /*    AGT-related programs go here .                                   */
38 /* ------------------------------------------------------------------- */
39 
40 
41 /* ------------------------------------------------------------ */
42 /* The PC --> ASCII conversion table. This converts the 8th-bit */
43 /*   PC characters to their nearest ASCII equivalent.            */
44 /* ------------------------------------------------------------ */
45 
46 const char trans_ibm[] =
47 	"CueaaaaceeeiiiAA"   /* 80 */
48 	"E@@ooouuyOUc$$pf"   /* 90 */
49 	"aiounNao?....!<>"   /* A0 */
50 	"###|++|+++|\\/++\\"   /* B0 */
51 	"\\+++-+||\\/+++=+="   /* C0 */
52 	"+=+++++++//@@@@@"   /* D0 */
53 	"abGpSsmtFTOd.fe^"   /* E0 */
54 	"=+><fj/=***/n2# ";  /* F0 */
55 
56 
57 /* ------------------------------------------------------------- */
58 /* Tables of built in properties and attributes */
59 /* ------------------------------------------------------------- */
60 
61 #define rnc(p) {#p,offsetof(room_rec,p),offsetof(noun_rec,p), \
62 		offsetof(creat_rec,p)}
63 #define rn(p) {#p,offsetof(room_rec,p),offsetof(noun_rec,p),-1}
64 #define nc(p) {#p,-1,offsetof(noun_rec,p),offsetof(creat_rec,p)}
65 #define rc(p) {#p,offsetof(room_rec,p),-1,offsetof(creat_rec,p)}
66 #define r(p) {#p, offsetof(room_rec,p), -1, -1}
67 #define n(p) {#p, -1, offsetof(noun_rec,p),-1}
68 #define c(p) {#p, -1, -1, offsetof(creat_rec,p)}
69 
70 const prop_struct proplist[NUM_PROP] = {
71 	/* The following are writable */
72 	rnc(oclass), rnc(points), r(light),
73 	n(num_shots), n(weight), n(size),
74 	c(counter), c(timecounter),
75 	/* The remaining properties are read-only */
76 	nc(location), rn(key), c(weapon), c(threshold), c(timethresh),
77 	nc(flagnum)
78 };
79 
80 const prop_struct attrlist[NUM_ATTR] = {
81 	/* The following are writable */
82 	n(on), n(open), n(locked), n(movable),
83 	c(groupmemb), c(hostile),
84 	/* The remaining attributes are read-only */
85 	r(end), rn(win), r(killplayer), n(plural),
86 	n(pushable), n(pullable), n(turnable), n(playable), n(readable), n(closable),
87 	n(lockable), n(edible), n(wearable), n(drinkable), n(poisonous), n(light),
88 	n(shootable), nc(isglobal),
89 	/* This is writable again */
90 	rnc(seen),
91 	nc(proper) /* This is not writable */
92 };
93 
94 #undef rnc
95 #undef rn
96 #undef rc
97 #undef cn
98 #undef r
99 #undef c
100 #undef n
101 
102 
103 
104 /* ------------------------------------------------------------- */
105 /*  Tables of Opcodes                                            */
106 /*   These gives the names and argument types of all of the AGT  */
107 /*   opcodes.                                                    */
108 /* ------------------------------------------------------------- */
109 
110 
111 /* All of the following are undefined again just after the table */
112 
113 #define n AGT_NUM
114 #define v AGT_VAR
115 #define r AGT_ROOM
116 #define i AGT_ITEM
117 
118 #define o (AGT_ITEM|AGT_CREAT)  /* "object" */
119 #define l (r|o|AGT_NONE|AGT_SELF|AGT_WORN)   /* "location" */
120 
121 
122 
123 /* opcode, argnum, arg1, arg2 */
124 #ifdef LOWMEM
125 #define a(s)  {"",0,0,0}
126 #define b(s,a1) {"",1,(a1),0}
127 #define c(s,a1,a2) {"",2,(a1),(a2)}
128 #else
129 #define a(s)  {#s,0,0,0}
130 #define b(s,a1) {#s,1,(a1),0}
131 #define c(s,a1,a2) {#s,2,(a1),(a2)}
132 #endif
133 
134 const opdef cond_def[] = {
135 	b(AtLocation, r), b(AtLocationGT, n), b(AtLocationLT, n),
136 	a(SongPlaying), a(SoundIsOn), a(DirectionOK), b(DirectionIs, AGT_DIR),
137 	c(BetweenRooms, n, n), b(HasVisitedRoom, r),
138 	a(EnteredObject), b(TimeGT, n), b(TimeLT, n),
139 	a(FirstVisitToRoom),
140 	a(NewLife),
141 	a(IsCarryingSomething), a(IsCarryingNothing),
142 	a(IsWearingSomething),
143 	b(IsCarryingTreasure, n),
144 	a(IsWearingNothing),
145 	b(LoadWeightEquals, n), b(LoadWeightGT, n), b(LoadWeightLT, n),
146 	b(Present, o), b(IsWearing, o), b(IsCarrying, o),
147 	b(IsNowhere, o), b(IsSomewhere, o),
148 	b(InRoom, o), c(IsLocated, o, l), c(Together, o, o),
149 	b(IsON, o), b(IsOFF, o),
150 	b(IsGroupMember, AGT_CREAT),
151 	b(IsOpen, o), b(IsClosed, o), b(IsLocked, o), b(IsUnLocked, o),
152 	b(IsEdible, o), b(IsDrinkable, o), b(IsPoisonous, o),
153 	b(IsMovable, o),
154 	a(NOUNPresent), a(NOUNIsWearing), a(NOUNIsCarrying),
155 	a(NOUNIsNowhere), a(NOUNIsSomewhere),
156 	a(NOUNInRoom), b(NOUNIsLocated, l),
157 	a(NOUNIsOn), a(NOUNIsOff),
158 	a(NOUNIsOpen), a(NOUNIsClosed), a(NOUNIsLocked), a(NOUNIsUnLocked),
159 	a(NOUNIsEdible), a(NOUNIsDrinkable), a(NOUNIsPoisonous),
160 	a(NOUNIsMovable),
161 	b(NOUNpointsEquals, n), b(NOUNpointsGT, n), b(NOUNpointsLT, n),
162 	b(NOUNweightEquals, n), b(NOUNweightGT, n), b(NOUNweightLT, n),
163 	a(LightPresent), a(RoomNeedsLight),
164 	b(FlagON, AGT_FLAG), b(FlagOFF, AGT_FLAG),
165 	b(RoomFlagOn, AGT_ROOMFLAG), b(Room_PixHere, AGT_PIX),
166 	b(RoomFlagOff, AGT_ROOMFLAG),
167 	b(ScoreEquals, n), b(ScoreGT, n), b(ScoreLT, n),
168 	b(NumberEquals, n), b(NumberGT, n), b(NumberLT, n),
169 	a(AnswerIsCorrect), a(AnswerIsWrong),
170 	b(TurnsEquals, n), b(TurnsGT, n), b(TurnsLT, n),
171 	c(CounterEquals, AGT_CNT, n), c(CounterGT, AGT_CNT, n), c(CounterLT, AGT_CNT, n),
172 	c(VariableEquals, v | n, n), c(VariableGT, v | n, n), c(VariableLT, v | n, n),
173 	c(CompareVariables, v, v), c(VariableChance, v | n, n),
174 	a(NamePresent), b(NameIsNumber, o | AGT_NONE),  /* QQ:Not sure about these */
175 	b(NOUNIsNumber, o | AGT_NONE), b(ObjectIsNumber, o | AGT_NONE),
176 	b(SomethingInside, r | o | AGT_SELF),
177 	b(Chance, n),
178 	a(PromptForYES), a(PromptForNO),
179 	a(VerbIsDirection),
180 	a(NOUNIsCreature),
181 	a(NOUNIsMan), a(NOUNIsWoman), a(NOUNIsThing),
182 	a(OBJECTIsMan), a(OBJECTIsWoman), a(OBJECTIsThing),
183 	a(ObjectIsCreature),
184 	a(ObjectPresent),
185 	a(NOT), a(OR),
186 	a(BeforeCommand), a(AfterCommand),    /* 110,111 */
187 	b(HourEquals, n), b(HourGT, n), b(HourLT, n),
188 	b(MinuteEq, n), b(MinuteGT, n), b(MinuteLT, n),
189 	a(IsAM),
190 
191 	a(OnDisambig),
192 	b(IsHostile, o), a(HostilePresent),
193 	a(NameWasPresent), a(OncePerTurn),
194 	c(IsClass, r | o, AGT_NONE | r | o),
195 	c(AttrOn, r | o, AGT_ATTR),
196 	a(NumericNOUN), a(NumericOBJECT),
197 	c(Equal, n, n), c(GT, n, n), c(LT, n, n), c(GE, n, n), c(LE, n, n),
198 	c(CaseCompareStrings, AGT_STR, AGT_STR), c(CaseStringBefore, AGT_STR, AGT_STR),
199 	c(CaseStringAfter, AGT_STR, AGT_STR),
200 	c(CompareStrings, AGT_STR, AGT_STR), c(StringBefore, AGT_STR, AGT_STR),
201 	c(StringAfter, AGT_STR, AGT_STR),
202 	c(StringIsAnswer, AGT_STR, AGT_QUEST),
203 	b(HasSeen, r | o),
204 	c(ObjFlagON, r | o, AGT_OBJFLAG),
205 	c(ObjFlagOFF, r | o, AGT_OBJFLAG),
206 	c(CanGo, r | o | AGT_SELF, AGT_DIR)
207 };
208 
209 
210 const opdef act_def[] = {
211 	b(GoToRoom, r), c(GoToRandomRoom, r, r),
212 	b(MakeVarRoomNum, v), b(MakeVarNounNum, v), b(MakeVarObjectNum, v),
213 	b(GoToVariableRoom, v | r), c(SendToVariableRoom, o, v | l),
214 	b(GetVariableIt, v | o), b(PrintVariableMessage, v | AGT_MSG),
215 	b(GetIt, o), b(WearIt, o), b(DropIt, o), b(RemoveIt, o),
216 	b(LoadFont, AGT_FONT), b(ShowPicture, AGT_PIC), c(ChangePicture, AGT_PIC, AGT_PIC),
217 	b(IfYShowPicture, AGT_PIC),
218 	b(ShowRoom_Pix, AGT_PIX), b(IfYShowRoom_Pix, AGT_PIX),
219 	b(PlaySong, AGT_SONG), c(PlayRandom, n, n), b(RepeatSong, AGT_SONG),
220 	a(EndRepeatSong), a(StopSong), a(SuspendSong), a(ResumeSong),
221 	b(ToggleMovable, i), c(ChangeDescr, r | o, AGT_MSG), c(ChangePoints, r | o, n),
222 	a(DestroyOBJECT), b(GetString, AGT_STR),
223 	b(GetVariable, v), b(SetVariableToTime, v), b(SetTimeToVariable, v | n),
224 	b(SetTime, n), b(AddToTime, n), b(SetDeltaTime, n),
225 	b(DoSubroutine, AGT_SUB), a(Return),
226 	a(GetNOUN), a(WearNOUN), a(DropNOUN), a(RemoveNOUN),
227 	a(DropEverything), a(RemoveEverything), a(KillPlayer),
228 	b(PutInCurrentRoom, o), c(SendToRoom, o, l),
229 	c(RePosition, o, l),
230 	a(PutNOUNInCurrentRoom), b(SendNOUNToRoom, l),
231 	b(SendAllToRoom, l), c(SendTreasuresToRoom, l, n),
232 	c(RelocateAll, l, l),
233 	b(Destroy, o), a(DestroyNOUN),
234 	c(SwapLocations, o, o), c(SendToItem, o, o), b(SendNOUNtoItem, o),
235 	b(AddToGroup, AGT_CREAT), b(RemoveFromGroup, AGT_CREAT), b(MoveTheGroup, l),
236 	a(RedirectTo),
237 	c(RandomMessage, AGT_MSG, AGT_MSG), b(ShowContents, r | o | AGT_SELF | AGT_WORN),
238 	b(OpenIt, i), b(CloseIt, i), b(LockIt, i), b(UnlockIt, i),
239 	a(OpenNOUN), a(CloseNOUN), a(LockNOUN), a(UnlockNOUN),
240 	a(ShowScore), b(PlusScore, n), b(MinusScore, n),
241 	a(ShowInventory), a(WaitForReturn), a(TimePasses),
242 	b(Delay, n),
243 	a(ClearScreen),
244 	b(DescribeThing, r | o), a(LookAtRoom),
245 	b(PrintMessage, AGT_MSG), a(BlankLine), c(Tone, n, n),
246 	c(GetNumberInput, n, n), b(AskQuestion, AGT_QUEST),
247 	c(ChangePassageway, AGT_DIR, AGT_EXIT),
248 	b(TurnFlagOn, AGT_FLAG), b(TurnFlagOff, AGT_FLAG), b(ToggleFlag, AGT_FLAG),
249 	b(TurnRoomFlagOn, AGT_ROOMFLAG), b(TurnRoomFlagOff, AGT_ROOMFLAG),
250 	b(ToggleRoomFlag, AGT_ROOMFLAG),
251 	b(TurnCounterOn, AGT_CNT), b(TurnCounterOff, AGT_CNT),
252 	c(SetVariableTo, v, n), c(AddToVariable, v | n, n), c(SubtractFromVariable, v | n, n),
253 	c(AddVariables, v, v), c(SubtractVariables, v, v),
254 	c(RandomVariable, v, n),
255 	b(NounToVariable, v), b(ObjectToVariable, v),
256 	b(Quote, AGT_MSG),
257 	b(TimePlus, n), b(TimeMinus, n), b(SetHour, n), b(SetMinute, n),
258 	b(TimePlusVariable, v | n), b(TimeMinusVariable, v | n),
259 	b(SetHourToVariable, v | n), b(SetMinutesToVariable, v | n),
260 
261 	b(SubtractFromTime, n), b(SetDisambigPriority, n),
262 	b(SetVariableToDeltaTime, v), b(ChangeStatus, n),
263 	c(MultiplyVariable, v | n, n), c(DivideVariable, v | n, n),
264 	c(ComputeRemainder, v | n, n),
265 	a(WaitForKey),
266 	b(SetHE, o), b(SetSHE, o), b(SetIT, o), b(SetTHEY, o),
267 	b(PrintMessageNoNL, AGT_MSG),
268 	b(StandardMessage, AGT_ERR),
269 	b(FailMessage, AGT_MSG), b(FailStdMessage, AGT_ERR),
270 	c(ErrMessage, n, AGT_MSG),  c(ErrStdMessage, n, AGT_ERR),
271 	a(AND),
272 	c(SetClass, r | o, AGT_NONE | r | o),
273 	c(SetVariableToClass, v, r | o),
274 	b(PushStack, n), b(PopStack, v),
275 	a(AddStack), a(SubStack), a(MultStack), a(DivStack), a(ModStack),
276 	a(DupStack), a(DiscardStack),
277 	b(SetVariableToInput, v),
278 	c(TurnAttrOn, r | o, AGT_ATTR), c(TurnAttrOff, r | o, AGT_ATTR),
279 	c(PushProp, r | o, AGT_PROP), c(PopProp, r | o, AGT_PROP),
280 	b(Goto, n), b(OnFailGoto, n),
281 	b(EndDisambig, n),
282 	b(XRedirect, n),
283 	c(CopyString, AGT_STR, AGT_STR),
284 	b(UpcaseString, AGT_STR), b(DowncaseString, AGT_STR),
285 	c(TurnObjFlagON, r | o, AGT_OBJFLAG), c(TurnObjFlagOFF, r | o, AGT_OBJFLAG),
286 	c(ToggleObjFlag, r | o, AGT_OBJFLAG),
287 	c(PushObjProp, r | o, AGT_OBJPROP),
288 	c(PopObjProp, r | o, AGT_OBJPROP),
289 	c(MoveInDirection, o | AGT_SELF, AGT_DIR)
290 };
291 
292 const opdef end_def[] = {
293 	a(WinGame), a(EndGame),
294 	a(QuitThisCMD), a(QuitAllCMDs), a(DoneWithTurn)
295 };
296 
297 const opdef illegal_def = a(ILLEGAL);
298 
299 #undef a
300 #undef b
301 #undef c
302 
303 #undef n
304 #undef v
305 #undef r
306 #undef i
307 #undef o
308 #undef l
309 
310 
311 
312 
313 /* ------------------------------------------------------------- */
314 /*  Opcode Translation Tables                                    */
315 /*    These convert opcode numbers from the various AGT versions */
316 /*    to a uniform coding.                                       */
317 /* ------------------------------------------------------------- */
318 
319 /*NOTE this is being changed so that rather than the second term
320   is an absolute offset of the first term. Still applies to ranges
321   up until next one. Also incorporates the +1000 correction
322   into the correction set itself. (to avoid further problems
323   when including more opcodes, e.g. AGT 1.83).
324    The last table entry is now marked by a new value of -1.*/
325 
326 /* Versions of the command set:
327 	v1.21 apparantly has a compatible command set w/ 1.7 (!)
328 	  [except that their maxcmd is apparantly 22, not 30]
329 	1.0 doesn't; it seems to have an EOC code of 154, as opposed to
330 	165 or so.
331 	1.18 seems to be slightly different from 1.7, but seemingly only
332 	   by one opcode.
333 	[And of course both ME and 1.8 have their own extended command sets]
334 */
335 
336 static const cmd_fix_rec FIX_ME[] =  /* No longer using this as baseline */
337 {	{0, 0},
338 	{110, 1000}, /* i.e. commands moved to start at opcode 1000 */
339 	{215, WIN_ACT},
340 	{220, -1}
341 };
342 
343 static const cmd_fix_rec FIX_ME0[] =
344 	/* 169 */
345 {	{0, 0},
346 	{110, 1000},
347 	{136, 1028}, /* Skip ToggleMoveable and ChangeDescr */
348 	{156, 1049}, /* Skip RePosition */
349 	{212, WIN_ACT},
350 	{217, -1}
351 };
352 
353 static const cmd_fix_rec FIX_ME0A[] = /* Pre-ME/1.0: */
354 	/* 169 */
355 {	{0, 0},
356 	{110, 1000},
357 	{130, 1021}, /* Skip PlayRandom */
358 	{135, 1028}, /* Skip ToggleMoveable and ChangeDescr */
359 	{155, 1049}, /* Skip RePosition */
360 	{211, WIN_ACT},
361 	{216, -1}
362 };
363 
364 static const cmd_fix_rec FIX_ME15[] = {
365 	{0, 0},
366 	{110, 1000}, /* i.e. commands moved to start at opcode 1000 */
367 	{158, 1049},  /* Skip the one opcode added in 1.56: RePosition */
368 	{214, WIN_ACT},
369 	{219, -1}
370 };
371 
372 static const cmd_fix_rec FIX_135[] = {
373 	{0, 0},
374 	{3, 12},
375 	{59, 71},
376 	{88, 106},
377 	{92, 1000},
378 	{105, 1039}, /* 149 */
379 	{114, 1049}, /* 159 */
380 	{157, 1095}, /* 205 */
381 	{167, WIN_ACT},
382 	{172, -1}
383 };
384 
385 static const cmd_fix_rec FIX_118[] = {
386 	{0, 0},
387 	{3, 12},
388 	{59, 71},
389 	{88, 106},
390 	{92, 1000},
391 	{105, 1039}, /* 149 */
392 	{114, 1049}, /* 159 */
393 	{118, 1054}, /* Skip SendTreasuresToRoom */
394 	{156, 1095}, /* 205 */
395 	{166, WIN_ACT},
396 	{171, -1}
397 };
398 
399 
400 static const cmd_fix_rec FIX_182[] = {
401 	{0, 0},
402 	{3, 12},
403 	{53, 110}, /* Shift BeforeCmd and AfterCmd */
404 	{55, 62},
405 	{61, 71},
406 	{90, 106},
407 	{94, 1000},
408 	{107, 1039}, /* 149 */
409 	{116, 1049}, /* 159 */
410 	{143, 1105},  /* QUOTE-- need to move somewhere else */
411 	{144, 1076},
412 	{160, 1095}, /* 205 */
413 	{170, WIN_ACT},
414 	{175, -1}
415 };
416 
417 
418 static const cmd_fix_rec FIX_183[] = {
419 	{0, 0},
420 	{3, 12},
421 	{55, 110}, /* Shift BeforeCmd and AfterCmd */
422 	{57, 64},
423 	{61, 71},
424 	{90, 106},
425 	{94, 112},  /* Time condition tokens */
426 	{101, 1000},
427 	{114, 1039},
428 	{123, 1049},
429 	{158, 1105},  /* QUOTE-- need to move somewhere else */
430 	{159, 1084},
431 	{167, 1095},
432 	{169, 1106},    /* Time Action Tokens */
433 	{177, 1097},
434 	{185, WIN_ACT},
435 	{190, -1}
436 };
437 
438 static const cmd_fix_rec FIX_10[] = /* This *seems* to work */
439 {	{0, 0},
440 	{3, 12},
441 	{59, 71},
442 	{80, 95},
443 	{84, 108},
444 	{86, 1000},
445 	{88, 1009},
446 	{92, 1039},
447 	{101, 1049},
448 	{105, 1054},
449 	{115, 1065},
450 	{142, 1095},
451 	{152, WIN_ACT},
452 	{157, -1}
453 };
454 
455 static const cmd_fix_rec FIX_15[] =   /* This works */
456 {	{0, 0},
457 	{3, 12},    /* Skip 3-11 */
458 	{60, 70},   /* Skip 69 */
459 	/* {61,72}, */   /* Skip 71 -- WRONG! */
460 	{90, 106},  /* Skip 101-105 */
461 	{94, 1000},
462 	{107, 1039}, /* skip 1013-1038 */
463 	{116, 1049}, /* Skip 1048  */
464 	{172, WIN_ACT},
465 	{177, -1}
466 };
467 
468 const fix_array FIX_LIST[] = /* An array of arrays, indexed by aver */
469 {
470 	FIX_135, /* Aver=0: unknown format, might as well assume Classic */
471 	FIX_10, FIX_118, FIX_135, FIX_135, FIX_135, FIX_182, FIX_183,
472 	FIX_15, FIX_15, FIX_15, FIX_ME0, FIX_ME0A, FIX_ME15, FIX_ME15, FIX_ME
473 };
474 
475 
476 /* ------------------------------------------------------------- */
477 /* Miscellaneous collections of strings                          */
478 /* ------------------------------------------------------------- */
479 
480 const char *verstr[] = {"????", "SMALL", "BIG", "MASTER", "SOGGY"};
481 const char *averstr[] = {"????", "1.0", "1.18",
482 						 "1.2", "1.32/COS", "Classic",
483 						 "1.82", "1.83",
484 						 "1.5/H", "1.5/F", "1.6",
485 						 "ME/1.0b", "ME/1.0a",
486 						 "ME/1.5", "ME/1.55", "ME/1.6",
487 						 "Magx"
488 						};
489 
490 const char *portstr = PORTSTR;
491 const char *version_str = "version 1.1.1";
492 
493 const char nonestr[5] = {4, 'n', 'o', 'n', 'e'};
494 static const char NONEstr[5] = {4, 'N', 'O', 'N', 'E'};
495 
496 
497 /* Names of exits */
498 const char *exitname[13] =
499 {"N", "S", "E", "W", "NE", "NW", "SE", "SW", "U", "D", "IN", "OUT", "SPC"};
500 
501 
502 
503 
504 /* ------------------------------------------------------------- */
505 /* Verblist is the  array of canonical forms of all the verbs    */
506 /* ------------------------------------------------------------- */
507 /* The following long string defines all the built in AGT verbs, in the
508    following format:
509   verb syn syn syn , prep prep ; next_verb ....
510   except that if a verb takes no objects at all, it should be period
511   terminated and if it is a metaverb it should be terminated by '!'. */
512 static const char verbdef[] =
513 	"north n. south s. east e. west w."
514 	"northeast ne. northwest nw. southeast se. southwest sw."
515 	"up u. down d."
516 	"enter in inside go&in go&into go&in&to get&in get&into get&in&to."
517 	"exit leave out go&out get&out get&out&of. special."
518 	"throw cast dump, at to in into across inside;"
519 	"open , with; close shut; lock, with; unlock, with;"
520 	"look l. examine x ex check inspect look&at look&in;"
521 	"change_locations change_location;"
522 	"read; eat; drink; score! attack kill fight hit, with;"
523 	"wait z. yell shout scream."
524 	"put place, in with inside into near behind over under on;"
525 	"quit q! tell talk talk&to talk&with, to about;"
526 	"inventory inv i. get take pick pick&up; ask, about for;"
527 	"turn, on off; push touch press, with; pull; play;"
528 	"list. show, to; drop;"
529 	"listexit listexits list_exits list&exits show&exits."
530 	"brief! verbose! save! restore!"
531 	"light; extinguish ext put&out; fire shoot, at with;"
532 	"help h. wear put&on; remove take&off;"
533 	"script script&on! unscript script&off! magic_word. view; after."
534 	"instructions ins!"   /* INSTRUCTIONS is "1.83 only" */
535 	/* The following are not defined in the original AGT */
536 	"again g. restart! oops; undo. notify!"
537 	"listexit_on listexit&on listexits&on!"
538 	"listexit_off listexit&off listexits&off!"
539 	"agildebug agtdebug! log! logoff log&off log&close! replay!"
540 	"replay_step replay&step! menu! replay_fast replay&fast."
541 	"sound sound_on sound&on! sound_off sound&off! introduction intro!"
542 	"dir_addr.";
543 
544 /* 1.83: Removes listexit; adds instructions after remove. */
545 
546 /* Then come the dummy verbs */
547 /* Dummy verb n ==> n-55     105,122
548 	Dummy_verb1...Dummy_Verb50        */
549 
550 /* Possible extension to verb definitons (not implemented):
551    If it _requires_ a prep, use : ?
552    If it takes a prep and no dobj, use | ?
553 */
554 
555 /* These are alternative (that is, non-canonical) forms of verbs that
556    were present in the oringal AGT interpreters.  They have the property
557    that they have no effect if used in a dummy_verb declaration. */
558 /* Their dictionary indices are stored in old_agt_verb, which is
559    initialized by reinit_dict. */
560 /* PICK, GO */
561 const char *const old_agt_verb_str[] = {
562 	"n", "s", "e", "w", "ne", "nw", "se", "sw", "u", "d", "in", "inside", "leave",
563 	"cast", "dump", "shut", "l", "ex", "inspect", "check", "kill", "fight", "hit",
564 	"shout", "scream", "place", "q", "talk", "i", "take", "touch", "ext",
565 	"shoot", "h", "ins", nullptr
566 };
567 
568 
569 
570 /* ------------------------------------------------------------------- */
571 /* Dictionary primitives: the basic functions for manipulating the     */
572 /* dictionary data structures.                                         */
573 /* ------------------------------------------------------------------- */
574 #define HASHSIZE (1<<HASHBITS)
575 #define HASHMASK (HASHSIZE-1)
576 
577 #ifdef DOHASH
578 static word DOSFARDATA hash[HASHSIZE];
579 #endif
580 
hashfunc(const char * s)581 static int hashfunc(const char *s) {
582 	unsigned long n, i;
583 
584 	n = 0;
585 	for (; *s != 0; s++) {
586 		n += (n << 2) + (uchar) * s;
587 		i = n & ~HASHMASK;
588 		if (i)
589 			n = (n ^ (i >> HASHBITS))&HASHMASK;
590 	}
591 	return (n & HASHMASK);
592 }
593 
search0_dict(const char * s)594 static word search0_dict(const char *s) {
595 	int i;
596 
597 #ifdef DOHASH
598 	for (i = hashfunc(s);
599 	        hash[i] != -1 && strcmp(s, dict[hash[i]]) != 0;
600 	        i = (i + 1)&HASHMASK);
601 	return hash[i];
602 #else
603 	for (i = 0; strcmp(s, dict[i]) != 0 && i < dp; i++);
604 	if (i < dp) return i;
605 	return -1;
606 #endif
607 }
608 
search_dict(const char * s)609 word search_dict(const char *s)
610 /* This does a case-insensitive search */
611 {
612 	word w;
613 	char *t, *p;
614 
615 	t = rstrdup(s);
616 	for (p = t; *p; p++) *p = tolower(*p);
617 	w = search0_dict(t);
618 	rfree(t);
619 	return w;
620 }
621 
622 /* The basic routine to add s to the dictionary; this does no preprocessing
623   of s; use add_dict for that */
add0_dict(const char * s)624 static word add0_dict(const char *s) {
625 	int i;
626 	long newptr;
627 	char *newstr;
628 
629 	i = search0_dict(s);
630 	if (i != -1) return i;
631 	/* Okay, it's not in the dictionary; need to add it. */
632 	/*  rprintf("Adding %s\n",s);*/
633 
634 	dict = (char **)rrealloc(dict, sizeof(char *) * (dp + 1));
635 	newptr = dictstrptr + strlen(s) + 1;
636 	if (newptr > dictstrsize) { /* Enlarge dictstr */
637 		if (dictstrsize == 0) dictstrsize = DICT_INIT;
638 		while (newptr > dictstrsize)
639 			dictstrsize += DICT_GRAN;
640 		newstr = (char *)rrealloc(dictstr, dictstrsize);
641 		/* Now need to update all of our pointers */
642 		for (i = 0; i < dp; i++)
643 			dict[i] = (dict[i] - dictstr) + newstr;
644 		dictstr = newstr;
645 	}
646 	strcpy(dictstr + dictstrptr, s); /* Copy word into memory */
647 	dict[dp] = dictstr + dictstrptr;
648 	dictstrptr = newptr;
649 
650 #ifdef DOHASH  /* Need to update the hash table */
651 	if (dp > HASHSIZE) fatal("Hash table overflow");
652 	for (i = hashfunc(s); hash[i] != -1; i = (i + 1)&HASHMASK);
653 	hash[i] = dp;
654 #endif
655 	return dp++;
656 }
657 
658 #ifdef DOHASH
659 
init_hash(void)660 static void init_hash(void) {
661 	int i;
662 
663 	for (i = 0; i < HASHSIZE; i++) hash[i] = -1;
664 }
665 
666 
667 /* This routine rebuilds the hash table from the dictionary. */
668 /* It's used by the AGX reading routines, since they save */
669 /* the dictionary but not the hash table */
rebuild_hash(void)670 static void rebuild_hash(void) {
671 	int i, j;
672 
673 	if (dp > HASHSIZE) fatal("Hash table overflow");
674 	init_hash();
675 
676 	for (i = 0; i < dp; i++) {
677 		for (j = hashfunc(dict[i]); hash[j] != -1; j = (j + 1)&HASHMASK);
678 		hash[j] = i;
679 	}
680 }
681 #endif
682 
683 
init0_dict(void)684 static void init0_dict(void)
685 /* This sets up the basic data structures associated with the dictionary */
686 /* (It's called by init_dict, which also adds the basic verbs) */
687 {
688 #ifdef DOHASH
689 	init_hash();
690 	hash[hashfunc("any")] = 0;
691 #endif
692 
693 	dict = (char **)rmalloc(sizeof(char *));
694 	dictstr = (char *)rmalloc(DICT_GRAN);
695 	strcpy(dictstr, "any");
696 	dict[0] = dictstr;
697 
698 	dictstrptr = 4; /* Point just after 'any' */
699 	dictstrsize = DICT_GRAN;
700 	dp = 1;
701 	syntbl = NULL;
702 	synptr = 0;
703 	syntbl_size = 0; /* Clear synonym table */
704 }
705 
706 
707 
708 
709 /* ------------------------------------------------------------------- */
710 /* Higher level dictionary routines: Things that load initial vocab,   */
711 /* and massage strings into the correct form for the dictionary        */
712 /* ------------------------------------------------------------------- */
713 
714 static rbool no_syn;
715 
716 /* This splits dict[w] into space-separated pieces and adds them to
717    the dictionary and to a growing synonym list, which it marks the end of.
718    It returns a pointer to the beginning of this list.
719    If there are no spaces, it doesn't do anything and returns 0. */
add_multi_word(word w)720 slist add_multi_word(word w) {
721 	slist start_list;
722 	rbool end_found;
723 	char *curr;
724 	char *s, *t;
725 
726 	for (s = dict[w]; *s != 0 && *s != ' '; s++);
727 	if (*s != ' ') return 0;
728 
729 	start_list = synptr;
730 	curr = t = rstrdup(dict[w]);
731 	s = t + (s - dict[w]);
732 
733 	addsyn(w); /* First entry is the 'word' to condense to */
734 	while (1) {
735 		end_found = (*s == 0);
736 		*s = 0;
737 		addsyn(add0_dict(curr)); /* Add to comb list */
738 		if (end_found) break;
739 		curr = ++s;
740 		while (*s != 0 && *s != ' ') s++;
741 	}
742 	addsyn(-1); /* Mark the end of the list */
743 	rfree(t);
744 	return start_list;
745 }
746 
747 
748 /* Check verb vp for multiwords and enter any found in the auxilary
749    combination list */
verb_multiword(int vp)750 static void verb_multiword(int vp) {
751 	int i;
752 	slist ptr;
753 
754 	if (no_syn) return;
755 	for (i = auxsyn[vp]; syntbl[i] != 0; i++) {
756 		ptr = add_multi_word(syntbl[i]);
757 		if (ptr != 0) {
758 			num_auxcomb += 1;
759 			auxcomb = (slist *)rrealloc(auxcomb, num_auxcomb * sizeof(slist));
760 			auxcomb[num_auxcomb - 1] = ptr;
761 		}
762 	}
763 }
764 
765 
enter_verbs(int vp,const char * s)766 static void enter_verbs(int vp, const char *s)
767 /* Read definition string s, starting to make entries at verb # vp */
768 /* WARNING: This doesn't do any sort of checking; it assumes the input
769    string is correctly formed. */
770 {
771 	const char *p; /* Points along string. */
772 	words curr;  /* word currently being read. */
773 	int n; /* length of curr */
774 	rbool have_multiword;
775 
776 	n = 0;
777 	have_multiword = 0;
778 	auxsyn[vp] = synptr;
779 	for (p = s; *p != 0; p++)
780 		if (*p == ';' || *p == ',' || *p == '.' || *p == '!' || isspace(*p)) {
781 			if (n > 0) { /* word just ended: need to add it to dictionary etc */
782 				curr[n] = 0;
783 				n = 0;
784 				addsyn(add0_dict(curr)); /* Add to syn list or prep list, depending */
785 			}
786 			if (!isspace(*p))
787 				addsyn(-1); /* Mark the end of the list */
788 			if (*p == ';' || *p == '.' || *p == '!') {
789 				if (*p == ';') verbflag[vp] |= VERB_TAKEOBJ;
790 				if (*p == '!') verbflag[vp] |= VERB_META;
791 				if (have_multiword)
792 					verb_multiword(vp);
793 				have_multiword = 0;
794 				vp++;
795 				if (vp >= TOTAL_VERB) break;
796 				auxsyn[vp] = synptr; /* The following words will be the syn list */
797 			} else if (*p == ',')
798 				preplist[vp] = synptr; /* The following words will be the prep list */
799 		} else if (*p == '&') {
800 			curr[n++] = ' ';
801 			have_multiword = 1;
802 		} else curr[n++] = *p;
803 }
804 
805 
806 
807 
init_dict(void)808 void init_dict(void) {
809 	dict = NULL;
810 	verblist = NULL;
811 	syntbl = NULL;
812 	no_syn = 0;
813 	auxsyn = NULL;
814 	preplist = NULL;
815 	verbflag = NULL;
816 	auxcomb = NULL;
817 	old_agt_verb = NULL;
818 	num_auxcomb = 0;
819 }
820 
821 /* This is called by agttest.c */
build_verblist(void)822 void build_verblist(void) {
823 	int i;
824 
825 	verblist = (words *)rmalloc(sizeof(words) * TOTAL_VERB);
826 	for (i = 0; i < TOTAL_VERB; i++)
827 		strncpy(verblist[i], dict[syntbl[auxsyn[i]]], sizeof(words));
828 #ifdef DUMP_VLIST
829 	{
830 		int j;
831 		rprintf("VERB LIST:\n");
832 		for (i = 0; i < TOTAL_VERB; i++) {
833 			rprintf("%2d %s:", i, verblist[i]);
834 			for (j = auxsyn[i]; syntbl[j] != 0; j++)
835 				rprintf(" %s", dict[syntbl[auxsyn[i]]]);
836 			rprintf(" ==> ");
837 			for (j = preplist[i]; syntbl[j] != 0; j++)
838 				rprintf(" %s", dict[ syntbl[preplist[i]]]);
839 			writeln("");
840 		}
841 	}
842 #endif
843 }
844 
845 
846 
set_verbflag(void)847 void set_verbflag(void) {
848 	verbflag[14] |= VERB_MULTI; /* throw */
849 	verbflag[29] |= VERB_MULTI; /* put */
850 	verbflag[33] |= VERB_MULTI; /* get */
851 	verbflag[41] |= VERB_MULTI; /* drop */
852 	verbflag[51] |= VERB_MULTI; /* wear */
853 	verbflag[52] |= VERB_MULTI; /* remove */
854 }
855 
856 
reinit_dict(void)857 void reinit_dict(void)
858 /* reinit_dict initializes verblist and sets up aux_syn as well
859    as loading the initial vocabulary into the dictionary. */
860 {
861 	char buff[16]; /* Needs to be big enough to hold dummy_verbNNN\0
862 			or subroutineNNN\0 */
863 	int i;
864 
865 	no_syn = no_auxsyn;
866 
867 	auxsyn = (slist *)rmalloc(sizeof(slist) * TOTAL_VERB);
868 	auxcomb = NULL;
869 	num_auxcomb = 0;
870 	preplist = (slist *)rmalloc(sizeof(slist) * TOTAL_VERB);
871 	verbflag = (uchar *)rmalloc(sizeof(uchar) * TOTAL_VERB);
872 
873 	if (!agx_file)
874 		init0_dict();
875 #ifdef DOHASH
876 	else
877 		rebuild_hash();
878 #endif
879 
880 	for (i = 0; i < TOTAL_VERB; i++)
881 		verbflag[i] = 0;
882 
883 	auxsyn[0] = synptr;
884 	addsyn(-1);
885 
886 	enter_verbs(1, verbdef);
887 	set_verbflag(); /* Do additional verbflag initialization */
888 
889 	for (i = 0; i < DVERB; i++) {
890 		sprintf(buff, "dummy_verb%d", i + 1);
891 		auxsyn[i + BASE_VERB] = synptr;
892 		addsyn(add0_dict(buff));
893 		addsyn(-1);
894 	}
895 	for (i = 0; i < MAX_SUB; i++) {
896 		sprintf(buff, "subroutine%d", i + 1);
897 		auxsyn[i + BASE_VERB + DVERB] = synptr;
898 		addsyn(sub_name[i] = add0_dict(buff));
899 		addsyn(-1);
900 	}
901 	no_syn = 0; /* Return to usual state */
902 	verblist = NULL;
903 
904 	/* Now initialize old_agt_verb array */
905 	for (i = 0; old_agt_verb_str[i] != NULL; i++);
906 	rfree(old_agt_verb);
907 	old_agt_verb = (word *)rmalloc(sizeof(word) * (i + 1));
908 	for (i = 0; old_agt_verb_str[i] != NULL; i++) {
909 		old_agt_verb[i] = search_dict(old_agt_verb_str[i]);
910 		assert(old_agt_verb[i] != -1);
911 	}
912 	old_agt_verb[i] = -1; /* Mark end of list */
913 }
914 
915 
916 
917 
free_dict(void)918 void free_dict(void) {
919 	rfree(dict);
920 	rfree(verblist);
921 	rfree(syntbl);
922 	rfree(auxsyn);
923 	rfree(preplist);
924 	rfree(verbflag);
925 }
926 
add_dict(const char * str)927 word add_dict(const char *str) {
928 	int i, j;
929 	char s[50];
930 
931 	strncpy(s, str, 48);
932 	for (i = 0; s[i] != 0 && rspace(s[i]); i++);
933 	if (s[i] == 0) return 0; /* If it's all whitespace, ignore. */
934 	/* i now points at first non-whitespace character */
935 	/* Eliminate leading whitespace and lowercase the string. */
936 	for (j = 0; s[j + i] != 0; j++) s[j] = tolower(s[j + i]);
937 	s[j] = 0;
938 	/* Now eliminate trailing whitespace (j points to end of string) */
939 	for (j--; rspace(s[j]) && j > 0; j--);
940 	s[j + 1] = 0;
941 	/* Okay, now make sure it isn't 'none' */
942 	if (strcmp(s, "none") == 0) return 0;
943 	/* Finally, add it to the dictionary if it isn't already there */
944 	return add0_dict(s);
945 }
946 
947 /* Adds w to dynamically grown synonym list */
948 /* If no_syn is set, then *don't* add a synonym: return immediatly */
949 /*  (This is done by agt2agx to avoid creating the auxsyn lists,  */
950 /*   since those should be created when the interpreter loads the */
951 /*   game file and not before) */
addsyn(word w)952 void addsyn(word w) {
953 	if (no_syn) return;
954 	if (w == 0) return;
955 	if (w == -1) w = 0;
956 	if (synptr >= syntbl_size) {
957 		syntbl_size += SYN_GRAIN;
958 		if (syntbl_size > 0x7FFF)
959 			fatal("Too many synonyms.");
960 		syntbl = (word *)rrealloc(syntbl, ((long)syntbl_size) * sizeof(word));
961 	}
962 	syntbl[synptr++] = w;
963 }
964 
965 
966 /* Returns the given dictionary word with some checking for -1 */
gdict(word w)967 const char *gdict(word w) {
968 	assert(w >= -1 && w < dp);
969 	if (w == -1) return "___"; /* NONE */
970 	return dict[w];
971 }
972 
973 
974 
975 
976 
977 /* ------------------------------------------------------------------- */
978 /* General utilities linking objects to their names                   */
979 /* ------------------------------------------------------------------- */
980 
981 /* Search auxsyn for verb: that is, check built in synonyms */
verb_builtin(word w)982 int verb_builtin(word w) {
983 	int i, j;
984 
985 	for (i = 1; i < TOTAL_VERB; i++)
986 		for (j = auxsyn[i]; syntbl[j] != 0; j++)
987 			if (syntbl[j] == w) return i;
988 
989 	/* Failed to find a match */
990 	return 0;
991 }
992 
verb_authorsyn(word w)993 int verb_authorsyn(word w) {
994 	int i, j;
995 
996 	/* Check game-specific synonyms first */
997 	/* Scan in reverse so later synonyms will override earlier ones */
998 	for (i = TOTAL_VERB - 1; i > 0; i--)
999 		for (j = synlist[i]; syntbl[j] != 0; j++)
1000 			if (w == syntbl[j]) return i;
1001 	return 0;
1002 }
1003 
1004 
verb_code(word w)1005 int verb_code(word w)
1006 /* Given a word w, searches auxsyn and returns the verb id */
1007 {
1008 	int canon, tmp;
1009 
1010 	/* Expand author-defined synonyms */
1011 	tmp = verb_authorsyn(w);
1012 	if (tmp != 0) return tmp;
1013 
1014 	/* Expand built-in synonyms */
1015 	canon = verb_builtin(w);
1016 	if (canon != 0) {
1017 		/* Allow built-in verbs to be overridden */
1018 		tmp = verb_authorsyn(syntbl[auxsyn[canon]]);
1019 		if (tmp != 0) return tmp;
1020 	}
1021 
1022 	return canon; /* No new synonyms; return canonical match if it exists */
1023 }
1024 
1025 
1026 /* This is a faster version of the above  for use in the special case of
1027    command headers where the verb word is much more restricted; it should
1028    be the first auxsyn entry and it should never by a synlist entry. */
cmdverb_code(word w)1029 static int cmdverb_code(word w) {
1030 	int i, j;
1031 
1032 	for (i = 0; i < TOTAL_VERB; i++)
1033 		if (syntbl[auxsyn[i]] == w) return i;
1034 	/* Hmm... that failed. Search the rest of the auxsyns in case the
1035 	   order of auxsyns has changed or something */
1036 	agtwarn("Header verb not in canonical form.", 1);
1037 	for (i = 1; i < TOTAL_VERB; i++)
1038 		for (j = auxsyn[i]; syntbl[j] != 0; j++)
1039 			if (syntbl[j] == w) return i;
1040 	agtwarn("Header verb not in internal list.", 1);
1041 	return verb_code(w);
1042 }
1043 
objname(int i)1044 char *objname(int i) { /* returns malloc'd name string of object i */
1045 	char *s;
1046 
1047 	if (i < 0)
1048 		return rstrdup(dict[-i]);
1049 	if (i == 0)
1050 		return rstrdup("....");
1051 	if (i == 1) return rstrdup("*Self*");
1052 	if (i == 1000) return rstrdup("*Worn*");
1053 	if (i >= first_room && i <= maxroom)
1054 		return rstrdup(room[i - first_room].name);
1055 	if ((i >= first_noun && i <= maxnoun) || (i >= first_creat && i <= maxcreat)) {
1056 		word adjw, nounw;
1057 		if (i >= first_noun && i <= maxnoun) {
1058 			adjw = noun[i - first_noun].adj;
1059 			nounw = noun[i - first_noun].name;
1060 		} else {
1061 			adjw = creature[i - first_creat].adj;
1062 			nounw = creature[i - first_creat].name;
1063 		}
1064 		if (adjw == 0 || !strcmp(dict[adjw], "no_adjective"))
1065 			return rstrdup(dict[nounw]);
1066 		return concdup(dict[adjw], dict[nounw]);
1067 	}
1068 	/* At this point we can't get a name: return ILLn. */
1069 	s = (char *)rmalloc(3 + 1 + (5 * sizeof(int)) / 2 + 1);
1070 	/* Make sure we have enough space in case i is big */
1071 	sprintf(s, "ILL%d", i);
1072 	return s;
1073 }
1074 
1075 
1076 
1077 /* ------------------------------------------------------------------- */
1078 /*  Routines to sort the command array and construct verbptr           */
1079 /* ------------------------------------------------------------------- */
1080 
1081 #define SORT_META
1082 
1083 #ifdef SORT_META
1084 
1085 #define ch1 ((const cmd_rec*)cmd1)
1086 #define ch2 ((const cmd_rec*)cmd2)
1087 
1088 /* See notes below before trying to decipher this routine;
1089    during the sort, many of the fields are being used for nonstandard
1090    purposes */
1091 
1092 
1093 
1094 #define s_verb(cmd) ( (cmd)->actor<0 ? (cmd)->data[0] : (cmd)->verbcmd)
1095 
cmp_cmd(const void * cmd1,const void * cmd2)1096 static int cmp_cmd(const void *cmd1, const void *cmd2) {
1097 	word v1, v2;
1098 
1099 	/* We are sorting on command[].verbcmd, but if one of the headers
1100 	   is really the object of a redirect command then we need to use
1101 	   its parent's verbcmd */
1102 	/* For commands with actors, we need to avoid sorting them at all. */
1103 	v1 = s_verb(ch1);
1104 	v2 = s_verb(ch2);
1105 
1106 	if (v1 < v2) return -1;
1107 	if (v1 > v2) return +1;
1108 
1109 	/* v1==v2, so leave them in the same order as before */
1110 	/* We have to take absolute values here because we are using negatives
1111 	   to indicate redirection objects */
1112 	if (ABS(ch1->actor) < ABS(ch2->actor))
1113 		return -1;
1114 	else if (ABS(ch1->actor) == ABS(ch2->actor))
1115 		return 0;
1116 	else return 1;
1117 	/* Equality should be impossible */
1118 }
1119 
1120 #undef ch1
1121 #undef ch2
1122 
1123 /* This sets things up for qsort */
1124 /* We need a sort that is
1125    i) Stable and
1126    ii) Keeps "redirection headers" attached to the correct command */
1127 /* We steal the field actor for this purpose */
1128 /*   actor will equal the index of the header in the original list. */
1129 /*   (or negative the header if the command is a redirection) */
1130 /* For redirected commands, we steal the data pointer since it shouldn't
1131 	be being used anyhow. */
1132 /* In a field pointed to by data we store the verb word */
1133 /* NOTE: this routine requires that the data type of *data (namely
1134   integer) is big enough to hold a value of type word. */
1135 
rsort(void)1136 static void rsort(void) {
1137 	long i;
1138 	integer *save_actor;
1139 	word *save_verb;
1140 
1141 	save_actor = (integer *)rmalloc(last_cmd * sizeof(integer));
1142 	save_verb = (word *)rmalloc(last_cmd * sizeof(word));
1143 
1144 	/* The following loop does three things:
1145 	   i) Copies command[].actor to save_actor[]
1146 	   ii) Sets command[].actor to the commands index in the array
1147 	   iii) For actor commands, sets the verb to .... after saving it
1148 	          in save_verb.
1149 	   iv) For redirection commands, stores the verb of the owning
1150 	     header in a block pointed to by data */
1151 
1152 	for (i = 0; i < last_cmd; i++) { /* Copy actor to save_actor */
1153 		save_verb[i] = command[i].verbcmd;
1154 		if (command[i].actor > 1) /* i.e. there _is_ an actor */
1155 			command[i].verbcmd = syntbl[auxsyn[DIR_ADDR_CODE]];
1156 		save_actor[i] = command[i].actor;
1157 		command[i].actor = i;
1158 		if (save_actor[i] < 0) { /* Redirected command */
1159 			int j;
1160 
1161 			command[i].actor = -i;
1162 			rfree(command[i].data); /* data should be NULL, anyhow */
1163 			command[i].data = (integer *)rmalloc(sizeof(integer));
1164 			for (j = i; j > 0 && save_actor[j] < 0; j--);
1165 			if (save_actor[j] > 0)
1166 				command[i].data[0] = command[j].verbcmd;
1167 			else {
1168 				command[i].data[0] = 0;
1169 				agtwarn("First command header is REDIRECT object!", 0);
1170 			}
1171 		}
1172 	}
1173 
1174 	/* Now do the sort... */
1175 	qsort(command, last_cmd, sizeof(cmd_rec), cmp_cmd);
1176 
1177 #if 0  /* This is code to test the integrity of the sort */
1178 	for (i = 0; i < last_command; i++)
1179 		if (command[i].actor < 0)
1180 			assert(i == 0 || command[i].data[0] == command[i - 1].verbcmd);
1181 #endif
1182 
1183 	/* Finally, restore everything to normal */
1184 	for (i = 0; i < last_cmd; i++) { /* Restore actor */
1185 		command[i].verbcmd = save_verb[ABS(command[i].actor)];
1186 		command[i].actor = save_actor[ABS(command[i].actor)];
1187 		if (command[i].actor < 0) {
1188 			rfree(command[i].data);  /* Sets it to NULL automatically */
1189 			command[i].cmdsize = 0;
1190 		}
1191 	}
1192 	rfree(save_actor);
1193 	rfree(save_verb);
1194 }
1195 
1196 #endif
1197 
sort_cmd(void)1198 void sort_cmd(void) {
1199 	int i;
1200 	word curr_vb;
1201 	word all_word, global_word;
1202 
1203 	verbptr = (short *)rmalloc(sizeof(short) * TOTAL_VERB);
1204 	verbend = (short *)rmalloc(sizeof(short) * TOTAL_VERB);
1205 
1206 	if (mars_fix) {  /* Don't bother if mars scanning is active */
1207 		for (i = 0; i < TOTAL_VERB; i++) {
1208 			verbptr[i] = 0;       /* That is, scan the whole space for all verbs */
1209 			verbend[i] = last_cmd;
1210 		}
1211 		return;
1212 	}
1213 
1214 #ifdef SORT_META
1215 	if (!agx_file && aver >= AGX00) rsort();
1216 #endif
1217 
1218 
1219 	if (no_auxsyn) return; /* Used by agt2agx */
1220 
1221 	for (i = 0; i < TOTAL_VERB; i++) {
1222 		verbptr[i] = last_cmd;
1223 		verbend[i] = 0;
1224 	}
1225 
1226 	all_word = search_dict("all");
1227 	if (all_word == 0) all_word = -1; /* This means none of the metacommands
1228 				   used ALL, so prevent ANY matches */
1229 	global_word = search_dict("global_scope");
1230 	if (global_word == 0) global_word = -1; /* Ditto */
1231 
1232 
1233 	for (i = 0; i < last_cmd; i++) {
1234 		if (command[i].actor < 0) continue; /* Redirection */
1235 		if (command[i].nouncmd == all_word)
1236 			/* Detect multinoun accepting verbs by ALL */
1237 			verbflag[cmdverb_code(command[i].verbcmd)] |= VERB_MULTI;
1238 		if (command[i].actor > 1)
1239 			curr_vb = DIR_ADDR_CODE;
1240 		else
1241 			curr_vb = cmdverb_code(command[i].verbcmd);
1242 		if (i < verbptr[curr_vb]) verbptr[curr_vb] = i;
1243 		if (i > verbend[curr_vb]) verbend[curr_vb] = i;
1244 	}
1245 
1246 	for (i = 0; i < TOTAL_VERB; i++)
1247 		if (verbptr[i] == last_cmd) /* No occurences of this verb */
1248 			verbend[i] = last_cmd;
1249 		else verbend[i]++; /* Point *after* last occurance */
1250 
1251 	for (i = 0; i < TOTAL_VERB; i++) {
1252 		int j;
1253 
1254 		j = synlist[i];
1255 		if (syntbl[j] == 0) continue;
1256 		while (syntbl[j] != 0) j++;
1257 		j--;
1258 		if (syntbl[j] == global_word) { /* Ends with global_scope */
1259 			verbflag[i] |= VERB_GLOBAL;
1260 			syntbl[j] = 0;
1261 		}
1262 	}
1263 }
1264 
1265 
1266 
1267 
1268 /* ------------------------------------------------------------------- */
1269 /*  Functions for getting opcode information                           */
1270 /* ------------------------------------------------------------------- */
1271 
1272 
1273 /* Returns the opdef structure associated with an opcode */
get_opdef(integer op)1274 const opdef *get_opdef(integer op) {
1275 	op = op % 2048; /* Strip operand information */
1276 	if (op < 0 || (op > MAX_COND && op < START_ACT) || (op > PREWIN_ACT && op < WIN_ACT)
1277 	        || (op > MAX_ACT)) {
1278 		return &illegal_def;
1279 	}
1280 	if (op >= 2000)
1281 		return &end_def[op - 2000];
1282 	if (op >= 1000)
1283 		return &act_def[op - 1000];
1284 	return &cond_def[op];
1285 }
1286 
1287 
1288 
1289 /* ------------------------------------------------------------------- */
1290 /*  Functions for processing strings                                   */
1291 /* ------------------------------------------------------------------- */
1292 
new_str(char * buff,int max_leng,rbool pasc)1293 long new_str(char *buff, int max_leng, rbool pasc)
1294 /* Stores the (up to leng) characters of a string
1295   into our master string space (enlarging it if neccessary)
1296   and returns the offset into the array.
1297   pasc=1 ==> pascal-style string
1298   pasc=0 ==> C-style string; ignore max_leng and NONE strings
1299   */
1300 {
1301 	int leng, i;
1302 	long p;
1303 
1304 	if (pasc) {
1305 		leng = buff[0];
1306 		if (leng > max_leng) leng = max_leng;
1307 	} else
1308 		leng = strlen(buff);
1309 
1310 	if (ss_size < ss_end + leng + 1) {
1311 		while (ss_size < ss_end + leng + 1) ss_size += SS_GRAIN;
1312 		static_str = (char *)rrealloc(static_str, sizeof(char) * ss_size);
1313 	}
1314 
1315 	if (pasc)
1316 		if (memcmp(buff, nonestr, 5) == 0 || memcmp(buff, NONEstr, 5) == 0) {
1317 			/* "none" --> empty string */
1318 			if (ss_end != 0) return (ss_end - 1); /* Points to last \0 */
1319 			else { /* Very first string */
1320 				static_str[0] = 0;
1321 				ss_end = 1;
1322 				return 0;
1323 			}
1324 		}
1325 
1326 	p = ss_end; /* Remember begining of string */
1327 	for (i = 0; i < leng;)
1328 		static_str[ss_end++] = fixchar[(uchar)buff[pasc + (i++)]];
1329 	static_str[ss_end++] = 0;
1330 
1331 	return p;
1332 }
1333 
1334 
1335 
1336 /* ------------------------------------------------------------------- */
1337 /* Functions for reading in descriptions                               */
1338 /* ------------------------------------------------------------------- */
1339 
1340 
read_descr(long start,long size)1341 descr_line *read_descr(long start, long size) {
1342 	if (agx_file)
1343 		return agx_read_descr(start, size);
1344 	else
1345 		return agt_read_descr(start, size);
1346 }
1347 
free_descr(descr_line * txt)1348 void free_descr(descr_line *txt) {
1349 	if (txt == NULL) return;
1350 	if (mem_descr == NULL)
1351 		rfree(txt[0]);  /* First free the string block containing the text...*/
1352 	rfree(txt);    /* ... then the array of pointers to it */
1353 }
1354 
1355 
1356 
1357 /* ------------------------------------------------------------------- */
1358 /*  ObjFlag and ObjProp routines                                       */
1359 /* ------------------------------------------------------------------- */
1360 
objextsize(char op)1361 long objextsize(char op) {
1362 
1363 	/* op=0 for flags, =1 for props */
1364 	if (op == 0)
1365 		return num_rflags * rangefix(maxroom - first_room + 1)
1366 		       + num_nflags * rangefix(maxnoun - first_noun + 1)
1367 		       + num_cflags * rangefix(maxcreat - first_creat + 1);
1368 	else
1369 		return num_rprops * rangefix(maxroom - first_room + 1)
1370 		       + num_nprops * rangefix(maxnoun - first_noun + 1)
1371 		       + num_cprops * rangefix(maxcreat - first_creat + 1);
1372 }
1373 
lookup_objflag(int id,int t,char * ofs)1374 long lookup_objflag(int id, int t, char *ofs) {
1375 	if (id < 0 || id >= oflag_cnt) return -1;
1376 	switch (t) {
1377 	case 0:
1378 		*ofs = attrtable[id].rbit;
1379 		return attrtable[id].r;
1380 	case 1:
1381 		*ofs = attrtable[id].nbit;
1382 		return attrtable[id].n;
1383 	case 2:
1384 		*ofs = attrtable[id].cbit;
1385 		return attrtable[id].c;
1386 	default:
1387 		rprintf("INT ERROR: Invalid object type.\n");
1388 		return -1;
1389 	}
1390 }
1391 
lookup_objprop(int id,int t)1392 long lookup_objprop(int id, int t) {
1393 	if (id < 0 || id >= oprop_cnt) return -1;
1394 	switch (t) {
1395 	case 0:
1396 		return proptable[id].r;
1397 	case 1:
1398 		return proptable[id].n;
1399 	case 2:
1400 		return proptable[id].c;
1401 	default:
1402 		rprintf("INT ERROR: Invalid object type.\n");
1403 		return -1;
1404 	}
1405 }
1406 
num_oattrs(int t,rbool isflag)1407 int num_oattrs(int t, rbool isflag) {
1408 	switch (t) {
1409 	case 0:
1410 		return isflag ? num_rflags : num_rprops;
1411 	case 1:
1412 		return isflag ? num_nflags : num_nprops;
1413 	case 2:
1414 		return isflag ? num_cflags : num_cprops;
1415 	default:
1416 		rprintf("INT ERROR: Invalid object type.\n");
1417 		return 0;
1418 	}
1419 }
1420 
op_simpflag(uchar * pf,char ofs,int op)1421 rbool op_simpflag(uchar *pf, char ofs, int op)
1422 /* op: 0=clear, 1=set, 2=nop, 3=toggle    two bits: <ab> */
1423 {
1424 	unsigned char mask, amask, bmask;
1425 
1426 	mask = 1 << ofs;
1427 	amask = ~mask | ((op >> 1) << ofs);
1428 	bmask = (op & 1) << ofs;
1429 
1430 	*pf = (*pf & amask)^bmask;
1431 
1432 	return (*pf & mask) != 0;
1433 }
1434 
calcindex(integer obj,integer objbase,int ocnt,int base)1435 static long calcindex(integer obj, integer objbase, int ocnt, int base) {
1436 	int rval;
1437 
1438 	if (base == -1) rval = -1;
1439 	else rval = (obj - objbase) * ocnt + base;
1440 	/* rprintf("INDEX %d + %d::%d ==> %d\n",base,obj,ocnt,rval); */
1441 	return rval;
1442 }
1443 
1444 
have_objattr(rbool prop,integer obj,int id)1445 rbool have_objattr(rbool prop, integer obj, int id) {
1446 	int t;
1447 	char ofs;
1448 
1449 	if (troom(obj)) t = 0;
1450 	else if (tnoun(obj)) t = 1;
1451 	else if (tcreat(obj)) t = 2;
1452 	else return 0;
1453 	if (prop)
1454 		return (lookup_objprop(id, t) >= 0);
1455 	else
1456 		return (lookup_objflag(id, t, &ofs) >= 0);
1457 }
1458 
1459 
1460 
op_objflag(int op,integer obj,int id)1461 rbool op_objflag(int op, integer obj, int id) {
1462 	/* op: 0=clear, 1=set, 2=nop, 3=toggle    two bits: <ab> */
1463 	/*  <flagbit>= (<flagbit>&<a>)^<b> ) */
1464 	int index;
1465 	int t, firstobj;
1466 	char ofs;
1467 
1468 	if (troom(obj)) {
1469 		t = 0;
1470 		firstobj = first_room;
1471 	} else if (tnoun(obj)) {
1472 		t = 1;
1473 		firstobj = first_noun;
1474 	} else if (tcreat(obj)) {
1475 		t = 2;
1476 		firstobj = first_creat;
1477 	} else return 0;
1478 
1479 	index = calcindex(obj, firstobj, num_oattrs(t, 1), lookup_objflag(id, t, &ofs));
1480 	if (index == -1) return 0;
1481 
1482 	return op_simpflag(&objflag[index], ofs, op);
1483 }
1484 
op_objprop(int op,int obj,int id,long val)1485 long op_objprop(int op, int obj, int id, long val) {
1486 	/* op: 2=get, 1=set */
1487 	int index, t, firstobj;
1488 
1489 	if (troom(obj)) {
1490 		t = 0;
1491 		firstobj = first_room;
1492 	} else if (tnoun(obj)) {
1493 		t = 1;
1494 		firstobj = first_noun;
1495 	} else if (tcreat(obj)) {
1496 		t = 2;
1497 		firstobj = first_creat;
1498 	} else return 0;
1499 
1500 	index = calcindex(obj, firstobj, num_oattrs(t, 0), lookup_objprop(id, t));
1501 	if (index == -1) return 0;
1502 
1503 	if (op == 2) return objprop[index];
1504 	else objprop[index] = val;
1505 	return val;
1506 }
1507 
get_objattr_str(int dtype,int id,long val)1508 const char *get_objattr_str(int dtype, int id, long val) {
1509 	int max_val;
1510 
1511 	if (dtype == AGT_OBJPROP) {
1512 		if (!proptable || !propstr || id < 0 || id >= oprop_cnt) return "";
1513 		max_val = proptable[id].str_cnt;
1514 		if (val < 0) val = 0;
1515 		if (val >= max_val) val = max_val - 1;
1516 		if (max_val > 0)
1517 			return propstr[ proptable[id].str_list + val ];
1518 		return "";
1519 	} else if (dtype == AGT_VAR) {
1520 		if (!vartable || !propstr || id < 0 || id > VAR_NUM) return "";
1521 		max_val = vartable[id].str_cnt;
1522 		if (val < 0) val = 0;
1523 		if (val >= max_val) val = max_val - 1;
1524 		if (max_val > 0)
1525 			return propstr[ vartable[id].str_list + val ];
1526 		return "";
1527 	} else if (dtype == AGT_OBJFLAG) {
1528 		if (attrtable && id >= 0 && id < oflag_cnt)
1529 			return (val ? attrtable[id].ystr : attrtable[id].nstr);
1530 		else
1531 			return (val ? "yes" : "no");
1532 	} else if (dtype == AGT_FLAG) {
1533 		/* This uses yes/no as defaults, not on/off */
1534 		if (flagtable && id >= 0 && id <= FLAG_NUM)
1535 			return val ? flagtable[id].ystr : flagtable[id].nstr;
1536 		else
1537 			return val ? "on" : "off";
1538 	} else
1539 		rprintf("INTERNAL ERROR: Invalid data type for get_objattr_str().");
1540 	return "";
1541 }
1542 
1543 /* ------------------------------------------------------------------- */
1544 /* Warning and error functions                                         */
1545 /* ------------------------------------------------------------------- */
1546 
agtwarn(const char * s,int elev)1547 void agtwarn(const char *s, int elev) {
1548 	if (ERR_LEVEL >= elev)
1549 		rprintf("Warning: %s\n", s);
1550 }
1551 
agtnwarn(const char * s,int n,int elev)1552 void agtnwarn(const char *s, int n, int elev) {
1553 	if (ERR_LEVEL >= elev)
1554 		rprintf("Warning: %s%d.\n", s, n);
1555 }
1556 
fatal(const char * s)1557 void fatal(const char *s) {
1558 	error("Fatal error: %s", s);
1559 }
1560 
init_flags(void)1561 void init_flags(void) {
1562 	rm_trap = 1;
1563 	DIAG = def_DIAG;
1564 	interp_arg = def_interp_arg;
1565 	debug_da1 = def_debug_da1;
1566 	RAW_CMD_OUT = def_RAW_CMD_OUT;
1567 	ERR_LEVEL = def_ERR_LEVEL;
1568 	irun_mode = 0;
1569 	fix_ascii_flag = fix_ascii;
1570 	descr_maxmem = DESCR_BUFFSIZE;
1571 	bold_mode = 0;
1572 	dbg_nomsg = 0; /* Print out MSG arguments to metacommands */
1573 	debug_mode = 0;
1574 	dbgflagptr = NULL;
1575 	dbgvarptr = NULL;
1576 	dbgcntptr = NULL;
1577 	no_auxsyn = 0;
1578 	text_file = 0;
1579 #ifdef PATH_SEP
1580 	gamepath = NULL;
1581 #endif
1582 	BATCH_MODE = make_test = 0;
1583 	font_status = 0;
1584 #ifdef OPEN_AS_TEXT
1585 	open_as_binary = 0;
1586 #endif
1587 }
1588 
1589 } // End of namespace AGT
1590 } // End of namespace Glk
1591