Generalize dump_value().
[pspp] / dump.c
1 #include <stdbool.h>
2 #include <stdint.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <sys/stat.h>
7 #include <unistd.h>
8
9 static uint8_t *data;
10 static size_t n;
11
12 static bool
13 all_ascii(const uint8_t *p, size_t n)
14 {
15   for (size_t i = 0; i < n; i++)
16     if (p[i] < 32 || p[i] > 126)
17       return false;
18   return true;
19 }
20
21 static size_t
22 try_find(const char *target, size_t target_len)
23 {
24   const uint8_t *pos = (const uint8_t *) memmem (data, n, target, target_len);
25   return pos ? pos - data : 0;
26 }
27
28 static size_t
29 try_find_tail(const char *target, size_t target_len)
30 {
31   size_t pos = try_find(target, target_len);
32   return pos ? pos + target_len : 0;
33 }
34
35 static size_t
36 find(const char *target, size_t target_len)
37 {
38   size_t pos = try_find(target, target_len);
39   if (!pos)
40     {
41       fprintf (stderr, "not found\n");
42       exit(1);
43     }
44   return pos;
45 }
46
47 static size_t
48 find_tail(const char *target, size_t target_len)
49 {
50   size_t pos = try_find_tail(target, target_len);
51   if (!pos)
52     {
53       fprintf (stderr, "not found\n");
54       exit(1);
55     }
56   return pos;
57 }
58
59 size_t pos;
60
61 #define XSTR(x) #x
62 #define STR(x) XSTR(x)
63 #define WHERE __FILE__":" STR(__LINE__)
64
65 static unsigned int
66 get_u32(void)
67 {
68   uint32_t x;
69   memcpy(&x, &data[pos], 4);
70   pos += 4;
71   return x;
72 }
73
74 static double
75 get_double(void)
76 {
77   double x;
78   memcpy(&x, &data[pos], 8);
79   pos += 8;
80   return x;
81 }
82
83 static bool
84 match_u32(uint32_t x)
85 {
86   if (get_u32() == x)
87     return true;
88   pos -= 4;
89   return false;
90 }
91
92 static void
93 match_u32_assert(uint32_t x, const char *where)
94 {
95   unsigned int y = get_u32();
96   if (x != y)
97     {
98       fprintf(stderr, "%s: 0x%x: expected i%u, got i%u\n", where, pos - 4, x, y);
99       exit(1);
100     }
101 }
102 #define match_u32_assert(x) match_u32_assert(x, WHERE)
103
104 static bool
105 match_byte(uint8_t b)
106 {
107   if (pos < n && data[pos] == b)
108     {
109       pos++;
110       return true;
111     }
112   else
113     return false;
114 }
115
116 static void
117 match_byte_assert(uint8_t b, const char *where)
118 {
119   if (!match_byte(b))
120     {
121       fprintf(stderr, "%s: 0x%x: expected %02x, got %02x\n", where, pos, b, data[pos]);
122       exit(1);
123     }
124 }
125 #define match_byte_assert(b) match_byte_assert(b, WHERE)
126
127 static char *
128 get_string(const char *where)
129 {
130   if (1
131       /*data[pos + 1] == 0 && data[pos + 2] == 0 && data[pos + 3] == 0*/
132       /*&& all_ascii(&data[pos + 4], data[pos])*/)
133     {
134       int len = data[pos] + data[pos + 1] * 256;
135       char *s = malloc(len + 1);
136
137       memcpy(s, &data[pos + 4], len);
138       s[len] = 0;
139       pos += 4 + len;
140       return s;
141     }
142   else
143     {
144       fprintf(stderr, "%s: 0x%x: expected string\n", where, pos);
145       exit(1);
146     }
147 }
148 #define get_string() get_string(WHERE)
149
150 static void
151 dump_value_31(void)
152 {
153   if (match_byte (0x31))
154     {
155       if (match_u32 (0))
156         {
157           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       match_byte_assert (2);
387     }
388   else if (match_byte (2))
389     {
390       unsigned int format;
391       char *var, *vallab;
392       double value;
393
394       match_byte_assert (0x58);
395       format = get_u32 ();
396       value = get_double ();
397       var = get_string ();
398       vallab = get_string ();
399       printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
400               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
401       if (!match_u32 (3))
402         match_u32_assert (2);
403       match_byte (0);
404     }
405   else if (match_byte (1))
406     {
407       unsigned int format;
408       double value;
409
410       match_byte_assert (0x58);
411       format = get_u32 ();
412       value = get_double ();
413       printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
414       match_byte (1);
415       match_byte (0);
416       match_byte (0);
417       match_byte (0);
418       match_byte (1);
419     }
420   else
421     {
422       int subn;
423
424       match_byte (0);
425       match_byte_assert (0x31);
426       match_u32_assert (0);
427       match_u32_assert (0);
428       subn = get_u32 ();
429       printf ("nested %d bytes", subn);
430       pos += subn;
431       printf ("; \"%s\", substitutions:", get_string());
432       int total_subs = get_u32();
433       int x = get_u32();
434       if (x)
435         {
436           total_subs = (total_subs - 1) + x;
437           match_u32_assert (0);
438         }
439       printf (" (total %d)", total_subs);
440
441       for (int i = 0; i < total_subs; i++)
442         {
443           putc ('\n', stdout);
444           dump_value (level + 1);
445         }
446     }
447 }
448
449 static void
450 dump_category(int level)
451 {
452   match_byte (0);
453   match_byte (0);
454   match_byte (0);
455   match_byte (0);
456   dump_value (level);
457
458   if (match_u32 (2))
459     get_u32 ();
460   else if (match_u32 (1))
461     {
462       match_byte (0);
463       match_byte (0);
464       match_byte (0);
465       get_u32 ();
466     }
467   else if (match_byte (1))
468     {
469       match_byte (0);
470       if (!match_u32 (2))
471         match_u32_assert (1);
472       match_byte (0);
473       get_u32();
474     }
475   else
476     {
477       match_u32_assert (0);
478       get_u32 ();
479     }
480
481   int n_categories = get_u32();
482   if (n_categories > 0)
483     printf (", %d subcategories:", n_categories);
484   printf("\n");
485   for (int i = 0; i < n_categories; i++)
486     dump_category (level + 1);
487 }
488
489 static void
490 dump_dim(void)
491 {
492   int n_categories;
493   printf("next dim\n");
494   match_byte(0);
495   if (match_byte(3))
496     {
497       get_string();
498       match_byte_assert(0x58);
499       get_string();
500       printf("string \"%s\": ", get_string());
501       match_byte(1) || match_byte(0);
502     }
503   else if (match_byte(5)) 
504     {
505       match_byte_assert(0x58);
506       printf("variable \"%s\": ", get_string());
507       get_string();
508       if (!match_byte(2))
509         match_byte_assert(3);
510     }
511   else if (match_byte(0x31))
512     {
513       int subn;
514       int total_subs = 1;
515
516       match_u32_assert (0);
517       match_u32_assert (0);
518       subn = get_u32 ();
519       printf ("nested %d bytes", subn);
520       pos += subn;
521       printf ("; \"%s\", substitutions:", get_string());
522       for (;;)
523         {
524           int n_subst = get_u32();
525           if (!n_subst)
526             break;
527           printf (" %d", n_subst);
528           total_subs *= n_subst;
529         }
530
531       for (int i = 0; i < total_subs; i++)
532         {
533           putc ('\n', stdout);
534           dump_dim_value (0);
535         }
536     }
537   else
538     {
539       int total_subs = 1;
540
541       match_byte_assert (0x58);
542       printf ("\"%s\" with substitutions:", get_string());
543       for (;;)
544         {
545           int n_subst = get_u32();
546           if (!n_subst)
547             break;
548           printf (" %d", n_subst);
549           total_subs *= n_subst;
550         }
551
552       for (int i = 0; i < total_subs; i++)
553         {
554           putc ('\n', stdout);
555           dump_dim_value (0);
556         }
557     }
558
559   /* This byte is usually 0x02 but 0x00 and 0x75 (!) have also been spotted. */
560   pos++;
561
562   if (!match_byte(0) && !match_byte(1))
563     match_byte_assert(2);
564   if (!match_u32(0))
565     match_u32_assert(2);
566   if (!match_byte(0))
567     match_byte_assert(1);
568   match_byte(0);
569   match_byte(0);
570   match_byte(0);
571   match_byte(0);
572   get_u32();
573   match_byte(0);
574   match_byte(0);
575   match_byte(0);
576   match_byte(0);
577   n_categories = get_u32();
578   printf("%d nested categories\n", n_categories);
579   for (int i = 0; i < n_categories; i++)
580     dump_category (0);
581 }
582
583 int n_dims;
584 static void
585 dump_dims(void)
586 {
587   n_dims = get_u32();
588   printf ("%u dimensions\n", n_dims);
589   for (int i = 0; i < n_dims; i++)
590     {
591       printf("\n");
592       dump_dim ();
593     }
594 }
595
596 static void
597 dump_data_value_31(void)
598 {
599   if (match_byte (0x31))
600     {
601       if (match_u32 (0))
602         {
603           if (match_u32 (1))
604             get_string();
605           else
606             match_u32_assert (0);
607           int subn = get_u32 ();
608           printf ("nested %d bytes", subn);
609           pos += subn;
610         }
611       else if (match_u32 (1))
612         {
613           printf("(footnote %d) ", get_u32());
614           match_byte_assert (0);
615           match_byte_assert (0);
616           int subn = get_u32 ();
617           printf ("nested %d bytes", subn);
618           pos += subn;
619         }
620       else if (match_u32 (2))
621         {
622           printf("(special 2)");
623           match_byte_assert(0);
624           match_byte_assert(0);
625           match_u32_assert(1);
626           match_byte_assert(0);
627           match_byte_assert(0);
628           int subn = get_u32 ();
629           printf ("nested %d bytes", subn);
630           pos += subn;
631         }
632       else
633         {
634           match_u32_assert(3);
635           printf("(special 3)");
636           match_byte_assert(0);
637           match_byte_assert(0);
638           match_byte_assert(1);
639           match_byte_assert(0);
640           int subn = get_u32 ();
641           printf ("nested %d bytes, ", subn);
642           pos += subn;
643           subn = get_u32 ();
644           printf ("nested %d bytes, ", subn);
645           pos += subn;
646         }
647     }
648   else
649     match_byte_assert (0x58);
650 }
651
652 static void
653 dump_data(void)
654 {
655 #if 1
656   int a[16];
657   for (int i = 0; i < 3 + n_dims; i++)
658     a[i] = get_u32();
659   printf ("data intro:");
660   for (int i = 0; i < 3 + n_dims; i++)
661     printf(" %d", a[i]);
662   printf("\n");
663 #else
664   fprintf (stderr,"data intro (%d dims):", n_dims);
665   for (int i = 0; i < 3+n_dims; i++)
666     fprintf (stderr," %d", get_u32());
667   fprintf(stderr,"\n");
668 #endif
669   int x = get_u32();
670   printf ("%d data values, starting at %08x\n", x, pos);
671   for (int i = 0; i < x; i++)
672     {
673       printf("%08x, index %d:\n", pos, get_u32());
674       match_u32_assert(0);
675       match_byte(0);
676       match_byte(0);
677       match_byte(0);
678       match_byte(0);
679       if (match_byte (1))
680         {
681           unsigned int format;
682           double value;
683
684           dump_data_value_31();
685           format = get_u32 ();
686           value = get_double ();
687           printf ("    value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
688         }
689       else if (match_byte (3))
690         {
691           get_string();
692           dump_data_value_31();
693           get_string();
694           printf("string \"%s\"", get_string());
695           match_byte (0);
696         }
697       else if (match_byte (2))
698         {
699           unsigned int format;
700           char *var, *vallab;
701           double value;
702
703           match_byte_assert (0x58);
704           format = get_u32 ();
705           value = get_double ();
706           var = get_string ();
707           vallab = get_string ();
708           printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
709                   value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
710           if (!match_byte (1) && !match_byte(2))
711             match_byte_assert (3);
712         }
713       else if (match_byte (4))
714         {
715           unsigned int format;
716           char *var, *vallab, *value;
717
718           match_byte_assert (0x58);
719           format = get_u32 ();
720           vallab = get_string ();
721           var = get_string ();
722           if (!match_byte(1) && !match_byte(2))
723             match_byte_assert (3);
724           value = get_string ();
725           printf ("value \"%s\" format %d(%d.%d) var \"%s\" vallab \"%s\"",
726                   value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
727         }
728       else if (match_byte (5))
729         {
730           match_byte_assert (0x58);
731           printf ("variable \"%s\"", get_string());
732           get_string();
733           if (!match_byte(1) && !match_byte(2))
734             match_byte_assert(3);
735           match_byte (0);
736           match_byte (0);
737           match_byte (0);
738           match_byte (0);
739         }
740       else
741         {
742           dump_data_value_31();
743           char *base = get_string();
744           int x = get_u32();
745           printf ("\"%s\"; %d variables:\n", base, x);
746           for (int i = 0; i < x; i++)
747             {
748               int y = get_u32();
749               if (!y)
750                 y = 1;
751               else
752                 match_u32_assert(0);
753               for (int j = 0; j <= 0; j++)
754                 printf ("    ");
755               printf("variable %d has %d values:\n", i, y);
756               for (int j = 0; j < y; j++)
757                 {
758                   if (match_byte (1))
759                     {
760                       unsigned int format;
761                       double value;
762
763                       dump_data_value_31();
764                       format = get_u32 ();
765                       value = get_double ();
766                       printf ("    value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
767                     }
768                   else if (match_byte(3))
769                     {
770                       char *a = get_string();
771                       match_byte_assert(0x58);
772                       char *b = get_string();
773                       char *c = get_string();
774                       for (int k = 0; k <= 1; k++)
775                         printf ("    ");
776                       printf ("\"%s\", \"%s\", \"%s\"", a, b, c);
777                       match_byte(0);
778                       match_byte(0);
779                       match_byte(0);
780                       match_byte(0);
781                     }
782                   else if (match_byte(5))
783                     {
784                       match_byte_assert (0x58);
785                       printf ("variable \"%s\"", get_string());
786                       get_string();
787                       if (!match_byte(1) && !match_byte(2))
788                         match_byte_assert(3);
789                       match_byte (0);
790                       match_byte (0);
791                       match_byte (0);
792                       match_byte (0);
793                     }
794                   else
795                     dump_value (0);
796                   putchar('\n');
797                 }
798             }
799         }
800       putchar('\n');
801     }
802 }
803
804 static void
805 dump_title_value_31(int level)
806 {
807   if (match_byte (0x31))
808     {
809       if (match_u32 (0))
810         {
811           match_u32_assert (0);
812           int subn = get_u32 ();
813           printf ("nested %d bytes", subn);
814           pos += subn;
815         }
816       else if (match_u32 (1))
817         {
818           printf("(footnote %d) ", get_u32());
819           match_byte_assert (0);
820           match_byte_assert (0);
821           int subn = get_u32 ();
822           printf ("nested %d bytes", subn);
823           pos += subn;
824         }
825       else if (match_u32 (2))
826         {
827           printf("(special 2)");
828           match_byte_assert(0);
829           match_byte_assert(0);
830           if (!match_u32(2))
831             match_u32_assert(1);
832           match_byte_assert(0);
833           match_byte_assert(0);
834           int subn = get_u32 ();
835           printf ("nested %d bytes", subn);
836           pos += subn;
837         }
838       else
839         {
840           match_u32_assert(3);
841           printf("(special 3)");
842           match_byte_assert(0);
843           match_byte_assert(0);
844           match_byte_assert(1);
845           match_byte_assert(0);
846           int subn = get_u32 ();
847           printf ("nested %d bytes, ", subn);
848           pos += subn;
849           subn = get_u32 ();
850           printf ("nested %d bytes, ", subn);
851           pos += subn;
852         }
853     }
854   else
855     match_byte_assert (0x58);
856 }
857
858 static void
859 dump_title_value(int level)
860 {
861   for (int i = 0; i <= level; i++)
862     printf ("    ");
863
864   match_byte (0);
865   match_byte (0);
866   match_byte (0);
867   match_byte (0);
868   match_byte (0);
869   if (match_byte (3))
870     {
871       get_string();
872       dump_title_value_31(level);
873       get_string();
874       printf("string \"%s\"", get_string());
875       match_byte (0);
876       match_byte (0);
877       match_byte (0);
878       match_byte (1);
879       match_byte (1);
880       match_byte (0);
881       match_byte (0);
882       match_byte (0);
883       match_byte (1);
884     }
885   else if (match_byte (5))
886     {
887       dump_title_value_31(level);
888       printf ("variable \"%s\"", get_string());
889       get_string();
890       if (!match_byte(1) && !match_byte(2))
891         match_byte_assert(3);
892     }
893   else if (match_byte (2))
894     {
895       unsigned int format;
896       char *var, *vallab;
897       double value;
898
899       match_byte_assert (0x58);
900       format = get_u32 ();
901       value = get_double ();
902       var = get_string ();
903       vallab = get_string ();
904       printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
905               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
906       if (!match_byte (1) && !match_byte(2))
907         match_byte_assert (3);
908       match_byte (0);
909       match_byte (0);
910       match_byte (0);
911       match_byte (0);
912       match_byte (0);
913       match_byte (0);
914       match_byte (0);
915     }
916   else if (match_byte (4))
917     {
918       unsigned int format;
919       char *var, *vallab, *value;
920
921       match_byte_assert (0x58);
922       format = get_u32 ();
923       vallab = get_string ();
924       var = get_string ();
925       if (!match_byte(1) && !match_byte(2))
926         match_byte_assert (3);
927       value = get_string ();
928       printf ("value \"%s\" format %d(%d.%d) var \"%s\" vallab \"%s\"",
929               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
930       match_byte (0);
931       match_byte (0);
932       match_byte (0);
933       match_byte (0);
934     }
935   else if (match_byte (1))
936     {
937       unsigned int format;
938       double value;
939
940       dump_title_value_31(level);
941       format = get_u32 ();
942       value = get_double ();
943       printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
944       match_byte (1);
945       match_byte (0);
946       match_byte (0);
947       match_byte (0);
948       match_byte (1);
949     }
950   else
951     {
952       dump_title_value_31(level);
953
954       char *base = get_string();
955       int x = get_u32();
956       printf ("\"%s\" with %d variables:\n", base, x);
957       for (int i = 0; i < x; i++)
958         {
959           int y = get_u32();
960           if (!y)
961             y = 1;
962           else
963             match_u32_assert(0);
964           for (int j = 0; j <= level; j++)
965             printf ("    ");
966           printf("variable %d has %d values:\n", i, y);
967           for (int j = 0; j < y; j++)
968             {
969               match_byte(0);
970               if (match_byte(3))
971                 {
972                   char *a = get_string();
973                   match_byte_assert(0x58);
974                   char *b = get_string();
975                   char *c = get_string();
976                   for (int k = 0; k <= level + 1; k++)
977                     printf ("    ");
978                   printf ("\"%s\", \"%s\", \"%s\"", a, b, c);
979                 }
980               else
981                 dump_title_value (level+1);
982               putchar('\n');
983             }
984         }
985     }
986 }
987
988 static void
989 dump_footnote_value(int level)
990 {
991   for (int i = 0; i <= level; i++)
992     printf ("    ");
993
994   match_byte (0);
995   match_byte (0);
996   match_byte (0);
997   match_byte (0);
998   if (match_byte (3))
999     {
1000       get_string();
1001       if (match_byte (0x31))
1002         {
1003           if (match_u32 (1))
1004             {
1005               printf("(footnote %d) ", get_u32());
1006               match_byte_assert (0);
1007               match_byte_assert (0);
1008               int subn = get_u32 ();
1009               printf ("nested %d bytes", subn);
1010               pos += subn;
1011             }
1012           else if (match_u32 (2))
1013             {
1014               printf("(special 2)");
1015               match_byte_assert(0);
1016               match_byte_assert(0);
1017               match_u32_assert(1);
1018               match_byte_assert(0);
1019               match_byte_assert(0);
1020               int subn = get_u32 ();
1021               printf ("nested %d bytes", subn);
1022               pos += subn;
1023             }
1024           else
1025             {
1026               match_u32_assert(3);
1027               printf("(special 3)");
1028               match_byte_assert(0);
1029               match_byte_assert(0);
1030               match_byte_assert(1);
1031               match_byte_assert(0);
1032               int subn = get_u32 ();
1033               printf ("nested %d bytes, ", subn);
1034               pos += subn;
1035               subn = get_u32 ();
1036               printf ("nested %d bytes, ", subn);
1037               pos += subn;
1038             }
1039         }
1040       else
1041         match_byte_assert (0x58);
1042       get_string();
1043       printf("string \"%s\"", get_string());
1044       if (!match_byte (0))
1045         match_byte_assert (1);
1046     }
1047   else if (match_byte (5))
1048     {
1049       match_byte_assert (0x58);
1050       printf ("variable \"%s\"", get_string());
1051       get_string();
1052       if (!match_byte(1) && !match_byte(2))
1053         match_byte_assert(3);
1054     }
1055   else if (match_byte (2))
1056     {
1057       unsigned int format;
1058       char *var, *vallab;
1059       double value;
1060
1061       match_byte_assert (0x58);
1062       format = get_u32 ();
1063       value = get_double ();
1064       var = get_string ();
1065       vallab = get_string ();
1066       printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
1067               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
1068       if (!match_byte (1) && !match_byte(2))
1069         match_byte_assert (3);
1070       match_byte (0);
1071       match_byte (0);
1072       match_byte (0);
1073       match_byte (0);
1074       match_byte (0);
1075       match_byte (0);
1076       match_byte (0);
1077     }
1078   else if (match_byte (4))
1079     {
1080       unsigned int format;
1081       char *var, *vallab, *value;
1082
1083       match_byte_assert (0x58);
1084       format = get_u32 ();
1085       vallab = get_string ();
1086       var = get_string ();
1087       if (!match_byte(1) && !match_byte(2))
1088         match_byte_assert (3);
1089       value = get_string ();
1090       printf ("value \"%s\" format %d(%d.%d) var \"%s\" vallab \"%s\"",
1091               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
1092       match_byte (0);
1093       match_byte (0);
1094       match_byte (0);
1095       match_byte (0);
1096     }
1097   else if (match_byte (1))
1098     {
1099       unsigned int format;
1100       double value;
1101
1102       if (match_byte (0x31))
1103         {
1104           if (match_u32 (1))
1105             {
1106               printf("(footnote %d) ", get_u32());
1107               match_byte_assert (0);
1108               match_byte_assert (0);
1109               int subn = get_u32 ();
1110               printf ("nested %d bytes", subn);
1111               pos += subn;
1112             }
1113         }
1114       else
1115         match_byte_assert (0x58);
1116       format = get_u32 ();
1117       value = get_double ();
1118       printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
1119     }
1120   else if (match_byte (0x31))
1121     {
1122       if (match_u32 (1))
1123         {
1124           printf("(footnote %d) ", get_u32());
1125           match_byte_assert (0);
1126           match_byte_assert (0);
1127           int subn = get_u32 ();
1128           printf ("nested %d bytes", subn);
1129           pos += subn;
1130         }
1131       else
1132         {
1133           match_u32_assert (0);
1134           match_u32_assert (0);
1135           int subn = get_u32 ();
1136           printf ("nested %d bytes", subn);
1137           pos += subn;
1138         }
1139       char *base = get_string();
1140       int x = get_u32();
1141       printf ("\"%s\"; %d variables:\n", base, x);
1142       for (int i = 0; i < x; i++)
1143         {
1144           int y = get_u32();
1145           if (!y)
1146             y = 1;
1147           else
1148             match_u32_assert(0);
1149           for (int j = 0; j <= level; j++)
1150             printf ("    ");
1151           printf("variable %d has %d values:\n", i, y);
1152           for (int j = 0; j < y; j++)
1153             {
1154               if (match_byte(3))
1155                 {
1156                   char *a = get_string();
1157                   match_byte_assert(0x58);
1158                   char *b = get_string();
1159                   char *c = get_string();
1160                   for (int k = 0; k <= level + 1; k++)
1161                     printf ("    ");
1162                   printf ("\"%s\", \"%s\", \"%s\"", a, b, c);
1163                   if (!match_byte(1))
1164                     match_byte_assert(0);
1165                 }
1166               else
1167                 dump_footnote_value (level+1);
1168               putchar('\n');
1169             }
1170         }
1171     }
1172   else
1173     {
1174
1175       match_byte_assert (0x58);
1176       char *base = get_string();
1177       int x = get_u32();
1178       printf ("\"%s\" with %d variables:\n", base, x);
1179       for (int i = 0; i < x; i++)
1180         {
1181           int y = get_u32();
1182           if (!y)
1183             y = 1;
1184           else
1185             match_u32_assert(0);
1186           for (int j = 0; j <= level; j++)
1187             printf ("    ");
1188           printf("variable %d has %d values:\n", i, y);
1189           for (int j = 0; j < y; j++)
1190             {
1191               if (match_byte(3))
1192                 {
1193                   char *a = get_string();
1194                   match_byte_assert(0x58);
1195                   char *b = get_string();
1196                   char *c = get_string();
1197                   for (int k = 0; k <= level + 1; k++)
1198                     printf ("    ");
1199                   printf ("\"%s\", \"%s\", \"%s\"", a, b, c);
1200                   match_byte_assert(0);
1201                 }
1202               else
1203                 dump_footnote_value (level+1);
1204               putchar('\n');
1205             }
1206         }
1207     }
1208 }
1209
1210 static void
1211 dump_title(void)
1212 {
1213   pos = 0x27;
1214   dump_title_value(0); putchar('\n');
1215   dump_title_value(0); putchar('\n');
1216   match_byte_assert(0x31);
1217   dump_title_value(0); putchar('\n');
1218   match_byte(0);
1219   match_byte_assert(0x58);
1220   if (match_byte(0x31))
1221     {
1222       dump_footnote_value(0); putchar('\n');
1223     }
1224   else
1225     match_byte_assert(0x58);
1226
1227
1228   int n_footnotes = get_u32();
1229   if (n_footnotes >= 20)
1230     {
1231       fprintf(stderr, "%08x: %d footnotes\n", pos - 4, n_footnotes);
1232       exit(1);
1233     }
1234
1235   printf("------\n%d footnotes\n", n_footnotes);
1236   if (n_footnotes < 20)
1237     {
1238       for (int i = 0; i < n_footnotes; i++)
1239         {
1240           printf("footnote %d:\n", i);
1241           dump_footnote_value(0);
1242           match_byte(0);
1243           match_byte(0);
1244           match_byte(0);
1245           match_byte(0);
1246           if (match_byte (1))
1247             {
1248               unsigned int format;
1249               double value;
1250
1251               if (match_byte (0x31))
1252                 {
1253                   if (match_u32 (1))
1254                     {
1255                       printf("(footnote %d) ", get_u32());
1256                       match_byte_assert (0);
1257                       match_byte_assert (0);
1258                       int subn = get_u32 ();
1259                       printf ("nested %d bytes", subn);
1260                       pos += subn;
1261                     }
1262                 }
1263               else
1264                 match_byte_assert (0x58);
1265               format = get_u32 ();
1266               value = get_double ();
1267               printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
1268               match_byte (1);
1269               match_byte (0);
1270               match_byte (0);
1271               match_byte (0);
1272               match_byte (1);
1273             }
1274           else if (match_byte (0x31))
1275             {
1276               match_byte_assert(3);
1277               get_string();
1278               match_byte_assert(0x58);
1279               match_u32_assert(0);
1280               get_string();
1281               match_byte(0);
1282             }
1283           else
1284             match_byte_assert (0x58);
1285           printf("(%d)\n", get_u32());
1286         }
1287     }
1288 }
1289
1290 static int
1291 find_dimensions(void)
1292 {
1293   {
1294     const char dimensions[] = "-,,,.\0";
1295     int x = try_find_tail(dimensions, sizeof dimensions - 1);
1296     if (x)
1297       return x;
1298   }
1299
1300   const char dimensions[] = "-,,, .\0";
1301   return find_tail(dimensions, sizeof dimensions - 1);
1302 }
1303
1304 static void
1305 dump_fonts(void)
1306 {
1307   printf("fonts: offset=%08x\n", pos);
1308   match_byte(0);
1309   for (int i = 1; i <= 8; i++)
1310     {
1311       printf("%08x: font %d, ", pos, i);
1312       match_byte_assert(i);
1313       match_byte_assert(0x31);
1314       printf("%s, ", get_string());
1315       match_byte_assert(0);
1316       match_byte_assert(0);
1317       if (!match_byte(0x40) && !match_byte(0x20) && !match_byte(0x80) && !match_byte(0x10))
1318         match_byte_assert(0x50);
1319       if (!match_byte(0x41))
1320         match_byte_assert(0x51);
1321       pos += 13;
1322       printf ("%s, ", get_string());
1323       printf ("%s, ", get_string());
1324       match_u32_assert(0);
1325       match_u32_assert(0);
1326       pos++;
1327       get_u32();
1328       get_u32();
1329       get_u32();
1330       get_u32();
1331       putchar('\n');
1332     }
1333
1334   match_u32_assert(240);
1335   pos += 240;
1336
1337   match_u32_assert(18);
1338   pos += 18;
1339
1340   if (match_u32(117))
1341     pos += 117;
1342   else
1343     {
1344       match_u32_assert(142);
1345       pos += 142;
1346     }
1347
1348   int count = get_u32();
1349   pos += 4 * count;
1350
1351   char *encoding = get_string();
1352   printf("encoding=%s\n", encoding);
1353
1354   if (!match_u32(0))
1355     match_u32_assert(UINT32_MAX);
1356   if (!match_byte(0))
1357     match_byte_assert(1);
1358   match_byte_assert(0);
1359   if (!match_byte(0))
1360     match_byte_assert(1);
1361   if (!match_byte(0x99) && !match_byte(0x98))
1362     match_byte_assert(0x97);
1363   match_byte_assert(7);
1364   match_byte_assert(0);
1365   match_byte_assert(0);
1366   if (match_byte('.'))
1367     match_byte_assert(',');
1368   else
1369     {
1370       match_byte_assert(',');
1371       if (!match_byte('.'))
1372         match_byte_assert(' ');
1373     }
1374   match_u32_assert(5);
1375   for (int i = 0; i < 5; i++)
1376     get_string();
1377   pos += get_u32();
1378   if (pos != find_dimensions())
1379     fprintf (stderr, "%08x / %08x\n", pos, find_dimensions());
1380 }
1381
1382 int
1383 main(int argc, char *argv[])
1384 {
1385   size_t start;
1386   struct stat s;
1387
1388   if (isatty(STDIN_FILENO))
1389     {
1390       fprintf(stderr, "redirect stdin from a .bin file\n");
1391       exit(1);
1392     }
1393   if (fstat(STDIN_FILENO, &s))
1394     {
1395       perror("fstat");
1396       exit(1);
1397     }
1398   n = s.st_size;
1399   data = malloc(n);
1400   if (!data)
1401     {
1402       perror("malloc");
1403       exit(1);
1404     }
1405   if (read(STDIN_FILENO, data, n) != n)
1406     {
1407       perror("read");
1408       exit(1);
1409     }
1410
1411   if (argc > 1)
1412     {
1413       if (!strcmp(argv[1], "title0"))
1414         {
1415           pos = 0x27;
1416           if (match_byte (0x03)
1417               || (match_byte (0x05) && match_byte (0x58)))
1418             printf ("%s\n", get_string());
1419           else
1420             printf ("<unknown>\n");
1421           return 0;
1422         }
1423       else if (!strcmp(argv[1], "title"))
1424         {
1425           dump_title();
1426           exit(0);
1427         }
1428       else if (!strcmp(argv[1], "titleraw"))
1429         {
1430           const char fonts[] = "\x01\x31\x09\0\0\0SansSerif";
1431           start = 0x27;
1432           n = find(fonts, sizeof fonts - 1);
1433         }
1434       else if (!strcmp(argv[1], "fonts"))
1435         {
1436           const char fonts[] = "\x01\x31\x09\0\0\0SansSerif";
1437           const char styles[] = "\xf0\0\0\0";
1438           start = find(fonts, sizeof fonts - 1);
1439           n = find(styles, sizeof styles - 1);
1440         }
1441       else if (!strcmp(argv[1], "styles"))
1442         {
1443           const char styles[] = "\xf0\0\0\0";
1444           const char dimensions[] = "-,,,.\0";
1445           start = find(styles, sizeof styles - 1);
1446           n = find(dimensions, sizeof dimensions - 1) + sizeof dimensions - 1;
1447         }
1448       else if (!strcmp(argv[1], "dimensions") || !strcmp(argv[1], "all"))
1449         {
1450           pos = 0;
1451           match_byte_assert(1);
1452           match_byte_assert(0);
1453           match_u32_assert(3);
1454           match_byte_assert(1);
1455           if (!match_byte(0))
1456             match_byte_assert(1);
1457           match_byte_assert(0);
1458           match_byte_assert(0);
1459           if (!match_byte(0))
1460             match_byte_assert(1);
1461           pos++;
1462           match_byte_assert(0);
1463           match_byte_assert(0);
1464           match_byte_assert(0);
1465           dump_title ();
1466           dump_fonts();
1467           dump_dims ();
1468           printf("\n\ndata:\n");
1469           dump_data ();
1470           if (pos == n - 1)
1471             match_byte_assert (1);
1472           if (pos != n)
1473             {
1474               fprintf (stderr, "%x / %x\n", pos, n);
1475               exit(1);
1476             }
1477           exit(0);
1478         }
1479       else
1480         {
1481           fprintf (stderr, "unknown section %s\n", argv[1]);
1482           exit(1);
1483         }
1484     }
1485   else
1486     start = 0x27;
1487
1488   for (size_t i = start; i < n; )
1489     {
1490       if (i + 5 <= n
1491           && data[i]
1492           //&& !data[i + 1]
1493           && !data[i + 2]
1494           && !data[i + 3]
1495           && i + 4 + data[i] + data[i + 1] * 256 <= n
1496           && all_ascii(&data[i + 4], data[i] + data[i + 1] * 256))
1497         {
1498           fputs("\n\"", stdout);
1499           fwrite(&data[i + 4], 1, data[i] + data[i + 1] * 256, stdout);
1500           fputs("\" ", stdout);
1501
1502           i += 4 + data[i] + data[i + 1] * 256;
1503         }
1504       else if (i + 12 <= n
1505                && data[i + 1] == 40
1506                && data[i + 2] == 5
1507                && data[i + 3] == 0)
1508         {
1509           double d;
1510
1511           memcpy (&d, &data[i + 4], 8);
1512           printf ("F40.%d(%.*f)\n", data[i], data[i], d);
1513           i += 12;
1514         }
1515       else if (i + 12 <= n
1516                && data[i + 1] == 40
1517                && data[i + 2] == 31
1518                && data[i + 3] == 0)
1519         {
1520           double d;
1521
1522           memcpy (&d, &data[i + 4], 8);
1523           printf ("PCT40.%d(%.*f)\n", data[i], data[i], d);
1524           i += 12;
1525         }
1526       else if (i + 4 <= n
1527                && (data[i] && data[i] != 88 && data[i] != 0x41)
1528                && !data[i + 1]
1529                && !data[i + 2]
1530                && !data[i + 3])
1531         {
1532           printf ("i%d ", data[i]);
1533           i += 4;
1534         }
1535       else
1536         {
1537           printf("%02x ", data[i]);
1538           i++;
1539         }
1540     }
1541
1542   return 0;
1543 }