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/glulx/glulx.h"
24 
25 namespace Glk {
26 namespace Glulx {
27 
28 /* This code is actually very general; it could work for almost any
29    32-bit VM which remotely resembles Glulx or the Z-machine in design.
30 
31    To be precise, we make the following assumptions:
32 
33    - An argument list is an array of 32-bit values, which can represent
34 	 either integers or addresses.
35    - We can read or write to a 32-bit integer in VM memory using the macros
36 	 ReadMemory(addr) and WriteMemory(addr), where addr is an address
37 	 taken from the argument list.
38    - A character array is a sequence of bytes somewhere in VM memory.
39 	 The array can be turned into a C char array by the macro
40 	 CaptureCArray(addr, len), and released by ReleaseCArray().
41 	 The passin, passout hints may be used to avoid unnecessary copying.
42    - An integer array is a sequence of integers somewhere in VM memory.
43 	 The array can be turned into a C integer array by the macro
44 	 CaptureIArray(addr, len), and released by ReleaseIArray().
45 	 These macros are responsible for fixing byte-order and alignment
46 	 (if the C ABI does not match the VM's). The passin, passout hints
47 	 may be used to avoid unnecessary copying.
48    - A Glk object array is a sequence of integers in VM memory. It is
49 	 turned into a C pointer array (remember that C pointers may be more
50 	 than 4 bytes!) The pointer array is allocated by
51 	 CapturePtrArray(addr, len, objclass) and released by ReleasePtrArray().
52 	 Again, the macros handle the conversion.
53    - A Glk structure (such as event_t) is a set of integers somewhere
54 	 in VM memory, which can be read and written with the macros
55 	 ReadStructField(addr, fieldnum) and WriteStructField(addr, fieldnum).
56 	 The fieldnum is an integer (from 0 to 3, for event_t.)
57    - A VM string can be turned into a C-style string with the macro
58 	 ptr = DecodeVMString(addr). After the string is used, this code
59 	 calls ReleaseVMString(ptr), which should free any memory that
60 	 DecodeVMString allocates.
61    - A VM Unicode string can be turned into a zero-terminated array
62 	 of 32-bit integers, in the same way, with DecodeVMUstring
63 	 and ReleaseVMUstring.
64 
65 	 To work this code over for a new VM, just diddle the macros.
66 */
67 
classtable_register(void * obj,uint objclass)68 static gidispatch_rock_t classtable_register(void *obj, uint objclass) {
69 	return g_vm->glulxe_classtable_register(obj, objclass);
70 }
71 
classtable_unregister(void * obj,uint objclass,gidispatch_rock_t objrock)72 static void classtable_unregister(void *obj, uint objclass, gidispatch_rock_t objrock) {
73 	g_vm->glulxe_classtable_unregister(obj, objclass, objrock);
74 }
75 
retained_register(void * array,uint len,const char * typecode)76 static gidispatch_rock_t retained_register(void *array, uint len, const char *typecode) {
77 	return g_vm->glulxe_retained_register(array, len, typecode);
78 }
79 
retained_unregister(void * array,uint len,const char * typecode,gidispatch_rock_t objrock)80 static void retained_unregister(void *array, uint len, const char *typecode, gidispatch_rock_t objrock) {
81 	g_vm->glulxe_retained_unregister(array, len, typecode, objrock);
82 }
83 
glkopInit()84 void Glulx::glkopInit() {
85 	library_select_hook = nullptr;
86 	arrays = nullptr;
87 	num_classes = 0;
88 	classes = nullptr;
89 }
90 
init_dispatch()91 bool Glulx::init_dispatch() {
92 	int ix;
93 
94 	/* Set up the game-ID hook. (This is ifdeffed because not all Glk
95 	   libraries have this call.) */
96 #ifdef GI_DISPA_GAME_ID_AVAILABLE
97 	gidispatch_set_game_id_hook(&get_game_id);
98 #endif /* GI_DISPA_GAME_ID_AVAILABLE */
99 
100 	/* Allocate the class hash tables. */
101 	num_classes = gidispatch_count_classes();
102 	classes = (classtable_t **)glulx_malloc(num_classes  * sizeof(classtable_t *));
103 	if (!classes)
104 		return false;
105 
106 	for (ix = 0; ix < num_classes; ix++) {
107 		classes[ix] = new_classtable((glulx_random() % (uint)(101)) + 1);
108 		if (!classes[ix])
109 			return false;
110 	}
111 
112 	/* Set up the two callbacks. */
113 	gidispatch_set_object_registry(&classtable_register, &classtable_unregister);
114 	gidispatch_set_retained_registry(&retained_register, &retained_unregister);
115 
116 	/* If the library supports autorestore callbacks, set those up too.
117 	   (These are only used in iosglk, currently.) */
118 #ifdef GIDISPATCH_AUTORESTORE_REGISTRY
119 	gidispatch_set_autorestore_registry(&glulxe_array_locate, &glulxe_array_restore);
120 #endif /* GIDISPATCH_AUTORESTORE_REGISTRY */
121 
122 	return true;
123 }
124 
perform_glk(uint funcnum,uint numargs,uint * arglist)125 uint Glulx::perform_glk(uint funcnum, uint numargs, uint *arglist) {
126 	uint retval = 0;
127 
128 	switch (funcnum) {
129 	/* To speed life up, we implement commonly-used Glk functions
130 	   directly -- instead of bothering with the whole prototype
131 	   mess. */
132 
133 	case 0x0047: /* stream_set_current */
134 		if (numargs != 1)
135 			goto WrongArgNum;
136 		glk_stream_set_current(find_stream_by_id(arglist[0]));
137 		break;
138 	case 0x0048: /* stream_get_current */
139 		if (numargs != 0)
140 			goto WrongArgNum;
141 		retval = find_id_for_stream(glk_stream_get_current());
142 		break;
143 	case 0x0080: /* put_char */
144 		if (numargs != 1)
145 			goto WrongArgNum;
146 		glk_put_char(arglist[0] & 0xFF);
147 		break;
148 	case 0x0081: /* put_char_stream */
149 		if (numargs != 2)
150 			goto WrongArgNum;
151 		glk_put_char_stream(find_stream_by_id(arglist[0]), arglist[1] & 0xFF);
152 		break;
153 	case 0x00C0: /* select */
154 		/* call a library hook on every glk_select() */
155 		if (library_select_hook)
156 			library_select_hook(arglist[0]);
157 		/* but then fall through to full dispatcher, because there's no real
158 		   need for speed here */
159 		goto FullDispatcher;
160 	case 0x00A0: /* char_to_lower */
161 		if (numargs != 1)
162 			goto WrongArgNum;
163 		retval = glk_char_to_lower(arglist[0] & 0xFF);
164 		break;
165 	case 0x00A1: /* char_to_upper */
166 		if (numargs != 1)
167 			goto WrongArgNum;
168 		retval = glk_char_to_upper(arglist[0] & 0xFF);
169 		break;
170 	case 0x0128: /* put_char_uni */
171 		if (numargs != 1)
172 			goto WrongArgNum;
173 		glk_put_char_uni(arglist[0]);
174 		break;
175 	case 0x012B: /* put_char_stream_uni */
176 		if (numargs != 2)
177 			goto WrongArgNum;
178 		glk_put_char_stream_uni(find_stream_by_id(arglist[0]), arglist[1]);
179 		break;
180 
181 WrongArgNum:
182 		error("Wrong number of arguments to Glk function.");
183 		break;
184 
185 FullDispatcher:
186 	default: {
187 		/* Go through the full dispatcher prototype foo. */
188 		const char *proto, *cx;
189 		dispatch_splot_t splot;
190 		int argnum, argnum2;
191 
192 		/* Grab the string. */
193 		proto = gidispatch_prototype(funcnum);
194 		if (!proto)
195 			error("Unknown Glk function.");
196 
197 		splot.varglist = arglist;
198 		splot.numvargs = numargs;
199 		splot.retval = &retval;
200 
201 		/* The work goes in four phases. First, we figure out how many
202 		   arguments we want, and allocate space for the Glk argument
203 		   list. Then we go through the Glulx arguments and load them
204 		   into the Glk list. Then we call. Then we go through the
205 		   arguments again, unloading the data back into Glulx memory. */
206 
207 		/* Phase 0. */
208 		prepare_glk_args(proto, &splot);
209 
210 		/* Phase 1. */
211 		argnum = 0;
212 		cx = proto;
213 		parse_glk_args(&splot, &cx, 0, &argnum, 0, 0);
214 
215 		/* Phase 2. */
216 		gidispatch_call(funcnum, argnum, splot.garglist);
217 
218 		// WORKAROUND: For stream_open_file calls, for savegame handling
219 		// we need to store a copy of what the savegame description was,
220 		// for use when we actually generate the savefile contents
221 		if (funcnum == 0x42) {
222 			frefid_t fref = (frefid_t)splot.garglist[0]._opaqueref;
223 			_savegameDescription = fref->_description;
224 		}
225 
226 		/* Phase 3. */
227 		argnum2 = 0;
228 		cx = proto;
229 		unparse_glk_args(&splot, &cx, 0, &argnum2, 0, 0);
230 		if (argnum != argnum2)
231 			error("Argument counts did not match.");
232 
233 		break;
234 	}
235 	}
236 
237 	return retval;
238 }
239 
read_prefix(const char * cx,int * isref,int * isarray,int * passin,int * passout,int * nullok,int * isretained,int * isreturn)240 const char *Glulx::read_prefix(const char *cx, int *isref, int *isarray, int *passin, int *passout,
241 								int *nullok, int *isretained,  int *isreturn) {
242 	*isref = false;
243 	*passin = false;
244 	*passout = false;
245 	*nullok = true;
246 	*isarray = false;
247 	*isretained = false;
248 	*isreturn = false;
249 	while (1) {
250 		if (*cx == '<') {
251 			*isref = true;
252 			*passout = true;
253 		} else if (*cx == '>') {
254 			*isref = true;
255 			*passin = true;
256 		} else if (*cx == '&') {
257 			*isref = true;
258 			*passout = true;
259 			*passin = true;
260 		} else if (*cx == '+') {
261 			*nullok = false;
262 		} else if (*cx == ':') {
263 			*isref = true;
264 			*passout = true;
265 			*nullok = false;
266 			*isreturn = true;
267 		} else if (*cx == '#') {
268 			*isarray = true;
269 		} else if (*cx == '!') {
270 			*isretained = true;
271 		} else {
272 			break;
273 		}
274 		cx++;
275 	}
276 	return cx;
277 }
278 
prepare_glk_args(const char * proto,dispatch_splot_t * splot)279 void Glulx::prepare_glk_args(const char *proto, dispatch_splot_t *splot) {
280 	static gluniversal_t *garglist = nullptr;
281 	static int garglist_size = 0;
282 
283 	int ix;
284 	int numwanted, numvargswanted, maxargs;
285 	const char *cx;
286 
287 	cx = proto;
288 	numwanted = 0;
289 	while (*cx >= '0' && *cx <= '9') {
290 		numwanted = 10 * numwanted + (*cx - '0');
291 		cx++;
292 	}
293 	splot->numwanted = numwanted;
294 
295 	maxargs = 0;
296 	numvargswanted = 0;
297 	for (ix = 0; ix < numwanted; ix++) {
298 		int isref, passin, passout, nullok, isarray, isretained, isreturn;
299 		cx = read_prefix(cx, &isref, &isarray, &passin, &passout, &nullok,
300 		                 &isretained, &isreturn);
301 		if (isref) {
302 			maxargs += 2;
303 		} else {
304 			maxargs += 1;
305 		}
306 		if (!isreturn) {
307 			if (isarray) {
308 				numvargswanted += 2;
309 			} else {
310 				numvargswanted += 1;
311 			}
312 		}
313 
314 		if (*cx == 'I' || *cx == 'C') {
315 			cx += 2;
316 		} else if (*cx == 'Q') {
317 			cx += 2;
318 		} else if (*cx == 'S' || *cx == 'U') {
319 			cx += 1;
320 		} else if (*cx == '[') {
321 			int refdepth, nwx;
322 			cx++;
323 			nwx = 0;
324 			while (*cx >= '0' && *cx <= '9') {
325 				nwx = 10 * nwx + (*cx - '0');
326 				cx++;
327 			}
328 			maxargs += nwx; /* This is *only* correct because all structs contain
329 						 plain values. */
330 			refdepth = 1;
331 			while (refdepth > 0) {
332 				if (*cx == '[')
333 					refdepth++;
334 				else if (*cx == ']')
335 					refdepth--;
336 				cx++;
337 			}
338 		} else {
339 			error("Illegal format string.");
340 		}
341 	}
342 
343 	if (*cx != ':' && *cx != '\0')
344 		error("Illegal format string.");
345 
346 	splot->maxargs = maxargs;
347 
348 	if (splot->numvargs != numvargswanted)
349 		error("Wrong number of arguments to Glk function.");
350 
351 	if (garglist && garglist_size < maxargs) {
352 		glulx_free(garglist);
353 		garglist = nullptr;
354 		garglist_size = 0;
355 	}
356 	if (!garglist) {
357 		garglist_size = maxargs + 16;
358 		garglist = (gluniversal_t *)glulx_malloc(garglist_size
359 		           * sizeof(gluniversal_t));
360 	}
361 	if (!garglist)
362 		error("Unable to allocate storage for Glk arguments.");
363 
364 	splot->garglist = garglist;
365 }
366 
parse_glk_args(dispatch_splot_t * splot,const char ** proto,int depth,int * argnumptr,uint subaddress,int subpassin)367 void Glulx::parse_glk_args(dispatch_splot_t *splot, const char **proto, int depth, int *argnumptr,
368 							uint subaddress, int subpassin) {
369 	const char *cx;
370 	int ix, argx;
371 	int gargnum, numwanted;
372 	void *opref;
373 	gluniversal_t *garglist;
374 	uint *varglist;
375 
376 	garglist = splot->garglist;
377 	varglist = splot->varglist;
378 	gargnum = *argnumptr;
379 	cx = *proto;
380 
381 	numwanted = 0;
382 	while (*cx >= '0' && *cx <= '9') {
383 		numwanted = 10 * numwanted + (*cx - '0');
384 		cx++;
385 	}
386 
387 	for (argx = 0, ix = 0; argx < numwanted; argx++, ix++) {
388 		char typeclass;
389 		int skipval;
390 		int isref, passin, passout, nullok, isarray, isretained, isreturn;
391 		cx = read_prefix(cx, &isref, &isarray, &passin, &passout, &nullok,
392 		                 &isretained, &isreturn);
393 
394 		typeclass = *cx;
395 		cx++;
396 
397 		skipval = false;
398 		if (isref) {
399 			if (!isreturn && varglist[ix] == 0) {
400 				if (!nullok)
401 					error("Zero passed invalidly to Glk function.");
402 				garglist[gargnum]._ptrflag = false;
403 				gargnum++;
404 				skipval = true;
405 			} else {
406 				garglist[gargnum]._ptrflag = true;
407 				gargnum++;
408 			}
409 		}
410 		if (!skipval) {
411 			uint thisval;
412 
413 			if (typeclass == '[') {
414 
415 				parse_glk_args(splot, &cx, depth + 1, &gargnum, varglist[ix], passin);
416 
417 			} else if (isarray) {
418 				/* definitely isref */
419 
420 				switch (typeclass) {
421 				case 'C':
422 					/* This test checks for a giant array length, which is
423 					   deprecated. It displays a warning and cuts it down to
424 					   something reasonable. Future releases of this interpreter
425 					   may remove this test and go on to verify_array_addresses(),
426 					   which treats this case as a fatal error. */
427 					if (varglist[ix + 1] > endmem
428 					        || varglist[ix] + varglist[ix + 1] > endmem) {
429 						nonfatal_warning_i("Memory access was much too long -- perhaps a print_to_array call with only one argument", varglist[ix + 1]);
430 						varglist[ix + 1] = endmem - varglist[ix];
431 					}
432 					verify_array_addresses(varglist[ix], varglist[ix + 1], 1);
433 					garglist[gargnum]._array = CaptureCArray(varglist[ix], varglist[ix + 1], passin);
434 					gargnum++;
435 					ix++;
436 					garglist[gargnum]._uint = varglist[ix];
437 					gargnum++;
438 					cx++;
439 					break;
440 				case 'I':
441 					/* See comment above. */
442 					if (varglist[ix + 1] > endmem / 4
443 					        || varglist[ix + 1] > (endmem - varglist[ix]) / 4) {
444 						nonfatal_warning_i("Memory access was much too long -- perhaps a print_to_array call with only one argument", varglist[ix + 1]);
445 						varglist[ix + 1] = (endmem - varglist[ix]) / 4;
446 					}
447 					verify_array_addresses(varglist[ix], varglist[ix + 1], 4);
448 					garglist[gargnum]._array = CaptureIArray(varglist[ix], varglist[ix + 1], passin);
449 					gargnum++;
450 					ix++;
451 					garglist[gargnum]._uint = varglist[ix];
452 					gargnum++;
453 					cx++;
454 					break;
455 				case 'Q':
456 					/* This case was added after the giant arrays were deprecated,
457 					   so we don't bother to allow for that case. We just verify
458 					   the length. */
459 					verify_array_addresses(varglist[ix], varglist[ix + 1], 4);
460 					garglist[gargnum]._array = CapturePtrArray(varglist[ix], varglist[ix + 1], (*cx - 'a'), passin);
461 					gargnum++;
462 					ix++;
463 					garglist[gargnum]._uint = varglist[ix];
464 					gargnum++;
465 					cx++;
466 					break;
467 				default:
468 					error("Illegal format string.");
469 					break;
470 				}
471 			} else {
472 				/* a plain value or a reference to one. */
473 
474 				if (isreturn) {
475 					thisval = 0;
476 				} else if (depth > 0) {
477 					/* Definitely not isref or isarray. */
478 					if (subpassin)
479 						thisval = ReadStructField(subaddress, ix);
480 					else
481 						thisval = 0;
482 				} else if (isref) {
483 					if (passin)
484 						thisval = ReadMemory(varglist[ix]);
485 					else
486 						thisval = 0;
487 				} else {
488 					thisval = varglist[ix];
489 				}
490 
491 				switch (typeclass) {
492 				case 'I':
493 					if (*cx == 'u')
494 						garglist[gargnum]._uint = (uint)(thisval);
495 					else if (*cx == 's')
496 						garglist[gargnum]._sint = (int)(thisval);
497 					else
498 						error("Illegal format string.");
499 					gargnum++;
500 					cx++;
501 					break;
502 				case 'Q':
503 					if (thisval) {
504 						opref = classes_get(*cx - 'a', thisval);
505 						if (!opref) {
506 							error("Reference to nonexistent Glk object.");
507 						}
508 					} else {
509 						opref = nullptr;
510 					}
511 					garglist[gargnum]._opaqueref = opref;
512 					gargnum++;
513 					cx++;
514 					break;
515 				case 'C':
516 					if (*cx == 'u')
517 						garglist[gargnum]._uch = (unsigned char)(thisval);
518 					else if (*cx == 's')
519 						garglist[gargnum]._sch = (signed char)(thisval);
520 					else if (*cx == 'n')
521 						garglist[gargnum]._ch = (char)(thisval);
522 					else
523 						error("Illegal format string.");
524 					gargnum++;
525 					cx++;
526 					break;
527 				case 'S':
528 					garglist[gargnum]._charstr = DecodeVMString(thisval);
529 					gargnum++;
530 					break;
531 #ifdef GLK_MODULE_UNICODE
532 				case 'U':
533 					garglist[gargnum]._unicharstr = DecodeVMUstring(thisval);
534 					gargnum++;
535 					break;
536 #endif /* GLK_MODULE_UNICODE */
537 				default:
538 					error("Illegal format string.");
539 					break;
540 				}
541 			}
542 		} else {
543 			/* We got a null reference, so we have to skip the format element. */
544 			if (typeclass == '[') {
545 				int numsubwanted, refdepth;
546 				numsubwanted = 0;
547 				while (*cx >= '0' && *cx <= '9') {
548 					numsubwanted = 10 * numsubwanted + (*cx - '0');
549 					cx++;
550 				}
551 				refdepth = 1;
552 				while (refdepth > 0) {
553 					if (*cx == '[')
554 						refdepth++;
555 					else if (*cx == ']')
556 						refdepth--;
557 					cx++;
558 				}
559 			} else if (typeclass == 'S' || typeclass == 'U') {
560 				/* leave it */
561 			} else {
562 				cx++;
563 				if (isarray)
564 					ix++;
565 			}
566 		}
567 	}
568 
569 	if (depth > 0) {
570 		if (*cx != ']')
571 			error("Illegal format string.");
572 		cx++;
573 	} else {
574 		if (*cx != ':' && *cx != '\0')
575 			error("Illegal format string.");
576 	}
577 
578 	*proto = cx;
579 	*argnumptr = gargnum;
580 }
581 
unparse_glk_args(dispatch_splot_t * splot,const char ** proto,int depth,int * argnumptr,uint subaddress,int subpassout)582 void Glulx::unparse_glk_args(dispatch_splot_t *splot, const char **proto, int depth,
583 							  int *argnumptr, uint subaddress, int subpassout) {
584 	const char *cx;
585 	int ix, argx;
586 	int gargnum, numwanted;
587 	void *opref;
588 	gluniversal_t *garglist;
589 	uint *varglist;
590 
591 	garglist = splot->garglist;
592 	varglist = splot->varglist;
593 	gargnum = *argnumptr;
594 	cx = *proto;
595 
596 	numwanted = 0;
597 	while (*cx >= '0' && *cx <= '9') {
598 		numwanted = 10 * numwanted + (*cx - '0');
599 		cx++;
600 	}
601 
602 	for (argx = 0, ix = 0; argx < numwanted; argx++, ix++) {
603 		char typeclass;
604 		int skipval;
605 		int isref, passin, passout, nullok, isarray, isretained, isreturn;
606 		cx = read_prefix(cx, &isref, &isarray, &passin, &passout, &nullok,
607 		                 &isretained, &isreturn);
608 
609 		typeclass = *cx;
610 		cx++;
611 
612 		skipval = false;
613 		if (isref) {
614 			if (!isreturn && varglist[ix] == 0) {
615 				if (!nullok)
616 					error("Zero passed invalidly to Glk function.");
617 				garglist[gargnum]._ptrflag = false;
618 				gargnum++;
619 				skipval = true;
620 			} else {
621 				garglist[gargnum]._ptrflag = true;
622 				gargnum++;
623 			}
624 		}
625 		if (!skipval) {
626 			uint thisval = 0;
627 
628 			if (typeclass == '[') {
629 
630 				unparse_glk_args(splot, &cx, depth + 1, &gargnum, varglist[ix], passout);
631 
632 			} else if (isarray) {
633 				/* definitely isref */
634 
635 				switch (typeclass) {
636 				case 'C':
637 					ReleaseCArray((char *)garglist[gargnum]._array, varglist[ix], varglist[ix + 1], passout);
638 					gargnum++;
639 					ix++;
640 					gargnum++;
641 					cx++;
642 					break;
643 				case 'I':
644 					ReleaseIArray((uint *)garglist[gargnum]._array, varglist[ix], varglist[ix + 1], passout);
645 					gargnum++;
646 					ix++;
647 					gargnum++;
648 					cx++;
649 					break;
650 				case 'Q':
651 					ReleasePtrArray((void **)garglist[gargnum]._array, varglist[ix], varglist[ix + 1], (*cx - 'a'), passout);
652 					gargnum++;
653 					ix++;
654 					gargnum++;
655 					cx++;
656 					break;
657 				default:
658 					error("Illegal format string.");
659 					break;
660 				}
661 			} else {
662 				/* a plain value or a reference to one. */
663 
664 				if (isreturn || (depth > 0 && subpassout) || (isref && passout)) {
665 					skipval = false;
666 				} else {
667 					skipval = true;
668 				}
669 
670 				switch (typeclass) {
671 				case 'I':
672 					if (!skipval) {
673 						if (*cx == 'u')
674 							thisval = (uint)garglist[gargnum]._uint;
675 						else if (*cx == 's')
676 							thisval = (uint)garglist[gargnum]._sint;
677 						else
678 							error("Illegal format string.");
679 					}
680 					gargnum++;
681 					cx++;
682 					break;
683 				case 'Q':
684 					if (!skipval) {
685 						opref = garglist[gargnum]._opaqueref;
686 						if (opref) {
687 							gidispatch_rock_t objrock = gidispatch_get_objrock(opref, *cx - 'a');
688 							assert(objrock.ptr);
689 							thisval = ((classref_t *)objrock.ptr)->id;
690 						} else {
691 							thisval = 0;
692 						}
693 					}
694 					gargnum++;
695 					cx++;
696 					break;
697 				case 'C':
698 					if (!skipval) {
699 						if (*cx == 'u')
700 							thisval = (uint)garglist[gargnum]._uch;
701 						else if (*cx == 's')
702 							thisval = (uint)garglist[gargnum]._sch;
703 						else if (*cx == 'n')
704 							thisval = (uint)garglist[gargnum]._ch;
705 						else
706 							error("Illegal format string.");
707 					}
708 					gargnum++;
709 					cx++;
710 					break;
711 				case 'S':
712 					if (garglist[gargnum]._charstr)
713 						ReleaseVMString(garglist[gargnum]._charstr);
714 					gargnum++;
715 					break;
716 #ifdef GLK_MODULE_UNICODE
717 				case 'U':
718 					if (garglist[gargnum]._unicharstr)
719 						ReleaseVMUstring(garglist[gargnum]._unicharstr);
720 					gargnum++;
721 					break;
722 #endif /* GLK_MODULE_UNICODE */
723 				default:
724 					error("Illegal format string.");
725 					break;
726 				}
727 
728 				if (isreturn) {
729 					*(splot->retval) = thisval;
730 				} else if (depth > 0) {
731 					/* Definitely not isref or isarray. */
732 					if (subpassout)
733 						WriteStructField(subaddress, ix, thisval);
734 				} else if (isref) {
735 					if (passout)
736 						WriteMemory(varglist[ix], thisval);
737 				}
738 			}
739 		} else {
740 			/* We got a null reference, so we have to skip the format element. */
741 			if (typeclass == '[') {
742 				int numsubwanted, refdepth;
743 				numsubwanted = 0;
744 				while (*cx >= '0' && *cx <= '9') {
745 					numsubwanted = 10 * numsubwanted + (*cx - '0');
746 					cx++;
747 				}
748 				refdepth = 1;
749 				while (refdepth > 0) {
750 					if (*cx == '[')
751 						refdepth++;
752 					else if (*cx == ']')
753 						refdepth--;
754 					cx++;
755 				}
756 			} else if (typeclass == 'S' || typeclass == 'U') {
757 				/* leave it */
758 			} else {
759 				cx++;
760 				if (isarray)
761 					ix++;
762 			}
763 		}
764 	}
765 
766 	if (depth > 0) {
767 		if (*cx != ']')
768 			error("Illegal format string.");
769 		cx++;
770 	} else {
771 		if (*cx != ':' && *cx != '\0')
772 			error("Illegal format string.");
773 	}
774 
775 	*proto = cx;
776 	*argnumptr = gargnum;
777 }
778 
find_stream_by_id(uint objid)779 strid_t Glulx::find_stream_by_id(uint objid) {
780 	if (!objid)
781 		return nullptr;
782 
783 	// Recall that class 1 ("b") is streams
784 	return (strid_t)classes_get(gidisp_Class_Stream, objid);
785 }
786 
find_id_for_window(winid_t win)787 uint Glulx::find_id_for_window(winid_t win) {
788 	gidispatch_rock_t objrock;
789 
790 	if (!win)
791 		return 0;
792 
793 	objrock = gidispatch_get_objrock(win, gidisp_Class_Window);
794 	if (!objrock.ptr)
795 		return 0;
796 	return ((classref_t *)objrock.ptr)->id;
797 }
798 
find_id_for_stream(strid_t str)799 uint Glulx::find_id_for_stream(strid_t str) {
800 	gidispatch_rock_t objrock;
801 
802 	if (!str)
803 		return 0;
804 
805 	objrock = gidispatch_get_objrock(str, gidisp_Class_Stream);
806 	if (!objrock.ptr)
807 		return 0;
808 	return ((classref_t *)objrock.ptr)->id;
809 }
810 
find_id_for_fileref(frefid_t fref)811 uint Glulx::find_id_for_fileref(frefid_t fref) {
812 	gidispatch_rock_t objrock;
813 
814 	if (!fref)
815 		return 0;
816 
817 	objrock = gidispatch_get_objrock(fref, gidisp_Class_Fileref);
818 	if (!objrock.ptr)
819 		return 0;
820 	return ((classref_t *)objrock.ptr)->id;
821 }
822 
find_id_for_schannel(schanid_t schan)823 uint Glulx::find_id_for_schannel(schanid_t schan) {
824 	gidispatch_rock_t objrock;
825 
826 	if (!schan)
827 		return 0;
828 
829 	objrock = gidispatch_get_objrock(schan, gidisp_Class_Schannel);
830 	if (!objrock.ptr)
831 		return 0;
832 	return ((classref_t *)objrock.ptr)->id;
833 }
834 
new_classtable(uint firstid)835 classtable_t *Glulx::new_classtable(uint firstid) {
836 	int ix;
837 	classtable_t *ctab = (classtable_t *)glulx_malloc(sizeof(classtable_t));
838 	if (!ctab)
839 		return nullptr;
840 
841 	for (ix = 0; ix < CLASSHASH_SIZE; ix++)
842 		ctab->bucket[ix] = nullptr;
843 
844 	ctab->lastid = firstid;
845 
846 	return ctab;
847 }
848 
classes_get(int classid,uint objid)849 void *Glulx::classes_get(int classid, uint objid) {
850 	classtable_t *ctab;
851 	classref_t *cref;
852 	if (classid < 0 || classid >= num_classes)
853 		return nullptr;
854 	ctab = classes[classid];
855 	cref = ctab->bucket[objid % CLASSHASH_SIZE];
856 	for (; cref; cref = cref->next) {
857 		if (cref->id == objid)
858 			return cref->obj;
859 	}
860 	return nullptr;
861 }
862 
classes_put(int classid,void * obj,uint origid)863 classref_t *Glulx::classes_put(int classid, void *obj, uint origid) {
864 	int bucknum;
865 	classtable_t *ctab;
866 	classref_t *cref;
867 	if (classid < 0 || classid >= num_classes)
868 		return nullptr;
869 	ctab = classes[classid];
870 	cref = (classref_t *)glulx_malloc(sizeof(classref_t));
871 	if (!cref)
872 		return nullptr;
873 	cref->obj = obj;
874 	if (!origid) {
875 		cref->id = ctab->lastid;
876 		ctab->lastid++;
877 	} else {
878 		cref->id = origid;
879 		if (ctab->lastid <= origid)
880 			ctab->lastid = origid + 1;
881 	}
882 	bucknum = cref->id % CLASSHASH_SIZE;
883 	cref->bucknum = bucknum;
884 	cref->next = ctab->bucket[bucknum];
885 	ctab->bucket[bucknum] = cref;
886 	return cref;
887 }
888 
classes_remove(int classid,void * obj)889 void Glulx::classes_remove(int classid, void *obj) {
890 	classtable_t *ctab;
891 	classref_t *cref;
892 	classref_t **crefp;
893 	gidispatch_rock_t objrock;
894 	if (classid < 0 || classid >= num_classes)
895 		return;
896 	ctab = classes[classid];
897 	objrock = gidispatch_get_objrock(obj, classid);
898 	cref = (classref_t *)objrock.ptr;
899 	if (!cref)
900 		return;
901 	crefp = &(ctab->bucket[cref->bucknum]);
902 	for (; *crefp; crefp = &((*crefp)->next)) {
903 		if ((*crefp) == cref) {
904 			*crefp = cref->next;
905 			if (!cref->obj) {
906 				nonfatal_warning("attempt to free nullptr object!");
907 			}
908 			cref->obj = nullptr;
909 			cref->id = 0;
910 			cref->next = nullptr;
911 			glulx_free(cref);
912 			return;
913 		}
914 	}
915 	return;
916 }
917 
glulxe_classtable_register(void * obj,uint objclass)918 gidispatch_rock_t Glulx::glulxe_classtable_register(void *obj,  uint objclass) {
919 	classref_t *cref;
920 	gidispatch_rock_t objrock;
921 	cref = classes_put(objclass, obj, 0);
922 	objrock.ptr = cref;
923 	return objrock;
924 }
925 
glulxe_classtable_unregister(void * obj,uint objclass,gidispatch_rock_t objrock)926 void Glulx::glulxe_classtable_unregister(void *obj, uint objclass,
927 		gidispatch_rock_t objrock) {
928 	classes_remove(objclass, obj);
929 }
930 
glulxe_classtable_register_existing(void * obj,uint objclass,uint dispid)931 gidispatch_rock_t Glulx::glulxe_classtable_register_existing(void *obj, uint objclass, uint dispid) {
932 	classref_t *cref;
933 	gidispatch_rock_t objrock;
934 	cref = classes_put(objclass, obj, dispid);
935 	objrock.ptr = cref;
936 	return objrock;
937 }
938 
grab_temp_c_array(uint addr,uint len,int passin)939 char *Glulx::grab_temp_c_array(uint addr, uint len, int passin) {
940 	arrayref_t *arref = nullptr;
941 	char *arr = nullptr;
942 	uint ix, addr2;
943 
944 	if (len) {
945 		arr = (char *)glulx_malloc(len * sizeof(char));
946 		arref = (arrayref_t *)glulx_malloc(sizeof(arrayref_t));
947 		if (!arr || !arref)
948 			error("Unable to allocate space for array argument to Glk call.");
949 
950 		arref->array = arr;
951 		arref->addr = addr;
952 		arref->elemsize = 1;
953 		arref->retained = false;
954 		arref->len = len;
955 		arref->next = arrays;
956 		arrays = arref;
957 
958 		if (passin) {
959 			for (ix = 0, addr2 = addr; ix < len; ix++, addr2 += 1) {
960 				arr[ix] = Mem1(addr2);
961 			}
962 		}
963 	}
964 
965 	return arr;
966 }
967 
release_temp_c_array(char * arr,uint addr,uint len,int passout)968 void Glulx::release_temp_c_array(char *arr, uint addr, uint len, int passout) {
969 	arrayref_t *arref = nullptr;
970 	arrayref_t **aptr;
971 	uint ix, val, addr2;
972 
973 	if (arr) {
974 		for (aptr = (&arrays); (*aptr); aptr = (&((*aptr)->next))) {
975 			if ((*aptr)->array == arr)
976 				break;
977 		}
978 		arref = *aptr;
979 		if (!arref)
980 			error("Unable to re-find array argument in Glk call.");
981 		if (arref->addr != addr || arref->len != len)
982 			error("Mismatched array argument in Glk call.");
983 
984 		if (arref->retained) {
985 			return;
986 		}
987 
988 		*aptr = arref->next;
989 		arref->next = nullptr;
990 
991 		if (passout) {
992 			for (ix = 0, addr2 = addr; ix < len; ix++, addr2 += 1) {
993 				val = arr[ix];
994 				MemW1(addr2, val);
995 			}
996 		}
997 		glulx_free(arr);
998 		glulx_free(arref);
999 	}
1000 }
1001 
grab_temp_i_array(uint addr,uint len,int passin)1002 uint *Glulx::grab_temp_i_array(uint addr, uint len, int passin) {
1003 	arrayref_t *arref = nullptr;
1004 	uint *arr = nullptr;
1005 	uint ix, addr2;
1006 
1007 	if (len) {
1008 		arr = (uint *)glulx_malloc(len * sizeof(uint));
1009 		arref = (arrayref_t *)glulx_malloc(sizeof(arrayref_t));
1010 		if (!arr || !arref)
1011 			error("Unable to allocate space for array argument to Glk call.");
1012 
1013 		arref->array = arr;
1014 		arref->addr = addr;
1015 		arref->elemsize = 4;
1016 		arref->retained = false;
1017 		arref->len = len;
1018 		arref->next = arrays;
1019 		arrays = arref;
1020 
1021 		if (passin) {
1022 			for (ix = 0, addr2 = addr; ix < len; ix++, addr2 += 4) {
1023 				arr[ix] = Mem4(addr2);
1024 			}
1025 		}
1026 	}
1027 
1028 	return arr;
1029 }
1030 
release_temp_i_array(uint * arr,uint addr,uint len,int passout)1031 void Glulx::release_temp_i_array(uint *arr, uint addr, uint len, int passout) {
1032 	arrayref_t *arref = nullptr;
1033 	arrayref_t **aptr;
1034 	uint ix, val, addr2;
1035 
1036 	if (arr) {
1037 		for (aptr = (&arrays); (*aptr); aptr = (&((*aptr)->next))) {
1038 			if ((*aptr)->array == arr)
1039 				break;
1040 		}
1041 		arref = *aptr;
1042 		if (!arref)
1043 			error("Unable to re-find array argument in Glk call.");
1044 		if (arref->addr != addr || arref->len != len)
1045 			error("Mismatched array argument in Glk call.");
1046 
1047 		if (arref->retained) {
1048 			return;
1049 		}
1050 
1051 		*aptr = arref->next;
1052 		arref->next = nullptr;
1053 
1054 		if (passout) {
1055 			for (ix = 0, addr2 = addr; ix < len; ix++, addr2 += 4) {
1056 				val = arr[ix];
1057 				MemW4(addr2, val);
1058 			}
1059 		}
1060 		glulx_free(arr);
1061 		glulx_free(arref);
1062 	}
1063 }
1064 
grab_temp_ptr_array(uint addr,uint len,int objclass,int passin)1065 void **Glulx::grab_temp_ptr_array(uint addr, uint len, int objclass, int passin) {
1066 	arrayref_t *arref = nullptr;
1067 	void **arr = nullptr;
1068 	uint ix, addr2;
1069 
1070 	if (len) {
1071 		arr = (void **)glulx_malloc(len * sizeof(void *));
1072 		arref = (arrayref_t *)glulx_malloc(sizeof(arrayref_t));
1073 		if (!arr || !arref)
1074 			error("Unable to allocate space for array argument to Glk call.");
1075 
1076 		arref->array = arr;
1077 		arref->addr = addr;
1078 		arref->elemsize = sizeof(void *);
1079 		arref->retained = false;
1080 		arref->len = len;
1081 		arref->next = arrays;
1082 		arrays = arref;
1083 
1084 		if (passin) {
1085 			for (ix = 0, addr2 = addr; ix < len; ix++, addr2 += 4) {
1086 				uint thisval = Mem4(addr2);
1087 				if (thisval)
1088 					arr[ix] = classes_get(objclass, thisval);
1089 				else
1090 					arr[ix] = nullptr;
1091 			}
1092 		}
1093 	}
1094 
1095 	return arr;
1096 }
1097 
release_temp_ptr_array(void ** arr,uint addr,uint len,int objclass,int passout)1098 void Glulx::release_temp_ptr_array(void **arr, uint addr, uint len, int objclass, int passout) {
1099 	arrayref_t *arref = nullptr;
1100 	arrayref_t **aptr;
1101 	uint ix, val, addr2;
1102 
1103 	if (arr) {
1104 		for (aptr = (&arrays); (*aptr); aptr = (&((*aptr)->next))) {
1105 			if ((*aptr)->array == arr)
1106 				break;
1107 		}
1108 		arref = *aptr;
1109 		if (!arref)
1110 			error("Unable to re-find array argument in Glk call.");
1111 		if (arref->addr != addr || arref->len != len)
1112 			error("Mismatched array argument in Glk call.");
1113 
1114 		if (arref->retained) {
1115 			return;
1116 		}
1117 
1118 		*aptr = arref->next;
1119 		arref->next = nullptr;
1120 
1121 		if (passout) {
1122 			for (ix = 0, addr2 = addr; ix < len; ix++, addr2 += 4) {
1123 				void *opref = arr[ix];
1124 				if (opref) {
1125 					gidispatch_rock_t objrock =
1126 					    gidispatch_get_objrock(opref, objclass);
1127 					val = ((classref_t *)objrock.ptr)->id;
1128 				} else {
1129 					val = 0;
1130 				}
1131 				MemW4(addr2, val);
1132 			}
1133 		}
1134 		glulx_free(arr);
1135 		glulx_free(arref);
1136 	}
1137 }
1138 
glulxe_retained_register(void * array,uint len,const char * typecode)1139 gidispatch_rock_t Glulx::glulxe_retained_register(void *array, uint len, const char *typecode) {
1140 	gidispatch_rock_t rock;
1141 	arrayref_t *arref = nullptr;
1142 	arrayref_t **aptr;
1143 	uint elemsize = 0;
1144 
1145 	if (typecode[4] == 'C')
1146 		elemsize = 1;
1147 	else if (typecode[4] == 'I')
1148 		elemsize = 4;
1149 
1150 	if (!elemsize || array == nullptr) {
1151 		rock.ptr = nullptr;
1152 		return rock;
1153 	}
1154 
1155 	for (aptr = (&arrays); (*aptr); aptr = (&((*aptr)->next))) {
1156 		if ((*aptr)->array == array)
1157 			break;
1158 	}
1159 	arref = *aptr;
1160 	if (!arref)
1161 		error("Unable to re-find array argument in Glk call.");
1162 	if (arref->elemsize != elemsize || arref->len != len)
1163 		error("Mismatched array argument in Glk call.");
1164 
1165 	arref->retained = true;
1166 
1167 	rock.ptr = arref;
1168 	return rock;
1169 }
1170 
glulxe_retained_unregister(void * array,uint len,const char * typecode,gidispatch_rock_t objrock)1171 void Glulx::glulxe_retained_unregister(void *array, uint len, const  char *typecode, gidispatch_rock_t objrock) {
1172 	arrayref_t *arref = nullptr;
1173 	arrayref_t **aptr;
1174 	uint ix, addr2, val;
1175 	uint elemsize = 0;
1176 
1177 	// TODO: See if original GLULXE has code I'm overlooking to cleanly close everything before freeing memmap
1178 	if (!memmap)
1179 		return;
1180 
1181 	if (typecode[4] == 'C')
1182 		elemsize = 1;
1183 	else if (typecode[4] == 'I')
1184 		elemsize = 4;
1185 
1186 	if (!elemsize || array == nullptr) {
1187 		return;
1188 	}
1189 
1190 	for (aptr = (&arrays); (*aptr); aptr = (&((*aptr)->next))) {
1191 		if ((*aptr)->array == array)
1192 			break;
1193 	}
1194 	arref = *aptr;
1195 	if (!arref)
1196 		error("Unable to re-find array argument in Glk call.");
1197 	if (arref != objrock.ptr)
1198 		error("Mismatched array reference in Glk call.");
1199 	if (!arref->retained)
1200 		error("Unretained array reference in Glk call.");
1201 	if (arref->elemsize != elemsize || arref->len != len)
1202 		error("Mismatched array argument in Glk call.");
1203 
1204 	*aptr = arref->next;
1205 	arref->next = nullptr;
1206 
1207 	if (elemsize == 1) {
1208 		for (ix = 0, addr2 = arref->addr; ix < arref->len; ix++, addr2 += 1) {
1209 			val = ((char *)array)[ix];
1210 			MemW1(addr2, val);
1211 		}
1212 	} else if (elemsize == 4) {
1213 		for (ix = 0, addr2 = arref->addr; ix < arref->len; ix++, addr2 += 4) {
1214 			val = ((uint *)array)[ix];
1215 			MemW4(addr2, val);
1216 		}
1217 	}
1218 
1219 	glulx_free(array);
1220 	glulx_free(arref);
1221 }
1222 
glulxe_array_locate(void * array,uint len,char * typecode,gidispatch_rock_t objrock,int * elemsizeref)1223 long Glulx::glulxe_array_locate(void *array, uint len, char *typecode, gidispatch_rock_t objrock, int *elemsizeref) {
1224 	arrayref_t *arref = nullptr;
1225 	arrayref_t **aptr;
1226 	uint elemsize = 0;
1227 
1228 	if (typecode[4] == 'C')
1229 		elemsize = 1;
1230 	else if (typecode[4] == 'I')
1231 		elemsize = 4;
1232 
1233 	if (!elemsize || array == nullptr) {
1234 		*elemsizeref = 0; /* No need to save the array separately */
1235 		return (unsigned char *)array - memmap;
1236 	}
1237 
1238 	for (aptr = (&arrays); (*aptr); aptr = (&((*aptr)->next))) {
1239 		if ((*aptr)->array == array)
1240 			break;
1241 	}
1242 	arref = *aptr;
1243 	if (!arref)
1244 		error("Unable to re-find array argument in array_locate.");
1245 	if (arref != objrock.ptr)
1246 		error("Mismatched array reference in array_locate.");
1247 	if (!arref->retained)
1248 		error("Unretained array reference in array_locate.");
1249 	if (arref->elemsize != elemsize || arref->len != len)
1250 		error("Mismatched array argument in array_locate.");
1251 
1252 	*elemsizeref = arref->elemsize;
1253 	return arref->addr;
1254 }
1255 
glulxe_array_restore(long bufkey,uint len,char * typecode,void ** arrayref)1256 gidispatch_rock_t Glulx::glulxe_array_restore(long bufkey, uint len, char *typecode, void **arrayref) {
1257 	gidispatch_rock_t rock;
1258 	int elemsize = 0;
1259 
1260 	if (typecode[4] == 'C')
1261 		elemsize = 1;
1262 	else if (typecode[4] == 'I')
1263 		elemsize = 4;
1264 
1265 	if (!elemsize) {
1266 		unsigned char *buf = memmap + bufkey;
1267 		*arrayref = buf;
1268 		rock.ptr = nullptr;
1269 		return rock;
1270 	}
1271 
1272 	if (elemsize == 1) {
1273 		char *cbuf = grab_temp_c_array(bufkey, len, false);
1274 		rock = glulxe_retained_register(cbuf, len, typecode);
1275 		*arrayref = cbuf;
1276 	} else {
1277 		uint *ubuf = grab_temp_i_array(bufkey, len, false);
1278 		rock = glulxe_retained_register(ubuf, len, typecode);
1279 		*arrayref = ubuf;
1280 	}
1281 	return rock;
1282 }
1283 
set_library_select_hook(void (* func)(uint))1284 void Glulx::set_library_select_hook(void (*func)(uint)) {
1285 	library_select_hook = func;
1286 }
1287 
get_game_id()1288 char *Glulx::get_game_id() {
1289 	/* This buffer gets rewritten on every call, but that's okay -- the caller
1290 	   is supposed to copy out the result. */
1291 	static char buf[2 * 64 + 2];
1292 	int ix, jx;
1293 
1294 	if (!memmap)
1295 		return nullptr;
1296 
1297 	for (ix = 0, jx = 0; ix < 64; ix++) {
1298 		char ch = memmap[ix];
1299 		int val = ((ch >> 4) & 0x0F);
1300 		buf[jx++] = ((val < 10) ? (val + '0') : (val + 'A' - 10));
1301 		val = (ch & 0x0F);
1302 		buf[jx++] = ((val < 10) ? (val + '0') : (val + 'A' - 10));
1303 	}
1304 	buf[jx++] = '\0';
1305 
1306 	return buf;
1307 }
1308 
ReadMemory(uint addr)1309 uint Glulx::ReadMemory(uint addr) {
1310 	if (addr == 0xffffffff) {
1311 		stackptr -= 4;
1312 		return Stk4(stackptr);
1313 	} else {
1314 		return Mem4(addr);
1315 	}
1316 }
1317 
WriteMemory(uint addr,uint val)1318 void Glulx::WriteMemory(uint addr, uint val) {
1319 	if (addr == 0xffffffff) {
1320 		StkW4(stackptr, (val));
1321 		stackptr += 4;
1322 	} else {
1323 		MemW4(addr, val);
1324 	}
1325 }
1326 
CaptureCArray(uint addr,uint len,int passin)1327 char *Glulx::CaptureCArray(uint addr, uint len, int passin) {
1328 	return grab_temp_c_array(addr, len, passin);
1329 }
1330 
ReleaseCArray(char * ptr,uint addr,uint len,int passout)1331 void Glulx::ReleaseCArray(char *ptr, uint addr, uint len, int passout) {
1332 	release_temp_c_array(ptr, addr, len, passout);
1333 }
1334 
CaptureIArray(uint addr,uint len,int passin)1335 uint *Glulx::CaptureIArray(uint addr, uint len, int passin) {
1336 	return grab_temp_i_array(addr, len, passin);
1337 }
1338 
ReleaseIArray(uint * ptr,uint addr,uint len,int passout)1339 void Glulx::ReleaseIArray(uint *ptr, uint addr, uint len, int passout) {
1340 	release_temp_i_array(ptr, addr, len, passout);
1341 }
1342 
CapturePtrArray(uint addr,uint len,int objclass,int passin)1343 void **Glulx::CapturePtrArray(uint addr, uint len, int objclass, int passin) {
1344 	return grab_temp_ptr_array(addr, len, objclass, passin);
1345 }
1346 
ReleasePtrArray(void ** ptr,uint addr,uint len,int objclass,int passout)1347 void Glulx::ReleasePtrArray(void **ptr, uint addr, uint len, int objclass, int passout) {
1348 	return release_temp_ptr_array(ptr, addr, len, objclass, passout);
1349 }
1350 
ReadStructField(uint addr,uint fieldnum)1351 uint Glulx::ReadStructField(uint addr, uint fieldnum) {
1352 	if (addr == 0xffffffff) {
1353 		stackptr -= 4;
1354 		return Stk4(stackptr);
1355 	} else {
1356 		return Mem4(addr + (fieldnum * 4));
1357 	}
1358 }
1359 
WriteStructField(uint addr,uint fieldnum,uint val)1360 void Glulx::WriteStructField(uint addr, uint fieldnum, uint val) {
1361 	if (addr == 0xffffffff) {
1362 		StkW4(stackptr, val);
1363 		stackptr += 4;
1364 	} else {
1365 		MemW4(addr + (fieldnum * 4), val);
1366 	}
1367 }
1368 
DecodeVMString(uint addr)1369 char *Glulx::DecodeVMString(uint addr) {
1370 	return make_temp_string(addr);
1371 }
1372 
ReleaseVMString(char * ptr)1373 void Glulx::ReleaseVMString(char *ptr) {
1374 	free_temp_string(ptr);
1375 }
1376 
DecodeVMUstring(uint addr)1377 uint32 *Glulx::DecodeVMUstring(uint addr) {
1378 	return make_temp_ustring(addr);
1379 }
1380 
ReleaseVMUstring(uint32 * ptr)1381 void Glulx::ReleaseVMUstring(uint32 *ptr) {
1382 	free_temp_ustring(ptr);
1383 }
1384 
1385 } // End of namespace Glulx
1386 } // End of namespace Glk
1387