1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "glk/agt/agility.h"
24 #include "glk/quetzal.h"
25 #include "common/textconsole.h"
26 
27 namespace Glk {
28 namespace AGT {
29 
30 /* This includes wrappers for malloc, realloc, strdup, and free
31 	 that exit gracefully if we run out of memory. */
32 /* There are also various utilities:
33 	concdup:  Creates a new string that is concatation of two others.
34 	strcasecmp: case insensitive comparison of strings
35 	strncasecmp: case insensitive compare of first n characters of strings
36 	fixsign16, fixsign32: routines to assemble signed ints out of
37 		  individual bytes in an endian-free way. */
38 /* Also buffered file i/o routines and some misc. file utilites. */
39 
40 #ifdef force16
41 #undef int
42 #endif
43 
44 #ifdef force16
45 #define int short
46 #endif
47 
rangefix(long n)48 long rangefix(long n) {
49 	if (n > 0) return n;
50 	return 0;
51 }
52 
53 /*-------------------------------------------------------------------*/
54 /* Sign fixing routines, to build a signed 16- and 32-bit quantities */
55 /* out of their component bytes.                                     */
56 /*-------------------------------------------------------------------*/
57 
58 #ifndef FAST_FIXSIGN
fixsign16(uchar n1,uchar n2)59 short fixsign16(uchar n1, uchar n2) {
60 	rbool sflag;
61 	short n;
62 
63 	if (n2 > 0x80) {
64 		n2 &= 0x7F;
65 		sflag = 1;
66 	} else sflag = 0;
67 
68 	n = n1 + (n2 << 8);
69 	if (sflag) n = n - 0x7fff - 1;
70 	return n;
71 }
72 
fixsign32(uchar n1,uchar n2,uchar n3,uchar n4)73 long fixsign32(uchar n1, uchar n2, uchar n3, uchar n4) {
74 	rbool sflag;
75 	long n;
76 
77 	if (n4 > 0x80) {
78 		n4 &= 0x7F;
79 		sflag = 1;
80 	} else sflag = 0;
81 
82 	n = n1 + (((long)n2) << 8) + (((long)n3) << 16) + (((long)n4) << 24);
83 	if (sflag) n = n - 0x7fffffffL - 1L;
84 	return n;
85 }
86 #endif
87 
88 
89 
90 /*----------------------------------------------------------------------*/
91 /* rprintf(), uses writestr for output           */
92 /*  This function is used mainly for diagnostic information */
93 /* There should be no newlines in the format string or in any of the */
94 /*  arguments as those could confuse writestr, except for the last */
95 /*  character in the string which can be a newline. */
96 /*----------------------------------------------------------------------*/
97 
rprintf(const char * fmt,...)98 void rprintf(const char *fmt, ...) {
99 	int i;
100 	char s[100];
101 	va_list args;
102 
103 	va_start(args, fmt);
104 	vsprintf(s, fmt, args);
105 	va_end(args);
106 	i = strlen(s) - 1;
107 	if (i >= 0 && s[i] == '\n') {
108 		s[i] = 0;
109 		writeln(s);
110 	} else writestr(s);
111 }
112 
113 
114 /*----------------------------------------------------------------------*/
115 /* Memory allocation wrappers: All memory allocation should run through */
116 /* these routines, which trap error conditions and do accounting to     */
117 /* help track down memory leaks.                                        */
118 /*----------------------------------------------------------------------*/
119 
120 rbool rm_trap = 1;
121 
get_rm_size(void)122 long get_rm_size(void)
123 /* Return the amount of space being used by dynamically allocated things */
124 {
125 #ifdef MEM_INFO
126 	struct mstats memdata;
127 
128 	memdata = mstats();
129 	return memdata.bytes_used;
130 #endif
131 	return 0;
132 }
133 
get_rm_freesize(void)134 long get_rm_freesize(void)
135 /* Return estimate of amount of space left */
136 {
137 #ifdef MEM_INFO
138 	struct mstats memdata;
139 
140 	memdata = mstats();
141 	return memdata.bytes_free;
142 #endif
143 	return 0;
144 }
145 
146 
rmalloc(long size)147 void *rmalloc(long size) {
148 	void *p;
149 
150 	if (size > MAXSTRUC) {
151 		error("Memory allocation error: Over-sized structure requested.");
152 	}
153 	assert(size >= 0);
154 	if (size == 0) return NULL;
155 	p = malloc((size_t)size);
156 	if (p == NULL && rm_trap && size > 0) {
157 		error("Memory allocation error: Out of memory.");
158 	}
159 	if (rm_acct) ralloc_cnt++;
160 	return p;
161 }
162 
rrealloc(void * old,long size)163 void *rrealloc(void *old, long size) {
164 	void *p;
165 
166 	if (size > MAXSTRUC) {
167 		error("Memory reallocation error: Oversized structure requested.");
168 	}
169 	assert(size >= 0);
170 	if (size == 0) {
171 		r_free(old);
172 		return NULL;
173 	}
174 	if (rm_acct && old == NULL) ralloc_cnt++;
175 	p = realloc(old, (size_t)size);
176 	if (p == NULL && rm_trap && size > 0) {
177 		error("Memory reallocation error: Out of memory.");
178 	}
179 	return p;
180 }
181 
rstrdup(const char * s)182 char *rstrdup(const char *s) {
183 	if (s == NULL) return NULL;
184 
185 	char *t = scumm_strdup(s);
186 	if (t == NULL && rm_trap) {
187 		error("Memory duplication error: Out of memory.");
188 	}
189 	if (rm_acct) ralloc_cnt++;
190 
191 	return t;
192 }
193 
r_free(void * p)194 void r_free(void *p) {
195 	int tmp;
196 
197 	if (p == NULL) return;
198 
199 	tmp = get_rm_size();            /* Take worst case in all cases */
200 	if (tmp > rm_size) rm_size = tmp;
201 	tmp = get_rm_freesize();
202 	if (tmp < rm_freesize) rm_freesize = tmp;
203 
204 	if (rm_acct) rfree_cnt++;
205 	free(p);
206 }
207 
208 
209 
210 /*----------------------------------------------------------------------*/
211 /* String utilities: These are utilities to manipulate strings.         */
212 /*----------------------------------------------------------------------*/
213 
214 /* rnstrncpy copies src to dest, copying at most (max-1) characters.
215    Unlike ANSI strncpy, it doesn't fill extra space will nulls and
216    it always puts a terminating null. */
rstrncpy(char * dest,const char * src,int max)217 char *rstrncpy(char *dest, const char *src, int max) {
218 	int i;
219 	for (i = 0; i < max - 1 && src[i]; i++)
220 		dest[i] = src[i];
221 	dest[i] = 0;
222 	return dest;
223 }
224 
225 /* This does a case-insensitive match of the beginning of *pstr to match */
226 /* <match> must be all upper case */
227 /* *pstr is updated to point after the match, if it is succesful.
228    Otherwise *pstr is left alone. */
match_str(const char ** pstr,const char * match)229 rbool match_str(const char **pstr, const char *match) {
230 	int i;
231 	const char *s;
232 
233 	s = *pstr;
234 	for (i = 0; match[i] != 0 && s[i] != 0; i++)
235 		if (toupper(s[i]) != match[i]) return 0;
236 	if (match[i] != 0) return 0;
237 	*pstr += i;
238 	return 1;
239 }
240 
241 
242 
243 
244 /* Utility to concacate two strings with a space inserted */
245 
concdup(const char * s1,const char * s2)246 char *concdup(const char *s1, const char *s2) {
247 	int len1, len2;
248 	char *s;
249 
250 	len1 = len2 = 0;
251 	if (s1 != NULL) len1 = strlen(s1);
252 	if (s2 != NULL) len2 = strlen(s2);
253 
254 	s = (char *)rmalloc(sizeof(char) * (len1 + len2 + 2));
255 	if (s1 != NULL)
256 		memcpy(s, s1, len1);
257 	memcpy(s + len1, " ", 1);
258 	if (s2 != NULL)
259 		memcpy(s + len1 + 1, s2, len2);
260 	s[len1 + len2 + 1] = 0;
261 	return s;
262 }
263 
264 
265 /* Misc. C utility functions that may be supported locally.
266   If they are, use the local functions since they'll probably be faster
267   and more efficiant. */
268 
269 #ifdef NEED_STR_CMP
strcasecmp(const char * s1,const char * s2)270 int strcasecmp(const char *s1, const char *s2)
271 /* Compare strings s1 and s2, case insensitive; */
272 /* If equal, return 0. Otherwise return nonzero. */
273 {
274 	int i;
275 
276 	for (i = 0; tolower(s1[i]) == tolower(s2[i]) && s1[i] != 0; i++);
277 	if (tolower(s1[i]) == tolower(s2[i])) return 0;
278 	if (s1[i] == 0) return -1;
279 	if (s2[i] == 0) return 1;
280 	if (tolower(s1[i]) < tolower(s2[i])) return -1;
281 	return 1;
282 }
283 #endif /* NEED_STR_CMP */
284 
285 #ifdef NEED_STRN_CMP
strncasecmp(const char * s1,const char * s2,size_t n)286 int strncasecmp(const char *s1, const char *s2, size_t n)
287 /* Compare first n letters of strings s1 and s2, case insensitive; */
288 /* If equal, return 0. Otherwise return nonzero. */
289 {
290 	size_t i;
291 
292 	for (i = 0; i < n && tolower(s1[i]) == tolower(s2[i]) && s1[i] != 0; i++);
293 	if (i == n || tolower(s1[i]) == tolower(s2[i])) return 0;
294 	if (s1[i] == 0) return -1;
295 	if (s2[i] == 0) return 1;
296 	if (tolower(s1[i]) < tolower(s2[i])) return -1;
297 	return 1;
298 }
299 #endif /* NEED_STRN_CMP */
300 
301 /*----------------------------------------------------------------------*/
302 /* Character utilities: Do character translation                        */
303 /*----------------------------------------------------------------------*/
304 
build_trans_ascii(void)305 void build_trans_ascii(void) {
306 	int i;
307 
308 	for (i = 0; i < 256; i++)
309 		trans_ascii[i] = (!fix_ascii_flag || i < 0x80) ? i : trans_ibm[i & 0x7f];
310 	trans_ascii[0xFF] = 0xFF; /* Preserve format character */
311 }
312 
313 
314 /*----------------------------------------------------------------------*/
315 /* File utilities: Utilities to manipulate files.                       */
316 /*----------------------------------------------------------------------*/
317 
print_error(const char * fname,filetype ext,const char * err,rbool ferr)318 void print_error(const char *fname, filetype ext, const char *err, rbool ferr) {
319 	char *estring; /* Hold error string */
320 	estring = (char *)rmalloc(strlen(err) + strlen(fname) + 2);
321 	sprintf(estring, err, fname);
322 	if (ferr) fatal(estring);
323 	else writeln(estring);
324 	rfree(estring);
325 }
326 
327 /* Routine to open files with extensions and handle basic error conditions */
328 
fopen(const char * name,const char * how)329 genfile fopen(const char *name, const char *how) {
330 	if (!strcmp(how, "r") || !strcmp(how, "rb")) {
331 		Common::File *f = new Common::File();
332 		if (!f->open(name)) {
333 			delete f;
334 			f = nullptr;
335 		}
336 
337 		return f;
338 	} else if (!strcmp(how, "w") || !strcmp(how, "wb")) {
339 		Common::DumpFile *f = new Common::DumpFile();
340 		if (!f->open(name)) {
341 			delete f;
342 			f = nullptr;
343 		}
344 
345 		return f;
346 	} else {
347 		error("Unknown file open how");
348 	}
349 }
350 
fseek(genfile stream,long int offset,int whence)351 int fseek(genfile stream, long int offset, int whence) {
352 	Common::SeekableReadStream *rs = dynamic_cast<Common::SeekableReadStream *>(stream);
353 	assert(rs);
354 	return rs->seek(offset, whence);
355 }
356 
fread(void * ptr,size_t size,size_t nmemb,genfile stream)357 size_t fread(void *ptr, size_t size, size_t nmemb, genfile stream) {
358 	Common::SeekableReadStream *rs = dynamic_cast<Common::SeekableReadStream *>(stream);
359 	assert(rs);
360 	size_t bytesRead = rs->read(ptr, size * nmemb);
361 	return bytesRead / size;
362 }
363 
fwrite(const void * ptr,size_t size,size_t nmemb,genfile stream)364 size_t fwrite(const void *ptr, size_t size, size_t nmemb, genfile stream) {
365 	Common::WriteStream *ws = dynamic_cast<Common::WriteStream *>(stream);
366 	assert(ws);
367 	size_t bytesWritten = ws->write(ptr, size * nmemb);
368 	return bytesWritten / size;
369 }
370 
ftell(genfile f)371 size_t ftell(genfile f) {
372 	Common::SeekableReadStream *rs = dynamic_cast<Common::SeekableReadStream *>(f);
373 	assert(rs);
374 	return rs->pos();
375 }
376 
openfile(fc_type fc,filetype ext,const char * err,rbool ferr)377 genfile openfile(fc_type fc, filetype ext, const char *err, rbool ferr)
378 /* Opens the file fname+ext, printing out err if something goes wrong.
379   (unless err==NULL, in which case nothing will be printed) */
380 /* err can have one %s paramater in it, which will have the file name
381    plugged in to it. */
382 /* If ferr is true, then on failure the routine will abort with a fatal
383    error. */
384 {
385 	genfile tfile;  /* Actually, this may not be a text file anymore */
386 	const char *errstr;
387 
388 	tfile = readopen(fc, ext, &errstr);
389 	if (errstr != NULL && err != NULL)
390 		print_error("", ext, err, ferr);
391 
392 	return tfile;
393 }
394 
395 
openbin(fc_type fc,filetype ext,const char * err,rbool ferr)396 genfile openbin(fc_type fc, filetype ext, const char *err, rbool ferr)
397 /* Opens the file fname+ext, printing out err if something goes wrong.
398   (unless err==NULL, in which case nothing will be printed) */
399 /* err can have one %s paramater in it, which will have the file name
400    plugged in to it. */
401 /* If ferr is true, then on failure the routine will abort with a fatal
402    error. */
403 {
404 	genfile f;  /* Actually, this may not be a text file anymore */
405 	const char *errstr;
406 	char *fname;
407 
408 	f = readopen(fc, ext, &errstr);
409 	if (errstr != NULL && err != NULL) {
410 		fname = formal_name(fc, ext);
411 		print_error(fname, ext, err, ferr);
412 		rfree(fname);
413 	}
414 
415 	return f;
416 }
417 
418 
419 
420 /* This routine reads in a line from a 'text' file; it's designed to work
421    regardless of the EOL conventions of the platform, at least up to a point.
422    It should work with files that have \n, \r, or \r\n termined lines.  */
423 
424 #define READLN_GRAIN 64 /* Granularity of readln() rrealloc requests
425 				this needs to be at least the size of a tab
426 				character */
427 #define DOS_EOF 26    /* Ctrl-Z is the DOS end-of-file marker */
428 
readln(genfile f,char * buff,int n)429 char *readln(genfile f, char *buff, int n)
430 /* Reads first n characters of line, eliminates any newline,
431 and truncates the rest of the line. 'n' does *not* include terminating
432 null. */
433 /* If we pass it BUFF=NULL,  then it will reallocate buff as needed and
434 pass it back as its return value.  n is ignored in this case */
435 /* If it reaches EOF, it will return NULL */
436 /* This routine recognizes lines terminated by \n, \r, or \r\n */
437 {
438 	int c;
439 	int i, j, csize;
440 	int buffsize; /* Current size of buff, if we are allocating it dynamically */
441 
442 	if (buff == NULL) {
443 		buff = (char *)rrealloc(buff, READLN_GRAIN * sizeof(char));
444 		buffsize = READLN_GRAIN;
445 		n = buffsize - 1;
446 	} else buffsize = -1; /* So we know that we are using a fixed-size buffer */
447 
448 	i = 0;
449 	for (;;) {
450 		c = textgetc(f);
451 
452 		if (c == '\n' || c == '\r' || c == EOF || c == DOS_EOF) break;
453 
454 		csize = (c == '\t') ? 5 : 1; /* Tabs are translated into five spaces */
455 
456 		if (i + csize >= n && buffsize >= 0) {
457 			buffsize += READLN_GRAIN;
458 			n = buffsize - 1;
459 			buff = (char *)rrealloc(buff, buffsize * sizeof(char));
460 		}
461 
462 		if (c == 0) c = FORMAT_CODE;
463 		else if (c != '\t') {
464 			if (i < n) buff[i++] = c;
465 		} else for (j = 0; j < 5 && i < n; j++) buff[i++] = ' ';
466 
467 		/* We can't exit the loop if i>n since we still need to discard
468 		   the rest of the line */
469 	}
470 
471 	buff[i] = 0;
472 
473 	if (c == '\r') { /* Check for \r\n DOS-style newline  */
474 		char newc;
475 		newc = textgetc(f);
476 		if (newc != '\n') textungetc(f, newc);
477 		/* Replace the character we just read. */
478 	} else if (c == DOS_EOF) /* Ctrl-Z is the DOS EOF marker */
479 		textungetc(f, c); /* So it will be the first character we see next time */
480 
481 	if (i == 0 && (c == EOF || c == DOS_EOF)) { /* We've hit the end of the file */
482 		if (buffsize >= 0) rfree(buff);
483 		return NULL;
484 	}
485 
486 	if (buffsize >= 0) { /* Shrink buffer to appropriate size */
487 		buffsize = i + 1;
488 		buff = (char *)rrealloc(buff, buffsize);
489 	}
490 
491 	return buff;
492 }
493 
494 
495 /*-------------------------------------------------------------------------*/
496 /* Buffered file Input: Routines to do buffered file I/O for files organized */
497 /*  into records.  These routines are highly non-reentrant: they use a     */
498 /*  global buffer and a global file id, so only they can only access one   */
499 /*  file at a time.                                                        */
500 /* buffopen() should not be called on a new file until buffclose has been  */
501 /*  called on the old one.                                                 */
502 /*-------------------------------------------------------------------------*/
503 
504 genfile bfile;
505 
506 static uchar *buffer = NULL;
507 static long buffsize; /* How big the buffer is */
508 static long record_size;  /* Size of a record in the file */
509 static long buff_frame;  /* The file index corrosponding to buffer[0] */
510 static long buff_fcnt;  /* Number of records that can be held in the buffer */
511 static long real_buff_fcnt;  /* Number of records actually held in buffer */
512 static long buff_rsize;  /* Minimum amount that must be read. */
513 
514 static long block_size;  /* Size of the current block
515 				(for non-AGX files, this is just the filesize) */
516 static long block_offset; /* Offset of current block in file (this should
517 			   be zero for non-AGX files) */
518 
519 
buff_setrecsize(long recsize)520 static void buff_setrecsize(long recsize) {
521 	const char *errstr;
522 
523 	record_size = recsize;
524 	real_buff_fcnt = buff_fcnt = buffsize / record_size;
525 	buff_frame = 0;
526 
527 	/* Note that real_buff_cnt==buff_fcnt in this case because
528 	   the buffer will have already been resized to be <=
529 	   the block size-- so we don't need to worry about the
530 	   buffer being larger than the data we're reading in. */
531 
532 	binseek(bfile, block_offset);
533 	if (!binread(bfile, buffer, record_size, real_buff_fcnt, &errstr))
534 		fatal(errstr);
535 }
536 
537 
538 
buffopen(fc_type fc,filetype ext,long minbuff,const char * rectype,long recnum)539 long buffopen(fc_type fc, filetype ext, long minbuff, const char *rectype, long recnum)
540 /* Returns record size; print out error and halt on failure  */
541 /* (if agx_file, it returns the filesize instead) */
542 /* rectype="noun","room", etc.  recnum=number of records expected */
543 /* If rectype==NULL, buffopen() will return 0 on failure instead of
544 halting */
545 /* For AGX files, recsize should be set to minbuff... but
546 buffreopen will be called before any major file activity
547 (in particular, recnum should be 1) */
548 {
549 	long filesize;
550 	long recsize;
551 	char ebuff[200];
552 	const char *errstr;
553 
554 	assert(buffer == NULL); /* If not, it means these routines have been
555 			   called by someone else who isn't done yet */
556 
557 	bfile = readopen(fc, ext, &errstr);
558 	if (errstr != NULL) {
559 		if (rectype == NULL) {
560 			return 0;
561 		} else
562 			fatal(errstr);
563 	}
564 
565 	filesize = binsize(bfile);
566 
567 	block_size = filesize;
568 	block_offset = 0;
569 	if (agx_file) block_size = minbuff; /* Just for the beginning */
570 
571 	if (block_size % recnum != 0) {
572 		sprintf(ebuff, "Fractional record count in %s file.", rectype);
573 		agtwarn(ebuff, 0);
574 	}
575 	buff_rsize = recsize = block_size / recnum;
576 	if (buff_rsize > minbuff) buff_rsize = minbuff;
577 
578 	/* No point in having a buffer bigger than the block size */
579 	buffsize = BUFF_SIZE;
580 	if (block_size < buffsize) buffsize = block_size;
581 
582 	/* ... but it needs to be big enough: */
583 	if (buffsize < minbuff) buffsize = minbuff;
584 	if (buffsize < recsize) buffsize = recsize;
585 
586 	buffer = (uchar *)rmalloc(buffsize); /* Might want to make this adaptive eventually */
587 
588 	buff_setrecsize(recsize);
589 	if (!agx_file && DIAG) {
590 		char *s;
591 		s = formal_name(fc, ext);
592 		rprintf("Reading %s file %s (size:%ld)\n", rectype, s, filesize);
593 		rfree(s);
594 		rprintf("  Record size=  Formal:%ld    File:%ld", minbuff, recsize);
595 	}
596 	if (agx_file) return (long) filesize;
597 	else return (long) recsize;
598 }
599 
600 
601 /* Compute the game signature: a checksum of relevant parts of the file */
602 
compute_sig(uchar * buff)603 static void compute_sig(uchar *buff) {
604 	long bp;
605 	for (bp = 0; bp < buff_rsize; bp++)
606 		game_sig = (game_sig + buff[bp]) & 0xFFFF;
607 }
608 
buffread(long index)609 uchar *buffread(long index) {
610 	uchar *bptr;
611 	const char *errstr;
612 
613 	assert(buff_rsize <= record_size);
614 	if (index >= buff_frame && index < buff_frame + real_buff_fcnt)
615 		bptr = buffer + (index - buff_frame) * record_size;
616 	else {
617 		binseek(bfile, block_offset + index * record_size);
618 		real_buff_fcnt = block_size / record_size - index;  /* How many records
619 							 could we read in? */
620 		if (real_buff_fcnt > buff_fcnt)
621 			real_buff_fcnt = buff_fcnt; /* Don't overflow buffer */
622 		if (!binread(bfile, buffer, record_size, real_buff_fcnt, &errstr))
623 			fatal(errstr);
624 		buff_frame = index;
625 		bptr = buffer;
626 	}
627 	if (!agx_file) compute_sig(bptr);
628 	return bptr;
629 }
630 
631 
buffclose(void)632 void buffclose(void) {
633 	readclose(bfile);
634 	rfree(buffer);
635 }
636 
637 
638 /* This changes the record size and offset settings of the buffered
639    file so we can read files that consist of multiple sections with
640    different structures */
buffreopen(long f_ofs,long file_recsize,long recnum,long bl_size,const char * rectype)641 static void buffreopen(long f_ofs, long file_recsize, long recnum,
642 					   long bl_size, const char *rectype) {
643 	char ebuff[200];
644 	long recsize;
645 
646 	/* Compute basic statistics */
647 	block_offset = f_ofs; /* Offset of this block */
648 	block_size = bl_size; /* Size of the entire block (all records) */
649 	if (block_size % recnum != 0) {
650 		/* Check that the number of records divides the block size evenly */
651 		sprintf(ebuff, "Fractional record count in %s block.", rectype);
652 		agtwarn(ebuff, 0);
653 	}
654 	buff_rsize = recsize = block_size / recnum;
655 	if (buff_rsize > file_recsize) buff_rsize = file_recsize;
656 	/* recsize is the size of each record in the file.
657 	   buff_rsize is the internal size of each record (the part
658 	   we actually look at, which may be smaller than recsize) */
659 
660 	/* No point in having a buffer bigger than the block size */
661 	buffsize = BUFF_SIZE;
662 	if (block_size < buffsize) buffsize = block_size;
663 
664 	/* The buffer needs to be at least as big as one block, so
665 	   we have space to both read it in and so we can look at the
666 	   block without having to worry about how big it really is */
667 	if (buffsize < file_recsize) buffsize = file_recsize;
668 	if (buffsize < recsize) buffsize = recsize;
669 
670 	rfree(buffer);
671 	buffer = (uchar *)rmalloc(buffsize); /* Resize the buffer */
672 
673 	buff_setrecsize(recsize); /* Set up remaining stats */
674 }
675 
676 
677 /*-------------------------------------------------------------------------*/
678 /* Buffered file output: Routines to buffer output  for files organized    */
679 /*  into records.  These routines are highly non-reentrant: they use a     */
680 /*  global buffer and a global file id, so only they can only access one   */
681 /*  file at a time.                                                        */
682 /* This routine uses the same buffer and data structures as the reading    */
683 /*  routines above, so  both sets of routines should not be used           */
684 /*  concurrently                                                           */
685 /*-------------------------------------------------------------------------*/
686 
687 /* #define DEBUG_SEEK*/  /* Debug seek beyond EOF problem */
688 
689 static long bw_first, bw_last;  /* First and last record in buffer written to.
690 				 This is relative to the beginning of the
691 				 buffer bw_last points just beyond the last
692 				 one written to */
693 #ifdef DEBUG_SEEK
694 static long bw_fileleng;  /* Current file length */
695 #endif /* DEBUG_SEEK */
696 file_id_type bw_fileid;
697 
698 /* Unlike is reading counterpart, this doesn't actually allocate
699    a buffer; that's done by bw_setblock() which should be called before
700    any I/O */
bw_open(fc_type fc,filetype ext)701 void bw_open(fc_type fc, filetype ext) {
702 	const char *errstr;
703 
704 	assert(buffer == NULL);
705 
706 	bfile = writeopen(fc, ext, &bw_fileid, &errstr);
707 	if (errstr != NULL) fatal(errstr);
708 	bw_last = 0;
709 	buffsize = 0;
710 	buffer = NULL;
711 #ifdef DEBUG_SEEK
712 	bw_fileleng = 0;
713 #endif
714 }
715 
bw_seek(long offset)716 static void bw_seek(long offset) {
717 #ifdef DEBUG_SEEK
718 	assert(offset <= bw_fileleng);
719 #endif
720 	binseek(bfile, offset);
721 }
722 
723 
bw_flush(void)724 static void bw_flush(void) {
725 	if (bw_first == bw_last) return; /* Nothing to do */
726 	bw_first += buff_frame;
727 	bw_last += buff_frame;
728 	bw_seek(block_offset + bw_first * record_size);
729 	binwrite(bfile, buffer, record_size, bw_last - bw_first, 1);
730 #ifdef DEBUG_SEEK
731 	if (block_offset + bw_last * record_size > bw_fileleng)
732 		bw_fileleng = block_offset + bw_last * record_size;
733 #endif
734 	bw_first = bw_last = 0;
735 }
736 
737 
738 
bw_setblock(long fofs,long recnum,long rsize)739 static void bw_setblock(long fofs, long recnum, long rsize)
740 /* Set parameters for current block */
741 {
742 	/* First, flush old block if neccessary */
743 	if (buffer != NULL) {
744 		bw_flush();
745 		rfree(buffer);
746 	}
747 	block_size = rsize * recnum;
748 	block_offset = fofs;
749 	record_size = rsize;
750 	buff_frame = 0;
751 	bw_first = bw_last = 0;
752 	buffsize = BUFF_SIZE;
753 	if (buffsize > block_size) buffsize = block_size;
754 	if (buffsize < rsize) buffsize = rsize;
755 	buff_fcnt = buffsize / rsize;
756 	buffsize = buff_fcnt * rsize;
757 	buffer = (uchar *)rmalloc(buffsize);
758 }
759 
760 /* This routine returns a buffer of the current recsize and with
761  the specified index into the file */
762 /* The buffer will be written to disk after the next call to
763    bw_getbuff() or bw_closebuff() */
bw_getbuff(long index)764 static uchar *bw_getbuff(long index) {
765 	index -= buff_frame;
766 	if (index < bw_first || index > bw_last || index >= buff_fcnt) {
767 		bw_flush();
768 		bw_first = bw_last = 0;
769 		buff_frame = buff_frame + index;
770 		index = 0;
771 	}
772 	if (index == bw_last) bw_last++;
773 	return buffer + record_size * index;
774 }
775 
776 
777 /* This flushes all buffers to disk and closes all files */
bw_close(void)778 void bw_close(void) {
779 	bw_flush();
780 	rfree(buffer);
781 	writeclose(bfile, bw_fileid);
782 }
783 
784 
785 /*-------------------------------------------------------------------------*/
786 /* Block reading and writing code and support for internal buffers         */
787 /*-------------------------------------------------------------------------*/
788 
789 
790 /* If the internal buffer is not NULL, it is used instead of a file */
791 /* (This is used by RESTART, etc. to save state to memory rather than
792    to a file) */
793 static uchar *int_buff = NULL;
794 static long ibuff_ofs, ibuff_rsize;
795 
set_internal_buffer(void * buff)796 void set_internal_buffer(void *buff) {
797 	int_buff = (uchar *)buff;
798 }
799 
set_ibuff(long offset,long rsize)800 static void set_ibuff(long offset, long rsize) {
801 	ibuff_ofs = offset;
802 	record_size = ibuff_rsize = rsize;
803 }
804 
get_ibuff(long index)805 static uchar *get_ibuff(long index) {
806 	return int_buff + ibuff_ofs + index * ibuff_rsize;
807 }
808 
809 /* This does a block write to the currently buffered file.
810    At the moment this itself does no buffering at all; it's intended
811    for high speed reading of blocks of chars for which we've already
812    allocated the space. */
buff_blockread(void * buff,long size,long offset)813 static void buff_blockread(void *buff, long size, long offset) {
814 	const char *errstr;
815 
816 	if (int_buff != NULL)
817 		memcpy((char *)buff, int_buff + offset, size);
818 	else {
819 		binseek(bfile, offset);
820 		if (!binread(bfile, buff, size, 1, &errstr)) fatal(errstr);
821 	}
822 }
823 
824 
825 /* This writes buff to disk. */
bw_blockwrite(void * buff,long size,long offset)826 static void bw_blockwrite(void *buff, long size, long offset) {
827 	if (int_buff != NULL)
828 		memcpy(int_buff + offset, (char *)buff, size);
829 	else {
830 		bw_flush();
831 		bw_seek(offset);
832 		binwrite(bfile, buff, size, 1, 1);
833 #ifdef DEBUG_SEEK
834 		if (offset + size > bw_fileleng) bw_fileleng = offset + size;
835 #endif
836 	}
837 }
838 
839 
840 /*-------------------------------------------------------------------------*/
841 /* Platform-independent record-based file I/O: Routines to read and write  */
842 /*   files according to the file_info data structures.                     */
843 /* These routines use the buffered I/O routines above                      */
844 /*-------------------------------------------------------------------------*/
845 
846 /* Length of file datatypes */
847 const size_t ft_leng[FT_COUNT] = {0, 2, 2, /* END, int16, and uint16 */
848 								  4, 4,  /* int32 and uint32 */
849 								  1, 2, 0, /* byte, version, rbool */
850 								  8, 4,  /* descptr, ss_ptr */
851 								  2, 26,  /* slist, path[13] */
852 								  4, 4, /* cmdptr, dictptr */
853 								  81,  /* tline */
854 								  1, 1
855 								 };  /* char, cfg */
856 
857 
compute_recsize(file_info * recinfo)858 long compute_recsize(file_info *recinfo) {
859 	long cnt, bcnt;
860 
861 	cnt = 0;
862 	for (; recinfo->ftype != FT_END; recinfo++)
863 		if (recinfo->ftype == FT_BOOL) {
864 			for (bcnt = 0; recinfo->ftype == FT_BOOL; recinfo++, bcnt++);
865 			recinfo--;
866 			cnt += (bcnt + 7) / 8; /* +7 is to round up */
867 		} else
868 			cnt += ft_leng[recinfo->ftype];
869 	return cnt;
870 }
871 
872 static const int agx_version[] = {0, 0000, 1800, 2000, 3200, 3500, 8200, 8300,
873 								  5000, 5050, 5070, 10000, 10050, 15000, 15500, 16000, 20000
874 								 };
875 
agx_decode_version(int vercode)876 static int agx_decode_version(int vercode) {
877 	if (vercode & 1) /* Large/Soggy */
878 		if (vercode == 3201) ver = 4;
879 		else ver = 2;
880 	else if (vercode < 10000) ver = 1;
881 	else ver = 3;
882 	switch (vercode & (~1)) {
883 	case 0000:
884 		return AGT10;
885 	case 1800:
886 		return AGT118;
887 	case 1900:
888 		return AGT12;
889 	case 2000:
890 		return AGT12;
891 	case 3200:
892 		return AGTCOS;
893 	case 3500:
894 		return AGT135;
895 	case 5000:
896 		return AGT15;
897 	case 5050:
898 		return AGT15F;
899 	case 5070:
900 		return AGT16;
901 	case 8200:
902 		return AGT182;
903 	case 8300:
904 		return AGT183;
905 	case 8350:
906 		return AGT183;
907 	case 10000:
908 		return AGTME10;
909 	case 10050:
910 		return AGTME10A;
911 	case 15000:
912 		return AGTME15;
913 	case 15500:
914 		return AGTME155;
915 	case 16000:
916 		return AGTME16;
917 	case 20000:
918 		return AGX00;
919 	default:
920 		agtwarn("Unrecognize AGT version", 0);
921 		return 0;
922 	}
923 }
924 
925 /* The following reads a section of a file into variables, doing
926    the neccessary conversions. It is the foundation of all the generic
927    file reading code */
928 
929 #define p(t)  ((t*)(rec_desc->ptr))
930 #define fixu16(n1,n2)  ( ((long)(n1))|( ((long)(n2))<<8 ))
931 
932 /* This is as large as the largest data structure we could run into */
933 static const uchar zero_block[81] = {0, 0, 0, 0, 0, 0, 0, 0,
934 									 0, 0, 0, 0, 0, 0, 0, 0,
935 									 0, 0, 0, 0, 0, 0, 0, 0,
936 									 0, 0, 0, 0, 0, 0, 0, 0,
937 									 0, 0, 0, 0, 0, 0, 0, 0,
938 									 0, 0, 0, 0, 0, 0, 0, 0,
939 									 0, 0, 0, 0, 0, 0, 0, 0,
940 									 0, 0, 0, 0, 0, 0, 0, 0,
941 									 0, 0, 0, 0, 0, 0, 0, 0,
942 									 0, 0, 0, 0, 0, 0, 0, 0,
943 									 0
944 									};
945 
read_filerec(file_info * rec_desc,const uchar * filedata)946 static void read_filerec(file_info *rec_desc, const uchar *filedata) {
947 	uchar mask;
948 	rbool past_eob; /* Are we past the end of block? */
949 	const uchar *filebase;
950 
951 	mask = 1;
952 	past_eob = 0;
953 	filebase = filedata;
954 	for (; rec_desc->ftype != FT_END; rec_desc++) {
955 		if (mask != 1 && rec_desc->ftype != FT_BOOL) { /* Just finished rboolean */
956 			mask = 1;
957 			filedata += 1;
958 		}
959 		if (filebase == NULL || (filedata - filebase) >= record_size) {
960 			/* We're past the end of the block; read in zeros for the rest
961 			of entries. */
962 			past_eob = 1;
963 			filedata = zero_block;
964 			filebase = NULL;
965 		}
966 		switch (rec_desc->ftype) {
967 		case FT_INT16:
968 			if (rec_desc->dtype == DT_LONG)
969 				*p(long) = fixsign16(filedata[0], filedata[1]);
970 			else
971 				*p(integer) = fixsign16(filedata[0], filedata[1]);
972 			break;
973 		case FT_UINT16:
974 			*p(int32) = fixu16(filedata[0], filedata[1]);
975 			break;
976 		case FT_CMDPTR: /* cmd ptr */
977 		case FT_INT32:
978 			*p(int32) = fixsign32(filedata[0], filedata[1],
979 			                     filedata[2], filedata[3]);
980 			break;
981 		case FT_UINT32:
982 			if (filedata[3] & 0x80)
983 				agtwarn("File value out of range", 0);
984 			*p(uint32) = fixsign32(filedata[0], filedata[1],
985 			                     filedata[2], filedata[3] & 0x7F);
986 			break;
987 		case FT_BYTE:
988 			*p(uchar) = filedata[0];
989 			break;
990 		case FT_CHAR:
991 			*p(uchar) = trans_ascii[filedata[0]^'r'];
992 			break;
993 		case FT_VERSION:
994 			*p(int) = agx_decode_version(fixu16(filedata[0], filedata[1]));
995 			break;
996 		case FT_CFG:
997 			if (filedata[0] != 2 && !past_eob)
998 				*p(rbool) = filedata[0];
999 			break;
1000 		case FT_BOOL:
1001 			*p(rbool) = ((filedata[0] & mask) != 0);
1002 			if (mask == 0x80) {
1003 				filedata++;
1004 				mask = 1;
1005 			} else
1006 				mask <<= 1;
1007 			break;
1008 		case FT_DESCPTR:
1009 			if (skip_descr) break;
1010 			p(descr_ptr)->start = fixsign32(filedata[0], filedata[1],
1011 			                                filedata[2], filedata[3]);
1012 			p(descr_ptr)->size = fixsign32(filedata[4], filedata[5],
1013 			                               filedata[6], filedata[7]);
1014 			break;
1015 		case FT_STR:  /* ss_string ptr */
1016 			*p(char *) = static_str + fixsign32(filedata[0], filedata[1],
1017 			                                    filedata[2], filedata[3]);
1018 			break;
1019 		case FT_SLIST:
1020 			*p(slist) = fixsign16(filedata[0], filedata[1]);
1021 			break;
1022 		case FT_PATHARRAY: { /* integer array[13] */
1023 			int i;
1024 			for (i = 0; i < 13; i++)
1025 				p(integer)[i] = fixsign16(filedata[2 * i], filedata[2 * i + 1]);
1026 			break;
1027 		}
1028 		case FT_TLINE: { /* string of length at most 80 characters +null */
1029 			uchar *s;
1030 			int i;
1031 			s = (uchar *)*p(tline);
1032 			for (i = 0; i < 80; i++)
1033 				s[i] = trans_ascii[filedata[i]^'r'];
1034 			s[80] = 0;
1035 			break;
1036 		}
1037 		case FT_DICTPTR: /* ptr into dictstr */
1038 			*p(char *) = dictstr + fixsign32(filedata[0], filedata[1],
1039 			                                 filedata[2], filedata[3]);
1040 			break;
1041 		default:
1042 			fatal("Unreconized field type");
1043 		}
1044 		filedata += ft_leng[rec_desc->ftype];
1045 	}
1046 }
1047 
1048 
1049 #define v(t) (*(t*)(rec_desc->ptr))
1050 /* Here is the corresponding routien for _writing_ to files */
1051 /* This copies the contents of a record into a buffer */
1052 
write_filerec(file_info * rec_desc,uchar * filedata)1053 static void write_filerec(file_info *rec_desc, uchar *filedata) {
1054 	uchar mask;
1055 
1056 	mask = 1;
1057 	for (; rec_desc->ftype != FT_END; rec_desc++) {
1058 		if (mask != 1 && rec_desc->ftype != FT_BOOL) { /* Just finished rboolean */
1059 			mask = 1;
1060 			filedata += 1;
1061 		}
1062 		switch (rec_desc->ftype) {
1063 		case FT_INT16:
1064 			if (rec_desc->dtype == DT_LONG) {
1065 				filedata[0] = v(long) & 0xFF;
1066 				filedata[1] = (v(long) >> 8) & 0xFF;
1067 			} else {
1068 				filedata[0] = v(integer) & 0xFF;
1069 				filedata[1] = (v(integer) >> 8) & 0xFF;
1070 			}
1071 			break;
1072 		case FT_UINT16:
1073 			filedata[0] = v(long) & 0xFF;
1074 			filedata[1] = (v(long) >> 8) & 0xFF;
1075 			break;
1076 		case FT_CMDPTR: /* cmd ptr */
1077 		case FT_INT32:
1078 		case FT_UINT32:
1079 			filedata[0] = v(long) & 0xFF;
1080 			filedata[1] = (v(long) >> 8) & 0xFF;
1081 			filedata[2] = (v(long) >> 16) & 0xFF;
1082 			filedata[3] = (v(long) >> 24) & 0xFF;
1083 			break;
1084 		case FT_BYTE:
1085 			filedata[0] = v(uchar);
1086 			break;
1087 		case FT_CFG:
1088 			filedata[0] = v(uchar);
1089 			break;
1090 		case FT_CHAR:
1091 			filedata[0] = v(uchar)^'r';
1092 			break;
1093 		case FT_VERSION: {
1094 			int tver;
1095 			tver = agx_version[v(int)];
1096 			if (ver == 2 || ver == 4) tver += 1;
1097 			filedata[0] = tver & 0xFF;
1098 			filedata[1] = (tver >> 8) & 0xFF;
1099 			break;
1100 		}
1101 		case FT_BOOL:
1102 			if (mask == 1) filedata[0] = 0;
1103 			filedata[0] |= v(rbool) ? mask : 0;
1104 			if (mask == 0x80) {
1105 				filedata++;
1106 				mask = 1;
1107 			} else
1108 				mask <<= 1;
1109 			break;
1110 		case FT_DESCPTR: {
1111 			long i, n1, n2;
1112 			n1 = p(descr_ptr)->start;
1113 			n2 = p(descr_ptr)->size;
1114 			for (i = 0; i < 4; i++) {
1115 				filedata[i] = n1 & 0xFF;
1116 				filedata[i + 4] = n2 & 0xFF;
1117 				n1 >>= 8;
1118 				n2 >>= 8;
1119 			}
1120 		}
1121 		break;
1122 		case FT_STR: { /* ss_string ptr */
1123 			long delta;
1124 			delta = v(char *) - static_str;
1125 			filedata[0] = delta & 0xFF;
1126 			filedata[1] = (delta >> 8) & 0xFF;
1127 			filedata[2] = (delta >> 16) & 0xFF;
1128 			filedata[3] = (delta >> 24) & 0xFF;
1129 			break;
1130 		}
1131 		case FT_SLIST:
1132 			filedata[0] = v(slist) & 0xFF;
1133 			filedata[1] = (v(slist) >> 8) & 0xFF;
1134 			break;
1135 		case FT_PATHARRAY: { /* integer array[13] */
1136 			int i;
1137 			for (i = 0; i < 13; i++) {
1138 				filedata[2 * i] = *(p(integer) + i) & 0xFF;
1139 				filedata[2 * i + 1] = (*(p(integer) + i) >> 8) & 0xFF;
1140 			}
1141 			break;
1142 		}
1143 		case FT_TLINE: { /* string of length at most 80 characters +null */
1144 			uchar *s;
1145 			int i;
1146 			s = (uchar *)v(tline);
1147 			for (i = 0; i < 80; i++)
1148 				filedata[i] = s[i]^'r';
1149 			filedata[80] = 0;
1150 			break;
1151 		}
1152 		case FT_DICTPTR: { /* ptr into dictstr */
1153 			long delta;
1154 			delta = v(char *) - dictstr;
1155 			filedata[0] = delta & 0xFF;
1156 			filedata[1] = (delta >> 8) & 0xFF;
1157 			filedata[2] = (delta >> 16) & 0xFF;
1158 			filedata[3] = (delta >> 24) & 0xFF;
1159 			break;
1160 		}
1161 		default:
1162 			fatal("Unreconized field type");
1163 		}
1164 		filedata += ft_leng[rec_desc->ftype];
1165 	}
1166 }
1167 
1168 #undef v
1169 #undef p
1170 
1171 
1172 
1173 
1174 /* This reads in a structure array */
1175 /* base=the beginning of the array. If NULL, this is malloc'd and returned
1176    eltsize = the size of each structure
1177    numelts = the number of elements in the array
1178    field_info = the arrangement of fields within the strucutre
1179    rectype = string to print out for error messages
1180    file_offset = the offset of the beginning of the array into the file
1181    */
read_recarray(void * base,long eltsize,long numelts,file_info * field_info,const char * rectype,long file_offset,long file_blocksize)1182 void *read_recarray(void *base, long eltsize, long numelts,
1183 					file_info *field_info, const char *rectype,
1184 					long file_offset, long file_blocksize) {
1185 	long i;
1186 	file_info *curr;
1187 	uchar *file_data;
1188 
1189 	if (numelts == 0) return NULL;
1190 
1191 	if (int_buff)
1192 		set_ibuff(file_offset, compute_recsize(field_info));
1193 	else
1194 		buffreopen(file_offset, compute_recsize(field_info), numelts,
1195 		           file_blocksize, rectype);
1196 
1197 	if (base == NULL)
1198 		base = rmalloc(eltsize * numelts);
1199 
1200 	for (curr = field_info; curr->ftype != FT_END; curr++)
1201 		if (curr->dtype != DT_DESCPTR && curr->dtype != DT_CMDPTR)
1202 			curr->ptr = ((char *)base + curr->offset);
1203 
1204 	for (i = 0; i < numelts; i++) {
1205 		if (!int_buff)
1206 			file_data = buffread(i);
1207 		else
1208 			file_data = get_ibuff(i);
1209 		read_filerec(field_info, file_data);
1210 		for (curr = field_info; curr->ftype != FT_END; curr++)
1211 			if (curr->dtype == DT_DESCPTR)
1212 				curr->ptr = (char *)(curr->ptr) + sizeof(descr_ptr);
1213 			else if (curr->dtype == DT_CMDPTR)
1214 				curr->ptr = (char *)(curr->ptr) + sizeof(long);
1215 			else
1216 				curr->ptr = (char *)(curr->ptr) + eltsize;
1217 	}
1218 
1219 	return base;
1220 }
1221 
1222 
1223 /* A NULL value means to write junk; we're just producing
1224    a placeholder for systems that can't seek beyond the end-of-file */
1225 
write_recarray(void * base,long eltsize,long numelts,file_info * field_info,long file_offset)1226 long write_recarray(void *base, long eltsize, long numelts,
1227 					file_info *field_info, long file_offset) {
1228 	long i;
1229 	file_info *curr;
1230 	uchar *file_data;
1231 
1232 	if (numelts == 0) return 0;
1233 
1234 	if (int_buff)
1235 		set_ibuff(file_offset, compute_recsize(field_info));
1236 	else
1237 		bw_setblock(file_offset, numelts, compute_recsize(field_info));
1238 
1239 	if (base != NULL)
1240 		for (curr = field_info; curr->ftype != FT_END; curr++)
1241 			if (curr->dtype != DT_DESCPTR && curr->dtype != DT_CMDPTR)
1242 				curr->ptr = ((char *)base + curr->offset);
1243 
1244 	for (i = 0; i < numelts; i++) {
1245 		if (int_buff)
1246 			file_data = get_ibuff(i);
1247 		else
1248 			file_data = bw_getbuff(i);
1249 		if (base != NULL) {
1250 			write_filerec(field_info, file_data);
1251 			for (curr = field_info; curr->ftype != FT_END; curr++)
1252 				if (curr->dtype == DT_DESCPTR)
1253 					curr->ptr = (char *)(curr->ptr) + sizeof(descr_ptr);
1254 				else if (curr->dtype == DT_CMDPTR)
1255 					curr->ptr = (char *)(curr->ptr) + sizeof(long);
1256 				else
1257 					curr->ptr = (char *)(curr->ptr) + eltsize;
1258 		}
1259 	}
1260 	return compute_recsize(field_info) * numelts;
1261 }
1262 
1263 
read_globalrec(file_info * global_info,const char * rectype,long file_offset,long file_blocksize)1264 void read_globalrec(file_info *global_info, const char *rectype,
1265 					long file_offset, long file_blocksize) {
1266 	uchar *file_data;
1267 
1268 	if (int_buff) {
1269 		set_ibuff(file_offset, compute_recsize(global_info));
1270 		file_data = get_ibuff(0);
1271 	} else {
1272 		buffreopen(file_offset, compute_recsize(global_info), 1, file_blocksize,
1273 		           rectype);
1274 		file_data = buffread(0);
1275 	}
1276 	read_filerec(global_info, file_data);
1277 }
1278 
1279 
write_globalrec(file_info * global_info,long file_offset)1280 long write_globalrec(file_info *global_info, long file_offset) {
1281 	uchar *file_data;
1282 
1283 	if (int_buff) {
1284 		set_ibuff(file_offset, compute_recsize(global_info));
1285 		file_data = get_ibuff(0);
1286 	} else {
1287 		bw_setblock(file_offset, 1, compute_recsize(global_info));
1288 		file_data = bw_getbuff(0);
1289 	}
1290 	write_filerec(global_info, file_data);
1291 	return compute_recsize(global_info);
1292 }
1293 
1294 
1295 
1296 static file_info fi_temp[] = {
1297 	{0, DT_DEFAULT, NULL, 0},
1298 	endrec
1299 };
1300 
1301 /* This routine reads in an array of simple data */
1302 
read_recblock(void * base,int ftype,long numrec,long offset,long bl_size)1303 void *read_recblock(void *base, int ftype, long numrec,
1304 					long offset, long bl_size) {
1305 	int dsize;
1306 
1307 	switch (ftype) {
1308 	case FT_CHAR:
1309 	case FT_BYTE:
1310 		if (base == NULL) base = rmalloc(numrec * sizeof(char));
1311 		buff_blockread(base, numrec, offset);
1312 		if (ftype == FT_CHAR) {
1313 			long i;
1314 			for (i = 0; i < numrec; i++)
1315 				((uchar *)base)[i] = trans_ascii[((uchar *)base)[i]^'r' ];
1316 		}
1317 		return base;
1318 	case FT_SLIST:
1319 		dsize = sizeof(slist);
1320 		break;
1321 	case FT_INT16:
1322 		dsize = sizeof(integer);
1323 		break;
1324 	case FT_UINT16:
1325 	case FT_INT32:
1326 		dsize = sizeof(long);
1327 		break;
1328 	case FT_STR:
1329 	case FT_DICTPTR:
1330 		dsize = sizeof(char *);
1331 		break;
1332 	default:
1333 		fatal("Invalid argument to read_recblock.");
1334 		dsize = 0; /* Silence compiler warnings; this will never actually
1335 		  be reached. */
1336 	}
1337 
1338 	fi_temp[0].ftype = ftype;
1339 	return read_recarray(base, dsize, numrec, fi_temp, "", offset, bl_size);
1340 }
1341 
1342 
write_recblock(void * base,int ftype,long numrec,long offset)1343 long write_recblock(void *base, int ftype, long numrec, long offset) {
1344 	int dsize;
1345 
1346 	if (numrec == 0) return 0;
1347 	switch (ftype) {
1348 	case FT_CHAR: {
1349 		int i;
1350 		for (i = 0; i < numrec; i++)
1351 			((uchar *)base)[i] = ((uchar *)base)[i]^'r';
1352 	}
1353 	/* Fall through.... */
1354 	case FT_BYTE:
1355 		bw_blockwrite(base, numrec, offset);
1356 		return numrec;
1357 	case FT_SLIST:
1358 		dsize = sizeof(slist);
1359 		break;
1360 	case FT_INT16:
1361 		dsize = sizeof(integer);
1362 		break;
1363 	case FT_INT32:
1364 		dsize = sizeof(long);
1365 		break;
1366 	case FT_STR:
1367 	case FT_DICTPTR:
1368 		dsize = sizeof(char *);
1369 		break;
1370 	default:
1371 		fatal("Invalid argument to write_recblock.");
1372 		dsize = 0; /* Silence compiler warnings; this will never actually
1373 		  be reached. */
1374 	}
1375 
1376 	fi_temp[0].ftype = ftype;
1377 	return write_recarray(base, dsize, numrec, fi_temp, offset);
1378 }
1379 
textgets(genfile f,char * buf,size_t n)1380 char *textgets(genfile f, char *buf, size_t n) {
1381 	Common::ReadStream *rs = dynamic_cast<Common::ReadStream *>(f);
1382 	assert(rs);
1383 
1384 	size_t count = 0;
1385 	char c;
1386 
1387 	while (!rs->eos() && (count < (n - 1)) && (c = rs->readByte()) != '\n') {
1388 		buf[count] = c;
1389 		++count;
1390 	}
1391 
1392 	buf[count] = '\0';
1393 	return count ? buf : nullptr;
1394 }
1395 
textgetc(genfile f)1396 char textgetc(genfile f) {
1397 	Common::ReadStream *rs = dynamic_cast<Common::ReadStream *>(f);
1398 	assert(rs);
1399 
1400 	return rs->eos() ? EOF : rs->readByte();
1401 }
1402 
textungetc(genfile f,char c)1403 void textungetc(genfile f, char c) {
1404 	Common::SeekableReadStream *rs = dynamic_cast<Common::SeekableReadStream *>(f);
1405 	assert(rs);
1406 
1407 	rs->seek(-1, SEEK_SET);
1408 }
1409 
texteof(genfile f)1410 bool texteof(genfile f) {
1411 	Common::ReadStream *rs = dynamic_cast<Common::ReadStream *>(f);
1412 	assert(rs);
1413 
1414 	return rs->eos();
1415 }
1416 
textputs(genfile f,const char * s)1417 void textputs(genfile f, const char *s) {
1418 	Common::WriteStream *ws = dynamic_cast<Common::WriteStream *>(f);
1419 	assert(ws);
1420 
1421 	ws->write(s, strlen(s));
1422 }
1423 
1424 /* ------------------------------------------------------------------- */
1425 /* "Profiling" functions             */
1426 /* Routines for timing code execution */
1427 /* These will only work on POSIX systems */
1428 
1429 #ifdef PROFILE_SUPPORT
1430 
1431 static struct tms start;
1432 clock_t start_realtime;
1433 static struct tms delta;
1434 clock_t delta_realtime;
1435 
resetwatch(void)1436 void resetwatch(void) {
1437 	delta.tms_utime = delta.tms_stime = delta.tms_cutime = delta.tms_cstime = 0;
1438 	delta_realtime = 0;
1439 	start_realtime = times(&start);
1440 }
1441 
startwatch(void)1442 void startwatch(void) {
1443 	start_realtime = times(&start);
1444 }
1445 
1446 static char watchbuff[81];
timestring(void)1447 char *timestring(void) {
1448 	sprintf(watchbuff, "User:%ld.%02ld   Sys:%ld.%02ld   Total:%ld.%02ld"
1449 	        "   Real:%ld.%02ld",
1450 	        delta.tms_utime / 100, delta.tms_utime % 100,
1451 	        delta.tms_stime / 100, delta.tms_stime % 100,
1452 	        (delta.tms_utime + delta.tms_stime) / 100,
1453 	        (delta.tms_utime + delta.tms_stime) % 100,
1454 	        delta_realtime / 100, delta_realtime % 100
1455 	       );
1456 	return watchbuff;
1457 }
1458 
stopwatch(void)1459 char *stopwatch(void) {
1460 	struct tms curr;
1461 
1462 	delta_realtime += times(&curr) - start_realtime;
1463 	delta.tms_utime += (curr.tms_utime - start.tms_utime);
1464 	delta.tms_stime += (curr.tms_stime - start.tms_stime);
1465 	delta.tms_cutime += (curr.tms_cutime - start.tms_cutime);
1466 	delta.tms_cstime += (curr.tms_cstime - start.tms_cstime);
1467 	return timestring();
1468 }
1469 
1470 /* 5+7+9+8+4*3+4*?? = 41+?? */
1471 
1472 #endif /* PROFILE_SUPPORT */
1473 
1474 } // End of namespace AGT
1475 } // End of namespace Glk
1476