aboutsummaryrefslogtreecommitdiff
path: root/engines/glk/hugo/hugo.h
blob: f2d83b895b1bbbfb7b39939256df1336d2622f71 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 */

#ifndef GLK_HUGO_HUGO
#define GLK_HUGO_HUGO

#include "common/scummsys.h"
#include "common/str.h"
#include "glk/glk_api.h"
#include "glk/hugo/htokens.h"
#include "glk/hugo/hugo_defines.h"
#include "glk/hugo/hugo_types.h"
#include "glk/hugo/stringfn.h"

namespace Glk {
namespace Hugo {

/**
 * Hugo game interpreter
 */
class Hugo : public GlkAPI, public HTokens, public StringFunctions {
private:
	int _savegameSlot;
	winid_t mainwin, currentwin;
	winid_t secondwin, auxwin;
	bool runtime_warnings;
	int dbnest;

	/**
	 * address_scale refers to the factor by which addresses are multiplied to
	 * get the "real" address.  In this way, a 16-bit integer can reference
	 * 64K * 16 = 1024K of memory.
	 */
	int address_scale;

	// heexpr
	int eval[MAX_EVAL_ELEMENTS];		///< expression components
	int evalcount;						///< # of expr. components
	int var[MAXLOCALS + MAXGLOBALS];	///< variables
	int incdec;							///< value is being incremented/dec.
	char getaddress;					///< true when finding &routine
	char inexpr;						///< true when in expression
	char inobj;							///< true when in object compound
	int last_precedence;

	// hemedia
	schanid_t mchannel;
	schanid_t schannel;
	long resids[2][MAXRES];
	int numres[2];

	// hemisc
	char gamefile[255];
	int game_version;
	int object_size;
	Common::SeekableReadStream *game;
	HUGO_FILE script;
	HUGO_FILE playback;
	HUGO_FILE record;
	HUGO_FILE io; char ioblock; char ioerror;

	char id[3];
	char serial[9];
	unsigned int codestart;
	unsigned int objtable;
	unsigned int eventtable;
	unsigned int proptable;
	unsigned int arraytable;
	unsigned int dicttable;
	unsigned int syntable;
	unsigned int initaddr;
	unsigned int mainaddr;
	unsigned int parseaddr;
	unsigned int parseerroraddr;
	unsigned int findobjectaddr;
	unsigned int endgameaddr;
	unsigned int speaktoaddr;
	unsigned int performaddr;
	int objects;
	int events;
	int dictcount;
	int syncount;
	char context_command[MAX_CONTEXT_COMMANDS][64];
	int context_commands;
	unsigned char *mem;
	bool loaded_in_memory;
	unsigned int defseg;
	unsigned int gameseg;
	long codeptr;
	long codeend;
	char pbuffer[MAXBUFFER * 2 + 1];
	int currentpos;
	int currentline;
	int full;
	signed char def_fcolor, def_bgcolor, def_slfcolor, def_slbgcolor;
	signed char fcolor, bgcolor, icolor, default_bgcolor;
	int currentfont;
	char capital;
	unsigned int textto;
	int SCREENWIDTH, SCREENHEIGHT;
	int physical_windowwidth, physical_windowheight,
		physical_windowtop, physical_windowleft,
		physical_windowbottom, physical_windowright;
	int inwindow;
	int charwidth, lineheight, FIXEDCHARWIDTH, FIXEDLINEHEIGHT;
	int current_text_x, current_text_y;
	bool skipping_more;
	int undostack[MAXUNDO][5];
	int undoptr;
	int undoturn;
	char undoinvalid;
	char undorecord;

	bool in_valid_window;
	int glk_fcolor, glk_bgcolor;
	int mainwin_bgcolor;
	int glk_current_font;
	bool just_cleared_screen;
	int secondwin_bottom;

	// heobject
	int display_object;				///< i.e., non-existent (yet)
	char display_needs_repaint;		///< for display object
	int display_pointer_x, display_pointer_y;

	// heparse
	char buffer[MAXBUFFER + MAXWORDS];
	char errbuf[MAXBUFFER + 1];				///< last invalid input
	char line[1025];						///< line buffer

	int words;								///< parsed word count
	char *word[MAXWORDS + 1];				///< breakdown into words
	unsigned int wd[MAXWORDS + 1];			///<     "      "   dict. entries
	unsigned int parsed_number;				///< needed for numbers in input

