Factor out and generalize dump_data_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(int level)
899 {
900   for (int i = 0; i <= level; i++)
901     printf ("    ");
902
903   match_byte (0);
904   match_byte (0);
905   match_byte (0);
906   match_byte (0);
907   if (match_byte (3))
908     {
909       get_string();
910       if (match_byte (0x31))
911         {
912           if (match_u32 (1))
913             {
914               printf("(footnote %d) ", get_u32());
915               match_byte_assert (0);
916               match_byte_assert (0);
917               int subn = get_u32 ();
918               printf ("nested %d bytes", subn);
919               pos += subn;
920             }
921           else if (match_u32 (2))
922             {
923               printf("(special 2)");
924               match_byte_assert(0);
925               match_byte_assert(0);
926               match_u32_assert(1);
927               match_byte_assert(0);
928               match_byte_assert(0);
929               int subn = get_u32 ();
930               printf ("nested %d bytes", subn);
931               pos += subn;
932             }
933           else
934             {
935               match_u32_assert(3);
936               printf("(special 3)");
937               match_byte_assert(0);
938               match_byte_assert(0);
939               match_byte_assert(1);
940               match_byte_assert(0);
941               int subn = get_u32 ();
942               printf ("nested %d bytes, ", subn);
943               pos += subn;
944               subn = get_u32 ();
945               printf ("nested %d bytes, ", subn);
946               pos += subn;
947             }
948         }
949       else
950         match_byte_assert (0x58);
951       get_string();
952       printf("string \"%s\"", get_string());
953       if (!match_byte (0))
954         match_byte_assert (1);
955     }
956   else if (match_byte (5))
957     {
958       match_byte_assert (0x58);
959       printf ("variable \"%s\"", get_string());
960       get_string();
961       if (!match_byte(1) && !match_byte(2))
962         match_byte_assert(3);
963     }
964   else if (match_byte (2))
965     {
966       unsigned int format;
967       char *var, *vallab;
968       double value;
969
970       match_byte_assert (0x58);
971       format = get_u32 ();
972       value = get_double ();
973       var = get_string ();
974       vallab = get_string ();
975       printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
976               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
977       if (!match_byte (1) && !match_byte(2))
978         match_byte_assert (3);
979       match_byte (0);
980       match_byte (0);
981       match_byte (0);
982       match_byte (0);
983       match_byte (0);
984       match_byte (0);
985       match_byte (0);
986     }
987   else if (match_byte (4))
988     {
989       unsigned int format;
990       char *var, *vallab, *value;
991
992       match_byte_assert (0x58);
993       format = get_u32 ();
994       vallab = get_string ();
995       var = get_string ();
996       if (!match_byte(1) && !match_byte(2))
997         match_byte_assert (3);
998       value = get_string ();
999       printf ("value \"%s\" format %d(%d.%d) var \"%s\" vallab \"%s\"",
1000               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
1001       match_byte (0);
1002       match_byte (0);
1003       match_byte (0);
1004       match_byte (0);
1005     }
1006   else if (match_byte (1))
1007     {
1008       unsigned int format;
1009       double value;
1010
1011       if (match_byte (0x31))
1012         {
1013           if (match_u32 (1))
1014             {
1015               printf("(footnote %d) ", get_u32());
1016               match_byte_assert (0);
1017               match_byte_assert (0);
1018               int subn = get_u32 ();
1019               printf ("nested %d bytes", subn);
1020               pos += subn;
1021             }
1022         }
1023       else
1024         match_byte_assert (0x58);
1025       format = get_u32 ();
1026       value = get_double ();
1027       printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
1028     }
1029   else if (match_byte (0x31))
1030     {
1031       if (match_u32 (1))
1032         {
1033           printf("(footnote %d) ", get_u32());
1034           match_byte_assert (0);
1035           match_byte_assert (0);
1036           int subn = get_u32 ();
1037           printf ("nested %d bytes", subn);
1038           pos += subn;
1039         }
1040       else
1041         {
1042           match_u32_assert (0);
1043           match_u32_assert (0);
1044           int subn = get_u32 ();
1045           printf ("nested %d bytes", subn);
1046           pos += subn;
1047         }
1048       char *base = get_string();
1049       int x = get_u32();
1050       printf ("\"%s\"; %d variables:\n", base, x);
1051       for (int i = 0; i < x; i++)
1052         {
1053           int y = get_u32();
1054           if (!y)
1055             y = 1;
1056           else
1057             match_u32_assert(0);
1058           for (int j = 0; j <= level; j++)
1059             printf ("    ");
1060           printf("variable %d has %d values:\n", i, y);
1061           for (int j = 0; j < y; j++)
1062             {
1063               if (match_byte(3))
1064                 {
1065                   char *a = get_string();
1066                   match_byte_assert(0x58);
1067                   char *b = get_string();
1068                   char *c = get_string();
1069                   for (int k = 0; k <= level + 1; k++)
1070                     printf ("    ");
1071                   printf ("\"%s\", \"%s\", \"%s\"", a, b, c);
1072                   if (!match_byte(1))
1073                     match_byte_assert(0);
1074                 }
1075               else
1076                 dump_footnote_value (level+1);
1077               putchar('\n');
1078             }
1079         }
1080     }
1081   else
1082     {
1083
1084       match_byte_assert (0x58);
1085       char *base = get_string();
1086       int x = get_u32();
1087       printf ("\"%s\" with %d variables:\n", base, x);
1088       for (int i = 0; i < x; i++)
1089         {
1090           int y = get_u32();
1091           if (!y)
1092             y = 1;
1093           else
1094             match_u32_assert(0);
1095           for (int j = 0; j <= level; j++)
1096             printf ("    ");
1097           printf("variable %d has %d values:\n", i, y);
1098           for (int j = 0; j < y; j++)
1099             {
1100               if (match_byte(3))
1101                 {
1102                   char *a = get_string();
1103                   match_byte_assert(0x58);
1104                   char *b = get_string();
1105                   char *c = get_string();
1106                   for (int k = 0; k <= level + 1; k++)
1107                     printf ("    ");
1108                   printf ("\"%s\", \"%s\", \"%s\"", a, b, c);
1109                   match_byte_assert(0);
1110                 }
1111               else
1112                 dump_footnote_value (level+1);
1113               putchar('\n');
1114             }
1115         }
1116     }
1117 }
1118
1119 static void
1120 dump_title(void)
1121 {
1122   pos = 0x27;
1123   dump_title_value(0); putchar('\n');
1124   dump_title_value(0); putchar('\n');
1125   match_byte_assert(0x31);
1126   dump_title_value(0); putchar('\n');
1127   match_byte(0);
1128   match_byte_assert(0x58);
1129   if (match_byte(0x31))
1130     {
1131       dump_footnote_value(0); putchar('\n');
1132     }
1133   else
1134     match_byte_assert(0x58);
1135
1136
1137   int n_footnotes = get_u32();
1138   if (n_footnotes >= 20)
1139     {
1140       fprintf(stderr, "%08x: %d footnotes\n", pos - 4, n_footnotes);
1141       exit(1);
1142     }
1143
1144   printf("------\n%d footnotes\n", n_footnotes);
1145   if (n_footnotes < 20)
1146     {
1147       for (int i = 0; i < n_footnotes; i++)
1148         {
1149           printf("footnote %d:\n", i);
1150           dump_footnote_value(0);
1151           match_byte(0);
1152           match_byte(0);
1153           match_byte(0);
1154           match_byte(0);
1155           if (match_byte (1))
1156             {
1157               unsigned int format;
1158               double value;
1159
1160               if (match_byte (0x31))
1161                 {
1162                   if (match_u32 (1))
1163                     {
1164                       printf("(footnote %d) ", get_u32());
1165                       match_byte_assert (0);
1166                       match_byte_assert (0);
1167                       int subn = get_u32 ();
1168                       printf ("nested %d bytes", subn);
1169                       pos += subn;
1170                     }
1171                 }
1172               else
1173                 match_byte_assert (0x58);
1174               format = get_u32 ();
1175               value = get_double ();
1176               printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
1177               match_byte (1);
1178               match_byte (0);
1179               match_byte (0);
1180               match_byte (0);
1181               match_byte (1);
1182             }
1183           else if (match_byte (0x31))
1184             {
1185               match_byte_assert(3);
1186               get_string();
1187               match_byte_assert(0x58);
1188               match_u32_assert(0);
1189               get_string();
1190               match_byte(0);
1191             }
1192           else
1193             match_byte_assert (0x58);
1194           printf("(%d)\n", get_u32());
1195         }
1196     }
1197 }
1198
1199 static int
1200 find_dimensions(void)
1201 {
1202   {
1203     const char dimensions[] = "-,,,.\0";
1204     int x = try_find_tail(dimensions, sizeof dimensions - 1);
1205     if (x)
1206       return x;
1207   }
1208
1209   const char dimensions[] = "-,,, .\0";
1210   return find_tail(dimensions, sizeof dimensions - 1);
1211 }
1212
1213 static void
1214 dump_fonts(void)
1215 {
1216   printf("fonts: offset=%08x\n", pos);
1217   match_byte(0);
1218   for (int i = 1; i <= 8; i++)
1219     {
1220       printf("%08x: font %d, ", pos, i);
1221       match_byte_assert(i);
1222       match_byte_assert(0x31);
1223       printf("%s, ", get_string());
1224       match_byte_assert(0);
1225       match_byte_assert(0);
1226       if (!match_byte(0x40) && !match_byte(0x20) && !match_byte(0x80) && !match_byte(0x10))
1227         match_byte_assert(0x50);
1228       if (!match_byte(0x41))
1229         match_byte_assert(0x51);
1230       pos += 13;
1231       printf ("%s, ", get_string());
1232       printf ("%s, ", get_string());
1233       match_u32_assert(0);
1234       match_u32_assert(0);
1235       pos++;
1236       get_u32();
1237       get_u32();
1238       get_u32();
1239       get_u32();
1240       putchar('\n');
1241     }
1242
1243   match_u32_assert(240);
1244   pos += 240;
1245
1246   match_u32_assert(18);
1247   pos += 18;
1248
1249   if (match_u32(117))
1250     pos += 117;
1251   else
1252     {
1253       match_u32_assert(142);
1254       pos += 142;
1255     }
1256
1257   int count = get_u32();
1258   pos += 4 * count;
1259
1260   char *encoding = get_string();
1261   printf("encoding=%s\n", encoding);
1262
1263   if (!match_u32(0))
1264     match_u32_assert(UINT32_MAX);
1265   if (!match_byte(0))
1266     match_byte_assert(1);
1267   match_byte_assert(0);
1268   if (!match_byte(0))
1269     match_byte_assert(1);
1270   if (!match_byte(0x99) && !match_byte(0x98))
1271     match_byte_assert(0x97);
1272   match_byte_assert(7);
1273   match_byte_assert(0);
1274   match_byte_assert(0);
1275   if (match_byte('.'))
1276     match_byte_assert(',');
1277   else
1278     {
1279       match_byte_assert(',');
1280       if (!match_byte('.'))
1281         match_byte_assert(' ');
1282     }
1283   match_u32_assert(5);
1284   for (int i = 0; i < 5; i++)
1285     get_string();
1286   pos += get_u32();
1287   if (pos != find_dimensions())
1288     fprintf (stderr, "%08x / %08x\n", pos, find_dimensions());
1289 }
1290
1291 int
1292 main(int argc, char *argv[])
1293 {
1294   size_t start;
1295   struct stat s;
1296
1297   if (isatty(STDIN_FILENO))
1298     {
1299       fprintf(stderr, "redirect stdin from a .bin file\n");
1300       exit(1);
1301     }
1302   if (fstat(STDIN_FILENO, &s))
1303     {
1304       perror("fstat");
1305       exit(1);
1306     }
1307   n = s.st_size;
1308   data = malloc(n);
1309   if (!data)
1310     {
1311       perror("malloc");
1312       exit(1);
1313     }
1314   if (read(STDIN_FILENO, data, n) != n)
1315     {
1316       perror("read");
1317       exit(1);
1318     }
1319
1320   if (argc > 1)
1321     {
1322       if (!strcmp(argv[1], "title0"))
1323         {
1324           pos = 0x27;
1325           if (match_byte (0x03)
1326               || (match_byte (0x05) && match_byte (0x58)))
1327             printf ("%s\n", get_string());
1328           else
1329             printf ("<unknown>\n");
1330           return 0;
1331         }
1332       else if (!strcmp(argv[1], "title"))
1333         {
1334           dump_title();
1335           exit(0);
1336         }
1337       else if (!strcmp(argv[1], "titleraw"))
1338         {
1339           const char fonts[] = "\x01\x31\x09\0\0\0SansSerif";
1340           start = 0x27;
1341           n = find(fonts, sizeof fonts - 1);
1342         }
1343       else if (!strcmp(argv[1], "fonts"))
1344         {
1345           const char fonts[] = "\x01\x31\x09\0\0\0SansSerif";
1346           const char styles[] = "\xf0\0\0\0";
1347           start = find(fonts, sizeof fonts - 1);
1348           n = find(styles, sizeof styles - 1);
1349         }
1350       else if (!strcmp(argv[1], "styles"))
1351         {
1352           const char styles[] = "\xf0\0\0\0";
1353           const char dimensions[] = "-,,,.\0";
1354           start = find(styles, sizeof styles - 1);
1355           n = find(dimensions, sizeof dimensions - 1) + sizeof dimensions - 1;
1356         }
1357       else if (!strcmp(argv[1], "dimensions") || !strcmp(argv[1], "all"))
1358         {
1359           pos = 0;
1360           match_byte_assert(1);
1361           match_byte_assert(0);
1362           match_u32_assert(3);
1363           match_byte_assert(1);
1364           if (!match_byte(0))
1365             match_byte_assert(1);
1366           match_byte_assert(0);
1367           match_byte_assert(0);
1368           if (!match_byte(0))
1369             match_byte_assert(1);
1370           pos++;
1371           match_byte_assert(0);
1372           match_byte_assert(0);
1373           match_byte_assert(0);
1374           dump_title ();
1375           dump_fonts();
1376           dump_dims ();
1377           printf("\n\ndata:\n");
1378           dump_data ();
1379           if (pos == n - 1)
1380             match_byte_assert (1);
1381           if (pos != n)
1382             {
1383               fprintf (stderr, "%x / %x\n", pos, n);
1384               exit(1);
1385             }
1386           exit(0);
1387         }
1388       else
1389         {
1390           fprintf (stderr, "unknown section %s\n", argv[1]);
1391           exit(1);
1392         }
1393     }
1394   else
1395     start = 0x27;
1396
1397   for (size_t i = start; i < n; )
1398     {
1399       if (i + 5 <= n
1400           && data[i]
1401           //&& !data[i + 1]
1402           && !data[i + 2]
1403           && !data[i + 3]
1404           && i + 4 + data[i] + data[i + 1] * 256 <= n
1405           && all_ascii(&data[i + 4], data[i] + data[i + 1] * 256))
1406         {
1407           fputs("\n\"", stdout);
1408           fwrite(&data[i + 4], 1, data[i] + data[i + 1] * 256, stdout);
1409           fputs("\" ", stdout);
1410
1411           i += 4 + data[i] + data[i + 1] * 256;
1412         }
1413       else if (i + 12 <= n
1414                && data[i + 1] == 40
1415                && data[i + 2] == 5
1416                && data[i + 3] == 0)
1417         {
1418           double d;
1419
1420           memcpy (&d, &data[i + 4], 8);
1421           printf ("F40.%d(%.*f)\n", data[i], data[i], d);
1422           i += 12;
1423         }
1424       else if (i + 12 <= n
1425                && data[i + 1] == 40
1426                && data[i + 2] == 31
1427                && data[i + 3] == 0)
1428         {
1429           double d;
1430
1431           memcpy (&d, &data[i + 4], 8);
1432           printf ("PCT40.%d(%.*f)\n", data[i], data[i], d);
1433           i += 12;
1434         }
1435       else if (i + 4 <= n
1436                && (data[i] && data[i] != 88 && data[i] != 0x41)
1437                && !data[i + 1]
1438                && !data[i + 2]
1439                && !data[i + 3])
1440         {
1441           printf ("i%d ", data[i]);
1442           i += 4;
1443         }
1444       else
1445         {
1446           printf("%02x ", data[i]);
1447           i++;
1448         }
1449     }
1450
1451   return 0;
1452 }