]> git.pond.sub.org Git - empserver/blob - src/lib/common/xundump.c
Xdumps don't contain records not in use, cope with that:
[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
33  */
34
35 /* FIXME normalize terminology: table/rows/columns or file/records/fields */
36
37 #include <config.h>
38
39 #include <stdio.h>
40 #include <stdlib.h>
41
42 #include <ctype.h>
43 #include <string.h>
44 #include <stdarg.h>
45 #include <time.h>
46
47 #include "file.h"
48 #include "match.h"
49 #include "nsc.h"
50 #include "optlist.h"
51 #include "prototypes.h"
52
53 static char *fname;
54 static int lineno;
55 static int human;
56 static int cur_type, cur_id;
57 static void *cur_obj;
58 static int nflds;
59 static struct castr **fldca;
60 static int *fldidx;
61 static int *caflds;
62
63 static int gripe(char *, ...) ATTRIBUTE((format (printf, 1, 2)));
64 static int deffld(int, char *, int);
65 static int setnum(int, double);
66 static int setstr(int, char *);
67 static int xunsymbol1(char *, struct symbol *, struct castr *, int);
68 static int setsym(int, char *);
69 static int mtsymset(int, long *);
70 static int add2symset(int, long *, char *);
71 static struct symbol *get_symtab(struct castr *);
72 static int xundump1(FILE *, int, struct castr *);
73
74 static int
75 gripe(char *fmt, ...)
76 {
77     va_list ap;
78
79     fprintf(stderr, "%s:%d: ", fname, lineno);
80     va_start(ap, fmt);
81     vfprintf(stderr, fmt, ap);
82     va_end(ap);
83     putc('\n', stderr);
84
85     return -1;
86 }
87
88 static int
89 skipfs(FILE *fp)
90 {
91     int ch;
92
93     do {
94         ch = getc(fp);
95     } while (ch == ' ' || ch == '\t');
96
97     if (ch == '#') {
98         do {
99             ch = getc(fp);
100         } while (ch != EOF && ch != '\n');
101     }
102
103     return ch;
104 }
105
106 static int
107 getid(FILE *fp, char *buf)
108 {
109     int n;
110     if (fscanf(fp, "%1023[^#() \t\n]%n", buf, &n) != 1 || !isalpha(buf[0]))
111         return -1;
112     return n;
113 }
114
115 static char *
116 xuesc(char *buf)
117 {
118     char *src, *dst;
119     int octal_chr, n;
120
121     dst = buf;
122     src = buf;
123     while (*src) {
124         if (*src == '\\') {
125             if (sscanf(++src, "%3o%n", &octal_chr, &n) != 1 || n != 3)
126                 return NULL;
127             *dst++ = (char)octal_chr;
128             src += 3;
129         } else
130             *dst++ = *src++;
131     }
132     *dst = '\0';
133     return buf;
134 }
135
136 static int
137 xufldname(FILE *fp, int i)
138 {
139     int ch, idx;
140     char buf[1024];
141
142     ch = skipfs(fp);
143     switch (ch) {
144     case EOF:
145         return gripe("Unexpected EOF");
146     case '\n':
147         if (i != nflds)
148             return gripe("Header fields missing"); /* TODO which? */
149         lineno++;
150         return 0;
151     default:
152         ungetc(ch, fp);
153         if (getid(fp, buf) < 0)
154             return gripe("Junk in header field %d", i + 1);
155         ch = getc(fp);
156         if (ch != '(') {
157             ungetc(ch, fp);
158             return deffld(i, buf, -1);
159         }
160         ch = getc(fp);
161         ungetc(ch, fp);
162         if (isdigit(ch) || ch == '-' || ch == '+') {
163             if (fscanf(fp, "%d", &idx) != 1)
164                 return gripe("Malformed number in index of header field %d",
165                              i + 1);
166             if (idx < 0)
167                 return gripe("Index must not be negative in header field %d",
168                              i + 1);
169         } else {
170             if (getid(fp, buf) < 0)
171                 return gripe("Malformed index in header field %d", i + 1);
172             return gripe("Symbolic index in header field %d not yet implemented",
173                          i + 1);
174         }
175         ch = getc(fp);
176         if (ch != ')')
177             return gripe("Malformed index in header field %d", i + 1);
178         return deffld(i, buf, idx);
179     }
180 }
181
182 static int
183 xufld(FILE *fp, int i)
184 {
185     int ch;
186     char buf[1024];
187     double dbl;
188     long set;
189
190     ch = skipfs(fp);
191     switch (ch) {
192     case EOF:
193         return gripe("Unexpected EOF");
194     case '\n':
195         if (i != nflds)
196             return gripe("Field %s missing", fldca[i]->ca_name);
197         lineno++;
198         return 0;
199     case '+': case '-': case '.':
200     case '0': case '1': case '2': case '3': case '4':
201     case '5': case '6': case '7': case '8': case '9':
202         ungetc(ch, fp);
203         if (fscanf(fp, "%lg", &dbl) != 1)
204             return gripe("Malformed number in field %d", i + 1);
205         return setnum(i, dbl);
206     case '"':
207         ch = getc(fp);
208         if (ch == '"')
209             buf[0] = 0;
210         else {
211             ungetc(ch, fp);
212             if (fscanf(fp, "%1023[^\"\n]", buf) != 1 || getc(fp) != '"')
213                 return gripe("Malformed string in field %d", i + 1);
214             if (!xuesc(buf))
215                 return gripe("Invalid escape sequence in field %d",
216                              i + 1);
217         }
218         return setstr(i, buf);
219     case '(':
220         if (mtsymset(i, &set) < 0)
221             return -1;
222         for (;;) {
223             ch = skipfs(fp);
224             if (ch == EOF || ch == '\n')
225                 return gripe("Unmatched '(' in field %d", i + 1);
226             if (ch == ')')
227                 break;
228             ungetc(ch, fp);
229             if (getid(fp, buf) < 0)
230                 return gripe("Junk in field %d", i + 1);
231             if (add2symset(i, &set, buf) < 0)
232                 return -1;
233         }
234         return setnum(i, set);
235     default:
236         ungetc(ch, fp);
237         if (getid(fp, buf) < 0)
238             return gripe("Junk in field %d", i + 1);
239         if (!strcmp(buf, "nil"))
240             return setstr(i, NULL);
241         else
242             return setsym(i, buf);
243     }
244 }
245
246 static int
247 xuflds(FILE *fp, int (*parse)(FILE *, int))
248 {
249     int i, ch, res;
250
251     for (i = 0; ; i++) {
252         res = parse(fp, i);
253         if (res < 0)
254             return -1;
255         if (res == 0)
256             return i;
257         ch = getc(fp);
258         if (ch == '\n')
259             ungetc(ch, fp);
260         else if (ch != ' ' && ch != '\t')
261             return gripe("Bad field separator after field %d", i + 1);
262     }
263 }
264
265 static int
266 deffld(int fldno, char *name, int idx)
267 {
268     struct castr *ca = ef_cadef(cur_type);
269     int res;
270
271     res = stmtch(name, ca, offsetof(struct castr, ca_name),
272                      sizeof(struct castr));
273     if (res < 0)
274         return gripe("Header %s of field %d is %s", name, fldno + 1,
275                      res == M_NOTUNIQUE ? "ambiguous" : "unknown");
276     if (ca[res].ca_type != NSC_STRINGY && ca[res].ca_len != 0) {
277         if (idx < 0)
278             return gripe("Header %s requires an index in field %d",
279                          ca[res].ca_name, fldno + 1);
280         if (idx >= ca[res].ca_len)
281             return gripe("Header %s(%d) index out of bounds in field %d",
282                          ca[res].ca_name, idx, fldno + 1);
283         if (idx < caflds[res])
284             return gripe("Duplicate header %s(%d) in field %d",
285                          ca[res].ca_name, idx, fldno + 1);
286         if (idx > caflds[res])
287             return gripe("Expected header %s(%d) in field %d",
288                          ca[res].ca_name, caflds[res], fldno + 1);
289     } else {
290         if (idx >= 0)
291             return gripe("Header %s doesn't take an index in field %d",
292                          ca[res].ca_name, fldno + 1);
293         idx = 0;
294         if (caflds[res])
295             return gripe("Duplicate header %s in field %d",
296                          ca[res].ca_name, fldno + 1);
297     }
298     fldca[fldno] = &ca[res];
299     fldidx[fldno] = idx;
300     caflds[res]++;
301     return 1;
302 }
303
304 static struct castr *
305 getfld(int fldno, int *idx)
306 {
307     if (fldno >= nflds) {
308         gripe("Too many fields, expected only %d", nflds);
309         return NULL;
310     }
311     if (CANT_HAPPEN(fldno < 0))
312         return NULL;
313     if (idx)
314         *idx = fldidx[fldno];
315     return fldca[fldno];
316 }
317
318 static void *
319 getobj(struct castr *ca, int altid)
320 {
321     struct empfile *ep = &empfile[cur_type];
322     int need_sentinel = !EF_IS_GAME_STATE(cur_type);
323
324     if (!cur_obj) {
325         if (ca->ca_table == cur_type)
326             cur_id = altid;
327         if (cur_id >= ep->fids) {
328             /* TODO grow cache (and posssibly file) unless EFF_STATIC */
329             if (cur_id < ep->csize - !!need_sentinel)
330                 ep->cids = ep->fids = cur_id + 1;
331             /* else: ef_ptr() will fail */
332         }
333         cur_obj = ef_ptr(cur_type, cur_id);
334         if (!cur_obj)
335             gripe("Can't put ID %d into table %s, it holds only 0..%d.",
336                   cur_id, ep->name, ep->fids - 1);
337     }
338
339     return cur_obj;
340 }
341
342 static int
343 setnum(int fldno, double dbl)
344 {
345     struct castr *ca;
346     int idx;
347     char *memb_ptr;
348     double old;
349
350     ca = getfld(fldno, &idx);
351     if (!ca)
352         return -1;
353
354     memb_ptr = getobj(ca, (int)dbl);
355     if (!memb_ptr)
356         return -1;
357     memb_ptr += ca->ca_off;
358
359     switch (ca->ca_type) {
360     case NSC_CHAR:
361     case NSC_TYPEID:
362         old = ((signed char *)memb_ptr)[idx];
363         ((signed char *)memb_ptr)[idx] = (signed char)dbl;
364         break;
365     case NSC_UCHAR:
366         old = ((unsigned char *)memb_ptr)[idx];
367         ((unsigned char *)memb_ptr)[idx] = (unsigned char)dbl;
368         break;
369     case NSC_SHORT:
370         old = ((short *)memb_ptr)[idx];
371         ((short *)memb_ptr)[idx] = (short)dbl;
372         break;
373     case NSC_USHORT:
374         old = ((unsigned short *)memb_ptr)[idx];
375         ((unsigned short *)memb_ptr)[idx] = (unsigned short)dbl;
376         break;
377     case NSC_INT:
378         old = ((int *)memb_ptr)[idx];
379         ((int *)memb_ptr)[idx] = (int)dbl;
380         break;
381     case NSC_LONG:
382         old = ((long *)memb_ptr)[idx];
383         ((long *)memb_ptr)[idx] = (long)dbl;
384         break;
385     case NSC_XCOORD:
386         old = ((coord *)memb_ptr)[idx];
387         /* FIXME use variant of xrel() that takes orig instead of nation */
388         if (old >= WORLD_X / 2)
389             old -= WORLD_X;
390         ((coord *)memb_ptr)[idx] = XNORM((coord)dbl);
391         break;
392     case NSC_YCOORD:
393         old = ((coord *)memb_ptr)[idx];
394         /* FIXME use variant of yrel() that takes orig instead of nation */
395         if (old >= WORLD_Y / 2)
396             old -= WORLD_Y;
397         ((coord *)memb_ptr)[idx] = YNORM((coord)dbl);
398         break;
399     case NSC_FLOAT:
400         old = ((float *)memb_ptr)[idx];
401         ((float *)memb_ptr)[idx] = (float)dbl;
402         break;
403     case NSC_DOUBLE:
404         old = ((double *)memb_ptr)[idx];
405         ((double *)memb_ptr)[idx] = dbl;
406         break;
407     case NSC_TIME:
408         old = ((time_t *)memb_ptr)[idx];
409         ((time_t *)memb_ptr)[idx] = (time_t)dbl;
410         break;
411     default:
412         return gripe("Field %d doesn't take numbers", fldno + 1);
413     }
414
415     if ((ca->ca_flags & NSC_CONST) && old != dbl)
416         return gripe("Value for field %d must be %g", fldno + 1, old);
417
418     return 1;
419 }
420
421 static int
422 setstr(int fldno, char *str)
423 {
424     struct castr *ca;
425     int idx;
426     size_t len;
427     char *memb_ptr, *old;
428
429     ca = getfld(fldno, &idx);
430     if (!ca)
431         return -1;
432
433     memb_ptr = getobj(ca, cur_id);
434     if (!memb_ptr)
435         return -1;
436     memb_ptr += ca->ca_off;
437
438     switch (ca->ca_type) {
439     case NSC_STRING:
440         old = ((char **)memb_ptr)[idx];
441         if (!(ca->ca_flags & NSC_CONST))
442             ((char **)memb_ptr)[idx] = str ? strdup(str) : NULL;
443         len = 65535;            /* really SIZE_MAX, but it's C99 */
444         break;
445     case NSC_STRINGY:
446         if (CANT_HAPPEN(idx))
447             return -1;
448         if (!str)
449             return gripe("Field doesn't take nil");
450         len = ca->ca_len;
451         if (strlen(str) > len)
452             return gripe("Field %d takes at most %d characters",
453                          fldno + 1, len);
454         old = memb_ptr;
455         if (!(ca->ca_flags & NSC_CONST))
456             strncpy(memb_ptr, str, len);
457         break;
458     default:
459         return gripe("Field %d doesn't take strings", fldno + 1);
460     }
461
462     if (ca->ca_flags & NSC_CONST) {
463         if (old && (!str || strncmp(old, str, len)))
464             return gripe("Value for field %d must be \"%.*s\"",
465                          fldno + 1, len, old);
466         if (!old && str)
467             return gripe("Value for field %d must be nil", fldno + 1);
468     }
469
470     return 1;
471 }
472
473 static int
474 xunsymbol1(char *id, struct symbol *symtab, struct castr *ca, int n)
475 {
476     int i = stmtch(id, symtab, offsetof(struct symbol, name),
477                    sizeof(struct symbol));
478     if (i < 0)
479         return gripe("%s %s symbol `%s' in field %d",
480                      i == M_NOTUNIQUE ? "Ambiguous" : "Unknown",
481                      ca->ca_name, id, n);
482     return i;
483 }
484
485 static int
486 setsym(int fldno, char *sym)
487 {
488     struct castr *ca;
489     struct symbol *symtab;
490     int i;
491
492     ca = getfld(fldno, NULL);
493     if (!ca)
494         return -1;
495
496     symtab = get_symtab(ca);
497     if (!symtab || (ca->ca_flags & NSC_BITS))
498         return gripe("Field %d doesn't take symbols", fldno + 1);
499
500     i = xunsymbol1(sym, symtab, ca, fldno);
501     if (i < 0)
502         return -1;
503     return setnum(fldno, symtab[i].value);
504 }
505
506 static int
507 mtsymset(int fldno, long *set)
508 {
509     struct castr *ca;
510     struct symbol *symtab;
511
512     ca = getfld(fldno, NULL);
513     if (!ca)
514         return -1;
515
516     symtab = get_symtab(ca);
517     if (!symtab || !(ca->ca_flags & NSC_BITS)) {
518         return gripe("Field %d doesn't take symbol sets", fldno + 1);
519     }
520     *set = 0;
521     return 0;
522 }
523
524 static int
525 add2symset(int fldno, long *set, char *sym)
526 {
527     struct castr *ca;
528     struct symbol *symtab;
529     int i;
530
531     ca = getfld(fldno, NULL);
532     if (!ca)
533         return -1;
534
535     symtab = get_symtab(ca);
536     i = xunsymbol1(sym, symtab, ca, fldno);
537     if (i < 0)
538         return -1;
539     *set |= symtab[i].value;
540     return 0;
541 }
542
543 static struct symbol *
544 get_symtab(struct castr *ca)
545 {
546     int symtype = ca->ca_table;
547     struct symbol *symtab;
548
549     if (symtype == EF_BAD || ef_cadef(symtype) != symbol_ca)
550         return NULL;
551
552     symtab = ef_ptr(symtype, 0);
553     CANT_HAPPEN(!symtab);
554     return symtab;
555 }
556
557 static int
558 xuheader(FILE *fp, int expected_table)
559 {
560     char name[64];
561     int res, ch;
562     int type;
563
564     while ((ch = skipfs(fp)) == '\n')
565         lineno++;
566     if (ch == EOF && expected_table == EF_BAD)
567         return -2;
568     ungetc(ch, fp);
569
570     human = ch == 'c';
571     res = -1;
572     if ((human
573          ? fscanf(fp, "config%*[ \t]%63[^ \t#\n]%n", name, &res) != 1
574          : fscanf(fp, "XDUMP%*[ \t]%63[^ \t#\n]%*[ \t]%*[^ \t#\n]%n",
575                   name, &res) != 1) || res < 0)
576         return gripe("Expected xdump header");
577
578     type = ef_byname(name);
579     if (type < 0)
580         return gripe("Unknown table `%s'", name);
581     if (expected_table != EF_BAD && expected_table != type)
582         return gripe("Expected table `%s', not `%s'",
583                      ef_nameof(expected_table), name);
584
585     if (!ef_cadef(type) || !(ef_flags(type) & EFF_MEM)) {
586         CANT_HAPPEN(expected_table != EF_BAD);
587         return gripe("Undumping of table `%s' not implemented", name);
588     }
589
590     if (skipfs(fp) != '\n')
591         return gripe("Junk after xdump header");
592     lineno++;
593
594     return type;
595 }
596
597 static int
598 xuheader1(FILE *fp, int type, struct castr ca[])
599 {
600     struct castr **fca;
601     int *fidx;
602     int ch, i, j, n;
603
604     if (human) {
605         while ((ch = skipfs(fp)) == '\n')
606             lineno++;
607         ungetc(ch, fp);
608         if (xuflds(fp, xufldname) < 0)
609             return -1;
610     } else {
611         fca = fldca;
612         fidx = fldidx;
613
614         for (i = 0; ca[i].ca_name; i++) {
615             if ((ca[i].ca_flags & NSC_EXTRA))
616                 continue;
617             n = ca[i].ca_type != NSC_STRINGY ? ca[i].ca_len : 0;
618             j = 0;
619             do {
620                 *fca++ = &ca[i];
621                 *fidx++ = j;
622             } while (++j < n);
623         }
624     }
625
626     return 0;
627 }
628
629 static int
630 xutrailer(FILE *fp, int type, int row)
631 {
632     int rows, res;
633
634     res = -1;
635     if (human) {
636         if (fscanf(fp, "config%n",  &res) != 0 || res < 0)
637             return gripe("Malformed table footer");
638     } else {
639         if (fscanf(fp, "%d", &rows) != 1)
640             return gripe("Malformed table footer");
641         if (row != rows)
642             return gripe("Read %d rows, which doesn't match footer "
643                          "%d rows", row, rows);
644     }
645     if (skipfs(fp) != '\n')
646         return gripe("Junk after table footer");
647     lineno++;
648
649     return 0;
650 }
651
652 int
653 xundump(FILE *fp, char *file, int expected_table)
654 {
655     struct castr *ca;
656     int type, nca, i, ch;
657
658     if (fname != file) {
659         fname = file;
660         lineno = 1;
661     }
662
663     if ((type = xuheader(fp, expected_table)) < 0)
664         return type;
665
666     ca = ef_cadef(type);
667     if (CANT_HAPPEN(!ca))
668         return -1;
669
670     nca = nflds = 0;
671     for (i = 0; ca[i].ca_name; i++) {
672         nca++;
673         if (!(ca[i].ca_flags & NSC_EXTRA))
674             nflds += MAX(1, ca[i].ca_type != NSC_STRINGY ? ca[i].ca_len : 0);
675     }
676     fldca = calloc(nflds, sizeof(*fldca));
677     fldidx = calloc(nflds, sizeof(*fldidx));
678     caflds = calloc(nca, sizeof(*caflds));
679     cur_type = type;
680
681     if (xuheader1(fp, type, ca) < 0 || xundump1(fp, type, ca) < 0)
682         type = EF_BAD;
683
684     free(caflds);
685     free(fldidx);
686     free(fldca);
687
688     /* Skip empty lines so that callers can easily check for EOF */
689     while ((ch = skipfs(fp)) == '\n')
690         lineno++;
691     ungetc(ch, fp);
692
693     return type;
694 }
695
696 static int
697 xundump1(FILE *fp, int type, struct castr *ca)
698 {
699     struct empfile *ep = &empfile[type];
700     int need_sentinel = !EF_IS_GAME_STATE(type);
701     int row, n, ch;
702
703     n = 0;
704     for (row = 0;; ++row) {
705         while ((ch = skipfs(fp)) == '\n')
706             lineno++;
707         if (ch == '/')
708             break;
709         ungetc(ch, fp);
710         cur_obj = NULL;
711         cur_id = row;
712         if (xuflds(fp, xufld) < 0)
713             return -1;
714         n = MAX(n, cur_id + 1);
715     }
716
717     if (CANT_HAPPEN(n > ep->fids))
718         n = ep->fids;
719     if (n < ep->fids) {
720         if (EF_IS_GAME_STATE(type) && n != ep->csize)
721             /* TODO truncate file */
722             gripe("Warning: should resize table %s from %d to %d, not implemented",
723                   ef_nameof(type), ep->csize, n);
724         else if (type >= EF_SHIP_CHR && type <= EF_NUKE_CHR)
725             ;                   /* shrinking these is okay */
726         else
727             return gripe("Table %s requires %d rows, got %d",
728                          ef_nameof(type), ep->fids, n);
729     }
730
731     if (need_sentinel) {
732         if (CANT_HAPPEN(n >= ep->csize))
733             return gripe("No space for sentinel");
734         memset(ep->cache + ep->size * n, 0, ep->size);
735     }
736
737     if (xutrailer(fp, type, row) < 0)
738         return -1;
739
740     return 0;
741 }