	signed char remaining;					///< multiple commands in input
	char parseerr[MAXBUFFER + 1];			///< for passing to RunPrint, etc.
	char parsestr[MAXBUFFER + 1];			///< for passing quoted string
	char xverb;								///< flag; 0 = regular verb
	char starts_with_verb;					///< input line; 0 = no verb word
	unsigned int grammaraddr;				///< address in grammar
	char *obj_parselist;					///< objects with noun/adjective
	int domain, odomain;					///< of object(s)
	int objlist[MAXOBJLIST];				///< for objects of verb
	char objcount;							///< of objlist
	char parse_allflag;						///< for "all" in MatchObject
	pobject_structure pobjlist[MAXPOBJECTS];	///< for possible objects
	int pobjcount;							///< of pobjlist
	int pobj;								///< last remaining suspect
	int obj_match_state;					///< see MatchCommand() for details
	char objword_cache[MAXWORDS];			///< for MatchWord() xobject, etc.
	char object_is_number;					///< number used in player command
	unsigned int objgrammar;				///< for 2nd pass
	int objstart;							///<  "   "   "
	int objfinish;							///<  "   "   "
	bool addflag;							///< true if adding to objlist[]
	int speaking;							///< if command is addressed to obj.

	char oops[MAXBUFFER + 1];				///< illegal word
	int oopscount;							///< # of corrections in a row

	char parse_called_twice;
	char reparse_everything;
	char punc_string[64];					///< punctuation string
	byte full_buffer;

	/**
	 * to MatchObject()
	 * Necessary for proper disambiguation when addressing a character;
	 * i.e., when 'held' doesn't refer to held by the player, etc.
	 */
	char recursive_call;
	int parse_location;						///< usually var[location]

	// heres
	HUGO_FILE resource_file;
	int extra_param;
	char loaded_filename[MAX_RES_PATH];
	char loaded_resname[MAX_RES_PATH];
	char resource_type;

	// herun
	int passlocal[MAXLOCALS];		///< locals passed to routine
	int arguments_passed;			///< when calling routine
	int ret;						///< return value and returning flag
	char retflag;
	bool during_player_input;
	char override_full;
	bool game_reset;				///< for restore, undo, etc.

	CODE_BLOCK code_block[MAXSTACKDEPTH];
	int stack_depth;
	int tail_recursion;
	long tail_recursion_addr;

	// Used by RunWindow for setting current window dimensions:
	int last_window_top, last_window_bottom, last_window_left, last_window_right;
	int lowest_windowbottom,				///< in text lines
		physical_lowest_windowbottom;		///< in pixels or text lines
	bool just_left_window;

	// heset
	char game_title[MAX_GAME_TITLE];
	char arrexpr;							///< true when assigning array
	char multiprop;							///< true in multiple prop. assign.
	int set_value;

#if defined (DEBUGGER)
	char debug_line[MAX_DEBUG_LINE];
	bool debug_eval;
	bool debug_eval_error;
	bool debugger_step_over;
	bool debugger_finish;
	bool debugger_run;
	bool debugger_interrupt;
	bool debugger_skip;
	bool runtime_error;
	uint currentroutine;
	bool complex_prop_breakpoint;
	bool trace_complex_prop_routine;
	char *objectname[MAX_OBJECT];
	char *propertyname[MAX_PROPERTY];
	CALL call[MAXCALLS];
	int routines;
	int properties;
	WINDOW window[9];
	int codeline[9][100];
	char localname[9][100];
	int current_locals;
	long this_codeptr;
	int debug_workspace;
	int attributes;
	int original_dictcount;
	int buffered_code_lines;
	bool debugger_has_stepped_back;
	bool debugger_step_back;
	int debugger_collapsing;
	int runaway_counter;
	int history_count;
	int active_screen;
	int step_nest;
	BREAKPOINT breakpoint[MAXBREAKPOINTS];
	BREAKPOINT watch[MAXBREAKPOINTS];
	int code_history[MAX_CODE_HISTORY];
	int dbnest_history[MAX_CODE_HISTORY];
	int history_last;
#endif
private:
	/**
	 * \defgroup heexpr
	 * @{
	 */

