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