Generalize 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           match_u32_assert (0);
158           int subn = get_u32 ();
159           printf ("nested %d bytes", subn);
160           pos += subn;
161         }
162       else if (match_u32 (1))
163         {
164           printf("(footnote %d) ", get_u32());
165           match_byte_assert (0);
166           match_byte_assert (0);
167           int subn = get_u32 ();
168           printf ("nested %d bytes", subn);
169           pos += subn;
170         }
171       else if (match_u32 (2))
172         {
173           printf("(special 2)");
174           match_byte_assert(0);
175           match_byte_assert(0);
176           match_u32_assert(1);
177           match_byte_assert(0);
178           match_byte_assert(0);
179           int subn = get_u32 ();
180           printf ("nested %d bytes", subn);
181           pos += subn;
182         }
183       else
184         {
185           match_u32_assert(3);
186           printf("(special 3)");
187           match_byte_assert(0);
188           match_byte_assert(0);
189           match_byte_assert(1);
190           match_byte_assert(0);
191           int subn = get_u32 ();
192           printf ("nested %d bytes, ", subn);
193           pos += subn;
194           subn = get_u32 ();
195           printf ("nested %d bytes, ", subn);
196           pos += subn;
197         }
198     }
199   else
200     match_byte_assert (0x58);
201 }
202
203 static void
204 dump_value(int level)
205 {
206   for (int i = 0; i <= level; i++)
207     printf ("    ");
208
209   match_byte (0);
210   match_byte (0);
211   match_byte (0);
212   match_byte (0);
213   if (match_byte (3))
214     {
215       char *s1 = get_string();
216       dump_value_31();
217       char *s2 = get_string();
218       char *s3 = get_string();
219       if (strcmp(s1, s3))
220         printf("strings \"%s\", \"%s\" and \"%s\"", s1, s2, s3);
221       else
222         printf("string \"%s\" and \"%s\"", s1, s2);
223       match_byte (0);
224       match_byte (0);
225       match_byte (0);
226       match_byte (1);
227       match_byte (1);
228       match_byte (0);
229       match_byte (0);
230       match_byte (0);
231       match_byte (1);
232     }
233   else if (match_byte (5))
234     {
235       match_byte_assert (0x58);
236       printf ("variable \"%s\"", get_string());
237       get_string();
238       if (!match_byte(1) && !match_byte(2))
239         match_byte_assert(3);
240       match_byte (0);
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       match_byte (0);
264       match_byte (0);
265       match_byte (0);
266       match_byte (0);
267     }
268   else if (match_byte (4))
269     {
270       unsigned int format;
271       char *var, *vallab, *value;
272
273       match_byte_assert (0x58);
274       format = get_u32 ();
275       vallab = get_string ();
276       var = get_string ();
277       if (!match_byte(1) && !match_byte(2))
278         match_byte_assert (3);
279       value = get_string ();
280       printf ("value \"%s\" format %d(%d.%d) var \"%s\" vallab \"%s\"",
281               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
282       match_byte (0);
283       match_byte (0);
284       match_byte (0);
285       match_byte (0);
286     }
287   else if (match_byte (1))
288     {
289       unsigned int format;
290       double value;
291
292       dump_value_31();
293       format = get_u32 ();
294       value = get_double ();
295       printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
296       match_byte (1);
297       match_byte (0);
298       match_byte (0);
299       match_byte (0);
300       match_byte (1);
301     }
302   else
303     {
304       dump_value_31();
305       char *base = get_string();
306       int x = get_u32();
307       printf ("\"%s\" with %d variables:\n", base, x);
308       if (match_u32(0))
309         {
310           for (int i = 0; i < x; i++)
311             {
312               dump_value (level+1);
313               putchar('\n');
314             }
315         }
316       else
317         {
318           for (int i = 0; i < x; i++)
319             {
320               int y = get_u32();
321               match_u32_assert(0);
322               for (int j = 0; j <= level; j++)
323                 printf ("    ");
324               printf("variable %d has %d values:\n", i, y);
325               for (int j = 0; j < y; j++)
326                 {
327                   if (match_byte(3))
328                     {
329                       char *a = get_string();
330                       match_byte_assert(0x58);
331                       char *b = get_string();
332                       char *c = get_string();
333                       for (int k = 0; k <= level + 1; k++)
334                         printf ("    ");
335                       printf ("\"%s\", \"%s\", \"%s\"", a, b, c);
336                       match_byte(0);
337                       match_byte(0);
338                       match_byte(0);
339                       match_byte(0);
340                       match_byte(0);
341                     }
342                   else
343                     dump_value (level+1);
344                   putchar('\n');
345                 }
346             }
347         }
348     }
349 }
350
351 static void
352 dump_dim_value_31(void)
353 {
354   if (match_byte (0x31))
355     {
356       if (match_u32 (0))
357         {
358           match_u32_assert (0);
359           int subn = get_u32 ();
360           printf ("nested %d bytes", subn);
361           pos += subn;
362         }
363       else if (match_u32 (1)) 
364         {
365           printf("(footnote %d) ", get_u32());
366           match_byte_assert (0);
367           match_byte_assert (0);
368           int subn = get_u32 ();
369           printf ("nested %d bytes", subn);
370           pos += subn;
371         }
372     }
373   else
374     match_byte_assert (0x58);
375 }
376
377 static void
378 dump_dim_value(int level)
379 {
380   for (int i = 0; i <= level; i++)
381     printf ("    ");
382
383   if (match_byte (3))
384     {
385       get_string();
386       dump_dim_value_31();
387       get_string();
388       printf("string \"%s\"", get_string());
389       match_byte (0);
390       match_byte (1);
391     }
392   else if (match_byte (5))
393     {
394       match_byte_assert (0x58);
395       printf ("variable \"%s\"", get_string());
396       get_string();
397       if (!match_byte (2))
398         match_byte_assert(3);
399     }
400   else if (match_byte (2))
401     {
402       unsigned int format;
403       char *var, *vallab;
404       double value;
405
406       match_byte_assert (0x58);
407       format = get_u32 ();
408       value = get_double ();
409       var = get_string ();
410       vallab = get_string ();
411       printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
412               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
413       if (!match_u32 (3))
414         match_u32_assert (2);
415       match_byte (0);
416     }
417   else if (match_byte (1))
418     {
419       unsigned int format;
420       double value;
421
422       match_byte_assert (0x58);
423       format = get_u32 ();
424       value = get_double ();
425       printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
426       match_byte (1);
427       match_byte (0);
428       match_byte (0);
429       match_byte (0);
430       match_byte (1);
431     }
432   else
433     {
434       match_byte (0);
435       dump_dim_value_31();
436       printf ("; \"%s\", substitutions:", get_string());
437       int total_subs = get_u32();
438       int x = get_u32();
439       if (x)
440         {
441           total_subs = (total_subs - 1) + x;
442           match_u32_assert (0);
443         }
444       printf (" (total %d)", total_subs);
445
446       for (int i = 0; i < total_subs; i++)
447         {
448           putc ('\n', stdout);
449           dump_value (level + 1);
450         }
451     }
452 }
453
454 static void
455 dump_category(int level)
456 {
457   match_byte (0);
458   match_byte (0);
459   match_byte (0);
460   match_byte (0);
461   dump_value (level);
462
463   if (match_u32 (2))
464     get_u32 ();
465   else if (match_u32 (1))
466     {
467       match_byte (0);
468       match_byte (0);
469       match_byte (0);
470       get_u32 ();
471     }
472   else if (match_byte (1))
473     {
474       match_byte (0);
475       if (!match_u32 (2))
476         match_u32_assert (1);
477       match_byte (0);
478       get_u32();
479     }
480   else
481     {
482       match_u32_assert (0);
483       get_u32 ();
484     }
485
486   int n_categories = get_u32();
487   if (n_categories > 0)
488     printf (", %d subcategories:", n_categories);
489   printf("\n");
490   for (int i = 0; i < n_categories; i++)
491     dump_category (level + 1);
492 }
493
494 static void
495 dump_dim(void)
496 {
497   int n_categories;
498   printf("next dim\n");
499   match_byte(0);
500   dump_dim_value(0);
501
502   /* This byte is usually 0x02 but 0x00 and 0x75 (!) have also been spotted. */
503   pos++;
504
505   if (!match_byte(0) && !match_byte(1))
506     match_byte_assert(2);
507   if (!match_u32(0))
508     match_u32_assert(2);
509   if (!match_byte(0))
510     match_byte_assert(1);
511   match_byte(0);
512   match_byte(0);
513   match_byte(0);
514   match_byte(0);
515   get_u32();
516   match_byte(0);
517   match_byte(0);
518   match_byte(0);
519   match_byte(0);
520   n_categories = get_u32();
521   printf("%d nested categories\n", n_categories);
522   for (int i = 0; i < n_categories; i++)
523     dump_category (0);
524 }
525
526 int n_dims;
527 static void
528 dump_dims(void)
529 {
530   n_dims = get_u32();
531   printf ("%u dimensions\n", n_dims);
532   for (int i = 0; i < n_dims; i++)
533     {
534       printf("\n");
535       dump_dim ();
536     }
537 }
538
539 static void
540 dump_data_value_31(void)
541 {
542   if (match_byte (0x31))
543     {
544       if (match_u32 (0))
545         {
546           if (match_u32 (1))
547             get_string();
548           else
549             match_u32_assert (0);
550           int subn = get_u32 ();
551           printf ("nested %d bytes", subn);
552           pos += subn;
553         }
554       else if (match_u32 (1))
555         {
556           printf("(footnote %d) ", get_u32());
557           match_byte_assert (0);
558           match_byte_assert (0);
559           int subn = get_u32 ();
560           printf ("nested %d bytes", subn);
561           pos += subn;
562         }
563       else if (match_u32 (2))
564         {
565           printf("(special 2)");
566           match_byte_assert(0);
567           match_byte_assert(0);
568           match_u32_assert(1);
569           match_byte_assert(0);
570           match_byte_assert(0);
571           int subn = get_u32 ();
572           printf ("nested %d bytes", subn);
573           pos += subn;
574         }
575       else
576         {
577           match_u32_assert(3);
578           printf("(special 3)");
579           match_byte_assert(0);
580           match_byte_assert(0);
581           match_byte_assert(1);
582           match_byte_assert(0);
583           int subn = get_u32 ();
584           printf ("nested %d bytes, ", subn);
585           pos += subn;
586           subn = get_u32 ();
587           printf ("nested %d bytes, ", subn);
588           pos += subn;
589         }
590     }
591   else
592     match_byte_assert (0x58);
593 }
594
595 static void
596 dump_data_value(void)
597 {
598   match_byte(0);
599   match_byte(0);
600   match_byte(0);
601   match_byte(0);
602   if (match_byte (1))
603     {
604       unsigned int format;
605       double value;
606
607       dump_data_value_31();
608       format = get_u32 ();
609       value = get_double ();
610       printf ("    value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
611     }
612   else if (match_byte (3))
613     {
614       get_string();
615       dump_data_value_31();
616       get_string();
617       printf("string \"%s\"", get_string());
618       match_byte (0);
619     }
620   else if (match_byte (2))
621     {
622       unsigned int format;
623       char *var, *vallab;
624       double value;
625
626       match_byte_assert (0x58);
627       format = get_u32 ();
628       value = get_double ();
629       var = get_string ();
630       vallab = get_string ();
631       printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
632               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
633       if (!match_byte (1) && !match_byte(2))
634         match_byte_assert (3);
635     }
636   else if (match_byte (4))
637     {
638       unsigned int format;
639       char *var, *vallab, *value;
640
641       match_byte_assert (0x58);
642       format = get_u32 ();
643       vallab = get_string ();
644       var = get_string ();
645       if (!match_byte(1) && !match_byte(2))
646         match_byte_assert (3);
647       value = get_string ();
648       printf ("value \"%s\" format %d(%d.%d) var \"%s\" vallab \"%s\"",
649               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
650     }
651   else if (match_byte (5))
652     {
653       match_byte_assert (0x58);
654       printf ("variable \"%s\"", get_string());
655       get_string();
656       if (!match_byte(1) && !match_byte(2))
657         match_byte_assert(3);
658       match_byte (0);
659       match_byte (0);
660       match_byte (0);
661       match_byte (0);
662     }
663   else
664     {
665       dump_data_value_31();
666       char *base = get_string();
667       int x = get_u32();
668       printf ("\"%s\"; %d variables:\n", base, x);
669       for (int i = 0; i < x; i++)
670         {
671           int y = get_u32();
672           if (!y)
673             y = 1;
674           else
675             match_u32_assert(0);
676           for (int j = 0; j <= 0; j++)
677             printf ("    ");
678           printf("variable %d has %d values:\n", i, y);
679           for (int j = 0; j < y; j++)
680             dump_data_value();
681         }
682     }
683 }
684
685 static void
686 dump_data(void)
687 {
688 #if 1
689   int a[16];
690   for (int i = 0; i < 3 + n_dims; i++)
691     a[i] = get_u32();
692   printf ("data intro:");
693   for (int i = 0; i < 3 + n_dims; i++)
694     printf(" %d", a[i]);
695   printf("\n");
696 #else
697   fprintf (stderr,"data intro (%d dims):", n_dims);
698   for (int i = 0; i < 3+n_dims; i++)
699     fprintf (stderr," %d", get_u32());
700   fprintf(stderr,"\n");
701 #endif
702   int x = get_u32();
703   printf ("%d data values, starting at %08x\n", x, pos);
704   for (int i = 0; i < x; i++)
705     {
706       printf("%08x, index %d:\n", pos, get_u32());
707       match_u32_assert(0);
708       dump_data_value();
709       putchar('\n');
710     }
711 }
712
713 static void
714 dump_title_value_31(int level)
715 {
716   if (match_byte (0x31))
717     {
718       if (match_u32 (0))
719         {
720           match_u32_assert (0);
721           int subn = get_u32 ();
722           printf ("nested %d bytes", subn);
723           pos += subn;
724         }
725       else if (match_u32 (1))
726         {
727           printf("(footnote %d) ", get_u32());
728           match_byte_assert (0);
729           match_byte_assert (0);
730           int subn = get_u32 ();
731           printf ("nested %d bytes", subn);
732           pos += subn;
733         }
734       else if (match_u32 (2))
735         {
736           printf("(special 2)");
737           match_byte_assert(0);
738           match_byte_assert(0);
739           if (!match_u32(2))
740             match_u32_assert(1);
741           match_byte_assert(0);
742           match_byte_assert(0);
743           int subn = get_u32 ();
744           printf ("nested %d bytes", subn);
745           pos += subn;
746         }
747       else
748         {
749           match_u32_assert(3);
750           printf("(special 3)");
751           match_byte_assert(0);
752           match_byte_assert(0);
753           match_byte_assert(1);
754           match_byte_assert(0);
755           int subn = get_u32 ();
756           printf ("nested %d bytes, ", subn);
757           pos += subn;
758           subn = get_u32 ();
759           printf ("nested %d bytes, ", subn);
760           pos += subn;
761         }
762     }
763   else
764     match_byte_assert (0x58);
765 }
766
767 static void
768 dump_title_value(int level)
769 {
770   for (int i = 0; i <= level; i++)
771     printf ("    ");
772
773   match_byte (0);
774   match_byte (0);
775   match_byte (0);
776   match_byte (0);
777   match_byte (0);
778   if (match_byte (3))
779     {
780       get_string();
781       dump_title_value_31(level);
782       get_string();
783       printf("string \"%s\"", get_string());
784       match_byte (0);
785       match_byte (0);
786       match_byte (0);
787       match_byte (1);
788       match_byte (1);
789       match_byte (0);
790       match_byte (0);
791       match_byte (0);
792       match_byte (1);
793     }
794   else if (match_byte (5))
795     {
796       dump_title_value_31(level);
797       printf ("variable \"%s\"", get_string());
798       get_string();
799       if (!match_byte(1) && !match_byte(2))
800         match_byte_assert(3);
801     }
802   else if (match_byte (2))
803     {
804       unsigned int format;
805       char *var, *vallab;
806       double value;
807
808       match_byte_assert (0x58);
809       format = get_u32 ();
810       value = get_double ();
811       var = get_string ();
812       vallab = get_string ();
813       printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
814               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
815       if (!match_byte (1) && !match_byte(2))
816         match_byte_assert (3);
817       match_byte (0);
818       match_byte (0);
819       match_byte (0);
820       match_byte (0);
821       match_byte (0);
822       match_byte (0);
823       match_byte (0);
824     }
825   else if (match_byte (4))
826     {
827       unsigned int format;
828       char *var, *vallab, *value;
829
830       match_byte_assert (0x58);
831       format = get_u32 ();
832       vallab = get_string ();
833       var = get_string ();
834       if (!match_byte(1) && !match_byte(2))
835         match_byte_assert (3);
836       value = get_string ();
837       printf ("value \"%s\" format %d(%d.%d) var \"%s\" vallab \"%s\"",
838               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
839       match_byte (0);
840       match_byte (0);
841       match_byte (0);
842       match_byte (0);
843     }
844   else if (match_byte (1))
845     {
846       unsigned int format;
847       double value;
848
849       dump_title_value_31(level);
850       format = get_u32 ();
851       value = get_double ();
852       printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
853       match_byte (1);
854       match_byte (0);
855       match_byte (0);
856       match_byte (0);
857       match_byte (1);
858     }
859   else
860     {
861       dump_title_value_31(level);
862
863       char *base = get_string();
864       int x = get_u32();
865       printf ("\"%s\" with %d variables:\n", base, x);
866       for (int i = 0; i < x; i++)
867         {
868           int y = get_u32();
869           if (!y)
870             y = 1;
871           else
872             match_u32_assert(0);
873           for (int j = 0; j <= level; j++)
874             printf ("    ");
875           printf("variable %d has %d values:\n", i, y);
876           for (int j = 0; j < y; j++)
877             {
878               match_byte(0);
879               if (match_byte(3))
880                 {
881                   char *a = get_string();
882                   match_byte_assert(0x58);
883                   char *b = get_string();
884                   char *c = get_string();
885                   for (int k = 0; k <= level + 1; k++)
886                     printf ("    ");
887                   printf ("\"%s\", \"%s\", \"%s\"", a, b, c);
888                 }
889               else
890                 dump_title_value (level+1);
891               putchar('\n');
892             }
893         }
894     }
895 }
896
897 static void
898 dump_footnote_value_31(void)
899 {
900   if (match_byte (0x31))
901     {
902       if (match_u32 (0))
903         {
904
905           match_u32_assert (0);
906           int subn = get_u32 ();
907           printf ("nested %d bytes", subn);
908           pos += subn;
909         }
910       else if (match_u32 (1))
911         {
912           printf("(footnote %d) ", get_u32());
913           match_byte_assert (0);
914           match_byte_assert (0);
915           int subn = get_u32 ();
916           printf ("nested %d bytes", subn);
917           pos += subn;
918         }
919       else if (match_u32 (2))
920         {
921           printf("(special 2)");
922           match_byte_assert(0);
923           match_byte_assert(0);
924           match_u32_assert(1);
925           match_byte_assert(0);
926           match_byte_assert(0);
927           int subn = get_u32 ();
928           printf ("nested %d bytes", subn);
929           pos += subn;
930         }
931       else
932         {
933           match_u32_assert(3);
934           printf("(special 3)");
935           match_byte_assert(0);
936           match_byte_assert(0);
937           match_byte_assert(1);
938           match_byte_assert(0);
939           int subn = get_u32 ();
940           printf ("nested %d bytes, ", subn);
941           pos += subn;
942           subn = get_u32 ();
943           printf ("nested %d bytes, ", subn);
944           pos += subn;
945         }
946     }
947   else
948     match_byte_assert (0x58);
949 }
950
951 static void
952 dump_footnote_value(int level)
953 {
954   for (int i = 0; i <= level; i++)
955     printf ("    ");
956
957   match_byte (0);
958   match_byte (0);
959   match_byte (0);
960   match_byte (0);
961   if (match_byte (3))
962     {
963       get_string();
964       dump_footnote_value_31();
965       get_string();
966       printf("string \"%s\"", get_string());
967       if (!match_byte (0))
968         match_byte_assert (1);
969     }
970   else if (match_byte (5))
971     {
972       match_byte_assert (0x58);
973       printf ("variable \"%s\"", get_string());
974       get_string();
975       if (!match_byte(1) && !match_byte(2))
976         match_byte_assert(3);
977     }
978   else if (match_byte (2))
979     {
980       unsigned int format;
981       char *var, *vallab;
982       double value;
983
984       match_byte_assert (0x58);
985       format = get_u32 ();
986       value = get_double ();
987       var = get_string ();
988       vallab = get_string ();
989       printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
990               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
991       if (!match_byte (1) && !match_byte(2))
992         match_byte_assert (3);
993       match_byte (0);
994       match_byte (0);
995       match_byte (0);
996       match_byte (0);
997       match_byte (0);
998       match_byte (0);
999       match_byte (0);
1000     }
1001   else if (match_byte (4))
1002     {
1003       unsigned int format;
1004       char *var, *vallab, *value;
1005
1006       match_byte_assert (0x58);
1007       format = get_u32 ();
1008       vallab = get_string ();
1009       var = get_string ();
1010       if (!match_byte(1) && !match_byte(2))
1011         match_byte_assert (3);
1012       value = get_string ();
1013       printf ("value \"%s\" format %d(%d.%d) var \"%s\" vallab \"%s\"",
1014               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
1015       match_byte (0);
1016       match_byte (0);
1017       match_byte (0);
1018       match_byte (0);
1019     }
1020   else if (match_byte (1))
1021     {
1022       unsigned int format;
1023       double value;
1024
1025       dump_footnote_value_31();
1026       format = get_u32 ();
1027       value = get_double ();
1028       printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
1029     }
1030   else
1031     {
1032       dump_footnote_value_31();
1033       char *base = get_string();
1034       int x = get_u32();
1035       printf ("\"%s\"; %d variables:\n", base, x);
1036       for (int i = 0; i < x; i++)
1037         {
1038           int y = get_u32();
1039           if (!y)
1040             y = 1;
1041           else
1042             match_u32_assert(0);
1043           for (int j = 0; j <= level; j++)
1044             printf ("    ");
1045           printf("variable %d has %d values:\n", i, y);
1046           for (int j = 0; j < y; j++)
1047             {
1048               if (match_byte(3))
1049                 {
1050                   char *a = get_string();
1051                   match_byte_assert(0x58);
1052                   char *b = get_string();
1053                   char *c = get_string();
1054                   for (int k = 0; k <= level + 1; k++)
1055                     printf ("    ");
1056                   printf ("\"%s\", \"%s\", \"%s\"", a, b, c);
1057                   if (!match_byte(1))
1058                     match_byte_assert(0);
1059                 }
1060               else
1061                 dump_footnote_value (level+1);
1062               putchar('\n');
1063             }
1064         }
1065     }
1066 }
1067
1068 static void
1069 dump_title(void)
1070 {
1071   pos = 0x27;
1072   dump_title_value(0); putchar('\n');
1073   dump_title_value(0); putchar('\n');
1074   match_byte_assert(0x31);
1075   dump_title_value(0); putchar('\n');
1076   match_byte(0);
1077   match_byte_assert(0x58);
1078   if (match_byte(0x31))
1079     {
1080       dump_footnote_value(0); putchar('\n');
1081     }
1082   else
1083     match_byte_assert(0x58);
1084
1085
1086   int n_footnotes = get_u32();
1087   if (n_footnotes >= 20)
1088     {
1089       fprintf(stderr, "%08x: %d footnotes\n", pos - 4, n_footnotes);
1090       exit(1);
1091     }
1092
1093   printf("------\n%d footnotes\n", n_footnotes);
1094   if (n_footnotes < 20)
1095     {
1096       for (int i = 0; i < n_footnotes; i++)
1097         {
1098           printf("footnote %d:\n", i);
1099           dump_footnote_value(0);
1100           match_byte(0);
1101           match_byte(0);
1102           match_byte(0);
1103           match_byte(0);
1104           if (match_byte (1))
1105             {
1106               unsigned int format;
1107               double value;
1108
1109               if (match_byte (0x31))
1110                 {
1111                   if (match_u32 (1))
1112                     {
1113                       printf("(footnote %d) ", get_u32());
1114                       match_byte_assert (0);
1115                       match_byte_assert (0);
1116                       int subn = get_u32 ();
1117                       printf ("nested %d bytes", subn);
1118                       pos += subn;
1119                     }
1120                 }
1121               else
1122                 match_byte_assert (0x58);
1123               format = get_u32 ();
1124               value = get_double ();
1125               printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
1126               match_byte (1);
1127               match_byte (0);
1128               match_byte (0);
1129               match_byte (0);
1130               match_byte (1);
1131             }
1132           else if (match_byte (0x31))
1133             {
1134               match_byte_assert(3);
1135               get_string();
1136               match_byte_assert(0x58);
1137               match_u32_assert(0);
1138               get_string();
1139               match_byte(0);
1140             }
1141           else
1142             match_byte_assert (0x58);
1143           printf("(%d)\n", get_u32());
1144         }
1145     }
1146 }
1147
1148 static int
1149 find_dimensions(void)
1150 {
1151   {
1152     const char dimensions[] = "-,,,.\0";
1153     int x = try_find_tail(dimensions, sizeof dimensions - 1);
1154     if (x)
1155       return x;
1156   }
1157
1158   const char dimensions[] = "-,,, .\0";
1159   return find_tail(dimensions, sizeof dimensions - 1);
1160 }
1161
1162 static void
1163 dump_fonts(void)
1164 {
1165   printf("fonts: offset=%08x\n", pos);
1166   match_byte(0);
1167   for (int i = 1; i <= 8; i++)
1168     {
1169       printf("%08x: font %d, ", pos, i);
1170       match_byte_assert(i);
1171       match_byte_assert(0x31);
1172       printf("%s, ", get_string());
1173       match_byte_assert(0);
1174       match_byte_assert(0);
1175       if (!match_byte(0x40) && !match_byte(0x20) && !match_byte(0x80) && !match_byte(0x10))
1176         match_byte_assert(0x50);
1177       if (!match_byte(0x41))
1178         match_byte_assert(0x51);
1179       pos += 13;
1180       printf ("%s, ", get_string());
1181       printf ("%s, ", get_string());
1182       match_u32_assert(0);
1183       match_u32_assert(0);
1184       pos++;
1185       get_u32();
1186       get_u32();
1187       get_u32();
1188       get_u32();
1189       putchar('\n');
1190     }
1191
1192   match_u32_assert(240);
1193   pos += 240;
1194
1195   match_u32_assert(18);
1196   pos += 18;
1197
1198   if (match_u32(117))
1199     pos += 117;
1200   else
1201     {
1202       match_u32_assert(142);
1203       pos += 142;
1204     }
1205
1206   int count = get_u32();
1207   pos += 4 * count;
1208
1209   char *encoding = get_string();
1210   printf("encoding=%s\n", encoding);
1211
1212   if (!match_u32(0))
1213     match_u32_assert(UINT32_MAX);
1214   if (!match_byte(0))
1215     match_byte_assert(1);
1216   match_byte_assert(0);
1217   if (!match_byte(0))
1218     match_byte_assert(1);
1219   if (!match_byte(0x99) && !match_byte(0x98))
1220     match_byte_assert(0x97);
1221   match_byte_assert(7);
1222   match_byte_assert(0);
1223   match_byte_assert(0);
1224   if (match_byte('.'))
1225     match_byte_assert(',');
1226   else
1227     {
1228       match_byte_assert(',');
1229       if (!match_byte('.'))
1230         match_byte_assert(' ');
1231     }
1232   match_u32_assert(5);
1233   for (int i = 0; i < 5; i++)
1234     get_string();
1235   pos += get_u32();
1236   if (pos != find_dimensions())
1237     fprintf (stderr, "%08x / %08x\n", pos, find_dimensions());
1238 }
1239
1240 int
1241 main(int argc, char *argv[])
1242 {
1243   size_t start;
1244   struct stat s;
1245
1246   if (isatty(STDIN_FILENO))
1247     {
1248       fprintf(stderr, "redirect stdin from a .bin file\n");
1249       exit(1);
1250     }
1251   if (fstat(STDIN_FILENO, &s))
1252     {
1253       perror("fstat");
1254       exit(1);
1255     }
1256   n = s.st_size;
1257   data = malloc(n);
1258   if (!data)
1259     {
1260       perror("malloc");
1261       exit(1);
1262     }
1263   if (read(STDIN_FILENO, data, n) != n)
1264     {
1265       perror("read");
1266       exit(1);
1267     }
1268
1269   if (argc > 1)
1270     {
1271       if (!strcmp(argv[1], "title0"))
1272         {
1273           pos = 0x27;
1274           if (match_byte (0x03)
1275               || (match_byte (0x05) && match_byte (0x58)))
1276             printf ("%s\n", get_string());
1277           else
1278             printf ("<unknown>\n");
1279           return 0;
1280         }
1281       else if (!strcmp(argv[1], "title"))
1282         {
1283           dump_title();
1284           exit(0);
1285         }
1286       else if (!strcmp(argv[1], "titleraw"))
1287         {
1288           const char fonts[] = "\x01\x31\x09\0\0\0SansSerif";
1289           start = 0x27;
1290           n = find(fonts, sizeof fonts - 1);
1291         }
1292       else if (!strcmp(argv[1], "fonts"))
1293         {
1294           const char fonts[] = "\x01\x31\x09\0\0\0SansSerif";
1295           const char styles[] = "\xf0\0\0\0";
1296           start = find(fonts, sizeof fonts - 1);
1297           n = find(styles, sizeof styles - 1);
1298         }
1299       else if (!strcmp(argv[1], "styles"))
1300         {
1301           const char styles[] = "\xf0\0\0\0";
1302           const char dimensions[] = "-,,,.\0";
1303           start = find(styles, sizeof styles - 1);
1304           n = find(dimensions, sizeof dimensions - 1) + sizeof dimensions - 1;
1305         }
1306       else if (!strcmp(argv[1], "dimensions") || !strcmp(argv[1], "all"))
1307         {
1308           pos = 0;
1309           match_byte_assert(1);
1310           match_byte_assert(0);
1311           match_u32_assert(3);
1312           match_byte_assert(1);
1313           if (!match_byte(0))
1314             match_byte_assert(1);
1315           match_byte_assert(0);
1316           match_byte_assert(0);
1317           if (!match_byte(0))
1318             match_byte_assert(1);
1319           pos++;
1320           match_byte_assert(0);
1321           match_byte_assert(0);
1322           match_byte_assert(0);
1323           dump_title ();
1324           dump_fonts();
1325           dump_dims ();
1326           printf("\n\ndata:\n");
1327           dump_data ();
1328           if (pos == n - 1)
1329             match_byte_assert (1);
1330           if (pos != n)
1331             {
1332               fprintf (stderr, "%x / %x\n", pos, n);
1333               exit(1);
1334             }
1335           exit(0);
1336         }
1337       else
1338         {
1339           fprintf (stderr, "unknown section %s\n", argv[1]);
1340           exit(1);
1341         }
1342     }
1343   else
1344     start = 0x27;
1345
1346   for (size_t i = start; i < n; )
1347     {
1348       if (i + 5 <= n
1349           && data[i]
1350           //&& !data[i + 1]
1351           && !data[i + 2]
1352           && !data[i + 3]
1353           && i + 4 + data[i] + data[i + 1] * 256 <= n
1354           && all_ascii(&data[i + 4], data[i] + data[i + 1] * 256))
1355         {
1356           fputs("\n\"", stdout);
1357           fwrite(&data[i + 4], 1, data[i] + data[i + 1] * 256, stdout);
1358           fputs("\" ", stdout);
1359
1360           i += 4 + data[i] + data[i + 1] * 256;
1361         }
1362       else if (i + 12 <= n
1363                && data[i + 1] == 40
1364                && data[i + 2] == 5
1365                && data[i + 3] == 0)
1366         {
1367           double d;
1368
1369           memcpy (&d, &data[i + 4], 8);
1370           printf ("F40.%d(%.*f)\n", data[i], data[i], d);
1371           i += 12;
1372         }
1373       else if (i + 12 <= n
1374                && data[i + 1] == 40
1375                && data[i + 2] == 31
1376                && data[i + 3] == 0)
1377         {
1378           double d;
1379
1380           memcpy (&d, &data[i + 4], 8);
1381           printf ("PCT40.%d(%.*f)\n", data[i], data[i], d);
1382           i += 12;
1383         }
1384       else if (i + 4 <= n
1385                && (data[i] && data[i] != 88 && data[i] != 0x41)
1386                && !data[i + 1]
1387                && !data[i + 2]
1388                && !data[i + 3])
1389         {
1390           printf ("i%d ", data[i]);
1391           i += 4;
1392         }
1393       else
1394         {
1395           printf("%02x ", data[i]);
1396           i++;
1397         }
1398     }
1399
1400   return 0;
1401 }