	 /**
	  * The new-and-improved expression evaluator.  Evaluates the current expression
	  * (or sub-expression therein) beginning at eval[p].
	  */
	int EvalExpr(int p);

	/**
	 * Called by GetValue(); does the actual dirty work of returning a value from a
	 * simple data type.
	 */
	int GetVal();

	/**
	 * Does any reckoning for more sophisticated constructions.
	 */
	int GetValue();

	/**
	 * Actually performs the increment given below by IsIncrement.
	 */
	int Increment(int a, char inctype);

	/**
	 * If an increment/decrement is next up (i.e. ++, --, or +=, *=, etc.),
	 * then sets incdec equal to the increment/decrement and repositions codeptr.
	 * Returns the token number of the operation, if any.
	 */
	char IsIncrement(long addr);

	/**
	 *  Returns the precedence ranking of the operator represented by token[t].
	 * The lower the return value, the higher the rank in terms of processing order.
	 */
	int Precedence(int t);

	/**
	 * Reads the current expression from the current code position into eval[],
	 * using the following key:
	 *
	 * if eval[n] is 0, eval[n+1] is a value
	 * if eval[n] is 1, eval[n+1] is a token
	 *
	 * <inexpr> is used in various routines to keep track of whether or not we're currently
	 * reading an expression.  If <inexpr> is 1, we're in an expression; if 2, we may have
	 * to step back one code position if encountering a closing parentheses.
	 */
	void SetupExpr();

	/**
	 * Cuts off straggling components of eval[] after an expression or sub-expression
	 * has been successfully evaluated.
	 */
	void TrimExpr(int ptr);

	/**@}*/

	/**
	 * \defgroup heglk
	 * @{
	 */

	 /**
	  * Does whatever has to be done to initially set up the display
	  */
	void hugo_init_screen();

	/**
	 * Does whatever has to be done to clean up the display pre-termination
	 */
	void hugo_cleanup_screen() {
		// No implementation
	}

	void hugo_closefiles() {
		// Glk closes all files on exit
	}

	int hugo_getkey() const {
		// Not needed here--single-character events are handled solely by hugo_waitforkey(), below
		return 0;
	}

	/**
	 * Gets a line of input from the keyboard, storing it in <buffer>.
	 */
	void hugo_getline(const char *prmpt);

	/**
	 * Provided to be replaced by multitasking systems where cycling while waiting
	 * for a keystroke may not be such a hot idea.
	 */
	int hugo_waitforkey();

	/**
	 * Returns true if a keypress is waiting to be retrieved.
	 */
	int hugo_iskeywaiting();

	/**
	 * Waits for 1/n seconds.  Returns false if waiting is unsupported.
	 */
	int hugo_timewait(int n);

	/**
	 * Clears everything on the screen, moving the cursor to the top-left corner of the screen
	 */
	void hugo_clearfullscreen();

	/**
	 * Clears the currently defined window, moving the cursor to the top-left corner of the window
	 */
	void hugo_clearwindow();

	/**
	 * This function does whatever is necessary to set the system up for a standard text display
	 */
	void hugo_settextmode();

	void hugo_settextwindow(int left, int top, int right, int bottom);

	/**
	 * Specially accommodated in GetProp() While the engine thinks that the linelength is 0x7fff,
	this tells things like the display object the actual length.  (Defined as ACTUAL_LINELENGTH)
	*/
	int heglk_get_linelength();

	/**
	 * Similar to heglk_get_linelength().  (Defined as ACTUAL_SCREENHEIGHT)
	 */
	int heglk_get_screenheight();

	void hugo_settextpos(int x, int y);

	/**
	 * Essentially the same as printf() without formatting, since printf() generally doesn't take
	 * into account color setting, font changes, windowing, etc.
	 *
	 * The newline character '\n' must be explicitly included at the end of a line in order to
	 * produce a linefeed.  The new cursor position is set to the end of this printed text.
	 * Upon hitting the right edge of the screen, the printing position wraps to the start
	 * of the next line.
	 */
	void hugo_print(const char *a);

	/**
	 * Scroll the text window
	 */
	void hugo_scrollwindowup() {
		// No implementation. Glk takes care of it
	}

	/**
	 * Set the font
	 * @param f		The <f> argument is a mask containing any or none of:
	 *				BOLD_FONT, UNDERLINE_FONT, ITALIC_FONT, PROP_FONT.
	 */
	void hugo_font(int f);

