more progress
[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(3);
721   if (!match_byte(1))
722     match_byte_assert(0);
723   match_zeros_assert(3);
724   match_DspSimpleText();
725   parse_flexible();
726 }
727
728 static void
729 parse_NavOleItem(void)
730 {
731   match_byte_assert(0);
732   match_byte_assert(1);
733   match_zeros_assert(2);
734   pos++;
735   match_zeros_assert(9);
736   match_byte_assert(1);
737   match_zeros_assert(10);
738   match_byte_assert(1);
739   match_zeros_assert(6);
740   match_byte_assert(1);
741   parse_weirdness();
742   match_byte_assert(0);
743   pos++;
744   match_zeros_assert(11);
745   match_byte_assert(1);
746   match_zeros_assert(3);
747   get_string4();
748   match_byte_assert(0);
749 }
750
751 static void
752 parse_NavTitle(void)
753 {
754   match_byte_assert(2);
755   match_zeros_assert(8);
756   match_u32_assert(24);
757   get_u32();
758   pos++;
759   match_byte_assert(4);
760   match_zeros_assert(2);
761   get_u32();
762   match_u32_assert(2);
763   match_u32_assert(1);
764 }
765
766 static void
767 parse_flexible(void)
768 {
769   int start = pos;
770   if (data[pos] == 0xff && data[pos + 1] == 0xff)
771     {
772       match_u16_assert(0xffff);
773       match_u16_assert(0);
774       char *heading = get_string2();
775       if (!strcmp(heading, "DspCell"))
776         parse_DspCell();
777       else if (!strcmp(heading, "DspNumber"))
778         parse_DspNumber();
779       else if (!strcmp(heading, "DspString"))
780         parse_DspString();
781       else if (!strcmp(heading, "NavHead"))
782         parse_NavHead();
783       else if (!strcmp(heading, "IndexedCollection"))
784         match_zeros_assert(14);
785       else if (!strcmp(heading, "NavOleItem"))
786         parse_NavOleItem();
787       else if (!strcmp(heading, "NavTitle"))
788         parse_NavTitle();
789       else
790         {
791           fprintf(stderr, "don't know %s at offset 0x%x: ", heading, start);
792           hex_dump(stderr, pos, 64);
793           assert(0);
794         }
795     }
796   else if (data[pos + 1] == 0x80)
797     {
798       if (data[pos] == 0x2a && data[pos + 1] == 0x80)
799         match_DspNumber();
800       else if (data[pos] == 0x27 && data[pos + 1] == 0x80)
801         match_DspCell();
802       else if (data[pos] == 0x5 && data[pos + 1] == 0x80)
803         match_DspString();
804       else if (data[pos] == 0x7 && data[pos + 1] == 0x80)
805         match_NavTreeViewItem();
806       else if (data[pos] == 0x3 && data[pos + 1] == 0x80)
807         match_DspSimpleText();
808       else if ((data[pos] == 0x3c || data[pos] == 0x39)
809                && data[pos + 1] == 0x80)
810         {
811           /* 3c 80 */
812           /* 39 80 */
813           pos += 2;
814           parse_format();
815 /*      match_byte_assert(0x01);
816         match_byte_assert(0x02);
817         match_byte_assert(0x0d); */
818         }
819       else if (data[pos] == 0x15 && data[pos + 1] == 0x80)
820         {
821           /* 15 80 */
822           data += 2;
823           match_byte_assert(2);
824           printf ("15 80(%f", get_double());
825           printf (" %s)\n", get_string1());
826         }
827       else if (data[pos] == 0x9 && data[pos + 1] == 0x80)
828         {
829           match_NavLog();
830         }
831       else
832         {
833           fprintf (stderr, "bad record 0x%02x at offset %x\n",
834                    data[pos], pos);
835           hex_dump (stderr, pos, 64);
836           assert(0);
837         }
838     }
839   else if (match_byte(0xa)) 
840     {
841       match_zeros_assert(5);
842       assert(pos == n);
843       exit (0);
844     }
845   else
846     {
847       fprintf (stderr, "bad record start at offset %x: ", pos);
848       hex_dump (stderr, pos, 64);
849       assert(0);
850     }
851 }
852
853
854
855 int
856 main(int argc, char *argv[])
857 {
858   bool print_offsets = false;
859   for (;;)
860     {
861       int c = getopt (argc, argv, "o");
862       if (c == -1)
863         break;
864
865       switch (c)
866         {
867         case 'o':
868           print_offsets = true;
869           break;
870
871         case '?':
872           exit (-1);
873         }
874     }
875   if (argc - optind != 1)
876     {
877       fprintf (stderr, "usage: %s FILE.bin", argv[0]);
878       exit (1);
879     }
880
881   const char *filename = argv[optind];
882   int fd = open(filename, O_RDONLY);
883   if (fd < 0)
884     {
885       fprintf (stderr, "%s: open failed (%s)", filename, strerror (errno));
886       exit (1);
887     }
888
889   struct stat s;
890   if (fstat(fd, &s))
891     {
892       perror("fstat");
893       exit(1);
894     }
895   n = s.st_size;
896   data = malloc(n + 256);
897   if (!data)
898     {
899       perror("malloc");
900       exit(1);
901     }
902   if (read(fd, data, n) != n)
903     {
904       perror("read");
905       exit(1);
906     }
907   for (int i = 0; i < 256; i++)
908     data[n + i] = i % 2 ? 0xaa : 0x55;
909   close(fd);
910
911   setvbuf (stdout, NULL, _IOLBF, 0);
912
913   match_byte_assert(4);
914   match_u32_assert(0);
915   match_string1_assert("SPSS Output Document");
916   match_u32_assert(1);
917   match_byte_assert(0x63);
918
919   parse_heading("NavRoot");
920   match_byte_assert(2);
921   match_zeros_assert(32);
922
923   parse_heading("DspSimpleText");
924   match_zeros_assert(10);
925
926   parse_heading("DspString");
927   parse_DspString();
928
929   parse_heading("NavTreeViewItem");
930   match_byte_assert(0);
931   if (!match_u32(1))
932     match_u32_assert(0);
933   match_byte_assert(2);
934   match_byte_assert(0);
935   match_byte_assert(1);
936   match_zeros_assert(9);
937   match_u32_assert(1);
938
939   match_u32_assert(0);
940   match_u32_assert(0x18);
941   if (!match_u32(0))
942     match_u32_assert(0xffffffd8);
943   match_u32_assert(0xffffffde);
944   match_u32_assert(0x18);
945   if (!match_u32(0))
946     match_u32_assert(0xffffffd8);
947   match_u32_assert(0x28);
948   match_u32_assert(0x28);
949   pos += 8;
950   if (data[pos] == 0)
951     {
952       match_zeros_assert(5);
953
954       if (match_u32(8500))
955         match_u32_assert(11000);
956       else
957         {
958           match_u32_assert(11000);
959           match_u32_assert(8500);
960         }
961       pos += 32;
962       get_string1();
963       if (!match_byte(0))
964         match_byte_assert(1);
965       pos++;
966       pos++;
967       pos++;
968       pos++;
969       get_string4();                /* page title */
970       match_byte_assert(1);
971       match_byte_assert(1);
972       match_zeros_assert(3);
973       get_string4();                /* page number */
974       match_byte_assert(0);
975       pos += 2;
976       match_u16_assert(2);
977     }
978
979   if (data[pos + 9] != 'L')
980     exit(0);
981   parse_heading("NavLog");
982   parse_NavLog();
983   parse_flexible();
984   parse_flexible();
985   parse_flexible();
986   parse_flexible();
987   parse_flexible();
988   parse_flexible();
989   exit(0);
990   puts(get_padded_string(32));
991   if (!match_u32(80))
992     match_u32_assert(132);
993   match_zeros_assert(8);
994   match_u32_assert(1);
995   printf ("0x%x\n", pos);
996   get_string4();
997   match_byte_assert(0);
998
999   parse_heading("NavHead");
1000   parse_NavHead();
1001   match_NavTreeViewItem();
1002   match_zeros_assert(3);
1003
1004   parse_heading("NavTitle");
1005   pos += 33;
1006   match_DspSimpleText();
1007   match_DspString();
1008   match_NavTreeViewItem();
1009
1010   match_byte_assert(1);
1011   match_byte_assert(1);
1012   match_u32_assert(-19);
1013   match_zeros_assert(12);
1014   match_byte_assert(0xbc);
1015   match_byte_assert(2);
1016   match_zeros_assert(9);
1017   match_byte_assert(0x22);
1018   puts(get_padded_string(32));
1019   match_u32_assert(80);
1020   match_zeros_assert(8);
1021   match_u32_assert(1);
1022   get_string4();
1023   match_byte_assert(0);
1024
1025   parse_heading("NavNote");
1026   match_byte_assert(2);
1027   match_zeros_assert(8);
1028   match_u32_assert(24);
1029   if (!match_u32(0))
1030     match_u32_assert(-40);
1031   pos += 8;
1032   match_u32_assert(2);
1033   match_u32_assert(1);
1034   match_DspSimpleText();
1035   match_DspString();
1036   match_NavTreeViewItem();
1037   match_byte_assert(1);
1038
1039   parse_heading("PTPivotController");
1040   match_byte_assert(2);
1041   pos += 8;
1042   match_u32_assert(100);
1043   match_u32_assert(100);
1044   match_u32_assert(100);
1045   match_u32_assert(100);
1046
1047   parse_heading("PVPivotView");
1048   match_u32_assert(5);
1049   match_byte_assert(0);
1050
1051   parse_heading("PMPivotModel");
1052   match_byte_assert(3);
1053
1054   parse_heading("NDimensional__DspCell");
1055   match_byte_assert(0);
1056   match_u32_assert(1);
1057
1058   parse_heading("IndexedCollection");
1059   match_byte_assert(0);
1060   pos++;
1061   match_zeros_assert(3);
1062   match_byte_assert(1);
1063   match_byte_assert(0);
1064   match_zeros_assert(7);
1065
1066   while (data[pos] != 1)
1067     {
1068       if (data[pos] == 0)
1069         pos++;
1070       else
1071         parse_flexible();
1072     }
1073
1074   match_byte_assert(1);
1075   match_byte_assert(0);
1076   puts(get_string1());
1077   if (!match_u32(0))
1078     match_u32_assert(2);
1079   puts(get_string1());
1080
1081   match_byte_assert(0);
1082   match_byte_assert(1);
1083   match_byte_assert(0);
1084   match_byte_assert(0);
1085   match_byte_assert(0);
1086   match_byte_assert(1);
1087   match_byte_assert(0);
1088
1089   exit (0);
1090
1091   parse_heading("PMPivotItemTree");
1092   match_byte_assert(0);
1093
1094   parse_heading("AbstractTreeBranch");
1095   match_byte_assert(0);
1096
1097   parse_heading("PMModelItemInfo");
1098   parse_PMModelItemInfo();
1099   match_DspSimpleText();
1100   match_DspString();
1101
1102   match_u32_assert(7);
1103   match_PMPivotItemTree();
1104
1105   match_u32_assert(0);
1106   match_PMPivotItemTree();
1107
1108   match_u32_assert(0);
1109   match_PMPivotItemTree();
1110
1111   match_u32_assert(6);
1112   match_PMPivotItemTree();
1113
1114   match_u32_assert(0);
1115   match_PMPivotItemTree();
1116
1117   match_u32_assert(0);
1118   match_PMPivotItemTree();
1119
1120   match_u32_assert(0);
1121   match_PMPivotItemTree();
1122
1123   match_u32_assert(0);
1124   match_PMPivotItemTree();
1125
1126   match_u32_assert(0);
1127   match_PMPivotItemTree();
1128
1129   match_u32_assert(0);
1130   match_PMPivotItemTree();
1131
1132   match_u32_assert(2);
1133   match_PMPivotItemTree();
1134
1135   match_u32_assert(0);
1136   match_PMPivotItemTree();
1137
1138   match_u32_assert(0);
1139   match_PMPivotItemTree();
1140
1141   match_u32_assert(0);
1142   match_PMPivotItemTree();
1143
1144   match_u32_assert(0);
1145   match_PMPivotItemTree();
1146
1147   match_u32_assert(2);
1148   match_PMPivotItemTree();
1149
1150   match_u32_assert(0);
1151   match_PMPivotItemTree();
1152
1153   match_u32_assert(0);
1154
1155   /* ...unknown... */
1156
1157   while (data[pos] != 0xff || data[pos + 1] != 0xff)
1158     pos++;
1159   parse_heading("PVViewDimension");
1160
1161   int i;
1162   for (i = 0; data[pos + i] != 0xff || data[pos + i + 1] != 0xff; i++)
1163     ;
1164   hex_dump(stdout, pos, i);
1165
1166   printf ("%#x: end of successful parse\n", pos);
1167
1168   return 0;
1169 }