start moving beyond PMModelItemInfo
[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 if (!match_u16(0))
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   if (match_u16(0x8011))
728     {
729       printf("11 80(");
730       pos += 2;
731       for (size_t i = 0; i < 16; i++)
732         printf("%s%02x", i > 0 ? " " : "", data[pos++]);
733       printf(") ");
734     }
735   match_DspString();
736
737   int n_subcategories = get_u32();
738   if (n_subcategories)
739     assert (leaf_idx == 0);
740   else
741     {
742       assert (leaf_idx == *n_leaves);
743       ++*n_leaves;
744     }
745   for (int k = 0; k < n_subcategories; k++)
746     parse_category(level + 1, k, n_leaves);
747 }
748
749 static void
750 parse_dimension(int i)
751 {
752   printf ("%#x: dimension %d\n", pos, i);
753   if (i == 0)
754     {
755       match_zeros_assert(5);
756     }
757   else
758     {
759       match_zeros_assert(6);
760
761       int n_units16 = get_u32();
762       match_u16_assert(1);
763       for (int j = 0; j < n_units16; j++)
764         get_u16();
765
766       match_byte_assert(0);
767
768       int n_units32 = get_u32();
769       match_u16_assert(0);
770       for (int j = 0; j < n_units32; j++)
771         get_u32();
772
773       get_u16(); match_byte_assert(0);
774
775       get_u16(); match_byte_assert(0);
776       get_u16(); match_byte_assert(0);
777       match_u32_assert(0);
778     }
779
780   if (!match_u32(0))
781     match_u32_assert(1);
782
783   get_u16();
784   if (!match_u16(0xffff))
785     match_u16_assert(0x0e74);
786   match_byte_assert(0);
787   match_DspSimpleText();
788   match_DspString();
789
790   int n_leaves = 0;
791   int n_categories = get_u32();
792   for (int j = 0; j < n_categories; j++)
793     parse_category(1, j, &n_leaves);
794 }
795
796 static void skip_item(const char *name);
797
798 static void
799 parse_PMModelItemInfo(void)
800 {
801   for (int i = 0; i < n_dims; i++)
802     parse_dimension(i);
803   printf("%#x: end of model\n", pos);
804
805   int n_units16 = get_u32();
806   match_u16_assert(0);
807   for (int j = 0; j < n_units16; j++)
808     get_u16();
809
810   n_units16 = get_u32();
811   match_u16_assert(1);
812   for (int j = 0; j < n_units16; j++)
813     get_u16();
814
815   match_byte_assert(0);
816   int n_units32 = get_u32();
817   match_u16_assert(0);
818   for (int j = 0; j < n_units32; j++)
819     match_u32_assert(j);
820
821   skip_item("end of PMModelItemInfo");
822 }
823
824 static void
825 match_PMModelItemInfo(void)
826 {                               /* 54 80 */
827   match_byte_assert(0x54);
828   match_byte_assert(0x80);
829   parse_PMModelItemInfo();
830   /* DspSimpleText */
831   /* DspString */
832 }
833
834 static void
835 match_PMPivotItemTree(void)
836 {                               /* 52 80 */
837   match_byte_assert(0x52);
838   match_byte_assert(0x80);
839   match_byte_assert(0);
840   match_PMModelItemInfo();
841 }
842
843 static void
844 parse_NavHead(void)
845 {
846   match_byte_assert(2);
847   match_zeros_assert(24);
848   match_byte_assert(1);
849   match_zeros_assert(3);
850   if (!match_byte(1))
851     match_byte_assert(0);
852   match_zeros_assert(3);
853   /* DspSimpleText */
854 }
855
856 static void
857 parse_NavOleItem(void)
858 {
859   match_byte_assert(0);
860   match_byte_assert(1);
861   match_zeros_assert(2);
862   pos++;
863   match_zeros_assert(9);
864   match_byte_assert(1);
865   match_zeros_assert(10);
866   match_byte_assert(1);
867   match_zeros_assert(5);
868   get_string1();
869   match_byte_assert(1);
870   parse_weirdness();
871   match_byte_assert(0);
872   pos++;
873   match_zeros_assert(11);
874   match_byte_assert(1);
875   match_zeros_assert(3);
876   get_string4();
877   match_byte_assert(0);
878 }
879
880 static void
881 match_NavOleItem(void)
882 {                               /* 0e 80 or 12 80*/
883   if (!match_byte(0x12))
884     match_byte_assert(0x0e);
885   match_byte_assert(0x80);
886   parse_NavOleItem();
887 }
888
889 static void
890 parse_NavTitle(void)
891 {
892   match_byte_assert(2);
893   match_zeros_assert(8);
894   match_u32_assert(24);
895   get_u32();
896   pos++;
897   if (!match_byte(3))
898     match_byte_assert(4);
899   match_zeros_assert(2);
900   get_u32();
901   match_u32_assert(2);
902   if (!match_u32(2))
903     match_u32_assert(1);
904 }
905
906 static void
907 parse_NavNote(void)
908 {
909   match_byte_assert(2);
910   match_zeros_assert(8);
911   match_u32_assert(24);
912   if (!match_u32(0) && !match_u32(0xffffff4b))
913     match_u32_assert(-40);
914   pos += 8;
915   if (!match_u32(1))
916     match_u32_assert(2);
917   if (!match_u32(2))
918     match_u32_assert(1);
919 }
920
921 static void
922 parse_PTPivotController(void)
923 {
924   match_byte_assert(2);
925   pos += 8;
926   match_u32_assert(100);
927   match_u32_assert(100);
928   match_u32_assert(100);
929   match_u32_assert(100);
930 }
931
932 static void
933 parse_PVPivotView(void)
934 {
935   match_byte_assert(5);
936   printf ("PVPivotView(%d)\n", get_u32());
937 }
938
939 static void
940 parse_NDimensional__DspCell(void)
941 {
942   match_byte_assert(0);
943   n_dims = get_u32();
944   printf ("NDimensional__DspCell(n_dims=%d)\n", n_dims);
945 }
946
947 static void
948 parse_IndexedCollection(void)
949 {
950   printf("IndexedCollection");
951   for (size_t i = 0; ; i++)
952     {
953       match_byte_assert(0);
954       printf("%c%d", i ? 'x' : '(', get_u32());
955       match_u16_assert(1);
956       if (!match_u16(0x8011))
957         break;
958     }
959   printf(")\n");
960 }
961
962 static void
963 parse_PTTableLook(void)
964 {
965   match_byte_assert(2);
966   match_byte_assert(2);
967   match_zeros_assert(7);
968   match_u32_assert(0x36);
969   match_u32_assert(0x12);
970 }
971
972 static void
973 parse_PVViewDimension(void)
974 {
975   while (data[pos + 1] != 0x80
976          && (data[pos] != 0xff || data[pos + 1] != 0xff))
977     {
978       assert(pos < n);
979       pos++;
980     }
981 }
982
983 static void
984 parse_PVSeparatorStyle(void)
985 {
986   match_byte_assert(0);
987   match_byte_assert(1);
988   match_zeros_assert(15);
989   pos++;
990   match_byte_assert(0x80);
991   match_byte_assert(0);
992
993   match_byte_assert(1);
994   match_zeros_assert(9);
995   while (data[pos + 1] != 0x80
996          && (data[pos] != 0xff || data[pos + 1] != 0xff))
997     {
998       assert(pos < n);
999       pos++;
1000     }
1001 }
1002
1003 static void
1004 parse_PVCellStyle(void)
1005 {
1006   match_byte_assert(0);
1007   match_byte_assert(1);
1008   match_zeros_assert(5);
1009   match_u32_assert(0xffffff);
1010   match_zeros_assert(2);
1011 }
1012
1013 static void
1014 skip_item(const char *name)
1015 {
1016   int start_pos = pos;
1017   printf("%#x: skipping %s bytes...", pos, name);
1018   while (pos < n
1019          && data[pos + 1] != 0x80
1020          && !(data[pos] == 0xff && data[pos + 1] == 0xff
1021               && data[pos + 2] == 0 && data[pos + 3] == 0))
1022     {
1023       pos++;
1024     }
1025   printf("until %#x:", pos);
1026   hex_dump(stdout, start_pos, pos - start_pos);
1027 }
1028
1029 static void
1030 parse_DspAnnotation(void)
1031 {
1032   match_zeros_assert(10);
1033   match_u32_assert(1);
1034 }
1035
1036 static void
1037 parse_DspTextComponentHandle(void)
1038 {
1039   match_byte_assert(0);
1040   match_DspString();
1041   printf("after DspTextComponentHandle: ");
1042   for (int i = 0; ; i++)
1043     if (!memcmp(&data[pos + 6 + i], "PVView", 6))
1044       {
1045         hex_dump(stdout, pos, i);
1046         exit(0);
1047       }
1048 }
1049
1050 static void
1051 parse_flexible(void)
1052 {
1053   int start = pos;
1054   if (match_u16(0xffff))
1055     {
1056       match_u16_assert(0);
1057       char *heading = get_string2();
1058       printf("%#x: %s\n", pos, heading);
1059       if (!strcmp(heading, "NavRoot"))
1060         {
1061           match_byte_assert(2);
1062           match_zeros_assert(32);
1063         }
1064       else if (!strcmp(heading, "NavPivot"))
1065         {
1066           hex_dump(stdout, pos, 021);
1067           pos += 0x21;
1068         }
1069       else if (!strcmp(heading, "DspCell"))
1070         parse_DspCell();
1071       else if (!strcmp(heading, "DspSimpleText"))
1072         parse_DspSimpleText();
1073       else if (!strcmp(heading, "DspNumber"))
1074         parse_DspNumber();
1075       else if (!strcmp(heading, "DspString"))
1076         parse_DspString();
1077       else if (!strcmp(heading, "NavHead"))
1078         parse_NavHead();
1079       else if (!strcmp(heading, "NavTreeViewItem"))
1080         {
1081           if (0)
1082             parse_NavTreeViewItem();
1083           else
1084             skip_item(heading);
1085         }
1086       else if (!strcmp(heading, "IndexedCollection"))
1087         parse_IndexedCollection();
1088       else if (!strcmp(heading, "NavOleItem"))
1089         parse_NavOleItem();
1090       else if (!strcmp(heading, "NavTitle"))
1091         parse_NavTitle();
1092       else if (!strcmp(heading, "NavNote"))
1093         parse_NavNote();
1094       else if (!strcmp(heading, "PTPivotController"))
1095         parse_PTPivotController();
1096       else if (!strcmp(heading, "PVPivotView"))
1097         parse_PVPivotView();
1098       else if (!strcmp(heading, "PMPivotModel"))
1099         match_byte_assert(3);
1100       else if (!strcmp(heading, "NDimensional__DspCell"))
1101         parse_NDimensional__DspCell();
1102       else if (!strcmp(heading, "PMPivotItemTree"))
1103         match_byte_assert(0);
1104       else if (!strcmp(heading, "PMModelItemInfo"))
1105         parse_PMModelItemInfo();
1106       else if (!strcmp(heading, "AbstractTreeBranch"))
1107         match_byte_assert(0);
1108       else if (!strcmp(heading, "PTTableLook"))
1109         parse_PTTableLook();
1110       else if (!strcmp(heading, "PVViewDimension"))
1111         parse_PVViewDimension();
1112       else if (!strcmp(heading, "PVSeparatorStyle"))
1113         parse_PVSeparatorStyle();
1114       else if (!strcmp(heading, "PVCellStyle"))
1115         parse_PVCellStyle();
1116       else if (!strcmp(heading, "PVTextStyle"))
1117         exit(0);
1118       else if (!strcmp(heading, "DspAnnotation"))
1119         parse_DspAnnotation();
1120       else if (!strcmp(heading, "DspTextComponentHandle"))
1121         parse_DspTextComponentHandle();
1122       else
1123         {
1124           fprintf(stderr, "don't know %s at offset 0x%x: ", heading, start);
1125           hex_dump(stderr, pos, 128);
1126           assert(0);
1127         }
1128     }
1129   else if (data[pos + 1] == 0x80)
1130     {
1131       if ((data[pos] == 0x2a || data[pos] == 0x18 || data[pos] == 0x19) && data[pos + 1] == 0x80)
1132         match_DspNumber();
1133       else if (data[pos] == 0x27 && data[pos + 1] == 0x80)
1134         match_DspCell();
1135       else if (data[pos] == 0x5 && data[pos + 1] == 0x80)
1136         match_DspString();
1137       else if (data[pos] == 0x7 && data[pos + 1] == 0x80)
1138         {
1139           if (0)
1140             match_NavTreeViewItem();
1141           else
1142             {
1143               pos += 2;
1144               skip_item("NavTreeViewItem");
1145             }
1146         }
1147       else if (data[pos] == 0x3 && data[pos + 1] == 0x80)
1148         match_DspSimpleText();
1149       else if ((data[pos] == 0x3c || data[pos] == 0x39)
1150                && data[pos + 1] == 0x80)
1151         {
1152           /* 3c 80 */
1153           /* 39 80 */
1154           printf("%#x: %02x %02x ", pos, data[pos], data[pos + 1]);
1155           pos += 2;
1156           parse_format();
1157           printf ("\n");
1158 /*      match_byte_assert(0x01);
1159         match_byte_assert(0x02);
1160         match_byte_assert(0x0d); */
1161         }
1162       else if ((data[pos] == 0x15 || data[pos] == 0x14)
1163                && data[pos + 1] == 0x80)
1164         {
1165           /* 14 80 */
1166           /* 15 80 */
1167           pos += 2;
1168           if (match_byte(2))
1169             {
1170               printf ("%02x 80(%f", data[pos - 2], get_double());
1171               printf (" \"%s\")\n", get_string1());
1172               if (match_byte(1))
1173                 {
1174                   match_byte_assert(0);
1175                   get_string1();
1176                   if (!match_byte(2) && !match_byte(3))
1177                     match_byte_assert(0);
1178                   match_zeros_assert(3);
1179                   get_string1();
1180                   match_byte_assert(0);
1181                   match_byte_assert(1);
1182                   match_zeros_assert(3);
1183                   match_byte_assert(1);
1184                   match_byte_assert(0);
1185                 }
1186             }
1187           else
1188             {
1189               match_byte_assert(0);
1190             }
1191         }
1192       else if (data[pos] == 0x17 || data[pos] == 0x25)
1193         {
1194           printf("%02x %02x(%02x %02x %02x)\n",
1195                  data[pos], data[pos + 1],
1196                  data[pos + 2], data[pos + 3], data[pos + 4]);
1197           pos += 5;
1198         }
1199       else if (data[pos] == 0x9 && data[pos + 1] == 0x80)
1200         {
1201           match_NavLog();
1202         }
1203       else if (data[pos] == 0xe || data[pos] == 0x12)
1204         match_NavOleItem();
1205       else if (data[pos] == 0x11 || data[pos] == 0x13)
1206         {
1207           int type = data[pos];
1208           pos += 2;
1209           match_byte_assert(0);
1210           if (data[pos] != 0)
1211             {
1212               int x = get_u32();
1213               int y = get_u16();
1214               if (y == 0)
1215                 {
1216                   int index = get_u32();
1217                   printf("%02x 80(footnote %d)\n", type, index);
1218                 }
1219               else
1220                 printf("%02x 80(%d %d)\n", type, x, y);
1221             }
1222           else
1223             match_zeros_assert(13);
1224         }
1225       else if (data[pos] == 0x29 ||
1226                data[pos] == 0x2b ||
1227                data[pos] == 0x2d ||
1228                data[pos] == 0x31 ||
1229                data[pos] == 0x32 ||
1230                data[pos] == 0x4a ||
1231                data[pos] == 0x4c ||
1232                data[pos] == 0x4f ||
1233                data[pos] == 0x4d ||
1234                data[pos] == 0x50 ||
1235                data[pos] == 0x36 ||
1236                data[pos] == 0x52 ||
1237                data[pos] == 0x53 ||
1238                data[pos] == 0x54 ||
1239                data[pos] == 0x55 ||
1240                data[pos] == 0x57 ||
1241                data[pos] == 0x56 ||
1242                data[pos] == 0x58 ||
1243                data[pos] == 0x5c ||
1244                data[pos] == 0x5b ||
1245                data[pos] == 0x5e ||
1246                data[pos] == 0x62 ||
1247                data[pos] == 0x64 ||
1248                data[pos] == 0x4e ||
1249                data[pos] == 0x51 ||
1250                data[pos] == 0x59 ||
1251                data[pos] == 0x5a ||
1252                data[pos] == 0x5d ||
1253                data[pos] == 0x66 ||
1254                data[pos] == 0x60 ||
1255                data[pos] == 0x68 ||
1256                data[pos] == 0x48 ||
1257                data[pos] == 0x6a ||
1258                data[pos] == 0x37)
1259         {
1260           pos += 2;
1261           match_byte_assert(0);
1262         }
1263       else if (data[pos] == 0x2c ||
1264                data[pos] == 0x2e ||
1265                data[pos] == 0x30 ||
1266                data[pos] == 0x34 ||
1267                data[pos] == 0x3d ||
1268                data[pos] == 0x40 ||
1269                data[pos] == 0x3f ||
1270                data[pos] == 0x42 ||
1271                data[pos] == 0x43 ||
1272                data[pos] == 0x44 ||
1273                data[pos] == 0x49 ||
1274                data[pos] == 0x3e ||
1275                data[pos] == 0x46)
1276         {
1277           printf ("%#x: %02x %02x(%02x %02x %02x)\n",
1278                   pos, data[pos], data[pos + 1],
1279                   data[pos + 2], data[pos + 3], data[pos + 4]);
1280           pos += 2;
1281           pos += 3;
1282         }
1283       else
1284         {
1285           fprintf (stderr, "%#x: unknown record", pos);
1286           hex_dump (stderr, pos, 64);
1287           exit(1);
1288         }
1289     }
1290   else if (match_byte(0xa)) 
1291     {
1292       if (!match_byte(7))
1293         match_byte_assert(0);
1294       if (match_u16(0x0e74))
1295         match_byte_assert(0);
1296       else
1297         {
1298           match_zeros_assert(4);
1299           if (pos == n)
1300             exit (0);
1301           match_zeros_assert (2);
1302         }
1303     }
1304 #if 0
1305   else if (match_byte(1))
1306     {
1307       match_byte_assert(0);
1308       get_string1();
1309       if (!match_byte(2))
1310         match_byte_assert(0);
1311       if (match_byte(0))
1312         {
1313           match_zeros_assert(2);
1314           get_string1();
1315           if (match_byte(0x08))
1316             {
1317               match_byte_assert(0);
1318               match_u16_assert(0x0e74);
1319               match_byte_assert(0);
1320             }
1321           else if (match_byte(3))
1322             {
1323               match_byte_assert(0);
1324               if (match_u16(0x0e74))
1325                 match_byte_assert(0);
1326               else
1327                 {
1328                   match_zeros_assert(6);
1329                   if (!match_byte(0xe) && !match_byte(0x11))
1330                     match_byte_assert(0);
1331                   match_byte_assert(0);
1332                   if (!match_u16(0x0e74))
1333                     match_u16_assert(0);
1334                   match_byte_assert(0);
1335                 }
1336             }
1337           else
1338             {
1339               match_byte_assert(0);
1340               match_byte_assert(1);
1341               match_zeros_assert(3);
1342               match_byte_assert(1);
1343               match_byte_assert(0);
1344             }
1345         }
1346     }
1347 #endif
1348   else if (match_u16(1))
1349     {
1350       int start_pos = pos;
1351       char *title = get_string1();
1352       printf("%#x: title(\"%s\", ", start_pos, title);
1353       if (!match_u32(2))
1354         match_u32_assert(0);
1355       char *id = get_string1();
1356       printf("\"%s\")\n", id);
1357       match_byte_assert(0);
1358       if (!match_u32(2))
1359         match_u32_assert(3);
1360       match_u16_assert(1);
1361     }
1362   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))
1363     skip_item("unknown");
1364 #if 0
1365   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))
1366     {
1367       if (!match_byte(7))
1368         match_byte_assert(0);
1369       if (!match_u16(0x0e74))
1370         match_byte_assert(0);
1371       match_byte_assert(0);
1372     }
1373   else if (match_byte(2) || match_byte(3))
1374     {
1375       match_byte_assert(0);
1376       if (!match_u16(0x0e74))
1377         {
1378           match_zeros_assert(2);
1379           if (match_byte(0))
1380             {
1381               match_zeros_assert(3);
1382               if (match_byte(0))
1383                 match_zeros_assert(4);
1384               else
1385                 {
1386                   pos++;
1387                   match_byte(0);
1388                   match_u16_assert(0x0e74);
1389                 }
1390             }
1391         }
1392       //match_byte_assert(0);
1393     }
1394   else if (match_byte(0xd) || match_byte(0xe) || match_byte(0xf)
1395            || match_byte(0x11) || match_byte(0x12) || match_byte(0x13)
1396            || match_byte(0x14) || match_byte(0x1b))
1397     {
1398       if (!match_byte(0x07))
1399         match_byte_assert(0);
1400       if (!match_u16(0x0e74))
1401         match_zeros_assert(11);
1402       else
1403         match_byte_assert(0);
1404     }
1405   else if (match_byte(0xe3) || match_byte(0xdb) || match_byte(0xd8) || match_byte(0xe9) || match_byte(0xf3))
1406     {
1407       match_byte_assert(0x0e);
1408       match_byte_assert(0x74);
1409       match_byte_assert(0x0e);
1410       match_byte_assert(0);
1411     }
1412   else if (match_byte(0x9d) || match_byte(0x9e) || match_byte(0x9c))
1413     match_u32_assert(0x000e741a);
1414   else if (match_byte(0x10))
1415     {
1416       match_byte_assert(0);
1417       if (match_byte(0))
1418         match_zeros_assert(10);
1419       else
1420         {
1421           match_u16_assert(0x0e74);
1422           match_byte_assert(0);
1423         }
1424     }
1425   else if (match_byte(0x39) || match_byte(0x3a) || match_byte(0x3b))
1426     match_u32_assert(0x000e7409);
1427   else
1428     {
1429       //fprintf (stderr, "bad record start at offset %x: ", pos);
1430       hex_dump (stderr, pos, 64);
1431       assert(0);
1432     }
1433 #endif
1434 }
1435
1436
1437
1438 int
1439 main(int argc, char *argv[])
1440 {
1441   bool print_offsets = false;
1442   for (;;)
1443     {
1444       int c = getopt (argc, argv, "o");
1445       if (c == -1)
1446         break;
1447
1448       switch (c)
1449         {
1450         case 'o':
1451           print_offsets = true;
1452           break;
1453
1454         case '?':
1455           exit (-1);
1456         }
1457     }
1458   if (argc - optind != 1)
1459     {
1460       fprintf (stderr, "usage: %s FILE.bin", argv[0]);
1461       exit (1);
1462     }
1463
1464   const char *filename = argv[optind];
1465   int fd = open(filename, O_RDONLY);
1466   if (fd < 0)
1467     {
1468       fprintf (stderr, "%s: open failed (%s)", filename, strerror (errno));
1469       exit (1);
1470     }
1471
1472   struct stat s;
1473   if (fstat(fd, &s))
1474     {
1475       perror("fstat");
1476       exit(1);
1477     }
1478   n = s.st_size;
1479   data = malloc(n + 256);
1480   if (!data)
1481     {
1482       perror("malloc");
1483       exit(1);
1484     }
1485   if (read(fd, data, n) != n)
1486     {
1487       perror("read");
1488       exit(1);
1489     }
1490   for (int i = 0; i < 256; i++)
1491     data[n + i] = i % 2 ? 0xaa : 0x55;
1492   close(fd);
1493
1494   setvbuf (stdout, NULL, _IONBF, 0);
1495
1496   match_byte_assert(4);
1497   match_u32_assert(0);
1498   match_string1_assert("SPSS Output Document");
1499   match_u32_assert(1);
1500   match_byte_assert(0x63);
1501
1502   while (pos < n)
1503     {
1504       if (data[pos] == 0)
1505         {
1506           //printf("zero\n");
1507           pos++;
1508         }
1509       else
1510         parse_flexible();
1511     }
1512   exit(0);
1513
1514   parse_heading("NavRoot");
1515   match_byte_assert(2);
1516   match_zeros_assert(32);
1517
1518   parse_heading("DspSimpleText");
1519   match_zeros_assert(10);
1520
1521   parse_heading("DspString");
1522   parse_DspString();
1523
1524   parse_heading("NavTreeViewItem");
1525   match_byte_assert(0);
1526   if (!match_u32(1))
1527     match_u32_assert(0);
1528   match_byte_assert(2);
1529   match_byte_assert(0);
1530   match_byte_assert(1);
1531   match_zeros_assert(9);
1532   match_u32_assert(1);
1533
1534   match_u32_assert(0);
1535   match_u32_assert(0x18);
1536   if (!match_u32(0))
1537     match_u32_assert(0xffffffd8);
1538   match_u32_assert(0xffffffde);
1539   match_u32_assert(0x18);
1540   if (!match_u32(0))
1541     match_u32_assert(0xffffffd8);
1542   match_u32_assert(0x28);
1543   match_u32_assert(0x28);
1544   pos += 8;
1545   if (data[pos] == 0)
1546     {
1547       match_zeros_assert(5);
1548
1549       if (match_u32(8500))
1550         match_u32_assert(11000);
1551       else
1552         {
1553           match_u32_assert(11000);
1554           match_u32_assert(8500);
1555         }
1556       pos += 32;
1557       get_string1();
1558       if (!match_byte(0))
1559         match_byte_assert(1);
1560       pos++;
1561       pos++;
1562       pos++;
1563       pos++;
1564       get_string4();                /* page title */
1565       match_byte_assert(1);
1566       match_byte_assert(1);
1567       match_zeros_assert(3);
1568       get_string4();                /* page number */
1569       match_byte_assert(0);
1570       pos += 2;
1571       match_u16_assert(2);
1572     }
1573
1574   if (data[pos + 9] != 'L')
1575     exit(0);
1576   parse_heading("NavLog");
1577   parse_NavLog();
1578   for (;;)
1579     {
1580       if (data[pos] == 0)
1581         {
1582           //printf("zero\n");
1583           pos++;
1584         }
1585       else
1586         parse_flexible();
1587     }
1588   exit(0);
1589   puts(get_padded_string(32));
1590   if (!match_u32(80))
1591     match_u32_assert(132);
1592   match_zeros_assert(8);
1593   match_u32_assert(1);
1594   printf ("0x%x\n", pos);
1595   get_string4();
1596   match_byte_assert(0);
1597
1598   parse_heading("NavHead");
1599   parse_NavHead();
1600   match_NavTreeViewItem();
1601   match_zeros_assert(3);
1602
1603   parse_heading("NavTitle");
1604   pos += 33;
1605   match_DspSimpleText();
1606   match_DspString();
1607   match_NavTreeViewItem();
1608
1609   match_byte_assert(1);
1610   match_byte_assert(1);
1611   match_u32_assert(-19);
1612   match_zeros_assert(12);
1613   match_byte_assert(0xbc);
1614   match_byte_assert(2);
1615   match_zeros_assert(9);
1616   match_byte_assert(0x22);
1617   puts(get_padded_string(32));
1618   match_u32_assert(80);
1619   match_zeros_assert(8);
1620   match_u32_assert(1);
1621   get_string4();
1622   match_byte_assert(0);
1623
1624   parse_heading("NavNote");
1625   match_byte_assert(2);
1626   match_zeros_assert(8);
1627   match_u32_assert(24);
1628   if (!match_u32(0))
1629     match_u32_assert(-40);
1630   pos += 8;
1631   match_u32_assert(2);
1632   match_u32_assert(1);
1633   match_DspSimpleText();
1634   match_DspString();
1635   match_NavTreeViewItem();
1636   match_byte_assert(1);
1637
1638   parse_heading("PTPivotController");
1639   match_byte_assert(2);
1640   pos += 8;
1641   match_u32_assert(100);
1642   match_u32_assert(100);
1643   match_u32_assert(100);
1644   match_u32_assert(100);
1645
1646   parse_heading("PVPivotView");
1647   match_u32_assert(5);
1648   match_byte_assert(0);
1649
1650   parse_heading("PMPivotModel");
1651   match_byte_assert(3);
1652
1653   parse_heading("NDimensional__DspCell");
1654   match_byte_assert(0);
1655   match_u32_assert(1);
1656
1657   parse_heading("IndexedCollection");
1658   match_byte_assert(0);
1659   pos++;
1660   match_zeros_assert(3);
1661   match_byte_assert(1);
1662   match_byte_assert(0);
1663   match_zeros_assert(7);
1664
1665   while (pos < n)
1666     {
1667       if (data[pos] == 0)
1668         {
1669           printf("zero\n");
1670           pos++;
1671         }
1672       else
1673         parse_flexible();
1674     }
1675
1676   match_byte_assert(1);
1677   match_byte_assert(0);
1678   puts(get_string1());
1679   if (!match_u32(0))
1680     match_u32_assert(2);
1681   puts(get_string1());
1682
1683   match_byte_assert(0);
1684   match_byte_assert(1);
1685   match_byte_assert(0);
1686   match_byte_assert(0);
1687   match_byte_assert(0);
1688   match_byte_assert(1);
1689   match_byte_assert(0);
1690
1691   exit (0);
1692
1693   parse_heading("PMPivotItemTree");
1694   match_byte_assert(0);
1695
1696   parse_heading("AbstractTreeBranch");
1697   match_byte_assert(0);
1698
1699   parse_heading("PMModelItemInfo");
1700   parse_PMModelItemInfo();
1701   match_DspSimpleText();
1702   match_DspString();
1703
1704   match_u32_assert(7);
1705   match_PMPivotItemTree();
1706
1707   match_u32_assert(0);
1708   match_PMPivotItemTree();
1709
1710   match_u32_assert(0);
1711   match_PMPivotItemTree();
1712
1713   match_u32_assert(6);
1714   match_PMPivotItemTree();
1715
1716   match_u32_assert(0);
1717   match_PMPivotItemTree();
1718
1719   match_u32_assert(0);
1720   match_PMPivotItemTree();
1721
1722   match_u32_assert(0);
1723   match_PMPivotItemTree();
1724
1725   match_u32_assert(0);
1726   match_PMPivotItemTree();
1727
1728   match_u32_assert(0);
1729   match_PMPivotItemTree();
1730
1731   match_u32_assert(0);
1732   match_PMPivotItemTree();
1733
1734   match_u32_assert(2);
1735   match_PMPivotItemTree();
1736
1737   match_u32_assert(0);
1738   match_PMPivotItemTree();
1739
1740   match_u32_assert(0);
1741   match_PMPivotItemTree();
1742
1743   match_u32_assert(0);
1744   match_PMPivotItemTree();
1745
1746   match_u32_assert(0);
1747   match_PMPivotItemTree();
1748
1749   match_u32_assert(2);
1750   match_PMPivotItemTree();
1751
1752   match_u32_assert(0);
1753   match_PMPivotItemTree();
1754
1755   match_u32_assert(0);
1756
1757   /* ...unknown... */
1758
1759   while (data[pos] != 0xff || data[pos + 1] != 0xff)
1760     pos++;
1761   parse_heading("PVViewDimension");
1762
1763   int i;
1764   for (i = 0; data[pos + i] != 0xff || data[pos + i + 1] != 0xff; i++)
1765     ;
1766   hex_dump(stdout, pos, i);
1767
1768   printf ("%#x: end of successful parse\n", pos);
1769
1770   return 0;
1771 }