dump_value__() recursive case even more sane
[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, bool match1);
208
209 static void
210 dump_value__(int level, bool match1)
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       if (match1)
225         match_byte (1);
226     }
227   else if (match_byte (5))
228     {
229       dump_value_31();
230       printf ("variable \"%s\"", get_string());
231       get_string();
232       if (!match_byte(1) && !match_byte(2))
233         match_byte_assert(3);
234     }
235   else if (match_byte (2))
236     {
237       unsigned int format;
238       char *var, *vallab;
239       double value;
240
241       match_byte_assert (0x58);
242       format = get_u32 ();
243       value = get_double ();
244       var = get_string ();
245       vallab = get_string ();
246       printf ("value %g format %d(%d.%d) var \"%s\" vallab \"%s\"",
247               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
248       if (!match_byte (1) && !match_byte(2))
249         match_byte_assert (3);
250     }
251   else if (match_byte (4))
252     {
253       unsigned int format;
254       char *var, *vallab, *value;
255
256       match_byte_assert (0x58);
257       format = get_u32 ();
258       vallab = get_string ();
259       var = get_string ();
260       if (!match_byte(1) && !match_byte(2))
261         match_byte_assert (3);
262       value = get_string ();
263       printf ("value \"%s\" format %d(%d.%d) var \"%s\" vallab \"%s\"",
264               value, format >> 16, (format >> 8) & 0xff, format & 0xff, var, vallab);
265     }
266   else if (match_byte (1))
267     {
268       unsigned int format;
269       double value;
270
271       dump_value_31();
272       format = get_u32 ();
273       value = get_double ();
274       printf ("value %g format %d(%d.%d)", value, format >> 16, (format >> 8) & 0xff, format & 0xff);
275       if (match1)
276         match_byte (1);
277     }
278   else
279     {
280       dump_value_31();
281       char *base = get_string();
282
283       int x = get_u32();
284       printf ("\"%s\" with %d variables:\n", base, x);
285       for (int i = 0; i < x; i++)
286         {
287           int y = get_u32();
288           if (!y)
289             y = 1;
290           else
291             match_u32_assert(0);
292           for (int j = 0; j <= level; j++)
293             printf ("    ");
294           printf("variable %d has %d values:\n", i, y);
295           for (int j = 0; j < y; j++)
296             {
297               match_byte(0);
298               match_byte(0);
299               match_byte(0);
300               match_byte(0);
301               dump_value__ (level+1, false);
302               putchar('\n');
303             }
304         }
305     }
306 }
307
308 static void
309 dump_value(int level, bool match1)
310 {
311   for (int i = 0; i <= level; i++)
312     printf ("    ");
313
314   match_byte (0);
315   match_byte (0);
316   match_byte (0);
317   match_byte (0);
318   dump_value__(level, match1);
319   match_byte(0);
320   match_byte(0);
321   match_byte(0);
322 }
323
324 static void
325 dump_dim_value(int level)
326 {
327   for (int i = 0; i <= level; i++)
328     printf ("    ");
329
330   if (data[pos] == 3 || data[pos] == 5)
331     dump_value__(level, true);
332   else
333     dump_value(level, true);
334 }
335
336 static void
337 dump_category(int level)
338 {
339   dump_value (level, true);
340
341   if (match_u32 (1))
342     match_byte (0);
343   else if (match_byte (1))
344     {
345       match_byte (0);
346       if (!match_u32 (2))
347         match_u32_assert (1);
348       match_byte (0);
349     }
350   else if (!match_u32(2))
351     match_u32_assert (0);
352
353   get_u32 ();
354
355   int n_categories = get_u32();
356   if (n_categories > 0)
357     printf (", %d subcategories:", n_categories);
358   printf("\n");
359   for (int i = 0; i < n_categories; i++)
360     dump_category (level + 1);
361 }
362
363 static void
364 dump_dim(void)
365 {
366   int n_categories;
367   printf("next dim\n");
368   dump_dim_value(0);
369
370   /* This byte is usually 0x02 but 0x00 and 0x75 (!) have also been spotted. */
371   pos++;
372
373   if (!match_byte(0) && !match_byte(1))
374     match_byte_assert(2);
375   if (!match_u32(0))
376     match_u32_assert(2);
377   if (!match_byte(0))
378     match_byte_assert(1);
379   match_byte(0);
380   match_byte(0);
381   match_byte(0);
382   match_byte(0);
383   get_u32();
384   match_byte(0);
385   match_byte(0);
386   match_byte(0);
387   match_byte(0);
388   n_categories = get_u32();
389   printf("%d nested categories\n", n_categories);
390   for (int i = 0; i < n_categories; i++)
391     dump_category (0);
392 }
393
394 int n_dims;
395 static void
396 dump_dims(void)
397 {
398   n_dims = get_u32();
399   printf ("%u dimensions\n", n_dims);
400   for (int i = 0; i < n_dims; i++)
401     {
402       printf("\n");
403       dump_dim ();
404     }
405 }
406
407 static void
408 dump_substs(void (*dump)(int level), int level)
409 {
410   dump_value_31();
411
412   char *base = get_string();
413   int x = get_u32();
414   printf ("\"%s\" with %d variables:\n", base, x);
415   for (int i = 0; i < x; i++)
416     {
417       int y = get_u32();
418       if (!y)
419         y = 1;
420       else
421         match_u32_assert(0);
422       for (int j = 0; j <= level; j++)
423         printf ("    ");
424       printf("variable %d has %d values:\n", i, y);
425       for (int j = 0; j < y; j++)
426         {
427           dump (level+1);
428           putchar('\n');
429         }
430     }
431 }
432
433 static void
434 dump_data_value(int level)
435 {
436   for (int i = 0; i <= level; i++)
437     printf ("    ");
438
439   match_byte(0);
440   match_byte(0);
441   match_byte(0);
442   match_byte(0);
443   if (data[pos] == 1 || data[pos] == 2 || data[pos] == 3 || data[pos] == 4)
444     dump_value__(0, false);
445   else if (data[pos] == 5)
446     dump_value (0, true);
447   else
448     dump_substs (dump_data_value, level + 1);
449 }
450
451 static void
452 dump_data(void)
453 {
454 #if 1
455   int a[16];
456   for (int i = 0; i < 3 + n_dims; i++)
457     a[i] = get_u32();
458   printf ("data intro:");
459   for (int i = 0; i < 3 + n_dims; i++)
460     printf(" %d", a[i]);
461   printf("\n");
462 #else
463   fprintf (stderr,"data intro (%d dims):", n_dims);
464   for (int i = 0; i < 3+n_dims; i++)
465     fprintf (stderr," %d", get_u32());
466   fprintf(stderr,"\n");
467 #endif
468   int x = get_u32();
469   printf ("%d data values, starting at %08x\n", x, pos);
470   for (int i = 0; i < x; i++)
471     {
472       printf("%08x, index %d:\n", pos, get_u32());
473       match_u32_assert(0);
474       dump_data_value(0);
475       putchar('\n');
476     }
477 }
478
479 static void
480 dump_title_value(int level)
481 {
482   for (int i = 0; i <= level; i++)
483     printf ("    ");
484
485   match_byte (0);
486   match_byte (0);
487   match_byte (0);
488   match_byte (0);
489   if (data[pos] == 1 || data[pos] == 2 || data[pos] == 3 || data[pos] == 4)
490     dump_value(level, true);
491   else if (data[pos] == 5)
492     dump_value__(level, true);
493   else
494     dump_substs(dump_title_value, level + 1);
495 }
496
497 static void
498 dump_footnote_value(int level)
499 {
500   for (int i = 0; i <= level; i++)
501     printf ("    ");
502
503   match_byte (0);
504   match_byte (0);
505   match_byte (0);
506   match_byte (0);
507   if (data[pos] == 2 || data[pos] == 4)
508     dump_value(level, true);
509   else if (data[pos] == 1 || data[pos] == 3 || data[pos] == 5)
510     dump_value__(level, false);
511   else
512     dump_substs(dump_footnote_value, level + 1);
513 }
514
515 static void
516 dump_title(void)
517 {
518   pos = 0x27;
519   dump_title_value(0); putchar('\n');
520   dump_title_value(0); putchar('\n');
521   match_byte_assert(0x31);
522   dump_title_value(0); putchar('\n');
523   match_byte(0);
524   match_byte_assert(0x58);
525   if (match_byte(0x31))
526     {
527       dump_footnote_value(0); putchar('\n');
528     }
529   else
530     match_byte_assert(0x58);
531
532
533   int n_footnotes = get_u32();
534   if (n_footnotes >= 20)
535     {
536       fprintf(stderr, "%08x: %d footnotes\n", pos - 4, n_footnotes);
537       exit(1);
538     }
539
540   printf("------\n%d footnotes\n", n_footnotes);
541   if (n_footnotes < 20)
542     {
543       for (int i = 0; i < n_footnotes; i++)
544         {
545           printf("footnote %d:\n", i);
546           dump_footnote_value(0);
547           match_byte(0);
548           match_byte(0);
549           match_byte(0);
550           match_byte(0);
551           if (match_byte (0x31))
552             {
553               /* Custom footnote marker string. */
554               match_byte_assert(3);
555               get_string();
556               match_byte_assert(0x58);
557               match_u32_assert(0);
558               get_string();
559             }
560           else
561             match_byte_assert (0x58);
562           printf("(%d)\n", get_u32());
563         }
564     }
565 }
566
567 static int
568 find_dimensions(void)
569 {
570   {
571     const char dimensions[] = "-,,,.\0";
572     int x = try_find_tail(dimensions, sizeof dimensions - 1);
573     if (x)
574       return x;
575   }
576
577   const char dimensions[] = "-,,, .\0";
578   return find_tail(dimensions, sizeof dimensions - 1);
579 }
580
581 static void
582 dump_fonts(void)
583 {
584   printf("fonts: offset=%08x\n", pos);
585   match_byte(0);
586   for (int i = 1; i <= 8; i++)
587     {
588       printf("%08x: font %d, ", pos, i);
589       match_byte_assert(i);
590       match_byte_assert(0x31);
591       printf("%s, ", get_string());
592       match_byte_assert(0);
593       match_byte_assert(0);
594       if (!match_byte(0x40) && !match_byte(0x20) && !match_byte(0x80) && !match_byte(0x10))
595         match_byte_assert(0x50);
596       if (!match_byte(0x41))
597         match_byte_assert(0x51);
598       pos += 13;
599       printf ("%s, ", get_string());
600       printf ("%s, ", get_string());
601       match_u32_assert(0);
602       match_u32_assert(0);
603       pos++;
604       get_u32();
605       get_u32();
606       get_u32();
607       get_u32();
608       putchar('\n');
609     }
610
611   match_u32_assert(240);
612   pos += 240;
613
614   match_u32_assert(18);
615   pos += 18;
616
617   if (match_u32(117))
618     pos += 117;
619   else
620     {
621       match_u32_assert(142);
622       pos += 142;
623     }
624
625   int count = get_u32();
626   pos += 4 * count;
627
628   char *encoding = get_string();
629   printf("encoding=%s\n", encoding);
630
631   if (!match_u32(0))
632     match_u32_assert(UINT32_MAX);
633   if (!match_byte(0))
634     match_byte_assert(1);
635   match_byte_assert(0);
636   if (!match_byte(0))
637     match_byte_assert(1);
638   if (!match_byte(0x99) && !match_byte(0x98))
639     match_byte_assert(0x97);
640   match_byte_assert(7);
641   match_byte_assert(0);
642   match_byte_assert(0);
643   if (match_byte('.'))
644     match_byte_assert(',');
645   else
646     {
647       match_byte_assert(',');
648       if (!match_byte('.'))
649         match_byte_assert(' ');
650     }
651   match_u32_assert(5);
652   for (int i = 0; i < 5; i++)
653     get_string();
654   pos += get_u32();
655   if (pos != find_dimensions())
656     fprintf (stderr, "%08x / %08x\n", pos, find_dimensions());
657 }
658
659 int
660 main(int argc, char *argv[])
661 {
662   size_t start;
663   struct stat s;
664
665   if (isatty(STDIN_FILENO))
666     {
667       fprintf(stderr, "redirect stdin from a .bin file\n");
668       exit(1);
669     }
670   if (fstat(STDIN_FILENO, &s))
671     {
672       perror("fstat");
673       exit(1);
674     }
675   n = s.st_size;
676   data = malloc(n);
677   if (!data)
678     {
679       perror("malloc");
680       exit(1);
681     }
682   if (read(STDIN_FILENO, data, n) != n)
683     {
684       perror("read");
685       exit(1);
686     }
687
688   if (argc > 1)
689     {
690       if (!strcmp(argv[1], "title0"))
691         {
692           pos = 0x27;
693           if (match_byte (0x03)
694               || (match_byte (0x05) && match_byte (0x58)))
695             printf ("%s\n", get_string());
696           else
697             printf ("<unknown>\n");
698           return 0;
699         }
700       else if (!strcmp(argv[1], "title"))
701         {
702           dump_title();
703           exit(0);
704         }
705       else if (!strcmp(argv[1], "titleraw"))
706         {
707           const char fonts[] = "\x01\x31\x09\0\0\0SansSerif";
708           start = 0x27;
709           n = find(fonts, sizeof fonts - 1);
710         }
711       else if (!strcmp(argv[1], "fonts"))
712         {
713           const char fonts[] = "\x01\x31\x09\0\0\0SansSerif";
714           const char styles[] = "\xf0\0\0\0";
715           start = find(fonts, sizeof fonts - 1);
716           n = find(styles, sizeof styles - 1);
717         }
718       else if (!strcmp(argv[1], "styles"))
719         {
720           const char styles[] = "\xf0\0\0\0";
721           const char dimensions[] = "-,,,.\0";
722           start = find(styles, sizeof styles - 1);
723           n = find(dimensions, sizeof dimensions - 1) + sizeof dimensions - 1;
724         }
725       else if (!strcmp(argv[1], "dimensions") || !strcmp(argv[1], "all"))
726         {
727           pos = 0;
728           match_byte_assert(1);
729           match_byte_assert(0);
730           match_u32_assert(3);
731           match_byte_assert(1);
732           if (!match_byte(0))
733             match_byte_assert(1);
734           match_byte_assert(0);
735           match_byte_assert(0);
736           if (!match_byte(0))
737             match_byte_assert(1);
738           pos++;
739           match_byte_assert(0);
740           match_byte_assert(0);
741           match_byte_assert(0);
742           dump_title ();
743           dump_fonts();
744           dump_dims ();
745           printf("\n\ndata:\n");
746           dump_data ();
747           if (pos == n - 1)
748             match_byte_assert (1);
749           if (pos != n)
750             {
751               fprintf (stderr, "%x / %x\n", pos, n);
752               exit(1);
753             }
754           exit(0);
755         }
756       else
757         {
758           fprintf (stderr, "unknown section %s\n", argv[1]);
759           exit(1);
760         }
761     }
762   else
763     start = 0x27;
764
765   for (size_t i = start; i < n; )
766     {
767       if (i + 5 <= n
768           && data[i]
769           //&& !data[i + 1]
770           && !data[i + 2]
771           && !data[i + 3]
772           && i + 4 + data[i] + data[i + 1] * 256 <= n
773           && all_ascii(&data[i + 4], data[i] + data[i + 1] * 256))
774         {
775           fputs("\n\"", stdout);
776           fwrite(&data[i + 4], 1, data[i] + data[i + 1] * 256, stdout);
777           fputs("\" ", stdout);
778
779           i += 4 + data[i] + data[i + 1] * 256;
780         }
781       else if (i + 12 <= n
782                && data[i + 1] == 40
783                && data[i + 2] == 5
784                && data[i + 3] == 0)
785         {
786           double d;
787
788           memcpy (&d, &data[i + 4], 8);
789           printf ("F40.%d(%.*f)\n", data[i], data[i], d);
790           i += 12;
791         }
792       else if (i + 12 <= n
793                && data[i + 1] == 40
794                && data[i + 2] == 31
795                && data[i + 3] == 0)
796         {
797           double d;
798
799           memcpy (&d, &data[i + 4], 8);
800           printf ("PCT40.%d(%.*f)\n", data[i], data[i], d);
801           i += 12;
802         }
803       else if (i + 4 <= n
804                && (data[i] && data[i] != 88 && data[i] != 0x41)
805                && !data[i + 1]
806                && !data[i + 2]
807                && !data[i + 3])
808         {
809           printf ("i%d ", data[i]);
810           i += 4;
811         }
812       else
813         {
814           printf("%02x ", data[i]);
815           i++;
816         }
817     }
818
819   return 0;
820 }