work on spo files
authorBen Pfaff <blp@cs.stanford.edu>
Tue, 23 Jun 2020 00:55:08 +0000 (00:55 +0000)
committerBen Pfaff <blp@cs.stanford.edu>
Tue, 23 Jun 2020 00:55:08 +0000 (00:55 +0000)
Makefile
dump-float.c
dump-float2.c [new file with mode: 0644]
dump-spo.c
dump-spo2.c [new file with mode: 0644]
spo-notes

index c9a1c7d5517161c42ef6a26b4a445598705ab218..5e208d79bc0e7eeddb39e55653a553306f8a93a3 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -5,9 +5,11 @@ parse-xml.o: CFLAGS := $(shell pkg-config --cflags libxml-2.0) $(base_cflags)
 parse-xml: LDFLAGS := $(shell pkg-config --libs libxml-2.0) $(LDFLAGS)
 dump2.o: CFLAGS := $(base_cflags) -Wno-unused
 dump-spo.o: CFLAGS := $(base_cflags) -Wno-unused
+dump-spo2.o: CFLAGS := $(base_cflags) -Wno-unused
 
-all: dump dump2 parse-xml dump-spo
+all: dump dump2 parse-xml dump-spo dump-spo2
 dump-spo: dump-spo.o u8-mbtouc.o
+dump-spo2: dump-spo2.o u8-mbtouc.o
 dump: dump.o u8-mbtouc.o
 dump2: dump2.o u8-mbtouc.o
 parse-xml: parse-xml.o
index 84e4115e1915e7e8663241f54640d382c1efb769..21aa391d068cc319a863100348a0b01f9cf53782 100644 (file)
@@ -1,15 +1,33 @@
 #include <stdint.h>
 #include <stdio.h>
+#include <string.h>
+
+void
+print_double (const uint8_t *b)
+{
+  double d;
+  memcpy (&d, b, 8);
+  printf ("%f\n", d);
+}
+
+void
+print_float (const uint8_t *b)
+{
+  float d;
+  memcpy (&d, b, 4);
+  printf ("%f\n", d);
+}
 
 int
 main (void)
 {
-  union
-    {
-      uint8_t b[8];
-      double d;
-    }
-  x = { .b = { 0x18, 0x00, 0x00, 0x1c, 0xf0, 0xff, 0xff, 0x5f } };
-  printf ("%f\n", x.d);
+  uint8_t b[] = { 0x1f, 0x05, 0, 0, 0xa2, 3, 0, 0, 0x64, 0, 0, 0 };
+  int n = sizeof b;
+  for (int i = 0; i <= n - 8; i++)
+    print_double (b + i);
+/*
+  for (int i = 0; i <= n - 4; i++)
+    print_float (b + i);
+*/
   return 0;
 }
diff --git a/dump-float2.c b/dump-float2.c
new file mode 100644 (file)
index 0000000..0d27404
--- /dev/null
@@ -0,0 +1,11 @@
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+int
+main (void)
+{
+  double d = 100;
+  fwrite (&d, 1, sizeof d, stdout);
+  return 0;
+}
index 1ca9f0a03f3fda520956e510c9db883a84767d9c..fe35a0bb4f851b3b4d3c6ae36a75910bf203c554 100644 (file)
@@ -576,7 +576,7 @@ dump_cell(void)
 }
 
 static void
-dump_category (int level, uint8_t *catstart, bool *have_catstart)
+dump_category (int level, uint8_t *delim, int *delim_len)
 {
   int cat_index = get_u32();
   assert (cat_index < 256);
@@ -619,25 +619,30 @@ dump_category (int level, uint8_t *catstart, bool *have_catstart)
     printf (" (group with %d children)", n_children);
   else
     printf (" (category #%d)", cat_index);
+  printf ("\n");
 
-  printf (" %02x %02x %02x %02x %02x %02x\n",
-          data[pos], data[pos + 1], data[pos + 2],
-          data[pos + 3], data[pos + 4], data[pos + 5]);
-  if (!*have_catstart)
+  int dlen = 0;
+  for (int i = 0; i < 2; i++)
     {
-      *have_catstart = true;
-      memcpy(catstart, &data[pos], 6);
+      if (data[pos + dlen] == 0xff)
+        dlen += 7;
+      else
+        dlen += 3;
+    }
+  if (!*delim_len)
+    {
+      *delim_len = dlen;
+      memcpy(delim, &data[pos], dlen);
     }
-  pos += 6;
+  pos += dlen;
 
   for (int i = 0; i < n_children; i++)
-    dump_category (level + 1, catstart, have_catstart);
+    dump_category (level + 1, delim, delim_len);
 }
 
 static void
 dump_PMModelItemInfo(int ndims)
 {
-  return;
 #if 0
   if (data[pos + 9] && data[pos + 9] != 0xff)//count_zeros (&data[pos + 9]) < 4)
     return;
@@ -645,22 +650,22 @@ dump_PMModelItemInfo(int ndims)
   
   match_byte_assert (0);
 
-  uint8_t catstart[6];
-  bool have_catstart = false;
-  dump_category (0, catstart, &have_catstart);
-  assert(have_catstart);
+  uint8_t delim[14];
+  int delim_len = 0;
+  dump_category (0, delim, &delim_len);
+  assert(delim_len);
 
   for (int i = 1; i < ndims; i++)
     {
       for (int j = 0; ; j++, pos++)
         {
-          assert (j <= 1000);
-          if (!memcmp(&data[pos], catstart, 6))
+          assert (pos + j + delim_len < n);
+          if (!memcmp(&data[pos], delim, delim_len))
             break;
         }
-      pos += 6;
+      pos += delim_len;
 
-      dump_category (0, catstart, &have_catstart);
+      dump_category (0, delim, &delim_len);
     }
 }
 
@@ -720,6 +725,44 @@ main(int argc, char *argv[])
 
   setvbuf (stdout, NULL, _IOLBF, 0);
 