	/**
	 * Set the foreground (print) color
	 */
	void hugo_settextcolor(int c);

	/**
	 * Set the background color
	 */
	void hugo_setbackcolor(int c);

	/**
	 * Color-setting functions should always pass the color through hugo_color()
	 * in order to properly set default fore/background colors
	 */
	int hugo_color(int c);

	/**
	 * Get the width of a character
	 * @remarks		As given here, this function works only for non-proportional printing.
	 * For proportional printing, hugo_charwidth() should return the width of the supplied
	 * character in the current font and style.
	*/
	int hugo_charwidth(char a) const;

	/**
	 * Return the width of a string
	 */
	int hugo_textwidth(const char *a) const;

	/**
	 * Return the length of a string
	 */
	int hugo_strlen(const char *a) const;

	void hugo_setgametitle(const char *t);

	int hugo_hasvideo() const;

	int hugo_playvideo(HUGO_FILE infile, long reslength, char loop_flag, char background, int volume);

	void hugo_stopvideo();

	/**@}*/

	/**
	 * \defgroup hemisc
	 * @{
	 */

	/**
	 * The all-purpose printing routine that takes care of word-wrapping.
	 */
	void AP(const char *a);

	/**
	 * Used whenever a routine is called, assumes the routine address and begins
	 * with the arguments (if any).
	 */
	int CallRoutine(unsigned int addr);

	/**
	 * Adds a command to the context command list.  A zero value (i.e., an empty string)
	 * resets the list.
	 */
	void ContextCommand();

	/**
	 * Dynamically creates a new dictionary entry.
	 */
	unsigned int Dict();

	/**
	 * Generates a fatal error
	 */
	void FatalError(int n);

	void FileIO();

	void Flushpbuffer();

	void GetCommand();

	/**
	 * From any address <addr>; the segment must be defined prior to calling the function.
	 */
	char *GetString(long addr);

	/**
	 * Get text block from position <textaddr> in the text bank.  If the game was not fully loaded
	 * in memory, i.e., if loaded_in_memory is not true, the block is read from disk.
	 */
	char *GetText(long textaddr);

	/**
	 * From the dictionary table.
	 */
	char *GetWord(unsigned int w);

	void HandleTailRecursion(long addr);

	void InitGame();

	void LoadGame();

	/**
	 * Must be called before running every new routine, i.e. before calling RunRoutine().
	 * Unfortunately, the current locals must be saved in a temp array prior to calling.
	 * The argument n gives the number of arguments passed.
	 */
	void PassLocals(int n);

	inline unsigned char Peek(long a) {
		return MEM(defseg * 16L + a);
	}

	inline unsigned int PeekWord(long a) {
		return (unsigned char)MEM(defseg * 16L + a) + (unsigned char)MEM(defseg * 16L + a + 1) * 256;
	}

	inline void Poke(unsigned int a, unsigned char v) {
		SETMEM(defseg * 16L + a, v);
	}

	inline void PokeWord(unsigned int a, unsigned int v) {
		SETMEM(defseg * 16L + a, (char)(v % 256));
		SETMEM(defseg * 16L + a + 1, (char)(v / 256));
	}

	/**
	 * Returns <a> as a hex-number string in XXXXXX format.
	 */
	static const char *PrintHex(long a);

	/**
	 * Print to client display taking into account cursor relocation,
	 * font changes, color setting, and window scrolling.
	 */
	void Printout(char *a, int no_scrollback_linebreak);

	void PromptMore();

	int RecordCommands();

	/**
	 * Formats:
	 *
	 * end of turn:    (0, undoturn, 0, 0, 0)
	 * move obj.:      (MOVE_T, obj., parent, 0, 0)
	 * property:       (PROP_T, obj., prop., # or PROP_ROUTINE, val.)
	 * attribute:      (ATTR_T, obj., attr., 0 or 1, 0)
	 * variable:       (VAR_T, var., value, 0, 0)
	 * array:          (ARRAYDATA_T, array addr., element, val., 0)
	 * dict:           (DICT_T, entry length, 0, 0, 0)
	 * word setting:   (WORD_T, word number, new word, 0, 0)
	 */
	void SaveUndo(int a, int b, int c, int d, int e);

