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