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