home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / PASCAL / STREAM15.ZIP / STREAMS.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1993-03-28  |  93.8 KB  |  3,275 lines

  1. unit Streams;
  2. { Unit to provide enhancements to TV Objects unit streams in the form
  3.   of several filters, i.e. stream clients, and other streams. }
  4.  
  5. {#Z+}  { These comments don't need to go into the help file. }
  6.  
  7. {$B-}   { Use fast boolean evaluation. }
  8.  
  9. { Version 1.2 - Adds TNulStream and TXMSStream, from suggestion and
  10.                 code by Stefan Boether; TBitFilter, from suggestion
  11.                 by Rene Seguin; added call to Flush to TFilter.Done;
  12.                 UseBuf and OwnMem to TRAMStream.
  13.                 TTextFilter fixed so that mixed access methods work.
  14.           1.3 - Added TDupFilter, TSequential, CRCs and Checksums
  15.           1.4 - Recoded several of the TRAMStream methods in assembler for
  16.                 more speed; fixed numerous TTextFilter bugs and added
  17.                 TTextFilter.AssignStream and TextDemo.pas; fixed
  18.                 TXMSStream.Seek bug.  Changed xms_Memavail and xms_Maxavail
  19.                 to report in bytes, and added ems_Memavail and ems_Maxavail
  20.                 (based on code sent to me by Eyal Doron) and disk_Memavail
  21.                 and disk_Maxavail. Changed TXMSStream.Init to match
  22.                 TEMSStream.Init. Added TConcatFilter, TLimitFilter,
  23.                 TLoopFilter, TReverseFilter and TWorkStream.  Added OwnsBase
  24.                 field to TFilter.  Did some testing to assure that the unit
  25.                 works in BP 7 protected mode.  Thanks to Max Maschein, Eyal
  26.                 Doron, and others for bug fix help.
  27.           1.5 - The first public release of the 1.4 enhancements.}
  28.  
  29. { Load some conditional defines }
  30. {$i STDefine.inc}
  31.  
  32. {$ifdef overlays}
  33.   {$O-}
  34.   { Don't overlay this unit; it contains code that needs to participate
  35.          in overlay management. }
  36. {$endif}
  37.  
  38. {  Hierarchy:
  39.  
  40.    TStream                  (from Objects)
  41.      TFilter                Base type for filters
  42.        TEncryptFilter       Encrypts as it writes; decrypts as it reads
  43.        TLZWFilter           Compresses as it writes; expands as it reads
  44.        TTextFilter          Provides text file interface to stream
  45.        TLogFilter           Provides logging of text file activity
  46.        TBitFilter           Allows reads & writes by the bit
  47.        TDupFilter           Duplicates output, checks for matching input
  48.        TConcatFilter        Concatenates two streams
  49.        TLimitFilter         Limits I/O to a specific range
  50.          TLoopFilter        Joins end of stream to start
  51.        TReverseFilter       Reads and writes the stream in reverse order
  52.        TSequential          Filter that doesn't allow Seek
  53.          TChksumFilter      Calculates 16 bit checksum for reads and writes
  54.          TCRC16Filter       Calculates XMODEM-style 16 bit CRC
  55.          TCRCARCFilter      Calculates ARC-style 16 bit CRC
  56.          TCRC32Filter       Calculates ZIP/ZModem-style 32 bit CRC
  57.      TNulStream             Eats writes, returns constant on reads
  58.      TRAMStream             Stream in memory
  59.      TXMSStream             Stream in XMS
  60.      TDOSStream             (from Objects)
  61.        TBufStream           (from Objects)
  62.          TNamedBufStream    Buffered file stream that knows its name
  63.            TTempBufStream   Buffered file stream that erases itself when done
  64.      TWorkStream            Stream that grows as needed
  65.  
  66.    Procedures & functions:
  67.  
  68.    TempStream      allocates a temporary stream
  69.    OvrInitStream   like OvrInitEMS, but buffers overlays on a stream
  70.                    May be called several times to buffer different
  71.                    segments on different streams.
  72.    OvrDetachStream detaches stream from overlay system
  73.    OvrDisposeStreams detaches all streams from overlay system and disposes of
  74.                    them
  75.    OvrSizeNeeded   Calculates the size needed to load the rest of the segments
  76.                    to a stream
  77.    OvrLoadAll      immediately copies as many overlay segments to the stream
  78.                    as will fit
  79.    UpdateChkSum    updates a 16 bit checksum value
  80.    UpdateCRC16     updates a CRC16 value
  81.    UpdateCRCARC    updates a CRCARC value
  82.    UpdateCRC32     updates a CRC32 value
  83.    ReverseBytes    reverses the byte order within a buffer
  84.  
  85. }
  86. {#Z-}
  87.  
  88. interface
  89.  
  90. uses
  91.   {$ifdef windows}
  92.   strings,windos,winprocs,
  93.   {$else}
  94.   DOS,
  95.   {$endif}
  96.   {$ifdef overlays}
  97.   Overlay,
  98.   {$endif}
  99.   {$ifdef wobjects}
  100.   Wobjects;
  101.   {$else}
  102.   Objects;
  103.   {$endif}
  104.  
  105. const
  106.   stBadMode = 1;                  { Bad mode for stream - operation not
  107.                                     supported.  ErrorInfo = mode. }
  108.   stStreamFail = 2;               { Stream init failed }
  109.   stBaseError = 3;                { Error in base stream. ErrorInfo = base error value }
  110.   stMemError = 4;                 { Not enough memory for operation }
  111.   stSigError = 5;                 { Problem with LZ file signature }
  112.   stUsedAll = 6;                  { Used limit of allocation }
  113.   stUnsupported = 7;              { Operation unsupported in this stream }
  114.   stBase2Error = 8;               { Error in second base.  ErrorInfo = base2 error value }
  115.   stMisMatch = 9;                 { Two bases don't match.  ErrorInfo = mismatch position
  116.                                     in current buffer. }
  117.   stIntegrity = 10;               { Stream has detected an integrity error
  118.                                     in a self check.  Info depends on
  119.                                     stream type. }
  120. type
  121.   TOpenMode = $3C00..$3DFF;       { Allowable DOS stream open modes }
  122.   {$ifdef windows}
  123.   FNameStr = PChar;            { To make streams take names as in the manual. }
  124.   {$endif}
  125.  
  126.   PFilter = ^TFilter;
  127.   TFilter =
  128.     object(TStream)
  129.     { Generic object to filter another stream.  TFilter just passes everything
  130.       through, and mirrors the status of the base stream }
  131.  
  132.       Base : PStream;
  133.       { Pointer to the base stream. }
  134.  
  135.       Startofs : LongInt;
  136.       { The offset of the start of the filter in the base stream. }
  137.  
  138.       OwnsBase : Boolean;
  139.       { Defaults true; if set to false, then #Done# won't dispose of
  140.         the base. }
  141.  
  142.       constructor Init(ABase : PStream);
  143.         { Initialize the filter with the given base. }
  144.  
  145.       destructor Done; virtual;
  146.         { Flush filter, then dispose of base if #OwnsBase#. }
  147.  
  148.       function GetPos : LongInt; virtual;
  149.       function GetSize : LongInt; virtual;
  150.       procedure Read(var Buf; Count : Word); virtual;
  151.       procedure Seek(Pos : LongInt); virtual;
  152.       procedure Truncate; virtual;
  153.       procedure Write(var Buf; Count : Word); virtual;
  154.       procedure Flush; virtual;
  155.  
  156.       function CheckStatus : Boolean; virtual;
  157.     { Return true if status is stOK.
  158.       If status is stOK, but base is not, then reset the base.  This is a poor
  159.       substitute for a virtual Reset method. }
  160.  
  161.       procedure CheckBase;
  162.         { Check base stream for error, and copy status using own Error method. }
  163.     end;
  164.  
  165.   PEncryptFilter = ^TEncryptFilter;
  166.   TEncryptFilter =
  167.     object(TFilter)
  168.   { Filter which encrypts text going in or out; encrypting twice with the same
  169.     key decrypts. Not very sophisticated encryption. }
  170.  
  171.       Key : LongInt;
  172.       { Key is used as a Randseed replacement }
  173.  
  174.       constructor Init(Akey : LongInt; ABase : PStream);
  175.         { Init with a given key }
  176.  
  177.       procedure Read(var Buf; Count : Word); virtual;
  178.       procedure Seek(Pos : LongInt); virtual;
  179.       procedure Write(var Buf; Count : Word); virtual;
  180.     end;
  181.  
  182. const
  183.   MaxStack = 4096;                { Must match lzwstream.asm declaration! }
  184.  
  185. type
  186.   PLZWTables = ^TLZWTables;
  187.   TLZWTables =
  188.     record
  189.       Collision : array[0..MaxStack-1] of Byte; { Hash table entries }
  190.       PrefixTable : array[0..MaxStack-1] of Word; { Code for preceding stringf }
  191.       SuffixTable : array[0..MaxStack-1] of Byte; { Code for current character }
  192.       ChildTable : array[0..MaxStack-1] of Word; { Next duplicate in collision
  193.                                                  list. }
  194.       CharStack : array[0..MaxStack-1] of Byte; { Decompression stack }
  195.       StackPtr : Word;            { Decompression stack depth }
  196.       Prefix : Word;              { Previous code string }
  197.       TableUsed : Word;           { # string table entries used }
  198.       InputPos : Word;            { Index in input buffer }
  199.       OutputPos : Word;           { Index in output buffer }
  200.       LastHit : Word;             { Last empty slot in collision
  201.                                                  table. }
  202.       CodeBuf : Word;
  203.       SaveIP : Word;
  204.       SaveAX : Word;
  205.       SaveCX : Word;
  206.       SaveDX : Word;
  207.  
  208.       NotFound : Byte;            { Character combination found
  209.                                                  flag. }
  210.     end;
  211.  
  212.   PLZWFilter = ^TLZWFilter;
  213.   TLZWFilter =
  214.     object(TFilter)
  215.       Mode : Word;                { Either stOpenRead or stOpenWrite. }
  216.       Size,                       { The size of the expanded stream. }
  217.       Position : LongInt;         { The current position in the expanded stream }
  218.       Tables : PLZWTables;        { Tables holding the compressor state. }
  219.  
  220.       constructor Init(ABase : PStream; AMode : TOpenMode);
  221.     {  Create new compressor stream, to use ABase as the source/destination
  222.        for data.  AMode must be stOpenRead or stOpenWrite. }
  223.  
  224.       destructor Done; virtual;
  225.     {  Flushes all data to the stream, and writes the uncompressed
  226.        filesize to the head of it before calling TFilter.done. }
  227.  
  228.       procedure Flush; virtual;
  229.       function GetPos : LongInt; virtual;
  230.       function GetSize : LongInt; virtual;
  231.       procedure Read(var Buf; Count : Word); virtual;
  232.  
  233.       procedure Seek(Pos : LongInt); virtual;
  234.     {  Seek is not supported at all in Write mode.  In Read mode, it is
  235.        slow for seeking forwards, and very slow for seeking backwards:
  236.        it rewinds the file to the start and readforward from there. }
  237.  
  238.       procedure Truncate; virtual;
  239.     {  Truncate is not supported in either mode, and always causes a
  240.        call to Error. }
  241.  
  242.       procedure Write(var Buf; Count : Word); virtual;
  243.     end;
  244.  
  245. type
  246.   PTextFilter = ^TTextFilter;
  247.   TTextFilter =
  248.     object(TFilter)
  249.   { A filter to provide ReadLn/WriteLn interface to a stream.  First
  250.     open the stream and position it, then pass it to this filter;
  251.     then Reset, Rewrite, or Append the Textfile variable, and do all
  252.     reads and writes to it; they'll go to the stream through a TFDD.
  253.     You can also assign the stream to any other text variable using
  254.     the #AssignStream# method. }
  255.  
  256.       TextFile : Text;
  257.       { A fake text file to use with Read(ln)/Write(ln). }
  258.       TextPtr  : ^text;
  259.       { A pointer to the text file used by the filter.  Initialized
  260.         to point to TextFile, but #AssignStream# will change TextPtr. }
  261.  
  262.       constructor Init(ABase : PStream; AName : String);
  263.     { Initialize the interface to ABase; stores AName in the name field of
  264.       #Textfile#.  AName isn't used beyond this, but may be helpful
  265.       if you choose to watch the TextFile field in the debugger.  }
  266.  
  267.       destructor Done; virtual;
  268.       { Flushes the text file, then closes and disposes of the base stream. }
  269.  
  270.       procedure AssignStream(var NewText:text; AName : String);
  271.       { Close the currently assigned text file, and assign a new one.
  272.         As with #Init#, the name is stored in NewText, but is not otherwise
  273.         used.}
  274.  
  275.       function GetPos : LongInt; virtual;
  276.       function GetSize : LongInt; virtual;
  277.       procedure Flush; virtual;
  278.       procedure Read(var Buf; Count : Word); virtual;
  279.       procedure Seek(Pos : LongInt); virtual;
  280.       procedure Truncate; virtual;
  281.       procedure Write(var Buf; Count : Word); virtual;
  282.     end;
  283.  
  284. type
  285.   PLogFilter = ^TLogFilter;
  286.   TLogFilter =
  287.     object(TFilter)
  288.       { A filter to log activity on a text file. }
  289.  
  290.       LogList : ^Text;            { A pointer to the first logged file }
  291.  
  292.       constructor init(ABase:PStream);
  293.       { Initializes filter, but doesn't start logging anything }
  294.  
  295.       destructor Done; virtual;
  296.       { Stops logging all files, and closes & disposes of the base stream }
  297.  
  298.       procedure Log(var F : Text);
  299.     { Logs all input and output to F to the stream.  You must do the Assign to
  300.       F first, and not do another Assign without closing F. }
  301.  
  302.       function Unlog(var F : Text) : Boolean;
  303.     { Stops logging of F.  Called automatically if file is closed. Returns
  304.       false and does nothing on error. }
  305.     end;
  306.  
  307.   TBit = 0..1;                    { A single bit }
  308.  
  309.   PBitFilter = ^TBitFilter;
  310.   TBitFilter =
  311.     object(TFilter)
  312.       BitPos : ShortInt;
  313.       { Position of stream relative to base file.  Negative values signal
  314.         that the buffer is unchanged from the file, positive values signal
  315.         that the file needs to be updated.  Zero signals an empty buffer. }
  316.       Mask : Byte;                { Mask to extract next bit from buffer }
  317.       Buffer : Byte;              { Buffer of next 8 bits from stream }
  318.       AtEnd : Boolean;            { Flag to signal that we're at the end
  319.                                     of the base, and we shouldn't read
  320.                                     it.  Bases that change in length should
  321.                                     set this to false. }
  322.  
  323.       constructor Init(ABase : PStream);
  324.  
  325.       procedure Flush; virtual;   { Flush buffer to stream }
  326.       procedure Seek(Pos : LongInt); virtual; { Seek to bit at start of
  327.                                                pos byte. }
  328.       procedure Read(var Buf; Count : Word); virtual;
  329.       procedure Write(var Buf; Count : Word); virtual;
  330.  
  331.       function GetBit : TBit;     { Get next bit from stream }
  332.       function GetBits(Count : Byte) : LongInt; { Get up to 32 bits }
  333.       procedure ReadBits(var Buf; Count : LongInt); { Read bits from stream }
  334.  
  335.       procedure PutBit(ABit : TBit); { Put one bit to stream }
  336.       procedure PutBits(ABits : LongInt; Count : Byte); { Put up to 32 bits,
  337.                                                         { low bits first. }
  338.       procedure WriteBits(var Buf; Count : LongInt); { Write count bits to stream }
  339.  
  340.       procedure SeekBit(Pos : LongInt); { Seek to particular bit }
  341.       function GetBitPos : LongInt;
  342.  
  343.       procedure CopyBits(var S : TBitFilter; Count : LongInt); { Copy bits from S }
  344.       procedure ByteAlign;        { Seek forward to next byte boundary. }
  345.  
  346.       procedure PrepareBuffer(ForRead : Boolean);
  347.         { Internal method to assure that buffer is valid }
  348.     end;
  349.  
  350.   PDupFilter = ^TDupFilter;
  351.   TDupFilter =
  352.     object(TFilter)         { Duplicates output, confirms matching input }
  353.       Base2 : PStream;
  354.       { Pointer to the second base. }
  355.  
  356.       Startofs2 : LongInt;
  357.       { The offset of the start of the filter in the second base. }
  358.  
  359.       constructor Init(ABase, ABase2 : PStream);
  360.         { Initialize the filter with the given bases. }
  361.  
  362.       destructor Done; virtual;
  363.         { Flush filter, then dispose of both bases. }
  364.  
  365.       function MisMatch(var buf1,buf2; count:word):word; virtual;
  366.         { Checks for a mismatch between the two buffers.  Returns
  367.           the byte number of the mismatch (1 based), or 0 if they
  368.           test equal.  This default method checks for an exact match. }
  369.  
  370.       procedure Read(var Buf; Count : Word); virtual;
  371.       procedure Seek(Pos : LongInt); virtual;
  372.       procedure Truncate; virtual;
  373.       procedure Write(var Buf; Count : Word); virtual;
  374.       procedure Flush; virtual;
  375.  
  376.       function CheckStatus : Boolean; virtual;
  377.     { Return true if status is stOK.
  378.       If status is stOK, but base is not, then reset the base.  This is a poor
  379.       substitute for a virtual Reset method. }
  380.  
  381.       procedure CheckBase2;
  382.         { Check 2nd base stream for error, and copy status using own Error method. }
  383.     end;
  384.  
  385.   PConcatFilter = ^TConcatFilter;
  386.   TConcatFilter =
  387.     object(TFilter)
  388.       { A filter which acts to concatenate two streams (or parts of streams)
  389.         so that they appear as one.}
  390.       Base2 : PStream;
  391.         { Pointer to the second base.  This one logically follows the first.}
  392.  
  393.       Startofs2 : LongInt;
  394.         { The offset of the start of the filter in the second base. }
  395.  
  396.       Position : Longint;
  397.         { The current position of the filter.  The corresponding
  398.           base stream is kept synchronized with this }
  399.       Base1Size : Longint;
  400.         { This is used a lot to determine switching. }
  401.  
  402.       constructor Init(ABase, ABase2 : PStream);        { Initialize the filter with the given bases. }
  403.  
  404.       destructor Done; virtual;
  405.         { Flush filter, then dispose of both bases. }
  406.  
  407.       function GetPos:longint; virtual;
  408.       function GetSize:longint; virtual;
  409.       procedure Read(var Buf; Count : Word); virtual;
  410.       procedure Seek(Pos : LongInt); virtual;
  411.       procedure Truncate; virtual;
  412.       procedure Write(var Buf; Count : Word); virtual;
  413.       procedure Flush; virtual;
  414.       { These methods work directly on Base until its size
  415.         is reached, then switch over to Base2.  Base will *never* grow
  416.         from the size at stream initialization. }
  417.  
  418.       function CheckStatus : Boolean; virtual;
  419.  
  420.       procedure CheckBase2;
  421.         { Check 2nd base stream for error, and copy status using own Error method. }
  422.  
  423.     end;
  424.  
  425.   PLimitFilter = ^TLimitFilter;
  426.   TLimitFilter =
  427.     object(TFilter)
  428.       { Limits all access to the bytes between LoLimit and HiLimit. }
  429.       LoLimit,HiLimit : longint;
  430.       { The lower and upper limit points.  These are in the TFilter
  431.         scale, i.e. relative to #TFilter.Base#. }
  432.       constructor init(ABase:PStream;ALoLimit,AHiLimit:longint);
  433.       { Does the usual init, sets the limits, then does a Seek to ALoLimit
  434.         if it is non-zero. }
  435.  
  436.       function GetSize:longint; virtual;
  437.       { Returns the smaller of HiLimit and the #TFilter.GetSize# value. }
  438.  
  439.       procedure Read(var Buf; Count : Word); virtual;
  440.       procedure Seek(Pos : LongInt); virtual;
  441.       procedure Write(var Buf; Count : Word); virtual;
  442.     end;
  443.  
  444.   PLoopFilter = ^TLoopFilter;
  445.   TLoopFilter =
  446.     object(TLimitFilter)
  447.       { Moves all access to the bytes between LoLimit and HiLimit. }
  448.       function GetSize:longint; virtual;
  449.       { Returns the smaller of the size between the limits, or from
  450.         the low limit to the end of the base }
  451.       procedure Read(var Buf; Count : Word); virtual;
  452.       procedure Seek(Pos : LongInt); virtual;
  453.       procedure Write(var Buf; Count : Word); virtual;
  454.     end;
  455.  
  456.  
  457.  
  458.   PReverseFilter = ^TReverseFilter;
  459.   TReverseFilter =
  460.     object(TFilter)
  461.       { Reads and writes the base in reverse order. }
  462.       ReverseBlocks : Boolean;  { Whether to reverse the bytes within
  463.                                   a Read/Write block }
  464.       constructor init(ABase:PStream; AReverseBlocks:boolean);
  465.       { Standard initialization }
  466.       function GetPos:longint; virtual;
  467.       { Returns the position in bytes from the end of the base }
  468.       procedure Read(var Buf; Count : Word); virtual;
  469.       { See #Write#. }
  470.       procedure Write(var Buf; Count : Word); virtual;
  471.       { These methods read/write the block of bytes just previous to
  472.         the current base file pointer.  The bytes themselves are
  473.         reversed if #ReverseBlocks# is true. }
  474.       procedure Seek(Pos : LongInt); virtual;
  475.       { Does the Seek in the reversed byte order, i.e. count from the
  476.         end of the stream }
  477.       procedure Truncate; virtual;
  478.       { Triggers an #stUnsupported# error. }
  479.     end;
  480.  
  481.   procedure ReverseBytes(var Buf; Count : Word);
  482.   { Reverses the order of the bytes in the buffer }
  483.  
  484. type
  485.   PSequential = ^TSequential;
  486.   TSequential =
  487.     object(TFilter)                        { Filter for sequential access only }
  488.       procedure Seek(pos:longint); virtual;{ Signals stUnsupported if a Seek is attempted }
  489.     end;
  490.  
  491.   PChksumFilter = ^TChksumFilter;
  492.   TChksumFilter =
  493.     object(TSequential)                    { Calculates 16 bit checksum of
  494.                                              bytes read/written. }
  495.       Chksum : word;
  496.  
  497.       constructor Init(ABase : PStream;AChksum:word);
  498.         { Initialize the filter with the given base and starting checksum. }
  499.  
  500.       procedure Read(var Buf; Count : Word); virtual;
  501.       procedure Write(var Buf; Count : Word); virtual;
  502.     end;
  503.  
  504.   PCRC16Filter = ^TCRC16Filter;
  505.   TCRC16Filter =
  506.     object(TSequential)      { Calculates XMODEM style 16 bit CRC }
  507.       CRC16 : word;
  508.  
  509.       constructor Init(ABase : PStream;ACRC16:word);
  510.         { Initialize the filter with the given base and starting CRC. }
  511.  
  512.       procedure Read(var Buf; Count : Word); virtual;
  513.       procedure Write(var Buf; Count : Word); virtual;
  514.     end;
  515.  
  516.   PCRCARCFilter = ^TCRCARCFilter;
  517.   TCRCARCFilter =
  518.     object(TSequential)      { Calculates ARC-style 16 bit CRC }
  519.       CRCARC : word;
  520.  
  521.       constructor Init(ABase : PStream;ACRCARC:word);
  522.         { Initialize the filter with the given base and starting CRC. }
  523.  
  524.       procedure Read(var Buf; Count : Word); virtual;
  525.       procedure Write(var Buf; Count : Word); virtual;
  526.     end;
  527.  
  528.   PCRC32Filter = ^TCRC32Filter;
  529.   TCRC32Filter =
  530.     object(TSequential)      { Calculates PKZIP and ZModem style 32 bit CRC }
  531.       CRC32 : longint;
  532.  
  533.       constructor Init(ABase : PStream;ACRC32:longint);
  534.         { Initialize the filter with the given base and starting CRC. }
  535.  
  536.       procedure Read(var Buf; Count : Word); virtual;
  537.       procedure Write(var Buf; Count : Word); virtual;
  538.     end;
  539.  
  540.  
  541.   PNulStream = ^TNulStream;
  542.   TNulStream =
  543.     object(TStream)
  544.       Position : LongInt;         { The current position for the stream. }
  545.       Value : Byte;               { The value returned on reads. }
  546.  
  547.       constructor Init(AValue : Byte);
  548.       function GetPos : LongInt; virtual;
  549.       function GetSize : LongInt; virtual;
  550.       procedure Read(var Buf; Count : Word); virtual;
  551.       procedure Seek(Pos : LongInt); virtual;
  552.       procedure Write(var Buf; Count : Word); virtual;
  553.     end;
  554.  
  555.   Pbyte_array = ^Tbyte_array;
  556.   Tbyte_array = array[0..65520] of Byte; { Type used as a buffer. }
  557.  
  558.   PRAMStream = ^TRAMStream;
  559.   TRAMStream =
  560.     object(TStream)
  561.       Position : Word;            { The current position for the stream. }
  562.  
  563.       Size : Word;                { The current size of the stream. }
  564.       Alloc : Word;               { The size of the allocated block of memory. }
  565.  
  566.       Buffer : Pbyte_array;       { Points to the stream data. }
  567.       OwnMem : Boolean;           { Whether Done should dispose of data.}
  568.  
  569.       constructor Init(Asize : Word);
  570.     { Attempt to initialize the stream to a block size of Asize;
  571.        initial stream size and position are 0. }
  572.       constructor UseBuf(ABuffer : Pointer; Asize : Word);
  573.      { Initialize the stream using the specified buffer.  OwnMem is set
  574.        to false, so the buffer won't be disposed of. Initial position is 0,
  575.        size is Asize. }
  576.  
  577.       destructor Done; virtual;
  578.         { Dispose of the stream. }
  579.  
  580.       function GetPos : LongInt; virtual;
  581.       function GetSize : LongInt; virtual;
  582.       procedure Read(var Buf; Count : Word); virtual;
  583.       procedure Seek(Pos : LongInt); virtual;
  584.       procedure Truncate; virtual;
  585.       procedure Write(var Buf; Count : Word); virtual;
  586.     end;
  587.  
  588.   PXMSStream = ^TXMSStream;
  589.   TXMSStream =
  590.     object(TStream)
  591.       Handle : Word;              { XMS handle }
  592.       BlocksUsed : Word;          { Number of 1K blocks used. Always allocates
  593.                                     at least one byte more than Size. }
  594.       Size : LongInt;             { The current size of the stream }
  595.       Position : LongInt;         { Current position }
  596.  
  597.       constructor Init(MinSize,MaxSize:longint);
  598.       destructor Done; virtual;
  599.  
  600.       function GetPos : LongInt; virtual;
  601.       function GetSize : LongInt; virtual;
  602.       procedure Read(var Buf; Count : Word); virtual;
  603.       procedure Seek(Pos : LongInt); virtual;
  604.       procedure Truncate; virtual;
  605.       procedure Write(var Buf; Count : Word); virtual;
  606.  
  607.       procedure NewBlock;         { Internal method to allocate a block }
  608.       procedure FreeBlock;        { Internal method to free one block }
  609.     end;
  610.  
  611. function xms_MemAvail : Longint;
  612.   { Returns total of available XMS bytes. }
  613. function xms_MaxAvail : Longint;
  614.   { Returns size of largest available XMS block in bytes. }
  615. function ems_MemAvail : Longint;
  616.   { Returns total of available EMS in bytes. }
  617. function ems_MaxAvail : Longint;
  618.   { Returns size of largest available EMS block in bytes. }
  619.  
  620. const
  621.   TempEnvVar  : String[12]  = 'TEMP';
  622.   { The name of an environment variable holding a directory list
  623.     where #TTempBufStream# should go looking for disk space. }
  624.  
  625. function disk_MemAvail : Longint;
  626.   { Returns total of available disk space for temp streams, from the
  627.     list specified by #TempEnvVar#. }
  628. function disk_MaxAvail : Longint;
  629.   { Returns maximum available block of disk space for temp streams,
  630.     from the list specified by #TempEnvVar#. }
  631.  
  632. type
  633.   PNamedBufStream = ^TNamedBufStream;
  634.   TNamedBufStream =
  635.     object(TBufStream)
  636.       { A simple descendant of TBufStream which knows its own name.}
  637.  
  638.     {$ifdef windows}
  639.     filename : PChar;
  640.     {$else}
  641.       Filename : PString;
  642.     {$endif}
  643.       { The name of the stream. }
  644.  
  645.       constructor Init(Name : FNameStr; Mode : TOpenMode; ABufSize : Word);
  646.         { Open the file with the given name, and save the name. }
  647.  
  648.       destructor Done; virtual;
  649.         { Close the file. }
  650.  
  651.     end;
  652.  
  653.   PTempBufStream = ^TTempBufStream;
  654.   TTempBufStream =
  655.     object(TNamedBufStream)
  656.       { A temporary buffered file stream, which deletes itself when done.
  657.         It's allocated on one of the directories specified by #TempEnvVar#.}
  658.  
  659.       constructor Init(ABufSize : Word;InitSize,MaxSize : Longint);
  660.   { Create a temporary file with a unique name, in the directory
  661.     pointed to by the environment varable named in #TempEnvVar# or in
  662.     the current directory, open it in read/write mode, and try to grow
  663.     it to InitSize bytes.   }
  664.  
  665.       destructor Done; virtual;
  666.         { Close and delete the temporary file. }
  667.  
  668.     end;
  669.  
  670.   TStreamType = (NoStream, RAMStream, EMSStream, XMSStream, FileStream);
  671.   { The type of stream that a tempstream might be. }
  672.  
  673. const
  674.   NumTypes = Ord(FileStream);
  675.  
  676. type
  677.   TStreamRanking = array[1..NumTypes] of TStreamType;
  678.   { A ranking of preference for a type of stream, from most to least preferred }
  679.  
  680.   TAllocator = function (InitSize, MaxSize : LongInt;
  681.                        Preference : TStreamRanking) : PStream;
  682.   { This is a declaration just like the Streams.TempStream function.}
  683.  
  684.   PWorkStream = ^TWorkStream;
  685.   TWorkStream =
  686.     object(TFilter)
  687.      { This is a stream type that grows as you write to it by allocating new
  688.        blocks according to a specified strategy.  Blocks may be of mixed
  689.        types. It's a descendant of a filter, but it manages its own base. }
  690.  
  691.      Allocate : TAllocator;
  692.      BlockMin,                     { These fields are passed to Allocate }
  693.      BlockMax : longint;
  694.      Preference : TStreamRanking;
  695.      BlockStart: longint; { The offset in the stream where the
  696.                             last block starts. }
  697.  
  698.      constructor init(Allocator:TAllocator;ABlockmin,ABlockMax:Longint;
  699.                       APreference : TStreamRanking);
  700.      { ABlockmin to APreference are passed to the allocator to allocate
  701.        a new block whenever the current one gives a write error.
  702.        The TWorkStream will never try to write a single block that crosses
  703.        the ABlockMax boundary, so tests within the stream can be simple.}
  704.      procedure write(var Buf; Count:Word); virtual;
  705.      { The write procedure checks whether the write would make the
  706.        current block grow too large; if so, it splits up the write. }
  707.    end;
  708.  
  709. const
  710.   BufSize : Word = 2048;          { Buffer size if buffered stream is used. }
  711.  
  712. const ForSpeed : TStreamRanking = (RAMStream, EMSStream, XMSStream, FileStream);
  713.   { Streams ordered for speed }
  714.  
  715. const ForSize : TStreamRanking = (FileStream, EMSStream, XMSStream, RAMStream);
  716.   { Streams ordered for low impact on the heap }
  717.  
  718. const ForSizeInMem : TStreamRanking = (EMSStream, XMSStream, RAMStream, NoStream);
  719.   { Streams in memory only, ordered as #ForSize#. }
  720.  
  721. const ForOverlays : TStreamRanking = (EMSStream, XMSStream, FileStream, NoStream);
  722.   { Streams ordered for speed, but never in RAM. }
  723.  
  724. function TempStream(InitSize, MaxSize : LongInt;
  725.                     Preference : TStreamRanking) : PStream;
  726.  
  727. {      This procedure returns a pointer to a temporary stream from a
  728.        choice of 3, specified in the Preference array.  The first stream
  729.        type listed in the Preference array which can be successfully
  730.        created with the given sizes will be returned, or Nil if none can
  731.        be made. }
  732.  
  733. function StreamName(S:PStream):String;
  734. { This function returns a string naming the type of S^.  It's useful for
  735.   debugging programs that use TempStream and TWorkStream.  However,
  736.   it's for debugging only!  It links every single stream type into your
  737.   .EXE. }
  738.  
  739. {$ifdef overlays}
  740. procedure OvrInitStream(S : PStream);
  741. { Copies overlay segment code to S as new segments are loaded,
  742.   and does reloads from there.  Allows multiple calls, to buffer
  743.   different segments on different streams. }
  744.  
  745. procedure OvrDetachStream(BadS : PStream);
  746.   { Makes sure that the overlay system makes no references to BadS. }
  747.  
  748. procedure OvrDisposeStreams;
  749.   { Detaches and disposes of all streams being used by the overlay system }
  750.  
  751. function OvrSizeNeeded : LongInt;
  752. { Returns the size required to load any segments which still haven't
  753.   been loaded to a stream. }
  754.  
  755. function OvrLoadAll : Boolean;
  756. { Forces all overlay segments to be copied into the stream; if successful
  757.   (true) then no more references to the overlay file will be made. }
  758. {$endif windows}
  759.  
  760. Function UpdateChksum(Initsum: Word; Var InBuf; InLen : Word) : Word;
  761. { Updates the checksum Initsum by adding InLen bytes from InBuf }
  762.  
  763. Function UpdateCRC16(InitCRC : Word; Var InBuf; InLen : Word) : Word;
  764. { I believe this is the CRC used by the XModem protocol.  The transmitting
  765.   end should initialize with zero, UpdateCRC16 for the block, Continue the
  766.   UpdateCRC16 for two nulls, and append the result (hi order byte first) to
  767.   the transmitted block.  The receiver should initialize with zero and
  768.   UpdateCRC16 for the received block including the two byte CRC.  The
  769.   result will be zero (why?) if there were no transmission errors.  (I have
  770.   not tested this function with an actual XModem implementation, though I
  771.   did verify the behavior just described.  See TESTCRC.PAS.) }
  772.  
  773.  
  774. Function UpdateCRCArc(InitCRC : Word; Var InBuf; InLen : Word) : Word;
  775. { This function computes the CRC used by SEA's ARC utility.  Initialize
  776.   with zero. }
  777.  
  778. Function UpdateCRC32(InitCRC : LongInt; Var InBuf; InLen : Word) : LongInt;
  779. { This function computes the CRC used by PKZIP and Forsberg's ZModem.
  780.   Initialize with high-values ($FFFFFFFF), and finish by inverting all bits
  781.   (Not). }
  782.  
  783. implementation
  784.  
  785.   function MinLong(x,y:longint):longint;
  786.   begin
  787.     if x<y then
  788.       MinLong := x
  789.     else
  790.       MinLong := y;
  791.   end;
  792.  
  793.   function MaxLong(x,y:longint):longint;
  794.   begin
  795.     MaxLong := -MinLong(-x,-y);
  796.   end;
  797.  
  798.   {****** TFilter code *******}
  799.  
  800.   constructor TFilter.Init(ABase : PStream);
  801.   begin
  802.     TStream.Init;
  803.     Base := ABase;
  804.     CheckBase;
  805.     if Status = stOK then
  806.       Startofs := Base^.GetPos;
  807.     OwnsBase := true;
  808.   end;
  809.  
  810.   destructor TFilter.Done;
  811.   begin
  812.     if Base <> nil then
  813.     begin
  814.       Flush;
  815.       if OwnsBase then
  816.         Dispose(Base, Done);
  817.     end;
  818.     TStream.Done;
  819.   end;
  820.  
  821.   function TFilter.GetPos : LongInt;
  822.   begin
  823.     if CheckStatus then
  824.     begin
  825.       GetPos := Base^.GetPos-Startofs;
  826.       CheckBase;
  827.     end;
  828.   end;
  829.  
  830.   function TFilter.GetSize : LongInt;
  831.   begin
  832.     if CheckStatus then
  833.     begin
  834.       GetSize := Base^.GetSize-Startofs;
  835.       CheckBase;
  836.     end;
  837.   end;
  838.  
  839.   procedure TFilter.Read(var Buf; Count : Word);
  840.   begin
  841.     if CheckStatus then
  842.     begin
  843.       Base^.Read(Buf, Count);
  844.       CheckBase;
  845.     end;
  846.   end;
  847.  
  848.   procedure TFilter.Seek(Pos : LongInt);
  849.   begin
  850.     if CheckStatus then
  851.     begin
  852.       Base^.Seek(Pos+Startofs);
  853.       CheckBase;
  854.     end;
  855.   end;
  856.  
  857.   procedure TFilter.Truncate;
  858.   begin
  859.     if CheckStatus then
  860.     begin
  861.       Base^.Truncate;
  862.       CheckBase;
  863.     end;
  864.   end;
  865.  
  866.   procedure TFilter.Write(var Buf; Count : Word);
  867.   begin
  868.     if CheckStatus then
  869.     begin
  870.       Base^.Write(Buf, Count);
  871.       CheckBase;
  872.     end;
  873.   end;
  874.  
  875.   procedure TFilter.Flush;
  876.   begin
  877.     if CheckStatus then
  878.     begin
  879.       Base^.Flush;
  880.       CheckBase;
  881.     end;
  882.   end;
  883.  
  884.   function TFilter.CheckStatus : Boolean;
  885.   begin
  886.     if (Status = stOK) and (Base^.Status <> stOK) then
  887.       Base^.Reset;
  888.     CheckStatus := Status = stOK;
  889.   end;
  890.  
  891.   procedure TFilter.CheckBase;
  892.   begin
  893.     if Base^.Status <> stOK then
  894.       Error(stBaseError, Base^.Status);
  895.   end;
  896.  
  897.   constructor TEncryptFilter.Init(Akey : LongInt; ABase : PStream);
  898.   begin
  899.     TFilter.Init(ABase);
  900.     Key := Akey;
  901.   end;
  902.  
  903.   procedure TEncryptFilter.Read(var Buf; Count : Word);
  904.   var
  905.     i : Word;
  906.     SaveSeed : LongInt;
  907.     Bytes : Tbyte_array absolute Buf;
  908.   begin
  909.     SaveSeed := RandSeed;
  910.     RandSeed := Key;
  911.     TFilter.Read(Buf, Count);
  912.     for i := 0 to Count-1 do
  913.       Bytes[i] := Bytes[i] xor Random(256);
  914.     Key := RandSeed;
  915.     RandSeed := SaveSeed;
  916.   end;
  917.  
  918.   procedure CycleKey(Key, Cycles : LongInt);
  919. { For cycles > 0, mimics cycles calls to the TP random number generator.
  920.   For cycles < 0, backs it up the given number of calls. }
  921.   var
  922.     i : LongInt;
  923.     Junk : Integer;
  924.     SaveSeed : LongInt;
  925.   begin
  926.     if Cycles > 0 then
  927.     begin
  928.       SaveSeed := RandSeed;
  929.       RandSeed := Key;
  930.       for i := 1 to Cycles do
  931.         Junk := Random(0);
  932.       Key := RandSeed;
  933.       RandSeed := SaveSeed;
  934.     end
  935.     else
  936.       for i := -1 downto Cycles do
  937.         Key := (Key-1)*(-649090867);
  938.   end;
  939.  
  940.   procedure TEncryptFilter.Seek(Pos : LongInt);
  941.   var
  942.     OldPos : LongInt;
  943.   begin
  944.     OldPos := GetPos;
  945.     TFilter.Seek(Pos);
  946.     CycleKey(Key, Pos-OldPos);
  947.   end;
  948.  
  949.   procedure TEncryptFilter.Write(var Buf; Count : Word);
  950.   var
  951.     i : Word;
  952.     SaveSeed : LongInt;
  953.     BufPtr : Pointer;
  954.     BufPtrOffset : Word absolute BufPtr;
  955.     Buffer : array[0..255] of Byte;
  956.   begin
  957.     SaveSeed := RandSeed;
  958.     RandSeed := Key;
  959.     BufPtr := @Buf;
  960.     while Count > 256 do
  961.     begin
  962.       Move(BufPtr^, Buffer, 256);
  963.       for i := 0 to 255 do
  964.         Buffer[i] := Buffer[i] xor Random(256);
  965.       TFilter.Write(Buffer, 256);
  966.       Dec(Count, 256);
  967.       Inc(BufPtrOffset, 256);
  968.     end;
  969.     Move(BufPtr^, Buffer, Count);
  970.     for i := 0 to Count-1 do
  971.       Buffer[i] := Buffer[i] xor Random(256);
  972.     TFilter.Write(Buffer, Count);
  973.     Key := RandSeed;
  974.     RandSeed := SaveSeed;
  975.   end;
  976.  
  977.  
  978.   { ******* LZW code ******* }
  979.  
  980. {$L LZWSTREAM.OBJ}
  981.  
  982.   procedure Initialise(Tables : PLZWTables); External;
  983.  
  984.   function PutSignature(Tables : PLZWTables) : Boolean; External;
  985.  
  986.   function Crunch(InBufSize, OutBufSize : Word;
  987.                   var InBuffer, OutBuffer;
  988.   Tables : PLZWTables) : Pointer; External;
  989.  
  990. {  Crunch some more text.  Stops when Inbufsize bytes are used up, or
  991.    output buffer is full.   Returns bytes used in segment, bytes written
  992.    in offset of result }
  993.  
  994.   function FlushLZW(var OutBuffer;
  995.   Tables : PLZWTables) : Word; External;
  996. {  Flush the remaining characters to signal EOF.  Needs space for up to
  997.    3 characters. }
  998.  
  999.   function GetSignature(var InBuffer, Dummy;
  1000.   Tables : PLZWTables) : Boolean; External;
  1001. { Initializes for reading, and checks for 'LZ' signature in start of compressed
  1002.   code.  Inbuffer must contain at least 3 bytes.  Dummy is just there to put the
  1003.   Inbuffer in the right spot }
  1004.  
  1005.   function Uncrunch(InBufSize, OutBufSize : Word;
  1006.                     var InBuffer, OutBuffer;
  1007.   Tables : PLZWTables) : Pointer; External;
  1008. {  Uncrunch some text.  Will stop when it has done Outbufsize worth or has
  1009.    exhausted Inbufsize worth.  Returns bytes used in segment, bytes written
  1010.    in offset of result }
  1011.  
  1012.   constructor TLZWFilter.Init(ABase : PStream; AMode : TOpenMode);
  1013.     {  Create new compressor stream, to use ABase as the source/destination
  1014.        for data.  Mode must be stOpenRead or stOpenWrite. }
  1015.   var
  1016.     Buffer : array[1..3] of Byte;
  1017.     Info : Integer;
  1018.   begin
  1019.     Info := stBadMode;
  1020.     if (AMode = stOpenRead) or (AMode = stOpenWrite) then
  1021.     begin
  1022.       Info := stStreamFail;
  1023.       if TFilter.Init(ABase) then
  1024.       begin
  1025.         if Status = stOK then
  1026.         begin
  1027.           Info := stMemError;
  1028.           Startofs := Base^.GetPos;
  1029.           Position := 0;
  1030.           Mode := AMode;
  1031.  
  1032.           if MaxAvail >= SizeOf(TLZWTables) then
  1033.           begin
  1034.             Info := stSigError;
  1035.             GetMem(Tables, SizeOf(TLZWTables));
  1036.             Initialise(Tables);
  1037.             if Mode = stOpenRead then
  1038.             begin
  1039.               Base^.Read(Size, SizeOf(Size));
  1040.               Base^.Read(Buffer, 3);
  1041.               CheckBase;
  1042.               if GetSignature(Buffer, Buffer, Tables) then
  1043.                 Exit;             { Successfully opened for reading }
  1044.             end
  1045.             else if Mode = stOpenWrite then
  1046.             begin
  1047.               Size := 0;
  1048.               Base^.Write(Size, SizeOf(Size)); { Put a place holder }
  1049.               CheckBase;
  1050.               if PutSignature(Tables) then
  1051.                 Exit;             { Successful construction for writing! }
  1052.             end;
  1053.           end;
  1054.         end;
  1055.       end;
  1056.     end;
  1057.     Error(stInitError, Info);
  1058.   end;
  1059.  
  1060.   destructor TLZWFilter.Done;
  1061.   begin
  1062.     Flush;
  1063.     FreeMem(Tables, SizeOf(TLZWTables));
  1064.     TFilter.Done;
  1065.   end;
  1066.  
  1067.   procedure TLZWFilter.Write(var Buf; Count : Word);
  1068.   var
  1069.     Inbuf : array[0..65520] of Byte absolute Buf;
  1070.     Outbuf : array[0..255] of Byte;
  1071.     Inptr : Word;
  1072.     Sizes : record
  1073.               OutSize, UsedSize : Word;
  1074.             end;
  1075.   begin
  1076.     if CheckStatus then
  1077.     begin
  1078.       if Mode <> stOpenWrite then
  1079.         Error(stBadMode, Mode);
  1080.       Inptr := 0;
  1081.       repeat
  1082.         Pointer(Sizes) := Crunch(Count, SizeOf(Outbuf),
  1083.                                  Inbuf[Inptr], Outbuf, Tables);
  1084.         with Sizes do
  1085.         begin
  1086.           Base^.Write(Outbuf, OutSize);
  1087.  
  1088.           Dec(Count, UsedSize);
  1089.           Inc(Inptr, UsedSize);
  1090.           Inc(Size, UsedSize);
  1091.           Inc(Position, UsedSize);
  1092.         end;
  1093.       until Count = 0;
  1094.       CheckBase;
  1095.     end;
  1096.   end;
  1097.  
  1098.   procedure TLZWFilter.Flush;
  1099.   var
  1100.     Outbuf : array[0..255] of Byte;
  1101.     Sizes : record
  1102.               OutSize, UsedSize : Word;
  1103.             end;
  1104.     Pos : LongInt;
  1105.   begin
  1106.     if CheckStatus then
  1107.     begin
  1108.       if Mode = stOpenWrite then
  1109.       begin
  1110.         Pointer(Sizes) := Crunch(1, SizeOf(Outbuf), Outbuf, Outbuf, Tables);
  1111.         { Push one more character to match JA bug }
  1112.         with Sizes do
  1113.         begin
  1114.           Base^.Write(Outbuf, OutSize);
  1115.  
  1116.           OutSize := FlushLZW(Outbuf, Tables); { And flush }
  1117.           Base^.Write(Outbuf, OutSize);
  1118.         end;
  1119.         Pos := Base^.GetPos;
  1120.         Base^.Seek(Startofs);
  1121.         Base^.Write(Size, SizeOf(Size));
  1122.         Base^.Seek(Pos);
  1123.       end;
  1124.       Base^.Flush;
  1125.       Mode := 0;
  1126.       CheckBase;
  1127.     end;
  1128.   end;
  1129.  
  1130.   procedure TLZWFilter.Read(var Buf; Count : Word);
  1131.   var
  1132.     Outbuf : array[0..65520] of Byte absolute Buf;
  1133.     Inbuf : array[0..255] of Byte;
  1134.     OutPtr : Word;
  1135.     BlockSize : Word;
  1136.     Sizes : record
  1137.               OutSize, UsedSize : Word;
  1138.             end;
  1139.     BytesLeft : LongInt;
  1140.   begin
  1141.     if CheckStatus then
  1142.     begin
  1143.       if Mode <> stOpenRead then
  1144.         Error(stBadMode, Mode);
  1145.       OutPtr := 0;
  1146.       BlockSize := SizeOf(Inbuf);
  1147.       with Base^ do
  1148.         BytesLeft := GetSize-GetPos;
  1149.  
  1150.       if Position+Count > Size then
  1151.       begin
  1152.         Error(stReaderror, 0);
  1153.         FillChar(Buf, Count, 0);
  1154.         Exit;
  1155.       end;
  1156.  
  1157.       while Count > 0 do
  1158.       begin
  1159.         if BytesLeft < BlockSize then
  1160.           BlockSize := BytesLeft;
  1161.         Base^.Read(Inbuf, BlockSize);
  1162.         Pointer(Sizes) := Uncrunch(BlockSize, Count, Inbuf,
  1163.                                    Outbuf[OutPtr], Tables);
  1164.         with Sizes do
  1165.         begin
  1166.           if OutSize = 0 then
  1167.           begin
  1168.             Error(stReaderror, 0);
  1169.             FillChar(Outbuf[OutPtr], Count, 0);
  1170.             Exit;
  1171.           end;
  1172.           Dec(BytesLeft, UsedSize);
  1173.           Inc(Position, OutSize);
  1174.           Dec(Count, OutSize);
  1175.           Inc(OutPtr, OutSize);
  1176.           if UsedSize < BlockSize then
  1177.             with Base^ do         { seek back to the first unused byte }
  1178.               Seek(GetPos-(BlockSize-UsedSize));
  1179.         end;
  1180.       end;
  1181.       CheckBase;
  1182.     end;
  1183.   end;
  1184.  
  1185.   procedure TLZWFilter.Seek(Pos : LongInt);
  1186.   var
  1187.     Buf : array[0..255] of Byte;
  1188.     Bytes : Word;
  1189.   begin
  1190.     if CheckStatus then
  1191.     begin
  1192.       if Mode <> stOpenRead then
  1193.       begin
  1194.         Error(stBadMode, Mode);
  1195.         Exit;
  1196.       end;
  1197.       if Pos < Position then
  1198.       begin
  1199.         Base^.Seek(Startofs);
  1200.         FreeMem(Tables, SizeOf(TLZWTables));
  1201.  
  1202.         TLZWFilter.Init(Base, Mode); { Re-initialize everything.  Will this cause
  1203.                                      bugs in descendents? }
  1204.       end;
  1205.       while Pos > Position do
  1206.       begin
  1207.         if Pos-Position > SizeOf(Buf) then
  1208.           Bytes := SizeOf(Buf)
  1209.         else
  1210.           Bytes := Pos-Position;
  1211.         Read(Buf, Bytes);
  1212.       end;
  1213.     end;
  1214.   end;
  1215.  
  1216.   procedure TLZWFilter.Truncate;
  1217.   begin
  1218.     Error(stBadMode, Mode);
  1219.   end;
  1220.  
  1221.   function TLZWFilter.GetPos;
  1222.   begin
  1223.     GetPos := Position;
  1224.   end;
  1225.  
  1226.   function TLZWFilter.GetSize;
  1227.   begin
  1228.     GetSize := Size;
  1229.   end;
  1230.  
  1231.   { ***** Text Filter Code ******* }
  1232.  
  1233.   { These declarations are used both by TTextFilter and TLogFilter }
  1234.  
  1235. type
  1236.   TFDDfunc = function(var F : Text) : Integer;
  1237.  
  1238.   PStreamTextRec = ^StreamTextRec;
  1239.   PSaveText = ^TSaveText;
  1240.   TSaveText =
  1241.     record                        { Used when logging for original data values }
  1242.       OpenFunc,
  1243.       InOutFunc,
  1244.       FlushFunc,
  1245.       CloseFunc : TFDDfunc;
  1246.       S : PLogFilter;
  1247.       SaveData : PSaveText;
  1248.       Next : PStreamTextRec;
  1249.       Data : array[13..16] of Byte;
  1250.     end;
  1251.  
  1252.   StreamTextRec =
  1253.     record
  1254.       Handle : Word;
  1255.       Mode : Word;
  1256.       BufSize : Word;
  1257.       private : Word;
  1258.       BufPos : Word;
  1259.       BufEnd : Word;
  1260.       BufPtr : Pbyte_array;
  1261.       OpenFunc,
  1262.       InOutFunc,
  1263.       FlushFunc,
  1264.       CloseFunc : TFDDfunc;
  1265.       S : PFilter;                { This is a TTextFilter or a TLogFilter }
  1266.       SaveData : PSaveText;
  1267.       Next : PStreamTextRec;
  1268.       OtherData : array[13..16] of Byte;
  1269.       Name : array[0..79] of Char;
  1270.       Buffer : array[0..127] of Byte;
  1271.     end;
  1272.  
  1273.   function XLATstatus(var S:TStream):integer;
  1274.   const
  1275.     TextErrors : array[0..6] of integer = (0,5,5,100,101,212,212);
  1276.   var
  1277.     status : integer;
  1278.   begin
  1279.     status := S.status;
  1280.     if (status = stBaseError) or (status = stBase2Error) then
  1281.       status := S.errorinfo;
  1282.     if (-6 <= status) and (status <= 0) then
  1283.       XLATstatus := TextErrors[-status]
  1284.     else
  1285.       XLATstatus := 5;
  1286.   end;
  1287.  
  1288.   function TextIn(var F : Text) : Integer; Far;
  1289.   var
  1290.     savemode : word;
  1291.   begin
  1292.     with StreamTextRec(F), S^ do
  1293.     begin
  1294.       if Status = 0 then
  1295.       begin
  1296.         savemode := mode;
  1297.         mode := fmClosed;               { This stops infinite loop }
  1298.         if GetSize-GetPos > BufSize then
  1299.         begin
  1300.           Read(BufPtr^, BufSize);
  1301.           BufEnd := BufSize;
  1302.         end
  1303.         else
  1304.         begin
  1305.           BufEnd := GetSize-GetPos;
  1306.           if BufEnd > 0 then
  1307.             Read(BufPtr^, BufEnd);
  1308.         end;
  1309.         BufPos := 0;   { 1.3A bug fix }
  1310.         mode := savemode;
  1311.       end;
  1312.       TextIn := XLATStatus(S^);
  1313.     end;
  1314.   end;
  1315.  
  1316.   function TextOut(var F : Text) : Integer; Far;
  1317.   var
  1318.     savemode : word;
  1319.   begin
  1320.     with StreamTextRec(F), S^ do
  1321.     begin
  1322.       if Status = 0 then
  1323.       begin
  1324.         savemode := mode;
  1325.         mode := fmClosed;
  1326.         Write(BufPtr^, BufPos);
  1327.         mode := savemode;
  1328.         BufPos := 0;
  1329.       end;
  1330.       TextOut := XLATStatus(S^);
  1331.     end;
  1332.   end;
  1333.  
  1334.   function TextInFlush(var F : Text) : Integer; Far;
  1335.   begin
  1336.     TextInFlush := 0;           { 1.3A bug fix }
  1337.   end;
  1338.  
  1339.   function TextOutFlush(var F : Text) : Integer; Far;
  1340.   begin
  1341.     TextOutFlush := TextOut(F);
  1342.   end;
  1343.  
  1344.   function TextClose(var F : Text) : Integer; Far;
  1345.   begin
  1346.     with StreamTextRec(F) do
  1347.     begin
  1348.       S^.Flush;
  1349.       TextClose := XLATStatus(S^);
  1350.     end;
  1351.   end;
  1352.  
  1353.   function TextOpen(var F : Text) : Integer; Far;
  1354.   var
  1355.     saveMode : word;
  1356.   begin
  1357.     with StreamTextRec(F) do
  1358.     begin
  1359.       case Mode of
  1360.         fmInOut :
  1361.         begin
  1362.                   Mode := fmClosed;
  1363.                   S^.Seek(S^.GetSize);
  1364.                   Mode := fmOutput;
  1365.         end;
  1366.         fmInput,fmOutput :
  1367.         begin
  1368.                   saveMode := Mode;
  1369.                   Mode := fmClosed;
  1370.                   S^.Seek(0);
  1371.                   Mode := saveMode;
  1372.         end;
  1373.       end;
  1374.       case Mode of
  1375.         fmInput : begin
  1376.                     InOutFunc := TextIn;
  1377.                     FlushFunc := TextInFlush;
  1378.                   end;
  1379.         fmOutput : begin
  1380.                      InOutFunc := TextOut;
  1381.                      FlushFunc := TextOutFlush;
  1382.                    end;
  1383.       end;
  1384.       TextOpen := XLATStatus(S^);
  1385.     end;
  1386.   end;
  1387.  
  1388.   constructor TTextFilter.Init(ABase : PStream; AName : String);
  1389.   begin
  1390.     if not TFilter.Init(ABase) then
  1391.       Fail;
  1392.     TextPtr := nil;
  1393.     AssignStream(TextFile,AName);
  1394.   end;
  1395.  
  1396.   destructor TTextFilter.Done;
  1397.   begin
  1398.     if StreamTextRec(TextPtr^).Mode <> fmClosed then
  1399.       Close(Textptr^);
  1400.     TFilter.Done;
  1401.   end;
  1402.  
  1403.   procedure TTextFilter.AssignStream(var NewText:text;AName:string);
  1404.   begin
  1405.     if (TextPtr <> nil) and (StreamTextRec(TextPtr^).Mode <> fmClosed) then
  1406.       Close(TextPtr^);
  1407.     with StreamTextRec(NewText) do
  1408.     begin
  1409.       Mode := fmClosed;
  1410.       BufSize := SizeOf(Buffer);
  1411.       BufPtr := PByte_Array(@Buffer);
  1412.       OpenFunc := TextOpen;
  1413.       CloseFunc := TextClose;
  1414.       AName := Copy(AName, 1, 79);
  1415.       Move(AName[1], Name, Length(AName));
  1416.       Name[Length(AName)] := #0;
  1417.       S := @Self;
  1418.     end;
  1419.     TextPtr := @NewText;
  1420.   end;
  1421.  
  1422.   function TTextFilter.GetPos : LongInt;
  1423.   var
  1424.     result : longint;
  1425.   begin
  1426.     result := TFilter.GetPos;
  1427.     with StreamTextRec(Textptr^) do
  1428.       case Mode of
  1429.         fmInput  : result := result - (BufEnd - BufPos);
  1430.         fmOutput : result := result + (BufPos);
  1431.       end;
  1432.     GetPos := Result;
  1433.   end;
  1434.  
  1435.   function TTextFilter.GetSize : LongInt;
  1436.   begin
  1437.     if StreamTextRec(Textptr^).Mode <> fmClosed then
  1438.       System.Flush(TextPtr^);
  1439.     GetSize := TFilter.GetSize;
  1440.   end;
  1441.  
  1442.   procedure TTextFilter.Flush;
  1443.   begin
  1444.     with StreamTextRec(TextPtr^) do
  1445.     begin
  1446.       case Mode of
  1447.         fmOutput : system.flush(TextPtr^);
  1448.         fmInput  :
  1449.           begin
  1450.             TFilter.Seek(TFilter.GetPos - BufEnd + BufPos);
  1451.             BufPos := 0;
  1452.             BufEnd := 0;
  1453.           end;
  1454.       end;
  1455.     end;
  1456.     TFilter.Flush;
  1457.   end;
  1458.  
  1459.   procedure TTextFilter.Read(var Buf; Count : Word);
  1460.   begin
  1461.     Flush;
  1462.     TFilter.Read(Buf,Count);
  1463.   end;
  1464.  
  1465.   procedure TTextFilter.Seek(Pos : LongInt);
  1466.   begin
  1467.     Flush;
  1468.     TFilter.Seek(Pos);
  1469.   end;
  1470.  
  1471.   procedure TTextFilter.Truncate;
  1472.   begin
  1473.     Flush;
  1474.     TFilter.Truncate;
  1475.   end;
  1476.  
  1477.   procedure TTextFilter.Write(var Buf; Count : Word);
  1478.   begin
  1479.     Flush;
  1480.     TFilter.Write(Buf,Count);
  1481.   end;
  1482.  
  1483.   function DoOldCall(Func : TFDDfunc; var F : Text) : Integer;
  1484.   var
  1485.     Save : TSaveText;
  1486.   begin
  1487.     if @Func <> nil then
  1488.       with StreamTextRec(F) do
  1489.       begin
  1490.         Move(OpenFunc, Save, SizeOf(TSaveText));
  1491.         Move(SaveData^, OpenFunc, SizeOf(TSaveText)); { Now using old functions }
  1492.         DoOldCall := Func(F);
  1493.         Move(OpenFunc, Save.SaveData^, SizeOf(TSaveText)); { Save any changes }
  1494.         Move(Save, OpenFunc, SizeOf(TSaveText)); { Back to new ones }
  1495.       end;
  1496.   end;
  1497.  
  1498.   function LogIn(var F : Text) : Integer; Far;
  1499.   var
  1500.     Result : Integer;
  1501.   begin
  1502.     with StreamTextRec(F) do
  1503.     begin
  1504.       Result := DoOldCall(SaveData^.InOutFunc, F);
  1505.       if Result = 0 then
  1506.         S^.Write(BufPtr^, BufEnd); { Might want to record errors
  1507.                                                here }
  1508.       LogIn := Result;
  1509.     end;
  1510.   end;
  1511.  
  1512.   function LogOut(var F : Text) : Integer; Far;
  1513.   begin
  1514.     with StreamTextRec(F) do
  1515.     begin
  1516.       S^.Write(BufPtr^, BufPos);
  1517.       LogOut := DoOldCall(SaveData^.InOutFunc, F);
  1518.     end;
  1519.   end;
  1520.  
  1521.   function LogInFlush(var F : Text) : Integer; Far;
  1522.   begin
  1523.     with StreamTextRec(F) do
  1524.       LogInFlush := DoOldCall(SaveData^.FlushFunc, F);
  1525.   end;
  1526.  
  1527.   function LogOutFlush(var F : Text) : Integer; Far;
  1528.   var
  1529.     OldPos : Word;
  1530.   begin
  1531.     with StreamTextRec(F) do
  1532.     begin
  1533.       OldPos := BufPos;
  1534.       LogOutFlush := DoOldCall(SaveData^.FlushFunc, F);
  1535.       if BufPos = 0 then
  1536.         S^.Write(BufPtr^, OldPos);
  1537.     end;
  1538.   end;
  1539.  
  1540.   function LogClose(var F : Text) : Integer; Far;
  1541.   begin
  1542.     with StreamTextRec(F) do
  1543.     begin
  1544.       LogClose := DoOldCall(SaveData^.CloseFunc, F);
  1545.       if not PLogFilter(S)^.Unlog(F) then
  1546.         { Bug! } ;
  1547.     end;
  1548.   end;
  1549.  
  1550.   function LogOpen(var F : Text) : Integer; Far;
  1551.   begin
  1552.     with StreamTextRec(F) do
  1553.     begin
  1554.       LogOpen := DoOldCall(SaveData^.OpenFunc, F);
  1555.       case Mode of
  1556.         fmInOut, fmOutput : begin
  1557.                               InOutFunc := LogOut;
  1558.                               if @FlushFunc <> nil then
  1559.                                 FlushFunc := LogOutFlush;
  1560.                             end;
  1561.         fmInput : begin
  1562.                     InOutFunc := LogIn;
  1563.                     if @FlushFunc <> nil then
  1564.                       FlushFunc := LogInFlush;
  1565.                   end;
  1566.       end;
  1567.     end;
  1568.   end;
  1569.  
  1570.   { ******* TLogFilter methods ******** }
  1571.  
  1572.   constructor TLogFilter.Init(Abase:PStream);
  1573.   begin
  1574.     if not TFilter.init(ABase) then
  1575.       fail;
  1576.     LogList := nil;
  1577.   end;
  1578.  
  1579.   destructor TLogFilter.Done;
  1580.   begin
  1581.     while (LogList <> nil) and Unlog(LogList^) do ;
  1582.     TFilter.Done;
  1583.   end;
  1584.  
  1585.   procedure TLogFilter.Log(var F : Text);
  1586.   var
  1587.     Save : PSaveText;
  1588.     OldOpen : TFDDfunc;
  1589.     Junk : Integer;
  1590.  
  1591.   begin
  1592.     New(Save);
  1593.     with StreamTextRec(F) do
  1594.     begin
  1595.       Move(OpenFunc, Save^, SizeOf(TSaveText)); { Save the original contents }
  1596.       S := @Self;
  1597.       SaveData := Save;
  1598.       Next := PStreamTextRec(LogList);
  1599.       LogList := @F;              { Insert this file into the list of logged files }
  1600.       OldOpen := SaveData^.OpenFunc;
  1601.       Pointer(@SaveData^.OpenFunc) := nil; { Call LogOpen, but don't open. }
  1602.       Junk := LogOpen(F);
  1603.       SaveData^.OpenFunc := OldOpen;
  1604.       CloseFunc := LogClose;
  1605.     end;
  1606.   end;
  1607.  
  1608.   function TLogFilter.Unlog(var F : Text) : Boolean;
  1609.   var
  1610.     Save : PSaveText;
  1611.     Prev : PStreamTextRec;
  1612.   begin
  1613.     Unlog := False;               { Assume failure }
  1614.     with StreamTextRec(F) do
  1615.     begin
  1616.       if S = PFilter(@Self) then
  1617.       begin
  1618.         { First, delete it from the list. }
  1619.         if LogList = @F then
  1620.           LogList := Pointer(Next)
  1621.         else
  1622.         begin
  1623.           Prev := PStreamTextRec(LogList);
  1624.           while (Prev^.Next <> nil) and (Prev^.Next <> PStreamTextRec(@F)) do
  1625.             Prev := Prev^.Next;
  1626.           if Prev^.Next <> PStreamTextRec(@F) then
  1627.             Exit;                 { Couldn't find it in the list!? }
  1628.           Prev^.Next := Next;
  1629.         end;
  1630.         Save := SaveData;
  1631.         Move(Save^, OpenFunc, SizeOf(TSaveText));
  1632.         Dispose(Save);
  1633.         Unlog := True;
  1634.       end;
  1635.     end;
  1636.   end;
  1637.  
  1638. {$ifdef overlays}
  1639.  
  1640.   { ****** Overlay stream code ****** }
  1641.  
  1642. type
  1643.   { This is the structure at the start of each "thunk" segment }
  1644.   POvrhead = ^TOvrhead;
  1645.   TOvrhead = record
  1646.                Signature : Word;  { CD 3F  - INT 3F call used on returns }
  1647.                Ret_Ofs : Word;    { The offset to jump to when a return triggers a
  1648.                             reload }
  1649.                Offset : LongInt;  { The offset to the segment in the .OVR file }
  1650.                Code_Bytes,        { Size of the code image }
  1651.                Reloc_Bytes,       { Number of relocation fixups times 2 }
  1652.                Entry_Count,       { The number of entry points }
  1653.                NextSeg,           { Next overlay segment - add prefixseg + $10 to find
  1654.                             thunks.  List starts with System.ovrcodelist. }
  1655.                LoadSeg,           { The segment at which the overlay is loaded, or 0 }
  1656.                Reprieve,          { Set to 1 to if overlay used while on probation }
  1657.                NextLoaded : Word; { The segment of the next loaded overlay.  List starts
  1658.                             with System.ovrloadlist.  Updated *after* call to
  1659.                             ovrreadbuf. }
  1660.                case Integer of
  1661.                  1 : (EMSPage,    { The EMS page where this overlay is stored }
  1662.                       EMSOffset : Word); { The offset within the EMS page }
  1663.                  2 : (S : PStream; { The stream holding this segment's code }
  1664.                       Soffset : LongInt); { The offset within S }
  1665.              end;
  1666.  
  1667. var
  1668.   OldReadFunc : OvrReadFunc;
  1669.   OvrOldExitProc : Pointer;
  1670.   OvrStream : PStream;
  1671. const
  1672.   OvrStreamInstalled : Boolean = False;
  1673.   OvrExitHandler : Boolean = False;
  1674.  
  1675.   function OvrPtr(Seg : Word) : POvrhead;
  1676. { Convert map style segment number, as used by overlay manager, to
  1677.   pointer }
  1678.   begin
  1679.     OvrPtr := Ptr(Seg+PrefixSeg+$10, 0);
  1680.   end;
  1681.  
  1682.   function StdPtr(Seg : Word) : POvrhead;
  1683.     { Convert straight segment number to a pointer }
  1684.   begin
  1685.     StdPtr := Ptr(Seg, 0);
  1686.   end;
  1687.  
  1688.   function NewReadFunc(OvrSeg : Word) : Integer; Far;
  1689.   var
  1690.     Result : Integer;
  1691.   begin
  1692.     with StdPtr(OvrSeg)^ do
  1693.     begin
  1694.       if S = nil then
  1695.       begin                       { Segment not yet loaded }
  1696.         Result := OldReadFunc(OvrSeg);
  1697.         if Result = 0 then
  1698.         begin
  1699.           { Now copy the loaded code to our stream }
  1700.           Soffset := OvrStream^.GetSize;
  1701.           OvrStream^.Seek(Soffset);
  1702.           OvrStream^.Write(Ptr(LoadSeg, 0)^, Code_Bytes);
  1703.           Result := OvrStream^.Status;
  1704.           if Result = stOK then
  1705.             S := OvrStream
  1706.           else
  1707.             OvrStream^.Reset;     { Something failed; hope we haven't messed
  1708.                               up the stream too much }
  1709.         end;
  1710.       end
  1711.       else
  1712.       begin                       { Segment has been loaded into the stream }
  1713.         S^.Seek(Soffset);
  1714.         S^.Read(Ptr(LoadSeg, 0)^, Code_Bytes);
  1715.         Result := S^.Status;
  1716.         if Result <> stOK then
  1717.         begin
  1718.           S^.Reset;               { Fix the stream, and try a standard load }
  1719.           Result := OldReadFunc(OvrSeg);
  1720.         end;
  1721.       end;
  1722.     end;
  1723.     NewReadFunc := Result;
  1724.   end;
  1725.  
  1726.   procedure OvrExitProc; Far;
  1727. { Installed exit procedure; disposes of any streams that are still
  1728.   handling overlays. }
  1729.   begin
  1730.     ExitProc := OvrOldExitProc;
  1731.     OvrDisposeStreams;
  1732.   end;
  1733.  
  1734.   procedure OvrInitStream(S : PStream);
  1735.   begin
  1736.     if not OvrStreamInstalled then
  1737.     begin
  1738.       OldReadFunc := OvrReadBuf;  { Install our reader function }
  1739.       OvrReadBuf := NewReadFunc;
  1740.       OvrStreamInstalled := True;
  1741.     end;
  1742.     if not OvrExitHandler then
  1743.     begin
  1744.       OvrOldExitProc := ExitProc;
  1745.       ExitProc := @OvrExitProc;
  1746.       OvrExitHandler := True;
  1747.     end;
  1748.     OvrStream := S;               { And set stream to use }
  1749.   end;
  1750.  
  1751.   procedure OvrDetachStream(BadS : PStream);
  1752.   var
  1753.     OvrSeg : Word;
  1754.   begin
  1755.     if OvrStreamInstalled then
  1756.     begin
  1757.       if OvrStream = BadS then
  1758.         OvrStream := nil;         { Detach default stream }
  1759.       OvrSeg := OvrCodeList;
  1760.       while OvrSeg <> 0 do        { Walk the overlay list }
  1761.         with OvrPtr(OvrSeg)^ do
  1762.         begin
  1763.           if S <> nil then
  1764.           begin
  1765.             if S <> BadS then
  1766.             begin
  1767.               if OvrStream = nil then
  1768.                 OvrStream := S;   { Set default stream to first found }
  1769.             end
  1770.             else
  1771.               S := nil;           { Blank out BadS references }
  1772.           end;
  1773.           OvrSeg := NextSeg;
  1774.         end;
  1775.       if OvrStream = nil then
  1776.       begin
  1777.         OvrStreamInstalled := False; { If we don't have a stream, better
  1778.                                           uninstall. }
  1779.         OvrReadBuf := OldReadFunc;
  1780.       end;
  1781.     end;
  1782.   end;
  1783.  
  1784.   procedure OvrDisposeStreams;
  1785.   var
  1786.     S : PStream;
  1787.   begin
  1788.     while OvrStreamInstalled and (OvrStream <> nil) do
  1789.     begin
  1790.       S := OvrStream;
  1791.       OvrDetachStream(S);
  1792.       Dispose(S, Done);
  1793.     end;
  1794.   end;
  1795.  
  1796.   function OvrSizeNeeded : LongInt;
  1797.   var
  1798.     OvrSeg : Word;
  1799.     Result : LongInt;
  1800.   begin
  1801.     OvrSeg := OvrCodeList;
  1802.     Result := 0;
  1803.     while OvrSeg <> 0 do          { Walk the overlay list }
  1804.       with OvrPtr(OvrSeg)^ do
  1805.       begin
  1806.         if S = nil then
  1807.           Inc(Result, Code_Bytes);
  1808.         OvrSeg := NextSeg;
  1809.       end;
  1810.     OvrSizeNeeded := Result;
  1811.   end;
  1812.  
  1813.   function OvrLoadAll : Boolean;
  1814.   var
  1815.     OvrSeg : Word;
  1816.     Junk : Integer;
  1817.   begin
  1818.     if not OvrStreamInstalled then
  1819.       OvrLoadAll := False
  1820.     else
  1821.     begin
  1822.       OvrClearBuf;
  1823.       OvrSeg := OvrCodeList;
  1824.       while OvrSeg <> 0 do        { Walk the overlay list }
  1825.         with OvrPtr(OvrSeg)^ do
  1826.         begin
  1827.           if S = nil then
  1828.           begin
  1829.             LoadSeg := OvrHeapOrg; { load at start of overlay buffer }
  1830.             Junk := NewReadFunc(OvrSeg+PrefixSeg+$10);
  1831.             LoadSeg := 0;         { Don't really want it loaded yet }
  1832.           end;
  1833.           OvrSeg := NextSeg;
  1834.         end;
  1835.       OvrLoadAll := OvrStream^.Status = stOK;
  1836.     end;
  1837.   end;
  1838.  
  1839.   {$endif windows}
  1840.  
  1841.   { ****** Bit filter code ****** }
  1842.  
  1843.   constructor TBitFilter.Init(ABase : PStream);
  1844.   begin
  1845.     TFilter.Init(ABase);
  1846.     BitPos := 0;
  1847.     AtEnd := false;
  1848.   end;
  1849.  
  1850.   procedure TBitFilter.PrepareBuffer(ForRead : Boolean);
  1851.   begin
  1852.     if BitPos = 8 then            { Buffer full on write }
  1853.     begin
  1854.       Base^.Write(Buffer, 1);
  1855.       BitPos := 0;
  1856.     end;
  1857.     if BitPos = 0 then            { Buffer empty }
  1858.     begin
  1859.       if not AtEnd then
  1860.       begin
  1861.         if not ForRead then
  1862.           AtEnd := (Base^.GetPos >= Base^.GetSize);
  1863.         if (not AtEnd) or ForRead then
  1864.         begin
  1865.           Base^.Read(Buffer,1);
  1866.           BitPos := -8
  1867.         end;
  1868.       end;
  1869.       if AtEnd then
  1870.         Buffer := 0;
  1871.       Mask := 1;
  1872.     end;
  1873.     if (not ForRead) and (BitPos < 0) then
  1874.     begin
  1875.       Base^.Seek(Base^.GetPos-1);
  1876.       Inc(BitPos, 8);
  1877.       AtEnd := false;
  1878.     end;
  1879.   end;
  1880.  
  1881.   function TBitFilter.GetBit : TBit;
  1882.   begin
  1883.     if CheckStatus then
  1884.     begin
  1885.       PrepareBuffer(True);
  1886.       GetBit := TBit((Buffer and Mask) > 0);
  1887.       Mask := Mask shl 1;
  1888.       Inc(BitPos);
  1889.       CheckBase;
  1890.     end;
  1891.   end;
  1892.  
  1893.   function TBitFilter.GetBits(Count : Byte) : LongInt;
  1894.   var
  1895.     Result : LongInt;
  1896.   begin
  1897.     Result := 0;
  1898.     ReadBits(Result, Count);
  1899.     GetBits := Result;
  1900.   end;
  1901.  
  1902.   procedure TBitFilter.PutBit(ABit : TBit);
  1903.   begin
  1904.     if CheckStatus then
  1905.     begin
  1906.       PrepareBuffer(False);
  1907.       if ABit = 1 then
  1908.         Buffer := Buffer or Mask;
  1909.       Mask := Mask shl 1;
  1910.       Inc(BitPos);
  1911.     end;
  1912.   end;
  1913.  
  1914.   procedure TBitFilter.PutBits(ABits : LongInt; Count : Byte);
  1915.   begin
  1916.     WriteBits(ABits, Count);
  1917.   end;
  1918.  
  1919.   procedure TBitFilter.ReadBits(var Buf; Count : LongInt);
  1920.   var
  1921.     w : Word;
  1922.     b : array[1..2] of Byte absolute w;
  1923.     bBuf : TByte_Array absolute Buf;
  1924.     i, Bytes : Word;
  1925.     Shift : Word;
  1926.   begin
  1927.     if (Count > 0) and CheckStatus then
  1928.     begin
  1929.       PrepareBuffer(True);
  1930.       if BitPos > 0 then
  1931.       begin
  1932.         Base^.Write(Buffer, 1);
  1933.         Dec(BitPos, 8);
  1934.       end;
  1935.       Shift := BitPos+8;          { the number of bits to shift by }
  1936.       Bytes := (Count+Shift-1) div 8; { Count of whole bytes to read }
  1937.       if Bytes > 0 then
  1938.       begin
  1939.         TFilter.Read(Buf, Bytes);
  1940.         b[1] := Buffer;
  1941.         for i := 0 to Pred(Bytes) do
  1942.         begin
  1943.           b[2] := bBuf[i];
  1944.           w := w shr Shift;
  1945.           bBuf[i] := b[1];
  1946.           w := w shr (8-Shift);
  1947.         end;
  1948.         Buffer := b[1];
  1949.       end;
  1950.       { Now fix up the last few bits }
  1951.       Dec(Count, 8*LongInt(Bytes));
  1952.       if Count > 0 then
  1953.         bBuf[Bytes] := (Buffer shr Shift) and not($FF shl Count)
  1954.       else
  1955.         if Count < 0 then
  1956.           bBuf[Bytes-1] := bBuf[Bytes-1] and not($FF shl (8+Count));
  1957.       BitPos := BitPos+Count;
  1958.       Mask := 1 shl (BitPos+8);
  1959.     end;
  1960.   end;
  1961.  
  1962.   procedure TBitFilter.WriteBits(var Buf; Count : LongInt);
  1963.   var
  1964.     w : Word;
  1965.     b : array[1..2] of Byte absolute w;
  1966.     bBuf : TByte_Array absolute Buf;
  1967.     i, Bytes : Word;
  1968.     Shift : Word;
  1969.     SaveBuf : Byte;
  1970.     SavePos : ShortInt;
  1971.   begin
  1972.     if CheckStatus then
  1973.     begin
  1974.       PrepareBuffer(False);
  1975.       Bytes := (Count+BitPos-1) div 8; { Count of whole bytes to write }
  1976.       Shift := 8-BitPos;
  1977.       if Bytes > 0 then
  1978.       begin
  1979.         if Shift < 8 then
  1980.         begin
  1981.           b[1] := Buffer shl Shift;
  1982.           for i := 0 to Pred(Bytes) do
  1983.           begin
  1984.             b[2] := bBuf[i];
  1985.             w := w shr Shift;
  1986.             Base^.Write(b[1], 1);
  1987.             w := w shr (8-Shift);
  1988.           end;
  1989.           Buffer := b[1] shr Shift;
  1990.         end
  1991.         else
  1992.           Base^.Write(Buf, Bytes);
  1993.       end;
  1994.       Dec(Count, 8*LongInt(Bytes));
  1995.       if Count > 0 then
  1996.         Buffer := (Buffer or (bBuf[Bytes] shl (8-Shift)));
  1997.       BitPos := BitPos+Count;
  1998.       if BitPos > 0 then          { Fill in upper part of buffer }
  1999.       begin
  2000.         SaveBuf := Buffer;
  2001.         SavePos := BitPos;
  2002.         BitPos := 0;              { signal empty buffer }
  2003.         PrepareBuffer(False);     { and load it }
  2004.         Buffer := (Buffer and ($FF shl SavePos)) { old part }
  2005.                   or (SaveBuf and not($FF shl SavePos)); { new part }
  2006.         BitPos := SavePos;
  2007.       end;
  2008.       Mask := 1 shl BitPos;
  2009.       CheckBase;
  2010.     end;
  2011.   end;
  2012.  
  2013.   procedure TBitFilter.Flush;
  2014.   begin
  2015.     if CheckStatus then
  2016.     begin
  2017.       if BitPos > 0 then
  2018.         Base^.Write(Buffer, 1);
  2019.       Dec(BitPos, 8);
  2020.       AtEnd := false;
  2021.       CheckBase;
  2022.     end;
  2023.   end;
  2024.  
  2025.   procedure TBitFilter.Seek(Pos : LongInt);
  2026.   begin
  2027.     if CheckStatus then
  2028.     begin
  2029.       Flush;
  2030.       TFilter.Seek(Pos);
  2031.       BitPos := 0;
  2032.       AtEnd := false;
  2033.     end;
  2034.   end;
  2035.  
  2036.   procedure TBitFilter.Read(var Buf; Count : Word);
  2037.   begin
  2038.     ReadBits(Buf, 8*LongInt(Count));
  2039.   end;
  2040.  
  2041.   procedure TBitFilter.Write(var Buf; Count : Word);
  2042.   begin
  2043.     WriteBits(Buf, 8*LongInt(Count));
  2044.   end;
  2045.  
  2046.   procedure TBitFilter.SeekBit(Pos : LongInt);
  2047.   var
  2048.     i : Byte;
  2049.     b : TBit;
  2050.   begin
  2051.     if CheckStatus then
  2052.     begin
  2053.       Seek(Pos div 8);
  2054.       for i := 1 to (Pos and 7) do
  2055.         b := GetBit;
  2056.     end;
  2057.   end;
  2058.  
  2059.   function TBitFilter.GetBitPos : LongInt;
  2060.   begin
  2061.     GetBitPos := 8*TFilter.GetPos+BitPos;  { Need TFilter override in
  2062.                                              case descendants override
  2063.                                              GetPos }
  2064.   end;
  2065.  
  2066.   procedure TBitFilter.CopyBits(var S : TBitFilter; Count : LongInt);
  2067.   var
  2068.     localbuf : array[1..256] of Byte;
  2069.   begin
  2070.     while Count > 2048 do
  2071.     begin
  2072.       S.ReadBits(localbuf, 2048);
  2073.       WriteBits(localbuf, 2048);
  2074.       Dec(Count, 2048);
  2075.     end;
  2076.     if Count > 0 then
  2077.     begin
  2078.       S.ReadBits(localbuf, Count);
  2079.       WriteBits(localbuf, Count);
  2080.     end;
  2081.   end;
  2082.  
  2083.   procedure TBitFilter.ByteAlign;
  2084.   begin
  2085.     SeekBit((GetBitPos+7) and $FFFFFFF8);
  2086.   end;
  2087.  
  2088.   { ****** Duplicate filter code ****** }
  2089.  
  2090.   constructor TDupFilter.Init(ABase, ABase2 : PStream);
  2091.   { Initialize the filter with the given bases. }
  2092.   begin
  2093.     if not TFilter.Init(Abase) then
  2094.       fail;
  2095.     Base2 := ABase2;
  2096.     CheckBase2;
  2097.     if Status = stOK then
  2098.       Startofs2 := Base2^.GetPos;
  2099.   end;
  2100.  
  2101.   destructor TDupFilter.Done;
  2102.   { Flush filter, then dispose of both bases. }
  2103.   begin
  2104.     Flush;
  2105.     if Base2 <> nil then
  2106.       Dispose(Base2,done);
  2107.     TFilter.Done;
  2108.   end;
  2109.  
  2110.   function TDupFilter.MisMatch(var buf1,buf2;count:word):word;
  2111.   var
  2112.     i : word;
  2113.     bbuf1 : TByte_Array absolute buf1;
  2114.     bbuf2 : TByte_Array absolute buf2;
  2115.   begin
  2116.     for i := 0 to pred(count) do
  2117.       if bbuf1[i] <> bbuf2[i] then
  2118.       begin
  2119.         MisMatch := succ(i);
  2120.         exit;
  2121.       end;
  2122.     MisMatch := 0;
  2123.   end;
  2124.  
  2125.   procedure TDupFilter.Read(var Buf; Count : Word);
  2126.   var
  2127.     bpos : word;
  2128.     localbuf : array[0..255] of byte;
  2129.  
  2130.     procedure CompareBuffer(size:word);
  2131.     var
  2132.       epos : word;
  2133.       bbuf : TByte_Array absolute Buf;
  2134.     begin
  2135.       Base2^.Read(localbuf,size);
  2136.       dec(count,size);
  2137.       CheckBase2;
  2138.       if status = stOK then
  2139.       begin
  2140.         epos := MisMatch(bbuf[bpos],localbuf,size);
  2141.         if epos <> 0 then
  2142.           Error(stMismatch,bpos+epos);
  2143.       end;
  2144.       inc(bpos,size);
  2145.     end;
  2146.  
  2147.   begin
  2148.     TFilter.Read(buf, Count);
  2149.     bpos := 0;
  2150.     While (Status = stOK) and (Count >= sizeof(localbuf)) do
  2151.       CompareBuffer(Sizeof(localbuf));
  2152.     If (Status = stOK) and (Count > 0) then
  2153.       CompareBuffer(Count);
  2154.     { Be sure the bases are synchronized }
  2155.     Base2^.Seek(GetPos+StartOfs2);
  2156.   end;
  2157.  
  2158.   procedure TDupFilter.Seek(Pos : LongInt);
  2159.   begin
  2160.     TFilter.Seek(Pos);
  2161.     if Status = stOK then
  2162.     begin
  2163.       base2^.Seek(pos+startofs2);
  2164.       CheckBase2;
  2165.     end;
  2166.   end;
  2167.  
  2168.   procedure TDupFilter.Truncate;
  2169.   begin
  2170.     TFilter.Truncate;
  2171.     if Status = stOK then
  2172.     begin
  2173.       base2^.truncate;
  2174.       CheckBase2;
  2175.     end;
  2176.   end;
  2177.  
  2178.   procedure TDupFilter.Write(var Buf; Count : Word);
  2179.   begin
  2180.     TFilter.Write(buf,Count);
  2181.     if Status = stOK then
  2182.     begin
  2183.       Base2^.write(buf,Count);
  2184.       CheckBase2;
  2185.     end;
  2186.   end;
  2187.  
  2188.   procedure TDupFilter.Flush;
  2189.   begin
  2190.     TFilter.Flush;
  2191.     if Status = stOK then
  2192.     begin
  2193.       base2^.flush;
  2194.       CheckBase2;
  2195.     end;
  2196.   end;
  2197.  
  2198.   function TDupFilter.CheckStatus : Boolean;
  2199.   begin
  2200.     if TFilter.CheckStatus then
  2201.       if Base2^.Status <> stOK then
  2202.         Base2^.Reset;
  2203.     CheckStatus := Status = stOK;
  2204.   end;
  2205.  
  2206.   procedure TDupFilter.CheckBase2;
  2207.   begin
  2208.     if Base2^.status <> stOk then
  2209.       Error(stBase2Error,Base2^.status);
  2210.   end;
  2211.  
  2212.   { ****** Concatenating Filter code ****** }
  2213.  
  2214.   constructor TConcatFilter.Init(ABase, ABase2 : PStream);
  2215.   { Initialize the filter with the given bases. }
  2216.   begin
  2217.     if not TFilter.Init(ABase) then
  2218.       fail;
  2219.     Base2 := ABase2;
  2220.     CheckBase2;
  2221.     Base1Size := TFilter.GetSize;
  2222.     if Status = stOK then
  2223.       StartOfs2 := Base2^.GetPos;
  2224.     Position := Base1Size;
  2225.   end;
  2226.  
  2227.   destructor TConcatFilter.done;
  2228.   begin
  2229.     Flush;
  2230.     if Base2 <> nil then
  2231.       Dispose(Base2,done);
  2232.     if Base <> nil then
  2233.       Dispose(Base,Done);   { Can't call TFilter.Done!!!! }
  2234.     TStream.done;
  2235.   end;
  2236.  
  2237.   function TConcatFilter.GetPos:longint;
  2238.   begin
  2239.     GetPos := Position;
  2240.   end;
  2241.  
  2242.   function TConcatFilter.GetSize:longint;
  2243.   begin
  2244.     if CheckStatus then
  2245.     begin
  2246.       GetSize := Base1Size + Base2^.GetSize;
  2247.       CheckBase2;
  2248.     end;
  2249.   end;
  2250.  
  2251.   procedure TConcatFilter.Read(var Buf; Count : Word);
  2252.   var
  2253.     Buffer : TByte_array absolute Buf;
  2254.     base1part : word;
  2255.   begin
  2256.     { First read the Base 1 portion }
  2257.     if Position < Base1Size then
  2258.     begin
  2259.       base1part := Count;
  2260.       if Position+base1part > Base1Size then
  2261.         base1part := Base1Size - Position;
  2262.       TFilter.Read(Buf, base1part);
  2263.       dec(Count,base1part);
  2264.       inc(Position,Base1part);
  2265.       if Count > 0 then
  2266.         Base2^.Seek(StartOfs2);   { Be sure Base2 agrees with Pos now }
  2267.     end
  2268.     else
  2269.       base1part := 0;
  2270.     { Now read the Base 2 portion }
  2271.     if (Count > 0) and (status = stOK) then
  2272.     begin
  2273.       if Position = Base1Size then
  2274.         Base2^.Seek(StartOfs2);
  2275.       Base2^.Read(Buffer[base1part],Count);
  2276.       CheckBase2;
  2277.       inc(Position,count);
  2278.     end;
  2279.   end;
  2280.  
  2281.   procedure TConcatFilter.Seek(Pos : LongInt);
  2282.   begin
  2283.     if Pos < Base1Size then
  2284.       TFilter.Seek(Pos)
  2285.     else
  2286.     begin
  2287.       if CheckStatus then
  2288.       begin
  2289.         Base2^.Seek(Pos-Base1Size+StartOfs2);
  2290.         CheckBase2;
  2291.       end;
  2292.     end;
  2293.     if Status = stOK then
  2294.       Position := Pos;
  2295.   end;
  2296.  
  2297.   procedure TConcatFilter.Truncate;
  2298.   begin
  2299.     if Position < Base1Size then
  2300.       Error(stUnsupported,0)     { We don't allow Base to be truncated, only
  2301.                                    Base2 }
  2302.     else
  2303.       if CheckStatus then
  2304.       begin
  2305.         Base2^.Truncate;
  2306.         CheckBase2;
  2307.       end;
  2308.   end;
  2309.  
  2310.   procedure TConcatFilter.Write(var Buf; Count : Word);
  2311.   var
  2312.     Buffer : TByte_array absolute Buf;
  2313.     base1part : word;
  2314.   begin
  2315.     { First write the Base 1 portion }
  2316.     if Position < Base1Size then
  2317.     begin
  2318.       base1part := Count;
  2319.       if Position+base1part > Base1Size then
  2320.         base1part := Base1Size - Position;
  2321.       TFilter.Write(Buf, base1part);
  2322.       dec(Count,base1part);
  2323.       inc(Position,Base1part);
  2324.       if Count > 0 then
  2325.         Base2^.Seek(StartOfs2);   { Be sure Base2 agrees with Pos now }
  2326.     end
  2327.     else
  2328.       base1part := 0;
  2329.     { Now write the Base 2 portion }
  2330.     if (Count > 0) and (status = stOK) then
  2331.     begin
  2332.       Base2^.Write(Buffer[base1part],Count);
  2333.       CheckBase2;
  2334.       inc(Position,count);
  2335.     end;
  2336.   end;
  2337.  
  2338.   procedure TConcatFilter.Flush;
  2339.   begin
  2340.     TFilter.Flush;
  2341.     if status = stOK then
  2342.     begin
  2343.       Base2^.Flush;
  2344.       CheckBase2;
  2345.     end;
  2346.   end;
  2347.  
  2348.   function TConcatFilter.CheckStatus : Boolean;
  2349.   begin
  2350.     if TFilter.CheckStatus then
  2351.       if Base2^.Status <> stOK then
  2352.         Base2^.Reset;
  2353.     CheckStatus := Status = stOK;
  2354.   end;
  2355.  
  2356.   procedure TConcatFilter.CheckBase2;
  2357.   begin
  2358.     if Base2^.status <> stOk then
  2359.       Error(stBase2Error,Base2^.status);
  2360.   end;
  2361.  
  2362.   { ****** Limit Filter code *****}
  2363.  
  2364.   constructor TLimitFilter.init(ABase:PStream;ALoLimit,AHiLimit:longint);
  2365.   { Does the usual init, sets the limits, then does a Seek to ALoLimit
  2366.     if it is non-zero. }
  2367.   begin
  2368.     if not TFilter.Init(ABase) then
  2369.       fail;
  2370.     LoLimit := ALoLimit;
  2371.     HiLimit := AHiLimit;
  2372.     if ALoLimit <> 0 then
  2373.       Seek(ALoLimit);
  2374.   end;
  2375.  
  2376.   procedure TLimitFilter.Read(var Buf; Count : Word);
  2377.   begin
  2378.     if status = stOk then
  2379.     begin
  2380.       if GetPos + Count > HiLimit then
  2381.       begin
  2382.         Error(stReadError,0);
  2383.         Fillchar(Buf,Count,0);
  2384.       end
  2385.       else
  2386.         TFilter.Read(Buf,Count);
  2387.     end;
  2388.   end;
  2389.  
  2390.   procedure TLimitFilter.Seek(Pos : LongInt);
  2391.   begin
  2392.     if Status = stOK then
  2393.     begin
  2394.       if (Pos < LoLimit) or (Pos > HiLimit) then
  2395.         Error(stReadError,0)
  2396.       else
  2397.         TFilter.Seek(Pos);
  2398.     end;
  2399.   end;
  2400.  
  2401.   procedure TLimitFilter.Write(var Buf; Count : Word);
  2402.   begin
  2403.     if Status = stOk then
  2404.     begin
  2405.       if GetPos + Count > HiLimit then
  2406.         Error(stWriteError,0)
  2407.       else
  2408.         TFilter.Write(Buf,Count);
  2409.     end;
  2410.   end;
  2411.  
  2412.   function TLimitFilter.GetSize:longint;
  2413.   var
  2414.     result : longint;
  2415.   begin
  2416.     result := TFilter.GetSize;
  2417.     if result > HiLimit then
  2418.       GetSize := HiLimit
  2419.     else
  2420.       GetSize := result;
  2421.   end;
  2422.  
  2423.   { ****** Loop Filter code *****}
  2424.  
  2425.   procedure TLoopFilter.Read(var Buf; Count : Word);
  2426.   var
  2427.     buffer : TByte_Array absolute Buf;
  2428.     pos : word;
  2429.   begin
  2430.     if status = stOk then
  2431.     begin
  2432.       if GetPos + Count > HiLimit then
  2433.       begin
  2434.         pos := HiLimit - GetPos;
  2435.         TFilter.Read(Buf,pos);
  2436.         dec(count,pos);
  2437.         TFilter.Seek(LoLimit);
  2438.         Read(Buffer[pos],Count);  { Recursive call! }
  2439.       end
  2440.       else
  2441.         Tfilter.Read(Buf,Count);
  2442.     end;
  2443.   end;
  2444.  
  2445.   procedure TLoopFilter.Seek(Pos : LongInt);
  2446.   var
  2447.     size : longint;
  2448.   begin
  2449.     size := HiLimit - LoLimit;
  2450.     if Pos < LoLimit then
  2451.       Pos := LoLimit + (Pos - LoLimit) mod Size + Size;
  2452.     TFilter.Seek(LoLimit + (Pos - LoLimit) mod Size);
  2453.   end;
  2454.  
  2455.   procedure TLoopFilter.Write(var Buf; Count : Word);
  2456.   var
  2457.     buffer : TByte_Array absolute Buf;
  2458.     pos : word;
  2459.   begin
  2460.     if status = stOk then
  2461.     begin
  2462.       if GetPos + Count > HiLimit then
  2463.       begin
  2464.         pos := HiLimit - GetPos;
  2465.         TFilter.Write(Buf,pos);
  2466.         dec(count,pos);
  2467.         TFilter.Seek(LoLimit);
  2468.         Write(Buffer[pos],Count);  { Recursive call! }
  2469.       end
  2470.       else
  2471.         Tfilter.Write(Buf,Count);
  2472.     end;
  2473.   end;
  2474.  
  2475.   function TLoopFilter.GetSize:longint;
  2476.   var
  2477.     result : longint;
  2478.   begin
  2479.     result := TFilter.GetSize;
  2480.     if result > HiLimit then
  2481.       GetSize := HiLimit - LoLimit
  2482.     else
  2483.       GetSize := result - LoLimit;
  2484.   end;
  2485.  
  2486.   { ****** TReverseFilter code ******}
  2487.  
  2488.   constructor TReverseFilter.Init(ABase : PStream; AReverseBlocks:boolean);
  2489.   begin
  2490.     TFilter.Init(ABase);
  2491.     ReverseBlocks := AReverseBlocks;
  2492.   end;
  2493.  
  2494.   function TReverseFilter.GetPos:longint;
  2495.   begin
  2496.     GetPos := TFilter.GetSize-TFilter.GetPos;
  2497.   end;
  2498.  
  2499.   procedure TReverseFilter.Read(var Buf;Count : word);
  2500.   var
  2501.     curpos : longint;
  2502.   begin
  2503.     curpos := TFilter.GetPos;   { We call the Tfilter methods to propagate errors }
  2504.     Base^.Seek(curpos-Count);
  2505.     Base^.Read(Buf,Count);
  2506.     if ReverseBlocks then
  2507.       ReverseBytes(Buf,Count);
  2508.     TFilter.Seek(curpos-Count);
  2509.   end;
  2510.  
  2511.   procedure TReverseFilter.Write(var Buf;Count : word);
  2512.   var
  2513.     curpos : longint;
  2514.   begin
  2515.     curpos := TFilter.GetPos;   { We call the Tfilter methods to propagate errors }
  2516.     Base^.Seek(curpos-Count);
  2517.     if ReverseBlocks then
  2518.       ReverseBytes(Buf,Count);
  2519.     Base^.Write(Buf,Count);
  2520.     if ReverseBlocks then
  2521.       ReverseBytes(Buf,Count);
  2522.     TFilter.Seek(curpos-Count);
  2523.   end;
  2524.  
  2525.   procedure TReverseFilter.Seek(Pos:Longint);
  2526.   begin
  2527.     TFilter.Seek(TFilter.GetSize-Pos);
  2528.   end;
  2529.  
  2530.   procedure TReverseFilter.Truncate;
  2531.   begin
  2532.     Error(stUnsupported,0);
  2533.   end;
  2534.  
  2535.   procedure ReverseBytes(var Buf; Count:Word);
  2536.   var
  2537.     buffer : TByte_Array absolute Buf;
  2538.     i,j : word;
  2539.     t : byte;
  2540.   begin
  2541.     if Count > 1 then
  2542.     begin
  2543.       j := Count;
  2544.       for i:=0 to (Count div 2) - 1 do
  2545.       begin
  2546.         t := buffer[i];
  2547.         buffer[i] := buffer[j];
  2548.         buffer[j] := t;
  2549.         dec(j);
  2550.       end;
  2551.     end;
  2552.   end;
  2553.  
  2554.   { ****** Checksum/CRC code ******}
  2555.  
  2556.   Function UpdateChksum(initsum:word; var Inbuf; inlen:word):word;
  2557.   var
  2558.     i : word;
  2559.     bbuf : TByte_Array absolute inbuf;
  2560.   begin
  2561.     for i:=0 to pred(inlen) do
  2562.       inc(initsum,bbuf[i]);
  2563.     UpdateChksum := initsum;
  2564.   end;
  2565.  
  2566. { From the original CRC.PAS: }
  2567.  
  2568. { This unit provides three speed-optimized functions to compute (or continue
  2569.   computation of) a Cyclic Redundency Check (CRC).  These routines are
  2570.   contributed to the public domain (with the limitations noted by the
  2571.   original authors in the TASM sources).
  2572.  
  2573.   Each function takes three parameters:
  2574.  
  2575.   InitCRC - The initial CRC value.  This may be the recommended initialization
  2576.   value if this is the first or only block to be checked, or this may be
  2577.   a previously computed CRC value if this is a continuation.
  2578.  
  2579.   InBuf - An untyped parameter specifying the beginning of the memory area
  2580.   to be checked.
  2581.  
  2582.   InLen - A word indicating the length of the memory area to be checked.  If
  2583.   InLen is zero, the function returns the value of InitCRC.
  2584.  
  2585.   The function result is the updated CRC.  The input buffer is scanned under
  2586.   the limitations of the 8086 segmented architecture, so the result will be
  2587.   in error if InLen > 64k - Offset(InBuf).
  2588.  
  2589.   These conversions were done on 10-29-89 by:
  2590.  
  2591.   Edwin T. Floyd [76067,747]
  2592.   #9 Adams Park Court
  2593.   Columbus, GA 31909
  2594.   (404) 576-3305 (work)
  2595.   (404) 322-0076 (home)
  2596. }
  2597.  
  2598. Function UpdateCRC16(InitCRC : Word; Var InBuf; InLen : Word) : Word;
  2599.   external; {$L crc16.obj}
  2600. { I believe this is the CRC used by the XModem protocol.  The transmitting
  2601.   end should initialize with zero, UpdateCRC16 for the block, Continue the
  2602.   UpdateCRC16 for two nulls, and append the result (hi order byte first) to
  2603.   the transmitted block.  The receiver should initialize with zero and
  2604.   UpdateCRC16 for the received block including the two byte CRC.  The
  2605.   result will be zero (why?) if there were no transmission errors.  (I have
  2606.   not tested this function with an actual XModem implementation, though I
  2607.   did verify the behavior just described.  See TESTCRC.PAS.) }
  2608.  
  2609.  
  2610. Function UpdateCRCArc(InitCRC : Word; Var InBuf; InLen : Word) : Word;
  2611.   external; {$L crcarc.obj}
  2612. { This function computes the CRC used by SEA's ARC utility.  Initialize
  2613.   with zero. }
  2614.  
  2615. Function UpdateCRC32(InitCRC : LongInt; Var InBuf; InLen : Word) : LongInt;
  2616.   external; {$L crc32.obj}
  2617. { This function computes the CRC used by PKZIP and Forsberg's ZModem.
  2618.   Initialize with high-values ($FFFFFFFF), and finish by inverting all bits
  2619.   (Not). }
  2620.  
  2621.   { ****** Sequential filter code ****** }
  2622.  
  2623.   procedure TSequential.Seek(pos:longint);
  2624.   begin
  2625.     Error(stUnsupported,0);
  2626.   end;
  2627.  
  2628.   { ****** Chksum filter code ******}
  2629.  
  2630.   constructor TChkSumFilter.init(ABase:PStream; AChksum:word);
  2631.   begin
  2632.     if not TSequential.init(ABase) then
  2633.       fail;
  2634.     Chksum := AChksum;
  2635.   end;
  2636.  
  2637.   procedure TChkSumFilter.Read(var buf; Count:word);
  2638.   begin
  2639.     TSequential.Read(buf,count);
  2640.     if status = stOK then
  2641.       ChkSum := UpdateChksum(ChkSum,buf,Count);
  2642.   end;
  2643.  
  2644.   procedure TChkSumFilter.Write(var buf; Count:word);
  2645.   begin
  2646.     TSequential.Write(buf,count);
  2647.     if status = stOk then
  2648.       ChkSum := UpdateChksum(ChkSum,buf,Count);
  2649.   end;
  2650.  
  2651. { ***** CRC16 filter code ***** }
  2652.  
  2653.   constructor TCRC16Filter.init(ABase:PStream; ACRC16:word);
  2654.   begin
  2655.     if not TSequential.init(ABase) then
  2656.       fail;
  2657.     CRC16 := ACRC16;
  2658.   end;
  2659.  
  2660.   procedure TCRC16Filter.Read(var buf; Count:word);
  2661.   begin
  2662.     TSequential.Read(buf,count);
  2663.     if status = stOK then
  2664.       CRC16 := UpdateCRC16(CRC16,buf,count);
  2665.   end;
  2666.  
  2667.   procedure TCRC16Filter.Write(var buf; Count:word);
  2668.   begin
  2669.     TSequential.Write(buf,count);
  2670.     if status = stOk then
  2671.       CRC16 := UpdateCRC16(CRC16,buf,count);
  2672.   end;
  2673.  
  2674.   { ***** CRCARC filter code ***** }
  2675.  
  2676.   constructor TCRCARCFilter.init(ABase:PStream; ACRCARC:word);
  2677.   begin
  2678.     if not TSequential.init(ABase) then
  2679.       fail;
  2680.     CRCARC := ACRCARC;
  2681.   end;
  2682.  
  2683.   procedure TCRCARCFilter.Read(var buf; Count:word);
  2684.   begin
  2685.     TSequential.Read(buf,count);
  2686.     if status = stOK then
  2687.       CRCARC := UpdateCRCARC(CRCARC,buf,count);
  2688.   end;
  2689.  
  2690.   procedure TCRCARCFilter.Write(var buf; Count:word);
  2691.   begin
  2692.     TSequential.Write(buf,count);
  2693.     if status = stOk then
  2694.       CRCARC := UpdateCRCARC(CRCARC,buf,count);
  2695.   end;
  2696.  
  2697.   { ***** CRC32 filter code ***** }
  2698.  
  2699.   constructor TCRC32Filter.init(ABase:PStream; ACRC32:longint);
  2700.   begin
  2701.     if not TSequential.init(ABase) then
  2702.       fail;
  2703.     CRC32 := ACRC32;
  2704.   end;
  2705.  
  2706.   procedure TCRC32Filter.Read(var buf; Count:word);
  2707.   begin
  2708.     TSequential.Read(buf,count);
  2709.     if status = stOK then
  2710.       CRC32 := UpdateCRC32(CRC32,buf,count);
  2711.   end;
  2712.  
  2713.   procedure TCRC32Filter.Write(var buf; Count:word);
  2714.   begin
  2715.     TSequential.Write(buf,count);
  2716.     if status = stOk then
  2717.       CRC32 := UpdateCRC32(CRC32,buf,count);
  2718.   end;
  2719.  
  2720.   { ****** Null stream code ****** }
  2721.  
  2722.   constructor TNulStream.Init;
  2723.   begin
  2724.     TStream.Init;
  2725.     Position := 0;
  2726.     Value := AValue;
  2727.   end;
  2728.  
  2729.   function TNulStream.GetPos;
  2730.   begin
  2731.     GetPos := Position;
  2732.   end;
  2733.  
  2734.   function TNulStream.GetSize;
  2735.   begin
  2736.     GetSize := Position;
  2737.   end;
  2738.  
  2739.   procedure TNulStream.Read;
  2740.   begin
  2741.     FillChar(Buf, Count, Value);
  2742.     Inc(Position, Count);
  2743.   end;
  2744.  
  2745.   procedure TNulStream.Seek;
  2746.   begin
  2747.     Position := Pos;
  2748.   end;
  2749.  
  2750.   procedure TNulStream.Write;
  2751.   begin
  2752.     Inc(Position, Count);
  2753.   end;
  2754.  
  2755.   { ****** RAM stream code ****** }
  2756.  
  2757.   constructor TRAMStream.Init(Asize : Word);
  2758.   begin
  2759.     TStream.Init;
  2760.     Position := 0;
  2761.     Size := 0;
  2762.     Alloc := Asize;
  2763.     if MaxAvail < Alloc then
  2764.       Fail;
  2765.     GetMem(Buffer, Alloc);
  2766.     OwnMem := True;
  2767.     FillChar(Buffer^, Alloc, 0);
  2768.   end;
  2769.  
  2770.   constructor TRAMStream.UseBuf(ABuffer : Pointer; Asize : Word);
  2771.   begin
  2772.     TRAMStream.Init(0);
  2773.     Alloc := Asize;
  2774.     Size  := Asize;
  2775.     Buffer := ABuffer;
  2776.     OwnMem := False;
  2777.   end;
  2778.  
  2779.   destructor TRAMStream.Done;
  2780.   begin
  2781.     if OwnMem then
  2782.       FreeMem(Buffer, Alloc);
  2783.     TStream.Done;
  2784.   end;
  2785.  
  2786.   function TRAMStream.GetPos;
  2787. {  begin                         Replaced with assembler for speed.
  2788.     GetPos := Position;
  2789.    end; }
  2790.   assembler;
  2791.   asm
  2792.     les di,self
  2793.     mov ax,es:di[Position];
  2794.     xor dx,dx
  2795.   end;
  2796.  
  2797.   function TRAMStream.GetSize;
  2798. {  begin                         Replaced with assembler for speed.
  2799.     GetSize := Size;
  2800.    end; }
  2801.    assembler;
  2802.    asm
  2803.      les di,self
  2804.      mov ax,es:di[size]
  2805.      xor dx,dx
  2806.    end;
  2807.  
  2808.   function CheckInc(var pos:word;count,limit:word):boolean; assembler;
  2809.   { Increments pos by count, returns false if limit is exceeded }
  2810.   asm
  2811.     les di,pos
  2812.     mov bx,count
  2813.     mov al,true
  2814.     add bx,es:[di]
  2815.     jc  @1            { Carry means error }
  2816.     mov es:[di],bx
  2817.     sub bx,limit
  2818.     jbe @2
  2819.   @1:
  2820.     dec ax            { Set AX to false }
  2821.   @2:
  2822.   end;
  2823.  
  2824.   procedure TRAMStream.Read;
  2825.   begin
  2826.     Move(Buffer^[Position], Buf, Count);
  2827.     if not CheckInc(Position,Count,Size) then
  2828.     begin
  2829.       Error(stReadError,0);
  2830.       Dec(Position,Count);
  2831.       FillChar(Buf,Count,0);
  2832.     end;
  2833.   end;
  2834.  
  2835.   procedure TRAMStream.Seek;
  2836.   begin
  2837.     if Pos > Size then
  2838.       Error(stReaderror, 0)
  2839.     else
  2840.       Position := Pos;
  2841.   end;
  2842.  
  2843.   procedure TRAMStream.Truncate;
  2844.   begin
  2845.     Size := Position;
  2846.   end;
  2847.  
  2848.   procedure TRAMStream.Write;
  2849.   begin
  2850.     if not CheckInc(Position,Count,Alloc) then
  2851.       Error(stWriteError, 0)
  2852.     else
  2853.     begin
  2854.       Move(Buf, Buffer^[Position-Count], Count);
  2855.       if Position > Size then
  2856.         Size := Position;
  2857.     end;
  2858.   end;
  2859.  
  2860.   { ***** XMS stream code ***** }
  2861.  
  2862.   {$I xmsstrm.inc}
  2863.  
  2864.   { ***** EMS size code ***** }
  2865.  
  2866.   function exist_ems:boolean;
  2867.   const
  2868.     ems_found : boolean = false;  { Used as initialized var }
  2869.   var
  2870.     S : TEMSStream;
  2871.   begin
  2872.     if not ems_found then
  2873.     begin
  2874.       S.init(1,1);
  2875.       ems_found := S.status = stOk;
  2876.       S.done;
  2877.     end;
  2878.     exist_ems := ems_found;
  2879.   end;
  2880.  
  2881.   function ems_maxavail: longint;
  2882.   begin
  2883.     if not exist_ems then
  2884.       ems_maxavail:=0
  2885.     else
  2886.     asm
  2887.       mov ah,$42;
  2888.       int $67
  2889.       mov ax,16384
  2890.       mul bx
  2891.       mov word ptr @result,ax
  2892.       mov word ptr @result[2],dx
  2893.     end;
  2894.   end;
  2895.  
  2896.   function ems_memavail: longint;
  2897.   begin
  2898.     ems_memavail := ems_maxavail;
  2899.   end;
  2900.  
  2901.   function GetTempList:String;
  2902.   { Function to get the list of directories for temp files }
  2903.   var
  2904.   {$ifdef windows}
  2905.     p : PChar;
  2906.   {$endif}
  2907.     result : string;
  2908.   begin
  2909.   {$ifdef windows}
  2910.     p := GetEnvVar(@TempEnvVar[1]);
  2911.   if p <> nil then
  2912.     result := StrPas(p)
  2913.   else
  2914.     result := '';
  2915.   {$else}
  2916.     result := GetEnv(TempEnvVar);
  2917.   {$endif}
  2918.     if Length(result) = 0 then
  2919.       result := '.\';
  2920.     GetTempList := result;
  2921.   end;
  2922.  
  2923.   function GetTempDir(var TempList:string):string;
  2924.   { Strip one temp directory off the front of the list, and
  2925.     return it fully qualified, with a '\' at the end. }
  2926.   var
  2927.     Semicolon : byte;
  2928.     result : string;
  2929.     curdir : string;
  2930.   begin
  2931.     Semicolon := Pos(';',TempList);
  2932.     if Semicolon > 0 then
  2933.     begin
  2934.       result := Copy(TempList,1,Semicolon-1);
  2935.       TempList := Copy(TempList,Semicolon+1,255);
  2936.     end
  2937.     else
  2938.     begin
  2939.       result := TempList;
  2940.       TempList := '';
  2941.     end;
  2942.     if result[Length(result)] <> '\' then
  2943.       result := result+'\';
  2944.     if (length(result) < 2) or (result[2] <> ':') then
  2945.       GetDir(0,curdir)
  2946.     else
  2947.     begin
  2948.       GetDir(ord(upcase(result[1]))-ord('A')+1,curdir);
  2949.       result := copy(result,3,255);
  2950.     end;
  2951.     if (length(result) > 1) and (result[1] <> '\') then
  2952.       result := curdir + '\' + result
  2953.     else
  2954.       result := copy(curdir,1,2) + result;
  2955.     GetTempDir := result;
  2956.   end;
  2957.  
  2958.   function disk_maxavail: longint;
  2959.   var
  2960.     templist,tempname : string;
  2961.     result : longint;
  2962.   begin
  2963.     result := 0;
  2964.     templist := GetTempList;
  2965.     repeat
  2966.       tempname := GetTempDir(templist);
  2967.       result := MaxLong(result,
  2968.                         DiskFree(ord(upcase(tempname[1]))-ord('A')+1))
  2969.     until templist = '';
  2970.     disk_maxavail := result;
  2971.   end;
  2972.  
  2973.   function disk_memavail: longint;
  2974.   var
  2975.     templist,tempname : string;
  2976.     result,space : longint;
  2977.     disk : byte;
  2978.     disks : array[1..32] of boolean;
  2979.   begin
  2980.     fillchar(disks,sizeof(disks),false);
  2981.     result := 0;
  2982.     templist := GetTempList;
  2983.     repeat
  2984.       tempname := GetTempDir(templist);
  2985.       disk := ord(upcase(tempname[1]))-ord('A')+1;
  2986.       if not disks[disk] then
  2987.       begin
  2988.         disks[disk] := true;
  2989.         space := DiskFree(disk);
  2990.       end
  2991.       else
  2992.         space := 0;
  2993.       if space > 0 then
  2994.         inc(result,space);
  2995.     until templist = '';
  2996.     disk_memavail := result;
  2997.   end;
  2998.  
  2999.   { ***** Named Buffered file stream code ***** }
  3000.  
  3001.   constructor TNamedBufStream.Init(Name : FNameStr; Mode : TOpenMode; ABufSize : Word);
  3002.   begin
  3003.     if TBufStream.Init(Name, Mode, ABufSize) then
  3004.     {$ifdef windows}
  3005.     filename := StrNew(name)
  3006.     {$else}
  3007.       Filename := NewStr(Name)
  3008.     {$endif}
  3009.     else
  3010.       Fail;
  3011.   end;
  3012.  
  3013.   destructor TNamedBufStream.Done;
  3014.   begin
  3015.   {$ifdef windows}
  3016.   StrDispose(filename);
  3017.   {$else}
  3018.     DisposeStr(Filename);
  3019.   {$endif}
  3020.     TBufStream.Done;
  3021.   end;
  3022.  
  3023.   constructor TTempBufStream.Init(ABufSize : Word;
  3024.                                   InitSize,MaxSize : Longint);
  3025.   var
  3026.     TempList,TempName : String;
  3027.     Okay : Boolean;
  3028.     NewHandle : Word;
  3029.     F : File;
  3030.   begin
  3031.     if not TStream.Init then
  3032.       Fail;
  3033.     if MaxAvail < ABufSize then
  3034.       Fail;
  3035.     BufSize := ABufSize;
  3036.     GetMem(Buffer, BufSize);
  3037.     MaxSize := MaxLong(MinLong(MaxSize,Disk_MaxAvail),InitSize);
  3038.     TempList := GetTempList;
  3039.     repeat
  3040.       TempName := GetTempDir(TempList);
  3041.       FillChar(TempName[Length(TempName)+1], 255-Length(TempName), #0);
  3042.       asm
  3043.         push    ds
  3044.         push    ss
  3045.         pop     ds
  3046.         lea     dx,TempName[1]
  3047.         mov     ah, $5a
  3048.         xor     cx,cx
  3049.       {$ifdef windows}
  3050.         call dos3call
  3051.       {$else}
  3052.         int     $21                 { Create temporary file. }
  3053.       {$endif}
  3054.         pop     ds
  3055.         jc      @failed
  3056.         mov     Okay,True
  3057.         mov     NewHandle,ax
  3058.         jmp     @done
  3059. @failed:
  3060.         mov     Okay,False
  3061. @done:
  3062.       end;
  3063.       if okay then
  3064.       begin
  3065.         Handle := NewHandle;
  3066.         while TempName[Length(TempName)+1] <> #0 do
  3067.           Inc(TempName[0]);
  3068.         {$ifdef windows}
  3069.         Filename := StrNew(StrPCopy(@tempname[1],tempname));
  3070.         {$else}
  3071.         Filename := NewStr(TempName);
  3072.         {$endif}
  3073.         Seek(MaxSize-1);
  3074.         Write(okay,1);      { Write a 0 }
  3075.         Flush;
  3076.         Seek(InitSize);
  3077.         Truncate;
  3078.         okay := Status = stOK;
  3079.         if not okay and (TempList <> '') then
  3080.         begin
  3081.           asm
  3082.             mov ah,$3E
  3083.             mov bx,NewHandle
  3084.             int $21             { Close file }
  3085.           end;
  3086.           assign(F,filename^);
  3087.           Erase(F);
  3088.           Reset;
  3089.           {$ifdef windows}
  3090.           StrDispose(Filename);
  3091.           {$else}
  3092.           DisposeStr(Filename);
  3093.           {$endif}
  3094.           Filename := nil;
  3095.         end;
  3096.       end;
  3097.     until okay or (TempList = '');
  3098.   end;
  3099.  
  3100.   destructor TTempBufStream.Done;
  3101.   var
  3102.     F : file;
  3103.   begin
  3104.   {$ifdef windows}
  3105.   assign(f,StrPas(Filename));
  3106.   {$else}
  3107.     Assign(F, Filename^);
  3108.   {$endif}
  3109.     TNamedBufStream.Done;
  3110.     Erase(F);
  3111.   end;
  3112.  
  3113.   {******** TWorkStream code ******* }
  3114.  
  3115.   constructor TWorkStream.init(Allocator:TAllocator;ABlockmin,ABlockMax:Longint;
  3116.                    APreference : TStreamRanking);
  3117.   begin
  3118.     TFilter.init(Allocator(ABlockmin,ABlockmax,APreference));
  3119.     Allocate := Allocator;
  3120.     Blockmin := ABlockmin;
  3121.     Blockmax := ABlockmax;
  3122.     Preference := APreference;
  3123.     BlockStart := 0;
  3124.   end;
  3125.  
  3126.   procedure TWorkStream.write(var Buf; Count:Word);
  3127.   var
  3128.     Buffer : TByte_array absolute Buf;
  3129.     firstpart : word;
  3130.     byteswritten : word;
  3131.     pos : longint;
  3132.     NewBase : PStream;
  3133.     saveStatus, saveInfo : integer;
  3134.   begin
  3135.     pos := GetPos;
  3136.     byteswritten := 0;
  3137.     if CheckStatus then
  3138.       repeat
  3139.         firstpart := Count;
  3140.         if (Pos < BlockStart+BlockMax) and (Pos+firstpart > BlockStart+BlockMax) then
  3141.           firstpart := BlockStart+BlockMax-Pos;
  3142.         TFilter.Write(Buffer[byteswritten], firstpart);
  3143.  
  3144.         { **** crummy code to get around problems with TBufStream **** }
  3145.         { The test is an efficiency hack - we don't want to flush every
  3146.           segment of the stream, just the last one. }
  3147.         if typeof(Base^) = typeof(TConcatFilter) then
  3148.           PConcatFilter(Base)^.Base2^.Flush
  3149.         else
  3150.           Base^.Flush;          { Must flush all writes to see TBufStream
  3151.                                 errors immediately :-( }
  3152.         { **** end of crummy code :-) ***** }
  3153.         if Status = stOK then
  3154.         begin
  3155.           dec(Count,firstpart);
  3156.           inc(Pos,firstpart);
  3157.           inc(byteswritten,firstpart);
  3158.         end
  3159.         else
  3160.         begin
  3161.           saveStatus := Status;
  3162.           saveInfo   := ErrorInfo;
  3163.           Reset;
  3164.           if Pos = GetSize then
  3165.           begin
  3166.             { If write failed at eof, allocate a new block }
  3167.             Seek(0);
  3168.             NewBase := Allocate(BlockMin,BlockMax,Preference);
  3169.             if (NewBase = nil) or (NewBase^.Status <> stOK) then
  3170.             begin
  3171.               error(stBaseError, stWriteError);
  3172.               exit;
  3173.             end;
  3174.             Base := New(PConcatFilter,init(Base,NewBase));
  3175.             BlockStart := Pos;
  3176.           end
  3177.           else  { Some other kind of write failure; restore the error status }
  3178.           begin
  3179.             error(saveStatus,saveInfo);
  3180.             exit;
  3181.           end;
  3182.         end;
  3183.       until count = 0;
  3184.   end;
  3185.  
  3186.   { ***** Temp Stream Code ******* }
  3187.  
  3188.   function TempStream(InitSize, MaxSize : LongInt;
  3189.                       Preference : TStreamRanking) : PStream;
  3190.   var
  3191.     Choice : Integer;
  3192.     Result : PStream;
  3193.     StreamType : TStreamType;
  3194.     Nulls : TNulStream;
  3195.   begin
  3196.     Result := nil;
  3197.     Nulls.Init(0);
  3198.     for Choice := 1 to NumTypes do
  3199.     begin
  3200.       StreamType := Preference[Choice];
  3201.       case StreamType of
  3202.         RAMStream :
  3203.           if MaxSize < $10000 then
  3204.             Result := New(PRAMStream, Init(MaxSize));
  3205.         EMSStream :
  3206.           if ems_MaxAvail >= MaxSize then
  3207.             Result := New(PEMSStream, Init(InitSize, MaxSize));
  3208.         XMSStream :
  3209.           if xms_MaxAvail >= MaxSize then
  3210.             Result := New(PXMSStream, Init(InitSize, MaxSize));
  3211.         FileStream :
  3212.           if disk_MaxAvail >= MaxSize then
  3213.             Result := New(PTempBufStream, Init(2048, InitSize, MaxSize));
  3214.       end;
  3215.       if (Result <> nil) and (Result^.Status = stOK) then
  3216.       begin
  3217.         Result^.Copyfrom(Nulls, InitSize);
  3218.         Result^.Seek(0);
  3219.         if Result^.Status = stOK then
  3220.         begin
  3221.           Nulls.Done;
  3222.           TempStream := Result;
  3223.           Exit;
  3224.         end;
  3225.       end;
  3226.       if Result <> nil then
  3227.         Dispose(Result, Done); { Clean up and start over } ;
  3228.       Result := nil;
  3229.     end;
  3230.     TempStream := nil;
  3231.   end;
  3232.  
  3233.   function StreamName(S:PStream):String;
  3234.   { This function is for debugging only!  It links every single stream
  3235.     type into your .EXE. }
  3236.   var
  3237.     t : pointer;
  3238.   begin
  3239.     if S=nil then
  3240.       StreamName := 'nil'
  3241.     else
  3242.     begin
  3243.       t := typeof(S^);
  3244.            if t = typeof(TStream)         then StreamName := 'TStream'
  3245.       else if t = typeof(TEMSStream)      then StreamName := 'TEMSStream'
  3246.       else if t = typeof(TDOSStream)      then StreamName := 'TDOSStream'
  3247.       else if t = typeof(TBufStream)      then StreamName := 'TBufStream'
  3248.       else if t = typeof(TFilter)         then StreamName := 'TFilter'
  3249.       else if t = typeof(TEncryptFilter)  then StreamName := 'TEncryptFilter'
  3250.       else if t = typeof(TLZWFilter)      then StreamName := 'TLZWFilter'
  3251.       else if t = typeof(TTextFilter)     then StreamName := 'TTextFilter'
  3252.       else if t = typeof(TLogFilter)      then StreamName := 'TLogFilter'
  3253.       else if t = typeof(TBitFilter)      then StreamName := 'TBitFilter'
  3254.       else if t = typeof(TDupFilter)      then StreamName := 'TDupFilter'
  3255.       else if t = typeof(TConcatFilter)   then StreamName := 'TConcatFilter'
  3256.       else if t = typeof(TLimitFilter)    then StreamName := 'TLimitFilter'
  3257.       else if t = typeof(TLoopFilter)     then StreamName := 'TLoopFilter'
  3258.       else if t = typeof(TReverseFilter)  then StreamName := 'TReverseFilter'
  3259.       else if t = typeof(TSequential)     then StreamName := 'TSequential'
  3260.       else if t = typeof(TChksumFilter)   then StreamName := 'TChksumFilter'
  3261.       else if t = typeof(TCRC16Filter)    then StreamName := 'TCRC16Filter'
  3262.       else if t = typeof(TCRCARCFilter)   then StreamName := 'TCRCARCFilter'
  3263.       else if t = typeof(TCRC32Filter)    then StreamName := 'TCRC32Filter'
  3264.       else if t = typeof(TNulStream)      then StreamName := 'TNulStream'
  3265.       else if t = typeof(TRAMStream)      then StreamName := 'TRAMStream'
  3266.       else if t = typeof(TXMSStream)      then StreamName := 'TXMSStream'
  3267.       else if t = typeof(TNamedBufStream) then StreamName := 'TNamedBufStream'
  3268.       else if t = typeof(TTempBufStream)  then StreamName := 'TTempBufStream'
  3269.       else if t = typeof(TWorkStream)     then StreamName := 'TWorkStream'
  3270.       else StreamName := 'Unknown (or uninitialized) stream';
  3271.     end;
  3272.   end;
  3273.  
  3274. end.
  3275.