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