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