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