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