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