dump: Work on dumping to an XML-like format.
[pspp] / dump.c
1 #include <float.h>
2 #include <stdbool.h>
3 #include <stdint.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <sys/stat.h>
8 #include <unistd.h>
9
10 static uint8_t *data;
11 static size_t n;
12
13 static bool
14 all_ascii(const uint8_t *p, size_t n)
15 {
16   for (size_t i = 0; i < n; i++)
17     if (p[i] < 32 || p[i] > 126)
18       return false;
19   return true;
20 }
21
22 static size_t
23 try_find(const char *target, size_t target_len)
24 {
25   const uint8_t *pos = (const uint8_t *) memmem (data, n, target, target_len);
26   return pos ? pos - data : 0;
27 }
28
29 static size_t
30 try_find_tail(const char *target, size_t target_len)
31 {
32   size_t pos = try_find(target, target_len);
33   return pos ? pos + target_len : 0;
34 }
35
36 static size_t
37 find(const char *target, size_t target_len)
38 {
39   size_t pos = try_find(target, target_len);
40   if (!pos)
41     {
42       fprintf (stderr, "not found\n");
43       exit(1);
44     }
45   return pos;
46 }
47
48 static size_t
49 find_tail(const char *target, size_t target_len)
50 {
51   size_t pos = try_find_tail(target, target_len);
52   if (!pos)
53     {
54       fprintf (stderr, "not found\n");
55       exit(1);
56     }
57   return pos;
58 }
59
60 size_t pos;
61
62 #define XSTR(x) #x
63 #define STR(x) XSTR(x)
64 #define WHERE __FILE__":" STR(__LINE__)
65
66 static unsigned int
67 get_u32(void)
68 {
69   uint32_t x;
70   memcpy(&x, &data[pos], 4);
71   pos += 4;
72   return x;
73 }
74
75 static double
76 get_double(void)
77 {
78   double x;
79   memcpy(&x, &data[pos], 8);
80   pos += 8;
81   return x;
82 }
83
84 static bool
85 match_u32(uint32_t x)
86 {
87   if (get_u32() == x)
88     return true;
89   pos -= 4;
90   return false;
91 }
92
93 static void
94 match_u32_assert(uint32_t x, const char *where)
95 {
96   unsigned int y = get_u32();
97   if (x != y)
98     {
99       fprintf(stderr, "%s: 0x%x: expected i%u, got i%u\n", where, pos - 4, x, y);
100       exit(1);
101     }
102 }
103 #define match_u32_assert(x) match_u32_assert(x, WHERE)
104
105 static bool
106 match_byte(uint8_t b)
107 {
108   if (pos < n && data[pos] == b)
109     {
110       pos++;
111       return true;
112     }
113   else
114     return false;
115 }
116
117 static void
118 match_byte_assert(uint8_t b, const char *where)
119 {
120   if (!match_byte(b))
121     {
122       fprintf(stderr, "%s: 0x%x: expected %02x, got %02x\n", where, pos, b, data[pos]);
123       exit(1);
124     }
125 }
126 #define match_byte_assert(b) match_byte_assert(b, WHERE)
127
128 static void
129 dump_raw(FILE *stream, int start, int end, const char *separator)
130 {
131   for (size_t i = start; i < end; )
132     {
133       if (i + 5 <= n
134           && data[i]
135           //&& !data[i + 1]
136           && !data[i + 2]
137           && !data[i + 3]
138           && i + 4 + data[i] + data[i + 1] * 256 <= end
139           && all_ascii(&data[i + 4], data[i] + data[i + 1] * 256))
140         {
141           fprintf(stream, "%s\"", separator);
142           fwrite(&data[i + 4], 1, data[i] + data[i + 1] * 256, stream);
143           fputs("\" ", stream);
144
145           i += 4 + data[i] + data[i + 1] * 256;
146         }
147       else if (i + 12 <= end
148                && data[i + 1] == 40
149                && data[i + 2] == 5
150                && data[i + 3] == 0)
151         {
152           double d;
153
154           memcpy (&d, &data[i + 4], 8);
155           fprintf (stream, "F40.%d(%.*f)%s", data[i], data[i], d, separator);
156           i += 12;
157         }
158       else if (i + 12 <= end
159                && data[i + 1] == 40
160                && data[i + 2] == 31
161                && data[i + 3] == 0)
162         {
163           double d;
164
165           memcpy (&d, &data[i + 4], 8);
166           fprintf (stream, "PCT40.%d(%.*f)%s", data[i], data[i], d, separator);
167           i += 12;
168         }
169       else if (i + 4 <= end
170                && (data[i] && data[i] != 88 && data[i] != 0x41)
171                && !data[i + 1]
172                && !data[i + 2]
173                && !data[i + 3])
174         {
175           fprintf (stream, "i%d ", data[i]);
176           i += 4;
177         }
178       else
179         {
180           fprintf(stream, "%02x ", data[i]);
181           i++;
182         }
183     }
184
185
186 }
187
188 static char *
189 get_string(const char *where)
190 {
191   if (1
192       /*data[pos + 1] == 0 && data[pos + 2] == 0 && data[pos + 3] == 0*/
193       /*&& all_ascii(&data[pos + 4], data[pos])*/)
194     {
195       int len = data[pos] + data[pos + 1] * 256;
196       char *s = malloc(len + 1);
197
198       memcpy(s, &data[pos + 4], len);
199       s[len] = 0;
200       pos += 4 + len;
201       return s;
202     }
203   else
204     {
205       fprintf(stderr, "%s: 0x%x: expected string\n", where, pos);
206       exit(1);
207     }
208 }
209 #define get_string() get_string(WHERE)
210
211 static char *
212 dump_nested_string(void)
213 {
214   char *s = NULL;
215
216   match_byte_assert (0);
217   match_byte_assert (0);
218   int outer_end = pos + get_u32();
219   int inner_end = pos + get_u32();
220   if (pos != inner_end)
221     {
222       match_u32_assert(0);
223       if (match_byte(0x31))
224         s = get_string();
225       else
226         match_byte_assert(0x58);
227       if (pos != inner_end)
228         {
229           fprintf(stderr, "inner end discrepancy\n");
230           exit(1);
231         }
232     }
233   match_byte_assert(0x58);
234   match_byte_assert(0x58);
235   if (pos != outer_end)
236     {
237       fprintf(stderr, "outer end discrepancy\n");
238       exit(1);
239     }
240
241   return s;
242 }
243
244 static void
245 dump_value_31(FILE *stream)
246 {
247   if (match_byte (0x31))
248     {
249       if (match_u32 (0))
250         {
251           if (match_u32 (1))
252             {
253               /* Only "a" observed as a sample value (although it appears 44 times in the corpus). */
254               get_string();
255             }
256           else
257             match_u32_assert (0);
258
259           int outer_end = pos + get_u32();
260           int inner_end = pos + get_u32();
261           match_u32_assert(0);
262           if (match_byte(0x31))
263             {
264               /* Appears to be a template string, e.g. '^1 cells (^2) expf < 5. Min exp = ^3...'.
265                  Probably doesn't actually appear in output because many examples look unpolished,
266                  e.g. 'partial list cases value ^1 shown upper...' */
267               get_string();
268             }
269           else
270             match_byte_assert(0x58);
271           if (pos != inner_end)
272             {
273               fprintf(stderr, "inner end discrepancy\n");
274               exit(1);
275             }
276
277           if (match_byte(0x31))
278             {
279               /* Only one example in the corpus. */
280               match_byte(0);
281               match_byte(0);
282               match_byte(0);
283               match_byte_assert(1);
284               get_string();     /* foreground */
285               get_string();     /* background */
286               get_string();     /* font */
287               match_byte_assert(12); /* size? */
288             }
289           else
290             match_byte_assert(0x58);
291           match_byte_assert(0x58);
292           if (pos != outer_end)
293             {
294               fprintf(stderr, "outer end discrepancy\n");
295               exit(1);
296             }
297         }
298       else if (match_u32 (1))
299         {
300           fprintf(stream, "(footnote %d) ", get_u32());
301           dump_nested_string();
302         }
303       else if (match_u32 (2))
304         {
305           fprintf(stream, "(special 2)");
306           match_byte_assert(0);
307           match_byte_assert(0);
308           if (!match_u32 (2))
309             match_u32_assert(1);
310           dump_nested_string(); /* Our corpus doesn't contain any examples with strings though. */
311         }
312       else
313         {
314           match_u32_assert(3);
315           fprintf(stream, "(special 3)");
316           match_byte_assert(0);
317           match_byte_assert(0);
318           match_byte_assert(1);
319           match_byte_assert(0);
320           match_u32_assert(2);
321           dump_nested_string(); /* Our corpus doesn't contain any examples with strings though. */
322         }
323     }
324   else
325     match_byte_assert (0x58);
326 }
327
328 static const char *
329 format_to_string (int type)
330 {
331   static char tmp[16];
332   switch (type)
333     {
334     case 1: return "A";
335     case 2: return "AHEX";
336     case 3: return "COMMA";
337     case 4: return "DOLLAR";
338     case 5: case 40: return "F";
339     case 6: return "IB";
340     case 7: return "PIBHEX";
341     case 8: return "P";
342     case 9: return "PIB";
343     case 10: return "PK";
344     case 11: return "RB";
345     case 12: return "RBHEX";
346     case 15: return "Z";
347     case 16: return "N";
348     case 17: return "E";
349     case 20: return "DATE";
350     case 21: return "TIME";
351     case 22: return "DATETIME";
352     case 23: return "ADATE";
353     case 24: return "JDATE";
354     case 25: return "DTIME";
355     case 26: return "WKDAY";
356     case 27: return "MONTH";
357     case 28: return "MOYR";
358     case 29: return "QYR";
359     case 30: return "WKYR";
360     case 31: return "PCT";
361     case 32: return "DOT";
362     case 33: return "CCA";
363     case 34: return "CCB";
364     case 35: return "CCC";
365     case 36: return "CCD";
366     case 37: return "CCE";
367     case 38: return "EDATE";
368     case 39: return "SDATE";
369     default:
370       abort();
371       sprintf(tmp, "<%d>", type);
372       return tmp;
373     }
374 }
375
376 static void
377 dump_value__(FILE *stream, int level, bool match1)
378 {
379   match_byte(0);
380   match_byte(0);
381   match_byte(0);
382   match_byte(0);
383
384   for (int i = 0; i <= level; i++)
385     fprintf (stream, "    ");
386
387   if (match_byte (3))
388     {
389       char *text = get_string();
390       dump_value_31(stream);
391       char *identifier = get_string();
392       char *text_eng = get_string();
393       fprintf (stream, "<string c=\"%s\"", text_eng);
394       if (identifier[0])
395         fprintf (stream, " identifier=\"%s\"", identifier);
396       if (strcmp(text_eng, text))
397         fprintf (stream, " local=\"%s\"", text);
398       fprintf (stream, "/>\n");
399       if (!match_byte (0))
400         match_byte_assert(1);
401       if (match1)
402         match_byte (1);
403     }
404   else if (match_byte (5))
405     {
406       dump_value_31(stream);
407       char *name = get_string ();
408       char *label = get_string ();
409       fprintf (stream, "<variable name=\"%s\"", name);
410       if (label[0])
411         fprintf (stream, " label=\"%s\"", label);
412       fprintf (stream, "/>\n");
413       if (!match_byte(1) && !match_byte(2))
414         match_byte_assert(3);
415     }
416   else if (match_byte (2))
417     {
418       unsigned int format;
419       char *var, *vallab;
420       double value;
421
422       match_byte_assert (0x58);
423       format = get_u32 ();
424       value = get_double ();
425       var = get_string ();
426       vallab = get_string ();
427       fprintf (stream, "<numeric-datum value=\"%.*g\" format=\"%s%d.%d\"",
428               DBL_DIG, value, format_to_string(format >> 16), (format >> 8) & 0xff, format & 0xff);
429       if (var[0])
430         fprintf (stream, " variable=\"%s\"", var);
431       if (vallab[0])
432         fprintf (stream, " label=\"%s\"/>\n", vallab);
433       fprintf (stream, "/>\n");
434       if (!match_byte (1) && !match_byte(2))
435         match_byte_assert (3);
436     }
437   else if (match_byte (4))
438     {
439       unsigned int format;
440       char *var, *vallab, *value;
441
442       match_byte_assert (0x58);
443       format = get_u32 ();
444       vallab = get_string ();
445       var = get_string ();
446       if (!match_byte(1) && !match_byte(2))
447         match_byte_assert (3);
448       value = get_string ();
449       fprintf (stream, "<string-datum value=\"%s\" format=\"%s%d.%d\"",
450               value, format_to_string(format >> 16), (format >> 8) & 0xff, format & 0xff);
451       if (var[0])
452         fprintf (stream, " variable=\"%s\"", var);
453       if (vallab[0])
454         fprintf (stream, " label=\"%s\"/>\n", vallab);
455       fprintf (stream, "/>\n");
456     }
457   else if (match_byte (1))
458     {
459       unsigned int format;
460       double value;
461
462       dump_value_31(stream);
463       format = get_u32 ();
464       value = get_double ();
465       fprintf (stream, "<number value=\"%.*g\" format=\"%s%d.%d\"/>\n",
466                DBL_DIG, value, format_to_string(format >> 16), (format >> 8) & 0xff, format & 0xff);
467       if (match1)
468         match_byte (1);
469     }
470   else
471     {
472       dump_value_31(stream);
473
474       char *base = get_string();
475       int x = get_u32();
476       fprintf (stream, "<template format=\"%s\">\n", base);
477       for (int i = 0; i < x; i++)
478         {
479           int y = get_u32();
480           if (!y)
481             y = 1;
482           else
483             match_u32_assert(0);
484           for (int j = 0; j <= level + 1; j++)
485             fprintf (stream, "    ");
486           fprintf (stream, "<substitution index=\"%d\">\n", i + 1);
487           for (int j = 0; j < y; j++)
488             dump_value__ (stream, level + 2, false);
489           for (int j = 0; j <= level + 1; j++)
490             fprintf (stream, "    ");
491           fprintf (stream, "</substitution>\n");
492         }
493       for (int j = 0; j <= level; j++)
494         fprintf (stream, "    ");
495       fprintf (stream, "</template>\n");
496     }
497 }
498
499 static int
500 compare_int(const void *a_, const void *b_)
501 {
502   const int *a = a_;
503   const int *b = b_;
504   return *a < *b ? -1 : *a > *b;
505 }
506
507 static void
508 check_permutation(int *a, int n, const char *name)
509 {
510   int b[n];
511   memcpy(b, a, n * sizeof *a);
512   qsort(b, n, sizeof *b, compare_int);
513   for (int i = 0; i < n; i++)
514     if (b[i] != i)
515       {
516         fprintf(stderr, "bad %s permutation:", name);
517         for (int i = 0; i < n; i++)
518           fprintf(stderr, " %d", a[i]);
519         putc('\n', stderr);
520         exit(1);
521       }
522 }
523
524 static void
525 dump_category(int level, int *indexes, int *n_indexes)
526 {
527   dump_value__ (stdout, level, true);
528   match_byte(0);
529   match_byte(0);
530   match_byte(0);
531
532   if (match_u32 (1))
533     match_byte (0);
534   else if (match_byte (1))
535     {
536       match_byte (0);
537       if (!match_u32 (2))
538         match_u32_assert (1);
539       match_byte (0);
540     }
541   else if (!match_u32(2))
542     match_u32_assert (0);
543
544   int indx = get_u32();
545   int n_categories = get_u32();
546   if (indx != -1)
547     {
548       if (n_categories != 0)
549         {
550           fprintf(stderr, "index not -1 but subcategories\n");
551           exit(1);
552         }
553       indexes[(*n_indexes)++] = indx;
554     }
555   if (n_categories > 0)
556     printf (", %d subcategories:", n_categories);
557   else
558     printf (", index %d", indx);
559   printf("\n");
560   for (int i = 0; i < n_categories; i++)
561     dump_category (level + 1, indexes, n_indexes);
562 }
563
564 static void
565 dump_dim(void)
566 {
567   int n_categories;
568   printf("next dim\n");
569   dump_value__ (stdout, 0, false);
570
571   /* This byte is usually 0x02 but 0x00 and 0x75 (!) have also been spotted. */
572   pos++;
573
574   if (!match_byte(0) && !match_byte(1))
575     match_byte_assert(2);
576   if (!match_u32(0))
577     match_u32_assert(2);
578   if (!match_byte(0))
579     match_byte_assert(1);
580   if (!match_byte(0))
581     match_byte_assert(1);
582   match_byte_assert(1);
583   static int dim_indx = 0;
584   match_u32_assert(dim_indx++);
585   n_categories = get_u32();
586   printf("%d nested categories\n", n_categories);
587
588   int indexes[1024];
589   int n_indexes = 0;
590   for (int i = 0; i < n_categories; i++)
591     dump_category (0, indexes, &n_indexes);
592   check_permutation(indexes, n_indexes, "categories");
593 }
594
595 int n_dims;
596 static void
597 dump_dims(void)
598 {
599   n_dims = get_u32();
600   printf ("%u dimensions\n", n_dims);
601   for (int i = 0; i < n_dims; i++)
602     {
603       printf("\n");
604       dump_dim ();
605     }
606 }
607
608 static void
609 dump_data(void)
610 {
611   /* The first three numbers add to the number of dimensions. */
612   int t = get_u32();
613   t += get_u32();
614   match_u32_assert(n_dims - t);
615
616   /* The next n_dims numbers are a permutation of the dimension numbers. */
617   int a[n_dims];
618   for (int i = 0; i < n_dims; i++)
619     a[i] = get_u32();
620   check_permutation(a, n_dims, "dimensions");
621
622   int x = get_u32();
623   printf ("%d data values, starting at %08x\n", x, pos);
624   for (int i = 0; i < x; i++)
625     {
626       printf("%08x, index %d:\n", pos, get_u32());
627       match_u32_assert(0);
628       dump_value__(stdout, 0, false);
629       putchar('\n');
630     }
631 }
632
633 static void
634 dump_title(void)
635 {
636   pos = 0x27;
637   printf("text:\n");
638   dump_value__(stdout, 0, true); putchar('\n');
639   printf("subtype:\n");
640   dump_value__(stdout, 0, true); putchar('\n');
641   match_byte_assert(0x31);
642   printf("text_eng:\n");
643   dump_value__(stdout, 0, true); putchar('\n');
644   match_byte(0);
645   match_byte_assert(0x58);
646   if (match_byte(0x31))
647     {
648       printf("caption:\n");
649       dump_value__(stdout, 0, false); putchar('\n');
650     }
651   else
652     match_byte_assert(0x58);
653
654
655   int n_footnotes = get_u32();
656   if (n_footnotes >= 20)
657     {
658       fprintf(stderr, "%08x: %d footnotes\n", pos - 4, n_footnotes);
659       exit(1);
660     }
661
662   printf("------\n%d footnotes\n", n_footnotes);
663   if (n_footnotes < 20)
664     {
665       for (int i = 0; i < n_footnotes; i++)
666         {
667           printf("footnote %d:\n", i);
668           dump_value__(stdout, 0, false);
669           if (match_byte (0x31))
670             {
671               /* Custom footnote marker string. */
672               match_byte_assert(3);
673               get_string();
674               match_byte_assert(0x58);
675               match_u32_assert(0);
676               get_string();
677             }
678           else
679             match_byte_assert (0x58);
680           printf("(%d)\n", get_u32());
681         }
682     }
683 }
684
685 static int
686 find_dimensions(void)
687 {
688   {
689     const char dimensions[] = "-,,,.\0";
690     int x = try_find_tail(dimensions, sizeof dimensions - 1);
691     if (x)
692       return x;
693   }
694
695   const char dimensions[] = "-,,, .\0";
696   return find_tail(dimensions, sizeof dimensions - 1);
697 }
698
699 static void
700 dump_fonts(void)
701 {
702   printf("fonts: offset=%08x\n", pos);
703   match_byte(0);
704   for (int i = 1; i <= 8; i++)
705     {
706       printf("%08x: font %d, ", pos, i);
707       match_byte_assert(i);
708       match_byte_assert(0x31);
709       printf("%s, ", get_string());
710       match_byte_assert(0);
711       match_byte_assert(0);
712       if (!match_byte(0x40) && !match_byte(0x20) && !match_byte(0x80) && !match_byte(0x10))
713         match_byte_assert(0x50);
714       if (!match_byte(0x41))
715         match_byte_assert(0x51);
716       if (!match_u32(0))
717         match_u32_assert(1);
718       match_byte_assert(0);
719
720       /* OK, this seems really unlikely to be totally correct, but it matches my corpus... */
721       if (!match_u32(0) && !match_u32(2))
722         match_u32_assert(0xfaad);
723
724       if (!match_u32(0) && !match_u32(1) && !match_u32(2))
725         match_u32_assert(3);
726       printf ("%s, ", get_string());
727       printf ("%s, ", get_string());
728       match_u32_assert(0);
729       match_u32_assert(0);
730       match_byte_assert(0);
731
732       /* These seem unlikely to be correct too. */
733       if (i != 3)
734         {
735           match_u32_assert(8);
736           if (!match_u32(10))
737             match_u32_assert(11);
738           match_u32_assert(1);
739         }
740       else
741         {
742           get_u32();
743           if (!match_u32(-1) && !match_u32(8))
744             match_u32_assert(24);
745           if (!match_u32(-1) && !match_u32(2))
746             match_u32_assert(3);
747         }
748
749       /* Who knows? Ranges from -1 to 8 with no obvious pattern. */
750       get_u32();
751     }
752
753   match_u32_assert(240);
754   pos += 240;
755
756   match_u32_assert(18);
757   pos += 18;
758
759   if (match_u32(117))
760     pos += 117;
761   else
762     {
763       match_u32_assert(142);
764       pos += 142;
765     }
766
767   int count = get_u32();
768   pos += 4 * count;
769
770   char *encoding = get_string();
771   printf("encoding=%s\n", encoding);
772
773   if (!match_u32(0))
774     match_u32_assert(UINT32_MAX);
775   if (!match_byte(0))
776     match_byte_assert(1);
777   match_byte_assert(0);
778   if (!match_byte(0))
779     match_byte_assert(1);
780   if (!match_byte(0x99) && !match_byte(0x98))
781     match_byte_assert(0x97);
782   match_byte_assert(7);
783   match_byte_assert(0);
784   match_byte_assert(0);
785   if (match_byte('.'))
786     match_byte_assert(',');
787   else
788     {
789       match_byte_assert(',');
790       if (!match_byte('.'))
791         match_byte_assert(' ');
792     }
793   match_u32_assert(5);
794   for (int i = 0; i < 5; i++)
795     get_string();
796   pos += get_u32();
797   if (pos != find_dimensions())
798     fprintf (stderr, "%08x / %08x\n", pos, find_dimensions());
799 }
800
801 int
802 main(int argc, char *argv[])
803 {
804   size_t start;
805   struct stat s;
806
807   if (isatty(STDIN_FILENO))
808     {
809       fprintf(stderr, "redirect stdin from a .bin file\n");
810       exit(1);
811     }
812   if (fstat(STDIN_FILENO, &s))
813     {
814       perror("fstat");
815       exit(1);
816     }
817   n = s.st_size;
818   data = malloc(n);
819   if (!data)
820     {
821       perror("malloc");
822       exit(1);
823     }
824   if (read(STDIN_FILENO, data, n) != n)
825     {
826       perror("read");
827       exit(1);
828     }
829
830   if (argc > 1)
831     {
832       if (!strcmp(argv[1], "title0"))
833         {
834           pos = 0x27;
835           if (match_byte (0x03)
836               || (match_byte (0x05) && match_byte (0x58)))
837             printf ("%s\n", get_string());
838           else
839             printf ("<unknown>\n");
840           return 0;
841         }
842       else if (!strcmp(argv[1], "title"))
843         {
844           dump_title();
845           exit(0);
846         }
847       else if (!strcmp(argv[1], "titleraw"))
848         {
849           const char fonts[] = "\x01\x31\x09\0\0\0SansSerif";
850           start = 0x27;
851           n = find(fonts, sizeof fonts - 1);
852         }
853       else if (!strcmp(argv[1], "fonts"))
854         {
855           const char fonts[] = "\x01\x31\x09\0\0\0SansSerif";
856           const char styles[] = "\xf0\0\0\0";
857           start = find(fonts, sizeof fonts - 1);
858           n = find(styles, sizeof styles - 1);
859         }
860       else if (!strcmp(argv[1], "styles"))
861         {
862           const char styles[] = "\xf0\0\0\0";
863           const char dimensions[] = "-,,,.\0";
864           start = find(styles, sizeof styles - 1);
865           n = find(dimensions, sizeof dimensions - 1) + sizeof dimensions - 1;
866         }
867       else if (!strcmp(argv[1], "dimensions") || !strcmp(argv[1], "all"))
868         {
869           pos = 0;
870           match_byte_assert(1);
871           match_byte_assert(0);
872           match_u32_assert(3);
873           match_byte_assert(1);
874           if (!match_byte(0))
875             match_byte_assert(1);
876           match_byte_assert(0);
877           match_byte_assert(0);
878           if (!match_byte(0))
879             match_byte_assert(1);
880           pos++;
881           match_byte_assert(0);
882           match_byte_assert(0);
883           match_byte_assert(0);
884           dump_title ();
885           dump_fonts();
886           dump_dims ();
887           printf("\n\ndata:\n");
888           dump_data ();
889           match_byte (1);
890           if (pos != n)
891             {
892               fprintf (stderr, "%x / %x\n", pos, n);
893               exit(1);
894             }
895           exit(0);
896         }
897       else
898         {
899           fprintf (stderr, "unknown section %s\n", argv[1]);
900           exit(1);
901         }
902     }
903   else
904     start = 0x27;
905
906   dump_raw(stdout, start, n, "\n");
907
908   return 0;
909 }