Simplify dump_footnote_value().
[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       dump_value_31();
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
301       int x = get_u32();
302       printf ("\"%s\" with %d variables:\n", base, x);
303       if (match_u32(0))
304         {
305           for (int i = 0; i < x; i++)
306             {
307               dump_value (level+1);
308               putchar('\n');
309             }
310         }
311       else
312         {
313           for (int i = 0; i < x; i++)
314             {
315               int y = get_u32();
316               match_u32_assert(0);
317               for (int j = 0; j <= level; j++)
318                 printf ("    ");
319               printf("variable %d has %d values:\n", i, y);
320               for (int j = 0; j < y; j++)
321                 {
322                   match_byte(0);
323                   if (match_byte(3))
324                     {
325                       char *a = get_string();
326                       match_byte_assert(0x58);
327                       char *b = get_string();
328                       char *c = get_string();
329                       for (int k = 0; k <= level + 1; k++)
330                         printf ("    ");
331                       printf ("\"%s\", \"%s\", \"%s\"", a, b, c);
332                     }
333                   else
334                     dump_value (level+1);
335
336                   match_byte(0);
337                   match_byte(0);
338                   match_byte(0);
339                   match_byte(0);
340                   putchar('\n');
341                 }
342             }
343         }
344     }
345 }
346
347 static void
348 dump_dim_value(int level)
349 {
350   for (int i = 0; i <= level; i++)
351     printf ("    ");
352
353   if (match_byte (3))
354     {
355       get_string();
356       dump_value_31();
357       get_string();
358       printf("string \"%s\"", get_string());
359       match_byte (0);
360       match_byte (1);
361     }
362   else if (match_byte (5))
363     {
364       match_byte_assert (0x58);
365       printf ("variable \"%s\"", get_string());
366       get_string();
367       if (!match_byte (1) && !match_byte (2))
368         match_byte_assert(3);
369     }
370   else if (match_byte (2))
371     {
372       unsigned int format;
373       char *var, *vallab;
374       double value;
375
376       match_byte_assert (0x58);
377       format = get_u32 ();
378       value = get_double ();
379       var = get_string ();
380       vallab = get_string ();
381       printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
382               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
383       if (!match_byte (1) && !match_byte (2))
384         match_byte_assert (3);
385       match_byte (0);
386       match_byte (0);
387       match_byte (0);
388     }
389   else if (match_byte (1))
390     {
391       unsigned int format;
392       double value;
393
394       dump_value_31 ();
395       format = get_u32 ();
396       value = get_double ();
397       printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
398       match_byte (1);
399       match_byte (0);
400       match_byte (0);
401       match_byte (0);
402     }
403   else
404     {
405       dump_value_31();
406       printf ("; \"%s\", substitutions:", get_string());
407       int total_subs = get_u32();
408       int x = get_u32();
409       if (x)
410         {
411           total_subs = (total_subs - 1) + x;
412           match_u32_assert (0);
413         }
414       printf (" (total %d)", total_subs);
415
416       for (int i = 0; i < total_subs; i++)
417         {
418           putc ('\n', stdout);
419           dump_value (level + 1);
420         }
421     }
422 }
423
424 static void
425 dump_category(int level)
426 {
427   match_byte (0);
428   match_byte (0);
429   match_byte (0);
430   match_byte (0);
431   dump_value (level);
432
433   if (match_u32 (2))
434     get_u32 ();
435   else if (match_u32 (1))
436     {
437       match_byte (0);
438       match_byte (0);
439       match_byte (0);
440       get_u32 ();
441     }
442   else if (match_byte (1))
443     {
444       match_byte (0);
445       if (!match_u32 (2))
446         match_u32_assert (1);
447       match_byte (0);
448       get_u32();
449     }
450   else
451     {
452       match_u32_assert (0);
453       get_u32 ();
454     }
455
456   int n_categories = get_u32();
457   if (n_categories > 0)
458     printf (", %d subcategories:", n_categories);
459   printf("\n");
460   for (int i = 0; i < n_categories; i++)
461     dump_category (level + 1);
462 }
463
464 static void
465 dump_dim(void)
466 {
467   int n_categories;
468   printf("next dim\n");
469   match_byte(0);
470   dump_dim_value(0);
471
472   /* This byte is usually 0x02 but 0x00 and 0x75 (!) have also been spotted. */
473   pos++;
474
475   if (!match_byte(0) && !match_byte(1))
476     match_byte_assert(2);
477   if (!match_u32(0))
478     match_u32_assert(2);
479   if (!match_byte(0))
480     match_byte_assert(1);
481   match_byte(0);
482   match_byte(0);
483   match_byte(0);
484   match_byte(0);
485   get_u32();
486   match_byte(0);
487   match_byte(0);
488   match_byte(0);
489   match_byte(0);
490   n_categories = get_u32();
491   printf("%d nested categories\n", n_categories);
492   for (int i = 0; i < n_categories; i++)
493     dump_category (0);
494 }
495
496 int n_dims;
497 static void
498 dump_dims(void)
499 {
500   n_dims = get_u32();
501   printf ("%u dimensions\n", n_dims);
502   for (int i = 0; i < n_dims; i++)
503     {
504       printf("\n");
505       dump_dim ();
506     }
507 }
508
509 static void
510 dump_data_value(void)
511 {
512   match_byte(0);
513   match_byte(0);
514   match_byte(0);
515   match_byte(0);
516   if (match_byte (1))
517     {
518       unsigned int format;
519       double value;
520
521       dump_value_31();
522       format = get_u32 ();
523       value = get_double ();
524       printf ("    value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
525     }
526   else if (match_byte (3))
527     {
528       get_string();
529       dump_value_31();
530       get_string();
531       printf("string \"%s\"", get_string());
532       match_byte (0);
533     }
534   else if (match_byte (2))
535     {
536       unsigned int format;
537       char *var, *vallab;
538       double value;
539
540       match_byte_assert (0x58);
541       format = get_u32 ();
542       value = get_double ();
543       var = get_string ();
544       vallab = get_string ();
545       printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
546               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
547       if (!match_byte (1) && !match_byte(2))
548         match_byte_assert (3);
549     }
550   else if (match_byte (4))
551     {
552       unsigned int format;
553       char *var, *vallab, *value;
554
555       match_byte_assert (0x58);
556       format = get_u32 ();
557       vallab = get_string ();
558       var = get_string ();
559       if (!match_byte(1) && !match_byte(2))
560         match_byte_assert (3);
561       value = get_string ();
562       printf ("value \"%s\" format %d(%d.%d) var \"%s\" vallab \"%s\"",
563               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
564     }
565   else if (match_byte (5))
566     {
567       match_byte_assert (0x58);
568       printf ("variable \"%s\"", get_string());
569       get_string();
570       if (!match_byte(1) && !match_byte(2))
571         match_byte_assert(3);
572       match_byte (0);
573       match_byte (0);
574       match_byte (0);
575     }
576   else
577     {
578       dump_value_31();
579       char *base = get_string();
580       int x = get_u32();
581       printf ("\"%s\"; %d variables:\n", base, x);
582       for (int i = 0; i < x; i++)
583         {
584           int y = get_u32();
585           if (!y)
586             y = 1;
587           else
588             match_u32_assert(0);
589           for (int j = 0; j <= 0; j++)
590             printf ("    ");
591           printf("variable %d has %d values:\n", i, y);
592           for (int j = 0; j < y; j++)
593             dump_data_value();
594         }
595     }
596 }
597
598 static void
599 dump_data(void)
600 {
601 #if 1
602   int a[16];
603   for (int i = 0; i < 3 + n_dims; i++)
604     a[i] = get_u32();
605   printf ("data intro:");
606   for (int i = 0; i < 3 + n_dims; i++)
607     printf(" %d", a[i]);
608   printf("\n");
609 #else
610   fprintf (stderr,"data intro (%d dims):", n_dims);
611   for (int i = 0; i < 3+n_dims; i++)
612     fprintf (stderr," %d", get_u32());
613   fprintf(stderr,"\n");
614 #endif
615   int x = get_u32();
616   printf ("%d data values, starting at %08x\n", x, pos);
617   for (int i = 0; i < x; i++)
618     {
619       printf("%08x, index %d:\n", pos, get_u32());
620       match_u32_assert(0);
621       dump_data_value();
622       putchar('\n');
623     }
624 }
625
626 static void
627 dump_title_value(int level)
628 {
629   for (int i = 0; i <= level; i++)
630     printf ("    ");
631
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 (1);
644       match_byte (1);
645       match_byte (0);
646       match_byte (0);
647       match_byte (0);
648     }
649   else if (match_byte (5))
650     {
651       dump_value_31();
652       printf ("variable \"%s\"", get_string());
653       get_string();
654       if (!match_byte(1) && !match_byte(2))
655         match_byte_assert(3);
656     }
657   else if (match_byte (2))
658     {
659       unsigned int format;
660       char *var, *vallab;
661       double value;
662
663       match_byte_assert (0x58);
664       format = get_u32 ();
665       value = get_double ();
666       var = get_string ();
667       vallab = get_string ();
668       printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
669               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
670       if (!match_byte (1) && !match_byte(2))
671         match_byte_assert (3);
672       match_byte (0);
673       match_byte (0);
674       match_byte (0);
675     }
676   else if (match_byte (4))
677     {
678       unsigned int format;
679       char *var, *vallab, *value;
680
681       match_byte_assert (0x58);
682       format = get_u32 ();
683       vallab = get_string ();
684       var = get_string ();
685       if (!match_byte(1) && !match_byte(2))
686         match_byte_assert (3);
687       value = get_string ();
688       printf ("value \"%s\" format %d(%d.%d) var \"%s\" vallab \"%s\"",
689               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
690       match_byte (0);
691       match_byte (0);
692       match_byte (0);
693     }
694   else if (match_byte (1))
695     {
696       unsigned int format;
697       double value;
698
699       dump_value_31();
700       format = get_u32 ();
701       value = get_double ();
702       printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
703       match_byte (1);
704       match_byte (0);
705       match_byte (0);
706       match_byte (0);
707     }
708   else
709     {
710       dump_value_31();
711
712       char *base = get_string();
713       int x = get_u32();
714       printf ("\"%s\" with %d variables:\n", base, x);
715       for (int i = 0; i < x; i++)
716         {
717           int y = get_u32();
718           if (!y)
719             y = 1;
720           else
721             match_u32_assert(0);
722           for (int j = 0; j <= level; j++)
723             printf ("    ");
724           printf("variable %d has %d values:\n", i, y);
725           for (int j = 0; j < y; j++)
726             {
727               match_byte(0);
728               if (match_byte(3))
729                 {
730                   char *a = get_string();
731                   match_byte_assert(0x58);
732                   char *b = get_string();
733                   char *c = get_string();
734                   for (int k = 0; k <= level + 1; k++)
735                     printf ("    ");
736                   printf ("\"%s\", \"%s\", \"%s\"", a, b, c);
737                 }
738               else
739                 dump_title_value (level+1);
740               putchar('\n');
741             }
742         }
743     }
744 }
745
746 static void
747 dump_footnote_value(int level)
748 {
749   for (int i = 0; i <= level; i++)
750     printf ("    ");
751
752   match_byte (0);
753   match_byte (0);
754   match_byte (0);
755   match_byte (0);
756   if (match_byte (3))
757     {
758       get_string();
759       dump_value_31();
760       get_string();
761       printf("string \"%s\"", get_string());
762       if (!match_byte (0))
763         match_byte_assert (1);
764     }
765   else if (match_byte (5))
766     {
767       match_byte_assert (0x58);
768       printf ("variable \"%s\"", get_string());
769       get_string();
770       if (!match_byte(1) && !match_byte(2))
771         match_byte_assert(3);
772     }
773   else if (match_byte (2))
774     {
775       unsigned int format;
776       char *var, *vallab;
777       double value;
778
779       match_byte_assert (0x58);
780       format = get_u32 ();
781       value = get_double ();
782       var = get_string ();
783       vallab = get_string ();
784       printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
785               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
786       if (!match_byte (1) && !match_byte(2))
787         match_byte_assert (3);
788       match_byte (0);
789       match_byte (0);
790       match_byte (0);
791     }
792   else if (match_byte (4))
793     {
794       unsigned int format;
795       char *var, *vallab, *value;
796
797       match_byte_assert (0x58);
798       format = get_u32 ();
799       vallab = get_string ();
800       var = get_string ();
801       if (!match_byte(1) && !match_byte(2))
802         match_byte_assert (3);
803       value = get_string ();
804       printf ("value \"%s\" format %d(%d.%d) var \"%s\" vallab \"%s\"",
805               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
806       match_byte (0);
807       match_byte (0);
808       match_byte (0);
809     }
810   else if (match_byte (1))
811     {
812       unsigned int format;
813       double value;
814
815       dump_value_31();
816       format = get_u32 ();
817       value = get_double ();
818       printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
819     }
820   else
821     {
822       dump_value_31();
823       char *base = get_string();
824       int x = get_u32();
825       printf ("\"%s\"; %d variables:\n", base, x);
826       for (int i = 0; i < x; i++)
827         {
828           int y = get_u32();
829           if (!y)
830             y = 1;
831           else
832             match_u32_assert(0);
833           for (int j = 0; j <= level; j++)
834             printf ("    ");
835           printf("variable %d has %d values:\n", i, y);
836           for (int j = 0; j < y; j++)
837             {
838               dump_footnote_value (level+1);
839               putchar('\n');
840             }
841         }
842     }
843 }
844
845 static void
846 dump_title(void)
847 {
848   pos = 0x27;
849   dump_title_value(0); putchar('\n');
850   dump_title_value(0); putchar('\n');
851   match_byte_assert(0x31);
852   dump_title_value(0); putchar('\n');
853   match_byte(0);
854   match_byte_assert(0x58);
855   if (match_byte(0x31))
856     {
857       dump_footnote_value(0); putchar('\n');
858     }
859   else
860     match_byte_assert(0x58);
861
862
863   int n_footnotes = get_u32();
864   if (n_footnotes >= 20)
865     {
866       fprintf(stderr, "%08x: %d footnotes\n", pos - 4, n_footnotes);
867       exit(1);
868     }
869
870   printf("------\n%d footnotes\n", n_footnotes);
871   if (n_footnotes < 20)
872     {
873       for (int i = 0; i < n_footnotes; i++)
874         {
875           printf("footnote %d:\n", i);
876           dump_footnote_value(0);
877           match_byte(0);
878           match_byte(0);
879           match_byte(0);
880           match_byte(0);
881           if (match_byte (0x31))
882             {
883               /* Custom footnote marker string. */
884               match_byte_assert(3);
885               get_string();
886               match_byte_assert(0x58);
887               match_u32_assert(0);
888               get_string();
889             }
890           else
891             match_byte_assert (0x58);
892           printf("(%d)\n", get_u32());
893         }
894     }
895 }
896
897 static int
898 find_dimensions(void)
899 {
900   {
901     const char dimensions[] = "-,,,.\0";
902     int x = try_find_tail(dimensions, sizeof dimensions - 1);
903     if (x)
904       return x;
905   }
906
907   const char dimensions[] = "-,,, .\0";
908   return find_tail(dimensions, sizeof dimensions - 1);
909 }
910
911 static void
912 dump_fonts(void)
913 {
914   printf("fonts: offset=%08x\n", pos);
915   match_byte(0);
916   for (int i = 1; i <= 8; i++)
917     {
918       printf("%08x: font %d, ", pos, i);
919       match_byte_assert(i);
920       match_byte_assert(0x31);
921       printf("%s, ", get_string());
922       match_byte_assert(0);
923       match_byte_assert(0);
924       if (!match_byte(0x40) && !match_byte(0x20) && !match_byte(0x80) && !match_byte(0x10))
925         match_byte_assert(0x50);
926       if (!match_byte(0x41))
927         match_byte_assert(0x51);
928       pos += 13;
929       printf ("%s, ", get_string());
930       printf ("%s, ", get_string());
931       match_u32_assert(0);
932       match_u32_assert(0);
933       pos++;
934       get_u32();
935       get_u32();
936       get_u32();
937       get_u32();
938       putchar('\n');
939     }
940
941   match_u32_assert(240);
942   pos += 240;
943
944   match_u32_assert(18);
945   pos += 18;
946
947   if (match_u32(117))
948     pos += 117;
949   else
950     {
951       match_u32_assert(142);
952       pos += 142;
953     }
954
955   int count = get_u32();
956   pos += 4 * count;
957
958   char *encoding = get_string();
959   printf("encoding=%s\n", encoding);
960
961   if (!match_u32(0))
962     match_u32_assert(UINT32_MAX);
963   if (!match_byte(0))
964     match_byte_assert(1);
965   match_byte_assert(0);
966   if (!match_byte(0))
967     match_byte_assert(1);
968   if (!match_byte(0x99) && !match_byte(0x98))
969     match_byte_assert(0x97);
970   match_byte_assert(7);
971   match_byte_assert(0);
972   match_byte_assert(0);
973   if (match_byte('.'))
974     match_byte_assert(',');
975   else
976     {
977       match_byte_assert(',');
978       if (!match_byte('.'))
979         match_byte_assert(' ');
980     }
981   match_u32_assert(5);
982   for (int i = 0; i < 5; i++)
983     get_string();
984   pos += get_u32();
985   if (pos != find_dimensions())
986     fprintf (stderr, "%08x / %08x\n", pos, find_dimensions());
987 }
988
989 int
990 main(int argc, char *argv[])
991 {
992   size_t start;
993   struct stat s;
994
995   if (isatty(STDIN_FILENO))
996     {
997       fprintf(stderr, "redirect stdin from a .bin file\n");
998       exit(1);
999     }
1000   if (fstat(STDIN_FILENO, &s))
1001     {
1002       perror("fstat");
1003       exit(1);
1004     }
1005   n = s.st_size;
1006   data = malloc(n);
1007   if (!data)
1008     {
1009       perror("malloc");
1010       exit(1);
1011     }
1012   if (read(STDIN_FILENO, data, n) != n)
1013     {
1014       perror("read");
1015       exit(1);
1016     }
1017
1018   if (argc > 1)
1019     {
1020       if (!strcmp(argv[1], "title0"))
1021         {
1022           pos = 0x27;
1023           if (match_byte (0x03)
1024               || (match_byte (0x05) && match_byte (0x58)))
1025             printf ("%s\n", get_string());
1026           else
1027             printf ("<unknown>\n");
1028           return 0;
1029         }
1030       else if (!strcmp(argv[1], "title"))
1031         {
1032           dump_title();
1033           exit(0);
1034         }
1035       else if (!strcmp(argv[1], "titleraw"))
1036         {
1037           const char fonts[] = "\x01\x31\x09\0\0\0SansSerif";
1038           start = 0x27;
1039           n = find(fonts, sizeof fonts - 1);
1040         }
1041       else if (!strcmp(argv[1], "fonts"))
1042         {
1043           const char fonts[] = "\x01\x31\x09\0\0\0SansSerif";
1044           const char styles[] = "\xf0\0\0\0";
1045           start = find(fonts, sizeof fonts - 1);
1046           n = find(styles, sizeof styles - 1);
1047         }
1048       else if (!strcmp(argv[1], "styles"))
1049         {
1050           const char styles[] = "\xf0\0\0\0";
1051           const char dimensions[] = "-,,,.\0";
1052           start = find(styles, sizeof styles - 1);
1053           n = find(dimensions, sizeof dimensions - 1) + sizeof dimensions - 1;
1054         }
1055       else if (!strcmp(argv[1], "dimensions") || !strcmp(argv[1], "all"))
1056         {
1057           pos = 0;
1058           match_byte_assert(1);
1059           match_byte_assert(0);
1060           match_u32_assert(3);
1061           match_byte_assert(1);
1062           if (!match_byte(0))
1063             match_byte_assert(1);
1064           match_byte_assert(0);
1065           match_byte_assert(0);
1066           if (!match_byte(0))
1067             match_byte_assert(1);
1068           pos++;
1069           match_byte_assert(0);
1070           match_byte_assert(0);
1071           match_byte_assert(0);
1072           dump_title ();
1073           dump_fonts();
1074           dump_dims ();
1075           printf("\n\ndata:\n");
1076           dump_data ();
1077           if (pos == n - 1)
1078             match_byte_assert (1);
1079           if (pos != n)
1080             {
1081               fprintf (stderr, "%x / %x\n", pos, n);
1082               exit(1);
1083             }
1084           exit(0);
1085         }
1086       else
1087         {
1088           fprintf (stderr, "unknown section %s\n", argv[1]);
1089           exit(1);
1090         }
1091     }
1092   else
1093     start = 0x27;
1094
1095   for (size_t i = start; i < n; )
1096     {
1097       if (i + 5 <= n
1098           && data[i]
1099           //&& !data[i + 1]
1100           && !data[i + 2]
1101           && !data[i + 3]
1102           && i + 4 + data[i] + data[i + 1] * 256 <= n
1103           && all_ascii(&data[i + 4], data[i] + data[i + 1] * 256))
1104         {
1105           fputs("\n\"", stdout);
1106           fwrite(&data[i + 4], 1, data[i] + data[i + 1] * 256, stdout);
1107           fputs("\" ", stdout);
1108
1109           i += 4 + data[i] + data[i + 1] * 256;
1110         }
1111       else if (i + 12 <= n
1112                && data[i + 1] == 40
1113                && data[i + 2] == 5
1114                && data[i + 3] == 0)
1115         {
1116           double d;
1117
1118           memcpy (&d, &data[i + 4], 8);
1119           printf ("F40.%d(%.*f)\n", data[i], data[i], d);
1120           i += 12;
1121         }
1122       else if (i + 12 <= n
1123                && data[i + 1] == 40
1124                && data[i + 2] == 31
1125                && data[i + 3] == 0)
1126         {
1127           double d;
1128
1129           memcpy (&d, &data[i + 4], 8);
1130           printf ("PCT40.%d(%.*f)\n", data[i], data[i], d);
1131           i += 12;
1132         }
1133       else if (i + 4 <= n
1134                && (data[i] && data[i] != 88 && data[i] != 0x41)
1135                && !data[i + 1]
1136                && !data[i + 2]
1137                && !data[i + 3])
1138         {
1139           printf ("i%d ", data[i]);
1140           i += 4;
1141         }
1142       else
1143         {
1144           printf("%02x ", data[i]);
1145           i++;
1146         }
1147     }
1148
1149   return 0;
1150 }