home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / makedoc.zip / makedoc7.cpp next >
C/C++ Source or Header  |  1998-08-07  |  17KB  |  750 lines

  1. /**********************************************************************
  2.  * Rick,
  3.  *
  4.  * I made some more changes to fix another portability problem.  It seems
  5.  * that SOME compilers will pad a structure to a DWORD boundary when you
  6.  * use the sizeof operator.  In particular, for the Solaris compiler, the
  7.  * 78 byte tDocHeader structure is reported as having 80 bytes.  This shifts
  8.  * EVERYTHING by two bytes and wreaks havoc in the generated .prc file.
  9.  * I fixed this (look at the comments in struct tDocHeader and the DOCHEADSZ
  10.  * definition) in the two places it occurred.
  11.  *
  12.  * I also fixed a spelling error in an error message.
  13.  *
  14.  * I also changed the usage message to say this is version 0.7a (rather than
  15.  * 0.6).
  16.  *
  17.  * I also changed the return type of main() to be int and added various
  18.  * calls to exit() as needed.  Needed for portability and correctness.
  19.  *
  20.  * -- Harold Bamford
  21.  **********************************************************************/
  22. // MakeDoc
  23. // version 0.7a
  24. //
  25. // Compresses text files into a format that is ready to export to a Pilot
  26. // and work with Rick Bram's PilotDOC reader.
  27. //
  28. // Freeware
  29. //
  30. // ver 0.6   enforce 31 char limit on database names
  31. // ver 0.7   change header and record0 to structs
  32. // ver 0.7a  minor mispellings and portability issues
  33.  
  34. #ifdef sparc
  35. #    ifndef UNIX
  36. #    define UNIX 1
  37. #    endif
  38. #endif
  39.  
  40. #include <stdio.h>
  41. #include <stdlib.h>
  42. #include <string.h>
  43.  
  44. //template<class A> A max(const A& a, const A& b) {return (a<b) ? b : a;}
  45. #define max(a,b) ((a>b) ? a : b)
  46.  
  47. typedef unsigned char byte;
  48. typedef unsigned long DWORD;
  49. typedef unsigned short WORD;
  50. #define DISP_BITS 11
  51. #define COUNT_BITS 3
  52.  
  53. // all numbers in these structs are big-endian, MAC format
  54. struct tDocHeader {
  55.     char sName[32];        // 32 bytes
  56.     DWORD dwUnknown1;    // 36
  57.     DWORD dwTime1;        // 40
  58.     DWORD dwTime2;        // 44
  59.     DWORD dwTime3;        // 48
  60.     DWORD dwLastSync;    // 52
  61.     DWORD ofsSort;        // 56
  62.     DWORD ofsCatagories;    // 60
  63.     DWORD dwCreator;    // 64
  64.     DWORD dwType;        // 68
  65.     DWORD dwUnknown2;    // 72
  66.     DWORD dwUnknown3;    // 76
  67.     WORD  wNumRecs;        // 78
  68. };
  69.  
  70. // Some compilers pad structures out to DWORD boundaries so using sizeof()
  71. // doesn't give the intended result.
  72. #define DOCHEADSZ 78
  73.  
  74. struct tDocRecord0 {
  75.     WORD wVersion;        // 1=plain text, 2=compressed
  76.     WORD wSpare;
  77.     DWORD dwStoryLen;    // in bytes, when decompressed
  78.     WORD wNumRecs;         // text records only; equals tDocHeader.wNumRecs-1
  79.     WORD wRecSize;        // usually 0x1000
  80.     DWORD dwSpare2;
  81. };
  82.  
  83. ////////////// utilities //////////////////////////////////////
  84.  
  85. WORD SwapWord21(WORD r)
  86. {
  87.     return (r>>8) + (r<<8);
  88. }
  89. WORD SwapWord12(WORD r)
  90. {
  91.     return r;  
  92. }
  93. DWORD SwapLong4321(DWORD r)
  94. {
  95.     return  ((r>>24) & 0xFF) + (r<<24) + ((r>>8) & 0xFF00) + ((r<<8) & 0xFF0000);
  96. }
  97. DWORD SwapLong1234(DWORD r)
  98. {
  99.     return r;
  100. }
  101.  
  102. WORD (*SwapWord)(WORD r) = NULL;
  103. DWORD (*SwapLong)(DWORD r) = NULL;
  104.  
  105. // copy bytes into a word and double word and see how they fall,
  106. // then choose the appropriate swappers to make things come out
  107. // in the right order.
  108. int SwapChoose()
  109. {
  110.   union { char b[2]; WORD w; } w;
  111.   union { char b[4]; DWORD d; } d;
  112.  
  113.   strncpy(w.b, "\1\2", 2);
  114.   strncpy(d.b, "\1\2\3\4", 4);
  115.  
  116.   if (w.w == 0x0201)
  117.     SwapWord = SwapWord21;
  118.   else if (w.w == 0x0102)
  119.     SwapWord = SwapWord12;
  120.   else
  121.     return 0;
  122.  
  123.   if (d.d == 0x04030201)
  124.     SwapLong = SwapLong4321;
  125.   else if (d.d == 0x01020304)
  126.     SwapLong = SwapLong1234;
  127.   else
  128.     return 0;
  129.   
  130.   return 1;
  131. }  
  132.  
  133. // replacement for strstr() which deals with 0's in the data
  134. byte* memfind(byte* t, int t_len, byte* m, int m_len)
  135. {
  136.     int i;
  137.  
  138.     for (i = t_len - m_len + 1 ; i>0; i--, t++)
  139.         if (t[0]==m[0] && memcmp(t,m,m_len)==0)
  140.             return t;
  141.     return 0;
  142. }
  143.  
  144.  
  145. /////////////////////////////////////////////////////////////////////////////
  146. /////////////////////////////////////////////////////////////////////////////
  147. /////////////////////                                  //////////////////////
  148. /////////////////////      tBuf class                  //////////////////////
  149. /////////////////////                                  //////////////////////
  150. /////////////////////////////////////////////////////////////////////////////
  151. /////////////////////////////////////////////////////////////////////////////
  152.  
  153.  
  154. struct tBuf {
  155.     byte* buf;
  156.     unsigned len;
  157.  
  158.     tBuf() {buf = new byte[len = 6000];};
  159.     ~tBuf() {    if (buf) delete[] buf; }
  160.     unsigned Len() const {    return len;    }
  161.  
  162.     unsigned RemoveBinary();
  163.     unsigned Decompress();
  164.     unsigned Compress();
  165.     unsigned Issue(byte src, int& bSpace);
  166.     unsigned DuplicateCR();
  167.     void Clear() {delete[] buf; buf = new byte[len = 6000]; }
  168.     void Dump() {printf("\nbuffer len=%d",len);}
  169. };
  170.  
  171.  
  172.  
  173. //
  174. // Issue()
  175. //
  176. // action: handle the details of writing a single
  177. //        character to the compressed stream
  178. //
  179. unsigned
  180. tBuf::Issue(byte src, int& bSpace)
  181. {
  182.     int iDest = len;
  183.     byte* dest = buf;
  184.  
  185.     // if there is an outstanding space char, see if
  186.     // we can squeeze it in with an ASCII char
  187.     if (bSpace)
  188.     {
  189.         if (src>=0x40 && src<=0x7F)
  190.             dest[iDest++] = src ^ 0x80;
  191.         else
  192.         {
  193.             // couldn't squeeze it in, so issue the space char by itself
  194.             // most chars go out simple, except the range 1...8,0x80...0xFF
  195.             dest[iDest++] = ' ';
  196.             if (src<0x80 && (src==0 || src>8) )
  197.                 dest[iDest++] = src;
  198.             else
  199.                 dest[iDest++] = 1, dest[iDest++] = src;
  200.         }
  201.         // knock down the space flag
  202.         bSpace = 0;
  203.     }
  204.     else
  205.     {
  206.         // check for a space char
  207.         if (src==' ')
  208.             bSpace = 1;
  209.         else
  210.         {
  211.             if (src<0x80 && (src==0 || src>8))
  212.                 dest[iDest++] = src;
  213.             else
  214.                 dest[iDest++] = 1, dest[iDest++] = src;
  215.  
  216.         }
  217.     }
  218.     len = iDest;
  219.     return iDest;
  220. }
  221.  
  222. //
  223. // Compress
  224. //
  225. // params:     none
  226. //
  227. // action:    takes the given buffer,
  228. //                    and compresses
  229. //                    the original data down into a second buffer
  230. //
  231. // comment:    This version make heavy use of walking pointers.
  232. //
  233. unsigned tBuf::Compress()
  234. {
  235.     int i,j;
  236.     int bSpace = 0;
  237.  
  238.     // run through the input buffer
  239.     byte* pBuffer;        // points to the input buffer
  240.     byte* pHit;        // points to a walking test hit; works upwards on successive matches
  241.     byte* pPrevHit;        // previous value of pHit
  242.     byte* pTestHead;    // current test string
  243.     byte* pTestTail;    // current walking pointer; one past the current test buffer
  244.     byte* pEnd;        // 1 past the end of the input buffer
  245.  
  246.     pHit = pPrevHit = pTestHead = pBuffer = buf;
  247.     pTestTail = pTestHead+1;
  248.     pEnd = buf + len;
  249. //printf("pointers %x %x",pTestTail, pEnd);
  250. //printf("\nstart compression buf len=%d",len);
  251.  
  252.     // make a dest buffer and reassign the local buffer
  253.     buf = new byte[6000];
  254.     len = 0;        // used to walk through the output buffer
  255.  
  256.     // loop, absorbing one more char from the input buffer on each pass
  257.     for (; pTestHead != pEnd; pTestTail++)
  258.     {
  259. //printf("\npointers pTestHead %x pTestTail %x pTestHead[]=%x %x",pTestHead, pTestTail, pTestHead[0], pTestHead[1]);
  260.         // establish where the scan can begin
  261.         if (pTestHead - pPrevHit > ((1<<DISP_BITS)-1))
  262.             pPrevHit = pTestHead - ((1<<DISP_BITS)-1);
  263.  
  264.         // scan in the previous data for a match
  265.         pHit = memfind(pPrevHit, pTestTail - pPrevHit, pTestHead, pTestTail - pTestHead);
  266.  
  267.         if (pHit==0)
  268.             printf("!! bug source %x%x%x, dest %x%x%x, %d bytes",    pPrevHit[0],
  269.                 pPrevHit[1],pPrevHit[2],pTestHead[0],
  270.     pTestHead[1],    pTestHead[2],    pTestTail-pTestHead);
  271.  
  272.         // on a mismatch or end of buffer, issued codes
  273.         if (pHit==0
  274.             || pHit==pTestHead
  275.             || pTestTail-pTestHead>(1<<COUNT_BITS)+2
  276.             || pTestTail==pEnd)
  277.         {
  278.             // issued the codes
  279.             // first, check for short runs
  280.             if (pTestTail-pTestHead < 4)
  281.             {
  282. //printf("\nissue a char %x",pTestHead[0]);
  283.                 Issue(pTestHead[0], bSpace);
  284.                 pTestHead++;
  285.             }
  286.             // for longer runs, issue a run-code
  287.             else
  288.             {
  289.                 // issue space char if required
  290.                 if (bSpace) buf[len++] = ' ', bSpace = 0;
  291.  
  292.                 unsigned int dist = pTestHead - pPrevHit;
  293.                 unsigned int compound = (dist << COUNT_BITS) + pTestTail-pTestHead - 4;
  294.  
  295. if (dist>=(1<<DISP_BITS)) printf("\n!! error dist overflow");
  296. if (pTestTail-pTestHead-4>7) printf("\n!! error dist overflow");
  297.  
  298.                 buf[len++] = 0x80 + (compound>>8);
  299.                 buf[len++] = compound & 0xFF;
  300. //printf("\nissuing code for sequence len %d <%c%c%c>",pTestTail-pTestHead-1,pTestHead[0],pTestHead[1],pTestHead[2]);
  301. //printf("\n          <%x%x>",pOut[-2],pOut[-1]);
  302.                 // and start again
  303.                 pTestHead = pTestTail-1;
  304.             }
  305.             // start the search again
  306.             pPrevHit = pBuffer;
  307.         }
  308.         // got a match
  309.         else
  310.         {
  311.             pPrevHit = pHit;
  312.         }
  313. //printf("pointers %x %x %x",pTestHead, pTestTail, pPrevHit);
  314.         // when we get to the end of the buffer, don't inc past the end
  315.         // this forces the residue chars out one at a time
  316.         if (pTestTail==pEnd) pTestTail--;
  317.     }
  318.  
  319.     // clean up any dangling spaces
  320.     if (bSpace) buf[len++] = ' ';
  321.  
  322.  
  323.     // final scan to merge consecutive high chars together
  324.     int k;
  325.     for (i=k=0; i<len; i++,k++)
  326.     {
  327.         buf[k] = buf[i];
  328.         // skip the run-length codes
  329.         if (buf[k]>=0x80 && buf[k]<0xC0)
  330.             buf[++k] = buf[++i];
  331.         // if we hit a high char marker, look ahead for another
  332.         else if (buf[k]==1)
  333.         {
  334.             buf[k+1] = buf[i+1];
  335.             while (i+2<len && buf[i+2]==1 && buf[k]<8)
  336.             {
  337.                 buf[k]++;
  338.                 buf[k+buf[k]] = buf[i+3];
  339.                 i+=2;
  340.             }
  341.             k += buf[k]; i++;
  342.         }
  343.     }
  344.  
  345.     // delete original buffer
  346.     delete[] pBuffer;
  347.     len = k;
  348.  
  349.     return k;
  350. }
  351. /*
  352.     Decompress
  353.  
  354.     params:    none
  355.  
  356.     action: make a new buffer
  357.                     run through the source data
  358.                     check the 4 cases:
  359.                         0,9...7F represent self
  360.                         1...8        escape n chars
  361.                         80...bf reference earlier run
  362.                         c0...ff    space+ASCII
  363.  
  364. */
  365. unsigned
  366. tBuf::Decompress()
  367. {
  368.     // we "know" that all decompresses fit within 4096, right?
  369.     byte* pOut = new byte[6000];
  370.     byte* in_buf = buf;
  371.     byte* out_buf = pOut;
  372.  
  373.     int i,j;
  374.     for (j=i=0; j<len; )
  375.     {
  376.         unsigned int c;
  377.  
  378.         // take a char from the input buffer
  379.         c = in_buf[j++];
  380.  
  381.         // separate the char into zones: 0, 1...8, 9...0x7F, 0x80...0xBF, 0xC0...0xFF
  382.  
  383.         // codes 1...8 mean copy that many bytes; for accented chars & binary
  384.         if (c>0 && c<9)
  385.             while(c--) out_buf[i++] = in_buf[j++];
  386.  
  387.         // codes 0, 9...0x7F represent themselves
  388.         else if (c<0x80)
  389.             out_buf[i++] = c;
  390.  
  391.         // codes 0xC0...0xFF represent "space + ascii char"
  392.         else if (c>=0xC0)
  393.                 out_buf[i++] = ' ', out_buf[i++] = c ^ 0x80;
  394.  
  395.         // codes 0x80...0xBf represent sequences
  396.         else
  397.         {
  398.             int m,n;
  399.             c <<= 8;
  400.             c += in_buf[j++];
  401.             m = (c & 0x3FFF) >> COUNT_BITS;
  402.             n = c & ((1<<COUNT_BITS) - 1);
  403.             n += 3;
  404.             while (n--)
  405.             {
  406.                 out_buf[i] = out_buf[i-m];
  407.                 i++;
  408.             }
  409.         }
  410.     }
  411.     delete[] buf;
  412.     buf = pOut;
  413.     len = i;
  414.  
  415.     return i;
  416. }
  417.  
  418. unsigned tBuf::DuplicateCR()
  419. {
  420.     byte* pBuf = new byte[2*len];
  421.  
  422.     int k,j;
  423.     for (j=k=0; j<len; j++, k++)
  424.     {
  425.         pBuf[k] = buf[j];
  426.         if (pBuf[k]==0x0A) pBuf[k++] = 0x0D, pBuf[k] = 0x0A;
  427.     }
  428.     delete[] buf;
  429.     buf = pBuf;
  430.     len = k;
  431.     return k;
  432. }
  433.  
  434.  
  435. void Decomp(char* src, char* dest, int bBinary)
  436. {
  437.     FILE* fin;
  438.     FILE* fout;
  439.     fin = fopen(src,"rb");
  440.  
  441.     if (fin==0)
  442.     {
  443.         printf("problem opening source file %s", src);
  444.         exit(2);
  445.     }
  446.  
  447.     // just holds the first few bytes of the file
  448.     byte buf[0x100];
  449.     tDocHeader head;
  450.  
  451.     fread(&head, 1, DOCHEADSZ, fin);
  452.     if (strncmp((char *)&head.dwType, "REAd", 4) != 0
  453.         || strncmp((char *)&head.dwCreator, "TEXt", 4) != 0)
  454.     {
  455.       //printf("file contains %.4s, %.4s", (char *)&head.dwCreator, (char *)&head.dwType);
  456.         printf(".prc file is not the correct format");
  457.         exit(3);
  458.     }
  459.  
  460.     WORD bCompressed;
  461.     DWORD dwPos;
  462.     tDocRecord0 rec0;
  463.     // point to start of index
  464.     fseek(fin, 0x4E, SEEK_SET);
  465.     // read the location of the first record
  466.     fread(&dwPos, 4, 1, fin);
  467.     dwPos = SwapLong(dwPos);
  468.     fseek(fin, dwPos, SEEK_SET);
  469.     fread(&rec0, sizeof(rec0), 1, fin);
  470.     bCompressed = SwapWord(rec0.wVersion);
  471.     if (bCompressed!=1 && bCompressed!=2)
  472.         printf("\nWARNING: unknown file compression type:%d",bCompressed);
  473.     bCompressed--;
  474.  
  475.     fout = fopen(dest,"wb");
  476.     if (fout==0)
  477.     {
  478.         printf("problem opening output file %s",dest);
  479.         exit(2);
  480.     }
  481.  
  482.  
  483.     DWORD dwLen;
  484.     fseek(fin,0,SEEK_END);
  485.     dwLen = ftell(fin);
  486.  
  487.     WORD nRecs;
  488.     nRecs = SwapWord(head.wNumRecs) - 1;
  489.  
  490.     // this is the main record buffer
  491.     // it knows how to stretch to accomodate the decompress
  492.     tBuf t;
  493.  
  494.     DWORD dwRecLen;
  495.     for (int i=0; i<nRecs; i++)
  496.     {
  497.         // read the record offset
  498.         fseek(fin, 0x56 + 8*i, SEEK_SET);
  499.         fread(&dwPos, 4, 1, fin);
  500.         dwPos = SwapLong(dwPos);
  501.  
  502.         // read start of next record
  503.         fseek(fin, 0x5E + 8*i, SEEK_SET);
  504.         fread(&dwRecLen, 4, 1, fin);
  505.         dwRecLen = SwapLong(dwRecLen);
  506.  
  507.         // for the last, use the file len
  508.         if (i==nRecs-1) dwRecLen = dwLen;
  509.  
  510.         dwRecLen -= dwPos;
  511.  
  512.         fseek(fin,dwPos,SEEK_SET);
  513.         int n = fread(t.buf, 1, dwRecLen, fin);
  514.         t.len = n;
  515.         if(bCompressed)
  516.             t.Decompress();
  517.  
  518.         // check for CR insert
  519.         if (!bBinary)
  520.             t.DuplicateCR();
  521.         printf("\rreconverting %s: record %d of %d",head.sName,i,nRecs);
  522.  
  523.         fwrite(t.buf, 1, t.Len(), fout);
  524.     }
  525.  
  526.     fclose(fin);
  527.     fclose(fout);
  528.  
  529. }
  530.  
  531. // this nasty little beast removes really low ASCII and 0's
  532. // and handles the CR problem
  533. //
  534. // if a cr appears before a lf, then remove the cr
  535. // if a cr appears in isolation, change to a lf
  536. unsigned tBuf::RemoveBinary()
  537. {
  538.     byte* in_buf = buf;
  539.     byte* out_buf = new byte[len];
  540.  
  541.     int k,j;
  542.     for (j=k=0; j<len; j++,k++)
  543.     {
  544.         // copy each byte
  545.         out_buf[k] = in_buf[j];
  546.  
  547.         // throw away really low ASCII
  548.         if ((out_buf[k]>=0 && out_buf[k]<9)) k--;
  549.  
  550.         // for CR
  551.         if (out_buf[k]==0x0D)
  552.         {
  553.             // if next is LF, then drop it
  554.             if (j<len-1 && in_buf[j+1]==0x0A)
  555.                 k--;
  556.             else // turn it into a LF
  557.                 out_buf[k] = 0x0A;
  558.         }
  559.     }
  560.     delete[] buf;
  561.     buf = out_buf;
  562.     len = k;
  563.     return k;
  564. }
  565.  
  566. void out_word(short w, FILE* fout)
  567. {
  568.     short m = SwapWord(w);
  569.     fwrite(&m,2,1,fout);
  570. }
  571. void out_long(long d, FILE* fout)
  572. {
  573.     long d1 = SwapLong(d);
  574.     fwrite(&d1,4,1,fout);
  575. }
  576.  
  577.  
  578. int
  579. main(int argc, char** argv)
  580. {
  581.     printf("MakeDoc ver 0.7a\n");
  582.     if (argc<4)
  583.     {
  584.         printf("\nsyntax makedoc [-n] [-b] <text-file> <prc-file> <story-name>");
  585.         printf("\n                 convert text files to .PRC format");
  586.         printf("\n       makedoc -d [-b] <prc-file> <text-file>");
  587.         printf("\n                 decodes the PRC back into the txt file");
  588.         printf("\n       -n builds the .prc file without compression");
  589.         printf("\n       -b option compresses/decompresses binary");
  590. #if UNIX
  591.         printf("\n");
  592. #endif
  593.         exit(1);
  594.     }
  595.  
  596.     int iArg = 1;
  597.     int bDecomp = 0;
  598.     int bBinary = 0;
  599.     int bReport = 0;
  600.     int bCompress = 1;
  601.  
  602.     if ( ! SwapChoose()) {
  603.       printf("\nfailed to select proper byte swapping algorithm");
  604. #if UNIX
  605.       printf("\n");
  606. #endif
  607.       exit(1);
  608.     }
  609.  
  610.     while (argv[iArg][0]=='-' || argv[iArg][0]=='\\')
  611.     {
  612.         if (argv[iArg][1]=='d')
  613.             bDecomp = 1;
  614.         if (argv[iArg][1]=='b')
  615.             bBinary = 1;
  616.         if (argv[iArg][1]=='r')
  617.             bReport = 1;
  618.         if (argv[iArg][1]=='n')
  619.             bCompress = 0;
  620.         iArg++;
  621.     }
  622.  
  623.     if (bDecomp)
  624.         Decomp(argv[iArg], argv[iArg+1], bBinary);
  625.  
  626.     else
  627.     {
  628.         FILE* fin;
  629.         FILE* fout;
  630.         tDocHeader head1;
  631.  
  632.         fin = fopen(argv[iArg],"rb");
  633.         fout = fopen(argv[iArg+1],"wb");
  634.         if (fin==0 || fout==0)
  635.         {
  636.             printf("problem opening files");
  637.             exit(2);
  638.         }
  639.  
  640.         fseek(fin,0,SEEK_END);
  641.         DWORD storySize = ftell(fin);
  642.         fseek(fin,0,SEEK_SET);
  643.  
  644.         DWORD    x;
  645.         WORD w;
  646.         long    recSize = 4096;
  647.         DWORD        z,numRecs;
  648.  
  649.         sprintf(head1.sName,"%.31s",argv[iArg+2]);
  650.         head1.sName[31] = 0;
  651.         printf("saving to %s as <%s>,%s%s compressed",argv[iArg+1],argv[iArg+2],
  652.                 bBinary ? " binary mode," : "",
  653.                 bCompress ? "" : " not");
  654.  
  655.             /*LocalWrite just writes to the new file the number of bytes starting at the passed pointer*/
  656.  
  657.         head1.dwUnknown1 = 0;
  658.         strncpy((char *)&head1.dwTime1, "\x06\xD1\x44\xAE", 4);
  659.         strncpy((char *)&head1.dwTime2, "\x06\xD1\x44\xAE", 4);
  660.         head1.dwTime3 = 0;
  661.         head1.dwLastSync = 0;
  662.         head1.ofsSort = 0;
  663.         head1.ofsCatagories = 0;
  664.         strncpy((char *)&head1.dwCreator, "TEXt", 4);    // database creator
  665.             strncpy((char *)&head1.dwType, "REAd", 4);    // database type
  666.         head1.dwUnknown2 = 0;
  667.         head1.dwUnknown3 = 0;
  668.  
  669.  
  670.  
  671.         z = (int) (storySize/(long) recSize);
  672.         if (((long) z * recSize) < storySize)
  673.             z ++;
  674.  
  675.         numRecs = z;
  676.         z ++;
  677.  
  678.         head1.wNumRecs = SwapWord(z);        //  the number of records to follow
  679.         fwrite(&head1,1,DOCHEADSZ,fout);
  680.         unsigned long index;
  681.         index = 0x406F8000;        // the pattern for attributes=dirty + unique_id=0x6f8000
  682.         x = 0x50L + (long) z * 8;
  683.  
  684.         out_long(x,fout);        // start writing the record offsets
  685.         out_long(index,fout);
  686.         x += 0x0010L;
  687.  
  688.         index++;
  689.         z--;
  690.  
  691.         while(z--) {
  692.             out_long(x,fout);        //more record offsets
  693.             out_long(index++,fout);        // the attributes + ID's
  694.             x += 0x1000L;
  695.         }
  696.         // one more word.....
  697.         out_word(0,fout);
  698.  
  699.  
  700.         tDocRecord0 rec0;
  701.         rec0.wVersion = SwapWord(bCompress ? 2 : 1);
  702.         rec0.wSpare = 0;
  703.         rec0.dwStoryLen = SwapLong(storySize);
  704.         rec0.wNumRecs = SwapWord(SwapWord(head1.wNumRecs) - 1);
  705.         rec0.wRecSize = SwapWord(recSize);
  706.         rec0.dwSpare2 = 0;
  707.  
  708.         fwrite(&rec0,1,sizeof(rec0),fout);
  709.  
  710.         int n = recSize;
  711.         // dump the whole story into the new file
  712.         int recNum = 0;
  713.         printf("\n");
  714.  
  715.         tBuf buf;
  716.  
  717.         while(recNum < numRecs)
  718.             {
  719.             long pos;
  720.             pos = ftell(fout);
  721.             fseek(fout, 0x56 + 8*recNum, SEEK_SET);
  722.             if (recNum!=numRecs) out_long(pos,fout);
  723.             fseek(fout, pos, SEEK_SET);
  724.  
  725.             int nOrg;
  726.  
  727.             buf.Clear();
  728.             nOrg = n = fread(buf.buf,1,4096,fin);
  729.             buf.len = n;
  730.             if (n==0) break;
  731.  
  732.             if (!bBinary)
  733.                 buf.RemoveBinary();
  734.             if (bCompress)
  735.                 buf.Compress();
  736.             n = fwrite(buf.buf,1,buf.Len(),fout);
  737.  
  738.             printf("\rconverting record %d of %d",recNum+1,numRecs);
  739.             if (bReport && n && bCompress)
  740.                 printf("\noriginal %d bytes, compressed to %d bytes, ratio: %f5.1\n",
  741.                     nOrg, n, 100. * n / nOrg);
  742.             recNum++;
  743.             }
  744.  
  745.         fclose(fin);
  746.         fclose(fout);
  747.     }
  748.     exit(0);
  749. }
  750.