	/**
	 * Properly sets up the code_block structure for the current stack depth depending
	 * on if this is a called block (RUNROUTINE_BLOCK) or otherwise.
	 */
	void SetStackFrame(int depth, int type, long brk, long returnaddr);

	void SetupDisplay();

	/**
	 * The method is passed <a> as the string and <*i> as the position in the string.
	 * The character(s) at a[*i], a[*(i+1)], etc. are converted into a single Latin-1
	 * (i.e., greater than 127) character value.
	 *
	 * Assume that the AP() has already encountered a control character ('\'),
	 * and that a[*i]... is one of:
	 *
	 * `a	accent grave on following character (e.g., 'a')
	 * 'a	accent acute on following character (e.g., 'a')
	 * ~n	tilde on following (e.g., 'n' or 'N')
	 * :a	umlaut on following (e.g., 'a')
	 * ^a	circumflex on following (e.g., 'a')
	 * ,c	cedilla on following (e.g., 'c' or 'C')
	 * <	Spanish left quotation marks
	 * >	Spanish right quotation marks
	 * !	upside-down exclamation mark
	 * ?	upside-down question mark
	 * ae	ae ligature
	 * AE	AE ligature
	 * c	cents symbol
	 * L	British pound
	 * Y	Japanese Yen
	 * -	em (long) dash
	 * #nnn	character value given by nnn
	 *
	 * Note that the return value is a single character--which will be either unchanged
	 * or a Latin-1 character value.
	 */
	char SpecialChar(const char *a, int *i);

	int Undo();

	/**@}*/

	/**
	 * \defgroup hemedia
	 * @{
	 */

	int loadres(HUGO_FILE infile, int reslen, int type);

	int hugo_hasgraphics();

	int hugo_displaypicture(HUGO_FILE infile, long reslen);

	void initsound();

	void initmusic();

	int hugo_playmusic(HUGO_FILE infile, long reslen, char loop_flag);

	void hugo_musicvolume(int vol);

	void hugo_stopmusic();

	int hugo_playsample(HUGO_FILE infile, long reslen, char loop_flag);

	void hugo_samplevolume(int vol);

	void hugo_stopsample();

	/**@}*/

	/**
	 * \defgroup heobject - Object/property/attribute management functions
	 * @{
	 */
	int Child(int obj);

	int Children(int obj);

	int Elder(int obj);

	/**
	 * Returns one of four sets of 32 attributes.
	 */
	unsigned long GetAttributes(int obj, int attribute_set);

	/**
	 * Returns the value of '<obj>.<p> #<n>'  If <s> is true, the self global
	 * is not set to <obj> in order to facilitate <obj>..<p> calls.
	 */
	int GetProp(int obj, int p, int n, char s);

	/**
	 * Returns the value of the last object above <obj> in the tree before object 0.
	 */
	int GrandParent(int obj);

	void MoveObj(int obj, int p);

	const char *Name(int obj);

	int Parent(int obj);

	/**
	 * Returns address of <obj>.<p> (with <offset> provided for additive properties--
	 * i.e. subsequent calls with the same <obj> and <p>.
	 */
	unsigned int PropAddr(int obj, int p, unsigned int offset);

	/**
	 * Writes (puts) one of four sets of 32 attributes.
	 */
	void PutAttributes(int obj, unsigned long a, int attribute_set);

	/**
	 * Set an attribute
	 * c = 1 for set, 0 for clear
	 */
	void SetAttribute(int obj, int attr, int c);

	int Sibling(int obj);

	int TestAttribute(int obj, int attr, int nattr);

	int Youngest(int obj);

	/**@}*/

	/**
	 * \defgroup heparse
	 * @{
	 */

	void AddAllObjects(int loc);

	/**
	 * Adds the object <obj> to objlist[], making all related adjustments.
	 */
	void AddObj(int obj);

	/**
	 * Adds <obj> as a contender to the possible object list, noting that it was referred
	 * to as either a noun or an adjective.
	 */
	void AddPossibleObject(int obj, char type, unsigned int w);

	/**
	 * Move the address in the grammar table past the current token.
	 */
	void AdvanceGrammar();

	/**
	 * For when it's only necessary to know if word[wn] is an object word for any object,
	 * not a particular object.  Returns 1 for an object word or -1 for a non-object word.
	 */
	int AnyObjWord(int wn);

