dump: Crazy simplification.
[pspp] / dump.c
1 #include <stdbool.h>
2 #include <stdint.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <sys/stat.h>
7 #include <unistd.h>
8
9 static uint8_t *data;
10 static size_t n;
11
12 static bool
13 all_ascii(const uint8_t *p, size_t n)
14 {
15   for (size_t i = 0; i < n; i++)
16     if (p[i] < 32 || p[i] > 126)
17       return false;
18   return true;
19 }
20
21 static size_t
22 try_find(const char *target, size_t target_len)
23 {
24   const uint8_t *pos = (const uint8_t *) memmem (data, n, target, target_len);
25   return pos ? pos - data : 0;
26 }
27
28 static size_t
29 try_find_tail(const char *target, size_t target_len)
30 {
31   size_t pos = try_find(target, target_len);
32   return pos ? pos + target_len : 0;
33 }
34
35 static size_t
36 find(const char *target, size_t target_len)
37 {
38   size_t pos = try_find(target, target_len);
39   if (!pos)
40     {
41       fprintf (stderr, "not found\n");
42       exit(1);
43     }
44   return pos;
45 }
46
47 static size_t
48 find_tail(const char *target, size_t target_len)
49 {
50   size_t pos = try_find_tail(target, target_len);
51   if (!pos)
52     {
53       fprintf (stderr, "not found\n");
54       exit(1);
55     }
56   return pos;
57 }
58
59 size_t pos;
60
61 #define XSTR(x) #x
62 #define STR(x) XSTR(x)
63 #define WHERE __FILE__":" STR(__LINE__)
64
65 static unsigned int
66 get_u32(void)
67 {
68   uint32_t x;
69   memcpy(&x, &data[pos], 4);
70   pos += 4;
71   return x;
72 }
73
74 static double
75 get_double(void)
76 {
77   double x;
78   memcpy(&x, &data[pos], 8);
79   pos += 8;
80   return x;
81 }
82
83 static bool
84 match_u32(uint32_t x)
85 {
86   if (get_u32() == x)
87     return true;
88   pos -= 4;
89   return false;
90 }
91
92 static void
93 match_u32_assert(uint32_t x, const char *where)
94 {
95   unsigned int y = get_u32();
96   if (x != y)
97     {
98       fprintf(stderr, "%s: 0x%x: expected i%u, got i%u\n", where, pos - 4, x, y);
99       exit(1);
100     }
101 }
102 #define match_u32_assert(x) match_u32_assert(x, WHERE)
103
104 static bool
105 match_byte(uint8_t b)
106 {
107   if (pos < n && data[pos] == b)
108     {
109       pos++;
110       return true;
111     }
112   else
113     return false;
114 }
115
116 static void
117 match_byte_assert(uint8_t b, const char *where)
118 {
119   if (!match_byte(b))
120     {
121       fprintf(stderr, "%s: 0x%x: expected %02x, got %02x\n", where, pos, b, data[pos]);
122       exit(1);
123     }
124 }
125 #define match_byte_assert(b) match_byte_assert(b, WHERE)
126
127 static char *
128 get_string(const char *where)
129 {
130   if (1
131       /*data[pos + 1] == 0 && data[pos + 2] == 0 && data[pos + 3] == 0*/
132       /*&& all_ascii(&data[pos + 4], data[pos])*/)
133     {
134       int len = data[pos] + data[pos + 1] * 256;
135       char *s = malloc(len + 1);
136
137       memcpy(s, &data[pos + 4], len);
138       s[len] = 0;
139       pos += 4 + len;
140       return s;
141     }
142   else
143     {
144       fprintf(stderr, "%s: 0x%x: expected string\n", where, pos);
145       exit(1);
146     }
147 }
148 #define get_string() get_string(WHERE)
149
150 static void
151 dump_value_31(void)
152 {
153   if (match_byte (0x31))
154     {
155       if (match_u32 (0))
156         {
157           if (match_u32 (1))
158             get_string();
159           else
160             match_u32_assert (0);
161           int subn = get_u32 ();
162           printf ("nested %d bytes", subn);
163           pos += subn;
164         }
165       else if (match_u32 (1))
166         {
167           printf("(footnote %d) ", get_u32());
168           match_byte_assert (0);
169           match_byte_assert (0);
170           int subn = get_u32 ();
171           printf ("nested %d bytes", subn);
172           pos += subn;
173         }
174       else if (match_u32 (2))
175         {
176           printf("(special 2)");
177           match_byte_assert(0);
178           match_byte_assert(0);
179           if (!match_u32 (2))
180             match_u32_assert(1);
181           match_byte_assert(0);
182           match_byte_assert(0);
183           int subn = get_u32 ();
184           printf ("nested %d bytes", subn);
185           pos += subn;
186         }
187       else
188         {
189           match_u32_assert(3);
190           printf("(special 3)");
191           match_byte_assert(0);
192           match_byte_assert(0);
193           match_byte_assert(1);
194           match_byte_assert(0);
195           int subn = get_u32 ();
196           printf ("nested %d bytes, ", subn);
197           pos += subn;
198           subn = get_u32 ();
199           printf ("nested %d bytes, ", subn);
200           pos += subn;
201         }
202     }
203   else
204     match_byte_assert (0x58);
205 }
206
207 static void dump_substs(void (*dump)(int level), int level);
208 static void dump_value__(int level, bool match1);
209
210 static void
211 dump_value__with_preskip(int level)
212 {
213   match_byte(0);
214   match_byte(0);
215   match_byte(0);
216   match_byte(0);
217   dump_value__ (level, false);
218   putchar('\n');
219 }
220
221 static void
222 dump_value__(int level, bool match1)
223 {
224   for (int i = 0; i <= level; i++)
225     printf ("    ");
226
227   if (match_byte (3))
228     {
229       char *s1 = get_string();
230       dump_value_31();
231       char *s2 = get_string();
232       char *s3 = get_string();
233       if (strcmp(s1, s3))
234         printf("strings \"%s\", \"%s\" and \"%s\"", s1, s2, s3);
235       else
236         printf("string \"%s\" and \"%s\"", s1, s2);
237       if (!match_byte (0))
238         match_byte_assert(1);
239       if (match1)
240         match_byte (1);
241     }
242   else if (match_byte (5))
243     {
244       dump_value_31();
245       printf ("variable \"%s\"", get_string());
246       get_string();
247       if (!match_byte(1) && !match_byte(2))
248         match_byte_assert(3);
249     }
250   else if (match_byte (2))
251     {
252       unsigned int format;
253       char *var, *vallab;
254       double value;
255
256       match_byte_assert (0x58);
257       format = get_u32 ();
258       value = get_double ();
259       var = get_string ();
260       vallab = get_string ();
261       printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
262               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
263       if (!match_byte (1) && !match_byte(2))
264         match_byte_assert (3);
265     }
266   else if (match_byte (4))
267     {
268       unsigned int format;
269       char *var, *vallab, *value;
270
271       match_byte_assert (0x58);
272       format = get_u32 ();
273       vallab = get_string ();
274       var = get_string ();
275       if (!match_byte(1) && !match_byte(2))
276         match_byte_assert (3);
277       value = get_string ();
278       printf ("value \"%s\" format %d(%d.%d) var \"%s\" vallab \"%s\"",
279               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
280     }
281   else if (match_byte (1))
282     {
283       unsigned int format;
284       double value;
285
286       dump_value_31();
287       format = get_u32 ();
288       value = get_double ();
289       printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
290       if (match1)
291         match_byte (1);
292     }
293   else
294     dump_substs(dump_value__with_preskip, level + 1);
295 }
296
297 static void
298 dump_category(int level)
299 {
300   match_byte (0);
301   match_byte (0);
302   match_byte (0);
303   match_byte (0);
304   dump_value__ (level, true);
305   match_byte(0);
306   match_byte(0);
307   match_byte(0);
308
309   if (match_u32 (1))
310     match_byte (0);
311   else if (match_byte (1))
312     {
313       match_byte (0);
314       if (!match_u32 (2))
315         match_u32_assert (1);
316       match_byte (0);
317     }
318   else if (!match_u32(2))
319     match_u32_assert (0);
320
321   get_u32 ();
322
323   int n_categories = get_u32();
324   if (n_categories > 0)
325     printf (", %d subcategories:", n_categories);
326   printf("\n");
327   for (int i = 0; i < n_categories; i++)
328     dump_category (level + 1);
329 }
330
331 static void dump_title_value(int level);
332
333 static void
334 dump_dim(void)
335 {
336   int n_categories;
337   printf("next dim\n");
338   dump_title_value(0);
339
340   /* This byte is usually 0x02 but 0x00 and 0x75 (!) have also been spotted. */
341   pos++;
342
343   if (!match_byte(0) && !match_byte(1))
344     match_byte_assert(2);
345   if (!match_u32(0))
346     match_u32_assert(2);
347   if (!match_byte(0))
348     match_byte_assert(1);
349   get_u32();
350   match_byte(0);
351   match_byte(0);
352   n_categories = get_u32();
353   printf("%d nested categories\n", n_categories);
354   for (int i = 0; i < n_categories; i++)
355     dump_category (0);
356 }
357
358 int n_dims;
359 static void
360 dump_dims(void)
361 {
362   n_dims = get_u32();
363   printf ("%u dimensions\n", n_dims);
364   for (int i = 0; i < n_dims; i++)
365     {
366       printf("\n");
367       dump_dim ();
368     }
369 }
370
371 static void
372 dump_substs(void (*dump)(int level), int level)
373 {
374   dump_value_31();
375
376   char *base = get_string();
377   int x = get_u32();
378   printf ("\"%s\" with %d variables:\n", base, x);
379   for (int i = 0; i < x; i++)
380     {
381       int y = get_u32();
382       if (!y)
383         y = 1;
384       else
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           dump (level+1);
392           putchar('\n');
393         }
394     }
395 }
396
397 static void
398 dump_data_value(int level)
399 {
400   for (int i = 0; i <= level; i++)
401     printf ("    ");
402
403   match_byte(0);
404   match_byte(0);
405   match_byte(0);
406   match_byte(0);
407   if (data[pos] <= 5)
408     dump_value__(0, false);
409   else
410     dump_substs (dump_data_value, level + 1);
411 }
412
413 static void
414 dump_data(void)
415 {
416 #if 1
417   int a[16];
418   for (int i = 0; i < 3 + n_dims; i++)
419     a[i] = get_u32();
420   printf ("data intro:");
421   for (int i = 0; i < 3 + n_dims; i++)
422     printf(" %d", a[i]);
423   printf("\n");
424 #else
425   fprintf (stderr,"data intro (%d dims):", n_dims);
426   for (int i = 0; i < 3+n_dims; i++)
427     fprintf (stderr," %d", get_u32());
428   fprintf(stderr,"\n");
429 #endif
430   int x = get_u32();
431   printf ("%d data values, starting at %08x\n", x, pos);
432   for (int i = 0; i < x; i++)
433     {
434       printf("%08x, index %d:\n", pos, get_u32());
435       match_u32_assert(0);
436       dump_data_value(0);
437       putchar('\n');
438     }
439 }
440
441 static void
442 dump_title_value(int level)
443 {
444   for (int i = 0; i <= level; i++)
445     printf ("    ");
446
447   match_byte (0);
448   match_byte (0);
449   match_byte (0);
450   match_byte (0);
451   if (data[pos] <= 5)
452     dump_value__(level, true);
453   else
454     dump_substs(dump_title_value, level + 1);
455 }
456
457 static void
458 dump_title(void)
459 {
460   pos = 0x27;
461   dump_title_value(0); putchar('\n');
462   dump_title_value(0); putchar('\n');
463   match_byte_assert(0x31);
464   dump_title_value(0); putchar('\n');
465   match_byte(0);
466   match_byte_assert(0x58);
467   if (match_byte(0x31))
468     {
469       dump_data_value(0); putchar('\n');
470     }
471   else
472     match_byte_assert(0x58);
473
474
475   int n_footnotes = get_u32();
476   if (n_footnotes >= 20)
477     {
478       fprintf(stderr, "%08x: %d footnotes\n", pos - 4, n_footnotes);
479       exit(1);
480     }
481
482   printf("------\n%d footnotes\n", n_footnotes);
483   if (n_footnotes < 20)
484     {
485       for (int i = 0; i < n_footnotes; i++)
486         {
487           printf("footnote %d:\n", i);
488           dump_data_value(0);
489           if (match_byte (0x31))
490             {
491               /* Custom footnote marker string. */
492               match_byte_assert(3);
493               get_string();
494               match_byte_assert(0x58);
495               match_u32_assert(0);
496               get_string();
497             }
498           else
499             match_byte_assert (0x58);
500           printf("(%d)\n", get_u32());
501         }
502     }
503 }
504
505 static int
506 find_dimensions(void)
507 {
508   {
509     const char dimensions[] = "-,,,.\0";
510     int x = try_find_tail(dimensions, sizeof dimensions - 1);
511     if (x)
512       return x;
513   }
514
515   const char dimensions[] = "-,,, .\0";
516   return find_tail(dimensions, sizeof dimensions - 1);
517 }
518
519 static void
520 dump_fonts(void)
521 {
522   printf("fonts: offset=%08x\n", pos);
523   match_byte(0);
524   for (int i = 1; i <= 8; i++)
525     {
526       printf("%08x: font %d, ", pos, i);
527       match_byte_assert(i);
528       match_byte_assert(0x31);
529       printf("%s, ", get_string());
530       match_byte_assert(0);
531       match_byte_assert(0);
532       if (!match_byte(0x40) && !match_byte(0x20) && !match_byte(0x80) && !match_byte(0x10))
533         match_byte_assert(0x50);
534       if (!match_byte(0x41))
535         match_byte_assert(0x51);
536       pos += 13;
537       printf ("%s, ", get_string());
538       printf ("%s, ", get_string());
539       match_u32_assert(0);
540       match_u32_assert(0);
541       pos++;
542       get_u32();
543       get_u32();
544       get_u32();
545       get_u32();
546       putchar('\n');
547     }
548
549   match_u32_assert(240);
550   pos += 240;
551
552   match_u32_assert(18);
553   pos += 18;
554
555   if (match_u32(117))
556     pos += 117;
557   else
558     {
559       match_u32_assert(142);
560       pos += 142;
561     }
562
563   int count = get_u32();
564   pos += 4 * count;
565
566   char *encoding = get_string();
567   printf("encoding=%s\n", encoding);
568
569   if (!match_u32(0))
570     match_u32_assert(UINT32_MAX);
571   if (!match_byte(0))
572     match_byte_assert(1);
573   match_byte_assert(0);
574   if (!match_byte(0))
575     match_byte_assert(1);
576   if (!match_byte(0x99) && !match_byte(0x98))
577     match_byte_assert(0x97);
578   match_byte_assert(7);
579   match_byte_assert(0);
580   match_byte_assert(0);
581   if (match_byte('.'))
582     match_byte_assert(',');
583   else
584     {
585       match_byte_assert(',');
586       if (!match_byte('.'))
587         match_byte_assert(' ');
588     }
589   match_u32_assert(5);
590   for (int i = 0; i < 5; i++)
591     get_string();
592   pos += get_u32();
593   if (pos != find_dimensions())
594     fprintf (stderr, "%08x / %08x\n", pos, find_dimensions());
595 }
596
597 int
598 main(int argc, char *argv[])
599 {
600   size_t start;
601   struct stat s;
602
603   if (isatty(STDIN_FILENO))
604     {
605       fprintf(stderr, "redirect stdin from a .bin file\n");
606       exit(1);
607     }
608   if (fstat(STDIN_FILENO, &s))
609     {
610       perror("fstat");
611       exit(1);
612     }
613   n = s.st_size;
614   data = malloc(n);
615   if (!data)
616     {
617       perror("malloc");
618       exit(1);
619     }
620   if (read(STDIN_FILENO, data, n) != n)
621     {
622       perror("read");
623       exit(1);
624     }
625
626   if (argc > 1)
627     {
628       if (!strcmp(argv[1], "title0"))
629         {
630           pos = 0x27;
631           if (match_byte (0x03)
632               || (match_byte (0x05) && match_byte (0x58)))
633             printf ("%s\n", get_string());
634           else
635             printf ("<unknown>\n");
636           return 0;
637         }
638       else if (!strcmp(argv[1], "title"))
639         {
640           dump_title();
641           exit(0);
642         }
643       else if (!strcmp(argv[1], "titleraw"))
644         {
645           const char fonts[] = "\x01\x31\x09\0\0\0SansSerif";
646           start = 0x27;
647           n = find(fonts, sizeof fonts - 1);
648         }
649       else if (!strcmp(argv[1], "fonts"))
650         {
651           const char fonts[] = "\x01\x31\x09\0\0\0SansSerif";
652           const char styles[] = "\xf0\0\0\0";
653           start = find(fonts, sizeof fonts - 1);
654           n = find(styles, sizeof styles - 1);
655         }
656       else if (!strcmp(argv[1], "styles"))
657         {
658           const char styles[] = "\xf0\0\0\0";
659           const char dimensions[] = "-,,,.\0";
660           start = find(styles, sizeof styles - 1);
661           n = find(dimensions, sizeof dimensions - 1) + sizeof dimensions - 1;
662         }
663       else if (!strcmp(argv[1], "dimensions") || !strcmp(argv[1], "all"))
664         {
665           pos = 0;
666           match_byte_assert(1);
667           match_byte_assert(0);
668           match_u32_assert(3);
669           match_byte_assert(1);
670           if (!match_byte(0))
671             match_byte_assert(1);
672           match_byte_assert(0);
673           match_byte_assert(0);
674           if (!match_byte(0))
675             match_byte_assert(1);
676           pos++;
677           match_byte_assert(0);
678           match_byte_assert(0);
679           match_byte_assert(0);
680           dump_title ();
681           dump_fonts();
682           dump_dims ();
683           printf("\n\ndata:\n");
684           dump_data ();
685           match_byte (1);
686           if (pos != n)
687             {
688               fprintf (stderr, "%x / %x\n", pos, n);
689               exit(1);
690             }
691           exit(0);
692         }
693       else
694         {
695           fprintf (stderr, "unknown section %s\n", argv[1]);
696           exit(1);
697         }
698     }
699   else
700     start = 0x27;
701
702   for (size_t i = start; i < n; )
703     {
704       if (i + 5 <= n
705           && data[i]
706           //&& !data[i + 1]
707           && !data[i + 2]
708           && !data[i + 3]
709           && i + 4 + data[i] + data[i + 1] * 256 <= n
710           && all_ascii(&data[i + 4], data[i] + data[i + 1] * 256))
711         {
712           fputs("\n\"", stdout);
713           fwrite(&data[i + 4], 1, data[i] + data[i + 1] * 256, stdout);
714           fputs("\" ", stdout);
715
716           i += 4 + data[i] + data[i + 1] * 256;
717         }
718       else if (i + 12 <= n
719                && data[i + 1] == 40
720                && data[i + 2] == 5
721                && data[i + 3] == 0)
722         {
723           double d;
724
725           memcpy (&d, &data[i + 4], 8);
726           printf ("F40.%d(%.*f)\n", data[i], data[i], d);
727           i += 12;
728         }
729       else if (i + 12 <= n
730                && data[i + 1] == 40
731                && data[i + 2] == 31
732                && data[i + 3] == 0)
733         {
734           double d;
735
736           memcpy (&d, &data[i + 4], 8);
737           printf ("PCT40.%d(%.*f)\n", data[i], data[i], d);
738           i += 12;
739         }
740       else if (i + 4 <= n
741                && (data[i] && data[i] != 88 && data[i] != 0x41)
742                && !data[i + 1]
743                && !data[i + 2]
744                && !data[i + 3])
745         {
746           printf ("i%d ", data[i]);
747           i += 4;
748         }
749       else
750         {
751           printf("%02x ", data[i]);
752           i++;
753         }
754     }
755
756   return 0;
757 }