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