Fix a few footnotes.
[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 (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(int level)
152 {
153   for (int i = 0; i <= level; i++)
154     printf ("    ");
155
156   match_byte (0);
157   match_byte (0);
158   match_byte (0);
159   match_byte (0);
160   if (match_byte (3))
161     {
162       get_string();
163       if (match_byte (0x31))
164         {
165           if (match_u32 (1))
166             {
167               printf("(footnote %d) ", get_u32());
168               match_byte_assert (0);
169               match_byte_assert (0);
170               int subn = get_u32 ();
171               printf ("nested %d bytes", subn);
172               pos += subn;
173             }
174           else if (match_u32 (2))
175             {
176               printf("(special 2)");
177               match_byte_assert(0);
178               match_byte_assert(0);
179               match_u32_assert(1);
180               match_byte_assert(0);
181               match_byte_assert(0);
182               int subn = get_u32 ();
183               printf ("nested %d bytes", subn);
184               pos += subn;
185             }
186           else
187             {
188               match_u32_assert(3);
189               printf("(special 3)");
190               match_byte_assert(0);
191               match_byte_assert(0);
192               match_byte_assert(1);
193               match_byte_assert(0);
194               int subn = get_u32 ();
195               printf ("nested %d bytes, ", subn);
196               pos += subn;
197               subn = get_u32 ();
198               printf ("nested %d bytes, ", subn);
199               pos += subn;
200             }
201         }
202       else
203         match_byte_assert (0x58);
204       get_string();
205       printf("string \"%s\"", get_string());
206       match_byte (0);
207       match_byte (0);
208       match_byte (0);
209       match_byte (1);
210       match_byte (1);
211       match_byte (0);
212       match_byte (0);
213       match_byte (0);
214       match_byte (1);
215     }
216   else if (match_byte (5))
217     {
218       match_byte_assert (0x58);
219       printf ("variable \"%s\"", get_string());
220       get_string();
221       if (!match_byte(1) && !match_byte(2))
222         match_byte_assert(3);
223       match_byte (0);
224       match_byte (0);
225       match_byte (0);
226       match_byte (0);
227     }
228   else if (match_byte (2))
229     {
230       unsigned int format;
231       char *var, *vallab;
232       double value;
233
234       match_byte_assert (0x58);
235       format = get_u32 ();
236       value = get_double ();
237       var = get_string ();
238       vallab = get_string ();
239       printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
240               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
241       if (!match_byte (1) && !match_byte(2))
242         match_byte_assert (3);
243       match_byte (0);
244       match_byte (0);
245       match_byte (0);
246       match_byte (0);
247       match_byte (0);
248       match_byte (0);
249       match_byte (0);
250     }
251   else if (match_byte (4))
252     {
253       unsigned int format;
254       char *var, *vallab, *value;
255
256       match_byte_assert (0x58);
257       format = get_u32 ();
258       vallab = get_string ();
259       var = get_string ();
260       if (!match_byte(1) && !match_byte(2))
261         match_byte_assert (3);
262       value = get_string ();
263       printf ("value \"%s\" format %d(%d.%d) var \"%s\" vallab \"%s\"",
264               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
265       match_byte (0);
266       match_byte (0);
267       match_byte (0);
268       match_byte (0);
269     }
270   else if (match_byte (1))
271     {
272       unsigned int format;
273       double value;
274
275       if (match_byte (0x31))
276         {
277           if (match_u32 (1))
278             {
279               printf("(footnote %d) ", get_u32());
280               match_byte_assert (0);
281               match_byte_assert (0);
282               int subn = get_u32 ();
283               printf ("nested %d bytes", subn);
284               pos += subn;
285             }
286         }
287       else
288         match_byte_assert (0x58);
289       format = get_u32 ();
290       value = get_double ();
291       printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
292       match_byte (1);
293       match_byte (0);
294       match_byte (0);
295       match_byte (0);
296       match_byte (1);
297     }
298   else if (match_byte (0x31))
299     {
300       if (match_u32 (1))
301         {
302           printf("(footnote %d) ", get_u32());
303           match_byte_assert (0);
304           match_byte_assert (0);
305           int subn = get_u32 ();
306           printf ("nested %d bytes", subn);
307           pos += subn;
308         }
309       else
310         {
311           match_u32_assert (0);
312           match_u32_assert (0);
313           int subn = get_u32 ();
314           printf ("nested %d bytes", subn);
315           pos += subn;
316         }
317       char *base = get_string();
318       int x = get_u32();
319       printf ("\"%s\"; %d variables:\n", base, x);
320       if (match_u32(0))
321         {
322           for (int i = 0; i < x; i++)
323             {
324               dump_value (level+1);
325               putchar('\n');
326             }
327         }
328       else
329         {
330           for (int i = 0; i < x; i++)
331             {
332               int y = get_u32();
333               match_u32_assert(0);
334               for (int j = 0; j <= level; j++)
335                 printf ("    ");
336               printf("variable %d has %d values:\n", i, y);
337               for (int j = 0; j < y; j++)
338                 {
339                   if (match_byte(3))
340                     {
341                       char *a = get_string();
342                       match_byte_assert(0x58);
343                       char *b = get_string();
344                       char *c = get_string();
345                       for (int k = 0; k <= level + 1; k++)
346                         printf ("    ");
347                       printf ("\"%s\", \"%s\", \"%s\"", a, b, c);
348                       match_byte(0);
349                       match_byte(0);
350                       match_byte(0);
351                       match_byte(0);
352                       match_byte(0);
353                     }
354                   else
355                     dump_value (level+1);
356                   putchar('\n');
357                 }
358             }
359         }
360     }
361   else
362     {
363
364       match_byte_assert (0x58);
365       char *base = get_string();
366       int x = get_u32();
367       printf ("\"%s\" with %d variables:\n", base, x);
368       if (match_u32(0))
369         {
370           for (int i = 0; i < x; i++)
371             {
372               dump_value (level+1);
373               putchar('\n');
374             }
375         }
376       else
377         {
378           for (int i = 0; i < x; i++)
379             {
380               int y = get_u32();
381               match_u32_assert(0);
382               for (int j = 0; j <= level; j++)
383                 printf ("    ");
384               printf("variable %d has %d values:\n", i, y);
385               for (int j = 0; j < y; j++)
386                 {
387                   if (match_byte(3))
388                     {
389                       char *a = get_string();
390                       match_byte_assert(0x58);
391                       char *b = get_string();
392                       char *c = get_string();
393                       for (int k = 0; k <= level + 1; k++)
394                         printf ("    ");
395                       printf ("\"%s\", \"%s\", \"%s\"", a, b, c);
396                       match_byte(0);
397                       match_byte(0);
398                       match_byte(0);
399                       match_byte(0);
400                       match_byte(0);
401                     }
402                   else
403                     dump_value (level+1);
404                   putchar('\n');
405                 }
406             }
407         }
408     }
409 }
410
411 static void
412 dump_dim_value(int level)
413 {
414   for (int i = 0; i <= level; i++)
415     printf ("    ");
416
417   if (match_byte (3))
418     {
419       get_string();
420       if (match_byte (0x31))
421         {
422           match_u32 (1);
423           printf("(footnote %d) ", get_u32());
424           match_byte_assert (0);
425           match_byte_assert (0);
426           int subn = get_u32 ();
427           printf ("nested %d bytes", subn);
428           pos += subn;
429         }
430       else
431         match_byte_assert (0x58);
432       get_string();
433       printf("string \"%s\"", get_string());
434       match_byte (0);
435       match_byte_assert (1);
436       match_byte (0);
437       match_byte (0);
438       match_byte (0);
439       match_byte (1);
440     }
441   else if (match_byte (5))
442     {
443       match_byte_assert (0x58);
444       printf ("variable \"%s\"", get_string());
445       get_string();
446       match_byte_assert (2);
447     }
448   else if (match_byte (2))
449     {
450       unsigned int format;
451       char *var, *vallab;
452       double value;
453
454       match_byte_assert (0x58);
455       format = get_u32 ();
456       value = get_double ();
457       var = get_string ();
458       vallab = get_string ();
459       printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
460               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
461       if (!match_u32 (3))
462         match_u32_assert (2);
463       match_byte (0);
464     }
465   else if (match_byte (1))
466     {
467       unsigned int format;
468       double value;
469
470       match_byte_assert (0x58);
471       format = get_u32 ();
472       value = get_double ();
473       printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
474       match_byte (1);
475       match_byte (0);
476       match_byte (0);
477       match_byte (0);
478       match_byte (1);
479     }
480   else
481     {
482       int subn;
483
484       match_byte (0);
485       match_byte_assert (0x31);
486       match_u32_assert (0);
487       match_u32_assert (0);
488       subn = get_u32 ();
489       printf ("nested %d bytes", subn);
490       pos += subn;
491       printf ("; \"%s\", substitutions:", get_string());
492       int total_subs = get_u32();
493       int x = get_u32();
494       if (x)
495         {
496           total_subs = (total_subs - 1) + x;
497           match_u32_assert (0);
498         }
499       printf (" (total %d)", total_subs);
500
501       for (int i = 0; i < total_subs; i++)
502         {
503           putc ('\n', stdout);
504           dump_value (level + 1);
505         }
506     }
507 }
508
509 static void
510 dump_category(int level)
511 {
512   match_byte (0);
513   match_byte (0);
514   match_byte (0);
515   match_byte (0);
516   dump_value (level);
517
518   if (match_u32 (2))
519     get_u32 ();
520   else if (match_u32 (1))
521     {
522       match_byte (0);
523       match_byte (0);
524       match_byte (0);
525       get_u32 ();
526     }
527   else if (match_byte (1))
528     {
529       match_byte (0);
530       if (!match_u32 (2))
531         match_u32_assert (1);
532       match_byte (0);
533       get_u32();
534     }
535   else
536     {
537       match_u32_assert (0);
538       get_u32 ();
539     }
540
541   int n_categories = get_u32();
542   if (n_categories > 0)
543     printf (", %d subcategories:", n_categories);
544   printf("\n");
545   for (int i = 0; i < n_categories; i++)
546     dump_category (level + 1);
547 }
548
549 static void
550 dump_dim(void)
551 {
552   int n_categories;
553   printf("next dim\n");
554   match_byte(0);
555   if (match_byte(3))
556     {
557       get_string();
558       match_byte_assert(0x58);
559       get_string();
560       printf("string \"%s\": ", get_string());
561       match_byte(1) || match_byte(0);
562     }
563   else if (match_byte(5)) 
564     {
565       match_byte_assert(0x58);
566       printf("variable \"%s\": ", get_string());
567       get_string();
568       if (!match_byte(2))
569         match_byte_assert(3);
570     }
571   else if (match_byte(0x31))
572     {
573       int subn;
574       int total_subs = 1;
575
576       match_u32_assert (0);
577       match_u32_assert (0);
578       subn = get_u32 ();
579       printf ("nested %d bytes", subn);
580       pos += subn;
581       printf ("; \"%s\", substitutions:", get_string());
582       for (;;)
583         {
584           int n_subst = get_u32();
585           if (!n_subst)
586             break;
587           printf (" %d", n_subst);
588           total_subs *= n_subst;
589         }
590
591       for (int i = 0; i < total_subs; i++)
592         {
593           putc ('\n', stdout);
594           dump_dim_value (0);
595         }
596     }
597   else
598     {
599       int total_subs = 1;
600
601       match_byte_assert (0x58);
602       printf ("\"%s\" with substitutions:", get_string());
603       for (;;)
604         {
605           int n_subst = get_u32();
606           if (!n_subst)
607             break;
608           printf (" %d", n_subst);
609           total_subs *= n_subst;
610         }
611
612       for (int i = 0; i < total_subs; i++)
613         {
614           putc ('\n', stdout);
615           dump_dim_value (0);
616         }
617     }
618
619   /* This byte is usually 0x02 but 0x00 and 0x75 (!) have also been spotted. */
620   pos++;
621
622   if (!match_byte(0) && !match_byte(1))
623     match_byte_assert(2);
624   if (!match_u32(0))
625     match_u32_assert(2);
626   if (!match_byte(0))
627     match_byte_assert(1);
628   match_byte(0);
629   match_byte(0);
630   match_byte(0);
631   match_byte(0);
632   get_u32();
633   match_byte(0);
634   match_byte(0);
635   match_byte(0);
636   match_byte(0);
637   n_categories = get_u32();
638   printf("%d nested categories\n", n_categories);
639   for (int i = 0; i < n_categories; i++)
640     dump_category (0);
641 }
642
643 int n_dims;
644 static void
645 dump_dims(void)
646 {
647   n_dims = get_u32();
648   printf ("%u dimensions\n", n_dims);
649   for (int i = 0; i < n_dims; i++)
650     {
651       printf("\n");
652       dump_dim ();
653     }
654 }
655
656 static void
657 dump_data(void)
658 {
659 #if 1
660   int a[16];
661   for (int i = 0; i < 3 + n_dims; i++)
662     a[i] = get_u32();
663   printf ("data intro:");
664   for (int i = 0; i < 3 + n_dims; i++)
665     printf(" %d", a[i]);
666   printf("\n");
667 #else
668   fprintf (stderr,"data intro (%d dims):", n_dims);
669   for (int i = 0; i < 3+n_dims; i++)
670     fprintf (stderr," %d", get_u32());
671   fprintf(stderr,"\n");
672 #endif
673   int x = get_u32();
674   printf ("%d data values, starting at %08x\n", x, pos);
675   for (int i = 0; i < x; i++)
676     {
677       printf("%08x, index %d:\n", pos, get_u32());
678       match_u32_assert(0);
679       match_byte(0);
680       match_byte(0);
681       match_byte(0);
682       match_byte(0);
683       if (match_byte (1))
684         {
685           unsigned int format;
686           double value;
687
688           if (match_byte (0x31))
689             {
690               if (match_u32 (0))
691                 {
692                   if (match_u32 (1))
693                     get_string();
694                   else
695                     match_u32_assert (0);
696                   int subn = get_u32 ();
697                   printf ("nested %d bytes", subn);
698                   pos += subn;
699                 }
700               else if (match_u32 (1))
701                 {
702                   printf("(footnote %d) ", get_u32());
703                   match_byte_assert (0);
704                   match_byte_assert (0);
705                   int subn = get_u32 ();
706                   printf ("nested %d bytes", subn);
707                   pos += subn;
708                 }
709               else
710                 {
711                   match_u32_assert(2);
712                   printf("(special 2)");
713                   match_byte_assert(0);
714                   match_byte_assert(0);
715                   match_u32_assert(1);
716                   match_byte_assert(0);
717                   match_byte_assert(0);
718                   int subn = get_u32 ();
719                   printf ("nested %d bytes", subn);
720                   pos += subn;
721                 }
722             }
723           else
724             match_byte_assert (0x58);
725           format = get_u32 ();
726           value = get_double ();
727           printf ("    value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
728         }
729       else if (match_byte (3))
730         {
731           get_string();
732           if (match_byte (0x31))
733             {
734               if (match_u32 (0))
735                 {
736                   match_u32_assert (1);
737                   get_string ();
738                   int subn = get_u32 ();
739                   printf ("nested %d bytes", subn);
740                   pos += subn;
741                 }
742               else if (match_u32 (1))
743                 {
744                   printf("(footnote %d) ", get_u32());
745                   match_byte_assert (0);
746                   match_byte_assert (0);
747                   int subn = get_u32 ();
748                   printf ("nested %d bytes", subn);
749                   pos += subn;
750                 }
751               else if (match_u32 (2))
752                 {
753                   printf("(special 2)");
754                   match_byte_assert(0);
755                   match_byte_assert(0);
756                   match_u32_assert(1);
757                   match_byte_assert(0);
758                   match_byte_assert(0);
759                   int subn = get_u32 ();
760                   printf ("nested %d bytes", subn);
761                   pos += subn;
762                 }
763               else
764                 {
765                   match_u32_assert(3);
766                   printf("(special 3)");
767                   match_byte_assert(0);
768                   match_byte_assert(0);
769                   match_byte_assert(1);
770                   match_byte_assert(0);
771                   int subn = get_u32 ();
772                   printf ("nested %d bytes, ", subn);
773                   pos += subn;
774                   subn = get_u32 ();
775                   printf ("nested %d bytes, ", subn);
776                   pos += subn;
777                 }
778             }
779           else
780             match_byte_assert (0x58);
781           get_string();
782           printf("string \"%s\"", get_string());
783           match_byte (0);
784         }
785       else if (match_byte (2))
786         {
787           unsigned int format;
788           char *var, *vallab;
789           double value;
790
791           match_byte_assert (0x58);
792           format = get_u32 ();
793           value = get_double ();
794           var = get_string ();
795           vallab = get_string ();
796           printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
797                   value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
798           if (!match_byte (1) && !match_byte(2))
799             match_byte_assert (3);
800         }
801       else if (match_byte (4))
802         {
803           unsigned int format;
804           char *var, *vallab, *value;
805
806           match_byte_assert (0x58);
807           format = get_u32 ();
808           vallab = get_string ();
809           var = get_string ();
810           if (!match_byte(1) && !match_byte(2))
811             match_byte_assert (3);
812           value = get_string ();
813           printf ("value \"%s\" format %d(%d.%d) var \"%s\" vallab \"%s\"",
814                   value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
815         }
816       else if (match_byte (5))
817         {
818           match_byte_assert (0x58);
819           printf ("variable \"%s\"", get_string());
820           get_string();
821           if (!match_byte(1) && !match_byte(2))
822             match_byte_assert(3);
823           match_byte (0);
824           match_byte (0);
825           match_byte (0);
826           match_byte (0);
827         }
828       else if (match_byte(0x31))
829         {
830           if (match_u32 (1))
831             {
832               printf("(footnote %d) ", get_u32());
833               match_byte_assert (0);
834               match_byte_assert (0);
835               int subn = get_u32 ();
836               printf ("nested %d bytes", subn);
837               pos += subn;
838             }
839           else
840             {
841               match_u32_assert (0);
842               match_u32_assert (0);
843               int subn = get_u32 ();
844               printf ("nested %d bytes", subn);
845               pos += subn;
846             }
847           char *base = get_string();
848           int x = get_u32();
849           printf ("\"%s\"; %d variables:\n", base, x);
850           for (int i = 0; i < x; i++)
851             {
852               int y = get_u32();
853               if (!y)
854                 y = 1;
855               else
856                 match_u32_assert(0);
857               for (int j = 0; j <= 0; j++)
858                 printf ("    ");
859               printf("variable %d has %d values:\n", i, y);
860               for (int j = 0; j < y; j++)
861                 {
862                   if (match_byte (1))
863                     {
864                       unsigned int format;
865                       double value;
866
867                       if (match_byte (0x31))
868                         {
869                           if (match_u32 (0))
870                             {
871                               if (match_u32 (1))
872                                 get_string();
873                               else
874                                 match_u32_assert (0);
875                               int subn = get_u32 ();
876                               printf ("nested %d bytes", subn);
877                               pos += subn;
878                             }
879                           else if (match_u32 (1))
880                             {
881                               printf("(footnote %d) ", get_u32());
882                               match_byte_assert (0);
883                               match_byte_assert (0);
884                               int subn = get_u32 ();
885                               printf ("nested %d bytes", subn);
886                               pos += subn;
887                             }
888                           else
889                             {
890                               match_u32_assert(2);
891                               printf("(special 2)");
892                               match_byte_assert(0);
893                               match_byte_assert(0);
894                               match_u32_assert(1);
895                               match_byte_assert(0);
896                               match_byte_assert(0);
897                               int subn = get_u32 ();
898                               printf ("nested %d bytes", subn);
899                               pos += subn;
900                             }
901                         }
902                       else
903                         match_byte_assert (0x58);
904                       format = get_u32 ();
905                       value = get_double ();
906                       printf ("    value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
907                     }
908                   else if (match_byte(3))
909                     {
910                       char *a = get_string();
911                       match_byte_assert(0x58);
912                       char *b = get_string();
913                       char *c = get_string();
914                       for (int k = 0; k <= 1; k++)
915                         printf ("    ");
916                       printf ("\"%s\", \"%s\", \"%s\"", a, b, c);
917                       match_byte(0);
918                       match_byte(0);
919                       match_byte(0);
920                       match_byte(0);
921                     }
922                   else if (match_byte(5))
923                     {
924                       match_byte_assert (0x58);
925                       printf ("variable \"%s\"", get_string());
926                       get_string();
927                       if (!match_byte(1) && !match_byte(2))
928                         match_byte_assert(3);
929                       match_byte (0);
930                       match_byte (0);
931                       match_byte (0);
932                       match_byte (0);
933                     }
934                   else
935                     dump_value (0);
936                   putchar('\n');
937                 }
938             }
939         }
940       else
941         {
942           match_byte_assert (0x58);
943           char *base = get_string();
944           int x = get_u32();
945           printf ("\"%s\" with %d variables:\n", base, x);
946           for (int i = 0; i < x; i++)
947             {
948               int y = get_u32();
949               if (!y)
950                 y = 1;
951               else
952                 match_u32_assert(0);
953               for (int j = 0; j <= 0; j++)
954                 printf ("    ");
955               printf("variable %d has %d values:\n", i, y);
956               for (int j = 0; j < y; j++)
957                 {
958                   if (match_byte(3))
959                     {
960                       char *a = get_string();
961                       match_byte_assert(0x58);
962                       char *b = get_string();
963                       char *c = get_string();
964                       for (int k = 0; k <= 1; k++)
965                         printf ("    ");
966                       printf ("\"%s\", \"%s\", \"%s\"", a, b, c);
967                       match_byte(0);
968                       match_byte(0);
969                       match_byte(0);
970                       match_byte(0);
971                       match_byte(0);
972                     }
973                   else if (match_byte (1))
974                     {
975                       unsigned int format;
976                       double value;
977
978                       if (match_byte (0x31))
979                         {
980                           if (match_u32 (1))
981                             {
982                               printf("(footnote %d) ", get_u32());
983                               match_byte_assert (0);
984                               match_byte_assert (0);
985                               int subn = get_u32 ();
986                               printf ("nested %d bytes", subn);
987                               pos += subn;
988                             }
989                         }
990                       else
991                         match_byte_assert (0x58);
992                       format = get_u32 ();
993                       value = get_double ();
994                       printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
995                     }
996                   else
997                     dump_value (1);
998                   putchar('\n');
999                 }
1000             }
1001         }
1002       putchar('\n');
1003     }
1004 }
1005
1006 static void
1007 dump_title_value(int level)
1008 {
1009   for (int i = 0; i <= level; i++)
1010     printf ("    ");
1011
1012   match_byte (0);
1013   match_byte (0);
1014   match_byte (0);
1015   match_byte (0);
1016   match_byte (0);
1017   if (match_byte (3))
1018     {
1019       get_string();
1020       if (match_byte (0x31))
1021         {
1022           if (match_u32 (1))
1023             {
1024               printf("(footnote %d) ", get_u32());
1025               match_byte_assert (0);
1026               match_byte_assert (0);
1027               int subn = get_u32 ();
1028               printf ("nested %d bytes", subn);
1029               pos += subn;
1030             }
1031           else if (match_u32 (2))
1032             {
1033               printf("(special 2)");
1034               match_byte_assert(0);
1035               match_byte_assert(0);
1036               if (!match_u32(2))
1037                 match_u32_assert(1);
1038               match_byte_assert(0);
1039               match_byte_assert(0);
1040               int subn = get_u32 ();
1041               printf ("nested %d bytes", subn);
1042               pos += subn;
1043             }
1044           else
1045             {
1046               match_u32_assert(3);
1047               printf("(special 3)");
1048               match_byte_assert(0);
1049               match_byte_assert(0);
1050               match_byte_assert(1);
1051               match_byte_assert(0);
1052               int subn = get_u32 ();
1053               printf ("nested %d bytes, ", subn);
1054               pos += subn;
1055               subn = get_u32 ();
1056               printf ("nested %d bytes, ", subn);
1057               pos += subn;
1058             }
1059         }
1060       else
1061         match_byte_assert (0x58);
1062       get_string();
1063       printf("string \"%s\"", get_string());
1064       match_byte (0);
1065       match_byte (0);
1066       match_byte (0);
1067       match_byte (1);
1068       match_byte (1);
1069       match_byte (0);
1070       match_byte (0);
1071       match_byte (0);
1072       match_byte (1);
1073     }
1074   else if (match_byte (5))
1075     {
1076       if (match_byte (0x31))
1077         {
1078           if (match_u32 (1))
1079             {
1080               printf("(footnote %d) ", get_u32());
1081               match_byte_assert (0);
1082               match_byte_assert (0);
1083               int subn = get_u32 ();
1084               printf ("nested %d bytes", subn);
1085               pos += subn;
1086             }
1087         }
1088       else
1089         match_byte_assert (0x58);
1090       printf ("variable \"%s\"", get_string());
1091       get_string();
1092       if (!match_byte(1) && !match_byte(2))
1093         match_byte_assert(3);
1094     }
1095   else if (match_byte (2))
1096     {
1097       unsigned int format;
1098       char *var, *vallab;
1099       double value;
1100
1101       match_byte_assert (0x58);
1102       format = get_u32 ();
1103       value = get_double ();
1104       var = get_string ();
1105       vallab = get_string ();
1106       printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
1107               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
1108       if (!match_byte (1) && !match_byte(2))
1109         match_byte_assert (3);
1110       match_byte (0);
1111       match_byte (0);
1112       match_byte (0);
1113       match_byte (0);
1114       match_byte (0);
1115       match_byte (0);
1116       match_byte (0);
1117     }
1118   else if (match_byte (4))
1119     {
1120       unsigned int format;
1121       char *var, *vallab, *value;
1122
1123       match_byte_assert (0x58);
1124       format = get_u32 ();
1125       vallab = get_string ();
1126       var = get_string ();
1127       if (!match_byte(1) && !match_byte(2))
1128         match_byte_assert (3);
1129       value = get_string ();
1130       printf ("value \"%s\" format %d(%d.%d) var \"%s\" vallab \"%s\"",
1131               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
1132       match_byte (0);
1133       match_byte (0);
1134       match_byte (0);
1135       match_byte (0);
1136     }
1137   else if (match_byte (1))
1138     {
1139       unsigned int format;
1140       double value;
1141
1142       if (match_byte (0x31))
1143         {
1144           if (match_u32 (1))
1145             {
1146               printf("(footnote %d) ", get_u32());
1147               match_byte_assert (0);
1148               match_byte_assert (0);
1149               int subn = get_u32 ();
1150               printf ("nested %d bytes", subn);
1151               pos += subn;
1152             }
1153         }
1154       else
1155         match_byte_assert (0x58);
1156       format = get_u32 ();
1157       value = get_double ();
1158       printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
1159       match_byte (1);
1160       match_byte (0);
1161       match_byte (0);
1162       match_byte (0);
1163       match_byte (1);
1164     }
1165   else if (match_byte (0x31))
1166     {
1167       if (match_u32 (1))
1168         {
1169           printf("(footnote %d) ", get_u32());
1170           match_byte_assert (0);
1171           match_byte_assert (0);
1172           int subn = get_u32 ();
1173           printf ("nested %d bytes", subn);
1174           pos += subn;
1175         }
1176       else if (match_u32 (0))
1177         {
1178           match_u32_assert (0);
1179           int subn = get_u32 ();
1180           printf ("nested %d bytes", subn);
1181           pos += subn;
1182         }
1183       else
1184         {
1185           match_u32_assert(3);
1186           printf("(special 3)");
1187           match_byte_assert(0);
1188           match_byte_assert(0);
1189           match_byte_assert(1);
1190           match_byte_assert(0);
1191           int subn = get_u32 ();
1192           printf ("nested %d bytes, ", subn);
1193           pos += subn;
1194           subn = get_u32 ();
1195           printf ("nested %d bytes, ", subn);
1196           pos += subn;
1197         }
1198
1199       char *base = get_string();
1200       int x = get_u32();
1201       printf ("\"%s\"; %d variables:\n", base, x);
1202       for (int i = 0; i < x; i++)
1203         {
1204           int y = get_u32();
1205           if (!y)
1206             y = 1;
1207           else
1208             match_u32_assert(0);
1209           for (int j = 0; j <= level; j++)
1210             printf ("    ");
1211           printf("variable %d has %d values:\n", i, y);
1212           for (int j = 0; j < y; j++)
1213             {
1214               if (match_byte(3))
1215                 {
1216                   char *a = get_string();
1217                   match_byte_assert(0x58);
1218                   char *b = get_string();
1219                   char *c = get_string();
1220                   for (int k = 0; k <= level + 1; k++)
1221                     printf ("    ");
1222                   printf ("\"%s\", \"%s\", \"%s\"", a, b, c);
1223                   match_byte(0);
1224                 }
1225               else
1226                 dump_title_value (level+1);
1227               putchar('\n');
1228             }
1229         }
1230     }
1231   else
1232     {
1233       match_byte_assert (0x58);
1234       char *base = get_string();
1235       int x = get_u32();
1236       printf ("\"%s\" with %d variables:\n", base, x);
1237       for (int i = 0; i < x; i++)
1238         {
1239           int y = get_u32();
1240           if (!y)
1241             y = 1;
1242           else
1243             match_u32_assert(0);
1244           for (int j = 0; j <= level; j++)
1245             printf ("    ");
1246           printf("variable %d has %d values:\n", i, y);
1247           for (int j = 0; j < y; j++)
1248             {
1249               match_byte(0);
1250               if (match_byte(3))
1251                 {
1252                   char *a = get_string();
1253                   match_byte_assert(0x58);
1254                   char *b = get_string();
1255                   char *c = get_string();
1256                   for (int k = 0; k <= level + 1; k++)
1257                     printf ("    ");
1258                   printf ("\"%s\", \"%s\", \"%s\"", a, b, c);
1259                 }
1260               else
1261                 dump_title_value (level+1);
1262               putchar('\n');
1263             }
1264         }
1265     }
1266 }
1267
1268 static void
1269 dump_footnote_value(int level)
1270 {
1271   for (int i = 0; i <= level; i++)
1272     printf ("    ");
1273
1274   match_byte (0);
1275   match_byte (0);
1276   match_byte (0);
1277   match_byte (0);
1278   if (match_byte (3))
1279     {
1280       get_string();
1281       if (match_byte (0x31))
1282         {
1283           if (match_u32 (1))
1284             {
1285               printf("(footnote %d) ", get_u32());
1286               match_byte_assert (0);
1287               match_byte_assert (0);
1288               int subn = get_u32 ();
1289               printf ("nested %d bytes", subn);
1290               pos += subn;
1291             }
1292           else if (match_u32 (2))
1293             {
1294               printf("(special 2)");
1295               match_byte_assert(0);
1296               match_byte_assert(0);
1297               match_u32_assert(1);
1298               match_byte_assert(0);
1299               match_byte_assert(0);
1300               int subn = get_u32 ();
1301               printf ("nested %d bytes", subn);
1302               pos += subn;
1303             }
1304           else
1305             {
1306               match_u32_assert(3);
1307               printf("(special 3)");
1308               match_byte_assert(0);
1309               match_byte_assert(0);
1310               match_byte_assert(1);
1311               match_byte_assert(0);
1312               int subn = get_u32 ();
1313               printf ("nested %d bytes, ", subn);
1314               pos += subn;
1315               subn = get_u32 ();
1316               printf ("nested %d bytes, ", subn);
1317               pos += subn;
1318             }
1319         }
1320       else
1321         match_byte_assert (0x58);
1322       get_string();
1323       printf("string \"%s\"", get_string());
1324       match_byte (0);
1325       match_byte (0);
1326       match_byte (0);
1327       match_byte (1);
1328       match_byte (1);
1329       match_byte (0);
1330       match_byte (0);
1331       match_byte (0);
1332       match_byte (1);
1333     }
1334   else if (match_byte (5))
1335     {
1336       match_byte_assert (0x58);
1337       printf ("variable \"%s\"", get_string());
1338       get_string();
1339       if (!match_byte(1) && !match_byte(2))
1340         match_byte_assert(3);
1341       match_byte (0);
1342       match_byte (0);
1343       match_byte (0);
1344       match_byte (0);
1345     }
1346   else if (match_byte (2))
1347     {
1348       unsigned int format;
1349       char *var, *vallab;
1350       double value;
1351
1352       match_byte_assert (0x58);
1353       format = get_u32 ();
1354       value = get_double ();
1355       var = get_string ();
1356       vallab = get_string ();
1357       printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
1358               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
1359       if (!match_byte (1) && !match_byte(2))
1360         match_byte_assert (3);
1361       match_byte (0);
1362       match_byte (0);
1363       match_byte (0);
1364       match_byte (0);
1365       match_byte (0);
1366       match_byte (0);
1367       match_byte (0);
1368     }
1369   else if (match_byte (4))
1370     {
1371       unsigned int format;
1372       char *var, *vallab, *value;
1373
1374       match_byte_assert (0x58);
1375       format = get_u32 ();
1376       vallab = get_string ();
1377       var = get_string ();
1378       if (!match_byte(1) && !match_byte(2))
1379         match_byte_assert (3);
1380       value = get_string ();
1381       printf ("value \"%s\" format %d(%d.%d) var \"%s\" vallab \"%s\"",
1382               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
1383       match_byte (0);
1384       match_byte (0);
1385       match_byte (0);
1386       match_byte (0);
1387     }
1388   else if (match_byte (1))
1389     {
1390       unsigned int format;
1391       double value;
1392
1393       if (match_byte (0x31))
1394         {
1395           if (match_u32 (1))
1396             {
1397               printf("(footnote %d) ", get_u32());
1398               match_byte_assert (0);
1399               match_byte_assert (0);
1400               int subn = get_u32 ();
1401               printf ("nested %d bytes", subn);
1402               pos += subn;
1403             }
1404         }
1405       else
1406         match_byte_assert (0x58);
1407       format = get_u32 ();
1408       value = get_double ();
1409       printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
1410     }
1411   else if (match_byte (0x31))
1412     {
1413       if (match_u32 (1))
1414         {
1415           printf("(footnote %d) ", get_u32());
1416           match_byte_assert (0);
1417           match_byte_assert (0);
1418           int subn = get_u32 ();
1419           printf ("nested %d bytes", subn);
1420           pos += subn;
1421         }
1422       else
1423         {
1424           match_u32_assert (0);
1425           match_u32_assert (0);
1426           int subn = get_u32 ();
1427           printf ("nested %d bytes", subn);
1428           pos += subn;
1429         }
1430       char *base = get_string();
1431       int x = get_u32();
1432       printf ("\"%s\"; %d variables:\n", base, x);
1433       for (int i = 0; i < x; i++)
1434         {
1435           int y = get_u32();
1436           if (!y)
1437             y = 1;
1438           else
1439             match_u32_assert(0);
1440           for (int j = 0; j <= level; j++)
1441             printf ("    ");
1442           printf("variable %d has %d values:\n", i, y);
1443           for (int j = 0; j < y; j++)
1444             {
1445               if (match_byte(3))
1446                 {
1447                   char *a = get_string();
1448                   match_byte_assert(0x58);
1449                   char *b = get_string();
1450                   char *c = get_string();
1451                   for (int k = 0; k <= level + 1; k++)
1452                     printf ("    ");
1453                   printf ("\"%s\", \"%s\", \"%s\"", a, b, c);
1454                   match_byte(0);
1455                   match_byte(0);
1456                   match_byte(0);
1457                   match_byte(0);
1458                   match_byte(0);
1459                 }
1460               else
1461                 dump_footnote_value (level+1);
1462               putchar('\n');
1463             }
1464         }
1465     }
1466   else
1467     {
1468
1469       match_byte_assert (0x58);
1470       char *base = get_string();
1471       int x = get_u32();
1472       printf ("\"%s\" with %d variables:\n", base, x);
1473       for (int i = 0; i < x; i++)
1474         {
1475           int y = get_u32();
1476           if (!y)
1477             y = 1;
1478           else
1479             match_u32_assert(0);
1480           for (int j = 0; j <= level; j++)
1481             printf ("    ");
1482           printf("variable %d has %d values:\n", i, y);
1483           for (int j = 0; j < y; j++)
1484             {
1485               if (match_byte(3))
1486                 {
1487                   char *a = get_string();
1488                   match_byte_assert(0x58);
1489                   char *b = get_string();
1490                   char *c = get_string();
1491                   for (int k = 0; k <= level + 1; k++)
1492                     printf ("    ");
1493                   printf ("\"%s\", \"%s\", \"%s\"", a, b, c);
1494                   match_byte(0);
1495                   match_byte(0);
1496                   match_byte(0);
1497                   match_byte(0);
1498                   match_byte(0);
1499                 }
1500               else
1501                 dump_footnote_value (level+1);
1502               putchar('\n');
1503             }
1504         }
1505     }
1506 }
1507
1508 static void
1509 dump_title(void)
1510 {
1511   pos = 0x27;
1512   dump_title_value(0); putchar('\n'); putchar('\n');
1513   dump_title_value(0); putchar('\n'); putchar('\n');
1514   match_byte_assert(0x31);
1515   dump_title_value(0); putchar('\n'); putchar('\n');
1516   match_byte(0);
1517   match_byte_assert(0x58);
1518   if (match_byte(0x31))
1519     {
1520       dump_title_value(0); putchar('\n');
1521     }
1522   else
1523     match_byte_assert(0x58);
1524
1525
1526   int n_footnotes = get_u32();
1527   if (n_footnotes >= 20)
1528     {
1529       fprintf(stderr, "%08x: %d footnotes\n", pos - 4, n_footnotes);
1530       exit(1);
1531     }
1532
1533   printf("------\n%d footnotes\n", n_footnotes);
1534   if (n_footnotes < 20)
1535     {
1536       for (int i = 0; i < n_footnotes; i++)
1537         {
1538           printf("footnote %d:\n", i);
1539           dump_footnote_value(0);
1540           match_byte(0);
1541           match_byte(0);
1542           match_byte(0);
1543           match_byte(0);
1544           if (match_byte (1))
1545             {
1546               unsigned int format;
1547               double value;
1548
1549               if (match_byte (0x31))
1550                 {
1551                   if (match_u32 (1))
1552                     {
1553                       printf("(footnote %d) ", get_u32());
1554                       match_byte_assert (0);
1555                       match_byte_assert (0);
1556                       int subn = get_u32 ();
1557                       printf ("nested %d bytes", subn);
1558                       pos += subn;
1559                     }
1560                 }
1561               else
1562                 match_byte_assert (0x58);
1563               format = get_u32 ();
1564               value = get_double ();
1565               printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
1566               match_byte (1);
1567               match_byte (0);
1568               match_byte (0);
1569               match_byte (0);
1570               match_byte (1);
1571             }
1572           else if (match_byte (0x31))
1573             {
1574               match_byte_assert(3);
1575               get_string();
1576               match_byte_assert(0x58);
1577               match_u32_assert(0);
1578               get_string();
1579               match_byte(0);
1580             }
1581           else
1582             match_byte_assert (0x58);
1583           printf("(%d)\n", get_u32());
1584         }
1585     }
1586 }
1587
1588 int
1589 main(int argc, char *argv[])
1590 {
1591   size_t start;
1592   struct stat s;
1593
1594   if (isatty(STDIN_FILENO))
1595     {
1596       fprintf(stderr, "redirect stdin from a .bin file\n");
1597       exit(1);
1598     }
1599   if (fstat(STDIN_FILENO, &s))
1600     {
1601       perror("fstat");
1602       exit(1);
1603     }
1604   n = s.st_size;
1605   data = malloc(n);
1606   if (!data)
1607     {
1608       perror("malloc");
1609       exit(1);
1610     }
1611   if (read(STDIN_FILENO, data, n) != n)
1612     {
1613       perror("read");
1614       exit(1);
1615     }
1616
1617   if (argc > 1)
1618     {
1619       if (!strcmp(argv[1], "title0"))
1620         {
1621           pos = 0x27;
1622           if (match_byte (0x03)
1623               || (match_byte (0x05) && match_byte (0x58)))
1624             printf ("%s\n", get_string());
1625           else
1626             printf ("<unknown>\n");
1627           return 0;
1628         }
1629       else if (!strcmp(argv[1], "title"))
1630         {
1631           dump_title();
1632           exit(0);
1633         }
1634       else if (!strcmp(argv[1], "titleraw"))
1635         {
1636           const char fonts[] = "\x01\x31\x09\0\0\0SansSerif";
1637           start = 0x27;
1638           n = find(fonts, sizeof fonts - 1);
1639         }
1640       else if (!strcmp(argv[1], "fonts"))
1641         {
1642           const char fonts[] = "\x01\x31\x09\0\0\0SansSerif";
1643           const char styles[] = "\xf0\0\0\0";
1644           start = find(fonts, sizeof fonts - 1);
1645           n = find(styles, sizeof styles - 1);
1646         }
1647       else if (!strcmp(argv[1], "styles"))
1648         {
1649           const char styles[] = "\xf0\0\0\0";
1650           const char dimensions[] = "-,,,.\0";
1651           start = find(styles, sizeof styles - 1);
1652           n = find(dimensions, sizeof dimensions - 1) + sizeof dimensions - 1;
1653         }
1654       else if (!strcmp(argv[1], "dimensions") || !strcmp(argv[1], "all"))
1655         {
1656           {
1657             const char dimensions[] = "-,,,.\0";
1658             start = try_find_tail(dimensions, sizeof dimensions - 1);
1659           }
1660
1661           if (!start)
1662             {
1663               const char dimensions[] = "-,,, .\0";
1664               start = find_tail(dimensions, sizeof dimensions - 1);
1665             }
1666
1667           pos = start;
1668           dump_dims ();
1669           dump_data ();
1670           if (!strcmp(argv[1], "all"))
1671             dump_title ();
1672           exit(0);
1673         }
1674       else
1675         {
1676           fprintf (stderr, "unknown section %s\n", argv[1]);
1677           exit(1);
1678         }
1679     }
1680   else
1681     start = 0x27;
1682
1683   for (size_t i = start; i < n; )
1684     {
1685       if (i + 5 <= n
1686           && data[i]
1687           //&& !data[i + 1]
1688           && !data[i + 2]
1689           && !data[i + 3]
1690           && i + 4 + data[i] + data[i + 1] * 256 <= n
1691           && all_ascii(&data[i + 4], data[i] + data[i + 1] * 256))
1692         {
1693           fputs("\n\"", stdout);
1694           fwrite(&data[i + 4], 1, data[i] + data[i + 1] * 256, stdout);
1695           fputs("\" ", stdout);
1696
1697           i += 4 + data[i] + data[i + 1] * 256;
1698         }
1699       else if (i + 12 <= n
1700                && data[i + 1] == 40
1701                && data[i + 2] == 5
1702                && data[i + 3] == 0)
1703         {
1704           double d;
1705
1706           memcpy (&d, &data[i + 4], 8);
1707           printf ("F40.%d(%.*f)\n", data[i], data[i], d);
1708           i += 12;
1709         }
1710       else if (i + 12 <= n
1711                && data[i + 1] == 40
1712                && data[i + 2] == 31
1713                && data[i + 3] == 0)
1714         {
1715           double d;
1716
1717           memcpy (&d, &data[i + 4], 8);
1718           printf ("PCT40.%d(%.*f)\n", data[i], data[i], d);
1719           i += 12;
1720         }
1721       else if (i + 4 <= n
1722                && (data[i] && data[i] != 88 && data[i] != 0x41)
1723                && !data[i + 1]
1724                && !data[i + 2]
1725                && !data[i + 3])
1726         {
1727           printf ("i%d ", data[i]);
1728           i += 4;
1729         }
1730       else
1731         {
1732           printf("%02x ", data[i]);
1733           i++;
1734         }
1735     }
1736
1737   return 0;
1738 }