Make dump_title_value() closer to 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                   if (match_byte(3))
323                     {
324                       char *a = get_string();
325                       match_byte_assert(0x58);
326                       char *b = get_string();
327                       char *c = get_string();
328                       for (int k = 0; k <= level + 1; k++)
329                         printf ("    ");
330                       printf ("\"%s\", \"%s\", \"%s\"", a, b, c);
331                       match_byte(0);
332                       match_byte(0);
333                       match_byte(0);
334                       match_byte(0);
335                       match_byte(0);
336                     }
337                   else
338                     dump_value (level+1);
339                   putchar('\n');
340                 }
341             }
342         }
343     }
344 }
345
346 static void
347 dump_dim_value(int level)
348 {
349   for (int i = 0; i <= level; i++)
350     printf ("    ");
351
352   if (match_byte (3))
353     {
354       get_string();
355       dump_value_31();
356       get_string();
357       printf("string \"%s\"", get_string());
358       match_byte (0);
359       match_byte (1);
360     }
361   else if (match_byte (5))
362     {
363       match_byte_assert (0x58);
364       printf ("variable \"%s\"", get_string());
365       get_string();
366       if (!match_byte (2))
367         match_byte_assert(3);
368     }
369   else if (match_byte (2))
370     {
371       unsigned int format;
372       char *var, *vallab;
373       double value;
374
375       match_byte_assert (0x58);
376       format = get_u32 ();
377       value = get_double ();
378       var = get_string ();
379       vallab = get_string ();
380       printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
381               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
382       if (!match_u32 (3))
383         match_u32_assert (2);
384       match_byte (0);
385     }
386   else if (match_byte (1))
387     {
388       unsigned int format;
389       double value;
390
391       match_byte_assert (0x58);
392       format = get_u32 ();
393       value = get_double ();
394       printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
395       match_byte (1);
396       match_byte (0);
397       match_byte (0);
398       match_byte (0);
399       match_byte (1);
400     }
401   else
402     {
403       match_byte (0);
404       dump_value_31();
405       printf ("; \"%s\", substitutions:", get_string());
406       int total_subs = get_u32();
407       int x = get_u32();
408       if (x)
409         {
410           total_subs = (total_subs - 1) + x;
411           match_u32_assert (0);
412         }
413       printf (" (total %d)", total_subs);
414
415       for (int i = 0; i < total_subs; i++)
416         {
417           putc ('\n', stdout);
418           dump_value (level + 1);
419         }
420     }
421 }
422
423 static void
424 dump_category(int level)
425 {
426   match_byte (0);
427   match_byte (0);
428   match_byte (0);
429   match_byte (0);
430   dump_value (level);
431
432   if (match_u32 (2))
433     get_u32 ();
434   else if (match_u32 (1))
435     {
436       match_byte (0);
437       match_byte (0);
438       match_byte (0);
439       get_u32 ();
440     }
441   else if (match_byte (1))
442     {
443       match_byte (0);
444       if (!match_u32 (2))
445         match_u32_assert (1);
446       match_byte (0);
447       get_u32();
448     }
449   else
450     {
451       match_u32_assert (0);
452       get_u32 ();
453     }
454
455   int n_categories = get_u32();
456   if (n_categories > 0)
457     printf (", %d subcategories:", n_categories);
458   printf("\n");
459   for (int i = 0; i < n_categories; i++)
460     dump_category (level + 1);
461 }
462
463 static void
464 dump_dim(void)
465 {
466   int n_categories;
467   printf("next dim\n");
468   match_byte(0);
469   dump_dim_value(0);
470
471   /* This byte is usually 0x02 but 0x00 and 0x75 (!) have also been spotted. */
472   pos++;
473
474   if (!match_byte(0) && !match_byte(1))
475     match_byte_assert(2);
476   if (!match_u32(0))
477     match_u32_assert(2);
478   if (!match_byte(0))
479     match_byte_assert(1);
480   match_byte(0);
481   match_byte(0);
482   match_byte(0);
483   match_byte(0);
484   get_u32();
485   match_byte(0);
486   match_byte(0);
487   match_byte(0);
488   match_byte(0);
489   n_categories = get_u32();
490   printf("%d nested categories\n", n_categories);
491   for (int i = 0; i < n_categories; i++)
492     dump_category (0);
493 }
494
495 int n_dims;
496 static void
497 dump_dims(void)
498 {
499   n_dims = get_u32();
500   printf ("%u dimensions\n", n_dims);
501   for (int i = 0; i < n_dims; i++)
502     {
503       printf("\n");
504       dump_dim ();
505     }
506 }
507
508 static void
509 dump_data_value(void)
510 {
511   match_byte(0);
512   match_byte(0);
513   match_byte(0);
514   match_byte(0);
515   if (match_byte (1))
516     {
517       unsigned int format;
518       double value;
519
520       dump_value_31();
521       format = get_u32 ();
522       value = get_double ();
523       printf ("    value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
524     }
525   else if (match_byte (3))
526     {
527       get_string();
528       dump_value_31();
529       get_string();
530       printf("string \"%s\"", get_string());
531       match_byte (0);
532     }
533   else if (match_byte (2))
534     {
535       unsigned int format;
536       char *var, *vallab;
537       double value;
538
539       match_byte_assert (0x58);
540       format = get_u32 ();
541       value = get_double ();
542       var = get_string ();
543       vallab = get_string ();
544       printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
545               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
546       if (!match_byte (1) && !match_byte(2))
547         match_byte_assert (3);
548     }
549   else if (match_byte (4))
550     {
551       unsigned int format;
552       char *var, *vallab, *value;
553
554       match_byte_assert (0x58);
555       format = get_u32 ();
556       vallab = get_string ();
557       var = get_string ();
558       if (!match_byte(1) && !match_byte(2))
559         match_byte_assert (3);
560       value = get_string ();
561       printf ("value \"%s\" format %d(%d.%d) var \"%s\" vallab \"%s\"",
562               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
563     }
564   else if (match_byte (5))
565     {
566       match_byte_assert (0x58);
567       printf ("variable \"%s\"", get_string());
568       get_string();
569       if (!match_byte(1) && !match_byte(2))
570         match_byte_assert(3);
571       match_byte (0);
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       match_byte (0);
792       match_byte (0);
793       match_byte (0);
794       match_byte (0);
795     }
796   else if (match_byte (4))
797     {
798       unsigned int format;
799       char *var, *vallab, *value;
800
801       match_byte_assert (0x58);
802       format = get_u32 ();
803       vallab = get_string ();
804       var = get_string ();
805       if (!match_byte(1) && !match_byte(2))
806         match_byte_assert (3);
807       value = get_string ();
808       printf ("value \"%s\" format %d(%d.%d) var \"%s\" vallab \"%s\"",
809               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
810       match_byte (0);
811       match_byte (0);
812       match_byte (0);
813       match_byte (0);
814     }
815   else if (match_byte (1))
816     {
817       unsigned int format;
818       double value;
819
820       dump_value_31();
821       format = get_u32 ();
822       value = get_double ();
823       printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
824     }
825   else
826     {
827       dump_value_31();
828       char *base = get_string();
829       int x = get_u32();
830       printf ("\"%s\"; %d variables:\n", base, x);
831       for (int i = 0; i < x; i++)
832         {
833           int y = get_u32();
834           if (!y)
835             y = 1;
836           else
837             match_u32_assert(0);
838           for (int j = 0; j <= level; j++)
839             printf ("    ");
840           printf("variable %d has %d values:\n", i, y);
841           for (int j = 0; j < y; j++)
842             {
843               if (match_byte(3))
844                 {
845                   char *a = get_string();
846                   match_byte_assert(0x58);
847                   char *b = get_string();
848                   char *c = get_string();
849                   for (int k = 0; k <= level + 1; k++)
850                     printf ("    ");
851                   printf ("\"%s\", \"%s\", \"%s\"", a, b, c);
852                   if (!match_byte(1))
853                     match_byte_assert(0);
854                 }
855               else
856                 dump_footnote_value (level+1);
857               putchar('\n');
858             }
859         }
860     }
861 }
862
863 static void
864 dump_title(void)
865 {
866   pos = 0x27;
867   dump_title_value(0); putchar('\n');
868   dump_title_value(0); putchar('\n');
869   match_byte_assert(0x31);
870   dump_title_value(0); putchar('\n');
871   match_byte(0);
872   match_byte_assert(0x58);
873   if (match_byte(0x31))
874     {
875       dump_footnote_value(0); putchar('\n');
876     }
877   else
878     match_byte_assert(0x58);
879
880
881   int n_footnotes = get_u32();
882   if (n_footnotes >= 20)
883     {
884       fprintf(stderr, "%08x: %d footnotes\n", pos - 4, n_footnotes);
885       exit(1);
886     }
887
888   printf("------\n%d footnotes\n", n_footnotes);
889   if (n_footnotes < 20)
890     {
891       for (int i = 0; i < n_footnotes; i++)
892         {
893           printf("footnote %d:\n", i);
894           dump_footnote_value(0);
895           match_byte(0);
896           match_byte(0);
897           match_byte(0);
898           match_byte(0);
899           if (match_byte (0x31))
900             {
901               /* Custom footnote marker string. */
902               match_byte_assert(3);
903               get_string();
904               match_byte_assert(0x58);
905               match_u32_assert(0);
906               get_string();
907             }
908           else
909             match_byte_assert (0x58);
910           printf("(%d)\n", get_u32());
911         }
912     }
913 }
914
915 static int
916 find_dimensions(void)
917 {
918   {
919     const char dimensions[] = "-,,,.\0";
920     int x = try_find_tail(dimensions, sizeof dimensions - 1);
921     if (x)
922       return x;
923   }
924
925   const char dimensions[] = "-,,, .\0";
926   return find_tail(dimensions, sizeof dimensions - 1);
927 }
928
929 static void
930 dump_fonts(void)
931 {
932   printf("fonts: offset=%08x\n", pos);
933   match_byte(0);
934   for (int i = 1; i <= 8; i++)
935     {
936       printf("%08x: font %d, ", pos, i);
937       match_byte_assert(i);
938       match_byte_assert(0x31);
939       printf("%s, ", get_string());
940       match_byte_assert(0);
941       match_byte_assert(0);
942       if (!match_byte(0x40) && !match_byte(0x20) && !match_byte(0x80) && !match_byte(0x10))
943         match_byte_assert(0x50);
944       if (!match_byte(0x41))
945         match_byte_assert(0x51);
946       pos += 13;
947       printf ("%s, ", get_string());
948       printf ("%s, ", get_string());
949       match_u32_assert(0);
950       match_u32_assert(0);
951       pos++;
952       get_u32();
953       get_u32();
954       get_u32();
955       get_u32();
956       putchar('\n');
957     }
958
959   match_u32_assert(240);
960   pos += 240;
961
962   match_u32_assert(18);
963   pos += 18;
964
965   if (match_u32(117))
966     pos += 117;
967   else
968     {
969       match_u32_assert(142);
970       pos += 142;
971     }
972
973   int count = get_u32();
974   pos += 4 * count;
975
976   char *encoding = get_string();
977   printf("encoding=%s\n", encoding);
978
979   if (!match_u32(0))
980     match_u32_assert(UINT32_MAX);
981   if (!match_byte(0))
982     match_byte_assert(1);
983   match_byte_assert(0);
984   if (!match_byte(0))
985     match_byte_assert(1);
986   if (!match_byte(0x99) && !match_byte(0x98))
987     match_byte_assert(0x97);
988   match_byte_assert(7);
989   match_byte_assert(0);
990   match_byte_assert(0);
991   if (match_byte('.'))
992     match_byte_assert(',');
993   else
994     {
995       match_byte_assert(',');
996       if (!match_byte('.'))
997         match_byte_assert(' ');
998     }
999   match_u32_assert(5);
1000   for (int i = 0; i < 5; i++)
1001     get_string();
1002   pos += get_u32();
1003   if (pos != find_dimensions())
1004     fprintf (stderr, "%08x / %08x\n", pos, find_dimensions());
1005 }
1006
1007 int
1008 main(int argc, char *argv[])
1009 {
1010   size_t start;
1011   struct stat s;
1012
1013   if (isatty(STDIN_FILENO))
1014     {
1015       fprintf(stderr, "redirect stdin from a .bin file\n");
1016       exit(1);
1017     }
1018   if (fstat(STDIN_FILENO, &s))
1019     {
1020       perror("fstat");
1021       exit(1);
1022     }
1023   n = s.st_size;
1024   data = malloc(n);
1025   if (!data)
1026     {
1027       perror("malloc");
1028       exit(1);
1029     }
1030   if (read(STDIN_FILENO, data, n) != n)
1031     {
1032       perror("read");
1033       exit(1);
1034     }
1035
1036   if (argc > 1)
1037     {
1038       if (!strcmp(argv[1], "title0"))
1039         {
1040           pos = 0x27;
1041           if (match_byte (0x03)
1042               || (match_byte (0x05) && match_byte (0x58)))
1043             printf ("%s\n", get_string());
1044           else
1045             printf ("<unknown>\n");
1046           return 0;
1047         }
1048       else if (!strcmp(argv[1], "title"))
1049         {
1050           dump_title();
1051           exit(0);
1052         }
1053       else if (!strcmp(argv[1], "titleraw"))
1054         {
1055           const char fonts[] = "\x01\x31\x09\0\0\0SansSerif";
1056           start = 0x27;
1057           n = find(fonts, sizeof fonts - 1);
1058         }
1059       else if (!strcmp(argv[1], "fonts"))
1060         {
1061           const char fonts[] = "\x01\x31\x09\0\0\0SansSerif";
1062           const char styles[] = "\xf0\0\0\0";
1063           start = find(fonts, sizeof fonts - 1);
1064           n = find(styles, sizeof styles - 1);
1065         }
1066       else if (!strcmp(argv[1], "styles"))
1067         {
1068           const char styles[] = "\xf0\0\0\0";
1069           const char dimensions[] = "-,,,.\0";
1070           start = find(styles, sizeof styles - 1);
1071           n = find(dimensions, sizeof dimensions - 1) + sizeof dimensions - 1;
1072         }
1073       else if (!strcmp(argv[1], "dimensions") || !strcmp(argv[1], "all"))
1074         {
1075           pos = 0;
1076           match_byte_assert(1);
1077           match_byte_assert(0);
1078           match_u32_assert(3);
1079           match_byte_assert(1);
1080           if (!match_byte(0))
1081             match_byte_assert(1);
1082           match_byte_assert(0);
1083           match_byte_assert(0);
1084           if (!match_byte(0))
1085             match_byte_assert(1);
1086           pos++;
1087           match_byte_assert(0);
1088           match_byte_assert(0);
1089           match_byte_assert(0);
1090           dump_title ();
1091           dump_fonts();
1092           dump_dims ();
1093           printf("\n\ndata:\n");
1094           dump_data ();
1095           if (pos == n - 1)
1096             match_byte_assert (1);
1097           if (pos != n)
1098             {
1099               fprintf (stderr, "%x / %x\n", pos, n);
1100               exit(1);
1101             }
1102           exit(0);
1103         }
1104       else
1105         {
1106           fprintf (stderr, "unknown section %s\n", argv[1]);
1107           exit(1);
1108         }
1109     }
1110   else
1111     start = 0x27;
1112
1113   for (size_t i = start; i < n; )
1114     {
1115       if (i + 5 <= n
1116           && data[i]
1117           //&& !data[i + 1]
1118           && !data[i + 2]
1119           && !data[i + 3]
1120           && i + 4 + data[i] + data[i + 1] * 256 <= n
1121           && all_ascii(&data[i + 4], data[i] + data[i + 1] * 256))
1122         {
1123           fputs("\n\"", stdout);
1124           fwrite(&data[i + 4], 1, data[i] + data[i + 1] * 256, stdout);
1125           fputs("\" ", stdout);
1126
1127           i += 4 + data[i] + data[i + 1] * 256;
1128         }
1129       else if (i + 12 <= n
1130                && data[i + 1] == 40
1131                && data[i + 2] == 5
1132                && data[i + 3] == 0)
1133         {
1134           double d;
1135
1136           memcpy (&d, &data[i + 4], 8);
1137           printf ("F40.%d(%.*f)\n", data[i], data[i], d);
1138           i += 12;
1139         }
1140       else if (i + 12 <= n
1141                && data[i + 1] == 40
1142                && data[i + 2] == 31
1143                && data[i + 3] == 0)
1144         {
1145           double d;
1146
1147           memcpy (&d, &data[i + 4], 8);
1148           printf ("PCT40.%d(%.*f)\n", data[i], data[i], d);
1149           i += 12;
1150         }
1151       else if (i + 4 <= n
1152                && (data[i] && data[i] != 88 && data[i] != 0x41)
1153                && !data[i + 1]
1154                && !data[i + 2]
1155                && !data[i + 3])
1156         {
1157           printf ("i%d ", data[i]);
1158           i += 4;
1159         }
1160       else
1161         {
1162           printf("%02x ", data[i]);
1163           i++;
1164         }
1165     }
1166
1167   return 0;
1168 }