	/**
	 * The non_grammar argument is true when called from a non-grammar function such as RunEvents().
	 */
	int Available(int obj, char non_grammar);

	void CallLibraryParse();

	/**
	 * Takes into account the preset domain for checking an object's presence;
	 * <domain> is 0, -1, or an object number..
	 */
	int DomainObj(int obj);

	/**
	 * Returns the dictionary address of <a>.
	 */
	unsigned int FindWord(const char *a);

	/**
	 * Checks to see if <obj> is in objlist[].
	 */
	int InList(int obj);

	/**
	 * Deletes word[a].
	 */
	void KillWord(int a);

	/**
	 * Here, briefly, is how MatchCommand() works:
	 *
	 * 1.  Match the verb.
	 *
	 * 2.  If no match, check to see if the line begins with an object (character)
	 * and try to match it.
	 *
	 * 3.  If found, try to match a syntax for that verb, including objects, dictionary words,
	 * numbers, attributes, and routines.  If any objects are specified, skip over them for now,
	 * marking the start and finish.  This is done mostly in MatchWord().
	 *
	 * 4.  Match the xobject, if there is one--via MatchObject().
	 *
	 * 5.  If all is well, return to match the objects that were previously skipped over,
	 * loading them into objlist[]. Once again, this is done by MatchObject().
	 *
	 * (The reason the objects are initially skipped is because it may be necessary to know 
	 * where to look for them--this may require knowing what the xobject is, if the syntax
	 * is something like:
	 *
	 * "get" <object> "from" <xobject>)
	 *
	 * The variable <obj_match_state> is the indicator of what stage object-matching is at:
	 *
	 * obj_match_state = 0  - haven't matched anything yet
	 *
	 * obj_match_state = 1  - xobject has been matched
	 *
	 * obj_match_state = 2  - matching object(s), loading objlist[]
	 *
	 * obj_match_state = 5  - matching first word/name, i.e., "Bob, <do something>"
	 */
	int MatchCommand();

	/**
	 * The argument is the word number we're starting matching on.
	 *
	 * NOTE:  recusive_call is set to 0 if this is the first call. MatchObject() sets it to 1
	 * when calling itself when asking for clarification as to which object is meant.
	 *
	 * Return true on a recursive call to allow parsing to continue.
	 */
	bool MatchObject(int *wordnum);

	int MatchWord(int *wordnum);

	/**
	 * Returns true if the specified object has the specified word as an adjective or noun
	 * (as specified by type).
	 */
	int ObjWordType(int obj, unsigned int w, int type);

	/**
	 * Returns <adjective> if the word at dictionary address <w> is an adjective of <obj>,
	 * or <noun> if it is a noun.
	 */
	int ObjWord(int obj, unsigned int w);

	/**
	 * Turns word[] into dictionary addresses stored in wd[].  Takes care of fingering illegal
	 * (unknown) words and doing alterations such as compounds, removals, and synonyms.
	 */
	int Parse();

	void ParseError(int e, int a);

	/**
	 * Deletes wd[a].
	 */
	void RemoveWord(int a);

	/**
	 * Call FindObject(0, 0) to reset library's disambiguation mechanism.
	 */
	void ResetFindObject();

	/**
	 * Splits <buffer> into the word[] array.  Also does nifty things such as turning time
	 * values such as hh:mm into a single number (representing minutes from midnight).
	 */
	void SeparateWords();

	/**
	 * Removes object <obj> from objlist[], making all related adjustments.
	 */
	void SubtractObj(int obj);

	/**
	 * Removes <obj> as a possible contender for object disambiguation.
	 */
	void SubtractPossibleObject(int obj);

	/**
	 * Called by MatchObject() to see if <obj> is available, and add it to or subtract
	 * it from objlist[] accordingly.
	 */
	void TryObj(int obj);

	/**
	 * Checks first of all to see if an object is available, then checks if it meets
	 * all the qualifications demanded by the grammar syntax.
	 */
	int ValidObj(int obj);

	/**@}*/

	/**
	 * \defgroup heres
	 * @{
	 */

	void DisplayPicture();

	void PlayMusic();

	void PlaySample();

	void PlayVideo();

