ff9e5300692260ae9f705fe3effb7171d683e544
[pspp] / dump-spo2.c
1 #include <assert.h>
2 #include <errno.h>
3 #include <fcntl.h>
4 #include <float.h>
5 #include <inttypes.h>
6 #include <stdbool.h>
7 #include <stdint.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <sys/stat.h>
12 #include <time.h>
13 #include <unistd.h>
14 #include "u8-mbtouc.h"
15
16 static const char *filename;
17 static uint8_t *data;
18 static size_t n;
19
20 int version;
21
22 unsigned int pos;
23
24 static int n_dims;
25
26 #define XSTR(x) #x
27 #define STR(x) XSTR(x)
28 #define WHERE __FILE__":" STR(__LINE__)
29
30 static void __attribute__((unused))
31 hex_dump(FILE *stream, int ofs, int n);
32
33 static uint8_t
34 get_byte(void)
35 {
36   return data[pos++];
37 }
38
39 static unsigned int
40 get_u32(void)
41 {
42   uint32_t x;
43   memcpy(&x, &data[pos], 4);
44   pos += 4;
45   return x;
46 }
47
48 static unsigned long long int
49 get_u64(void)
50 {
51   uint64_t x;
52   memcpy(&x, &data[pos], 8);
53   pos += 8;
54   return x;
55 }
56
57 static unsigned int
58 get_be32(void)
59 {
60   uint32_t x;
61   x = (data[pos] << 24) | (data[pos + 1] << 16) | (data[pos + 2] << 8) | data[pos + 3];
62   pos += 4;
63   return x;
64 }
65
66 static unsigned int
67 get_u16(void)
68 {
69   uint16_t x;
70   memcpy(&x, &data[pos], 2);
71   pos += 2;
72   return x;
73 }
74
75 static double
76 get_double(void)
77 {
78   double x;
79   memcpy(&x, &data[pos], 8);
80   pos += 8;
81   return x;
82 }
83
84 static double __attribute__((unused))
85 get_float(void)
86 {
87   float x;
88   memcpy(&x, &data[pos], 4);
89   pos += 4;
90   return x;
91 }
92
93 static bool
94 match_u32(uint32_t x)
95 {
96   if (get_u32() == x)
97     return true;
98   pos -= 4;
99   return false;
100 }
101
102 bool
103 match_u16(uint16_t x)
104 {
105   if (get_u16() == x)
106     return true;
107   pos -= 2;
108   return false;
109 }
110
111 static void
112 match_u32_assert(uint32_t x, const char *where)
113 {
114   unsigned int y = get_u32();
115   if (x != y)
116     {
117       fprintf(stderr, "%s: 0x%x: expected i%u, got i%u: ", where, pos - 4, x, y);
118       hex_dump(stderr, pos - 4, 64);
119       exit(1);
120     }
121 }
122 #define match_u32_assert(x) match_u32_assert(x, WHERE)
123
124 static void
125 match_u16_assert(uint16_t x, const char *where)
126 {
127   unsigned int y = get_u16();
128   if (x != y)
129     {
130       fprintf(stderr, "%s: 0x%x: expected u16:%u, got u16:%u: ", where, pos - 2, x, y);
131       hex_dump(stderr, pos - 2, 64);
132       exit(1);
133     }
134 }
135 #define match_u16_assert(x) match_u16_assert(x, WHERE)
136
137 static bool __attribute__((unused))
138 match_u64(uint64_t x)
139 {
140   if (get_u64() == x)
141     return true;
142   pos -= 8;
143   return false;
144 }
145
146 static void __attribute__((unused))
147 match_u64_assert(uint64_t x, const char *where)
148 {
149   unsigned long long int y = get_u64();
150   if (x != y)
151     {
152       fprintf(stderr, "%s: 0x%x: expected u64:%lu, got u64:%llu\n", where, pos - 8, x, y);
153       exit(1);
154     }
155 }
156 #define match_u64_assert(x) match_u64_assert(x, WHERE)
157
158 static bool __attribute__((unused))
159 match_be32(uint32_t x)
160 {
161   if (get_be32() == x)
162     return true;
163   pos -= 4;
164   return false;
165 }
166
167 static void
168 match_be32_assert(uint32_t x, const char *where)
169 {
170   unsigned int y = get_be32();
171   if (x != y)
172     {
173       fprintf(stderr, "%s: 0x%x: expected be%u, got be%u\n", where, pos - 4, x, y);
174       exit(1);
175     }
176 }
177 #define match_be32_assert(x) match_be32_assert(x, WHERE)
178
179 static bool
180 match_byte(uint8_t b)
181 {
182   if (pos < n && data[pos] == b)
183     {
184       pos++;
185       return true;
186     }
187   else
188     return false;
189 }
190
191 static void
192 match_byte_assert(uint8_t b, const char *where)
193 {
194   if (!match_byte(b))
195     {
196       fprintf(stderr, "%s: 0x%x: expected %02x, got %02x: ", where, pos, b, data[pos]);
197       hex_dump(stderr, pos, 64);
198       exit(1);
199     }
200 }
201 #define match_byte_assert(b) match_byte_assert(b, WHERE)
202
203 static bool
204 match_bytes(int start, const int *bytes, size_t n_bytes)
205 {
206   for (size_t i = 0; i < n_bytes; i++)
207     if (bytes[i] >= 0 && data[start + i] != bytes[i])
208       return false;
209   return true;
210 }
211
212 static char *
213 xmemdup0(const void *p, size_t n)
214 {
215   char *s = malloc(n + 1);
216   memcpy(s, p, n);
217   s[n] = 0;
218   return s;
219 }
220
221 static bool
222 get_bool(void)
223 {
224   if (match_byte(0))
225     return false;
226   match_byte_assert(1);
227   return true;
228 }
229
230 static bool __attribute__((unused))
231 is_ascii(uint8_t p)
232 {
233   return (p >= ' ' && p < 127) || p == '\r' || p == '\n' || p == '\t';
234 }
235
236 static int
237 count_zeros(const uint8_t *p)
238 {
239   size_t n = 0;
240   while (p[n] == 0)
241     n++;
242   return n;
243 }
244
245 static bool __attribute__((unused))
246 all_utf8(const char *p_, size_t len)
247 {
248   const uint8_t *p = (const uint8_t *) p_;
249   for (size_t ofs = 0, mblen; ofs < len; ofs += mblen)
250     {
251       ucs4_t uc;
252
253       mblen = u8_mbtouc (&uc, p + ofs, len - ofs);
254       if ((uc < 32 && uc != '\n') || uc == 127 || uc == 0xfffd)
255         return false;
256     }
257   return true;
258 }
259
260 static char *
261 pull_string(int len, const char *where)
262 {
263   assert (len >= 0);
264   for (int i = 0; i < len - 1; i++)
265     if (!data[pos + i])
266       {
267         fprintf(stderr, "%s: %d-byte string starting at 0x%x has null byte "
268                 "at offset %d: ", where, len, pos, i);
269         hex_dump(stderr, pos, len + 64);
270         exit(1);
271       }
272
273   char *s = xmemdup0(&data[pos], len);
274   pos += len;
275   return s;
276 }
277
278 static char *
279 get_string2(const char *where)
280 {
281   return pull_string(get_u16(), where);
282 }
283 #define get_string2() get_string2(WHERE)
284
285 static char *
286 get_string1(const char *where)
287 {
288   int len = data[pos++];
289   return len == 0xff ? (get_string2)(where) : pull_string(len, where);
290 }
291 #define get_string1() get_string1(WHERE)
292
293 static void
294 match_string1_assert(const char *exp, const char *where)
295 {
296   int start = pos;
297   char *act = (get_string1)(where);
298   if (strcmp(act, exp)) 
299     {
300       fprintf(stderr, "%s: 0x%x: expected \"%s\", got \"%s\"\n",
301               where, start, exp, act);
302       exit(1);
303     }
304 }
305 #define match_string1_assert(x) match_string1_assert(x, WHERE)
306
307 static void
308 match_string2_assert(const char *exp, const char *where)
309 {
310   int start = pos;
311   char *act = (get_string2)(where);
312   if (strcmp(act, exp)) 
313     {
314       fprintf(stderr, "%s: 0x%x: expected \"%s\", got \"%s\"\n",
315               where, start, exp, act);
316       exit(1);
317     }
318 }
319 #define match_string2_assert(x) match_string2_assert(x, WHERE)
320
321 static char *
322 get_string4(const char *where)
323 {
324   assert(data[pos + 3] == 0);
325   return pull_string(get_u32(), where);
326 }
327 #define get_string4() get_string4(WHERE)
328
329 static char *
330 get_padded_string(int len)
331 {
332   char *s = xmemdup0(&data[pos], len);
333   pos += len;
334   return s;
335 }
336
337 static char *
338 get_string_be(const char *where)
339 {
340   if (1
341       /*data[pos + 1] == 0 && data[pos + 2] == 0 && data[pos + 3] == 0*/
342       /*&& all_ascii(&data[pos + 4], data[pos])*/)
343     {
344       int len = data[pos + 2] * 256 + data[pos + 3];
345       char *s = malloc(len + 1);
346
347       memcpy(s, &data[pos + 4], len);
348       s[len] = 0;
349       pos += 4 + len;
350       return s;
351     }
352   else
353     {
354       fprintf(stderr, "%s: 0x%x: expected string\n", where, pos);
355       exit(1);
356     }
357 }
358 #define get_string_be() get_string_be(WHERE)
359
360 static int
361 get_end(void)
362 {
363   int len = get_u32();
364   return pos + len;
365 }
366
367 static void __attribute__((unused))
368 hex_dump(FILE *stream, int ofs, int n)
369 {
370   int n_ascii = 0;
371   for (int i = 0; i < n; i++)
372     {
373       int c = data[ofs + i];
374       n_ascii += is_ascii(c);
375       fprintf(stream, " %02x", c);
376     }
377   if (n_ascii >= 3)
378     {
379       putc(' ', stream);
380       for (int i = 0; i < n; i++)
381         {
382           int c = data[ofs + i];
383           putc(c >= 32 && c < 127 ? c : '.', stream);
384         }
385     }
386   putc('\n', stream);
387 }
388
389 static void __attribute__((unused))
390 char_dump(FILE *stream, int ofs, int n)
391 {
392   for (int i = 0; i < n; i++)
393     {
394       int c = data[ofs + i];
395       putc(c >= 32 && c < 127 ? c : '.', stream);
396     }
397   putc('\n', stream);
398 }
399
400
401 static int
402 compare_int(const void *a_, const void *b_)
403 {
404   const int *a = a_;
405   const int *b = b_;
406   return *a < *b ? -1 : *a > *b;
407 }
408
409
410 static const char *
411 format_name (int format, char *buf)
412 {
413   switch (format)
414     {
415     case 1: return "A";
416     case 2: return "AHEX";
417     case 3: return "COMMA";
418     case 4: return "DOLLAR";
419     case 5: case 40: return "F";
420     case 6: return "IB";
421     case 7: return "PIBHEX";
422     case 8: return "P";
423     case 9: return "PIB";
424     case 10: return "PK";
425     case 11: return "RB";
426     case 12: return "RBHEX";
427     case 15: return "Z";
428     case 16: return "N";
429     case 17: return "E";
430     case 20: return "DATE";
431     case 21: return "TIME";
432     case 22: return "DATETIME";
433     case 23: return "ADATE";
434     case 24: return "JDATE";
435     case 25: return "DTIME";
436     case 26: return "WKDAY";
437     case 27: return "MONTH";
438     case 28: return "MOYR";
439     case 29: return "QYR";
440     case 30: return "WKYR";
441     case 31: return "PCT";
442     case 32: return "DOT";
443     case 33: return "CCA";
444     case 34: return "CCB";
445     case 35: return "CCC";
446     case 36: return "CCD";
447     case 37: return "CCE";
448     case 38: return "EDATE";
449     case 39: return "SDATE";
450     default: sprintf(buf, "(%d)", format); return buf;
451     }
452 }
453
454 static void
455 parse_format(void)
456 {
457   int d = data[pos++];
458   int w = data[pos++];
459   int fmt = data[pos++];
460   char buf[32];
461   printf ("%s%d.%d", format_name(fmt, buf), w, d);
462 }
463
464 static void
465 parse_heading(const char *name)
466 {
467   match_u16_assert(0xffff);
468   match_u16_assert(0);
469   match_string2_assert(name);
470   printf("%#x: %s\n", pos, name);
471 }
472
473 static void
474 match_zeros_assert(int count, const char *where)
475 {
476   for (int i = 0; i < count; i++)
477     if (data[pos + i])
478       {
479         fprintf (stderr,
480                  "%s: %#x: expected %d zeros here but offset %d is %#"PRIx8": ",
481                  where, pos, count, i, data[pos + i]);
482         hex_dump (stderr, pos, 64);
483         exit (1);
484       }
485   pos += count;
486 }
487 #define match_zeros_assert(count) match_zeros_assert(count, WHERE)
488
489 static void
490 put_safe(const char *s)
491 {
492   while (*s)
493     {
494       if (*s == '\n')
495         printf ("\\n");
496       else if (*s == '\r')
497         printf ("\\r");
498       else if (*s < 0x20 || *s > 0x7e)
499         printf ("\\x%02"PRIx8, (uint8_t) *s);
500       else
501         putchar (*s);
502       s++;
503     }
504 }
505
506 static void parse_flexible(void);
507
508 static int count;
509 static void
510 parse_DspString(void)
511 {
512   printf("%#x: DspString#%d(", pos, count++);
513   if (match_byte(2))
514     {
515       printf("%f, \"", get_double());
516       printf("%s\")\n", get_string1());
517     }
518   else
519     {
520       match_byte_assert(1);
521       parse_format();
522       printf(" \"");
523       match_byte_assert(0);
524       match_byte_assert(1);
525       put_safe(get_string1());
526       printf("\")\n");
527     }
528   
529 }
530
531 static void
532 match_DspString(void)
533 {                               /* 05 80 */
534   match_byte_assert(5);
535   match_byte_assert(0x80);
536   parse_DspString();
537 }
538
539 static void
540 parse_DspSimpleText(void)
541 {
542   match_byte_assert(0);
543   if (match_byte(0))
544     {
545       match_zeros_assert(3);
546       if (!match_byte(0x10))
547         match_byte_assert(0);
548       match_zeros_assert(4);
549     }
550   /* Followed by DspString or DspNumber. */
551 }
552
553 static void
554 match_DspSimpleText(void)
555 {                               /* 03 80 */
556   match_byte_assert(3);
557   match_byte_assert(0x80);
558   parse_DspSimpleText();
559 }
560
561 static void
562 parse_weirdness(void)
563 {
564   match_byte_assert(1);
565   get_u32();
566   match_zeros_assert(12);
567   pos++;                        /* 90 or BC */
568   if (!match_byte(2))
569     match_byte_assert(1);
570   match_zeros_assert(5);
571   pos++;
572   match_zeros_assert(3);
573   puts(get_padded_string(32));
574 }
575
576 static void
577 parse_NavTreeViewItem(void)
578 {                               /* 07 80 */
579   int start_pos = pos;
580   match_zeros_assert(1);
581   if (!match_byte(0) && !match_byte(7) && !match_byte(2) && !match_byte(0xc))
582     match_byte_assert(8);
583   match_zeros_assert(3);
584   pos++;
585   match_byte_assert(0);
586   match_byte_assert(1);
587   match_byte_assert(0);
588   if (match_byte(0))
589     {
590       match_byte_assert(0);
591       if (!match_byte(1))
592         match_byte_assert(0);
593       match_zeros_assert(5);
594       if (!match_byte(0))
595         match_byte_assert(1);
596       match_zeros_assert(5);
597       get_string1();
598       if (match_byte(1))
599         {
600           if (data[pos] == 1)
601             {
602               parse_weirdness();
603               match_byte_assert(0);
604               pos++;
605               match_zeros_assert(11);
606               match_byte_assert(1);
607               match_zeros_assert(3);
608               get_string4();
609               match_byte_assert(0);
610               if (match_byte(0))
611                 {
612                   match_zeros_assert(2);
613                   if (match_u32(8500))
614                     match_u32_assert(11000);
615                   else
616                     {
617                       match_u32_assert(11000);
618                       match_u32_assert(8500);
619                     }
620                   pos += 32;
621                   get_string1();
622                   if (!match_byte(0))
623                     match_byte_assert(1);
624                   pos++;
625                   pos++;
626                   pos++;
627                   pos++;
628                   get_string4();                /* page title */
629                   match_byte_assert(1);
630                   match_byte_assert(1);
631                   match_zeros_assert(3);
632                   get_string4();                /* page number */
633                   match_byte_assert(0);
634                   pos += 2;
635                   match_u16_assert(2);
636                 }
637               parse_flexible();
638             }
639         }
640       else
641         match_zeros_assert(3);
642     }
643   //fprintf(stderr, "%#x ", pos - 16);
644   hex_dump(stdout, start_pos, pos - start_pos);
645 }
646
647 static void
648 match_NavTreeViewItem(void)
649 {
650   match_byte_assert(7);
651   match_byte_assert(0x80);
652   parse_NavTreeViewItem();
653 }
654
655 static void
656 parse_DspNumber(void)
657 {
658   printf("%#x: DspNumber#%d(", pos, count++);
659   match_byte_assert(1);
660   parse_format();
661   match_byte_assert(0x80);
662   match_byte(2);
663   printf (" %f", get_double());
664   printf (" \"%s\")\n", get_string1());
665 }
666
667 static void
668 match_DspNumber(void)
669 {
670   if (!match_byte(0x18) && !match_byte(0x19))
671     match_byte_assert(0x2a);
672   match_byte_assert(0x80);
673   parse_DspNumber();
674 }
675
676 static void
677 parse_DspCell(void)
678 {
679   match_byte_assert(0);
680 }
681
682 static void
683 match_DspCell(void)
684 {                               /* 27 80 */
685   match_byte_assert(0x27);
686   match_byte_assert(0x80);
687   parse_DspCell();
688 }
689
690 static void
691 parse_NavLog(void)
692 {
693   match_byte_assert(2);
694   pos += 32;
695 }
696
697 static void
698 match_NavLog(void)
699 {                               /* 09 80 */
700   match_byte_assert(9);
701   match_byte_assert(0x80);
702   parse_NavLog();
703 }
704
705 static void
706 parse_category(int level, int j, int *n_leaves)
707 {
708   for (size_t k = 0; k < level; k++)
709     putchar('\t');
710   get_u16(); match_byte_assert(0);
711   get_u16(); match_byte_assert(0);
712   int leaf_idx = get_u32();
713   printf("%d ", leaf_idx);
714   match_u32_assert(0);
715   if (get_u16() == 0xffff)
716     match_u16_assert(0xffff);
717   else
718     match_u16_assert(0x0e74);
719   match_byte_assert(0);
720   match_DspSimpleText();
721   if (match_u16(0x8018))
722     {
723       printf("18 80(%02x %02x %02x) ",
724              data[pos], data[pos + 1], data[pos + 2]);
725       pos += 3;
726     }
727   match_DspString();
728
729   int n_subcategories = get_u32();
730   if (n_subcategories)
731     assert (leaf_idx == 0);
732   else
733     {
734       assert (leaf_idx == *n_leaves);
735       ++*n_leaves;
736     }
737   for (int k = 0; k < n_subcategories; k++)
738     parse_category(level + 1, k, n_leaves);
739 }
740
741 static void
742 parse_dimension(int i)
743 {
744   printf ("%#x: dimension %d\n", pos, i);
745   if (i == 0)
746     {
747       match_zeros_assert(5);
748       match_u32_assert(1);
749     }
750   else
751     {
752       match_zeros_assert(6);
753
754       int n_units16 = get_u32();
755       match_u16_assert(1);
756       for (int j = 0; j < n_units16; j++)
757         get_u16();
758
759       match_byte_assert(0);
760
761       int n_units32 = get_u32();
762       match_u16_assert(0);
763       for (int j = 0; j < n_units32; j++)
764         get_u32();
765
766       get_u16(); match_byte_assert(0);
767
768       get_u16(); match_byte_assert(0);
769       get_u16(); match_byte_assert(0);
770       match_u32_assert(0);
771       match_u32_assert(1);
772     }
773
774   get_u16();
775   if (!match_u16(0xffff))
776     match_u16_assert(0x0e74);
777   match_byte_assert(0);
778   match_DspSimpleText();
779   match_DspString();
780
781   int n_leaves = 0;
782   int n_categories = get_u32();
783   for (int j = 0; j < n_categories; j++)
784     parse_category(1, j, &n_leaves);
785 }
786
787 static void
788 parse_PMModelItemInfo(void)
789 {
790   for (int i = 0; i < n_dims; i++)
791     parse_dimension(i);
792   printf("%#x: end of model\n", pos);
793   exit(0);
794 }
795
796 static void
797 match_PMModelItemInfo(void)
798 {                               /* 54 80 */
799   match_byte_assert(0x54);
800   match_byte_assert(0x80);
801   parse_PMModelItemInfo();
802   /* DspSimpleText */
803   /* DspString */
804 }
805
806 static void
807 match_PMPivotItemTree(void)
808 {                               /* 52 80 */
809   match_byte_assert(0x52);
810   match_byte_assert(0x80);
811   match_byte_assert(0);
812   match_PMModelItemInfo();
813 }
814
815 static void
816 parse_NavHead(void)
817 {
818   match_byte_assert(2);
819   match_zeros_assert(24);
820   match_byte_assert(1);
821   match_zeros_assert(3);
822   if (!match_byte(1))
823     match_byte_assert(0);
824   match_zeros_assert(3);
825   /* DspSimpleText */
826 }
827
828 static void
829 parse_NavOleItem(void)
830 {
831   match_byte_assert(0);
832   match_byte_assert(1);
833   match_zeros_assert(2);
834   pos++;
835   match_zeros_assert(9);
836   match_byte_assert(1);
837   match_zeros_assert(10);
838   match_byte_assert(1);
839   match_zeros_assert(5);
840   get_string1();
841   match_byte_assert(1);
842   parse_weirdness();
843   match_byte_assert(0);
844   pos++;
845   match_zeros_assert(11);
846   match_byte_assert(1);
847   match_zeros_assert(3);
848   get_string4();
849   match_byte_assert(0);
850 }
851
852 static void
853 match_NavOleItem(void)
854 {                               /* 0e 80 or 12 80*/
855   if (!match_byte(0x12))
856     match_byte_assert(0x0e);
857   match_byte_assert(0x80);
858   parse_NavOleItem();
859 }
860
861 static void
862 parse_NavTitle(void)
863 {
864   match_byte_assert(2);
865   match_zeros_assert(8);
866   match_u32_assert(24);
867   get_u32();
868   pos++;
869   if (!match_byte(3))
870     match_byte_assert(4);
871   match_zeros_assert(2);
872   get_u32();
873   match_u32_assert(2);
874   if (!match_u32(2))
875     match_u32_assert(1);
876 }
877
878 static void
879 parse_NavNote(void)
880 {
881   match_byte_assert(2);
882   match_zeros_assert(8);
883   match_u32_assert(24);
884   if (!match_u32(0) && !match_u32(0xffffff4b))
885     match_u32_assert(-40);
886   pos += 8;
887   match_u32_assert(2);
888   if (!match_u32(2))
889     match_u32_assert(1);
890 }
891
892 static void
893 parse_PTPivotController(void)
894 {
895   match_byte_assert(2);
896   pos += 8;
897   match_u32_assert(100);
898   match_u32_assert(100);
899   match_u32_assert(100);
900   match_u32_assert(100);
901 }
902
903 static void
904 parse_PVPivotView(void)
905 {
906   match_byte_assert(5);
907   printf ("PVPivotView(%d)\n", get_u32());
908 }
909
910 static void
911 parse_NDimensional__DspCell(void)
912 {
913   match_byte_assert(0);
914   n_dims = get_u32();
915   printf ("NDimensional__DspCell(n_dims=%d)\n", n_dims);
916 }
917
918 static void
919 parse_IndexedCollection(void)
920 {
921   printf("IndexedCollection");
922   for (size_t i = 0; ; i++)
923     {
924       match_byte_assert(0);
925       printf("%c%d", i ? 'x' : '(', get_u32());
926       match_u16_assert(1);
927       if (!match_u16(0x8011))
928         break;
929     }
930   printf(")\n");
931 }
932
933 static void
934 parse_PTTableLook(void)
935 {
936   match_byte_assert(2);
937   match_byte_assert(2);
938   match_zeros_assert(7);
939   match_u32_assert(0x36);
940   match_u32_assert(0x12);
941 }
942
943 static void
944 parse_PVViewDimension(void)
945 {
946   while (data[pos + 1] != 0x80
947          && (data[pos] != 0xff || data[pos + 1] != 0xff))
948     {
949       assert(pos < n);
950       pos++;
951     }
952 }
953
954 static void
955 parse_PVSeparatorStyle(void)
956 {
957   match_byte_assert(0);
958   match_byte_assert(1);
959   match_zeros_assert(15);
960   pos++;
961   match_byte_assert(0x80);
962   match_byte_assert(0);
963
964   match_byte_assert(1);
965   match_zeros_assert(9);
966   while (data[pos + 1] != 0x80
967          && (data[pos] != 0xff || data[pos + 1] != 0xff))
968     {
969       assert(pos < n);
970       pos++;
971     }
972 }
973
974 static void
975 parse_PVCellStyle(void)
976 {
977   match_byte_assert(0);
978   match_byte_assert(1);
979   match_zeros_assert(5);
980   match_u32_assert(0xffffff);
981   match_zeros_assert(2);
982 }
983
984 static void
985 skip_item(const char *name)
986 {
987   int start_pos = pos;
988   printf("%#x: skipping %s bytes...", pos, name);
989   while (data[pos + 1] != 0x80
990          && !(data[pos] == 0xff && data[pos + 1] == 0xff
991               && data[pos + 2] == 0 && data[pos + 3] == 0))
992     {
993       assert(pos < n);
994       pos++;
995     }
996   printf("until %#x:", pos);
997   hex_dump(stdout, start_pos, pos - start_pos);
998 }
999
1000 static void
1001 parse_flexible(void)
1002 {
1003   int start = pos;
1004   if (match_u16(0xffff))
1005     {
1006       match_u16_assert(0);
1007       char *heading = get_string2();
1008       printf("%#x: %s\n", pos, heading);
1009       if (!strcmp(heading, "NavRoot"))
1010         {
1011           match_byte_assert(2);
1012           match_zeros_assert(32);
1013         }
1014       else if (!strcmp(heading, "NavPivot"))
1015         {
1016           hex_dump(stdout, pos, 021);
1017           pos += 0x21;
1018         }
1019       else if (!strcmp(heading, "DspCell"))
1020         parse_DspCell();
1021       else if (!strcmp(heading, "DspSimpleText"))
1022         parse_DspSimpleText();
1023       else if (!strcmp(heading, "DspNumber"))
1024         parse_DspNumber();
1025       else if (!strcmp(heading, "DspString"))
1026         parse_DspString();
1027       else if (!strcmp(heading, "NavHead"))
1028         parse_NavHead();
1029       else if (!strcmp(heading, "NavTreeViewItem"))
1030         {
1031           if (0)
1032             parse_NavTreeViewItem();
1033           else
1034             skip_item(heading);
1035         }
1036       else if (!strcmp(heading, "IndexedCollection"))
1037         parse_IndexedCollection();
1038       else if (!strcmp(heading, "NavOleItem"))
1039         parse_NavOleItem();
1040       else if (!strcmp(heading, "NavTitle"))
1041         parse_NavTitle();
1042       else if (!strcmp(heading, "NavNote"))
1043         parse_NavNote();
1044       else if (!strcmp(heading, "PTPivotController"))
1045         parse_PTPivotController();
1046       else if (!strcmp(heading, "PVPivotView"))
1047         parse_PVPivotView();
1048       else if (!strcmp(heading, "PMPivotModel"))
1049         match_byte_assert(3);
1050       else if (!strcmp(heading, "NDimensional__DspCell"))
1051         parse_NDimensional__DspCell();
1052       else if (!strcmp(heading, "PMPivotItemTree"))
1053         match_byte_assert(0);
1054       else if (!strcmp(heading, "PMModelItemInfo"))
1055         parse_PMModelItemInfo();
1056       else if (!strcmp(heading, "AbstractTreeBranch"))
1057         match_byte_assert(0);
1058       else if (!strcmp(heading, "PTTableLook"))
1059         parse_PTTableLook();
1060       else if (!strcmp(heading, "PVViewDimension"))
1061         parse_PVViewDimension();
1062       else if (!strcmp(heading, "PVSeparatorStyle"))
1063         parse_PVSeparatorStyle();
1064       else if (!strcmp(heading, "PVCellStyle"))
1065         parse_PVCellStyle();
1066       else if (!strcmp(heading, "PVTextStyle"))
1067         exit(0);
1068       else
1069         {
1070           fprintf(stderr, "don't know %s at offset 0x%x: ", heading, start);
1071           hex_dump(stderr, pos, 128);
1072           assert(0);
1073         }
1074     }
1075   else if (data[pos + 1] == 0x80)
1076     {
1077       if ((data[pos] == 0x2a || data[pos] == 0x18 || data[pos] == 0x19) && data[pos + 1] == 0x80)
1078         match_DspNumber();
1079       else if (data[pos] == 0x27 && data[pos + 1] == 0x80)
1080         match_DspCell();
1081       else if (data[pos] == 0x5 && data[pos + 1] == 0x80)
1082         match_DspString();
1083       else if (data[pos] == 0x7 && data[pos + 1] == 0x80)
1084         match_NavTreeViewItem();
1085       else if (data[pos] == 0x3 && data[pos + 1] == 0x80)
1086         match_DspSimpleText();
1087       else if ((data[pos] == 0x3c || data[pos] == 0x39)
1088                && data[pos + 1] == 0x80)
1089         {
1090           /* 3c 80 */
1091           /* 39 80 */
1092           printf("%#x: %02x %02x ", pos, data[pos], data[pos + 1]);
1093           pos += 2;
1094           parse_format();
1095           printf ("\n");
1096 /*      match_byte_assert(0x01);
1097         match_byte_assert(0x02);
1098         match_byte_assert(0x0d); */
1099         }
1100       else if ((data[pos] == 0x15 || data[pos] == 0x14)
1101                && data[pos + 1] == 0x80)
1102         {
1103           /* 14 80 */
1104           /* 15 80 */
1105           pos += 2;
1106           if (match_byte(2))
1107             {
1108               printf ("%02x 80(%f", data[pos - 2], get_double());
1109               printf (" \"%s\")\n", get_string1());
1110               if (match_byte(1))
1111                 {
1112                   match_byte_assert(0);
1113                   get_string1();
1114                   if (!match_byte(2) && !match_byte(3))
1115                     match_byte_assert(0);
1116                   match_zeros_assert(3);
1117                   get_string1();
1118                   match_byte_assert(0);
1119                   match_byte_assert(1);
1120                   match_zeros_assert(3);
1121                   match_byte_assert(1);
1122                   match_byte_assert(0);
1123                 }
1124             }
1125           else
1126             {
1127               match_byte_assert(0);
1128             }
1129         }
1130       else if (data[pos] == 0x17)
1131         {
1132           printf("%02x %02x(%02x %02x %02x)\n",
1133                  data[pos], data[pos + 1],
1134                  data[pos + 2], data[pos + 3], data[pos + 4]);
1135           pos += 5;
1136         }
1137       else if (data[pos] == 0x9 && data[pos + 1] == 0x80)
1138         {
1139           match_NavLog();
1140         }
1141       else if (data[pos] == 0xe || data[pos] == 0x12)
1142         match_NavOleItem();
1143       else if (data[pos] == 0x11 || data[pos] == 0x13)
1144         {
1145           int type = data[pos];
1146           pos += 2;
1147           match_byte_assert(0);
1148           if (data[pos] != 0)
1149             {
1150               int x = get_u32();
1151               int y = get_u16();
1152               if (y == 0)
1153                 {
1154                   int index = get_u32();
1155                   printf("%02x 80(footnote %d)\n", type, index);
1156                 }
1157               else
1158                 printf("%02x 80(%d %d)\n", type, x, y);
1159             }
1160           else
1161             match_zeros_assert(13);
1162         }
1163       else if (data[pos] == 0x29 ||
1164                data[pos] == 0x2b ||
1165                data[pos] == 0x2d ||
1166                data[pos] == 0x31 ||
1167                data[pos] == 0x32 ||
1168                data[pos] == 0x4a ||
1169                data[pos] == 0x4c ||
1170                data[pos] == 0x4f ||
1171                data[pos] == 0x4d ||
1172                data[pos] == 0x50 ||
1173                data[pos] == 0x36 ||
1174                data[pos] == 0x52 ||
1175                data[pos] == 0x53 ||
1176                data[pos] == 0x54 ||
1177                data[pos] == 0x55 ||
1178                data[pos] == 0x57 ||
1179                data[pos] == 0x56 ||
1180                data[pos] == 0x58 ||
1181                data[pos] == 0x5c ||
1182                data[pos] == 0x5b ||
1183                data[pos] == 0x5e ||
1184                data[pos] == 0x62 ||
1185                data[pos] == 0x64 ||
1186                data[pos] == 0x4e ||
1187                data[pos] == 0x51 ||
1188                data[pos] == 0x59 ||
1189                data[pos] == 0x5a ||
1190                data[pos] == 0x5d ||
1191                data[pos] == 0x66 ||
1192                data[pos] == 0x60 ||
1193                data[pos] == 0x68 ||
1194                data[pos] == 0x48 ||
1195                data[pos] == 0x6a ||
1196                data[pos] == 0x37)
1197         {
1198           pos += 2;
1199           match_byte_assert(0);
1200         }
1201       else if (data[pos] == 0x2c ||
1202                data[pos] == 0x2e ||
1203                data[pos] == 0x30 ||
1204                data[pos] == 0x34 ||
1205                data[pos] == 0x3d ||
1206                data[pos] == 0x40 ||
1207                data[pos] == 0x3f ||
1208                data[pos] == 0x42 ||
1209                data[pos] == 0x43 ||
1210                data[pos] == 0x44 ||
1211                data[pos] == 0x49 ||
1212                data[pos] == 0x3e ||
1213                data[pos] == 0x46)
1214         {
1215           printf ("%#x: %02x %02x(%02x %02x %02x)\n",
1216                   pos, data[pos], data[pos + 1],
1217                   data[pos + 2], data[pos + 3], data[pos + 4]);
1218           pos += 2;
1219           pos += 3;
1220         }
1221       else
1222         {
1223           fprintf (stderr, "%#x: unknown record", pos);
1224           hex_dump (stderr, pos, 64);
1225           exit(1);
1226         }
1227     }
1228   else if (match_byte(0xa)) 
1229     {
1230       if (!match_byte(7))
1231         match_byte_assert(0);
1232       if (match_u16(0x0e74))
1233         match_byte_assert(0);
1234       else
1235         {
1236           match_zeros_assert(4);
1237           if (pos == n)
1238             exit (0);
1239           match_zeros_assert (2);
1240         }
1241     }
1242 #if 0
1243   else if (match_byte(1))
1244     {
1245       match_byte_assert(0);
1246       get_string1();
1247       if (!match_byte(2))
1248         match_byte_assert(0);
1249       if (match_byte(0))
1250         {
1251           match_zeros_assert(2);
1252           get_string1();
1253           if (match_byte(0x08))
1254             {
1255               match_byte_assert(0);
1256               match_u16_assert(0x0e74);
1257               match_byte_assert(0);
1258             }
1259           else if (match_byte(3))
1260             {
1261               match_byte_assert(0);
1262               if (match_u16(0x0e74))
1263                 match_byte_assert(0);
1264               else
1265                 {
1266                   match_zeros_assert(6);
1267                   if (!match_byte(0xe) && !match_byte(0x11))
1268                     match_byte_assert(0);
1269                   match_byte_assert(0);
1270                   if (!match_u16(0x0e74))
1271                     match_u16_assert(0);
1272                   match_byte_assert(0);
1273                 }
1274             }
1275           else
1276             {
1277               match_byte_assert(0);
1278               match_byte_assert(1);
1279               match_zeros_assert(3);
1280               match_byte_assert(1);
1281               match_byte_assert(0);
1282             }
1283         }
1284     }
1285 #endif
1286   else if (match_u16(1))
1287     {
1288       int start_pos = pos;
1289       char *title = get_string1();
1290       printf("%#x: title(\"%s\", ", start_pos, title);
1291       if (!match_u32(2))
1292         match_u32_assert(0);
1293       char *id = get_string1();
1294       printf("\"%s\")\n", id);
1295       match_byte_assert(0);
1296       if (!match_u32(2))
1297         match_u32_assert(3);
1298       match_u16_assert(1);
1299     }
1300   else //if (match_u16(2) || match_u16(3) || match_u16(4) || match_u16(5) || match_u16(6) || match_u16(7) || match_u16(8) || match_u16(9))
1301     skip_item("unknown");
1302 #if 0
1303   else if (match_byte(7) || match_byte(4) || match_byte(5) || match_byte(6) || match_byte(8) || match_byte(9) || match_byte(0xb) || match_byte(0xc) || match_byte(0x15) || match_byte(0x16) || match_byte(0x17) || match_byte(0x18) || match_byte(0x1e)  || match_byte(0x1a))
1304     {
1305       if (!match_byte(7))
1306         match_byte_assert(0);
1307       if (!match_u16(0x0e74))
1308         match_byte_assert(0);
1309       match_byte_assert(0);
1310     }
1311   else if (match_byte(2) || match_byte(3))
1312     {
1313       match_byte_assert(0);
1314       if (!match_u16(0x0e74))
1315         {
1316           match_zeros_assert(2);
1317           if (match_byte(0))
1318             {
1319               match_zeros_assert(3);
1320               if (match_byte(0))
1321                 match_zeros_assert(4);
1322               else
1323                 {
1324                   pos++;
1325                   match_byte(0);
1326                   match_u16_assert(0x0e74);
1327                 }
1328             }
1329         }
1330       //match_byte_assert(0);
1331     }
1332   else if (match_byte(0xd) || match_byte(0xe) || match_byte(0xf)
1333            || match_byte(0x11) || match_byte(0x12) || match_byte(0x13)
1334            || match_byte(0x14) || match_byte(0x1b))
1335     {
1336       if (!match_byte(0x07))
1337         match_byte_assert(0);
1338       if (!match_u16(0x0e74))
1339         match_zeros_assert(11);
1340       else
1341         match_byte_assert(0);
1342     }
1343   else if (match_byte(0xe3) || match_byte(0xdb) || match_byte(0xd8) || match_byte(0xe9) || match_byte(0xf3))
1344     {
1345       match_byte_assert(0x0e);
1346       match_byte_assert(0x74);
1347       match_byte_assert(0x0e);
1348       match_byte_assert(0);
1349     }
1350   else if (match_byte(0x9d) || match_byte(0x9e) || match_byte(0x9c))
1351     match_u32_assert(0x000e741a);
1352   else if (match_byte(0x10))
1353     {
1354       match_byte_assert(0);
1355       if (match_byte(0))
1356         match_zeros_assert(10);
1357       else
1358         {
1359           match_u16_assert(0x0e74);
1360           match_byte_assert(0);
1361         }
1362     }
1363   else if (match_byte(0x39) || match_byte(0x3a) || match_byte(0x3b))
1364     match_u32_assert(0x000e7409);
1365   else
1366     {
1367       //fprintf (stderr, "bad record start at offset %x: ", pos);
1368       hex_dump (stderr, pos, 64);
1369       assert(0);
1370     }
1371 #endif
1372 }
1373
1374
1375
1376 int
1377 main(int argc, char *argv[])
1378 {
1379   bool print_offsets = false;
1380   for (;;)
1381     {
1382       int c = getopt (argc, argv, "o");
1383       if (c == -1)
1384         break;
1385
1386       switch (c)
1387         {
1388         case 'o':
1389           print_offsets = true;
1390           break;
1391
1392         case '?':
1393           exit (-1);
1394         }
1395     }
1396   if (argc - optind != 1)
1397     {
1398       fprintf (stderr, "usage: %s FILE.bin", argv[0]);
1399       exit (1);
1400     }
1401
1402   const char *filename = argv[optind];
1403   int fd = open(filename, O_RDONLY);
1404   if (fd < 0)
1405     {
1406       fprintf (stderr, "%s: open failed (%s)", filename, strerror (errno));
1407       exit (1);
1408     }
1409
1410   struct stat s;
1411   if (fstat(fd, &s))
1412     {
1413       perror("fstat");
1414       exit(1);
1415     }
1416   n = s.st_size;
1417   data = malloc(n + 256);
1418   if (!data)
1419     {
1420       perror("malloc");
1421       exit(1);
1422     }
1423   if (read(fd, data, n) != n)
1424     {
1425       perror("read");
1426       exit(1);
1427     }
1428   for (int i = 0; i < 256; i++)
1429     data[n + i] = i % 2 ? 0xaa : 0x55;
1430   close(fd);
1431
1432   setvbuf (stdout, NULL, _IONBF, 0);
1433
1434   match_byte_assert(4);
1435   match_u32_assert(0);
1436   match_string1_assert("SPSS Output Document");
1437   match_u32_assert(1);
1438   match_byte_assert(0x63);
1439
1440   for (;;)
1441     {
1442       if (data[pos] == 0)
1443         {
1444           //printf("zero\n");
1445           pos++;
1446         }
1447       else
1448         parse_flexible();
1449     }
1450   exit(0);
1451
1452   parse_heading("NavRoot");
1453   match_byte_assert(2);
1454   match_zeros_assert(32);
1455
1456   parse_heading("DspSimpleText");
1457   match_zeros_assert(10);
1458
1459   parse_heading("DspString");
1460   parse_DspString();
1461
1462   parse_heading("NavTreeViewItem");
1463   match_byte_assert(0);
1464   if (!match_u32(1))
1465     match_u32_assert(0);
1466   match_byte_assert(2);
1467   match_byte_assert(0);
1468   match_byte_assert(1);
1469   match_zeros_assert(9);
1470   match_u32_assert(1);
1471
1472   match_u32_assert(0);
1473   match_u32_assert(0x18);
1474   if (!match_u32(0))
1475     match_u32_assert(0xffffffd8);
1476   match_u32_assert(0xffffffde);
1477   match_u32_assert(0x18);
1478   if (!match_u32(0))
1479     match_u32_assert(0xffffffd8);
1480   match_u32_assert(0x28);
1481   match_u32_assert(0x28);
1482   pos += 8;
1483   if (data[pos] == 0)
1484     {
1485       match_zeros_assert(5);
1486
1487       if (match_u32(8500))
1488         match_u32_assert(11000);
1489       else
1490         {
1491           match_u32_assert(11000);
1492           match_u32_assert(8500);
1493         }
1494       pos += 32;
1495       get_string1();
1496       if (!match_byte(0))
1497         match_byte_assert(1);
1498       pos++;
1499       pos++;
1500       pos++;
1501       pos++;
1502       get_string4();                /* page title */
1503       match_byte_assert(1);
1504       match_byte_assert(1);
1505       match_zeros_assert(3);
1506       get_string4();                /* page number */
1507       match_byte_assert(0);
1508       pos += 2;
1509       match_u16_assert(2);
1510     }
1511
1512   if (data[pos + 9] != 'L')
1513     exit(0);
1514   parse_heading("NavLog");
1515   parse_NavLog();
1516   for (;;)
1517     {
1518       if (data[pos] == 0)
1519         {
1520           //printf("zero\n");
1521           pos++;
1522         }
1523       else
1524         parse_flexible();
1525     }
1526   exit(0);
1527   puts(get_padded_string(32));
1528   if (!match_u32(80))
1529     match_u32_assert(132);
1530   match_zeros_assert(8);
1531   match_u32_assert(1);
1532   printf ("0x%x\n", pos);
1533   get_string4();
1534   match_byte_assert(0);
1535
1536   parse_heading("NavHead");
1537   parse_NavHead();
1538   match_NavTreeViewItem();
1539   match_zeros_assert(3);
1540
1541   parse_heading("NavTitle");
1542   pos += 33;
1543   match_DspSimpleText();
1544   match_DspString();
1545   match_NavTreeViewItem();
1546
1547   match_byte_assert(1);
1548   match_byte_assert(1);
1549   match_u32_assert(-19);
1550   match_zeros_assert(12);
1551   match_byte_assert(0xbc);
1552   match_byte_assert(2);
1553   match_zeros_assert(9);
1554   match_byte_assert(0x22);
1555   puts(get_padded_string(32));
1556   match_u32_assert(80);
1557   match_zeros_assert(8);
1558   match_u32_assert(1);
1559   get_string4();
1560   match_byte_assert(0);
1561
1562   parse_heading("NavNote");
1563   match_byte_assert(2);
1564   match_zeros_assert(8);
1565   match_u32_assert(24);
1566   if (!match_u32(0))
1567     match_u32_assert(-40);
1568   pos += 8;
1569   match_u32_assert(2);
1570   match_u32_assert(1);
1571   match_DspSimpleText();
1572   match_DspString();
1573   match_NavTreeViewItem();
1574   match_byte_assert(1);
1575
1576   parse_heading("PTPivotController");
1577   match_byte_assert(2);
1578   pos += 8;
1579   match_u32_assert(100);
1580   match_u32_assert(100);
1581   match_u32_assert(100);
1582   match_u32_assert(100);
1583
1584   parse_heading("PVPivotView");
1585   match_u32_assert(5);
1586   match_byte_assert(0);
1587
1588   parse_heading("PMPivotModel");
1589   match_byte_assert(3);
1590
1591   parse_heading("NDimensional__DspCell");
1592   match_byte_assert(0);
1593   match_u32_assert(1);
1594
1595   parse_heading("IndexedCollection");
1596   match_byte_assert(0);
1597   pos++;
1598   match_zeros_assert(3);
1599   match_byte_assert(1);
1600   match_byte_assert(0);
1601   match_zeros_assert(7);
1602
1603   while (data[pos] != 1)
1604     {
1605       if (data[pos] == 0)
1606         {
1607           printf("zero\n");
1608           pos++;
1609         }
1610       else
1611         parse_flexible();
1612     }
1613
1614   match_byte_assert(1);
1615   match_byte_assert(0);
1616   puts(get_string1());
1617   if (!match_u32(0))
1618     match_u32_assert(2);
1619   puts(get_string1());
1620
1621   match_byte_assert(0);
1622   match_byte_assert(1);
1623   match_byte_assert(0);
1624   match_byte_assert(0);
1625   match_byte_assert(0);
1626   match_byte_assert(1);
1627   match_byte_assert(0);
1628
1629   exit (0);
1630
1631   parse_heading("PMPivotItemTree");
1632   match_byte_assert(0);
1633
1634   parse_heading("AbstractTreeBranch");
1635   match_byte_assert(0);
1636
1637   parse_heading("PMModelItemInfo");
1638   parse_PMModelItemInfo();
1639   match_DspSimpleText();
1640   match_DspString();
1641
1642   match_u32_assert(7);
1643   match_PMPivotItemTree();
1644
1645   match_u32_assert(0);
1646   match_PMPivotItemTree();
1647
1648   match_u32_assert(0);
1649   match_PMPivotItemTree();
1650
1651   match_u32_assert(6);
1652   match_PMPivotItemTree();
1653
1654   match_u32_assert(0);
1655   match_PMPivotItemTree();
1656
1657   match_u32_assert(0);
1658   match_PMPivotItemTree();
1659
1660   match_u32_assert(0);
1661   match_PMPivotItemTree();
1662
1663   match_u32_assert(0);
1664   match_PMPivotItemTree();
1665
1666   match_u32_assert(0);
1667   match_PMPivotItemTree();
1668
1669   match_u32_assert(0);
1670   match_PMPivotItemTree();
1671
1672   match_u32_assert(2);
1673   match_PMPivotItemTree();
1674
1675   match_u32_assert(0);
1676   match_PMPivotItemTree();
1677
1678   match_u32_assert(0);
1679   match_PMPivotItemTree();
1680
1681   match_u32_assert(0);
1682   match_PMPivotItemTree();
1683
1684   match_u32_assert(0);
1685   match_PMPivotItemTree();
1686
1687   match_u32_assert(2);
1688   match_PMPivotItemTree();
1689
1690   match_u32_assert(0);
1691   match_PMPivotItemTree();
1692
1693   match_u32_assert(0);
1694
1695   /* ...unknown... */
1696
1697   while (data[pos] != 0xff || data[pos + 1] != 0xff)
1698     pos++;
1699   parse_heading("PVViewDimension");
1700
1701   int i;
1702   for (i = 0; data[pos + i] != 0xff || data[pos + i + 1] != 0xff; i++)
1703     ;
1704   hex_dump(stdout, pos, i);
1705
1706   printf ("%#x: end of successful parse\n", pos);
1707
1708   return 0;
1709 }