Generalize dump_dim().
[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(int level)
353 {
354   for (int i = 0; i <= level; i++)
355     printf ("    ");
356
357   if (match_byte (3))
358     {
359       get_string();
360       if (match_byte (0x31))
361         {
362           match_u32 (1);
363           printf("(footnote %d) ", get_u32());
364           match_byte_assert (0);
365           match_byte_assert (0);
366           int subn = get_u32 ();
367           printf ("nested %d bytes", subn);
368           pos += subn;
369         }
370       else
371         match_byte_assert (0x58);
372       get_string();
373       printf("string \"%s\"", get_string());
374       match_byte (0);
375       match_byte_assert (1);
376       match_byte (0);
377       match_byte (0);
378       match_byte (0);
379       match_byte (1);
380     }
381   else if (match_byte (5))
382     {
383       match_byte_assert (0x58);
384       printf ("variable \"%s\"", get_string());
385       get_string();
386       if (!match_byte (2))
387         match_byte_assert(3);
388     }
389   else if (match_byte (2))
390     {
391       unsigned int format;
392       char *var, *vallab;
393       double value;
394
395       match_byte_assert (0x58);
396       format = get_u32 ();
397       value = get_double ();
398       var = get_string ();
399       vallab = get_string ();
400       printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
401               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
402       if (!match_u32 (3))
403         match_u32_assert (2);
404       match_byte (0);
405     }
406   else if (match_byte (1))
407     {
408       unsigned int format;
409       double value;
410
411       match_byte_assert (0x58);
412       format = get_u32 ();
413       value = get_double ();
414       printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
415       match_byte (1);
416       match_byte (0);
417       match_byte (0);
418       match_byte (0);
419       match_byte (1);
420     }
421   else
422     {
423       int subn;
424
425       match_byte (0);
426       if (match_byte (0x31))
427         {
428           match_u32_assert (0);
429           match_u32_assert (0);
430           subn = get_u32 ();
431           printf ("nested %d bytes", subn);
432           pos += subn;
433         }
434       else
435         match_byte_assert(0x58);
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   if (match_byte(3))
501     {
502       get_string();
503       match_byte_assert(0x58);
504       get_string();
505       printf("string \"%s\": ", get_string());
506       match_byte(1) || match_byte(0);
507     }
508   else
509     dump_dim_value(0);
510
511   /* This byte is usually 0x02 but 0x00 and 0x75 (!) have also been spotted. */
512   pos++;
513
514   if (!match_byte(0) && !match_byte(1))
515     match_byte_assert(2);
516   if (!match_u32(0))
517     match_u32_assert(2);
518   if (!match_byte(0))
519     match_byte_assert(1);
520   match_byte(0);
521   match_byte(0);
522   match_byte(0);
523   match_byte(0);
524   get_u32();
525   match_byte(0);
526   match_byte(0);
527   match_byte(0);
528   match_byte(0);
529   n_categories = get_u32();
530   printf("%d nested categories\n", n_categories);
531   for (int i = 0; i < n_categories; i++)
532     dump_category (0);
533 }
534
535 int n_dims;
536 static void
537 dump_dims(void)
538 {
539   n_dims = get_u32();
540   printf ("%u dimensions\n", n_dims);
541   for (int i = 0; i < n_dims; i++)
542     {
543       printf("\n");
544       dump_dim ();
545     }
546 }
547
548 static void
549 dump_data_value_31(void)
550 {
551   if (match_byte (0x31))
552     {
553       if (match_u32 (0))
554         {
555           if (match_u32 (1))
556             get_string();
557           else
558             match_u32_assert (0);
559           int subn = get_u32 ();
560           printf ("nested %d bytes", subn);
561           pos += subn;
562         }
563       else if (match_u32 (1))
564         {
565           printf("(footnote %d) ", get_u32());
566           match_byte_assert (0);
567           match_byte_assert (0);
568           int subn = get_u32 ();
569           printf ("nested %d bytes", subn);
570           pos += subn;
571         }
572       else if (match_u32 (2))
573         {
574           printf("(special 2)");
575           match_byte_assert(0);
576           match_byte_assert(0);
577           match_u32_assert(1);
578           match_byte_assert(0);
579           match_byte_assert(0);
580           int subn = get_u32 ();
581           printf ("nested %d bytes", subn);
582           pos += subn;
583         }
584       else
585         {
586           match_u32_assert(3);
587           printf("(special 3)");
588           match_byte_assert(0);
589           match_byte_assert(0);
590           match_byte_assert(1);
591           match_byte_assert(0);
592           int subn = get_u32 ();
593           printf ("nested %d bytes, ", subn);
594           pos += subn;
595           subn = get_u32 ();
596           printf ("nested %d bytes, ", subn);
597           pos += subn;
598         }
599     }
600   else
601     match_byte_assert (0x58);
602 }
603
604 static void
605 dump_data(void)
606 {
607 #if 1
608   int a[16];
609   for (int i = 0; i < 3 + n_dims; i++)
610     a[i] = get_u32();
611   printf ("data intro:");
612   for (int i = 0; i < 3 + n_dims; i++)
613     printf(" %d", a[i]);
614   printf("\n");
615 #else
616   fprintf (stderr,"data intro (%d dims):", n_dims);
617   for (int i = 0; i < 3+n_dims; i++)
618     fprintf (stderr," %d", get_u32());
619   fprintf(stderr,"\n");
620 #endif
621   int x = get_u32();
622   printf ("%d data values, starting at %08x\n", x, pos);
623   for (int i = 0; i < x; i++)
624     {
625       printf("%08x, index %d:\n", pos, get_u32());
626       match_u32_assert(0);
627       match_byte(0);
628       match_byte(0);
629       match_byte(0);
630       match_byte(0);
631       if (match_byte (1))
632         {
633           unsigned int format;
634           double value;
635
636           dump_data_value_31();
637           format = get_u32 ();
638           value = get_double ();
639           printf ("    value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
640         }
641       else if (match_byte (3))
642         {
643           get_string();
644           dump_data_value_31();
645           get_string();
646           printf("string \"%s\"", get_string());
647           match_byte (0);
648         }
649       else if (match_byte (2))
650         {
651           unsigned int format;
652           char *var, *vallab;
653           double value;
654
655           match_byte_assert (0x58);
656           format = get_u32 ();
657           value = get_double ();
658           var = get_string ();
659           vallab = get_string ();
660           printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
661                   value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
662           if (!match_byte (1) && !match_byte(2))
663             match_byte_assert (3);
664         }
665       else if (match_byte (4))
666         {
667           unsigned int format;
668           char *var, *vallab, *value;
669
670           match_byte_assert (0x58);
671           format = get_u32 ();
672           vallab = get_string ();
673           var = get_string ();
674           if (!match_byte(1) && !match_byte(2))
675             match_byte_assert (3);
676           value = get_string ();
677           printf ("value \"%s\" format %d(%d.%d) var \"%s\" vallab \"%s\"",
678                   value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
679         }
680       else if (match_byte (5))
681         {
682           match_byte_assert (0x58);
683           printf ("variable \"%s\"", get_string());
684           get_string();
685           if (!match_byte(1) && !match_byte(2))
686             match_byte_assert(3);
687           match_byte (0);
688           match_byte (0);
689           match_byte (0);
690           match_byte (0);
691         }
692       else
693         {
694           dump_data_value_31();
695           char *base = get_string();
696           int x = get_u32();
697           printf ("\"%s\"; %d variables:\n", base, x);
698           for (int i = 0; i < x; i++)
699             {
700               int y = get_u32();
701               if (!y)
702                 y = 1;
703               else
704                 match_u32_assert(0);
705               for (int j = 0; j <= 0; j++)
706                 printf ("    ");
707               printf("variable %d has %d values:\n", i, y);
708               for (int j = 0; j < y; j++)
709                 {
710                   if (match_byte (1))
711                     {
712                       unsigned int format;
713                       double value;
714
715                       dump_data_value_31();
716                       format = get_u32 ();
717                       value = get_double ();
718                       printf ("    value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
719                     }
720                   else if (match_byte(3))
721                     {
722                       char *a = get_string();
723                       match_byte_assert(0x58);
724                       char *b = get_string();
725                       char *c = get_string();
726                       for (int k = 0; k <= 1; k++)
727                         printf ("    ");
728                       printf ("\"%s\", \"%s\", \"%s\"", a, b, c);
729                       match_byte(0);
730                       match_byte(0);
731                       match_byte(0);
732                       match_byte(0);
733                     }
734                   else if (match_byte(5))
735                     {
736                       match_byte_assert (0x58);
737                       printf ("variable \"%s\"", get_string());
738                       get_string();
739                       if (!match_byte(1) && !match_byte(2))
740                         match_byte_assert(3);
741                       match_byte (0);
742                       match_byte (0);
743                       match_byte (0);
744                       match_byte (0);
745                     }
746                   else
747                     dump_value (0);
748                   putchar('\n');
749                 }
750             }
751         }
752       putchar('\n');
753     }
754 }
755
756 static void
757 dump_title_value_31(int level)
758 {
759   if (match_byte (0x31))
760     {
761       if (match_u32 (0))
762         {
763           match_u32_assert (0);
764           int subn = get_u32 ();
765           printf ("nested %d bytes", subn);
766           pos += subn;
767         }
768       else if (match_u32 (1))
769         {
770           printf("(footnote %d) ", get_u32());
771           match_byte_assert (0);
772           match_byte_assert (0);
773           int subn = get_u32 ();
774           printf ("nested %d bytes", subn);
775           pos += subn;
776         }
777       else if (match_u32 (2))
778         {
779           printf("(special 2)");
780           match_byte_assert(0);
781           match_byte_assert(0);
782           if (!match_u32(2))
783             match_u32_assert(1);
784           match_byte_assert(0);
785           match_byte_assert(0);
786           int subn = get_u32 ();
787           printf ("nested %d bytes", subn);
788           pos += subn;
789         }
790       else
791         {
792           match_u32_assert(3);
793           printf("(special 3)");
794           match_byte_assert(0);
795           match_byte_assert(0);
796           match_byte_assert(1);
797           match_byte_assert(0);
798           int subn = get_u32 ();
799           printf ("nested %d bytes, ", subn);
800           pos += subn;
801           subn = get_u32 ();
802           printf ("nested %d bytes, ", subn);
803           pos += subn;
804         }
805     }
806   else
807     match_byte_assert (0x58);
808 }
809
810 static void
811 dump_title_value(int level)
812 {
813   for (int i = 0; i <= level; i++)
814     printf ("    ");
815
816   match_byte (0);
817   match_byte (0);
818   match_byte (0);
819   match_byte (0);
820   match_byte (0);
821   if (match_byte (3))
822     {
823       get_string();
824       dump_title_value_31(level);
825       get_string();
826       printf("string \"%s\"", get_string());
827       match_byte (0);
828       match_byte (0);
829       match_byte (0);
830       match_byte (1);
831       match_byte (1);
832       match_byte (0);
833       match_byte (0);
834       match_byte (0);
835       match_byte (1);
836     }
837   else if (match_byte (5))
838     {
839       dump_title_value_31(level);
840       printf ("variable \"%s\"", get_string());
841       get_string();
842       if (!match_byte(1) && !match_byte(2))
843         match_byte_assert(3);
844     }
845   else if (match_byte (2))
846     {
847       unsigned int format;
848       char *var, *vallab;
849       double value;
850
851       match_byte_assert (0x58);
852       format = get_u32 ();
853       value = get_double ();
854       var = get_string ();
855       vallab = get_string ();
856       printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
857               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
858       if (!match_byte (1) && !match_byte(2))
859         match_byte_assert (3);
860       match_byte (0);
861       match_byte (0);
862       match_byte (0);
863       match_byte (0);
864       match_byte (0);
865       match_byte (0);
866       match_byte (0);
867     }
868   else if (match_byte (4))
869     {
870       unsigned int format;
871       char *var, *vallab, *value;
872
873       match_byte_assert (0x58);
874       format = get_u32 ();
875       vallab = get_string ();
876       var = get_string ();
877       if (!match_byte(1) && !match_byte(2))
878         match_byte_assert (3);
879       value = get_string ();
880       printf ("value \"%s\" format %d(%d.%d) var \"%s\" vallab \"%s\"",
881               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
882       match_byte (0);
883       match_byte (0);
884       match_byte (0);
885       match_byte (0);
886     }
887   else if (match_byte (1))
888     {
889       unsigned int format;
890       double value;
891
892       dump_title_value_31(level);
893       format = get_u32 ();
894       value = get_double ();
895       printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
896       match_byte (1);
897       match_byte (0);
898       match_byte (0);
899       match_byte (0);
900       match_byte (1);
901     }
902   else
903     {
904       dump_title_value_31(level);
905
906       char *base = get_string();
907       int x = get_u32();
908       printf ("\"%s\" with %d variables:\n", base, x);
909       for (int i = 0; i < x; i++)
910         {
911           int y = get_u32();
912           if (!y)
913             y = 1;
914           else
915             match_u32_assert(0);
916           for (int j = 0; j <= level; j++)
917             printf ("    ");
918           printf("variable %d has %d values:\n", i, y);
919           for (int j = 0; j < y; j++)
920             {
921               match_byte(0);
922               if (match_byte(3))
923                 {
924                   char *a = get_string();
925                   match_byte_assert(0x58);
926                   char *b = get_string();
927                   char *c = get_string();
928                   for (int k = 0; k <= level + 1; k++)
929                     printf ("    ");
930                   printf ("\"%s\", \"%s\", \"%s\"", a, b, c);
931                 }
932               else
933                 dump_title_value (level+1);
934               putchar('\n');
935             }
936         }
937     }
938 }
939
940 static void
941 dump_footnote_value(int level)
942 {
943   for (int i = 0; i <= level; i++)
944     printf ("    ");
945
946   match_byte (0);
947   match_byte (0);
948   match_byte (0);
949   match_byte (0);
950   if (match_byte (3))
951     {
952       get_string();
953       if (match_byte (0x31))
954         {
955           if (match_u32 (1))
956             {
957               printf("(footnote %d) ", get_u32());
958               match_byte_assert (0);
959               match_byte_assert (0);
960               int subn = get_u32 ();
961               printf ("nested %d bytes", subn);
962               pos += subn;
963             }
964           else if (match_u32 (2))
965             {
966               printf("(special 2)");
967               match_byte_assert(0);
968               match_byte_assert(0);
969               match_u32_assert(1);
970               match_byte_assert(0);
971               match_byte_assert(0);
972               int subn = get_u32 ();
973               printf ("nested %d bytes", subn);
974               pos += subn;
975             }
976           else
977             {
978               match_u32_assert(3);
979               printf("(special 3)");
980               match_byte_assert(0);
981               match_byte_assert(0);
982               match_byte_assert(1);
983               match_byte_assert(0);
984               int subn = get_u32 ();
985               printf ("nested %d bytes, ", subn);
986               pos += subn;
987               subn = get_u32 ();
988               printf ("nested %d bytes, ", subn);
989               pos += subn;
990             }
991         }
992       else
993         match_byte_assert (0x58);
994       get_string();
995       printf("string \"%s\"", get_string());
996       if (!match_byte (0))
997         match_byte_assert (1);
998     }
999   else if (match_byte (5))
1000     {
1001       match_byte_assert (0x58);
1002       printf ("variable \"%s\"", get_string());
1003       get_string();
1004       if (!match_byte(1) && !match_byte(2))
1005         match_byte_assert(3);
1006     }
1007   else if (match_byte (2))
1008     {
1009       unsigned int format;
1010       char *var, *vallab;
1011       double value;
1012
1013       match_byte_assert (0x58);
1014       format = get_u32 ();
1015       value = get_double ();
1016       var = get_string ();
1017       vallab = get_string ();
1018       printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
1019               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
1020       if (!match_byte (1) && !match_byte(2))
1021         match_byte_assert (3);
1022       match_byte (0);
1023       match_byte (0);
1024       match_byte (0);
1025       match_byte (0);
1026       match_byte (0);
1027       match_byte (0);
1028       match_byte (0);
1029     }
1030   else if (match_byte (4))
1031     {
1032       unsigned int format;
1033       char *var, *vallab, *value;
1034
1035       match_byte_assert (0x58);
1036       format = get_u32 ();
1037       vallab = get_string ();
1038       var = get_string ();
1039       if (!match_byte(1) && !match_byte(2))
1040         match_byte_assert (3);
1041       value = get_string ();
1042       printf ("value \"%s\" format %d(%d.%d) var \"%s\" vallab \"%s\"",
1043               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
1044       match_byte (0);
1045       match_byte (0);
1046       match_byte (0);
1047       match_byte (0);
1048     }
1049   else if (match_byte (1))
1050     {
1051       unsigned int format;
1052       double value;
1053
1054       if (match_byte (0x31))
1055         {
1056           if (match_u32 (1))
1057             {
1058               printf("(footnote %d) ", get_u32());
1059               match_byte_assert (0);
1060               match_byte_assert (0);
1061               int subn = get_u32 ();
1062               printf ("nested %d bytes", subn);
1063               pos += subn;
1064             }
1065         }
1066       else
1067         match_byte_assert (0x58);
1068       format = get_u32 ();
1069       value = get_double ();
1070       printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
1071     }
1072   else if (match_byte (0x31))
1073     {
1074       if (match_u32 (1))
1075         {
1076           printf("(footnote %d) ", get_u32());
1077           match_byte_assert (0);
1078           match_byte_assert (0);
1079           int subn = get_u32 ();
1080           printf ("nested %d bytes", subn);
1081           pos += subn;
1082         }
1083       else
1084         {
1085           match_u32_assert (0);
1086           match_u32_assert (0);
1087           int subn = get_u32 ();
1088           printf ("nested %d bytes", subn);
1089           pos += subn;
1090         }
1091       char *base = get_string();
1092       int x = get_u32();
1093       printf ("\"%s\"; %d variables:\n", base, x);
1094       for (int i = 0; i < x; i++)
1095         {
1096           int y = get_u32();
1097           if (!y)
1098             y = 1;
1099           else
1100             match_u32_assert(0);
1101           for (int j = 0; j <= level; j++)
1102             printf ("    ");
1103           printf("variable %d has %d values:\n", i, y);
1104           for (int j = 0; j < y; j++)
1105             {
1106               if (match_byte(3))
1107                 {
1108                   char *a = get_string();
1109                   match_byte_assert(0x58);
1110                   char *b = get_string();
1111                   char *c = get_string();
1112                   for (int k = 0; k <= level + 1; k++)
1113                     printf ("    ");
1114                   printf ("\"%s\", \"%s\", \"%s\"", a, b, c);
1115                   if (!match_byte(1))
1116                     match_byte_assert(0);
1117                 }
1118               else
1119                 dump_footnote_value (level+1);
1120               putchar('\n');
1121             }
1122         }
1123     }
1124   else
1125     {
1126
1127       match_byte_assert (0x58);
1128       char *base = get_string();
1129       int x = get_u32();
1130       printf ("\"%s\" with %d variables:\n", base, x);
1131       for (int i = 0; i < x; i++)
1132         {
1133           int y = get_u32();
1134           if (!y)
1135             y = 1;
1136           else
1137             match_u32_assert(0);
1138           for (int j = 0; j <= level; j++)
1139             printf ("    ");
1140           printf("variable %d has %d values:\n", i, y);
1141           for (int j = 0; j < y; j++)
1142             {
1143               if (match_byte(3))
1144                 {
1145                   char *a = get_string();
1146                   match_byte_assert(0x58);
1147                   char *b = get_string();
1148                   char *c = get_string();
1149                   for (int k = 0; k <= level + 1; k++)
1150                     printf ("    ");
1151                   printf ("\"%s\", \"%s\", \"%s\"", a, b, c);
1152                   match_byte_assert(0);
1153                 }
1154               else
1155                 dump_footnote_value (level+1);
1156               putchar('\n');
1157             }
1158         }
1159     }
1160 }
1161
1162 static void
1163 dump_title(void)
1164 {
1165   pos = 0x27;
1166   dump_title_value(0); putchar('\n');
1167   dump_title_value(0); putchar('\n');
1168   match_byte_assert(0x31);
1169   dump_title_value(0); putchar('\n');
1170   match_byte(0);
1171   match_byte_assert(0x58);
1172   if (match_byte(0x31))
1173     {
1174       dump_footnote_value(0); putchar('\n');
1175     }
1176   else
1177     match_byte_assert(0x58);
1178
1179
1180   int n_footnotes = get_u32();
1181   if (n_footnotes >= 20)
1182     {
1183       fprintf(stderr, "%08x: %d footnotes\n", pos - 4, n_footnotes);
1184       exit(1);
1185     }
1186
1187   printf("------\n%d footnotes\n", n_footnotes);
1188   if (n_footnotes < 20)
1189     {
1190       for (int i = 0; i < n_footnotes; i++)
1191         {
1192           printf("footnote %d:\n", i);
1193           dump_footnote_value(0);
1194           match_byte(0);
1195           match_byte(0);
1196           match_byte(0);
1197           match_byte(0);
1198           if (match_byte (1))
1199             {
1200               unsigned int format;
1201               double value;
1202
1203               if (match_byte (0x31))
1204                 {
1205                   if (match_u32 (1))
1206                     {
1207                       printf("(footnote %d) ", get_u32());
1208                       match_byte_assert (0);
1209                       match_byte_assert (0);
1210                       int subn = get_u32 ();
1211                       printf ("nested %d bytes", subn);
1212                       pos += subn;
1213                     }
1214                 }
1215               else
1216                 match_byte_assert (0x58);
1217               format = get_u32 ();
1218               value = get_double ();
1219               printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
1220               match_byte (1);
1221               match_byte (0);
1222               match_byte (0);
1223               match_byte (0);
1224               match_byte (1);
1225             }
1226           else if (match_byte (0x31))
1227             {
1228               match_byte_assert(3);
1229               get_string();
1230               match_byte_assert(0x58);
1231               match_u32_assert(0);
1232               get_string();
1233               match_byte(0);
1234             }
1235           else
1236             match_byte_assert (0x58);
1237           printf("(%d)\n", get_u32());
1238         }
1239     }
1240 }
1241
1242 static int
1243 find_dimensions(void)
1244 {
1245   {
1246     const char dimensions[] = "-,,,.\0";
1247     int x = try_find_tail(dimensions, sizeof dimensions - 1);
1248     if (x)
1249       return x;
1250   }
1251
1252   const char dimensions[] = "-,,, .\0";
1253   return find_tail(dimensions, sizeof dimensions - 1);
1254 }
1255
1256 static void
1257 dump_fonts(void)
1258 {
1259   printf("fonts: offset=%08x\n", pos);
1260   match_byte(0);
1261   for (int i = 1; i <= 8; i++)
1262     {
1263       printf("%08x: font %d, ", pos, i);
1264       match_byte_assert(i);
1265       match_byte_assert(0x31);
1266       printf("%s, ", get_string());
1267       match_byte_assert(0);
1268       match_byte_assert(0);
1269       if (!match_byte(0x40) && !match_byte(0x20) && !match_byte(0x80) && !match_byte(0x10))
1270         match_byte_assert(0x50);
1271       if (!match_byte(0x41))
1272         match_byte_assert(0x51);
1273       pos += 13;
1274       printf ("%s, ", get_string());
1275       printf ("%s, ", get_string());
1276       match_u32_assert(0);
1277       match_u32_assert(0);
1278       pos++;
1279       get_u32();
1280       get_u32();
1281       get_u32();
1282       get_u32();
1283       putchar('\n');
1284     }
1285
1286   match_u32_assert(240);
1287   pos += 240;
1288
1289   match_u32_assert(18);
1290   pos += 18;
1291
1292   if (match_u32(117))
1293     pos += 117;
1294   else
1295     {
1296       match_u32_assert(142);
1297       pos += 142;
1298     }
1299
1300   int count = get_u32();
1301   pos += 4 * count;
1302
1303   char *encoding = get_string();
1304   printf("encoding=%s\n", encoding);
1305
1306   if (!match_u32(0))
1307     match_u32_assert(UINT32_MAX);
1308   if (!match_byte(0))
1309     match_byte_assert(1);
1310   match_byte_assert(0);
1311   if (!match_byte(0))
1312     match_byte_assert(1);
1313   if (!match_byte(0x99) && !match_byte(0x98))
1314     match_byte_assert(0x97);
1315   match_byte_assert(7);
1316   match_byte_assert(0);
1317   match_byte_assert(0);
1318   if (match_byte('.'))
1319     match_byte_assert(',');
1320   else
1321     {
1322       match_byte_assert(',');
1323       if (!match_byte('.'))
1324         match_byte_assert(' ');
1325     }
1326   match_u32_assert(5);
1327   for (int i = 0; i < 5; i++)
1328     get_string();
1329   pos += get_u32();
1330   if (pos != find_dimensions())
1331     fprintf (stderr, "%08x / %08x\n", pos, find_dimensions());
1332 }
1333
1334 int
1335 main(int argc, char *argv[])
1336 {
1337   size_t start;
1338   struct stat s;
1339
1340   if (isatty(STDIN_FILENO))
1341     {
1342       fprintf(stderr, "redirect stdin from a .bin file\n");
1343       exit(1);
1344     }
1345   if (fstat(STDIN_FILENO, &s))
1346     {
1347       perror("fstat");
1348       exit(1);
1349     }
1350   n = s.st_size;
1351   data = malloc(n);
1352   if (!data)
1353     {
1354       perror("malloc");
1355       exit(1);
1356     }
1357   if (read(STDIN_FILENO, data, n) != n)
1358     {
1359       perror("read");
1360       exit(1);
1361     }
1362
1363   if (argc > 1)
1364     {
1365       if (!strcmp(argv[1], "title0"))
1366         {
1367           pos = 0x27;
1368           if (match_byte (0x03)
1369               || (match_byte (0x05) && match_byte (0x58)))
1370             printf ("%s\n", get_string());
1371           else
1372             printf ("<unknown>\n");
1373           return 0;
1374         }
1375       else if (!strcmp(argv[1], "title"))
1376         {
1377           dump_title();
1378           exit(0);
1379         }
1380       else if (!strcmp(argv[1], "titleraw"))
1381         {
1382           const char fonts[] = "\x01\x31\x09\0\0\0SansSerif";
1383           start = 0x27;
1384           n = find(fonts, sizeof fonts - 1);
1385         }
1386       else if (!strcmp(argv[1], "fonts"))
1387         {
1388           const char fonts[] = "\x01\x31\x09\0\0\0SansSerif";
1389           const char styles[] = "\xf0\0\0\0";
1390           start = find(fonts, sizeof fonts - 1);
1391           n = find(styles, sizeof styles - 1);
1392         }
1393       else if (!strcmp(argv[1], "styles"))
1394         {
1395           const char styles[] = "\xf0\0\0\0";
1396           const char dimensions[] = "-,,,.\0";
1397           start = find(styles, sizeof styles - 1);
1398           n = find(dimensions, sizeof dimensions - 1) + sizeof dimensions - 1;
1399         }
1400       else if (!strcmp(argv[1], "dimensions") || !strcmp(argv[1], "all"))
1401         {
1402           pos = 0;
1403           match_byte_assert(1);
1404           match_byte_assert(0);
1405           match_u32_assert(3);
1406           match_byte_assert(1);
1407           if (!match_byte(0))
1408             match_byte_assert(1);
1409           match_byte_assert(0);
1410           match_byte_assert(0);
1411           if (!match_byte(0))
1412             match_byte_assert(1);
1413           pos++;
1414           match_byte_assert(0);
1415           match_byte_assert(0);
1416           match_byte_assert(0);
1417           dump_title ();
1418           dump_fonts();
1419           dump_dims ();
1420           printf("\n\ndata:\n");
1421           dump_data ();
1422           if (pos == n - 1)
1423             match_byte_assert (1);
1424           if (pos != n)
1425             {
1426               fprintf (stderr, "%x / %x\n", pos, n);
1427               exit(1);
1428             }
1429           exit(0);
1430         }
1431       else
1432         {
1433           fprintf (stderr, "unknown section %s\n", argv[1]);
1434           exit(1);
1435         }
1436     }
1437   else
1438     start = 0x27;
1439
1440   for (size_t i = start; i < n; )
1441     {
1442       if (i + 5 <= n
1443           && data[i]
1444           //&& !data[i + 1]
1445           && !data[i + 2]
1446           && !data[i + 3]
1447           && i + 4 + data[i] + data[i + 1] * 256 <= n
1448           && all_ascii(&data[i + 4], data[i] + data[i + 1] * 256))
1449         {
1450           fputs("\n\"", stdout);
1451           fwrite(&data[i + 4], 1, data[i] + data[i + 1] * 256, stdout);
1452           fputs("\" ", stdout);
1453
1454           i += 4 + data[i] + data[i + 1] * 256;
1455         }
1456       else if (i + 12 <= n
1457                && data[i + 1] == 40
1458                && data[i + 2] == 5
1459                && data[i + 3] == 0)
1460         {
1461           double d;
1462
1463           memcpy (&d, &data[i + 4], 8);
1464           printf ("F40.%d(%.*f)\n", data[i], data[i], d);
1465           i += 12;
1466         }
1467       else if (i + 12 <= n
1468                && data[i + 1] == 40
1469                && data[i + 2] == 31
1470                && data[i + 3] == 0)
1471         {
1472           double d;
1473
1474           memcpy (&d, &data[i + 4], 8);
1475           printf ("PCT40.%d(%.*f)\n", data[i], data[i], d);
1476           i += 12;
1477         }
1478       else if (i + 4 <= n
1479                && (data[i] && data[i] != 88 && data[i] != 0x41)
1480                && !data[i + 1]
1481                && !data[i + 2]
1482                && !data[i + 3])
1483         {
1484           printf ("i%d ", data[i]);
1485           i += 4;
1486         }
1487       else
1488         {
1489           printf("%02x ", data[i]);
1490           i++;
1491         }
1492     }
1493
1494   return 0;
1495 }