966080b00cc159fde898c4e1f0b4cca58e6d565c
[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(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       char *s1 = 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       char *s2 = get_string();
205       char *s3 = get_string();
206       if (strcmp(s1, s3))
207         printf("strings \"%s\", \"%s\" and \"%s\"", s1, s2, s3);
208       else
209         printf("string \"%s\" and \"%s\"", s1, s2);
210       match_byte (0);
211       match_byte (0);
212       match_byte (0);
213       match_byte (1);
214       match_byte (1);
215       match_byte (0);
216       match_byte (0);
217       match_byte (0);
218       match_byte (1);
219     }
220   else if (match_byte (5))
221     {
222       match_byte_assert (0x58);
223       printf ("variable \"%s\"", get_string());
224       get_string();
225       if (!match_byte(1) && !match_byte(2))
226         match_byte_assert(3);
227       match_byte (0);
228       match_byte (0);
229       match_byte (0);
230       match_byte (0);
231     }
232   else if (match_byte (2))
233     {
234       unsigned int format;
235       char *var, *vallab;
236       double value;
237
238       match_byte_assert (0x58);
239       format = get_u32 ();
240       value = get_double ();
241       var = get_string ();
242       vallab = get_string ();
243       printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
244               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
245       if (!match_byte (1) && !match_byte(2))
246         match_byte_assert (3);
247       match_byte (0);
248       match_byte (0);
249       match_byte (0);
250       match_byte (0);
251       match_byte (0);
252       match_byte (0);
253       match_byte (0);
254     }
255   else if (match_byte (4))
256     {
257       unsigned int format;
258       char *var, *vallab, *value;
259
260       match_byte_assert (0x58);
261       format = get_u32 ();
262       vallab = get_string ();
263       var = get_string ();
264       if (!match_byte(1) && !match_byte(2))
265         match_byte_assert (3);
266       value = get_string ();
267       printf ("value \"%s\" format %d(%d.%d) var \"%s\" vallab \"%s\"",
268               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
269       match_byte (0);
270       match_byte (0);
271       match_byte (0);
272       match_byte (0);
273     }
274   else if (match_byte (1))
275     {
276       unsigned int format;
277       double value;
278
279       if (match_byte (0x31))
280         {
281           if (match_u32 (1))
282             {
283               printf("(footnote %d) ", get_u32());
284               match_byte_assert (0);
285               match_byte_assert (0);
286               int subn = get_u32 ();
287               printf ("nested %d bytes", subn);
288               pos += subn;
289             }
290         }
291       else
292         match_byte_assert (0x58);
293       format = get_u32 ();
294       value = get_double ();
295       printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
296       match_byte (1);
297       match_byte (0);
298       match_byte (0);
299       match_byte (0);
300       match_byte (1);
301     }
302   else if (match_byte (0x31))
303     {
304       if (match_u32 (1))
305         {
306           printf("(footnote %d) ", get_u32());
307           match_byte_assert (0);
308           match_byte_assert (0);
309           int subn = get_u32 ();
310           printf ("nested %d bytes", subn);
311           pos += subn;
312         }
313       else
314         {
315           match_u32_assert (0);
316           match_u32_assert (0);
317           int subn = get_u32 ();
318           printf ("nested %d bytes", subn);
319           pos += subn;
320         }
321       char *base = get_string();
322       int x = get_u32();
323       printf ("\"%s\"; %d variables:\n", base, x);
324       if (match_u32(0))
325         {
326           for (int i = 0; i < x; i++)
327             {
328               dump_value (level+1);
329               putchar('\n');
330             }
331         }
332       else
333         {
334           for (int i = 0; i < x; i++)
335             {
336               int y = get_u32();
337               match_u32_assert(0);
338               for (int j = 0; j <= level; j++)
339                 printf ("    ");
340               printf("variable %d has %d values:\n", i, y);
341               for (int j = 0; j < y; j++)
342                 {
343                   if (match_byte(3))
344                     {
345                       char *a = get_string();
346                       match_byte_assert(0x58);
347                       char *b = get_string();
348                       char *c = get_string();
349                       for (int k = 0; k <= level + 1; k++)
350                         printf ("    ");
351                       printf ("\"%s\", \"%s\", \"%s\"", a, b, c);
352                       match_byte(0);
353                       match_byte(0);
354                       match_byte(0);
355                       match_byte(0);
356                       match_byte(0);
357                     }
358                   else
359                     dump_value (level+1);
360                   putchar('\n');
361                 }
362             }
363         }
364     }
365   else
366     {
367
368       match_byte_assert (0x58);
369       char *base = get_string();
370       int x = get_u32();
371       printf ("\"%s\" with %d variables:\n", base, x);
372       if (match_u32(0))
373         {
374           for (int i = 0; i < x; i++)
375             {
376               dump_value (level+1);
377               putchar('\n');
378             }
379         }
380       else
381         {
382           for (int i = 0; i < x; i++)
383             {
384               int y = get_u32();
385               match_u32_assert(0);
386               for (int j = 0; j <= level; j++)
387                 printf ("    ");
388               printf("variable %d has %d values:\n", i, y);
389               for (int j = 0; j < y; j++)
390                 {
391                   if (match_byte(3))
392                     {
393                       char *a = get_string();
394                       match_byte_assert(0x58);
395                       char *b = get_string();
396                       char *c = get_string();
397                       for (int k = 0; k <= level + 1; k++)
398                         printf ("    ");
399                       printf ("\"%s\", \"%s\", \"%s\"", a, b, c);
400                       match_byte(0);
401                       match_byte(0);
402                       match_byte(0);
403                       match_byte(0);
404                       match_byte(0);
405                     }
406                   else
407                     dump_value (level+1);
408                   putchar('\n');
409                 }
410             }
411         }
412     }
413 }
414
415 static void
416 dump_dim_value(int level)
417 {
418   for (int i = 0; i <= level; i++)
419     printf ("    ");
420
421   if (match_byte (3))
422     {
423       get_string();
424       if (match_byte (0x31))
425         {
426           match_u32 (1);
427           printf("(footnote %d) ", get_u32());
428           match_byte_assert (0);
429           match_byte_assert (0);
430           int subn = get_u32 ();
431           printf ("nested %d bytes", subn);
432           pos += subn;
433         }
434       else
435         match_byte_assert (0x58);
436       get_string();
437       printf("string \"%s\"", get_string());
438       match_byte (0);
439       match_byte_assert (1);
440       match_byte (0);
441       match_byte (0);
442       match_byte (0);
443       match_byte (1);
444     }
445   else if (match_byte (5))
446     {
447       match_byte_assert (0x58);
448       printf ("variable \"%s\"", get_string());
449       get_string();
450       match_byte_assert (2);
451     }
452   else if (match_byte (2))
453     {
454       unsigned int format;
455       char *var, *vallab;
456       double value;
457
458       match_byte_assert (0x58);
459       format = get_u32 ();
460       value = get_double ();
461       var = get_string ();
462       vallab = get_string ();
463       printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
464               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
465       if (!match_u32 (3))
466         match_u32_assert (2);
467       match_byte (0);
468     }
469   else if (match_byte (1))
470     {
471       unsigned int format;
472       double value;
473
474       match_byte_assert (0x58);
475       format = get_u32 ();
476       value = get_double ();
477       printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
478       match_byte (1);
479       match_byte (0);
480       match_byte (0);
481       match_byte (0);
482       match_byte (1);
483     }
484   else
485     {
486       int subn;
487
488       match_byte (0);
489       match_byte_assert (0x31);
490       match_u32_assert (0);
491       match_u32_assert (0);
492       subn = get_u32 ();
493       printf ("nested %d bytes", subn);
494       pos += subn;
495       printf ("; \"%s\", substitutions:", get_string());
496       int total_subs = get_u32();
497       int x = get_u32();
498       if (x)
499         {
500           total_subs = (total_subs - 1) + x;
501           match_u32_assert (0);
502         }
503       printf (" (total %d)", total_subs);
504
505       for (int i = 0; i < total_subs; i++)
506         {
507           putc ('\n', stdout);
508           dump_value (level + 1);
509         }
510     }
511 }
512
513 static void
514 dump_category(int level)
515 {
516   match_byte (0);
517   match_byte (0);
518   match_byte (0);
519   match_byte (0);
520   dump_value (level);
521
522   if (match_u32 (2))
523     get_u32 ();
524   else if (match_u32 (1))
525     {
526       match_byte (0);
527       match_byte (0);
528       match_byte (0);
529       get_u32 ();
530     }
531   else if (match_byte (1))
532     {
533       match_byte (0);
534       if (!match_u32 (2))
535         match_u32_assert (1);
536       match_byte (0);
537       get_u32();
538     }
539   else
540     {
541       match_u32_assert (0);
542       get_u32 ();
543     }
544
545   int n_categories = get_u32();
546   if (n_categories > 0)
547     printf (", %d subcategories:", n_categories);
548   printf("\n");
549   for (int i = 0; i < n_categories; i++)
550     dump_category (level + 1);
551 }
552
553 static void
554 dump_dim(void)
555 {
556   int n_categories;
557   printf("next dim\n");
558   match_byte(0);
559   if (match_byte(3))
560     {
561       get_string();
562       match_byte_assert(0x58);
563       get_string();
564       printf("string \"%s\": ", get_string());
565       match_byte(1) || match_byte(0);
566     }
567   else if (match_byte(5)) 
568     {
569       match_byte_assert(0x58);
570       printf("variable \"%s\": ", get_string());
571       get_string();
572       if (!match_byte(2))
573         match_byte_assert(3);
574     }
575   else if (match_byte(0x31))
576     {
577       int subn;
578       int total_subs = 1;
579
580       match_u32_assert (0);
581       match_u32_assert (0);
582       subn = get_u32 ();
583       printf ("nested %d bytes", subn);
584       pos += subn;
585       printf ("; \"%s\", substitutions:", get_string());
586       for (;;)
587         {
588           int n_subst = get_u32();
589           if (!n_subst)
590             break;
591           printf (" %d", n_subst);
592           total_subs *= n_subst;
593         }
594
595       for (int i = 0; i < total_subs; i++)
596         {
597           putc ('\n', stdout);
598           dump_dim_value (0);
599         }
600     }
601   else
602     {
603       int total_subs = 1;
604
605       match_byte_assert (0x58);
606       printf ("\"%s\" with substitutions:", get_string());
607       for (;;)
608         {
609           int n_subst = get_u32();
610           if (!n_subst)
611             break;
612           printf (" %d", n_subst);
613           total_subs *= n_subst;
614         }
615
616       for (int i = 0; i < total_subs; i++)
617         {
618           putc ('\n', stdout);
619           dump_dim_value (0);
620         }
621     }
622
623   /* This byte is usually 0x02 but 0x00 and 0x75 (!) have also been spotted. */
624   pos++;
625
626   if (!match_byte(0) && !match_byte(1))
627     match_byte_assert(2);
628   if (!match_u32(0))
629     match_u32_assert(2);
630   if (!match_byte(0))
631     match_byte_assert(1);
632   match_byte(0);
633   match_byte(0);
634   match_byte(0);
635   match_byte(0);
636   get_u32();
637   match_byte(0);
638   match_byte(0);
639   match_byte(0);
640   match_byte(0);
641   n_categories = get_u32();
642   printf("%d nested categories\n", n_categories);
643   for (int i = 0; i < n_categories; i++)
644     dump_category (0);
645 }
646
647 int n_dims;
648 static void
649 dump_dims(void)
650 {
651   n_dims = get_u32();
652   printf ("%u dimensions\n", n_dims);
653   for (int i = 0; i < n_dims; i++)
654     {
655       printf("\n");
656       dump_dim ();
657     }
658 }
659
660 static void
661 dump_data_value_31(void)
662 {
663   if (match_byte (0x31))
664     {
665       if (match_u32 (0))
666         {
667           if (match_u32 (1))
668             get_string();
669           else
670             match_u32_assert (0);
671           int subn = get_u32 ();
672           printf ("nested %d bytes", subn);
673           pos += subn;
674         }
675       else if (match_u32 (1))
676         {
677           printf("(footnote %d) ", get_u32());
678           match_byte_assert (0);
679           match_byte_assert (0);
680           int subn = get_u32 ();
681           printf ("nested %d bytes", subn);
682           pos += subn;
683         }
684       else if (match_u32 (2))
685         {
686           printf("(special 2)");
687           match_byte_assert(0);
688           match_byte_assert(0);
689           match_u32_assert(1);
690           match_byte_assert(0);
691           match_byte_assert(0);
692           int subn = get_u32 ();
693           printf ("nested %d bytes", subn);
694           pos += subn;
695         }
696       else
697         {
698           match_u32_assert(3);
699           printf("(special 3)");
700           match_byte_assert(0);
701           match_byte_assert(0);
702           match_byte_assert(1);
703           match_byte_assert(0);
704           int subn = get_u32 ();
705           printf ("nested %d bytes, ", subn);
706           pos += subn;
707           subn = get_u32 ();
708           printf ("nested %d bytes, ", subn);
709           pos += subn;
710         }
711     }
712   else
713     match_byte_assert (0x58);
714 }
715
716 static void
717 dump_data(void)
718 {
719 #if 1
720   int a[16];
721   for (int i = 0; i < 3 + n_dims; i++)
722     a[i] = get_u32();
723   printf ("data intro:");
724   for (int i = 0; i < 3 + n_dims; i++)
725     printf(" %d", a[i]);
726   printf("\n");
727 #else
728   fprintf (stderr,"data intro (%d dims):", n_dims);
729   for (int i = 0; i < 3+n_dims; i++)
730     fprintf (stderr," %d", get_u32());
731   fprintf(stderr,"\n");
732 #endif
733   int x = get_u32();
734   printf ("%d data values, starting at %08x\n", x, pos);
735   for (int i = 0; i < x; i++)
736     {
737       printf("%08x, index %d:\n", pos, get_u32());
738       match_u32_assert(0);
739       match_byte(0);
740       match_byte(0);
741       match_byte(0);
742       match_byte(0);
743       if (match_byte (1))
744         {
745           unsigned int format;
746           double value;
747
748           dump_data_value_31();
749           format = get_u32 ();
750           value = get_double ();
751           printf ("    value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
752         }
753       else if (match_byte (3))
754         {
755           get_string();
756           dump_data_value_31();
757           get_string();
758           printf("string \"%s\"", get_string());
759           match_byte (0);
760         }
761       else if (match_byte (2))
762         {
763           unsigned int format;
764           char *var, *vallab;
765           double value;
766
767           match_byte_assert (0x58);
768           format = get_u32 ();
769           value = get_double ();
770           var = get_string ();
771           vallab = get_string ();
772           printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
773                   value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
774           if (!match_byte (1) && !match_byte(2))
775             match_byte_assert (3);
776         }
777       else if (match_byte (4))
778         {
779           unsigned int format;
780           char *var, *vallab, *value;
781
782           match_byte_assert (0x58);
783           format = get_u32 ();
784           vallab = get_string ();
785           var = get_string ();
786           if (!match_byte(1) && !match_byte(2))
787             match_byte_assert (3);
788           value = get_string ();
789           printf ("value \"%s\" format %d(%d.%d) var \"%s\" vallab \"%s\"",
790                   value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
791         }
792       else if (match_byte (5))
793         {
794           match_byte_assert (0x58);
795           printf ("variable \"%s\"", get_string());
796           get_string();
797           if (!match_byte(1) && !match_byte(2))
798             match_byte_assert(3);
799           match_byte (0);
800           match_byte (0);
801           match_byte (0);
802           match_byte (0);
803         }
804       else if (match_byte(0x31))
805         {
806           if (match_u32 (1))
807             {
808               printf("(footnote %d) ", get_u32());
809               match_byte_assert (0);
810               match_byte_assert (0);
811               int subn = get_u32 ();
812               printf ("nested %d bytes", subn);
813               pos += subn;
814             }
815           else
816             {
817               match_u32_assert (0);
818               match_u32_assert (0);
819               int subn = get_u32 ();
820               printf ("nested %d bytes", subn);
821               pos += subn;
822             }
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 <= 0; 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 (1))
839                     {
840                       unsigned int format;
841                       double value;
842
843                       if (match_byte (0x31))
844                         {
845                           if (match_u32 (0))
846                             {
847                               if (match_u32 (1))
848                                 get_string();
849                               else
850                                 match_u32_assert (0);
851                               int subn = get_u32 ();
852                               printf ("nested %d bytes", subn);
853                               pos += subn;
854                             }
855                           else if (match_u32 (1))
856                             {
857                               printf("(footnote %d) ", get_u32());
858                               match_byte_assert (0);
859                               match_byte_assert (0);
860                               int subn = get_u32 ();
861                               printf ("nested %d bytes", subn);
862                               pos += subn;
863                             }
864                           else
865                             {
866                               match_u32_assert(2);
867                               printf("(special 2)");
868                               match_byte_assert(0);
869                               match_byte_assert(0);
870                               match_u32_assert(1);
871                               match_byte_assert(0);
872                               match_byte_assert(0);
873                               int subn = get_u32 ();
874                               printf ("nested %d bytes", subn);
875                               pos += subn;
876                             }
877                         }
878                       else
879                         match_byte_assert (0x58);
880                       format = get_u32 ();
881                       value = get_double ();
882                       printf ("    value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
883                     }
884                   else if (match_byte(3))
885                     {
886                       char *a = get_string();
887                       match_byte_assert(0x58);
888                       char *b = get_string();
889                       char *c = get_string();
890                       for (int k = 0; k <= 1; k++)
891                         printf ("    ");
892                       printf ("\"%s\", \"%s\", \"%s\"", a, b, c);
893                       match_byte(0);
894                       match_byte(0);
895                       match_byte(0);
896                       match_byte(0);
897                     }
898                   else if (match_byte(5))
899                     {
900                       match_byte_assert (0x58);
901                       printf ("variable \"%s\"", get_string());
902                       get_string();
903                       if (!match_byte(1) && !match_byte(2))
904                         match_byte_assert(3);
905                       match_byte (0);
906                       match_byte (0);
907                       match_byte (0);
908                       match_byte (0);
909                     }
910                   else
911                     dump_value (0);
912                   putchar('\n');
913                 }
914             }
915         }
916       else
917         {
918           match_byte_assert (0x58);
919           char *base = get_string();
920           int x = get_u32();
921           printf ("\"%s\" with %d variables:\n", base, x);
922           for (int i = 0; i < x; i++)
923             {
924               int y = get_u32();
925               if (!y)
926                 y = 1;
927               else
928                 match_u32_assert(0);
929               for (int j = 0; j <= 0; j++)
930                 printf ("    ");
931               printf("variable %d has %d values:\n", i, y);
932               for (int j = 0; j < y; j++)
933                 {
934                   if (match_byte(3))
935                     {
936                       char *a = get_string();
937                       match_byte_assert(0x58);
938                       char *b = get_string();
939                       char *c = get_string();
940                       for (int k = 0; k <= 1; k++)
941                         printf ("    ");
942                       printf ("\"%s\", \"%s\", \"%s\"", a, b, c);
943                       match_byte(0);
944                       match_byte(0);
945                       match_byte(0);
946                       match_byte(0);
947                       match_byte(0);
948                     }
949                   else if (match_byte (1))
950                     {
951                       unsigned int format;
952                       double value;
953
954                       if (match_byte (0x31))
955                         {
956                           if (match_u32 (1))
957                             {
958                               printf("(footnote %d) ", get_u32());
959                               match_byte_assert (0);
960                               match_byte_assert (0);
961                               int subn = get_u32 ();
962                               printf ("nested %d bytes", subn);
963                               pos += subn;
964                             }
965                         }
966                       else
967                         match_byte_assert (0x58);
968                       format = get_u32 ();
969                       value = get_double ();
970                       printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
971                     }
972                   else
973                     dump_value (1);
974                   putchar('\n');
975                 }
976             }
977         }
978       putchar('\n');
979     }
980 }
981
982 static void
983 dump_title_value_31(int level)
984 {
985   if (match_byte (0x31))
986     {
987       if (match_u32 (0))
988         {
989           match_u32_assert (0);
990           int subn = get_u32 ();
991           printf ("nested %d bytes", subn);
992           pos += subn;
993         }
994       else if (match_u32 (1))
995         {
996           printf("(footnote %d) ", get_u32());
997           match_byte_assert (0);
998           match_byte_assert (0);
999           int subn = get_u32 ();
1000           printf ("nested %d bytes", subn);
1001           pos += subn;
1002         }
1003       else if (match_u32 (2))
1004         {
1005           printf("(special 2)");
1006           match_byte_assert(0);
1007           match_byte_assert(0);
1008           if (!match_u32(2))
1009             match_u32_assert(1);
1010           match_byte_assert(0);
1011           match_byte_assert(0);
1012           int subn = get_u32 ();
1013           printf ("nested %d bytes", subn);
1014           pos += subn;
1015         }
1016       else
1017         {
1018           match_u32_assert(3);
1019           printf("(special 3)");
1020           match_byte_assert(0);
1021           match_byte_assert(0);
1022           match_byte_assert(1);
1023           match_byte_assert(0);
1024           int subn = get_u32 ();
1025           printf ("nested %d bytes, ", subn);
1026           pos += subn;
1027           subn = get_u32 ();
1028           printf ("nested %d bytes, ", subn);
1029           pos += subn;
1030         }
1031     }
1032   else
1033     match_byte_assert (0x58);
1034 }
1035
1036 static void
1037 dump_title_value(int level)
1038 {
1039   for (int i = 0; i <= level; i++)
1040     printf ("    ");
1041
1042   match_byte (0);
1043   match_byte (0);
1044   match_byte (0);
1045   match_byte (0);
1046   match_byte (0);
1047   if (match_byte (3))
1048     {
1049       get_string();
1050       dump_title_value_31(level);
1051       get_string();
1052       printf("string \"%s\"", get_string());
1053       match_byte (0);
1054       match_byte (0);
1055       match_byte (0);
1056       match_byte (1);
1057       match_byte (1);
1058       match_byte (0);
1059       match_byte (0);
1060       match_byte (0);
1061       match_byte (1);
1062     }
1063   else if (match_byte (5))
1064     {
1065       dump_title_value_31(level);
1066       printf ("variable \"%s\"", get_string());
1067       get_string();
1068       if (!match_byte(1) && !match_byte(2))
1069         match_byte_assert(3);
1070     }
1071   else if (match_byte (2))
1072     {
1073       unsigned int format;
1074       char *var, *vallab;
1075       double value;
1076
1077       match_byte_assert (0x58);
1078       format = get_u32 ();
1079       value = get_double ();
1080       var = get_string ();
1081       vallab = get_string ();
1082       printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
1083               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
1084       if (!match_byte (1) && !match_byte(2))
1085         match_byte_assert (3);
1086       match_byte (0);
1087       match_byte (0);
1088       match_byte (0);
1089       match_byte (0);
1090       match_byte (0);
1091       match_byte (0);
1092       match_byte (0);
1093     }
1094   else if (match_byte (4))
1095     {
1096       unsigned int format;
1097       char *var, *vallab, *value;
1098
1099       match_byte_assert (0x58);
1100       format = get_u32 ();
1101       vallab = get_string ();
1102       var = get_string ();
1103       if (!match_byte(1) && !match_byte(2))
1104         match_byte_assert (3);
1105       value = get_string ();
1106       printf ("value \"%s\" format %d(%d.%d) var \"%s\" vallab \"%s\"",
1107               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
1108       match_byte (0);
1109       match_byte (0);
1110       match_byte (0);
1111       match_byte (0);
1112     }
1113   else if (match_byte (1))
1114     {
1115       unsigned int format;
1116       double value;
1117
1118       dump_title_value_31(level);
1119       format = get_u32 ();
1120       value = get_double ();
1121       printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
1122       match_byte (1);
1123       match_byte (0);
1124       match_byte (0);
1125       match_byte (0);
1126       match_byte (1);
1127     }
1128   else
1129     {
1130       dump_title_value_31(level);
1131
1132       char *base = get_string();
1133       int x = get_u32();
1134       printf ("\"%s\" with %d variables:\n", base, x);
1135       for (int i = 0; i < x; i++)
1136         {
1137           int y = get_u32();
1138           if (!y)
1139             y = 1;
1140           else
1141             match_u32_assert(0);
1142           for (int j = 0; j <= level; j++)
1143             printf ("    ");
1144           printf("variable %d has %d values:\n", i, y);
1145           for (int j = 0; j < y; j++)
1146             {
1147               match_byte(0);
1148               if (match_byte(3))
1149                 {
1150                   char *a = get_string();
1151                   match_byte_assert(0x58);
1152                   char *b = get_string();
1153                   char *c = get_string();
1154                   for (int k = 0; k <= level + 1; k++)
1155                     printf ("    ");
1156                   printf ("\"%s\", \"%s\", \"%s\"", a, b, c);
1157                 }
1158               else
1159                 dump_title_value (level+1);
1160               putchar('\n');
1161             }
1162         }
1163     }
1164 }
1165
1166 static void
1167 dump_footnote_value(int level)
1168 {
1169   for (int i = 0; i <= level; i++)
1170     printf ("    ");
1171
1172   match_byte (0);
1173   match_byte (0);
1174   match_byte (0);
1175   match_byte (0);
1176   if (match_byte (3))
1177     {
1178       get_string();
1179       if (match_byte (0x31))
1180         {
1181           if (match_u32 (1))
1182             {
1183               printf("(footnote %d) ", get_u32());
1184               match_byte_assert (0);
1185               match_byte_assert (0);
1186               int subn = get_u32 ();
1187               printf ("nested %d bytes", subn);
1188               pos += subn;
1189             }
1190           else if (match_u32 (2))
1191             {
1192               printf("(special 2)");
1193               match_byte_assert(0);
1194               match_byte_assert(0);
1195               match_u32_assert(1);
1196               match_byte_assert(0);
1197               match_byte_assert(0);
1198               int subn = get_u32 ();
1199               printf ("nested %d bytes", subn);
1200               pos += subn;
1201             }
1202           else
1203             {
1204               match_u32_assert(3);
1205               printf("(special 3)");
1206               match_byte_assert(0);
1207               match_byte_assert(0);
1208               match_byte_assert(1);
1209               match_byte_assert(0);
1210               int subn = get_u32 ();
1211               printf ("nested %d bytes, ", subn);
1212               pos += subn;
1213               subn = get_u32 ();
1214               printf ("nested %d bytes, ", subn);
1215               pos += subn;
1216             }
1217         }
1218       else
1219         match_byte_assert (0x58);
1220       get_string();
1221       printf("string \"%s\"", get_string());
1222       if (!match_byte (0))
1223         match_byte_assert (1);
1224     }
1225   else if (match_byte (5))
1226     {
1227       match_byte_assert (0x58);
1228       printf ("variable \"%s\"", get_string());
1229       get_string();
1230       if (!match_byte(1) && !match_byte(2))
1231         match_byte_assert(3);
1232     }
1233   else if (match_byte (2))
1234     {
1235       unsigned int format;
1236       char *var, *vallab;
1237       double value;
1238
1239       match_byte_assert (0x58);
1240       format = get_u32 ();
1241       value = get_double ();
1242       var = get_string ();
1243       vallab = get_string ();
1244       printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
1245               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
1246       if (!match_byte (1) && !match_byte(2))
1247         match_byte_assert (3);
1248       match_byte (0);
1249       match_byte (0);
1250       match_byte (0);
1251       match_byte (0);
1252       match_byte (0);
1253       match_byte (0);
1254       match_byte (0);
1255     }
1256   else if (match_byte (4))
1257     {
1258       unsigned int format;
1259       char *var, *vallab, *value;
1260
1261       match_byte_assert (0x58);
1262       format = get_u32 ();
1263       vallab = get_string ();
1264       var = get_string ();
1265       if (!match_byte(1) && !match_byte(2))
1266         match_byte_assert (3);
1267       value = get_string ();
1268       printf ("value \"%s\" format %d(%d.%d) var \"%s\" vallab \"%s\"",
1269               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
1270       match_byte (0);
1271       match_byte (0);
1272       match_byte (0);
1273       match_byte (0);
1274     }
1275   else if (match_byte (1))
1276     {
1277       unsigned int format;
1278       double value;
1279
1280       if (match_byte (0x31))
1281         {
1282           if (match_u32 (1))
1283             {
1284               printf("(footnote %d) ", get_u32());
1285               match_byte_assert (0);
1286               match_byte_assert (0);
1287               int subn = get_u32 ();
1288               printf ("nested %d bytes", subn);
1289               pos += subn;
1290             }
1291         }
1292       else
1293         match_byte_assert (0x58);
1294       format = get_u32 ();
1295       value = get_double ();
1296       printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
1297     }
1298   else if (match_byte (0x31))
1299     {
1300       if (match_u32 (1))
1301         {
1302           printf("(footnote %d) ", get_u32());
1303           match_byte_assert (0);
1304           match_byte_assert (0);
1305           int subn = get_u32 ();
1306           printf ("nested %d bytes", subn);
1307           pos += subn;
1308         }
1309       else
1310         {
1311           match_u32_assert (0);
1312           match_u32_assert (0);
1313           int subn = get_u32 ();
1314           printf ("nested %d bytes", subn);
1315           pos += subn;
1316         }
1317       char *base = get_string();
1318       int x = get_u32();
1319       printf ("\"%s\"; %d variables:\n", base, x);
1320       for (int i = 0; i < x; i++)
1321         {
1322           int y = get_u32();
1323           if (!y)
1324             y = 1;
1325           else
1326             match_u32_assert(0);
1327           for (int j = 0; j <= level; j++)
1328             printf ("    ");
1329           printf("variable %d has %d values:\n", i, y);
1330           for (int j = 0; j < y; j++)
1331             {
1332               if (match_byte(3))
1333                 {
1334                   char *a = get_string();
1335                   match_byte_assert(0x58);
1336                   char *b = get_string();
1337                   char *c = get_string();
1338                   for (int k = 0; k <= level + 1; k++)
1339                     printf ("    ");
1340                   printf ("\"%s\", \"%s\", \"%s\"", a, b, c);
1341                   if (!match_byte(1))
1342                     match_byte_assert(0);
1343                 }
1344               else
1345                 dump_footnote_value (level+1);
1346               putchar('\n');
1347             }
1348         }
1349     }
1350   else
1351     {
1352
1353       match_byte_assert (0x58);
1354       char *base = get_string();
1355       int x = get_u32();
1356       printf ("\"%s\" with %d variables:\n", base, x);
1357       for (int i = 0; i < x; i++)
1358         {
1359           int y = get_u32();
1360           if (!y)
1361             y = 1;
1362           else
1363             match_u32_assert(0);
1364           for (int j = 0; j <= level; j++)
1365             printf ("    ");
1366           printf("variable %d has %d values:\n", i, y);
1367           for (int j = 0; j < y; j++)
1368             {
1369               if (match_byte(3))
1370                 {
1371                   char *a = get_string();
1372                   match_byte_assert(0x58);
1373                   char *b = get_string();
1374                   char *c = get_string();
1375                   for (int k = 0; k <= level + 1; k++)
1376                     printf ("    ");
1377                   printf ("\"%s\", \"%s\", \"%s\"", a, b, c);
1378                   match_byte_assert(0);
1379                 }
1380               else
1381                 dump_footnote_value (level+1);
1382               putchar('\n');
1383             }
1384         }
1385     }
1386 }
1387
1388 static void
1389 dump_title(void)
1390 {
1391   pos = 0x27;
1392   dump_title_value(0); putchar('\n');
1393   dump_title_value(0); putchar('\n');
1394   match_byte_assert(0x31);
1395   dump_title_value(0); putchar('\n');
1396   match_byte(0);
1397   match_byte_assert(0x58);
1398   if (match_byte(0x31))
1399     {
1400       dump_footnote_value(0); putchar('\n');
1401     }
1402   else
1403     match_byte_assert(0x58);
1404
1405
1406   int n_footnotes = get_u32();
1407   if (n_footnotes >= 20)
1408     {
1409       fprintf(stderr, "%08x: %d footnotes\n", pos - 4, n_footnotes);
1410       exit(1);
1411     }
1412
1413   printf("------\n%d footnotes\n", n_footnotes);
1414   if (n_footnotes < 20)
1415     {
1416       for (int i = 0; i < n_footnotes; i++)
1417         {
1418           printf("footnote %d:\n", i);
1419           dump_footnote_value(0);
1420           match_byte(0);
1421           match_byte(0);
1422           match_byte(0);
1423           match_byte(0);
1424           if (match_byte (1))
1425             {
1426               unsigned int format;
1427               double value;
1428
1429               if (match_byte (0x31))
1430                 {
1431                   if (match_u32 (1))
1432                     {
1433                       printf("(footnote %d) ", get_u32());
1434                       match_byte_assert (0);
1435                       match_byte_assert (0);
1436                       int subn = get_u32 ();
1437                       printf ("nested %d bytes", subn);
1438                       pos += subn;
1439                     }
1440                 }
1441               else
1442                 match_byte_assert (0x58);
1443               format = get_u32 ();
1444               value = get_double ();
1445               printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
1446               match_byte (1);
1447               match_byte (0);
1448               match_byte (0);
1449               match_byte (0);
1450               match_byte (1);
1451             }
1452           else if (match_byte (0x31))
1453             {
1454               match_byte_assert(3);
1455               get_string();
1456               match_byte_assert(0x58);
1457               match_u32_assert(0);
1458               get_string();
1459               match_byte(0);
1460             }
1461           else
1462             match_byte_assert (0x58);
1463           printf("(%d)\n", get_u32());
1464         }
1465     }
1466 }
1467
1468 static int
1469 find_dimensions(void)
1470 {
1471   {
1472     const char dimensions[] = "-,,,.\0";
1473     int x = try_find_tail(dimensions, sizeof dimensions - 1);
1474     if (x)
1475       return x;
1476   }
1477
1478   const char dimensions[] = "-,,, .\0";
1479   return find_tail(dimensions, sizeof dimensions - 1);
1480 }
1481
1482 static void
1483 dump_fonts(void)
1484 {
1485   printf("fonts: offset=%08x\n", pos);
1486   match_byte(0);
1487   for (int i = 1; i <= 8; i++)
1488     {
1489       printf("%08x: font %d, ", pos, i);
1490       match_byte_assert(i);
1491       match_byte_assert(0x31);
1492       printf("%s, ", get_string());
1493       match_byte_assert(0);
1494       match_byte_assert(0);
1495       if (!match_byte(0x40) && !match_byte(0x20) && !match_byte(0x80) && !match_byte(0x10))
1496         match_byte_assert(0x50);
1497       if (!match_byte(0x41))
1498         match_byte_assert(0x51);
1499       pos += 13;
1500       printf ("%s, ", get_string());
1501       printf ("%s, ", get_string());
1502       match_u32_assert(0);
1503       match_u32_assert(0);
1504       pos++;
1505       get_u32();
1506       get_u32();
1507       get_u32();
1508       get_u32();
1509       putchar('\n');
1510     }
1511
1512   match_u32_assert(240);
1513   pos += 240;
1514
1515   match_u32_assert(18);
1516   pos += 18;
1517
1518   if (match_u32(117))
1519     pos += 117;
1520   else
1521     {
1522       match_u32_assert(142);
1523       pos += 142;
1524     }
1525
1526   int count = get_u32();
1527   pos += 4 * count;
1528
1529   char *encoding = get_string();
1530   printf("encoding=%s\n", encoding);
1531
1532   if (!match_u32(0))
1533     match_u32_assert(UINT32_MAX);
1534   if (!match_byte(0))
1535     match_byte_assert(1);
1536   match_byte_assert(0);
1537   if (!match_byte(0))
1538     match_byte_assert(1);
1539   if (!match_byte(0x99) && !match_byte(0x98))
1540     match_byte_assert(0x97);
1541   match_byte_assert(7);
1542   match_byte_assert(0);
1543   match_byte_assert(0);
1544   if (match_byte('.'))
1545     match_byte_assert(',');
1546   else
1547     {
1548       match_byte_assert(',');
1549       if (!match_byte('.'))
1550         match_byte_assert(' ');
1551     }
1552   match_u32_assert(5);
1553   for (int i = 0; i < 5; i++)
1554     get_string();
1555   pos += get_u32();
1556   if (pos != find_dimensions())
1557     fprintf (stderr, "%08x / %08x\n", pos, find_dimensions());
1558 }
1559
1560 int
1561 main(int argc, char *argv[])
1562 {
1563   size_t start;
1564   struct stat s;
1565
1566   if (isatty(STDIN_FILENO))
1567     {
1568       fprintf(stderr, "redirect stdin from a .bin file\n");
1569       exit(1);
1570     }
1571   if (fstat(STDIN_FILENO, &s))
1572     {
1573       perror("fstat");
1574       exit(1);
1575     }
1576   n = s.st_size;
1577   data = malloc(n);
1578   if (!data)
1579     {
1580       perror("malloc");
1581       exit(1);
1582     }
1583   if (read(STDIN_FILENO, data, n) != n)
1584     {
1585       perror("read");
1586       exit(1);
1587     }
1588
1589   if (argc > 1)
1590     {
1591       if (!strcmp(argv[1], "title0"))
1592         {
1593           pos = 0x27;
1594           if (match_byte (0x03)
1595               || (match_byte (0x05) && match_byte (0x58)))
1596             printf ("%s\n", get_string());
1597           else
1598             printf ("<unknown>\n");
1599           return 0;
1600         }
1601       else if (!strcmp(argv[1], "title"))
1602         {
1603           dump_title();
1604           exit(0);
1605         }
1606       else if (!strcmp(argv[1], "titleraw"))
1607         {
1608           const char fonts[] = "\x01\x31\x09\0\0\0SansSerif";
1609           start = 0x27;
1610           n = find(fonts, sizeof fonts - 1);
1611         }
1612       else if (!strcmp(argv[1], "fonts"))
1613         {
1614           const char fonts[] = "\x01\x31\x09\0\0\0SansSerif";
1615           const char styles[] = "\xf0\0\0\0";
1616           start = find(fonts, sizeof fonts - 1);
1617           n = find(styles, sizeof styles - 1);
1618         }
1619       else if (!strcmp(argv[1], "styles"))
1620         {
1621           const char styles[] = "\xf0\0\0\0";
1622           const char dimensions[] = "-,,,.\0";
1623           start = find(styles, sizeof styles - 1);
1624           n = find(dimensions, sizeof dimensions - 1) + sizeof dimensions - 1;
1625         }
1626       else if (!strcmp(argv[1], "dimensions") || !strcmp(argv[1], "all"))
1627         {
1628           pos = 0;
1629           match_byte_assert(1);
1630           match_byte_assert(0);
1631           match_u32_assert(3);
1632           match_byte_assert(1);
1633           if (!match_byte(0))
1634             match_byte_assert(1);
1635           match_byte_assert(0);
1636           match_byte_assert(0);
1637           if (!match_byte(0))
1638             match_byte_assert(1);
1639           pos++;
1640           match_byte_assert(0);
1641           match_byte_assert(0);
1642           match_byte_assert(0);
1643           dump_title ();
1644           dump_fonts();
1645           dump_dims ();
1646           printf("\n\ndata:\n");
1647           dump_data ();
1648           if (pos == n - 1)
1649             match_byte_assert (1);
1650           if (pos != n)
1651             {
1652               fprintf (stderr, "%x / %x\n", pos, n);
1653               exit(1);
1654             }
1655           exit(0);
1656         }
1657       else
1658         {
1659           fprintf (stderr, "unknown section %s\n", argv[1]);
1660           exit(1);
1661         }
1662     }
1663   else
1664     start = 0x27;
1665
1666   for (size_t i = start; i < n; )
1667     {
1668       if (i + 5 <= n
1669           && data[i]
1670           //&& !data[i + 1]
1671           && !data[i + 2]
1672           && !data[i + 3]
1673           && i + 4 + data[i] + data[i + 1] * 256 <= n
1674           && all_ascii(&data[i + 4], data[i] + data[i + 1] * 256))
1675         {
1676           fputs("\n\"", stdout);
1677           fwrite(&data[i + 4], 1, data[i] + data[i + 1] * 256, stdout);
1678           fputs("\" ", stdout);
1679
1680           i += 4 + data[i] + data[i + 1] * 256;
1681         }
1682       else if (i + 12 <= n
1683                && data[i + 1] == 40
1684                && data[i + 2] == 5
1685                && data[i + 3] == 0)
1686         {
1687           double d;
1688
1689           memcpy (&d, &data[i + 4], 8);
1690           printf ("F40.%d(%.*f)\n", data[i], data[i], d);
1691           i += 12;
1692         }
1693       else if (i + 12 <= n
1694                && data[i + 1] == 40
1695                && data[i + 2] == 31
1696                && data[i + 3] == 0)
1697         {
1698           double d;
1699
1700           memcpy (&d, &data[i + 4], 8);
1701           printf ("PCT40.%d(%.*f)\n", data[i], data[i], d);
1702           i += 12;
1703         }
1704       else if (i + 4 <= n
1705                && (data[i] && data[i] != 88 && data[i] != 0x41)
1706                && !data[i + 1]
1707                && !data[i + 2]
1708                && !data[i + 3])
1709         {
1710           printf ("i%d ", data[i]);
1711           i += 4;
1712         }
1713       else
1714         {
1715           printf("%02x ", data[i]);
1716           i++;
1717         }
1718     }
1719
1720   return 0;
1721 }