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