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