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