Drop dump_title_value_31() in favor of dump_value_31().
[pspp] / dump.c
1 #include <stdbool.h>
2 #include <stdint.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <sys/stat.h>
7 #include <unistd.h>
8
9 static uint8_t *data;
10 static size_t n;
11
12 static bool
13 all_ascii(const uint8_t *p, size_t n)
14 {
15   for (size_t i = 0; i < n; i++)
16     if (p[i] < 32 || p[i] > 126)
17       return false;
18   return true;
19 }
20
21 static size_t
22 try_find(const char *target, size_t target_len)
23 {
24   const uint8_t *pos = (const uint8_t *) memmem (data, n, target, target_len);
25   return pos ? pos - data : 0;
26 }
27
28 static size_t
29 try_find_tail(const char *target, size_t target_len)
30 {
31   size_t pos = try_find(target, target_len);
32   return pos ? pos + target_len : 0;
33 }
34
35 static size_t
36 find(const char *target, size_t target_len)
37 {
38   size_t pos = try_find(target, target_len);
39   if (!pos)
40     {
41       fprintf (stderr, "not found\n");
42       exit(1);
43     }
44   return pos;
45 }
46
47 static size_t
48 find_tail(const char *target, size_t target_len)
49 {
50   size_t pos = try_find_tail(target, target_len);
51   if (!pos)
52     {
53       fprintf (stderr, "not found\n");
54       exit(1);
55     }
56   return pos;
57 }
58
59 size_t pos;
60
61 #define XSTR(x) #x
62 #define STR(x) XSTR(x)
63 #define WHERE __FILE__":" STR(__LINE__)
64
65 static unsigned int
66 get_u32(void)
67 {
68   uint32_t x;
69   memcpy(&x, &data[pos], 4);
70   pos += 4;
71   return x;
72 }
73
74 static double
75 get_double(void)
76 {
77   double x;
78   memcpy(&x, &data[pos], 8);
79   pos += 8;
80   return x;
81 }
82
83 static bool
84 match_u32(uint32_t x)
85 {
86   if (get_u32() == x)
87     return true;
88   pos -= 4;
89   return false;
90 }
91
92 static void
93 match_u32_assert(uint32_t x, const char *where)
94 {
95   unsigned int y = get_u32();
96   if (x != y)
97     {
98       fprintf(stderr, "%s: 0x%x: expected i%u, got i%u\n", where, pos - 4, x, y);
99       exit(1);
100     }
101 }
102 #define match_u32_assert(x) match_u32_assert(x, WHERE)
103
104 static bool
105 match_byte(uint8_t b)
106 {
107   if (pos < n && data[pos] == b)
108     {
109       pos++;
110       return true;
111     }
112   else
113     return false;
114 }
115
116 static void
117 match_byte_assert(uint8_t b, const char *where)
118 {
119   if (!match_byte(b))
120     {
121       fprintf(stderr, "%s: 0x%x: expected %02x, got %02x\n", where, pos, b, data[pos]);
122       exit(1);
123     }
124 }
125 #define match_byte_assert(b) match_byte_assert(b, WHERE)
126
127 static char *
128 get_string(const char *where)
129 {
130   if (1
131       /*data[pos + 1] == 0 && data[pos + 2] == 0 && data[pos + 3] == 0*/
132       /*&& all_ascii(&data[pos + 4], data[pos])*/)
133     {
134       int len = data[pos] + data[pos + 1] * 256;
135       char *s = malloc(len + 1);
136
137       memcpy(s, &data[pos + 4], len);
138       s[len] = 0;
139       pos += 4 + len;
140       return s;
141     }
142   else
143     {
144       fprintf(stderr, "%s: 0x%x: expected string\n", where, pos);
145       exit(1);
146     }
147 }
148 #define get_string() get_string(WHERE)
149
150 static void
151 dump_value_31(void)
152 {
153   if (match_byte (0x31))
154     {
155       if (match_u32 (0))
156         {
157           if (match_u32 (1))
158             get_string();
159           else
160             match_u32_assert (0);
161           int subn = get_u32 ();
162           printf ("nested %d bytes", subn);
163           pos += subn;
164         }
165       else if (match_u32 (1))
166         {
167           printf("(footnote %d) ", get_u32());
168           match_byte_assert (0);
169           match_byte_assert (0);
170           int subn = get_u32 ();
171           printf ("nested %d bytes", subn);
172           pos += subn;
173         }
174       else if (match_u32 (2))
175         {
176           printf("(special 2)");
177           match_byte_assert(0);
178           match_byte_assert(0);
179           if (!match_u32 (2))
180             match_u32_assert(1);
181           match_byte_assert(0);
182           match_byte_assert(0);
183           int subn = get_u32 ();
184           printf ("nested %d bytes", subn);
185           pos += subn;
186         }
187       else
188         {
189           match_u32_assert(3);
190           printf("(special 3)");
191           match_byte_assert(0);
192           match_byte_assert(0);
193           match_byte_assert(1);
194           match_byte_assert(0);
195           int subn = get_u32 ();
196           printf ("nested %d bytes, ", subn);
197           pos += subn;
198           subn = get_u32 ();
199           printf ("nested %d bytes, ", subn);
200           pos += subn;
201         }
202     }
203   else
204     match_byte_assert (0x58);
205 }
206
207 static void
208 dump_value(int level)
209 {
210   for (int i = 0; i <= level; i++)
211     printf ("    ");
212
213   match_byte (0);
214   match_byte (0);
215   match_byte (0);
216   match_byte (0);
217   if (match_byte (3))
218     {
219       char *s1 = get_string();
220       dump_value_31();
221       char *s2 = get_string();
222       char *s3 = get_string();
223       if (strcmp(s1, s3))
224         printf("strings \"%s\", \"%s\" and \"%s\"", s1, s2, s3);
225       else
226         printf("string \"%s\" and \"%s\"", s1, s2);
227       match_byte (0);
228       match_byte (1);
229       match_byte (1);
230       match_byte (0);
231       match_byte (0);
232       match_byte (0);
233     }
234   else if (match_byte (5))
235     {
236       match_byte_assert (0x58);
237       printf ("variable \"%s\"", get_string());
238       get_string();
239       if (!match_byte(1) && !match_byte(2))
240         match_byte_assert(3);
241       match_byte (0);
242       match_byte (0);
243       match_byte (0);
244     }
245   else if (match_byte (2))
246     {
247       unsigned int format;
248       char *var, *vallab;
249       double value;
250
251       match_byte_assert (0x58);
252       format = get_u32 ();
253       value = get_double ();
254       var = get_string ();
255       vallab = get_string ();
256       printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
257               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
258       if (!match_byte (1) && !match_byte(2))
259         match_byte_assert (3);
260       match_byte (0);
261       match_byte (0);
262       match_byte (0);
263     }
264   else if (match_byte (4))
265     {
266       unsigned int format;
267       char *var, *vallab, *value;
268
269       match_byte_assert (0x58);
270       format = get_u32 ();
271       vallab = get_string ();
272       var = get_string ();
273       if (!match_byte(1) && !match_byte(2))
274         match_byte_assert (3);
275       value = get_string ();
276       printf ("value \"%s\" format %d(%d.%d) var \"%s\" vallab \"%s\"",
277               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
278       match_byte (0);
279       match_byte (0);
280       match_byte (0);
281     }
282   else if (match_byte (1))
283     {
284       unsigned int format;
285       double value;
286
287       dump_value_31();
288       format = get_u32 ();
289       value = get_double ();
290       printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
291       match_byte (1);
292       match_byte (0);
293       match_byte (0);
294       match_byte (0);
295     }
296   else
297     {
298       dump_value_31();
299       char *base = get_string();
300       int x = get_u32();
301       printf ("\"%s\" with %d variables:\n", base, x);
302       if (match_u32(0))
303         {
304           for (int i = 0; i < x; i++)
305             {
306               dump_value (level+1);
307               putchar('\n');
308             }
309         }
310       else
311         {
312           for (int i = 0; i < x; i++)
313             {
314               int y = get_u32();
315               match_u32_assert(0);
316               for (int j = 0; j <= level; j++)
317                 printf ("    ");
318               printf("variable %d has %d values:\n", i, y);
319               for (int j = 0; j < y; j++)
320                 {
321                   if (match_byte(3))
322                     {
323                       char *a = get_string();
324                       match_byte_assert(0x58);
325                       char *b = get_string();
326                       char *c = get_string();
327                       for (int k = 0; k <= level + 1; k++)
328                         printf ("    ");
329                       printf ("\"%s\", \"%s\", \"%s\"", a, b, c);
330                       match_byte(0);
331                       match_byte(0);
332                       match_byte(0);
333                       match_byte(0);
334                       match_byte(0);
335                     }
336                   else
337                     dump_value (level+1);
338                   putchar('\n');
339                 }
340             }
341         }
342     }
343 }
344
345 static void
346 dump_dim_value(int level)
347 {
348   for (int i = 0; i <= level; i++)
349     printf ("    ");
350
351   if (match_byte (3))
352     {
353       get_string();
354       dump_value_31();
355       get_string();
356       printf("string \"%s\"", get_string());
357       match_byte (0);
358       match_byte (1);
359     }
360   else if (match_byte (5))
361     {
362       match_byte_assert (0x58);
363       printf ("variable \"%s\"", get_string());
364       get_string();
365       if (!match_byte (2))
366         match_byte_assert(3);
367     }
368   else if (match_byte (2))
369     {
370       unsigned int format;
371       char *var, *vallab;
372       double value;
373
374       match_byte_assert (0x58);
375       format = get_u32 ();
376       value = get_double ();
377       var = get_string ();
378       vallab = get_string ();
379       printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
380               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
381       if (!match_u32 (3))
382         match_u32_assert (2);
383       match_byte (0);
384     }
385   else if (match_byte (1))
386     {
387       unsigned int format;
388       double value;
389
390       match_byte_assert (0x58);
391       format = get_u32 ();
392       value = get_double ();
393       printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
394       match_byte (1);
395       match_byte (0);
396       match_byte (0);
397       match_byte (0);
398       match_byte (1);
399     }
400   else
401     {
402       match_byte (0);
403       dump_value_31();
404       printf ("; \"%s\", substitutions:", get_string());
405       int total_subs = get_u32();
406       int x = get_u32();
407       if (x)
408         {
409           total_subs = (total_subs - 1) + x;
410           match_u32_assert (0);
411         }
412       printf (" (total %d)", total_subs);
413
414       for (int i = 0; i < total_subs; i++)
415         {
416           putc ('\n', stdout);
417           dump_value (level + 1);
418         }
419     }
420 }
421
422 static void
423 dump_category(int level)
424 {
425   match_byte (0);
426   match_byte (0);
427   match_byte (0);
428   match_byte (0);
429   dump_value (level);
430
431   if (match_u32 (2))
432     get_u32 ();
433   else if (match_u32 (1))
434     {
435       match_byte (0);
436       match_byte (0);
437       match_byte (0);
438       get_u32 ();
439     }
440   else if (match_byte (1))
441     {
442       match_byte (0);
443       if (!match_u32 (2))
444         match_u32_assert (1);
445       match_byte (0);
446       get_u32();
447     }
448   else
449     {
450       match_u32_assert (0);
451       get_u32 ();
452     }
453
454   int n_categories = get_u32();
455   if (n_categories > 0)
456     printf (", %d subcategories:", n_categories);
457   printf("\n");
458   for (int i = 0; i < n_categories; i++)
459     dump_category (level + 1);
460 }
461
462 static void
463 dump_dim(void)
464 {
465   int n_categories;
466   printf("next dim\n");
467   match_byte(0);
468   dump_dim_value(0);
469
470   /* This byte is usually 0x02 but 0x00 and 0x75 (!) have also been spotted. */
471   pos++;
472
473   if (!match_byte(0) && !match_byte(1))
474     match_byte_assert(2);
475   if (!match_u32(0))
476     match_u32_assert(2);
477   if (!match_byte(0))
478     match_byte_assert(1);
479   match_byte(0);
480   match_byte(0);
481   match_byte(0);
482   match_byte(0);
483   get_u32();
484   match_byte(0);
485   match_byte(0);
486   match_byte(0);
487   match_byte(0);
488   n_categories = get_u32();
489   printf("%d nested categories\n", n_categories);
490   for (int i = 0; i < n_categories; i++)
491     dump_category (0);
492 }
493
494 int n_dims;
495 static void
496 dump_dims(void)
497 {
498   n_dims = get_u32();
499   printf ("%u dimensions\n", n_dims);
500   for (int i = 0; i < n_dims; i++)
501     {
502       printf("\n");
503       dump_dim ();
504     }
505 }
506
507 static void
508 dump_data_value(void)
509 {
510   match_byte(0);
511   match_byte(0);
512   match_byte(0);
513   match_byte(0);
514   if (match_byte (1))
515     {
516       unsigned int format;
517       double value;
518
519       dump_value_31();
520       format = get_u32 ();
521       value = get_double ();
522       printf ("    value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
523     }
524   else if (match_byte (3))
525     {
526       get_string();
527       dump_value_31();
528       get_string();
529       printf("string \"%s\"", get_string());
530       match_byte (0);
531     }
532   else if (match_byte (2))
533     {
534       unsigned int format;
535       char *var, *vallab;
536       double value;
537
538       match_byte_assert (0x58);
539       format = get_u32 ();
540       value = get_double ();
541       var = get_string ();
542       vallab = get_string ();
543       printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
544               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
545       if (!match_byte (1) && !match_byte(2))
546         match_byte_assert (3);
547     }
548   else if (match_byte (4))
549     {
550       unsigned int format;
551       char *var, *vallab, *value;
552
553       match_byte_assert (0x58);
554       format = get_u32 ();
555       vallab = get_string ();
556       var = get_string ();
557       if (!match_byte(1) && !match_byte(2))
558         match_byte_assert (3);
559       value = get_string ();
560       printf ("value \"%s\" format %d(%d.%d) var \"%s\" vallab \"%s\"",
561               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
562     }
563   else if (match_byte (5))
564     {
565       match_byte_assert (0x58);
566       printf ("variable \"%s\"", get_string());
567       get_string();
568       if (!match_byte(1) && !match_byte(2))
569         match_byte_assert(3);
570       match_byte (0);
571       match_byte (0);
572       match_byte (0);
573       match_byte (0);
574     }
575   else
576     {
577       dump_value_31();
578       char *base = get_string();
579       int x = get_u32();
580       printf ("\"%s\"; %d variables:\n", base, x);
581       for (int i = 0; i < x; i++)
582         {
583           int y = get_u32();
584           if (!y)
585             y = 1;
586           else
587             match_u32_assert(0);
588           for (int j = 0; j <= 0; j++)
589             printf ("    ");
590           printf("variable %d has %d values:\n", i, y);
591           for (int j = 0; j < y; j++)
592             dump_data_value();
593         }
594     }
595 }
596
597 static void
598 dump_data(void)
599 {
600 #if 1
601   int a[16];
602   for (int i = 0; i < 3 + n_dims; i++)
603     a[i] = get_u32();
604   printf ("data intro:");
605   for (int i = 0; i < 3 + n_dims; i++)
606     printf(" %d", a[i]);
607   printf("\n");
608 #else
609   fprintf (stderr,"data intro (%d dims):", n_dims);
610   for (int i = 0; i < 3+n_dims; i++)
611     fprintf (stderr," %d", get_u32());
612   fprintf(stderr,"\n");
613 #endif
614   int x = get_u32();
615   printf ("%d data values, starting at %08x\n", x, pos);
616   for (int i = 0; i < x; i++)
617     {
618       printf("%08x, index %d:\n", pos, get_u32());
619       match_u32_assert(0);
620       dump_data_value();
621       putchar('\n');
622     }
623 }
624
625 static void
626 dump_title_value(int level)
627 {
628   for (int i = 0; i <= level; i++)
629     printf ("    ");
630
631   match_byte (0);
632   match_byte (0);
633   match_byte (0);
634   match_byte (0);
635   match_byte (0);
636   if (match_byte (3))
637     {
638       get_string();
639       dump_value_31();
640       get_string();
641       printf("string \"%s\"", get_string());
642       match_byte (0);
643       match_byte (0);
644       match_byte (0);
645       match_byte (1);
646       match_byte (1);
647       match_byte (0);
648       match_byte (0);
649       match_byte (0);
650       match_byte (1);
651     }
652   else if (match_byte (5))
653     {
654       dump_value_31();
655       printf ("variable \"%s\"", get_string());
656       get_string();
657       if (!match_byte(1) && !match_byte(2))
658         match_byte_assert(3);
659     }
660   else if (match_byte (2))
661     {
662       unsigned int format;
663       char *var, *vallab;
664       double value;
665
666       match_byte_assert (0x58);
667       format = get_u32 ();
668       value = get_double ();
669       var = get_string ();
670       vallab = get_string ();
671       printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
672               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
673       if (!match_byte (1) && !match_byte(2))
674         match_byte_assert (3);
675       match_byte (0);
676       match_byte (0);
677       match_byte (0);
678       match_byte (0);
679       match_byte (0);
680       match_byte (0);
681       match_byte (0);
682     }
683   else if (match_byte (4))
684     {
685       unsigned int format;
686       char *var, *vallab, *value;
687
688       match_byte_assert (0x58);
689       format = get_u32 ();
690       vallab = get_string ();
691       var = get_string ();
692       if (!match_byte(1) && !match_byte(2))
693         match_byte_assert (3);
694       value = get_string ();
695       printf ("value \"%s\" format %d(%d.%d) var \"%s\" vallab \"%s\"",
696               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
697       match_byte (0);
698       match_byte (0);
699       match_byte (0);
700       match_byte (0);
701     }
702   else if (match_byte (1))
703     {
704       unsigned int format;
705       double value;
706
707       dump_value_31();
708       format = get_u32 ();
709       value = get_double ();
710       printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
711       match_byte (1);
712       match_byte (0);
713       match_byte (0);
714       match_byte (0);
715       match_byte (1);
716     }
717   else
718     {
719       dump_value_31();
720
721       char *base = get_string();
722       int x = get_u32();
723       printf ("\"%s\" with %d variables:\n", base, x);
724       for (int i = 0; i < x; i++)
725         {
726           int y = get_u32();
727           if (!y)
728             y = 1;
729           else
730             match_u32_assert(0);
731           for (int j = 0; j <= level; j++)
732             printf ("    ");
733           printf("variable %d has %d values:\n", i, y);
734           for (int j = 0; j < y; j++)
735             {
736               match_byte(0);
737               if (match_byte(3))
738                 {
739                   char *a = get_string();
740                   match_byte_assert(0x58);
741                   char *b = get_string();
742                   char *c = get_string();
743                   for (int k = 0; k <= level + 1; k++)
744                     printf ("    ");
745                   printf ("\"%s\", \"%s\", \"%s\"", a, b, c);
746                 }
747               else
748                 dump_title_value (level+1);
749               putchar('\n');
750             }
751         }
752     }
753 }
754
755 static void
756 dump_footnote_value_31(void)
757 {
758   if (match_byte (0x31))
759     {
760       if (match_u32 (0))
761         {
762
763           match_u32_assert (0);
764           int subn = get_u32 ();
765           printf ("nested %d bytes", subn);
766           pos += subn;
767         }
768       else if (match_u32 (1))
769         {
770           printf("(footnote %d) ", get_u32());
771           match_byte_assert (0);
772           match_byte_assert (0);
773           int subn = get_u32 ();
774           printf ("nested %d bytes", subn);
775           pos += subn;
776         }
777       else if (match_u32 (2))
778         {
779           printf("(special 2)");
780           match_byte_assert(0);
781           match_byte_assert(0);
782           match_u32_assert(1);
783           match_byte_assert(0);
784           match_byte_assert(0);
785           int subn = get_u32 ();
786           printf ("nested %d bytes", subn);
787           pos += subn;
788         }
789       else
790         {
791           match_u32_assert(3);
792           printf("(special 3)");
793           match_byte_assert(0);
794           match_byte_assert(0);
795           match_byte_assert(1);
796           match_byte_assert(0);
797           int subn = get_u32 ();
798           printf ("nested %d bytes, ", subn);
799           pos += subn;
800           subn = get_u32 ();
801           printf ("nested %d bytes, ", subn);
802           pos += subn;
803         }
804     }
805   else
806     match_byte_assert (0x58);
807 }
808
809 static void
810 dump_footnote_value(int level)
811 {
812   for (int i = 0; i <= level; i++)
813     printf ("    ");
814
815   match_byte (0);
816   match_byte (0);
817   match_byte (0);
818   match_byte (0);
819   if (match_byte (3))
820     {
821       get_string();
822       dump_footnote_value_31();
823       get_string();
824       printf("string \"%s\"", get_string());
825       if (!match_byte (0))
826         match_byte_assert (1);
827     }
828   else if (match_byte (5))
829     {
830       match_byte_assert (0x58);
831       printf ("variable \"%s\"", get_string());
832       get_string();
833       if (!match_byte(1) && !match_byte(2))
834         match_byte_assert(3);
835     }
836   else if (match_byte (2))
837     {
838       unsigned int format;
839       char *var, *vallab;
840       double value;
841
842       match_byte_assert (0x58);
843       format = get_u32 ();
844       value = get_double ();
845       var = get_string ();
846       vallab = get_string ();
847       printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
848               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
849       if (!match_byte (1) && !match_byte(2))
850         match_byte_assert (3);
851       match_byte (0);
852       match_byte (0);
853       match_byte (0);
854       match_byte (0);
855       match_byte (0);
856       match_byte (0);
857       match_byte (0);
858     }
859   else if (match_byte (4))
860     {
861       unsigned int format;
862       char *var, *vallab, *value;
863
864       match_byte_assert (0x58);
865       format = get_u32 ();
866       vallab = get_string ();
867       var = get_string ();
868       if (!match_byte(1) && !match_byte(2))
869         match_byte_assert (3);
870       value = get_string ();
871       printf ("value \"%s\" format %d(%d.%d) var \"%s\" vallab \"%s\"",
872               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
873       match_byte (0);
874       match_byte (0);
875       match_byte (0);
876       match_byte (0);
877     }
878   else if (match_byte (1))
879     {
880       unsigned int format;
881       double value;
882
883       dump_footnote_value_31();
884       format = get_u32 ();
885       value = get_double ();
886       printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
887     }
888   else
889     {
890       dump_footnote_value_31();
891       char *base = get_string();
892       int x = get_u32();
893       printf ("\"%s\"; %d variables:\n", base, x);
894       for (int i = 0; i < x; i++)
895         {
896           int y = get_u32();
897           if (!y)
898             y = 1;
899           else
900             match_u32_assert(0);
901           for (int j = 0; j <= level; j++)
902             printf ("    ");
903           printf("variable %d has %d values:\n", i, y);
904           for (int j = 0; j < y; j++)
905             {
906               if (match_byte(3))
907                 {
908                   char *a = get_string();
909                   match_byte_assert(0x58);
910                   char *b = get_string();
911                   char *c = get_string();
912                   for (int k = 0; k <= level + 1; k++)
913                     printf ("    ");
914                   printf ("\"%s\", \"%s\", \"%s\"", a, b, c);
915                   if (!match_byte(1))
916                     match_byte_assert(0);
917                 }
918               else
919                 dump_footnote_value (level+1);
920               putchar('\n');
921             }
922         }
923     }
924 }
925
926 static void
927 dump_title(void)
928 {
929   pos = 0x27;
930   dump_title_value(0); putchar('\n');
931   dump_title_value(0); putchar('\n');
932   match_byte_assert(0x31);
933   dump_title_value(0); putchar('\n');
934   match_byte(0);
935   match_byte_assert(0x58);
936   if (match_byte(0x31))
937     {
938       dump_footnote_value(0); putchar('\n');
939     }
940   else
941     match_byte_assert(0x58);
942
943
944   int n_footnotes = get_u32();
945   if (n_footnotes >= 20)
946     {
947       fprintf(stderr, "%08x: %d footnotes\n", pos - 4, n_footnotes);
948       exit(1);
949     }
950
951   printf("------\n%d footnotes\n", n_footnotes);
952   if (n_footnotes < 20)
953     {
954       for (int i = 0; i < n_footnotes; i++)
955         {
956           printf("footnote %d:\n", i);
957           dump_footnote_value(0);
958           match_byte(0);
959           match_byte(0);
960           match_byte(0);
961           match_byte(0);
962           if (match_byte (0x31))
963             {
964               /* Custom footnote marker string. */
965               match_byte_assert(3);
966               get_string();
967               match_byte_assert(0x58);
968               match_u32_assert(0);
969               get_string();
970             }
971           else
972             match_byte_assert (0x58);
973           printf("(%d)\n", get_u32());
974         }
975     }
976 }
977
978 static int
979 find_dimensions(void)
980 {
981   {
982     const char dimensions[] = "-,,,.\0";
983     int x = try_find_tail(dimensions, sizeof dimensions - 1);
984     if (x)
985       return x;
986   }
987
988   const char dimensions[] = "-,,, .\0";
989   return find_tail(dimensions, sizeof dimensions - 1);
990 }
991
992 static void
993 dump_fonts(void)
994 {
995   printf("fonts: offset=%08x\n", pos);
996   match_byte(0);
997   for (int i = 1; i <= 8; i++)
998     {
999       printf("%08x: font %d, ", pos, i);
1000       match_byte_assert(i);
1001       match_byte_assert(0x31);
1002       printf("%s, ", get_string());
1003       match_byte_assert(0);
1004       match_byte_assert(0);
1005       if (!match_byte(0x40) && !match_byte(0x20) && !match_byte(0x80) && !match_byte(0x10))
1006         match_byte_assert(0x50);
1007       if (!match_byte(0x41))
1008         match_byte_assert(0x51);
1009       pos += 13;
1010       printf ("%s, ", get_string());
1011       printf ("%s, ", get_string());
1012       match_u32_assert(0);
1013       match_u32_assert(0);
1014       pos++;
1015       get_u32();
1016       get_u32();
1017       get_u32();
1018       get_u32();
1019       putchar('\n');
1020     }
1021
1022   match_u32_assert(240);
1023   pos += 240;
1024
1025   match_u32_assert(18);
1026   pos += 18;
1027
1028   if (match_u32(117))
1029     pos += 117;
1030   else
1031     {
1032       match_u32_assert(142);
1033       pos += 142;
1034     }
1035
1036   int count = get_u32();
1037   pos += 4 * count;
1038
1039   char *encoding = get_string();
1040   printf("encoding=%s\n", encoding);
1041
1042   if (!match_u32(0))
1043     match_u32_assert(UINT32_MAX);
1044   if (!match_byte(0))
1045     match_byte_assert(1);
1046   match_byte_assert(0);
1047   if (!match_byte(0))
1048     match_byte_assert(1);
1049   if (!match_byte(0x99) && !match_byte(0x98))
1050     match_byte_assert(0x97);
1051   match_byte_assert(7);
1052   match_byte_assert(0);
1053   match_byte_assert(0);
1054   if (match_byte('.'))
1055     match_byte_assert(',');
1056   else
1057     {
1058       match_byte_assert(',');
1059       if (!match_byte('.'))
1060         match_byte_assert(' ');
1061     }
1062   match_u32_assert(5);
1063   for (int i = 0; i < 5; i++)
1064     get_string();
1065   pos += get_u32();
1066   if (pos != find_dimensions())
1067     fprintf (stderr, "%08x / %08x\n", pos, find_dimensions());
1068 }
1069
1070 int
1071 main(int argc, char *argv[])
1072 {
1073   size_t start;
1074   struct stat s;
1075
1076   if (isatty(STDIN_FILENO))
1077     {
1078       fprintf(stderr, "redirect stdin from a .bin file\n");
1079       exit(1);
1080     }
1081   if (fstat(STDIN_FILENO, &s))
1082     {
1083       perror("fstat");
1084       exit(1);
1085     }
1086   n = s.st_size;
1087   data = malloc(n);
1088   if (!data)
1089     {
1090       perror("malloc");
1091       exit(1);
1092     }
1093   if (read(STDIN_FILENO, data, n) != n)
1094     {
1095       perror("read");
1096       exit(1);
1097     }
1098
1099   if (argc > 1)
1100     {
1101       if (!strcmp(argv[1], "title0"))
1102         {
1103           pos = 0x27;
1104           if (match_byte (0x03)
1105               || (match_byte (0x05) && match_byte (0x58)))
1106             printf ("%s\n", get_string());
1107           else
1108             printf ("<unknown>\n");
1109           return 0;
1110         }
1111       else if (!strcmp(argv[1], "title"))
1112         {
1113           dump_title();
1114           exit(0);
1115         }
1116       else if (!strcmp(argv[1], "titleraw"))
1117         {
1118           const char fonts[] = "\x01\x31\x09\0\0\0SansSerif";
1119           start = 0x27;
1120           n = find(fonts, sizeof fonts - 1);
1121         }
1122       else if (!strcmp(argv[1], "fonts"))
1123         {
1124           const char fonts[] = "\x01\x31\x09\0\0\0SansSerif";
1125           const char styles[] = "\xf0\0\0\0";
1126           start = find(fonts, sizeof fonts - 1);
1127           n = find(styles, sizeof styles - 1);
1128         }
1129       else if (!strcmp(argv[1], "styles"))
1130         {
1131           const char styles[] = "\xf0\0\0\0";
1132           const char dimensions[] = "-,,,.\0";
1133           start = find(styles, sizeof styles - 1);
1134           n = find(dimensions, sizeof dimensions - 1) + sizeof dimensions - 1;
1135         }
1136       else if (!strcmp(argv[1], "dimensions") || !strcmp(argv[1], "all"))
1137         {
1138           pos = 0;
1139           match_byte_assert(1);
1140           match_byte_assert(0);
1141           match_u32_assert(3);
1142           match_byte_assert(1);
1143           if (!match_byte(0))
1144             match_byte_assert(1);
1145           match_byte_assert(0);
1146           match_byte_assert(0);
1147           if (!match_byte(0))
1148             match_byte_assert(1);
1149           pos++;
1150           match_byte_assert(0);
1151           match_byte_assert(0);
1152           match_byte_assert(0);
1153           dump_title ();
1154           dump_fonts();
1155           dump_dims ();
1156           printf("\n\ndata:\n");
1157           dump_data ();
1158           if (pos == n - 1)
1159             match_byte_assert (1);
1160           if (pos != n)
1161             {
1162               fprintf (stderr, "%x / %x\n", pos, n);
1163               exit(1);
1164             }
1165           exit(0);
1166         }
1167       else
1168         {
1169           fprintf (stderr, "unknown section %s\n", argv[1]);
1170           exit(1);
1171         }
1172     }
1173   else
1174     start = 0x27;
1175
1176   for (size_t i = start; i < n; )
1177     {
1178       if (i + 5 <= n
1179           && data[i]
1180           //&& !data[i + 1]
1181           && !data[i + 2]
1182           && !data[i + 3]
1183           && i + 4 + data[i] + data[i + 1] * 256 <= n
1184           && all_ascii(&data[i + 4], data[i] + data[i + 1] * 256))
1185         {
1186           fputs("\n\"", stdout);
1187           fwrite(&data[i + 4], 1, data[i] + data[i + 1] * 256, stdout);
1188           fputs("\" ", stdout);
1189
1190           i += 4 + data[i] + data[i + 1] * 256;
1191         }
1192       else if (i + 12 <= n
1193                && data[i + 1] == 40
1194                && data[i + 2] == 5
1195                && data[i + 3] == 0)
1196         {
1197           double d;
1198
1199           memcpy (&d, &data[i + 4], 8);
1200           printf ("F40.%d(%.*f)\n", data[i], data[i], d);
1201           i += 12;
1202         }
1203       else if (i + 12 <= n
1204                && data[i + 1] == 40
1205                && data[i + 2] == 31
1206                && data[i + 3] == 0)
1207         {
1208           double d;
1209
1210           memcpy (&d, &data[i + 4], 8);
1211           printf ("PCT40.%d(%.*f)\n", data[i], data[i], d);
1212           i += 12;
1213         }
1214       else if (i + 4 <= n
1215                && (data[i] && data[i] != 88 && data[i] != 0x41)
1216                && !data[i + 1]
1217                && !data[i + 2]
1218                && !data[i + 3])
1219         {
1220           printf ("i%d ", data[i]);
1221           i += 4;
1222         }
1223       else
1224         {
1225           printf("%02x ", data[i]);
1226           i++;
1227         }
1228     }
1229
1230   return 0;
1231 }