]> git.pond.sub.org Git - empserver/blob - src/lib/common/xundump.c
528055723622b0caf293f250f64d8d5062992dce
[empserver] / src / lib / common / xundump.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2006, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                           Ken Stevens, Steve McClure
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  *  ---
21  *
22  *  See files README, COPYING and CREDITS in the root of the source
23  *  tree for related information and legal notices.  It is expected
24  *  that future projects/authors will amend these files as needed.
25  *
26  *  ---
27  *
28  *  xundump.c: Load back xdump output
29  * 
30  *  Known contributors to this file:
31  *     Ron Koenderink, 2005
32  *     Markus Armbruster, 2005-2006
33  */
34
35 /*
36  * FIXME:
37  * - Normalize terminology: table/rows/columns or file/records/fields
38  * - Loading tables with NSC_STRING elements more than once leaks memory
39  * TODO:
40  * - Check each partial table supplies the same rows
41  * - Check EFF_CFG tables are dense
42  * - Symbolic references to non-symbol tables
43  * - Symbolic array indexes
44  * TODO, but hardly worth the effort:
45  * - Permit reordering of array elements
46  * - Permit repetition of array elements in split tables
47  */
48
49 #include <config.h>
50
51 #include <ctype.h>
52 #include <stdarg.h>
53 #include <stdio.h>
54 #include <time.h>
55 #include "file.h"
56 #include "match.h"
57 #include "nsc.h"
58 #include "optlist.h"
59 #include "prototypes.h"
60
61 static char *fname;
62 static int lineno;
63 static int human;
64 static int ellipsis, is_partial;
65 static int cur_type, cur_id;
66 static void *cur_obj;
67 static int cur_obj_is_blank;
68 static int nflds;
69 static struct castr **fldca;
70 static int *fldidx;
71 static int *caflds;
72 static unsigned char *caseen;
73
74 static int gripe(char *, ...) ATTRIBUTE((format (printf, 1, 2)));
75 static int deffld(int, char *, int);
76 static int defellipsis(int fldno);
77 static int chkflds(void);
78 static int setnum(int, double);
79 static int setstr(int, char *);
80 static int xunsymbol(char *, struct castr *, int);
81 static int setsym(int, char *);
82 static int mtsymset(int, long *);
83 static int add2symset(int, long *, char *);
84 static int xundump1(FILE *, int, struct castr *);
85 static int xundump2(FILE *, int, struct castr *);
86
87 static int
88 gripe(char *fmt, ...)
89 {
90     va_list ap;
91
92     fprintf(stderr, "%s:%d: ", fname, lineno);
93     va_start(ap, fmt);
94     vfprintf(stderr, fmt, ap);
95     va_end(ap);
96     putc('\n', stderr);
97
98     return -1;
99 }
100
101 static int
102 skipfs(FILE *fp)
103 {
104     int ch;
105
106     do {
107         ch = getc(fp);
108     } while (ch == ' ' || ch == '\t');
109
110     if (ch == '#') {
111         do {
112             ch = getc(fp);
113         } while (ch != EOF && ch != '\n');
114     }
115
116     return ch;
117 }
118
119 static int
120 getid(FILE *fp, char *buf)
121 {
122     int n;
123     if (fscanf(fp, "%1023[^#()<>=#\" \t\n]%n", buf, &n) != 1
124         || !isalpha(buf[0]))
125         return -1;
126     return n;
127 }
128
129 static char *
130 xuesc(char *buf)
131 {
132     char *src, *dst;
133     int octal_chr, n;
134
135     dst = buf;
136     src = buf;
137     while (*src) {
138         if (*src == '\\') {
139             if (sscanf(++src, "%3o%n", &octal_chr, &n) != 1 || n != 3)
140                 return NULL;
141             *dst++ = (char)octal_chr;
142             src += 3;
143         } else
144             *dst++ = *src++;
145     }
146     *dst = '\0';
147     return buf;
148 }
149
150 static int
151 xufldname(FILE *fp, int i)
152 {
153     int ch, idx;
154     char buf[1024];
155
156     ch = skipfs(fp);
157     switch (ch) {
158     case EOF:
159         return gripe("Unexpected EOF");
160     case '\n':
161         if (chkflds() < 0)
162             return -1;
163         lineno++;
164         return 0;
165     case '.':
166         if (getc(fp) != '.' || getc(fp) != '.')
167             return gripe("Junk in header field %d", i + 1);
168         if (i == 0)
169             return gripe("... not allowed in field 1");
170         if (defellipsis(i) < 0)
171             return -1;
172         ch = skipfs(fp);
173         if (ch != EOF && ch != '\n')
174             return gripe("Junk after ...");
175         ungetc(ch, fp);
176         return 1;
177     default:
178         ungetc(ch, fp);
179         if (getid(fp, buf) < 0)
180             return gripe("Junk in header field %d", i + 1);
181         ch = getc(fp);
182         if (ch != '(') {
183             ungetc(ch, fp);
184             return deffld(i, buf, -1);
185         }
186         ch = getc(fp);
187         ungetc(ch, fp);
188         if (isdigit(ch) || ch == '-' || ch == '+') {
189             if (fscanf(fp, "%d", &idx) != 1)
190                 return gripe("Malformed number in index of header field %d",
191                              i + 1);
192             if (idx < 0)
193                 return gripe("Index must not be negative in header field %d",
194                              i + 1);
195         } else {
196             if (getid(fp, buf) < 0)
197                 return gripe("Malformed index in header field %d", i + 1);
198             return gripe("Symbolic index in header field %d not yet implemented",
199                          i + 1);
200         }
201         ch = getc(fp);
202         if (ch != ')')
203             return gripe("Malformed index in header field %d", i + 1);
204         return deffld(i, buf, idx);
205     }
206 }
207
208 static int
209 xufld(FILE *fp, int i)
210 {
211     int ch;
212     char buf[1024];
213     double dbl;
214     long set;
215
216     ch = skipfs(fp);
217     switch (ch) {
218     case EOF:
219         return gripe("Unexpected EOF");
220     case '\n':
221         if (i != nflds) {
222             if (fldca[i]->ca_type != NSC_STRINGY && fldca[i]->ca_len)
223                 return gripe("Field %s(%d) missing",
224                              fldca[i]->ca_name, fldidx[i]);
225             return gripe("Field %s missing", fldca[i]->ca_name);
226         }
227         lineno++;
228         return 0;
229     case '+': case '-': case '.':
230     case '0': case '1': case '2': case '3': case '4':
231     case '5': case '6': case '7': case '8': case '9':
232         ungetc(ch, fp);
233         if (fscanf(fp, "%lg", &dbl) != 1)
234             return gripe("Malformed number in field %d", i + 1);
235         return setnum(i, dbl);
236     case '"':
237         ch = getc(fp);
238         if (ch == '"')
239             buf[0] = 0;
240         else {
241             ungetc(ch, fp);
242             if (fscanf(fp, "%1023[^\"\n]", buf) != 1 || getc(fp) != '"')
243                 return gripe("Malformed string in field %d", i + 1);
244             if (!xuesc(buf))
245                 return gripe("Invalid escape sequence in field %d",
246                              i + 1);
247         }
248         return setstr(i, buf);
249     case '(':
250         if (mtsymset(i, &set) < 0)
251             return -1;
252         for (;;) {
253             ch = skipfs(fp);
254             if (ch == EOF || ch == '\n')
255                 return gripe("Unmatched '(' in field %d", i + 1);
256             if (ch == ')')
257                 break;
258             ungetc(ch, fp);
259             if (getid(fp, buf) < 0)
260                 return gripe("Junk in field %d", i + 1);
261             if (add2symset(i, &set, buf) < 0)
262                 return -1;
263         }
264         return setnum(i, set);
265     default:
266         ungetc(ch, fp);
267         if (getid(fp, buf) < 0)
268             return gripe("Junk in field %d", i + 1);
269         if (!strcmp(buf, "nil"))
270             return setstr(i, NULL);
271         else
272             return setsym(i, buf);
273     }
274 }
275
276 static int
277 xuflds(FILE *fp, int (*parse)(FILE *, int))
278 {
279     int i, ch, res;
280
281     for (i = 0; ; i++) {
282         res = parse(fp, i);
283         if (res < 0)
284             return -1;
285         if (res == 0)
286             return i;
287         ch = getc(fp);
288         if (ch == '\n')
289             ungetc(ch, fp);
290         else if (ch != ' ' && ch != '\t')
291             return gripe("Bad field separator after field %d", i + 1);
292     }
293 }
294
295 static int
296 deffld(int fldno, char *name, int idx)
297 {
298     struct castr *ca = ef_cadef(cur_type);
299     int res;
300
301     res = stmtch(name, ca, offsetof(struct castr, ca_name),
302                      sizeof(struct castr));
303     if (res < 0)
304         return gripe("Header %s of field %d is %s", name, fldno + 1,
305                      res == M_NOTUNIQUE ? "ambiguous" : "unknown");
306     if (ca[res].ca_type != NSC_STRINGY && ca[res].ca_len != 0) {
307         if (idx < 0)
308             return gripe("Header %s requires an index in field %d",
309                          ca[res].ca_name, fldno + 1);
310         if (idx >= ca[res].ca_len)
311             return gripe("Header %s(%d) index out of bounds in field %d",
312                          ca[res].ca_name, idx, fldno + 1);
313         if (idx < caflds[res])
314             return gripe("Duplicate header %s(%d) in field %d",
315                          ca[res].ca_name, idx, fldno + 1);
316         if (idx > caflds[res])
317             return gripe("Expected header %s(%d) in field %d",
318                          ca[res].ca_name, caflds[res], fldno + 1);
319     } else {
320         if (idx >= 0)
321             return gripe("Header %s doesn't take an index in field %d",
322                          ca[res].ca_name, fldno + 1);
323         idx = 0;
324         if (caflds[res] && !caseen[res])
325             /* FIXME doesn't catch dupes within table part when caseen[res] */
326             return gripe("Duplicate header %s in field %d",
327                          ca[res].ca_name, fldno + 1);
328     }
329     fldca[fldno] = &ca[res];
330     fldidx[fldno] = idx;
331     if (!caseen[res])
332         caflds[res]++;
333     return 1;
334 }
335
336 static int
337 defellipsis(int fldno)
338 {
339     struct castr *ca = ef_cadef(cur_type);
340
341     if (ca[0].ca_table != cur_type)
342         return gripe("Table %s doesn't support ...", ef_nameof(cur_type));
343     ellipsis = fldno;
344     is_partial = 1;
345     return 0;
346 }
347
348 static int
349 chkflds(void)
350 {
351     struct castr *ca = ef_cadef(cur_type);
352     int i, len, res = 0;
353
354     if (is_partial) {
355         /* Require index field */
356         if (!caflds[0])
357             return gripe("Header field %s required with ...", ca[0].ca_name);
358         /* Want the index field again in continued table: */
359         caflds[0] = 0;
360         return 0;
361     }
362
363     for (i = 0; ca[i].ca_name; i++) {
364         if (ca[i].ca_flags & NSC_EXTRA)
365             continue;
366         len = ca[i].ca_type != NSC_STRINGY ? ca[i].ca_len : 0;
367         if (!len && !caflds[i])
368             res = gripe("Header field %s missing", ca[i].ca_name);
369         else if (len && caflds[i] == len - 1)
370             res = gripe("Header field %s(%d) missing",
371                         ca[i].ca_name, len - 1);
372         else if (len && caflds[i] < len - 1)
373             res = gripe("Header fields %s(%d) ... %s(%d) missing",
374                         ca[i].ca_name, caflds[i], ca[i].ca_name, len - 1);
375     }
376
377     return res;
378 }
379
380 static struct castr *
381 getfld(int fldno, int *idx)
382 {
383     if (fldno >= nflds) {
384         gripe("Too many fields, expected only %d", nflds);
385         return NULL;
386     }
387     if (CANT_HAPPEN(fldno < 0))
388         return NULL;
389     if (idx)
390         *idx = fldidx[fldno];
391     return fldca[fldno];
392 }
393
394 static int
395 fldval_must_match(int fldno)
396 {
397     struct castr *ca = ef_cadef(cur_type);
398     int i = fldca[fldno] - ca;
399
400     return (!cur_obj_is_blank && (fldca[fldno]->ca_flags & NSC_CONST))
401         || caseen[i];
402 }
403
404 static void *
405 getobj(struct castr *ca, int altid)
406 {
407     struct empfile *ep = &empfile[cur_type];
408     int need_sentinel = !EF_IS_GAME_STATE(cur_type);
409
410     if (!cur_obj) {
411         if (ca->ca_table == cur_type)
412             cur_id = altid;
413         cur_obj_is_blank = cur_id >= ep->fids;
414         if (cur_obj_is_blank) {
415             /* TODO grow cache (and posssibly file) unless EFF_STATIC */
416             if (cur_id < ep->csize - !!need_sentinel)
417                 ep->cids = ep->fids = cur_id + 1;
418             /* else: ef_ptr() will fail */
419         }
420         cur_obj = ef_ptr(cur_type, cur_id);
421         if (!cur_obj)
422             gripe("Can't put ID %d into table %s, it holds only 0..%d.",
423                   cur_id, ep->name, ep->fids - 1);
424     }
425
426     return cur_obj;
427 }
428
429 static int
430 setnum(int fldno, double dbl)
431 {
432     struct castr *ca;
433     int idx;
434     char *memb_ptr;
435     double old;
436
437     ca = getfld(fldno, &idx);
438     if (!ca)
439         return -1;
440
441     memb_ptr = getobj(ca, (int)dbl);
442     if (!memb_ptr)
443         return -1;
444     memb_ptr += ca->ca_off;
445
446     /* FIXME check assignment preserves value */
447     switch (ca->ca_type) {
448     case NSC_CHAR:
449         old = ((signed char *)memb_ptr)[idx];
450         ((signed char *)memb_ptr)[idx] = (signed char)dbl;
451         break;
452     case NSC_UCHAR:
453     case NSC_HIDDEN:
454         old = ((unsigned char *)memb_ptr)[idx];
455         ((unsigned char *)memb_ptr)[idx] = (unsigned char)dbl;
456         break;
457     case NSC_SHORT:
458         old = ((short *)memb_ptr)[idx];
459         ((short *)memb_ptr)[idx] = (short)dbl;
460         break;
461     case NSC_USHORT:
462         old = ((unsigned short *)memb_ptr)[idx];
463         ((unsigned short *)memb_ptr)[idx] = (unsigned short)dbl;
464         break;
465     case NSC_INT:
466         old = ((int *)memb_ptr)[idx];
467         ((int *)memb_ptr)[idx] = (int)dbl;
468         break;
469     case NSC_LONG:
470         old = ((long *)memb_ptr)[idx];
471         ((long *)memb_ptr)[idx] = (long)dbl;
472         break;
473     case NSC_XCOORD:
474         old = ((coord *)memb_ptr)[idx];
475         /* FIXME use variant of xrel() that takes orig instead of nation */
476         if (old >= WORLD_X / 2)
477             old -= WORLD_X;
478         ((coord *)memb_ptr)[idx] = XNORM((coord)dbl);
479         break;
480     case NSC_YCOORD:
481         old = ((coord *)memb_ptr)[idx];
482         /* FIXME use variant of yrel() that takes orig instead of nation */
483         if (old >= WORLD_Y / 2)
484             old -= WORLD_Y;
485         ((coord *)memb_ptr)[idx] = YNORM((coord)dbl);
486         break;
487     case NSC_FLOAT:
488         old = ((float *)memb_ptr)[idx];
489         ((float *)memb_ptr)[idx] = (float)dbl;
490         break;
491     case NSC_DOUBLE:
492         old = ((double *)memb_ptr)[idx];
493         ((double *)memb_ptr)[idx] = dbl;
494         break;
495     case NSC_TIME:
496         old = ((time_t *)memb_ptr)[idx];
497         ((time_t *)memb_ptr)[idx] = (time_t)dbl;
498         break;
499     default:
500         return gripe("Field %d doesn't take numbers", fldno + 1);
501     }
502
503     if (fldval_must_match(fldno) && old != dbl)
504         return gripe("Value for field %d must be %g", fldno + 1, old);
505
506     return 1;
507 }
508
509 static int
510 setstr(int fldno, char *str)
511 {
512     struct castr *ca;
513     int must_match, idx;
514     size_t len;
515     char *memb_ptr, *old;
516
517     ca = getfld(fldno, &idx);
518     if (!ca)
519         return -1;
520
521     memb_ptr = getobj(ca, cur_id);
522     if (!memb_ptr)
523         return -1;
524     memb_ptr += ca->ca_off;
525     must_match = fldval_must_match(fldno);
526
527     switch (ca->ca_type) {
528     case NSC_STRING:
529         old = ((char **)memb_ptr)[idx];
530         if (!must_match)
531             ((char **)memb_ptr)[idx] = str ? strdup(str) : NULL;
532         len = 65535;            /* really SIZE_MAX, but that's C99 */
533         break;
534     case NSC_STRINGY:
535         if (CANT_HAPPEN(idx))
536             return -1;
537         if (!str)
538             return gripe("Field %d doesn't take nil", fldno + 1);
539         len = ca->ca_len;
540         if (strlen(str) > len)
541             return gripe("Field %d takes at most %d characters",
542                          fldno + 1, (int)len);
543         old = memb_ptr;
544         if (!must_match)
545             strncpy(memb_ptr, str, len);
546         break;
547     default:
548         return gripe("Field %d doesn't take strings", fldno + 1);
549     }
550
551     if (must_match) {
552         if (old && (!str || strncmp(old, str, len)))
553             return gripe("Value for field %d must be \"%.*s\"",
554                          fldno + 1, (int)len, old);
555         if (!old && str)
556             return gripe("Value for field %d must be nil", fldno + 1);
557     }
558
559     return 1;
560 }
561
562 static int
563 xunsymbol(char *id, struct castr *ca, int n)
564 {
565     int i = ef_elt_byname(ca->ca_table, id);
566     if (i < 0)
567         return gripe("%s %s symbol `%s' in field %d",
568                      i == M_NOTUNIQUE ? "Ambiguous" : "Unknown",
569                      ca->ca_name, id, n + 1);
570     return i;
571 }
572
573 static int
574 symval(struct castr *ca, int i)
575 {
576     int type = ca->ca_table;
577
578     if (ef_check(type) < 0)
579         return -1;
580     if (ef_cadef(type) == symbol_ca)
581         /* symbol table, value is in the table */
582         return ((struct symbol *)ef_ptr(type, i))->value;
583     /* value is the table index */
584     return i;
585 }
586
587 static int
588 setsym(int fldno, char *sym)
589 {
590     struct castr *ca;
591     int i;
592
593     ca = getfld(fldno, NULL);
594     if (!ca)
595         return -1;
596
597     if (ca->ca_table == EF_BAD || (ca->ca_flags & NSC_BITS))
598         return gripe("Field %d doesn't take symbols", fldno + 1);
599
600     i = xunsymbol(sym, ca, fldno);
601     if (i < 0)
602         return -1;
603     return setnum(fldno, symval(ca, i));
604 }
605
606 static int
607 mtsymset(int fldno, long *set)
608 {
609     struct castr *ca;
610
611     ca = getfld(fldno, NULL);
612     if (!ca)
613         return -1;
614
615     if (ca->ca_table == EF_BAD || ef_cadef(ca->ca_table) != symbol_ca
616         || !(ca->ca_flags & NSC_BITS))
617         return gripe("Field %d doesn't take symbol sets", fldno + 1);
618     *set = 0;
619     return 0;
620 }
621
622 static int
623 add2symset(int fldno, long *set, char *sym)
624 {
625     struct castr *ca;
626     int i;
627
628     ca = getfld(fldno, NULL);
629     if (!ca)
630         return -1;
631
632     i = xunsymbol(sym, ca, fldno);
633     if (i < 0)
634         return -1;
635     *set |= symval(ca, i);
636     return 0;
637 }
638
639 static int
640 xuheader(FILE *fp, int expected_table)
641 {
642     char name[64];
643     int res, ch;
644     int type;
645
646     while ((ch = skipfs(fp)) == '\n')
647         lineno++;
648     if (ch == EOF && expected_table == EF_BAD)
649         return -2;
650     ungetc(ch, fp);
651
652     human = ch == 'c';
653     res = -1;
654     if ((human
655          ? fscanf(fp, "config%*[ \t]%63[^ \t#\n]%n", name, &res) != 1
656          : fscanf(fp, "XDUMP%*[ \t]%63[^ \t#\n]%*[ \t]%*[^ \t#\n]%n",
657                   name, &res) != 1) || res < 0)
658         return gripe("Expected xdump header");
659
660     type = ef_byname(name);
661     if (type < 0)
662         return gripe("Unknown table `%s'", name);
663     if (expected_table != EF_BAD && expected_table != type)
664         return gripe("Expected table `%s', not `%s'",
665                      ef_nameof(expected_table), name);
666
667     if (!ef_cadef(type) || !(ef_flags(type) & EFF_MEM)) {
668         CANT_HAPPEN(expected_table != EF_BAD);
669         return gripe("Table `%s' is not permitted here", name);
670     }
671
672     if (skipfs(fp) != '\n')
673         return gripe("Junk after xdump header");
674     lineno++;
675
676     return type;
677 }
678
679 static int
680 xuheader1(FILE *fp, int type, struct castr ca[])
681 {
682     struct castr **fca;
683     int *fidx;
684     int ch, i, j, n;
685
686     if (human) {
687         while ((ch = skipfs(fp)) == '\n')
688             lineno++;
689         ungetc(ch, fp);
690         ellipsis = 0;
691         nflds = xuflds(fp, xufldname);
692         if (nflds < 0)
693             return -1;
694         nflds -= ellipsis != 0;
695     } else {
696         fca = fldca;
697         fidx = fldidx;
698
699         for (i = 0; ca[i].ca_name; i++) {
700             if ((ca[i].ca_flags & NSC_EXTRA))
701                 continue;
702             n = ca[i].ca_type != NSC_STRINGY ? ca[i].ca_len : 0;
703             j = 0;
704             do {
705                 *fca++ = &ca[i];
706                 *fidx++ = j;
707             } while (++j < n);
708         }
709
710         nflds = fidx - fldidx;
711     }
712
713     return 0;
714 }
715
716 static int
717 xutrailer(FILE *fp, int type, int row)
718 {
719     int rows, res;
720
721     res = -1;
722     if (human) {
723         if (fscanf(fp, "config%n",  &res) != 0 || res < 0)
724             return gripe("Malformed table footer");
725     } else {
726         if (fscanf(fp, "%d", &rows) != 1)
727             return gripe("Malformed table footer");
728         if (row != rows)
729             return gripe("Read %d rows, which doesn't match footer "
730                          "%d rows", row, rows);
731     }
732     if (skipfs(fp) != '\n')
733         return gripe("Junk after table footer");
734     lineno++;
735
736     return 0;
737 }
738
739 int
740 xundump(FILE *fp, char *file, int expected_table)
741 {
742     struct castr *ca;
743     int type, nca, nf, i, ch;
744
745     if (fname != file) {
746         fname = file;
747         lineno = 1;
748     }
749
750     if ((type = xuheader(fp, expected_table)) < 0)
751         return type;
752
753     ca = ef_cadef(type);
754     if (CANT_HAPPEN(!ca))
755         return -1;
756
757     nca = nf = 0;
758     for (i = 0; ca[i].ca_name; i++) {
759         nca++;
760         if (!(ca[i].ca_flags & NSC_EXTRA))
761             nf += MAX(1, ca[i].ca_type != NSC_STRINGY ? ca[i].ca_len : 0);
762     }
763     fldca = calloc(nf, sizeof(*fldca));
764     fldidx = calloc(nf, sizeof(*fldidx));
765     caflds = calloc(nca, sizeof(*caflds));
766     caseen = calloc(nca, sizeof(*caseen));
767     cur_type = type;
768
769     if (xundump2(fp, type, ca) < 0)
770         type = EF_BAD;
771
772     free(caseen);
773     free(caflds);
774     free(fldidx);
775     free(fldca);
776
777     /* Skip empty lines so that callers can easily check for EOF */
778     while ((ch = skipfs(fp)) == '\n')
779         lineno++;
780     ungetc(ch, fp);
781
782     return type;
783 }
784
785 static int
786 xundump2(FILE *fp, int type, struct castr *ca)
787 {
788     int i;
789
790     is_partial = 0;
791     for (;;) {
792         if (xuheader1(fp, type, ca) < 0)
793             return -1;
794         if (xundump1(fp, type, ca) < 0)
795             return -1;
796         if (!ellipsis)
797             return 0;
798         for (i = 0; ca[i].ca_name; i++)
799             caseen[i] = caflds[i] != 0;
800         if (xuheader(fp, type) < 0)
801             return -1;
802     }
803 }
804
805 static int
806 xundump1(FILE *fp, int type, struct castr *ca)
807 {
808     struct empfile *ep = &empfile[type];
809     int need_sentinel = !EF_IS_GAME_STATE(type);
810     int row, n, ch;
811
812     n = 0;
813     for (row = 0;; ++row) {
814         while ((ch = skipfs(fp)) == '\n')
815             lineno++;
816         if (ch == '/')
817             break;
818         ungetc(ch, fp);
819         cur_obj = NULL;
820         cur_id = row;
821         if (xuflds(fp, xufld) < 0)
822             return -1;
823         n = MAX(n, cur_id + 1);
824     }
825
826     if (CANT_HAPPEN(n > ep->fids))
827         n = ep->fids;
828     if (n < ep->fids) {
829         if (EF_IS_GAME_STATE(type) && n != ep->csize)
830             /* TODO truncate file */
831             gripe("Warning: should resize table %s from %d to %d, not implemented",
832                   ef_nameof(type), ep->csize, n);
833         else if (type >= EF_SHIP_CHR && type <= EF_NUKE_CHR)
834             ep->cids = ep->fids = n;
835         else
836             return gripe("Table %s requires %d rows, got %d",
837                          ef_nameof(type), ep->fids, n);
838     }
839
840     if (need_sentinel) {
841         if (CANT_HAPPEN(n >= ep->csize))
842             return gripe("No space for sentinel");
843         memset(ep->cache + ep->size * n, 0, ep->size);
844     }
845
846     if (xutrailer(fp, type, row) < 0)
847         return -1;
848
849     return 0;
850 }