+  pos = 0x39b;
+  unsigned int rtflen = get_u32();
+  pos += rtflen - 1;            /* Almost past SPSS syntax */
+  assert(pos < n);
+  pos += 0x45 + data[pos + 0x44]; /* Just past the string */
+  pos += 0x1a + data[pos + 0x19]; /* Just past the string again */
+  pos += 0x66 + data[pos + 0x65]; /* Just past the third string */
+  pos += 0x4e;
+  rtflen = get_u32();
+  pos += rtflen - 1;            /* Almost past 2nd RTF */
+  pos += 0x64 + data[pos + 0x63]; /* Just past the fourth string */
+  /* as-number: */
+  if (data[pos + 0x114] == 0xff)
+    pos += 0x117 + data[pos + 0x115] + 256 * data[pos + 0x116];
+  else
+    pos += 0x115 + data[pos + 0x114];
+  pos += 0x18 + data[pos + 0x17]; /* Just past "<none>" or dataset name. */
+  pos += 0x18 + data[pos + 0x17]; /* Just past "<none>" or dataset name. */
+  pos += count_zeros(&data[pos]); /* Skip optional zeros. */
+  pos += 0x18 + data[pos + 0x17]; /* Just past "<none>" or dataset name. */
+  pos += 0x3e + data[pos + 0x3d]; /* Skip past "100" etc. */
+  pos += count_zeros(&data[pos]); /* Skip optional zeros. */
+  pos += 0x18 + data[pos + 0x17]; /* Just past "User-defined...". */
+  pos += 0x18 + data[pos + 0x17]; /* Just past "Statistics are based... */
+  if (data[pos + 0x19] == 0xff)
+    pos += 0x1c + data[pos + 0x1a] + 256 * data[pos + 0x1b];
+  else
+    pos += 0x1a + data[pos + 0x19];
+  pos += 0x61 + data[pos + 0x60]; /* Just past "Cluster_Notes". */
+  pos += 0x992 + data[pos + 0x991]; /* Just past "Cluster". */
+  pos += 0x4e;
+  rtflen = get_u32();
+  pos += rtflen - 1;            /* Almost past RTF with filesystem path */
+  pos += 0x45 + data[pos + 0x44]; /* Just past "Statistics" */
+  pos += 0x1a + data[pos + 0x19]; /* Just past "Cluster". */
+  fwrite (&data[pos], 1, n - pos, stdout);
+  exit (0);
+
   int sum = 0;
   
 #if 0
@@ -1048,7 +1091,20 @@ main(int argc, char *argv[])
           continue;
         }
 
-      
+      if (data[pos] == 0 && data[pos + 1] == 0xff
+          && (!data[pos + 2] || data[pos + 2] == 0xff) && data[pos + 3] == 0xff
+          && data[pos + 4] == 0)
+        {
+          pos++;
+          if (prev_end != pos)
+            {
+              if (print_offsets)
+                printf ("%04x ", prev_end);
+              hex_dump (stdout, prev_end, pos - prev_end);
+            }
+          prev_end = pos;
+          continue;
+        }
 
 
       if (!is_ascii(data[pos]))
