Make dump_title_value() still a little closer to dump_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 (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_u32 (3))
384         match_u32_assert (2);
385       match_byte (0);
386     }
387   else if (match_byte (1))
388     {
389       unsigned int format;
390       double value;
391
392       match_byte_assert (0x58);
393       format = get_u32 ();
394       value = get_double ();
395       printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
396       match_byte (1);
397       match_byte (0);
398       match_byte (0);
399       match_byte (0);
400       match_byte (1);
401     }
402   else
403     {
404       match_byte (0);
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       match_byte (0);
576     }
577   else
578     {
579       dump_value_31();
580       char *base = get_string();
581       int x = get_u32();
582       printf ("\"%s\"; %d variables:\n", base, x);
583       for (int i = 0; i < x; i++)
584         {
585           int y = get_u32();
586           if (!y)
587             y = 1;
588           else
589             match_u32_assert(0);
590           for (int j = 0; j <= 0; j++)
591             printf ("    ");
592           printf("variable %d has %d values:\n", i, y);
593           for (int j = 0; j < y; j++)
594             dump_data_value();
595         }
596     }
597 }
598
599 static void
600 dump_data(void)
601 {
602 #if 1
603   int a[16];
604   for (int i = 0; i < 3 + n_dims; i++)
605     a[i] = get_u32();
606   printf ("data intro:");
607   for (int i = 0; i < 3 + n_dims; i++)
608     printf(" %d", a[i]);
609   printf("\n");
610 #else
611   fprintf (stderr,"data intro (%d dims):", n_dims);
612   for (int i = 0; i < 3+n_dims; i++)
613     fprintf (stderr," %d", get_u32());
614   fprintf(stderr,"\n");
615 #endif
616   int x = get_u32();
617   printf ("%d data values, starting at %08x\n", x, pos);
618   for (int i = 0; i < x; i++)
619     {
620       printf("%08x, index %d:\n", pos, get_u32());
621       match_u32_assert(0);
622       dump_data_value();
623       putchar('\n');
624     }
625 }
626
627 static void
628 dump_title_value(int level)
629 {
630   for (int i = 0; i <= level; i++)
631     printf ("    ");
632
633   match_byte (0);
634   match_byte (0);
635   match_byte (0);
636   match_byte (0);
637   if (match_byte (3))
638     {
639       get_string();
640       dump_value_31();
641       get_string();
642       printf("string \"%s\"", get_string());
643       match_byte (0);
644       match_byte (1);
645       match_byte (1);
646       match_byte (0);
647       match_byte (0);
648       match_byte (0);
649     }
650   else if (match_byte (5))
651     {
652       dump_value_31();
653       printf ("variable \"%s\"", get_string());
654       get_string();
655       if (!match_byte(1) && !match_byte(2))
656         match_byte_assert(3);
657     }
658   else if (match_byte (2))
659     {
660       unsigned int format;
661       char *var, *vallab;
662       double value;
663
664       match_byte_assert (0x58);
665       format = get_u32 ();
666       value = get_double ();
667       var = get_string ();
668       vallab = get_string ();
669       printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
670               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
671       if (!match_byte (1) && !match_byte(2))
672         match_byte_assert (3);
673       match_byte (0);
674       match_byte (0);
675       match_byte (0);
676     }
677   else if (match_byte (4))
678     {
679       unsigned int format;
680       char *var, *vallab, *value;
681
682       match_byte_assert (0x58);
683       format = get_u32 ();
684       vallab = get_string ();
685       var = get_string ();
686       if (!match_byte(1) && !match_byte(2))
687         match_byte_assert (3);
688       value = get_string ();
689       printf ("value \"%s\" format %d(%d.%d) var \"%s\" vallab \"%s\"",
690               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
691       match_byte (0);
692       match_byte (0);
693       match_byte (0);
694     }
695   else if (match_byte (1))
696     {
697       unsigned int format;
698       double value;
699
700       dump_value_31();
701       format = get_u32 ();
702       value = get_double ();
703       printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
704       match_byte (1);
705       match_byte (0);
706       match_byte (0);
707       match_byte (0);
708     }
709   else
710     {
711       dump_value_31();
712
713       char *base = get_string();
714       int x = get_u32();
715       printf ("\"%s\" with %d variables:\n", base, x);
716       for (int i = 0; i < x; i++)
717         {
718           int y = get_u32();
719           if (!y)
720             y = 1;
721           else
722             match_u32_assert(0);
723           for (int j = 0; j <= level; j++)
724             printf ("    ");
725           printf("variable %d has %d values:\n", i, y);
726           for (int j = 0; j < y; j++)
727             {
728               match_byte(0);
729               if (match_byte(3))
730                 {
731                   char *a = get_string();
732                   match_byte_assert(0x58);
733                   char *b = get_string();
734                   char *c = get_string();
735                   for (int k = 0; k <= level + 1; k++)
736                     printf ("    ");
737                   printf ("\"%s\", \"%s\", \"%s\"", a, b, c);
738                 }
739               else
740                 dump_title_value (level+1);
741               putchar('\n');
742             }
743         }
744     }
745 }
746
747 static void
748 dump_footnote_value(int level)
749 {
750   for (int i = 0; i <= level; i++)
751     printf ("    ");
752
753   match_byte (0);
754   match_byte (0);
755   match_byte (0);
756   match_byte (0);
757   if (match_byte (3))
758     {
759       get_string();
760       dump_value_31();
761       get_string();
762       printf("string \"%s\"", get_string());
763       if (!match_byte (0))
764         match_byte_assert (1);
765     }
766   else if (match_byte (5))
767     {
768       match_byte_assert (0x58);
769       printf ("variable \"%s\"", get_string());
770       get_string();
771       if (!match_byte(1) && !match_byte(2))
772         match_byte_assert(3);
773     }
774   else if (match_byte (2))
775     {
776       unsigned int format;
777       char *var, *vallab;
778       double value;
779
780       match_byte_assert (0x58);
781       format = get_u32 ();
782       value = get_double ();
783       var = get_string ();
784       vallab = get_string ();
785       printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
786               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
787       if (!match_byte (1) && !match_byte(2))
788         match_byte_assert (3);
789       match_byte (0);
790       match_byte (0);
791       match_byte (0);
792       match_byte (0);
793       match_byte (0);
794       match_byte (0);
795       match_byte (0);
796     }
797   else if (match_byte (4))
798     {
799       unsigned int format;
800       char *var, *vallab, *value;
801
802       match_byte_assert (0x58);
803       format = get_u32 ();
804       vallab = get_string ();
805       var = get_string ();
806       if (!match_byte(1) && !match_byte(2))
807         match_byte_assert (3);
808       value = get_string ();
809       printf ("value \"%s\" format %d(%d.%d) var \"%s\" vallab \"%s\"",
810               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
811       match_byte (0);
812       match_byte (0);
813       match_byte (0);
814       match_byte (0);
815     }
816   else if (match_byte (1))
817     {
818       unsigned int format;
819       double value;
820
821       dump_value_31();
822       format = get_u32 ();
823       value = get_double ();
824       printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
825     }
826   else
827     {
828       dump_value_31();
829       char *base = get_string();
830       int x = get_u32();
831       printf ("\"%s\"; %d variables:\n", base, x);
832       for (int i = 0; i < x; i++)
833         {
834           int y = get_u32();
835           if (!y)
836             y = 1;
837           else
838             match_u32_assert(0);
839           for (int j = 0; j <= level; j++)
840             printf ("    ");
841           printf("variable %d has %d values:\n", i, y);
842           for (int j = 0; j < y; j++)
843             {
844               if (match_byte(3))
845                 {
846                   char *a = get_string();
847                   match_byte_assert(0x58);
848                   char *b = get_string();
849                   char *c = get_string();
850                   for (int k = 0; k <= level + 1; k++)
851                     printf ("    ");
852                   printf ("\"%s\", \"%s\", \"%s\"", a, b, c);
853                   if (!match_byte(1))
854                     match_byte_assert(0);
855                 }
856               else
857                 dump_footnote_value (level+1);
858               putchar('\n');
859             }
860         }
861     }
862 }
863
864 static void
865 dump_title(void)
866 {
867   pos = 0x27;
868   dump_title_value(0); putchar('\n');
869   dump_title_value(0); putchar('\n');
870   match_byte_assert(0x31);
871   dump_title_value(0); putchar('\n');
872   match_byte(0);
873   match_byte_assert(0x58);
874   if (match_byte(0x31))
875     {
876       dump_footnote_value(0); putchar('\n');
877     }
878   else
879     match_byte_assert(0x58);
880
881
882   int n_footnotes = get_u32();
883   if (n_footnotes >= 20)
884     {
885       fprintf(stderr, "%08x: %d footnotes\n", pos - 4, n_footnotes);
886       exit(1);
887     }
888
889   printf("------\n%d footnotes\n", n_footnotes);
890   if (n_footnotes < 20)
891     {
892       for (int i = 0; i < n_footnotes; i++)
893         {
894           printf("footnote %d:\n", i);
895           dump_footnote_value(0);
896           match_byte(0);
897           match_byte(0);
898           match_byte(0);
899           match_byte(0);
900           if (match_byte (0x31))
901             {
902               /* Custom footnote marker string. */
903               match_byte_assert(3);
904               get_string();
905               match_byte_assert(0x58);
906               match_u32_assert(0);
907               get_string();
908             }
909           else
910             match_byte_assert (0x58);
911           printf("(%d)\n", get_u32());
912         }
913     }
914 }
915
916 static int
917 find_dimensions(void)
918 {
919   {
920     const char dimensions[] = "-,,,.\0";
921     int x = try_find_tail(dimensions, sizeof dimensions - 1);
922     if (x)
923       return x;
924   }
925
926   const char dimensions[] = "-,,, .\0";
927   return find_tail(dimensions, sizeof dimensions - 1);
928 }
929
930 static void
931 dump_fonts(void)
932 {
933   printf("fonts: offset=%08x\n", pos);
934   match_byte(0);
935   for (int i = 1; i <= 8; i++)
936     {
937       printf("%08x: font %d, ", pos, i);
938       match_byte_assert(i);
939       match_byte_assert(0x31);
940       printf("%s, ", get_string());
941       match_byte_assert(0);
942       match_byte_assert(0);
943       if (!match_byte(0x40) && !match_byte(0x20) && !match_byte(0x80) && !match_byte(0x10))
944         match_byte_assert(0x50);
945       if (!match_byte(0x41))
946         match_byte_assert(0x51);
947       pos += 13;
948       printf ("%s, ", get_string());
949       printf ("%s, ", get_string());
950       match_u32_assert(0);
951       match_u32_assert(0);
952       pos++;
953       get_u32();
954       get_u32();
955       get_u32();
956       get_u32();
957       putchar('\n');
958     }
959
960   match_u32_assert(240);
961   pos += 240;
962
963   match_u32_assert(18);
964   pos += 18;
965
966   if (match_u32(117))
967     pos += 117;
968   else
969     {
970       match_u32_assert(142);
971       pos += 142;
972     }
973
974   int count = get_u32();
975   pos += 4 * count;
976
977   char *encoding = get_string();
978   printf("encoding=%s\n", encoding);
979
980   if (!match_u32(0))
981     match_u32_assert(UINT32_MAX);
982   if (!match_byte(0))
983     match_byte_assert(1);
984   match_byte_assert(0);
985   if (!match_byte(0))
986     match_byte_assert(1);
987   if (!match_byte(0x99) && !match_byte(0x98))
988     match_byte_assert(0x97);
989   match_byte_assert(7);
990   match_byte_assert(0);
991   match_byte_assert(0);
992   if (match_byte('.'))
993     match_byte_assert(',');
994   else
995     {
996       match_byte_assert(',');
997       if (!match_byte('.'))
998         match_byte_assert(' ');
999     }
1000   match_u32_assert(5);
1001   for (int i = 0; i < 5; i++)
1002     get_string();
1003   pos += get_u32();
1004   if (pos != find_dimensions())
1005     fprintf (stderr, "%08x / %08x\n", pos, find_dimensions());
1006 }
1007
1008 int
1009 main(int argc, char *argv[])
1010 {
1011   size_t start;
1012   struct stat s;
1013
1014   if (isatty(STDIN_FILENO))
1015     {
1016       fprintf(stderr, "redirect stdin from a .bin file\n");
1017       exit(1);
1018     }
1019   if (fstat(STDIN_FILENO, &s))
1020     {
1021       perror("fstat");
1022       exit(1);
1023     }
1024   n = s.st_size;
1025   data = malloc(n);
1026   if (!data)
1027     {
1028       perror("malloc");
1029       exit(1);
1030     }
1031   if (read(STDIN_FILENO, data, n) != n)
1032     {
1033       perror("read");
1034       exit(1);
1035     }
1036
1037   if (argc > 1)
1038     {
1039       if (!strcmp(argv[1], "title0"))
1040         {
1041           pos = 0x27;
1042           if (match_byte (0x03)
1043               || (match_byte (0x05) && match_byte (0x58)))
1044             printf ("%s\n", get_string());
1045           else
1046             printf ("<unknown>\n");
1047           return 0;
1048         }
1049       else if (!strcmp(argv[1], "title"))
1050         {
1051           dump_title();
1052           exit(0);
1053         }
1054       else if (!strcmp(argv[1], "titleraw"))
1055         {
1056           const char fonts[] = "\x01\x31\x09\0\0\0SansSerif";
1057           start = 0x27;
1058           n = find(fonts, sizeof fonts - 1);
1059         }
1060       else if (!strcmp(argv[1], "fonts"))
1061         {
1062           const char fonts[] = "\x01\x31\x09\0\0\0SansSerif";
1063           const char styles[] = "\xf0\0\0\0";
1064           start = find(fonts, sizeof fonts - 1);
1065           n = find(styles, sizeof styles - 1);
1066         }
1067       else if (!strcmp(argv[1], "styles"))
1068         {
1069           const char styles[] = "\xf0\0\0\0";
1070           const char dimensions[] = "-,,,.\0";
1071           start = find(styles, sizeof styles - 1);
1072           n = find(dimensions, sizeof dimensions - 1) + sizeof dimensions - 1;
1073         }
1074       else if (!strcmp(argv[1], "dimensions") || !strcmp(argv[1], "all"))
1075         {
1076           pos = 0;
1077           match_byte_assert(1);
1078           match_byte_assert(0);
1079           match_u32_assert(3);
1080           match_byte_assert(1);
1081           if (!match_byte(0))
1082             match_byte_assert(1);
1083           match_byte_assert(0);
1084           match_byte_assert(0);
1085           if (!match_byte(0))
1086             match_byte_assert(1);
1087           pos++;
1088           match_byte_assert(0);
1089           match_byte_assert(0);
1090           match_byte_assert(0);
1091           dump_title ();
1092           dump_fonts();
1093           dump_dims ();
1094           printf("\n\ndata:\n");
1095           dump_data ();
1096           if (pos == n - 1)
1097             match_byte_assert (1);
1098           if (pos != n)
1099             {
1100               fprintf (stderr, "%x / %x\n", pos, n);
1101               exit(1);
1102             }
1103           exit(0);
1104         }
1105       else
1106         {
1107           fprintf (stderr, "unknown section %s\n", argv[1]);
1108           exit(1);
1109         }
1110     }
1111   else
1112     start = 0x27;
1113
1114   for (size_t i = start; i < n; )
1115     {
1116       if (i + 5 <= n
1117           && data[i]
1118           //&& !data[i + 1]
1119           && !data[i + 2]
1120           && !data[i + 3]
1121           && i + 4 + data[i] + data[i + 1] * 256 <= n
1122           && all_ascii(&data[i + 4], data[i] + data[i + 1] * 256))
1123         {
1124           fputs("\n\"", stdout);
1125           fwrite(&data[i + 4], 1, data[i] + data[i + 1] * 256, stdout);
1126           fputs("\" ", stdout);
1127
1128           i += 4 + data[i] + data[i + 1] * 256;
1129         }
1130       else if (i + 12 <= n
1131                && data[i + 1] == 40
1132                && data[i + 2] == 5
1133                && data[i + 3] == 0)
1134         {
1135           double d;
1136
1137           memcpy (&d, &data[i + 4], 8);
1138           printf ("F40.%d(%.*f)\n", data[i], data[i], d);
1139           i += 12;
1140         }
1141       else if (i + 12 <= n
1142                && data[i + 1] == 40
1143                && data[i + 2] == 31
1144                && data[i + 3] == 0)
1145         {
1146           double d;
1147
1148           memcpy (&d, &data[i + 4], 8);
1149           printf ("PCT40.%d(%.*f)\n", data[i], data[i], d);
1150           i += 12;
1151         }
1152       else if (i + 4 <= n
1153                && (data[i] && data[i] != 88 && data[i] != 0x41)
1154                && !data[i + 1]
1155                && !data[i + 2]
1156                && !data[i + 3])
1157         {
1158           printf ("i%d ", data[i]);
1159           i += 4;
1160         }
1161       else
1162         {
1163           printf("%02x ", data[i]);
1164           i++;
1165         }
1166     }
1167
1168   return 0;
1169 }