Fix lots of footnotes, 16 left.
[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               if (match_byte(3))
1250                 {
1251                   char *a = get_string();
1252                   match_byte_assert(0x58);
1253                   char *b = get_string();
1254                   char *c = get_string();
1255                   for (int k = 0; k <= level + 1; k++)
1256                     printf ("    ");
1257                   printf ("\"%s\", \"%s\", \"%s\"", a, b, c);
1258                 }
1259               else
1260                 dump_title_value (level+1);
1261               putchar('\n');
1262             }
1263         }
1264     }
1265 }
1266
1267 static void
1268 dump_title(void)
1269 {
1270   pos = 0x27;
1271   dump_title_value(0); putchar('\n'); putchar('\n');
1272   dump_title_value(0); putchar('\n'); putchar('\n');
1273   match_byte_assert(0x31);
1274   dump_title_value(0); putchar('\n'); putchar('\n');
1275   match_byte(0);
1276   match_byte_assert(0x58);
1277   if (match_byte(0x31))
1278     {
1279       dump_title_value(0); putchar('\n');
1280     }
1281   else
1282     match_byte_assert(0x58);
1283
1284
1285   int n_footnotes = get_u32();
1286   fprintf(stderr, "%d footnotes\n", n_footnotes);
1287   printf("------\n%d footnotes\n", n_footnotes);
1288   if (n_footnotes < 10)
1289     {
1290       for (int i = 0; i < n_footnotes; i++)
1291         {
1292           printf("footnote %d:\n", i);
1293           dump_value(0);
1294           if (match_byte (0x31))
1295             {
1296               match_byte_assert(3);
1297               get_string();
1298               match_byte_assert(0x58);
1299               match_u32_assert(0);
1300               get_string();
1301               match_byte_assert(0);
1302             }
1303           else
1304             match_byte_assert (0x58);
1305           printf("(%d)\n", get_u32());
1306         }
1307     }
1308 }
1309
1310 int
1311 main(int argc, char *argv[])
1312 {
1313   size_t start;
1314   struct stat s;
1315
1316   if (isatty(STDIN_FILENO))
1317     {
1318       fprintf(stderr, "redirect stdin from a .bin file\n");
1319       exit(1);
1320     }
1321   if (fstat(STDIN_FILENO, &s))
1322     {
1323       perror("fstat");
1324       exit(1);
1325     }
1326   n = s.st_size;
1327   data = malloc(n);
1328   if (!data)
1329     {
1330       perror("malloc");
1331       exit(1);
1332     }
1333   if (read(STDIN_FILENO, data, n) != n)
1334     {
1335       perror("read");
1336       exit(1);
1337     }
1338
1339   if (argc > 1)
1340     {
1341       if (!strcmp(argv[1], "title0"))
1342         {
1343           pos = 0x27;
1344           if (match_byte (0x03)
1345               || (match_byte (0x05) && match_byte (0x58)))
1346             printf ("%s\n", get_string());
1347           else
1348             printf ("<unknown>\n");
1349           return 0;
1350         }
1351       else if (!strcmp(argv[1], "title"))
1352         {
1353           dump_title();
1354           exit(0);
1355         }
1356       else if (!strcmp(argv[1], "titleraw"))
1357         {
1358           const char fonts[] = "\x01\x31\x09\0\0\0SansSerif";
1359           start = 0x27;
1360           n = find(fonts, sizeof fonts - 1);
1361         }
1362       else if (!strcmp(argv[1], "fonts"))
1363         {
1364           const char fonts[] = "\x01\x31\x09\0\0\0SansSerif";
1365           const char styles[] = "\xf0\0\0\0";
1366           start = find(fonts, sizeof fonts - 1);
1367           n = find(styles, sizeof styles - 1);
1368         }
1369       else if (!strcmp(argv[1], "styles"))
1370         {
1371           const char styles[] = "\xf0\0\0\0";
1372           const char dimensions[] = "-,,,.\0";
1373           start = find(styles, sizeof styles - 1);
1374           n = find(dimensions, sizeof dimensions - 1) + sizeof dimensions - 1;
1375         }
1376       else if (!strcmp(argv[1], "dimensions") || !strcmp(argv[1], "all"))
1377         {
1378           {
1379             const char dimensions[] = "-,,,.\0";
1380             start = try_find_tail(dimensions, sizeof dimensions - 1);
1381           }
1382
1383           if (!start)
1384             {
1385               const char dimensions[] = "-,,, .\0";
1386               start = find_tail(dimensions, sizeof dimensions - 1);
1387             }
1388
1389           pos = start;
1390           dump_dims ();
1391           dump_data ();
1392           if (!strcmp(argv[1], "all"))
1393             dump_title ();
1394           exit(0);
1395         }
1396       else
1397         {
1398           fprintf (stderr, "unknown section %s\n", argv[1]);
1399           exit(1);
1400         }
1401     }
1402   else
1403     start = 0x27;
1404
1405   for (size_t i = start; i < n; )
1406     {
1407       if (i + 5 <= n
1408           && data[i]
1409           //&& !data[i + 1]
1410           && !data[i + 2]
1411           && !data[i + 3]
1412           && i + 4 + data[i] + data[i + 1] * 256 <= n
1413           && all_ascii(&data[i + 4], data[i] + data[i + 1] * 256))
1414         {
1415           fputs("\n\"", stdout);
1416           fwrite(&data[i + 4], 1, data[i] + data[i + 1] * 256, stdout);
1417           fputs("\" ", stdout);
1418
1419           i += 4 + data[i] + data[i + 1] * 256;
1420         }
1421       else if (i + 12 <= n
1422                && data[i + 1] == 40
1423                && data[i + 2] == 5
1424                && data[i + 3] == 0)
1425         {
1426           double d;
1427
1428           memcpy (&d, &data[i + 4], 8);
1429           printf ("F40.%d(%.*f)\n", data[i], data[i], d);
1430           i += 12;
1431         }
1432       else if (i + 12 <= n
1433                && data[i + 1] == 40
1434                && data[i + 2] == 31
1435                && data[i + 3] == 0)
1436         {
1437           double d;
1438
1439           memcpy (&d, &data[i + 4], 8);
1440           printf ("PCT40.%d(%.*f)\n", data[i], data[i], d);
1441           i += 12;
1442         }
1443       else if (i + 4 <= n
1444                && (data[i] && data[i] != 88 && data[i] != 0x41)
1445                && !data[i + 1]
1446                && !data[i + 2]
1447                && !data[i + 3])
1448         {
1449           printf ("i%d ", data[i]);
1450           i += 4;
1451         }
1452       else
1453         {
1454           printf("%02x ", data[i]);
1455           i++;
1456         }
1457     }
1458
1459   return 0;
1460 }