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