diff --git a/dump-spo2.c b/dump-spo2.c
new file mode 100644 (file)
index 0000000..3367163
--- /dev/null
@@ -0,0 +1,467 @@
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <float.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <unistd.h>
+#include "u8-mbtouc.h"
+
+static const char *filename;
+static uint8_t *data;
+static size_t n;
+
+int version;
+
+unsigned int pos;
+
+#define XSTR(x) #x
+#define STR(x) XSTR(x)
+#define WHERE __FILE__":" STR(__LINE__)
+
+static uint8_t
+get_byte(void)
+{
+  return data[pos++];
+}
+
+static unsigned int
+get_u32(void)
+{
+  uint32_t x;
+  memcpy(&x, &data[pos], 4);
+  pos += 4;
+  return x;
+}
+
+static unsigned long long int
+get_u64(void)
+{
+  uint64_t x;
+  memcpy(&x, &data[pos], 8);
+  pos += 8;
+  return x;
+}
+
+static unsigned int
+get_be32(void)
+{
+  uint32_t x;
+  x = (data[pos] << 24) | (data[pos + 1] << 16) | (data[pos + 2] << 8) | data[pos + 3];
+  pos += 4;
+  return x;
+}
+
+static unsigned int
+get_u16(void)
+{
+  uint16_t x;
+  memcpy(&x, &data[pos], 2);
+  pos += 2;
+  return x;
+}
+
+static double
+get_double(void)
+{
+  double x;
+  memcpy(&x, &data[pos], 8);
+  pos += 8;
+  return x;
+}
+
+static double __attribute__((unused))
+get_float(void)
+{
+  float x;
+  memcpy(&x, &data[pos], 4);
+  pos += 4;
+  return x;
+}
+
+static bool
+match_u32(uint32_t x)
+{
+  if (get_u32() == x)
+    return true;
+  pos -= 4;
+  return false;
+}
+
+bool
+match_u16(uint16_t x)
+{
+  if (get_u16() == x)
+    return true;
+  pos -= 2;
+  return false;
+}
+
+static void
+match_u32_assert(uint32_t x, const char *where)
+{
+  unsigned int y = get_u32();
+  if (x != y)
+    {
+      fprintf(stderr, "%s: 0x%x: expected i%u, got i%u\n", where, pos - 4, x, y);
+      exit(1);
+    }
+}
+#define match_u32_assert(x) match_u32_assert(x, WHERE)
+
+static void
+match_u16_assert(uint16_t x, const char *where)
+{
+  unsigned int y = get_u16();
+  if (x != y)
+    {
+      fprintf(stderr, "%s: 0x%x: expected u16:%u, got u16:%u\n", where, pos - 2, x, y);
+      exit(1);
+    }
+}
+#define match_u16_assert(x) match_u16_assert(x, WHERE)
+
+static bool __attribute__((unused))
+match_u64(uint64_t x)
+{
+  if (get_u64() == x)
+    return true;
+  pos -= 8;
+  return false;
+}
+
+static void __attribute__((unused))
+match_u64_assert(uint64_t x, const char *where)
+{
+  unsigned long long int y = get_u64();
+  if (x != y)
+    {
+      fprintf(stderr, "%s: 0x%x: expected u64:%lu, got u64:%llu\n", where, pos - 8, x, y);
+      exit(1);
+    }
+}
+#define match_u64_assert(x) match_u64_assert(x, WHERE)
+
+static bool __attribute__((unused))
+match_be32(uint32_t x)
+{
+  if (get_be32() == x)
+    return true;
+  pos -= 4;
+  return false;
+}
+
+static void
+match_be32_assert(uint32_t x, const char *where)
+{
+  unsigned int y = get_be32();
+  if (x != y)
+    {
+      fprintf(stderr, "%s: 0x%x: expected be%u, got be%u\n", where, pos - 4, x, y);
+      exit(1);
+    }
+}
+#define match_be32_assert(x) match_be32_assert(x, WHERE)
+
+static bool
+match_byte(uint8_t b)
+{
+  if (pos < n && data[pos] == b)
+    {
+      pos++;
+      return true;
+    }
+  else
+    return false;
+}
+
+static void
+match_byte_assert(uint8_t b, const char *where)
+{
+  if (!match_byte(b))
+    {
+      fprintf(stderr, "%s: 0x%x: expected %02x, got %02x\n", where, pos, b, data[pos]);
+      exit(1);
+    }
+}
+#define match_byte_assert(b) match_byte_assert(b, WHERE)
+
+static bool
+match_bytes(int start, const int *bytes, size_t n_bytes)
+{
+  for (size_t i = 0; i < n_bytes; i++)
+    if (bytes[i] >= 0 && data[start + i] != bytes[i])
+      return false;
+  return true;
+}
+
+static char *
+xmemdup0(const void *p, size_t n)
+{
+  char *s = malloc(n + 1);
+  memcpy(s, p, n);
+  s[n] = 0;
+  return s;
+}
+
+static bool
+get_bool(void)
+{
+  if (match_byte(0))
+    return false;
+  match_byte_assert(1);
+  return true;
+}
+
+static bool __attribute__((unused))
+is_ascii(uint8_t p)
+{
+  return (p >= ' ' && p < 127) || p == '\r' || p == '\n' || p == '\t';
+}
+
+static int
+count_zeros(const uint8_t *p)
+{
+  size_t n = 0;
+  while (p[n] == 0)
+    n++;
+  return n;
+}
+
+static bool __attribute__((unused))
+all_utf8(const char *p_, size_t len)
+{
+  const uint8_t *p = (const uint8_t *) p_;
+  for (size_t ofs = 0, mblen; ofs < len; ofs += mblen)
+    {
+      ucs4_t uc;
+
+      mblen = u8_mbtouc (&uc, p + ofs, len - ofs);
+      if ((uc < 32 && uc != '\n') || uc == 127 || uc == 0xfffd)
+        return false;
+    }
+  return true;
+}
+
+static char *
+get_string1(void)
+{
+  int len = data[pos++];
+  char *s = xmemdup0(&data[pos], len);
+  pos += len;
+  return s;
+}
+
+static char *
+get_string2(void)
+{
+  int len = data[pos] + data[pos + 1] * 256;
+  char *s = xmemdup0(&data[pos + 2], len);
+  pos += 2 + len;
+  return s;
+}
+
+static char *
+get_string(const char *where)
+{
+  if (1
+      /*data[pos + 1] == 0 && data[pos + 2] == 0 && data[pos + 3] == 0*/
+      /*&& all_ascii(&data[pos + 4], data[pos])*/)
+    {
+      int len = data[pos] + data[pos + 1] * 256;
+      char *s = malloc(len + 1);
+
+      memcpy(s, &data[pos + 4], len);
+      s[len] = 0;
+      pos += 4 + len;
+      return s;
+    }
+  else
+    {
+      fprintf(stderr, "%s: 0x%x: expected string\n", where, pos);
+      exit(1);
+    }
+}
+#define get_string() get_string(WHERE)
+
+static char *
+get_string_be(const char *where)
+{
+  if (1
+      /*data[pos + 1] == 0 && data[pos + 2] == 0 && data[pos + 3] == 0*/
+      /*&& all_ascii(&data[pos + 4], data[pos])*/)
+    {
+      int len = data[pos + 2] * 256 + data[pos + 3];
+      char *s = malloc(len + 1);
+
+      memcpy(s, &data[pos + 4], len);
+      s[len] = 0;
+      pos += 4 + len;
+      return s;
+    }
+  else
+    {
+      fprintf(stderr, "%s: 0x%x: expected string\n", where, pos);
+      exit(1);
+    }
+}
+#define get_string_be() get_string_be(WHERE)
+
+static int
+get_end(void)
+{
+  int len = get_u32();
+  return pos + len;
+}
+
+static void __attribute__((unused))
+hex_dump(FILE *stream, int ofs, int n)
+{
+  int n_ascii = 0;
+  for (int i = 0; i < n; i++)
+    {
+      int c = data[ofs + i];
+      n_ascii += is_ascii(c);
+      fprintf(stream, " %02x", c);
+    }
+  if (n_ascii >= 3)
+    {
+      putc(' ', stream);
+      for (int i = 0; i < n; i++)
+        {
+          int c = data[ofs + i];
+          putc(c >= 32 && c < 127 ? c : '.', stream);
+        }
+    }
+  putc('\n', stream);
+}
+
+static void __attribute__((unused))
+char_dump(FILE *stream, int ofs, int n)
+{
+  for (int i = 0; i < n; i++)
+    {
+      int c = data[ofs + i];
+      putc(c >= 32 && c < 127 ? c : '.', stream);
+    }
+  putc('\n', stream);
+}
+
+
+static int
+compare_int(const void *a_, const void *b_)
+{
+  const int *a = a_;
+  const int *b = b_;
+  return *a < *b ? -1 : *a > *b;
+}
+
+
+static const char *
+format_name (int format, char *buf)
+{
+  switch (format)
+    {
+    case 1: return "A";
+    case 2: return "AHEX";
+    case 3: return "COMMA";
+    case 4: return "DOLLAR";
+    case 5: case 40: return "F";
+    case 6: return "IB";
+    case 7: return "PIBHEX";
+    case 8: return "P";
+    case 9: return "PIB";
+    case 10: return "PK";
+    case 11: return "RB";
+    case 12: return "RBHEX";
+    case 15: return "Z";
+    case 16: return "N";
+    case 17: return "E";
+    case 20: return "DATE";
+    case 21: return "TIME";
+    case 22: return "DATETIME";
+    case 23: return "ADATE";
+    case 24: return "JDATE";
+    case 25: return "DTIME";
+    case 26: return "WKDAY";
+    case 27: return "MONTH";
+    case 28: return "MOYR";
+    case 29: return "QYR";
+    case 30: return "WKYR";
+    case 31: return "PCT";
+    case 32: return "DOT";
+    case 33: return "CCA";
+    case 34: return "CCB";
+    case 35: return "CCC";
+    case 36: return "CCD";
+    case 37: return "CCE";
+    case 38: return "EDATE";
+    case 39: return "SDATE";
+    default: sprintf(buf, "(%d)", format); return buf;
+    }
+}
+
+
+int
+main(int argc, char *argv[])
+{
+  bool print_offsets = false;
+  for (;;)
+    {
+      int c = getopt (argc, argv, "o");
+      if (c == -1)
+        break;
+
+      switch (c)
+        {
+        case 'o':
+          print_offsets = true;
+          break;
+
+        case '?':
+          exit (-1);
+        }
+    }
+  if (argc - optind != 1)
+    {
+      fprintf (stderr, "usage: %s FILE.bin", argv[0]);
+      exit (1);
+    }
+
+  const char *filename = argv[optind];
+  int fd = open(filename, O_RDONLY);
+  if (fd < 0)
+    {
+      fprintf (stderr, "%s: open failed (%s)", filename, strerror (errno));
+      exit (1);
+    }
+
+  struct stat s;
+  if (fstat(fd, &s))
+    {
+      perror("fstat");
+      exit(1);
+    }
+  n = s.st_size;
+  data = malloc(n);
+  if (!data)
+    {
+      perror("malloc");
+      exit(1);
+    }
+  if (read(fd, data, n) != n)
+    {
+      perror("read");
+      exit(1);
+    }
+  close(fd);
+
+  setvbuf (stdout, NULL, _IOLBF, 0);
+
+  return 0;
+}
index 92038d722c03b54136410024af846522cd94954b..30132d6f5c87d1ebc62aa089f8bf6f8914b6dc40 100644 (file)
--- a/spo-notes
+++ b/spo-notes
@@ -25,6 +25,13 @@ and the DspString case:
        01 02 28 (00|05) 00 (00|01)
        often followed by a string
 
