as-date and as-number pass
[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 #define XSTR(x) #x
25 #define STR(x) XSTR(x)
26 #define WHERE __FILE__":" STR(__LINE__)
27
28 static uint8_t
29 get_byte(void)
30 {
31   return data[pos++];
32 }
33
34 static unsigned int
35 get_u32(void)
36 {
37   uint32_t x;
38   memcpy(&x, &data[pos], 4);
39   pos += 4;
40   return x;
41 }
42
43 static unsigned long long int
44 get_u64(void)
45 {
46   uint64_t x;
47   memcpy(&x, &data[pos], 8);
48   pos += 8;
49   return x;
50 }
51
52 static unsigned int
53 get_be32(void)
54 {
55   uint32_t x;
56   x = (data[pos] << 24) | (data[pos + 1] << 16) | (data[pos + 2] << 8) | data[pos + 3];
57   pos += 4;
58   return x;
59 }
60
61 static unsigned int
62 get_u16(void)
63 {
64   uint16_t x;
65   memcpy(&x, &data[pos], 2);
66   pos += 2;
67   return x;
68 }
69
70 static double
71 get_double(void)
72 {
73   double x;
74   memcpy(&x, &data[pos], 8);
75   pos += 8;
76   return x;
77 }
78
79 static double __attribute__((unused))
80 get_float(void)
81 {
82   float x;
83   memcpy(&x, &data[pos], 4);
84   pos += 4;
85   return x;
86 }
87
88 static bool
89 match_u32(uint32_t x)
90 {
91   if (get_u32() == x)
92     return true;
93   pos -= 4;
94   return false;
95 }
96
97 bool
98 match_u16(uint16_t x)
99 {
100   if (get_u16() == x)
101     return true;
102   pos -= 2;
103   return false;
104 }
105
106 static void
107 match_u32_assert(uint32_t x, const char *where)
108 {
109   unsigned int y = get_u32();
110   if (x != y)
111     {
112       fprintf(stderr, "%s: 0x%x: expected i%u, got i%u\n", where, pos - 4, x, y);
113       exit(1);
114     }
115 }
116 #define match_u32_assert(x) match_u32_assert(x, WHERE)
117
118 static void
119 match_u16_assert(uint16_t x, const char *where)
120 {
121   unsigned int y = get_u16();
122   if (x != y)
123     {
124       fprintf(stderr, "%s: 0x%x: expected u16:%u, got u16:%u\n", where, pos - 2, x, y);
125       exit(1);
126     }
127 }
128 #define match_u16_assert(x) match_u16_assert(x, WHERE)
129
130 static bool __attribute__((unused))
131 match_u64(uint64_t x)
132 {
133   if (get_u64() == x)
134     return true;
135   pos -= 8;
136   return false;
137 }
138
139 static void __attribute__((unused))
140 match_u64_assert(uint64_t x, const char *where)
141 {
142   unsigned long long int y = get_u64();
143   if (x != y)
144     {
145       fprintf(stderr, "%s: 0x%x: expected u64:%lu, got u64:%llu\n", where, pos - 8, x, y);
146       exit(1);
147     }
148 }
149 #define match_u64_assert(x) match_u64_assert(x, WHERE)
150
151 static bool __attribute__((unused))
152 match_be32(uint32_t x)
153 {
154   if (get_be32() == x)
155     return true;
156   pos -= 4;
157   return false;
158 }
159
160 static void
161 match_be32_assert(uint32_t x, const char *where)
162 {
163   unsigned int y = get_be32();
164   if (x != y)
165     {
166       fprintf(stderr, "%s: 0x%x: expected be%u, got be%u\n", where, pos - 4, x, y);
167       exit(1);
168     }
169 }
170 #define match_be32_assert(x) match_be32_assert(x, WHERE)
171
172 static bool
173 match_byte(uint8_t b)
174 {
175   if (pos < n && data[pos] == b)
176     {
177       pos++;
178       return true;
179     }
180   else
181     return false;
182 }
183
184 static void
185 match_byte_assert(uint8_t b, const char *where)
186 {
187   if (!match_byte(b))
188     {
189       fprintf(stderr, "%s: 0x%x: expected %02x, got %02x\n", where, pos, b, data[pos]);
190       exit(1);
191     }
192 }
193 #define match_byte_assert(b) match_byte_assert(b, WHERE)
194
195 static bool
196 match_bytes(int start, const int *bytes, size_t n_bytes)
197 {
198   for (size_t i = 0; i < n_bytes; i++)
199     if (bytes[i] >= 0 && data[start + i] != bytes[i])
200       return false;
201   return true;
202 }
203
204 static char *
205 xmemdup0(const void *p, size_t n)
206 {
207   char *s = malloc(n + 1);
208   memcpy(s, p, n);
209   s[n] = 0;
210   return s;
211 }
212
213 static bool
214 get_bool(void)
215 {
216   if (match_byte(0))
217     return false;
218   match_byte_assert(1);
219   return true;
220 }
221
222 static bool __attribute__((unused))
223 is_ascii(uint8_t p)
224 {
225   return (p >= ' ' && p < 127) || p == '\r' || p == '\n' || p == '\t';
226 }
227
228 static int
229 count_zeros(const uint8_t *p)
230 {
231   size_t n = 0;
232   while (p[n] == 0)
233     n++;
234   return n;
235 }
236
237 static bool __attribute__((unused))
238 all_utf8(const char *p_, size_t len)
239 {
240   const uint8_t *p = (const uint8_t *) p_;
241   for (size_t ofs = 0, mblen; ofs < len; ofs += mblen)
242     {
243       ucs4_t uc;
244
245       mblen = u8_mbtouc (&uc, p + ofs, len - ofs);
246       if ((uc < 32 && uc != '\n') || uc == 127 || uc == 0xfffd)
247         return false;
248     }
249   return true;
250 }
251
252 static char *
253 get_string2(void)
254 {
255   int len = data[pos] + data[pos + 1] * 256;
256   char *s = xmemdup0(&data[pos + 2], len);
257   pos += 2 + len;
258   return s;
259 }
260
261 static char *
262 get_string1(void)
263 {
264   int len = data[pos++];
265   if (len == 0xff)
266     return get_string2();
267   else
268     {
269       char *s = xmemdup0(&data[pos], len);
270       pos += len;
271       return s;
272     }
273 }
274
275 static void
276 match_string1_assert(const char *exp, const char *where)
277 {
278   int start = pos;
279   char *act = get_string1();
280   if (strcmp(act, exp)) 
281     {
282       fprintf(stderr, "%s: 0x%x: expected \"%s\", got \"%s\"\n",
283               where, start, exp, act);
284       exit(1);
285     }
286 }
287 #define match_string1_assert(x) match_string1_assert(x, WHERE)
288
289 static void
290 match_string2_assert(const char *exp, const char *where)
291 {
292   int start = pos;
293   char *act = get_string2();
294   if (strcmp(act, exp)) 
295     {
296       fprintf(stderr, "%s: 0x%x: expected \"%s\", got \"%s\"\n",
297               where, start, exp, act);
298       exit(1);
299     }
300 }
301 #define match_string2_assert(x) match_string2_assert(x, WHERE)
302
303 static char *
304 get_string4(const char *where)
305 {
306   if (1
307       /*data[pos + 1] == 0 && data[pos + 2] == 0 && data[pos + 3] == 0*/
308       /*&& all_ascii(&data[pos + 4], data[pos])*/)
309     {
310       assert(data[pos + 3] == 0);
311       int len = data[pos] + data[pos + 1] * 256 + data[pos + 2] * 65536;
312       char *s = malloc(len + 1);
313
314       memcpy(s, &data[pos + 4], len);
315       s[len] = 0;
316       pos += 4 + len;
317       return s;
318     }
319   else
320     {
321       fprintf(stderr, "%s: 0x%x: expected string\n", where, pos);
322       exit(1);
323     }
324 }
325 #define get_string4() get_string4(WHERE)
326
327 static char *
328 get_padded_string(int len)
329 {
330   char *s = xmemdup0(&data[pos], len);
331   pos += len;
332   return s;
333 }
334
335 static char *
336 get_string_be(const char *where)
337 {
338   if (1
339       /*data[pos + 1] == 0 && data[pos + 2] == 0 && data[pos + 3] == 0*/
340       /*&& all_ascii(&data[pos + 4], data[pos])*/)
341     {
342       int len = data[pos + 2] * 256 + data[pos + 3];
343       char *s = malloc(len + 1);
344
345       memcpy(s, &data[pos + 4], len);
346       s[len] = 0;
347       pos += 4 + len;
348       return s;
349     }
350   else
351     {
352       fprintf(stderr, "%s: 0x%x: expected string\n", where, pos);
353       exit(1);
354     }
355 }
356 #define get_string_be() get_string_be(WHERE)
357
358 static int
359 get_end(void)
360 {
361   int len = get_u32();
362   return pos + len;
363 }
364
365 static void __attribute__((unused))
366 hex_dump(FILE *stream, int ofs, int n)
367 {
368   int n_ascii = 0;
369   for (int i = 0; i < n; i++)
370     {
371       int c = data[ofs + i];
372       n_ascii += is_ascii(c);
373       fprintf(stream, " %02x", c);
374     }
375   if (n_ascii >= 3)
376     {
377       putc(' ', stream);
378       for (int i = 0; i < n; i++)
379         {
380           int c = data[ofs + i];
381           putc(c >= 32 && c < 127 ? c : '.', stream);
382         }
383     }
384   putc('\n', stream);
385 }
386
387 static void __attribute__((unused))
388 char_dump(FILE *stream, int ofs, int n)
389 {
390   for (int i = 0; i < n; i++)
391     {
392       int c = data[ofs + i];
393       putc(c >= 32 && c < 127 ? c : '.', stream);
394     }
395   putc('\n', stream);
396 }
397
398
399 static int
400 compare_int(const void *a_, const void *b_)
401 {
402   const int *a = a_;
403   const int *b = b_;
404   return *a < *b ? -1 : *a > *b;
405 }
406
407
408 static const char *
409 format_name (int format, char *buf)
410 {
411   switch (format)
412     {
413     case 1: return "A";
414     case 2: return "AHEX";
415     case 3: return "COMMA";
416     case 4: return "DOLLAR";
417     case 5: case 40: return "F";
418     case 6: return "IB";
419     case 7: return "PIBHEX";
420     case 8: return "P";
421     case 9: return "PIB";
422     case 10: return "PK";
423     case 11: return "RB";
424     case 12: return "RBHEX";
425     case 15: return "Z";
426     case 16: return "N";
427     case 17: return "E";
428     case 20: return "DATE";
429     case 21: return "TIME";
430     case 22: return "DATETIME";
431     case 23: return "ADATE";
432     case 24: return "JDATE";
433     case 25: return "DTIME";
434     case 26: return "WKDAY";
435     case 27: return "MONTH";
436     case 28: return "MOYR";
437     case 29: return "QYR";
438     case 30: return "WKYR";
439     case 31: return "PCT";
440     case 32: return "DOT";
441     case 33: return "CCA";
442     case 34: return "CCB";
443     case 35: return "CCC";
444     case 36: return "CCD";
445     case 37: return "CCE";
446     case 38: return "EDATE";
447     case 39: return "SDATE";
448     default: sprintf(buf, "(%d)", format); return buf;
449     }
450 }
451
452 static void
453 parse_format(void)
454 {
455   int d = data[pos++];
456   int w = data[pos++];
457   int fmt = data[pos++];
458   char buf[32];
459   printf ("%s%d.%d", format_name(fmt, buf), w, d);
460 }
461
462 static void
463 parse_heading(const char *name)
464 {
465   match_u16_assert(0xffff);
466   match_u16_assert(0);
467   match_string2_assert(name);
468 }
469
470 static void
471 match_zeros_assert(int count, const char *where)
472 {
473   for (int i = 0; i < count; i++)
474     if (data[pos + i])
475       {
476         fprintf (stderr,
477                  "%s: %#x: expected %d zeros here but offset %d is %#"PRIx8"\n",
478                  where, pos, count, i, data[pos + i]);
479         exit (1);
480       }
481   pos += count;
482 }
483 #define match_zeros_assert(count) match_zeros_assert(count, WHERE)
484
485 static void
486 put_safe(const char *s)
487 {
488   while (*s)
489     {
490       if (*s == '\n')
491         printf ("\\n");
492       else if (*s == '\r')
493         printf ("\\r");
494       else if (*s < 0x20 || *s > 0x7e)
495         printf ("\\x%02"PRIx8, (uint8_t) *s);
496       else
497         putchar (*s);
498       s++;
499     }
500 }
501
502 static void
503 parse_DspString(void)
504 {
505   if (match_byte(2))
506     {
507       printf("DspString(%f, \"", get_double());
508       printf("%s\")\n", get_string1());
509     }
510   else
511     {
512       match_byte_assert(1);
513       printf ("DspString(");
514       parse_format();
515       printf(" \"");
516       match_byte_assert(0);
517       match_byte_assert(1);
518       put_safe(get_string1());
519       printf("\")\n");
520     }
521 }
522
523 static void
524 match_DspString(void)
525 {                               /* 05 80 */
526   match_byte_assert(5);
527   match_byte_assert(0x80);
528   parse_DspString();
529 }
530
531 static void
532 match_DspSimpleText(void)
533 {                               /* 03 80 */
534   match_byte_assert(3);
535   match_byte_assert(0x80);
536   match_zeros_assert(5);
537   if (!match_byte(0x10))
538     match_byte_assert(0);
539   match_zeros_assert(4);
540 }
541
542 static void
543 match_NavTreeViewItem(void)
544 {                               /* 07 80 */
545   match_byte_assert(7);
546   match_byte_assert(0x80);
547   match_zeros_assert(1);
548   if (!match_byte(0) && !match_byte(7))
549     match_byte_assert(8);
550   match_zeros_assert(3);
551   pos++;
552   match_byte_assert(0);
553   match_byte_assert(1);
554   match_zeros_assert(3);
555   if (!match_byte(0))
556     match_byte_assert(1);
557   match_zeros_assert(5);
558   match_byte_assert(1);
559   match_zeros_assert(5);
560
561   put_safe(get_string1());
562   putc('\n', stdout);
563 }
564
565 static void
566 parse_DspNumber(void)
567 {
568   match_byte_assert(1);
569   printf("DspNumber(");
570   parse_format();
571   match_byte_assert(0x80);
572   match_byte(2);
573   printf (" %f", get_double());
574   printf (" \"%s\")\n", get_string1());
575 }
576
577 static void
578 match_DspNumber(void)
579 {
580   match_byte_assert(0x2a);
581   match_byte_assert(0x80);
582   parse_DspNumber();
583 }
584
585 static void parse_flexible(void);
586
587 static void
588 parse_DspCell(void)
589 {
590   match_byte_assert(0);
591   match_DspSimpleText();
592   parse_flexible();             /* DspString or DspNumber. */
593 }
594
595 static void
596 match_DspCell(void)
597 {                               /* 27 80 */
598   match_byte_assert(0x27);
599   match_byte_assert(0x80);
600   parse_DspCell();
601 }
602
603 static void
604 parse_PMModelItemInfo(void)
605 {                               /* 54 80 */
606   match_byte_assert(0);
607   pos += 1;                     /* Counter */
608   match_zeros_assert(7);
609   pos += 3;
610   if (!match_byte(0))
611     match_byte_assert(0xe);
612   match_byte_assert(0);
613 }
614
615 static void
616 match_PMModelItemInfo(void)
617 {                               /* 54 80 */
618   match_byte_assert(0x54);
619   match_byte_assert(0x80);
620   parse_PMModelItemInfo();
621   match_DspSimpleText();
622   match_DspString();
623 }
624
625 static void
626 match_PMPivotItemTree(void)
627 {                               /* 52 80 */
628   match_byte_assert(0x52);
629   match_byte_assert(0x80);
630   match_byte_assert(0);
631   match_PMModelItemInfo();
632 }
633
634 static void
635 parse_flexible(void)
636 {
637   if (data[pos] == 0xff && data[pos + 1] == 0xff)
638     {
639       match_u16_assert(0xffff);
640       match_u16_assert(0);
641       char *heading = get_string2();
642       if (!strcmp(heading, "DspCell"))
643         parse_DspCell();
644       else if (!strcmp(heading, "DspNumber"))
645         parse_DspNumber();
646       else if (!strcmp(heading, "DspString"))
647         parse_DspString();
648       else
649         assert(0);
650     }
651   else if (data[pos] == 0x2a && data[pos + 1] == 0x80)
652     match_DspNumber();
653   else if (data[pos] == 0x27 && data[pos + 1] == 0x80)
654     match_DspCell();
655   else if (data[pos] == 0x5 && data[pos + 1] == 0x80)
656     match_DspString();
657   else if ((data[pos] == 0x3c || data[pos] == 0x39)
658             && data[pos + 1] == 0x80)
659     {
660       /* 3c 80 */
661       /* 39 80 */
662       pos += 2;
663       parse_format();
664 /*      match_byte_assert(0x01);
665       match_byte_assert(0x02);
666       match_byte_assert(0x0d); */
667     }
668   else if (data[pos] == 0x15 && data[pos + 1] == 0x80)
669     {
670       /* 15 80 */
671       data += 2;
672       match_byte_assert(2);
673       printf ("15 80(%f", get_double());
674       printf (" %s)\n", get_string1());
675     }
676   else
677     {
678       fprintf (stderr, "bad data cell 0x%02x at offset %x\n",
679                data[pos], pos);
680       hex_dump (stderr, pos, 64);
681       assert(0);
682     }
683 }
684
685 int
686 main(int argc, char *argv[])
687 {
688   bool print_offsets = false;
689   for (;;)
690     {
691       int c = getopt (argc, argv, "o");
692       if (c == -1)
693         break;
694
695       switch (c)
696         {
697         case 'o':
698           print_offsets = true;
699           break;
700
701         case '?':
702           exit (-1);
703         }
704     }
705   if (argc - optind != 1)
706     {
707       fprintf (stderr, "usage: %s FILE.bin", argv[0]);
708       exit (1);
709     }
710
711   const char *filename = argv[optind];
712   int fd = open(filename, O_RDONLY);
713   if (fd < 0)
714     {
715       fprintf (stderr, "%s: open failed (%s)", filename, strerror (errno));
716       exit (1);
717     }
718
719   struct stat s;
720   if (fstat(fd, &s))
721     {
722       perror("fstat");
723       exit(1);
724     }
725   n = s.st_size;
726   data = malloc(n);
727   if (!data)
728     {
729       perror("malloc");
730       exit(1);
731     }
732   if (read(fd, data, n) != n)
733     {
734       perror("read");
735       exit(1);
736     }
737   close(fd);
738
739   setvbuf (stdout, NULL, _IOLBF, 0);
740
741   match_byte_assert(4);
742   match_u32_assert(0);
743   match_string1_assert("SPSS Output Document");
744   match_u32_assert(1);
745   match_byte_assert(0x63);
746
747   parse_heading("NavRoot");
748   match_byte_assert(2);
749   match_zeros_assert(32);
750
751   parse_heading("DspSimpleText");
752   match_zeros_assert(10);
753
754   parse_heading("DspString");
755   parse_DspString();
756
757   parse_heading("NavTreeViewItem");
758   match_byte_assert(0);
759   match_u32_assert(0);
760   match_byte_assert(2);
761   match_byte_assert(0);
762   match_byte_assert(1);
763   match_zeros_assert(9);
764   match_u32_assert(1);
765   assert (pos == 0xb0);
766
767   pos += 0x28;
768   match_zeros_assert(5);
769   if (match_u32(8500))
770     match_u32_assert(11000);
771   else
772     {
773       match_u32_assert(11000);
774       match_u32_assert(8500);
775     }
776   pos = 0x105;
777   match_string1_assert("(Continued)");
778   match_byte_assert(1);
779   match_byte_assert(1);
780   match_zeros_assert(3);
781   get_string4();                /* page title */
782   match_byte_assert(1);
783   match_byte_assert(1);
784   match_zeros_assert(3);
785   get_string4();                /* page number */
786   match_byte_assert(0);
787   pos += 2;
788   match_u16_assert(2);
789
790   parse_heading("NavLog");
791   pos = 0x36b;
792   puts(get_padded_string(32));
793   if (!match_u32(80))
794     match_u32_assert(132);
795   match_zeros_assert(8);
796   match_u32_assert(1);
797   printf ("0x%x\n", pos);
798   get_string4();
799   match_byte_assert(0);
800
801   parse_heading("NavHead");
802   match_byte_assert(2);
803   match_zeros_assert(24);
804   match_u32_assert(1);
805   match_u32_assert(0);
806   match_DspSimpleText();
807   match_DspString();
808   match_NavTreeViewItem();
809   match_zeros_assert(3);
810
811   parse_heading("NavTitle");
812   pos += 33;
813   match_DspSimpleText();
814   match_DspString();
815   match_NavTreeViewItem();
816
817   match_byte_assert(1);
818   match_byte_assert(1);
819   match_u32_assert(-19);
820   match_zeros_assert(12);
821   match_byte_assert(0xbc);
822   match_byte_assert(2);
823   match_zeros_assert(9);
824   match_byte_assert(0x22);
825   puts(get_padded_string(32));
826   match_u32_assert(80);
827   match_zeros_assert(8);
828   match_u32_assert(1);
829   get_string4();
830   match_byte_assert(0);
831
832   parse_heading("NavNote");
833   match_byte_assert(2);
834   match_zeros_assert(8);
835   match_u32_assert(24);
836   if (!match_u32(0))
837     match_u32_assert(-40);
838   pos += 8;
839   match_u32_assert(2);
840   match_u32_assert(1);
841   match_DspSimpleText();
842   match_DspString();
843   match_NavTreeViewItem();
844   match_byte_assert(1);
845
846   parse_heading("PTPivotController");
847   match_byte_assert(2);
848   pos += 8;
849   match_u32_assert(100);
850   match_u32_assert(100);
851   match_u32_assert(100);
852   match_u32_assert(100);
853
854   parse_heading("PVPivotView");
855   match_u32_assert(5);
856   match_byte_assert(0);
857
858   parse_heading("PMPivotModel");
859   match_byte_assert(3);
860
861   parse_heading("NDimensional__DspCell");
862   match_byte_assert(0);
863   match_u32_assert(1);
864
865   parse_heading("IndexedCollection");
866   match_byte_assert(0);
867   pos++;
868   match_zeros_assert(3);
869   match_byte_assert(1);
870   match_byte_assert(0);
871
872   while (data[pos] != 1)
873     {
874       if (data[pos] == 0)
875         pos++;
876       else
877         parse_flexible();
878     }
879
880   match_byte_assert(1);
881   match_byte_assert(0);
882   puts(get_string1());
883   if (!match_u32(0))
884     match_u32_assert(2);
885   puts(get_string1());
886
887   match_byte_assert(0);
888   match_byte_assert(1);
889   match_byte_assert(0);
890   match_byte_assert(0);
891   match_byte_assert(0);
892   match_byte_assert(1);
893   match_byte_assert(0);
894
895   exit (0);
896
897   parse_heading("PMPivotItemTree");
898   match_byte_assert(0);
899
900   parse_heading("AbstractTreeBranch");
901   match_byte_assert(0);
902
903   parse_heading("PMModelItemInfo");
904   parse_PMModelItemInfo();
905   match_DspSimpleText();
906   match_DspString();
907
908   match_u32_assert(7);
909   match_PMPivotItemTree();
910
911   match_u32_assert(0);
912   match_PMPivotItemTree();
913
914   match_u32_assert(0);
915   match_PMPivotItemTree();
916
917   match_u32_assert(6);
918   match_PMPivotItemTree();
919
920   match_u32_assert(0);
921   match_PMPivotItemTree();
922
923   match_u32_assert(0);
924   match_PMPivotItemTree();
925
926   match_u32_assert(0);
927   match_PMPivotItemTree();
928
929   match_u32_assert(0);
930   match_PMPivotItemTree();
931
932   match_u32_assert(0);
933   match_PMPivotItemTree();
934
935   match_u32_assert(0);
936   match_PMPivotItemTree();
937
938   match_u32_assert(2);
939   match_PMPivotItemTree();
940
941   match_u32_assert(0);
942   match_PMPivotItemTree();
943
944   match_u32_assert(0);
945   match_PMPivotItemTree();
946
947   match_u32_assert(0);
948   match_PMPivotItemTree();
949
950   match_u32_assert(0);
951   match_PMPivotItemTree();
952
953   match_u32_assert(2);
954   match_PMPivotItemTree();
955
956   match_u32_assert(0);
957   match_PMPivotItemTree();
958
959   match_u32_assert(0);
960
961   /* ...unknown... */
962
963   while (data[pos] != 0xff || data[pos + 1] != 0xff)
964     pos++;
965   parse_heading("PVViewDimension");
966
967   int i;
968   for (i = 0; data[pos + i] != 0xff || data[pos + i + 1] != 0xff; i++)
969     ;
970   hex_dump(stdout, pos, i);
971
972   printf ("%#x: end of successful parse\n", pos);
973
974   return 0;
975 }