	/**
	 * Assumes that filename/resname contain a resourcefile name and a resource name.
	 * If resname is "", filename contains the path of the resource on disk.
	 * Returns the length of the resource if if the named resource is found.
	 *
	 * If FindResource() returns non-zero, the file is hot, i.e., it is open and positioned
	 * to the start of the resource.
	 *
	 * Note that resourcefiles are expected to be in (if not the current directory) "object" or "games",
	 * and on-disk resources in (if not the given directory) "source" or "resource" (where these are the
	 * environment variables "HUGO_...", not actual on-disk directories).
	 */
	long FindResource(char *filename, char *resname);

	/**
	 * Processes resourcefile/filename (and resource, if applicable).
	 * Returns 0 if a valid 0 parameter is passed as in "music 0" or "sound 0".
	 */
	int GetResourceParameters(char *filename, char *resname, int restype);

	/**@}*/

	/**
	* \defgroup herun
	* @{
	*/

	void RunDo();

	void RunEvents();

	void playGame();

	void RunIf(char override);

	void RunInput();

	/**
	 * (All the debugger range-checking is important because invalid memory writes due
	 * to invalid object location calculations are a good way to crash the system.)
	 */
	void RunMove();

	void RunPrint();

	int RunRestart();

	int RestoreGameData();

	int RunRestore();

	/**
	 * This is the main loop for running each line of code in sequence;
	 * the main switch statement is based on the first token in each line.
	 * 
	 * This routine is relatively complex, especially given the addition of debugger control.
	 * Basically it is structured like this:
	 *
	 * 1.  If this is the debugger build, see what debugger information has to be set up upon
	 * calling this block of code
	 *
	 * 2.  Get the next token, and as long as it isn't CLOSE_BRACE_T ('}')...
	 *
	 * 3.  ...If this is the debugger build, see if there is a standing debugger_interrupt
	 * to pass control back to the debugger, and perform all operations for stepping
	 * tracing, breakpoints, etc.
	 *
	 * 4.  ...See what token we're dealing with and execute accordingly
	 *
	 * 5.  ...Loop back to (2)
	 *
	 * 6.  If this is the debugger build, do whatever is necessary to tidy up after finishing
	 * this block of code
	 *
	 * There's a bit of a trick involved since the original language design uses "{...}"
	 * structures for both conditionals and blocks that necessitate another (i.e., nested) call
	 * to RunRoutine(). The call_block structure array and stack_depth variable are the
	 * navigation guides.
	 */
	void RunRoutine(long addr);

	int RunSave();

	int RunScriptSet();

	/**
	 * As in 'x = string(<array>, "<string>"[, maxlen]'.
	 */
	int RunString();

	int RunSystem();

	void SaveWindowData(SAVED_WINDOW_DATA *spw);

	void RestoreWindowData(SAVED_WINDOW_DATA *spw);

	void RunWindow();

	/**@}*/

	/**
	 * \defgroup heglk
	 * @{
	 */

	/**
	 * If gotvalue is passed as -1, then no value has already been as the (potential) object, etc.
	 * comprising the first part of the object.property, for example, to be set.
	 */
	void RunSet(int gotvalue);

	unsigned int GetAnonymousFunction(long addr);

	int SetCompound(int t);

	/**@}*/

	/**
	* \defgroup Miscellaneous
	* @{
	*/

	int hugo_fseek(Common::SeekableReadStream *s, long int offset, int whence) {
		return !s->seek(offset, whence);
	}
	int hugo_fseek(strid_t s, long int offset, int whence) {
		Common::SeekableReadStream *rs = *s;
		return hugo_fseek(rs, offset, whence);
	}

	int hugo_fgetc(Common::SeekableReadStream *s) {
		return s->readByte();
	}
	int hugo_fgetc(strid_t s) {
		Common::SeekableReadStream *ws = *s;
		return hugo_fgetc(ws);
	}

	int hugo_fputc(int c, Common::WriteStream *s) {
		s->writeByte(c);
		return s->err() ? EOF : 0;
	}
	int hugo_fputc(int c, strid_t s) {
		Common::WriteStream *ws = *s;
		return hugo_fputc(c, ws);
	}

	char *hugo_fgets(char *buf, int max, Common::SeekableReadStream *s) {
		char *ptr = buf;
		char c;
		while (s->pos() < s->size() && --max > 0) {
			c = hugo_fgetc(s);
			if (c == '\n' || c == '\0')
				break;
			*ptr++ = c;
		}
		*ptr++ = '\0';
		return buf;
	}
	char *hugo_fgets(char *buf, int max, strid_t s) {
		Common::SeekableReadStream *rs = *s;
		return hugo_fgets(buf, max, rs);
	}