+The first byte might be a version number.  "Save As" or "Save" has the effect:
+    00 -> 00 (tried 1 out of 3)
+    01 -> 04 (tried 3 out of 3)
+    02 -> 04 (tried 1 out of many: deska, regress)
+    03 -> 04 (tried 1 out of many: 3d67bb)
+    04 -> 04 (tried 1 out of many: detailer2)
+
 Charset numbers come from the list for \fcharset in the RTF
 specification:
 http://latex2rtf.sourceforge.net/rtfspec_6.html#rtfspec_10 The charset
@@ -188,7 +195,7 @@ ffff 0000 "IndexedCollection"
        01 00
 
 ffff 0000 "DspCell"
-       00 03 80 00 00 00 00 00 00 00 00 00 00
+       00 03 80 00*10
 
 ffff 0000 "DspNumber"
 
@@ -557,3 +564,502 @@ that if a node is collapsed it *increases* by 2000 then decreases by
     421:-83360 (-2000)  04 00 00 00 04 00 00
     422:cell F40.2 "LogAC"
 
+
+ZMAW_zaj3:
+
+rec:PVTextStyle
+.. 02 .. 02
+.. c8 .. LM .. RM .. TM .. BM .. .. .. 01 .. yy yy yy yy ww ww .. .. xx xx xx xx .. .. .. .. zz zz zz zz ii uu ss cc cc cc cc 22 "Arial" FF FF FF .. .. .. a7 80 .. 01 .. .. .. .. .. BB BB BB .. SS .. a9 80 .. 01 .. ..
+.. c8 .. 78 .. a0 .. 14 .. 14 .. .. .. 01 .. f3 ff ff ff .. .. .. .. .. .. .. .. .. .. .. .. 90 01 .. .. .. .. .. ee 00 00 00 22 "Arial" .. .. .. .. .. .. a7 80 .. 01 .. .. .. .. .. ff ff ff .. .. .. a9 80 .. 01 .. ..
+.. c8 .. 78 .. a0 .. 14 .. 14 .. .. .. 01 .. f3 ff ff ff .. .. .. .. .. .. .. .. .. .. .. .. 90 01 .. .. .. .. .. ee 00 00 00 22 "Arial" .. .. .. .. .. .. a7 80 .. 01 .. .. .. .. .. ff ff ff .. .. .. a9 80 .. .. .. ..
+.. c8 .. 78 .. a0 .. 14 .. 14 .. .. .. 01 .. f3 ff ff ff .. .. .. .. .. .. .. .. .. .. .. .. 90 01 .. .. .. .. .. ee 00 00 00 22 "Arial" .. .. .. .. .. .. a7 80 .. 01 .. .. .. .. .. ff ff ff .. .. .. a9 80 .. 01 .. 02
+.. c8 .. 78 .. a0 .. 14 .. 14 .. .. .. 01 .. f3 ff ff ff .. .. .. .. .. .. .. .. .. .. .. .. 90 01 .. .. .. .. .. ee 00 00 00 22 "Arial" .. .. .. .. .. .. a7 80 .. 01 .. .. .. .. .. ff ff ff .. .. .. a9 80 .. 02 .. 03
+.. c8 .. 78 .. a0 .. 14 .. 14 .. .. .. 01 .. f3 ff ff ff .. .. .. .. .. .. .. .. .. .. .. .. 90 01 .. .. .. .. .. ee 00 00 00 22 "Arial" .. .. .. .. .. .. a7 80 .. 01 .. .. .. .. .. ff ff ff .. .. .. a9 80 .. 01 .. ..
+.. c8 .. 78 .. a0 .. 14 .. 14 .. .. .. 01 .. f3 ff ff ff .. .. .. .. .. .. .. .. .. .. .. .. 90 01 .. .. .. .. .. ee 00 00 00 22 "Arial" .. .. .. .. .. .. a7 80 .. 01 .. .. .. .. .. ff ff ff .. .. .. a9 80 .. 01 .. ..
+.. c8 .. 68 01 68 01 28 .. 28 .. .. .. 01 .. f3 ff ff ff .. .. .. .. .. .. .. .. .. .. .. .. 90 01 .. .. .. .. .. ee 00 00 00 22 "Arial" .. .. .. .. .. .. .. .. 01 .. .. .. .. .. .. .. 01 .. 01 .. .. .. .. .. .. .. 01
+.. 01 .. .. .. .. .. .. .. 01 .. 01 .. .. .. .. .. .. .. 01 .. .. .. .. .. .. .. .. .. 01 .. .. .. .. .. .. .. 01 .. 01 .. .. .. .. .. .. .. 01 ..
+
+yy: font size, f2ffffff or f3ffffff is normalish, f8ffffff is tiny,
+f0ffffff is big
+ww: something to do with font size or stretch. 01 is normal, 02+ gets weird
+xx: rotation angle
+zz: 400 is normal weight, 700+ is bold
+ii: 1+ is italic
+uu: 1+ is underline
+ss: 1+ is strikethrough
+cc: RTF charset number, e.g. 0 for ANSI, 238 for Polish
+FF: RGB foreground
+BB: RGB background
+SS: shade fraction from 0=unshaded to 0xa=opaque
+LT, RM, TM, BM: margins, 20=1 point
+
+Some confusion on separation, BB and later seem to correspond to the next area
+
+Areas:
+0. title
+1. layers
+2. corner
+3. row heads
+4. column heads
+5. data
+6. caption
+7. footnotes
+
+ rr gg bb 00 00 00 ss 00 01
+ ff 00 ff 00 00 00 01 00 01 00
+ ff 00 ff 00 00 00 01 00 01 00
+ ff 00 ff 00 00 00 01 00 01 00
+ ff 00 ff 00 00 00 01 00 00 00
+ 00 00 00 00 00 00 01 00
+ ff 00 ff 00 00 00 01 00 01 00
+ ff 00 ff 00 00 00 01 00
+
+rr gg bb: stroke color
+ss: style, 0=thin, 1=normal, 2=thick, 3=very thick, otherwise weird
+
+Borders:
+- horizontal category columns
+- vertical category columns
+...
+- left inner frame
+- right inner frame
+- top inner frame
+- bottom inner frame
+- left data area
+- top data area
+
+
+..
+01 .. ff .. ff .. .. .. .. ..
+.. .. .. .. .. .. 03 80 ..
+01 .. ff .. ff .. .. .. .. ..
+01 .. ff .. ff .. .. .. .. ..
+01 .. ff .. ff .. .. .. .. ..
+01 .. ff .. ff .. .. .. .. ..
+
+..
+01 .. .. .. .. .. .. .. .. ..
+.. .. .. .. .. .. 03 80 ..
+01 .. .. .. .. .. .. .. .. .. .. ..
+01 .. .. .. .. .. .. .. .. .. .. ..
+
+..
+01 .. 80 80 80 .. .. .. 01 ..
+01 .. 80 80 80 .. .. .. 01 ..
+01 .. 80 80 80 .. .. .. .. ..
+01 .. 80 80 80 .. .. .. .. ..
+ba 80 ..
+01 .. 80 80 80 .. .. .. 01 ..
+01 .. 80 80 80 .. .. .. 01 ..
+01 .. 80 80 80 .. .. .. .. ..
+01 .. 80 80 80 .. .. .. .. ..
+
+class {00000319-0000-0000-c000-000000000046}: Enhanced Metafile
+   For these, dropping the first 4 bytes of Embedding*/CONTENTS file
+   yields an EMF file that the printemf program can dissect.
+
+class {62078ae0-e4fe-11cd-838d-0000c0f17248}:
+    harder to pin down what's in these
+
+view-spo can Edit|copy notes and tables for pasting into other applications
+
+rigger: contains working graph!
+
+correspondence:
+ 1/01 80 ? "NavRoot"
+ 3/03 80 -> "DspSimpleText"
+ 5/05 80 -> "DspString" 01 02 28 (00|05) 00 01 b"string"
+ 7/07 80 -> "NavTreeViewItem"
+ 9/09 80 -> "NavLog"
+11/0b 80 ?  "NavHead"
+13/0d 80 ?  "NavTitle"
+15/0f 80 ???
+17/11 80 ???
+19/13 80 ???
+21/15 80 ???
+24 18 80 -> "NavNote"
+29 1d 80 -> "PTPivotController"
+31 1f 80 -> "PTPivotView"
+33/21 80 -> "PMPivotModel"
+35/23 80 -> "NDimensional__DspCell"
+37/25 80 -> "IndexedCollection"
+39/27 80 -> "DspCell"
+41/29 80 ? "DspNumber"
+  /50 80 -> "PMPivotItemTree"
+  /52 80 -> "AbstractTreeBranch"
+  /54 80 -> "PMModelItemInfo"
+
+
+When the top-level "Output" node is selected for save:
+       04 i0
+       "SPSS Output Document"
+       i1 63
+       ffff 0000 "NavRoot" 02 00*32
+       ffff 0000 "DspSimpleText" 00*10
+       ffff 0000 "DspString" 01 02 28 00 00 01 b"Output"
+       ffff 0000 "NavTreeViewItem" 00 i0 02 00 01 00*9 i1
+00c0   (i0 | i24) (i24 | i-40) (i0 | i40 | i-40) (i40 | i-34)
+00d0   (i1048 | some other 3-hex-digit number | i0)
+00d4   (i0 | some 3 to 4-hex-digit number | i-40)
+00d8   00*5
+00dd    (i8500 i11000 | i11000 i8500)
+00e5   (s1 | s9) (s1 | s7 | s15) (s1 | s2)
+00eb   i1270*4
+00fb   s0 i240 s?? s1 b"(Continued)" 01 01 00*3 byte 00*3
+0116   w"{\rtf... pagetitle}"
+01fd   01 01 00*3
+0202   w"{\rtf... page number}"
+02e9   00 short s2
+
+(This is a valid spot for the file to contain 00000000 then eof)
+
+02ee   ffff 0000 "NavLog" 02 i0*2 i24 
+0307   (i0 | i-40) (i691 | i987) (2-3 digit negative int) i1
+0317   i0
+0318   03 80 00*10
+0324   05 80 01 02 28 05 00 01 b"Log"
+0333   07 80 00 i2 0a 00 01 00*9 i1 00*2 01 01 (f3|f4) ff ff ff 00*4
+0357   00*8 90 01 00*9 (22|31)
+036b   32-bytes of null-padded font name, e.g. "Courier New".
+       Sometimes garbage after the first null.
+038b   (i80 | i132) 00*8 i1
+038f   00*8 i1
+039b   w"{\rtf ... SPSS syntax...}"
+
+Almost past SPSS syntax (only the } included):
+0000   '}' 00
+0002   ffff 0000 "NavHead" 02
+0010   00*24 i1 i0
+0030   03 80 00 00 00 00 00 00  00 00 00 00 05 80 01 02
+0040   28 05 00 01
+0044   b"Cluster|Crosstabs|..."
+
+Just past the string:
+0000   07 80 00 00 00 00 00 0f  00 01 00 00 00 00 00 00
+0010   00 00 00 01 00 00 00 00  00
+0019   b"Cluster|Crosstabs|..."
+
+Just past the string again:
+0000   00*3
+0003   ffff 0000 "NavTitle"
+0011   65 02 00 00 00 00 00 00  00 00 18 00 00 00 byte byte
+0020   ff ff 00 04 00 00 byte byte
+0028   ff ff 02 00 00 00 01 00  00 00 03 80 00*10
+003e   05 80  01 02 28 00 00 01 b"Title"
+004c   07 80 00 08
+0050   00 00 00 14 00 01 00*9 01
+0060   00*5
+0065   b"Cluster|Crosstabs|..."
+
+Just past the third string:
+0000   01 01 ed ff ff ff 00 00  00 00 00 00 00 00 00 00
+0010   00 00 bc 02 00 00 00 00  00 00 00 00 00 22
+001e   32-bytes of buggy zero-padded string "Arial"
+003e   50 00 00 00 00 00 00 00  00 00 00 00 01 00 00 00
+004e   w"{\rtf...}"
+
+Almost past the RTF (only } included):
+0000   '}' 00
+0002   ffff 0000 "NavNote"
+0010   02 00 00 00 00 00 00 00 00  18 00 00 00 (i0 | i-40)
+0020   int int i2 i1
+0030   03 80 00*10
+003c   05 80 01 02 28 05 00 01 b"Notes"
+004a   07 80 00 i7 19 00 01 00 00*2 01 00*5 01 00*5
+0063   b"Cluster|Crosstabs|..."
+
+Just past the fourth string:
+0000   01
+
+0001   ffff 0000 "PTPivotController"
+0018   02
+0019    byte*8    # weird ASCIIish garbage
+0021   i100*4
+
+0031   ffff 0000 "PVPivotView"
+0042   05 00 00 00 00
+
+0047   ffff 0000 "PMPivotModel"
+0059   03
+
+005a   ffff 0000 "NDimensional__DspCell"
+0075   00 01 00 00 00
+
+007a   ffff 0000 "IndexedCollection"
+0091   00 byte 00 00 00 01 00
+
+0098   ffff 0000 "DspCell"
+00a6   00
+00a7   03 80 00*10
+
+Fork: cell contains number ("as-number"):
+00b2   ffff 0000 "DspNumber"
+00c1    01 00 14 16       # 00 14 16 is DATETIME20.0
+00c5    80 02
+00c7    double            # seconds since 1582...
+00cf   b"16-APR-2007 15:20:00"    # or whatever
+00e4   27
+00e5   80 00 03 80 00 00 00  00 00 00 00 00 00 00 05
+00f4   80 01 02 28 05 00 01 01  20
+       # Some files have extra 00 00 here
+       # but we're only considering as-number-nozeros
+00fd   27 80 00
+0100   03 80 00*10
+010e   05 80 01 02 28 05 00 01
+0114   # Either b"filename" with a length < 255
+       # or ff "filename" with a length >= 255
+
+as-number-nozeros, after filename:
+0000   27 80 00
+0003   03 80 00*10 05
+0010   80 01 02 28 05 00 01 b"<none> or DataSet1 or whatever"
+
+as-number-nozeros, after dataset name:
+0000   27 80 00
+0003   03 80 00*10 05
+0010   80 01 02 28 05 00 01 b"<none>"
+
+# Some files have extra 00*8 here, we skip it:
+0000   27 80 00
+0003   03 80 00*5 (00|10) 00*4 05
+0010   80 01 02 28 05 00 01 b"<none> or DATASET..."
+
+as-number-nozeros-majority, after dataset name:
+0000   27 80 00 03 80 00*10 05 80 01 02 28 05 00 01 b"<none>"
+001e   27 80 00 03 80 00*10 2a 80 01 00 28 05 80 02 100.0 b"100"
+       # Followed by 6 optional zeros
+
+as-number-nozeros-majority, after optional zeros:
+0000    27 80 00 03 80 00*10 05 80 01 02 28 05 00 01 b"User-defined missing values..."
+                       ^ sixth 00 may be 10 instead
+
+as-number-nozeros-majority, after "User-defined missing values...":
+0000   27 80 00 03 80 00*10 (05|2a) 80 01 02 28 05 00 01 b"Statistics are based on..."
+
+as-number-nozeros-majority, after "Statistics are based on...":
+0000   00 00
+0002   27 80 00 03 80 00*10 05 80 01 02 28 05 00 01 b"syntax fragment"
+                       ^ sixth 00 may be 10 instead
+
+as-number-nozeros-majority, after syntax fragment:
+0000   27 80 00 03 80 00*10 2a 80 01 02 0d 15 80 02 double b"0:00:00.02"
+selecting for the ones that include Processor Time instead of RAM:
+002a   27 80 00 03 80 00*10 2a 80 01 02 0d 15 80 02 double b"0:00:00.03"
+0054   01 00 b"Notes" 00*4
+0060   b"Cluster_Notes"
+
+processor-time, after previous string:
+0000   00 01 00 00 00 01 00
+
+0007   ffff 0000 "PMPivotItemTree" 00
+001d   ffff 0000 "AbstractTreeBranch" 00
+0036   ffff 0000 "PMModelItemInfo"
+                00*9 byte*3 0e 00 03 80 00*10
+0065   05 80 01 02 28 05 00 01 b"Contents" i7
+       52 80 00
+       54 80 00 00*9 07 00 74 0e 00 03 80 00*10
+0099   05 80 01 02 28 05 00 01 b"Output Created" i0
+       52 80 00
+       54 80 00 01 00*7 08 00 74 0e 00 03 80 00*10
+00d3   05 80 01 02 28 05 00 01 b"Comments" i0
+00e8   52 80 00
+       54 80 00*9 09 00 74 0e 00 03 80 00*10
+       05 80 01 02 28 05 00 01 b"Input" i6
+       52 80 00
+       54 80 00 02 00*7 0a 00 74 0e 00 03 80 00*10
+0138   05 80 01 02 28 05 00 01 b"Data" i0
+       52 80 00
+       54 80 00 03 00*12 03 80 00*10
+       05 80 01 02 28 05 00 01 b"Active Dataset" i0
+0183   52 80 00
+       54 80 00 04 00*7 0e 00 74 0e 00 03 80 00*9
+01a2   05 80 01 02 28 05 00 01 b"Filter" i0
+01b5   52 80 00
+       54 80 00 05 00*7 11 00 74 0e 00 03 80 00*10
+01d4   05 80 01 02 28 05 00 01 b"Weight" i0
+01e7   52 80 00
+       54 80 00 06 00 00
+01f0   00 00 00 00 00 12 00 74  0e 00 03 80 00 00 00 00
+0200   00 00 00 00 00 00 05 80  01 02 28 05 00 01 b"Split File"
+0219   00 00 00 00 52 80 00
+0220   54 80 00 07 00 00 00 00  00 00 00 13 00 74 0e 00
+0230   03 80 00 00 00 00 00 00  00 00 00 00 05 80 01 02
+0240   28 05 00 01 b"N of Rows in Working Data File"
+0263   00 00 00 00 52  80 00 54 80 00 00 00 00
+0270   00 00 00 00 00 byte*2 74 0e 00 03 80 00 00 00 00
+0280   00 00 00 00 00 00 05 80  01 02 28 05 00 01
+028e   b"Missing Value Handling"
+02a5   02 00 00  00 52 80 00 54 80 00 08
+02b0   00 00 00 00 00 00 00 byte*2 74 0e 00 03 80 00 00
+02c0   00 00 00 00 00 00 00 00  05 80 01 02 28 05 00 01
+02d0   b"Definition of Missing"
+02e6   00 00  00 00 52 80 00 54 80 00
+02f0   09 00 00 00 00 00 00 00  byte*2 74 0e 00 03 80 00
+0300   00 00 00 00 00 00 00 00  00 05 80 01 02 28 05 00
+0310   01 b"Cases Used" 00*4
+0320   52 80 00 54 80 00 0a 00  00 00 00 00 00 00 17 00
+0330   74 0e 00 03 80 00 00 00  00 00 00 00 00 00 00 05
+0340   80 01 02 28 05 00 01 b"Weight Handling"
+0357   00 00 00 00 52 80 00 54 80
+0360   00 0b 00 00 00 00 00 00  00 18 00 74 0e 00 03 80
+0370   00 00 00 00 00 00 00 00  00 00 05 80 01 02 28 05
+0380   00 01 b"Syntax" 00 00 00 00 52 80 00
+0390   54 80 00 00 00 00 00 00  00 00 00 byte*2 74 0e 00
+03a0   03 80 00 00 00 00 00 00  00 00 00 00 05 80 01 02
+03b0   28 05 00 01 b"Resources" 02 00
+03c0   00 00 52 80 00 54 80 00  0c 00 00 00 00 00 00 00
+03d0   1b 00 74 0e 00 03 80 00  00 00 00 00 00 00 00 00
+03e0   00 05 80 01 02 28 05 00  01 b"Elapsed Time"
+03f6   00 00  00 00 52 80 00 54 80 00
+0400   0d 00 00 00 00 00 00 00  00 00 00 00 00 03 80 00
+0410   00 00 00 00 00 00 00 00  00 05 80 01 02 28 05 00
+0420   01 b"Processor Time"
+0430   00 00 00 00 00 00 00 00  00 00 0e 00 00 00 01 00
+0440   58 00 5c 00 64 00 68 00  6c 00 70 00 74 00 78 00
+0450   80 00 84 00 88 00 8c 00  94 00 98 00 00 0e 00 00
+0460   00 00 00 00 00 00 00 01  00 00 00 02 00 00 00 03
+0470   00 00 00 04 00 00 00 05  00 00 00 06 00 00 00 07
+0480   00 00 00 08 00 00 00 09  00 00 00 0a 00 00 00 0b
+0490   00 00 00 0c 00 00 00 0d  00 00 00 00 00 00 00 00
+04a0   01 00 00 11 00 00 00 00  00 04 00 00 00 e1 00 00
+04b0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
+04c0   00 00 00 00 01 00 00 00  00 00 00 00 00 00 00 00
+04d0   00 00 00 00 00 00 00 00  00 00 00 00 00 01 00 00
+04e0   00 01 00 ff ff 00 00 b"PVViewDimension"
+04f8   00 51 00 00 00 00 00 00
+0500   00 00 00 00 0e 00 00 00  00 00 69 00 00 00 54 00
+0510   00 00 4b 00 00 00 63 00  00 00 4b 00 00 00 4b 00
+0520   00 00 4b 00 00 00 7d 00  00 00 87 00 00 00 5c 00
+0530   00 00 72 00 00 00 4b 00  00 00 63 00 00 00 6f 00
+0540   00 00 00 03 00 00 00 00  00 00 00 00 00 00 00 00
+0550   00 87 00 00 00 00 03 80  00 00 00 00 00 00 00 00
+0560   00 00 05 80 01 02 28 05  00 01 b"Notes"
+0570   00 03 80 00 00 00 00 00  00 00 00 00 00 05 80 01
+0580   02 28 00 00 01 00 00 00  00 00 00 00 00 00 03 80
+0590   00 00 00 00 00 00 00 00  00 00 05 80 01 02 28 00
+05a0   00 01 00
+
+05a3   ffff 0000 "PTTableLook"
+05b4   02 02 00 00  00 00 00 00 00 36 00 00
+05c0   00 12 00 00 00
+
+05c5   ffff 0000 "PVSeparatorStyle"
+05db   00 01 00 00 00
+05e0   00 00 00 00 00 00 00 00  00 00 00 00 a6 80 00 01  |................|
+05f0   00 00 00 00 00 00 00 00  00 01 00 00 00 00 00 00  |................|
+0600   00 00 00 01 00 00 00 00  00 00 00 00 00 01 00 00  |................|
+0610   00 00 00 00 00 00 00
+
+0607   ffff 0000 "PVCellStyle"
+0628   00 01 00 00 00 00 00 ff
+
+0630   ffff 0000 00
+
+0637   ffff 0000 "PVTextStyle"
+0646   00 02 00 02 00 c8 00 78 00 a0
+0650   00 14 00 78 00 00 00 01  00 f3 ff ff ff 00 00 00
+0660   00 00 00 00 00 00 00 00  00 bc 02 00 00 00 00 00
+0670   00 00 00 00 22 b"Arial" 00 00 00 00 00
+0680   00 a9 80 00 01 00 00 00  00 00 ff ff ff 00 00 00
+0690   ab 80 00 01 00 00 00 c8  00 78 00 a0 00 14 00 14
+06a0   00 00 00 01 00 f3 ff ff  ff 00 00 00 00 00 00 00
+06b0   00 00 00 00 00 90 01 00  00 00 00 00 00 00 00 00
+06c0   22 b"Arial" 00  00 00 00 00 00 a9 80 00
+06d0   01 00 00 00 00 00 ff ff  ff 00 00 00 ab 80 00 01
+06e0   00 00 00 c8 00 78 00 a0  00 14 00 14 00 00 00 01
+06f0   00 f3 ff ff ff 00 00 00  00 00 00 00 00 00 00 00
+0700   00 90 01 00 00 00 00 00  00 00 00 00 22 b"Arial"
+0713   00 00 00 00 00  00 a9 80 00 01 00 00 00
+0720   00 00 ff ff ff 00 00 00  ab 80 00 00 00 00 00 c8
+0730   00 78 00 a0 00 14 00 14  00 00 00 01 00 f3 ff ff
+0740   ff 00 00 00 00 00 00 00  00 00 00 00 00 90 01 00
+0750   00 00 00 00 00 00 00 00  22 b"Arial" 00
+0760   00 00 00 00 00 a9 80 00  01 00 00 00 00 00 ff ff
+0770   ff 00 00 00 ab 80 00 01  00 02 00 c8 00 78 00 a0
+0780   00 14 00 14 00 00 00 01  00 f3 ff ff ff 00 00 00
+0790   00 00 00 00 00 00 00 00  00 90 01 00 00 00 00 00
+07a0   00 00 00 00 22 b"Arial" 00 00 00 00 00
+07b0   00 a9 80 00 01 00 00 00  00 00 ff ff ff 00 00 00
+07c0   ab 80 00 02 00 03 00 c8  00 78 00 a0 00 14 00 14
+07d0   00 00 00 01 00 f3 ff ff  ff 00 00 00 00 00 00 00
+07e0   00 00 00 00 00 90 01 00  00 00 00 00 00 00 00 00
+07f0   22 b"Arial" 00  00 00 00 00 00 a9 80 00
+0800   01 00 00 00 00 00 ff ff  ff 00 00 00 ab 80 00 01
+0810   00 00 00 c8 00 78 00 a0  00 14 00 14 00 00 00 01
+0820   00 f3 ff ff ff 00 00 00  00 00 00 00 00 00 00 00
+0830   00 90 01 00 00 00 00 00  00 00 00 00 22 b"Arial"
+0843   00 00 00 00 00  00 a9 80 00 01 00 00 00
+0850   00 00 ff ff ff 00 00 00  ab 80 00 01 00 00 00 c8
+0860   00 68 01 68 01 28 00 28  00 00 00 01 00 f3 ff ff
+0870   ff 00 00 00 00 00 00 00  00 00 00 00 00 90 01 00
+0880   00 00 00 00 00 00 00 00  22 b"Arial" 00
+0890   00 00 00 00 00 00 00 01  00 00 00 00 00 00 00 01
+08a0   00 01 00 00 00 00 00 00  00 01 00 01 00 00 00 00
+08b0   00 00 00 01 00 01 00 00  00 00 00 00 00 01 00 00
+08c0   00 00 00 00 00 00 00 01  00 00 00 00 00 00 00 01
+08d0   00 01 00 00 00 00 00 00  00 01 00 b"(cont.)"
+08e3   24 00 00 00 48  00 00 00 24 00 00 00 78
+08f0   00 00 00 00 00 00 00 00  00 00 00
+
+08fb   ffff 0000 "PVPrintManager" 02
+0910   1e 00 02 00 00 00 03 00  00 00 01 00 00 00 00 00
+0920   00 00 00 00 00 00 00
+
+0927   ffff 0000 "NavText"
+0934   02 00 00 00  00 00 00 00 00 18 00 00
+0940   00 byte*2 ff ff byte*2 00  00 byte*2 ff ff 02 00 00
+0950   00 01 00 00 00 03 80 00  00 00 00 00 00 00 00 00
+0960   00 05 80 01 02 28 05 00  01 b"Active Dataset"
+0978   07 80 00 10 00 00 00 be
+0980   00 01 00 00 00 00 00 00  00 00 00 01 00 00 00 00
+0990   00 b"Cluster etc." 07 43 6c 75 73 74 65  72 01 01 f3 ff ff ff 00
+0990   00 0b 46 72 65 71 75 65  6e 63 69 65 73 01 01 f3
+
+Just past "Cluster":
+0000   01 01 (f3|f4) ff ff ff 00 00  00 00 00 00 00 00 00 00
+0010   00 00 90 01 00 00 00 00  00 00 00 00 00 (22|31)
+001e   32-bytes of null-padded font name, e.g. "Courier New".
+       Sometimes garbage after the first null.
+003e   byte 00*11 w"{\rtf... giving filesystem path}"
+
+Almost past RTF (just trailing }):
+0000   '}' ffff 0000 "NavPivot" 02 int int i24 int
+0020   int int i2 i1
+0030   03 80 00*10 05 80 01 02
+0040   28 05 00 01 b"Statistics etc."
+
+Just past "Statistics":
+0000   07 80 00 0c 00 00 00 c3  00 01 00 00 00 00 00 00
+0010   00 00 00 01 00 00 00 00  00 b"Cluster etc."
+
+Just past "Cluster", using boot/Contents as exemplary:
+0000   01
+0001   1d 80 02 i0*2 i100*4        # 1d 80 corresponds to "PTPivotController"
+001c   1f 80 05 00*4               # 1f 80 corresponds to "PTPivotView"
+0023   21 80 03                    # 21 80 corresponds to "PMPivotModel"
+0026   23 80 00 (02|03) 00 00 00   # 23 80 corresponds to "NDimensional__DspCell"
+002d   25 80 00 i100 01 00         # 25 80 corresponds to "IndexedCollection"
+0036   25 80 00 i100 01 00
+003f   27 80 00 03 80 00*10 2a 80 01 F40.0 80 02 100.0 b"100"
+0062   27 80 00 03 80 00*10 2a 80 01 F40.0 80 02 100.0 b"100"
+        ...repeats...
+0f03   25 80 00 6c 00 00 00 01 00
+0f0c    27 80 00 03 80 00*10 2a 80 01 F40.0 80 02 0.0 b"0"
+       ...repeats...
+1cf8   25 80 00 6c 00 00 00 01 00
+1d01   27 80 00 03 80 00*10 2a 80 01 F40.8 1
+
+Notes
+Output Created         16-JAN-2008 02:45:55
+Comments                
+Input  Data    \\Ugly\amead\consulting\center\Dell\Exams\RE\analyses\Blended15\pilotdata-2008-01-15.csv
+       Active Dataset  raw1
+       Filter  <none>
+       Weight  <none>
+       Split File      <none>
+       N of Rows in Working Data File  366
+Missing Value Handling Definition of Missing   User-defined missing values are treated as missing.
+       Cases Used      Statistics are based on all cases with valid data.
+Syntax         FREQUENCIES\r  VARIABLES=exam_id exam_name restarts\r  /ORDER=  ANALYSIS .\r
+Resources      Elapsed Time    0:00:00.02
+       Processor Time  0:00:00.03