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