	size_t hugo_fread(void *ptr, size_t size, size_t count, Common::SeekableReadStream *s) {
		return s->read(ptr, size * count);
	}
	size_t hugo_fread(void *ptr, size_t size, size_t count, strid_t s) {
		Common::SeekableReadStream *rs = *s;
		return hugo_fread(ptr, size, count, rs);
	}

	int hugo_fprintf(Common::WriteStream *s, const char *fmt, ...) {
		va_list va;
		va_start(va, fmt);
		Common::String text = Common::String::vformat(fmt, va);
		va_end(va);

		s->write(text.c_str(), text.size());
		return s->err() ? -1 : 0;
	}
	int hugo_fprintf(strid_t s, const char *fmt, ...) {
		va_list va;
		va_start(va, fmt);
		Common::String text = Common::String::vformat(fmt, va);
		va_end(va);

		Common::WriteStream *str = *s;
		str->write(text.c_str(), text.size());
		return str->err() ? -1 : 0;
	}

	int hugo_fputs(const char *str, Common::WriteStream *s) {
		return s->write(str, strlen(str)) == strlen(str) ? 0 : -1;
	}
	int hugo_fputs(const char *str, strid_t s) {
		Common::WriteStream *ws = *s;
		return hugo_fputs(str, ws);
	}

	bool hugo_ferror(Common::SeekableReadStream *s) const {
		return s->err();
	}
	bool hugo_ferror(strid_t s) const {
		Common::SeekableReadStream *rs = *s;
		return hugo_ferror(rs);
	}

	long hugo_ftell(Common::SeekableReadStream *s) {
		return s->pos();
	}
	long hugo_ftell(strid_t s) {
		Common::SeekableReadStream *rs = *s;
		return hugo_ftell(rs);
	}

	int hugo_fclose(strid_t f) {
		delete f;
		return 0;
	}

	void hugo_exit(const char *msg) {
		error("%s", line);
	}

	uint hugo_rand() {
		return _random.getRandomNumber(0xffffff);
	}

	char *itoa(int value, char *str, int base) {
		assert(base == 10);
		sprintf(str, "%d", value);
		return str;
	}

	/**@}*/
private:
	/**
	 * Allocate memory block
	 */
	void *hugo_blockalloc(size_t num) { return malloc(num); }

	void hugo_blockfree(void *block) { free(block); }

#if defined (DEBUGGER)
	int CheckinRange(uint v1, uint v2, const char *v3) { return 1; }

	/**
	* Shorthand since many of these object functions may call CheckinRange() if the debugger
	* is running and runtime_warnings is set.
	*/
	int CheckObjectRange(int obj);

	void DebugRunRoutine(long addr) { RunRoutine(addr); }

	void RuntimeWarning(const char *msg) {}

	void DebugMessageBox(const char *title, const char *msg) {}

	bool IsBreakpoint(long loc) const { return false; }

	const char *RoutineName(long loc) { return "Routine"; }

	void AddStringtoCodeWindow(const char *str) {}

	void SwitchtoDebugger() {}

	void Debugger() {}
	
	void UpdateDebugScreen() {}

	void SwitchtoGame() {}

	void DebuggerFatal(DEBUGGER_ERROR err) { error("Debugger error"); }

	void AddLinetoCodeWindow(int lineNum) {}

	void RecoverLastGood() {}

	void SetupWatchEval(int num) {}

	bool EvalWatch() { return false; }
#endif
public:
	/**
	 * Constructor
	 */
	Hugo(OSystem *syst, const GlkGameDescription &gameDesc);

	/**
	 * Run the game
	 */
	void runGame();

	/**
	 * Returns the running interpreter type
	 */
	virtual InterpreterType getInterpreterType() const override { return INTERPRETER_HUGO; }

	/**
	 * Load a savegame from the passed stream
	 */
	virtual Common::Error loadGameData(strid_t save) override;

	/**
	 * Save the game to the passed stream
	 */
	virtual Common::Error saveGameData(strid_t save, const Common::String &desc) override;
};

} // End of namespace Hugo
} // End of namespace Glk

#endif