home *** CD-ROM | disk | FTP | other *** search
- unit Streams;
- { Unit to provide enhancements to TV Objects unit streams in the form
- of several filters, i.e. stream clients, and other streams. }
-
- {#Z+} { These comments don't need to go into the help file. }
-
- {$B-} { Use fast boolean evaluation. }
-
- { Version 1.2 - Adds TNulStream and TXMSStream, from suggestion and
- code by Stefan Boether; TBitFilter, from suggestion
- by Rene Seguin; added call to Flush to TFilter.Done;
- UseBuf and OwnMem to TRAMStream.
- TTextFilter fixed so that mixed access methods work.
- 1.3 - Added TDupFilter, TSequential, CRCs and Checksums
- 1.4 - Recoded several of the TRAMStream methods in assembler for
- more speed; fixed numerous TTextFilter bugs and added
- TTextFilter.AssignStream and TextDemo.pas; fixed
- TXMSStream.Seek bug. Changed xms_Memavail and xms_Maxavail
- to report in bytes, and added ems_Memavail and ems_Maxavail
- (based on code sent to me by Eyal Doron) and disk_Memavail
- and disk_Maxavail. Changed TXMSStream.Init to match
- TEMSStream.Init. Added TConcatFilter, TLimitFilter,
- TLoopFilter, TReverseFilter and TWorkStream. Added OwnsBase
- field to TFilter. Did some testing to assure that the unit
- works in BP 7 protected mode. Thanks to Max Maschein, Eyal
- Doron, and others for bug fix help.
- 1.5 - The first public release of the 1.4 enhancements.}
-
- { Load some conditional defines }
- {$i STDefine.inc}
-
- {$ifdef overlays}
- {$O-}
- { Don't overlay this unit; it contains code that needs to participate
- in overlay management. }
- {$endif}
-
- { Hierarchy:
-
- TStream (from Objects)
- TFilter Base type for filters
- TEncryptFilter Encrypts as it writes; decrypts as it reads
- TLZWFilter Compresses as it writes; expands as it reads
- TTextFilter Provides text file interface to stream
- TLogFilter Provides logging of text file activity
- TBitFilter Allows reads & writes by the bit
- TDupFilter Duplicates output, checks for matching input
- TConcatFilter Concatenates two streams
- TLimitFilter Limits I/O to a specific range
- TLoopFilter Joins end of stream to start
- TReverseFilter Reads and writes the stream in reverse order
- TSequential Filter that doesn't allow Seek
- TChksumFilter Calculates 16 bit checksum for reads and writes
- TCRC16Filter Calculates XMODEM-style 16 bit CRC
- TCRCARCFilter Calculates ARC-style 16 bit CRC
- TCRC32Filter Calculates ZIP/ZModem-style 32 bit CRC
- TNulStream Eats writes, returns constant on reads
- TRAMStream Stream in memory
- TXMSStream Stream in XMS
- TDOSStream (from Objects)
- TBufStream (from Objects)
- TNamedBufStream Buffered file stream that knows its name
- TTempBufStream Buffered file stream that erases itself when done
- TWorkStream Stream that grows as needed
-
- Procedures & functions:
-
- TempStream allocates a temporary stream
- OvrInitStream like OvrInitEMS, but buffers overlays on a stream
- May be called several times to buffer different
- segments on different streams.
- OvrDetachStream detaches stream from overlay system
- OvrDisposeStreams detaches all streams from overlay system and disposes of
- them
- OvrSizeNeeded Calculates the size needed to load the rest of the segments
- to a stream
- OvrLoadAll immediately copies as many overlay segments to the stream
- as will fit
- UpdateChkSum updates a 16 bit checksum value
- UpdateCRC16 updates a CRC16 value
- UpdateCRCARC updates a CRCARC value
- UpdateCRC32 updates a CRC32 value
- ReverseBytes reverses the byte order within a buffer
-
- }
- {#Z-}
-
- interface
-
- uses
- {$ifdef windows}
- strings,windos,winprocs,
- {$else}
- DOS,
- {$endif}
- {$ifdef overlays}
- Overlay,
- {$endif}
- {$ifdef wobjects}
- Wobjects;
- {$else}
- Objects;
- {$endif}
-
- const
- stBadMode = 1; { Bad mode for stream - operation not
- supported. ErrorInfo = mode. }
- stStreamFail = 2; { Stream init failed }
- stBaseError = 3; { Error in base stream. ErrorInfo = base error value }
- stMemError = 4; { Not enough memory for operation }
- stSigError = 5; { Problem with LZ file signature }
- stUsedAll = 6; { Used limit of allocation }
- stUnsupported = 7; { Operation unsupported in this stream }
- stBase2Error = 8; { Error in second base. ErrorInfo = base2 error value }
- stMisMatch = 9; { Two bases don't match. ErrorInfo = mismatch position
- in current buffer. }
- stIntegrity = 10; { Stream has detected an integrity error
- in a self check. Info depends on
- stream type. }
- type
- TOpenMode = $3C00..$3DFF; { Allowable DOS stream open modes }
- {$ifdef windows}
- FNameStr = PChar; { To make streams take names as in the manual. }
- {$endif}
-
- PFilter = ^TFilter;
- TFilter =
- object(TStream)
- { Generic object to filter another stream. TFilter just passes everything
- through, and mirrors the status of the base stream }
-
- Base : PStream;
- { Pointer to the base stream. }
-
- Startofs : LongInt;
- { The offset of the start of the filter in the base stream. }
-
- OwnsBase : Boolean;
- { Defaults true; if set to false, then #Done# won't dispose of
- the base. }
-
- constructor Init(ABase : PStream);
- { Initialize the filter with the given base. }
-
- destructor Done; virtual;
- { Flush filter, then dispose of base if #OwnsBase#. }
-
- function GetPos : LongInt; virtual;
- function GetSize : LongInt; virtual;
- procedure Read(var Buf; Count : Word); virtual;
- procedure Seek(Pos : LongInt); virtual;
- procedure Truncate; virtual;
- procedure Write(var Buf; Count : Word); virtual;
- procedure Flush; virtual;
-
- function CheckStatus : Boolean; virtual;
- { Return true if status is stOK.
- If status is stOK, but base is not, then reset the base. This is a poor
- substitute for a virtual Reset method. }
-
- procedure CheckBase;
- { Check base stream for error, and copy status using own Error method. }
- end;
-
- PEncryptFilter = ^TEncryptFilter;
- TEncryptFilter =
- object(TFilter)
- { Filter which encrypts text going in or out; encrypting twice with the same
- key decrypts. Not very sophisticated encryption. }
-
- Key : LongInt;
- { Key is used as a Randseed replacement }
-
- constructor Init(Akey : LongInt; ABase : PStream);
- { Init with a given key }
-
- procedure Read(var Buf; Count : Word); virtual;
- procedure Seek(Pos : LongInt); virtual;
- procedure Write(var Buf; Count : Word); virtual;
- end;
-
- const
- MaxStack = 4096; { Must match lzwstream.asm declaration! }
-
- type
- PLZWTables = ^TLZWTables;
- TLZWTables =
- record
- Collision : array[0..MaxStack-1] of Byte; { Hash table entries }
- PrefixTable : array[0..MaxStack-1] of Word; { Code for preceding stringf }
- SuffixTable : array[0..MaxStack-1] of Byte; { Code for current character }
- ChildTable : array[0..MaxStack-1] of Word; { Next duplicate in collision
- list. }
- CharStack : array[0..MaxStack-1] of Byte; { Decompression stack }
- StackPtr : Word; { Decompression stack depth }
- Prefix : Word; { Previous code string }
- TableUsed : Word; { # string table entries used }
- InputPos : Word; { Index in input buffer }
- OutputPos : Word; { Index in output buffer }
- LastHit : Word; { Last empty slot in collision
- table. }
- CodeBuf : Word;
- SaveIP : Word;
- SaveAX : Word;
- SaveCX : Word;
- SaveDX : Word;
-
- NotFound : Byte; { Character combination found
- flag. }
- end;
-
- PLZWFilter = ^TLZWFilter;
- TLZWFilter =
- object(TFilter)
- Mode : Word; { Either stOpenRead or stOpenWrite. }
- Size, { The size of the expanded stream. }
- Position : LongInt; { The current position in the expanded stream }
- Tables : PLZWTables; { Tables holding the compressor state. }
-
- constructor Init(ABase : PStream; AMode : TOpenMode);
- { Create new compressor stream, to use ABase as the source/destination
- for data. AMode must be stOpenRead or stOpenWrite. }
-
- destructor Done; virtual;
- { Flushes all data to the stream, and writes the uncompressed
- filesize to the head of it before calling TFilter.done. }
-
- procedure Flush; virtual;
- function GetPos : LongInt; virtual;
- function GetSize : LongInt; virtual;
- procedure Read(var Buf; Count : Word); virtual;
-
- procedure Seek(Pos : LongInt); virtual;
- { Seek is not supported at all in Write mode. In Read mode, it is
- slow for seeking forwards, and very slow for seeking backwards:
- it rewinds the file to the start and readforward from there. }
-
- procedure Truncate; virtual;
- { Truncate is not supported in either mode, and always causes a
- call to Error. }
-
- procedure Write(var Buf; Count : Word); virtual;
- end;
-
- type
- PTextFilter = ^TTextFilter;
- TTextFilter =
- object(TFilter)
- { A filter to provide ReadLn/WriteLn interface to a stream. First
- open the stream and position it, then pass it to this filter;
- then Reset, Rewrite, or Append the Textfile variable, and do all
- reads and writes to it; they'll go to the stream through a TFDD.
- You can also assign the stream to any other text variable using
- the #AssignStream# method. }
-
- TextFile : Text;
- { A fake text file to use with Read(ln)/Write(ln). }
- TextPtr : ^text;
- { A pointer to the text file used by the filter. Initialized
- to point to TextFile, but #AssignStream# will change TextPtr. }
-
- constructor Init(ABase : PStream; AName : String);
- { Initialize the interface to ABase; stores AName in the name field of
- #Textfile#. AName isn't used beyond this, but may be helpful
- if you choose to watch the TextFile field in the debugger. }
-
- destructor Done; virtual;
- { Flushes the text file, then closes and disposes of the base stream. }
-
- procedure AssignStream(var NewText:text; AName : String);
- { Close the currently assigned text file, and assign a new one.
- As with #Init#, the name is stored in NewText, but is not otherwise
- used.}
-
- function GetPos : LongInt; virtual;
- function GetSize : LongInt; virtual;
- procedure Flush; virtual;
- procedure Read(var Buf; Count : Word); virtual;
- procedure Seek(Pos : LongInt); virtual;
- procedure Truncate; virtual;
- procedure Write(var Buf; Count : Word); virtual;
- end;
-
- type
- PLogFilter = ^TLogFilter;
- TLogFilter =
- object(TFilter)
- { A filter to log activity on a text file. }
-
- LogList : ^Text; { A pointer to the first logged file }
-
- constructor init(ABase:PStream);
- { Initializes filter, but doesn't start logging anything }
-
- destructor Done; virtual;
- { Stops logging all files, and closes & disposes of the base stream }
-
- procedure Log(var F : Text);
- { Logs all input and output to F to the stream. You must do the Assign to
- F first, and not do another Assign without closing F. }
-
- function Unlog(var F : Text) : Boolean;
- { Stops logging of F. Called automatically if file is closed. Returns
- false and does nothing on error. }
- end;
-
- TBit = 0..1; { A single bit }
-
- PBitFilter = ^TBitFilter;
- TBitFilter =
- object(TFilter)
- BitPos : ShortInt;
- { Position of stream relative to base file. Negative values signal
- that the buffer is unchanged from the file, positive values signal
- that the file needs to be updated. Zero signals an empty buffer. }
- Mask : Byte; { Mask to extract next bit from buffer }
- Buffer : Byte; { Buffer of next 8 bits from stream }
- AtEnd : Boolean; { Flag to signal that we're at the end
- of the base, and we shouldn't read
- it. Bases that change in length should
- set this to false. }
-
- constructor Init(ABase : PStream);
-
- procedure Flush; virtual; { Flush buffer to stream }
- procedure Seek(Pos : LongInt); virtual; { Seek to bit at start of
- pos byte. }
- procedure Read(var Buf; Count : Word); virtual;
- procedure Write(var Buf; Count : Word); virtual;
-
- function GetBit : TBit; { Get next bit from stream }
- function GetBits(Count : Byte) : LongInt; { Get up to 32 bits }
- procedure ReadBits(var Buf; Count : LongInt); { Read bits from stream }
-
- procedure PutBit(ABit : TBit); { Put one bit to stream }
- procedure PutBits(ABits : LongInt; Count : Byte); { Put up to 32 bits,
- { low bits first. }
- procedure WriteBits(var Buf; Count : LongInt); { Write count bits to stream }
-
- procedure SeekBit(Pos : LongInt); { Seek to particular bit }
- function GetBitPos : LongInt;
-
- procedure CopyBits(var S : TBitFilter; Count : LongInt); { Copy bits from S }
- procedure ByteAlign; { Seek forward to next byte boundary. }
-
- procedure PrepareBuffer(ForRead : Boolean);
- { Internal method to assure that buffer is valid }
- end;
-
- PDupFilter = ^TDupFilter;
- TDupFilter =
- object(TFilter) { Duplicates output, confirms matching input }
- Base2 : PStream;
- { Pointer to the second base. }
-
- Startofs2 : LongInt;
- { The offset of the start of the filter in the second base. }
-
- constructor Init(ABase, ABase2 : PStream);
- { Initialize the filter with the given bases. }
-
- destructor Done; virtual;
- { Flush filter, then dispose of both bases. }
-
- function MisMatch(var buf1,buf2; count:word):word; virtual;
- { Checks for a mismatch between the two buffers. Returns
- the byte number of the mismatch (1 based), or 0 if they
- test equal. This default method checks for an exact match. }
-
- procedure Read(var Buf; Count : Word); virtual;
- procedure Seek(Pos : LongInt); virtual;
- procedure Truncate; virtual;
- procedure Write(var Buf; Count : Word); virtual;
- procedure Flush; virtual;
-
- function CheckStatus : Boolean; virtual;
- { Return true if status is stOK.
- If status is stOK, but base is not, then reset the base. This is a poor
- substitute for a virtual Reset method. }
-
- procedure CheckBase2;
- { Check 2nd base stream for error, and copy status using own Error method. }
- end;
-
- PConcatFilter = ^TConcatFilter;
- TConcatFilter =
- object(TFilter)
- { A filter which acts to concatenate two streams (or parts of streams)
- so that they appear as one.}
- Base2 : PStream;
- { Pointer to the second base. This one logically follows the first.}
-
- Startofs2 : LongInt;
- { The offset of the start of the filter in the second base. }
-
- Position : Longint;
- { The current position of the filter. The corresponding
- base stream is kept synchronized with this }
- Base1Size : Longint;
- { This is used a lot to determine switching. }
-
- constructor Init(ABase, ABase2 : PStream); { Initialize the filter with the given bases. }
-
- destructor Done; virtual;
- { Flush filter, then dispose of both bases. }
-
- function GetPos:longint; virtual;
- function GetSize:longint; virtual;
- procedure Read(var Buf; Count : Word); virtual;
- procedure Seek(Pos : LongInt); virtual;
- procedure Truncate; virtual;
- procedure Write(var Buf; Count : Word); virtual;
- procedure Flush; virtual;
- { These methods work directly on Base until its size
- is reached, then switch over to Base2. Base will *never* grow
- from the size at stream initialization. }
-
- function CheckStatus : Boolean; virtual;
-
- procedure CheckBase2;
- { Check 2nd base stream for error, and copy status using own Error method. }
-
- end;
-
- PLimitFilter = ^TLimitFilter;
- TLimitFilter =
- object(TFilter)
- { Limits all access to the bytes between LoLimit and HiLimit. }
- LoLimit,HiLimit : longint;
- { The lower and upper limit points. These are in the TFilter
- scale, i.e. relative to #TFilter.Base#. }
- constructor init(ABase:PStream;ALoLimit,AHiLimit:longint);
- { Does the usual init, sets the limits, then does a Seek to ALoLimit
- if it is non-zero. }
-
- function GetSize:longint; virtual;
- { Returns the smaller of HiLimit and the #TFilter.GetSize# value. }
-
- procedure Read(var Buf; Count : Word); virtual;
- procedure Seek(Pos : LongInt); virtual;
- procedure Write(var Buf; Count : Word); virtual;
- end;
-
- PLoopFilter = ^TLoopFilter;
- TLoopFilter =
- object(TLimitFilter)
- { Moves all access to the bytes between LoLimit and HiLimit. }
- function GetSize:longint; virtual;
- { Returns the smaller of the size between the limits, or from
- the low limit to the end of the base }
- procedure Read(var Buf; Count : Word); virtual;
- procedure Seek(Pos : LongInt); virtual;
- procedure Write(var Buf; Count : Word); virtual;
- end;
-
-
-
- PReverseFilter = ^TReverseFilter;
- TReverseFilter =
- object(TFilter)
- { Reads and writes the base in reverse order. }
- ReverseBlocks : Boolean; { Whether to reverse the bytes within
- a Read/Write block }
- constructor init(ABase:PStream; AReverseBlocks:boolean);
- { Standard initialization }
- function GetPos:longint; virtual;
- { Returns the position in bytes from the end of the base }
- procedure Read(var Buf; Count : Word); virtual;
- { See #Write#. }
- procedure Write(var Buf; Count : Word); virtual;
- { These methods read/write the block of bytes just previous to
- the current base file pointer. The bytes themselves are
- reversed if #ReverseBlocks# is true. }
- procedure Seek(Pos : LongInt); virtual;
- { Does the Seek in the reversed byte order, i.e. count from the
- end of the stream }
- procedure Truncate; virtual;
- { Triggers an #stUnsupported# error. }
- end;
-
- procedure ReverseBytes(var Buf; Count : Word);
- { Reverses the order of the bytes in the buffer }
-
- type
- PSequential = ^TSequential;
- TSequential =
- object(TFilter) { Filter for sequential access only }
- procedure Seek(pos:longint); virtual;{ Signals stUnsupported if a Seek is attempted }
- end;
-
- PChksumFilter = ^TChksumFilter;
- TChksumFilter =
- object(TSequential) { Calculates 16 bit checksum of
- bytes read/written. }
- Chksum : word;
-
- constructor Init(ABase : PStream;AChksum:word);
- { Initialize the filter with the given base and starting checksum. }
-
- procedure Read(var Buf; Count : Word); virtual;
- procedure Write(var Buf; Count : Word); virtual;
- end;
-
- PCRC16Filter = ^TCRC16Filter;
- TCRC16Filter =
- object(TSequential) { Calculates XMODEM style 16 bit CRC }
- CRC16 : word;
-
- constructor Init(ABase : PStream;ACRC16:word);
- { Initialize the filter with the given base and starting CRC. }
-
- procedure Read(var Buf; Count : Word); virtual;
- procedure Write(var Buf; Count : Word); virtual;
- end;
-
- PCRCARCFilter = ^TCRCARCFilter;
- TCRCARCFilter =
- object(TSequential) { Calculates ARC-style 16 bit CRC }
- CRCARC : word;
-
- constructor Init(ABase : PStream;ACRCARC:word);
- { Initialize the filter with the given base and starting CRC. }
-
- procedure Read(var Buf; Count : Word); virtual;
- procedure Write(var Buf; Count : Word); virtual;
- end;
-
- PCRC32Filter = ^TCRC32Filter;
- TCRC32Filter =
- object(TSequential) { Calculates PKZIP and ZModem style 32 bit CRC }
- CRC32 : longint;
-
- constructor Init(ABase : PStream;ACRC32:longint);
- { Initialize the filter with the given base and starting CRC. }
-
- procedure Read(var Buf; Count : Word); virtual;
- procedure Write(var Buf; Count : Word); virtual;
- end;
-
-
- PNulStream = ^TNulStream;
- TNulStream =
- object(TStream)
- Position : LongInt; { The current position for the stream. }
- Value : Byte; { The value returned on reads. }
-
- constructor Init(AValue : Byte);
- function GetPos : LongInt; virtual;
- function GetSize : LongInt; virtual;
- procedure Read(var Buf; Count : Word); virtual;
- procedure Seek(Pos : LongInt); virtual;
- procedure Write(var Buf; Count : Word); virtual;
- end;
-
- Pbyte_array = ^Tbyte_array;
- Tbyte_array = array[0..65520] of Byte; { Type used as a buffer. }
-
- PRAMStream = ^TRAMStream;
- TRAMStream =
- object(TStream)
- Position : Word; { The current position for the stream. }
-
- Size : Word; { The current size of the stream. }
- Alloc : Word; { The size of the allocated block of memory. }
-
- Buffer : Pbyte_array; { Points to the stream data. }
- OwnMem : Boolean; { Whether Done should dispose of data.}
-
- constructor Init(Asize : Word);
- { Attempt to initialize the stream to a block size of Asize;
- initial stream size and position are 0. }
- constructor UseBuf(ABuffer : Pointer; Asize : Word);
- { Initialize the stream using the specified buffer. OwnMem is set
- to false, so the buffer won't be disposed of. Initial position is 0,
- size is Asize. }
-
- destructor Done; virtual;
- { Dispose of the stream. }
-
- function GetPos : LongInt; virtual;
- function GetSize : LongInt; virtual;
- procedure Read(var Buf; Count : Word); virtual;
- procedure Seek(Pos : LongInt); virtual;
- procedure Truncate; virtual;
- procedure Write(var Buf; Count : Word); virtual;
- end;
-
- PXMSStream = ^TXMSStream;
- TXMSStream =
- object(TStream)
- Handle : Word; { XMS handle }
- BlocksUsed : Word; { Number of 1K blocks used. Always allocates
- at least one byte more than Size. }
- Size : LongInt; { The current size of the stream }
- Position : LongInt; { Current position }
-
- constructor Init(MinSize,MaxSize:longint);
- destructor Done; virtual;
-
- function GetPos : LongInt; virtual;
- function GetSize : LongInt; virtual;
- procedure Read(var Buf; Count : Word); virtual;
- procedure Seek(Pos : LongInt); virtual;
- procedure Truncate; virtual;
- procedure Write(var Buf; Count : Word); virtual;
-
- procedure NewBlock; { Internal method to allocate a block }
- procedure FreeBlock; { Internal method to free one block }
- end;
-
- function xms_MemAvail : Longint;
- { Returns total of available XMS bytes. }
- function xms_MaxAvail : Longint;
- { Returns size of largest available XMS block in bytes. }
- function ems_MemAvail : Longint;
- { Returns total of available EMS in bytes. }
- function ems_MaxAvail : Longint;
- { Returns size of largest available EMS block in bytes. }
-
- const
- TempEnvVar : String[12] = 'TEMP';
- { The name of an environment variable holding a directory list
- where #TTempBufStream# should go looking for disk space. }
-
- function disk_MemAvail : Longint;
- { Returns total of available disk space for temp streams, from the
- list specified by #TempEnvVar#. }
- function disk_MaxAvail : Longint;
- { Returns maximum available block of disk space for temp streams,
- from the list specified by #TempEnvVar#. }
-
- type
- PNamedBufStream = ^TNamedBufStream;
- TNamedBufStream =
- object(TBufStream)
- { A simple descendant of TBufStream which knows its own name.}
-
- {$ifdef windows}
- filename : PChar;
- {$else}
- Filename : PString;
- {$endif}
- { The name of the stream. }
-
- constructor Init(Name : FNameStr; Mode : TOpenMode; ABufSize : Word);
- { Open the file with the given name, and save the name. }
-
- destructor Done; virtual;
- { Close the file. }
-
- end;
-
- PTempBufStream = ^TTempBufStream;
- TTempBufStream =
- object(TNamedBufStream)
- { A temporary buffered file stream, which deletes itself when done.
- It's allocated on one of the directories specified by #TempEnvVar#.}
-
- constructor Init(ABufSize : Word;InitSize,MaxSize : Longint);
- { Create a temporary file with a unique name, in the directory
- pointed to by the environment varable named in #TempEnvVar# or in
- the current directory, open it in read/write mode, and try to grow
- it to InitSize bytes. }
-
- destructor Done; virtual;
- { Close and delete the temporary file. }
-
- end;
-
- TStreamType = (NoStream, RAMStream, EMSStream, XMSStream, FileStream);
- { The type of stream that a tempstream might be. }
-
- const
- NumTypes = Ord(FileStream);
-
- type
- TStreamRanking = array[1..NumTypes] of TStreamType;
- { A ranking of preference for a type of stream, from most to least preferred }
-
- TAllocator = function (InitSize, MaxSize : LongInt;
- Preference : TStreamRanking) : PStream;
- { This is a declaration just like the Streams.TempStream function.}
-
- PWorkStream = ^TWorkStream;
- TWorkStream =
- object(TFilter)
- { This is a stream type that grows as you write to it by allocating new
- blocks according to a specified strategy. Blocks may be of mixed
- types. It's a descendant of a filter, but it manages its own base. }
-
- Allocate : TAllocator;
- BlockMin, { These fields are passed to Allocate }
- BlockMax : longint;
- Preference : TStreamRanking;
- BlockStart: longint; { The offset in the stream where the
- last block starts. }
-
- constructor init(Allocator:TAllocator;ABlockmin,ABlockMax:Longint;
- APreference : TStreamRanking);
- { ABlockmin to APreference are passed to the allocator to allocate
- a new block whenever the current one gives a write error.
- The TWorkStream will never try to write a single block that crosses
- the ABlockMax boundary, so tests within the stream can be simple.}
- procedure write(var Buf; Count:Word); virtual;
- { The write procedure checks whether the write would make the
- current block grow too large; if so, it splits up the write. }
- end;
-
- const
- BufSize : Word = 2048; { Buffer size if buffered stream is used. }
-
- const ForSpeed : TStreamRanking = (RAMStream, EMSStream, XMSStream, FileStream);
- { Streams ordered for speed }
-
- const ForSize : TStreamRanking = (FileStream, EMSStream, XMSStream, RAMStream);
- { Streams ordered for low impact on the heap }
-
- const ForSizeInMem : TStreamRanking = (EMSStream, XMSStream, RAMStream, NoStream);
- { Streams in memory only, ordered as #ForSize#. }
-
- const ForOverlays : TStreamRanking = (EMSStream, XMSStream, FileStream, NoStream);
- { Streams ordered for speed, but never in RAM. }
-
- function TempStream(InitSize, MaxSize : LongInt;
- Preference : TStreamRanking) : PStream;
-
- { This procedure returns a pointer to a temporary stream from a
- choice of 3, specified in the Preference array. The first stream
- type listed in the Preference array which can be successfully
- created with the given sizes will be returned, or Nil if none can
- be made. }
-
- function StreamName(S:PStream):String;
- { This function returns a string naming the type of S^. It's useful for
- debugging programs that use TempStream and TWorkStream. However,
- it's for debugging only! It links every single stream type into your
- .EXE. }
-
- {$ifdef overlays}
- procedure OvrInitStream(S : PStream);
- { Copies overlay segment code to S as new segments are loaded,
- and does reloads from there. Allows multiple calls, to buffer
- different segments on different streams. }
-
- procedure OvrDetachStream(BadS : PStream);
- { Makes sure that the overlay system makes no references to BadS. }
-
- procedure OvrDisposeStreams;
- { Detaches and disposes of all streams being used by the overlay system }
-
- function OvrSizeNeeded : LongInt;
- { Returns the size required to load any segments which still haven't
- been loaded to a stream. }
-
- function OvrLoadAll : Boolean;
- { Forces all overlay segments to be copied into the stream; if successful
- (true) then no more references to the overlay file will be made. }
- {$endif windows}
-
- Function UpdateChksum(Initsum: Word; Var InBuf; InLen : Word) : Word;
- { Updates the checksum Initsum by adding InLen bytes from InBuf }
-
- Function UpdateCRC16(InitCRC : Word; Var InBuf; InLen : Word) : Word;
- { I believe this is the CRC used by the XModem protocol. The transmitting
- end should initialize with zero, UpdateCRC16 for the block, Continue the
- UpdateCRC16 for two nulls, and append the result (hi order byte first) to
- the transmitted block. The receiver should initialize with zero and
- UpdateCRC16 for the received block including the two byte CRC. The
- result will be zero (why?) if there were no transmission errors. (I have
- not tested this function with an actual XModem implementation, though I
- did verify the behavior just described. See TESTCRC.PAS.) }
-
-
- Function UpdateCRCArc(InitCRC : Word; Var InBuf; InLen : Word) : Word;
- { This function computes the CRC used by SEA's ARC utility. Initialize
- with zero. }
-
- Function UpdateCRC32(InitCRC : LongInt; Var InBuf; InLen : Word) : LongInt;
- { This function computes the CRC used by PKZIP and Forsberg's ZModem.
- Initialize with high-values ($FFFFFFFF), and finish by inverting all bits
- (Not). }
-
- implementation
-
- function MinLong(x,y:longint):longint;
- begin
- if x<y then
- MinLong := x
- else
- MinLong := y;
- end;
-
- function MaxLong(x,y:longint):longint;
- begin
- MaxLong := -MinLong(-x,-y);
- end;
-
- {****** TFilter code *******}
-
- constructor TFilter.Init(ABase : PStream);
- begin
- TStream.Init;
- Base := ABase;
- CheckBase;
- if Status = stOK then
- Startofs := Base^.GetPos;
- OwnsBase := true;
- end;
-
- destructor TFilter.Done;
- begin
- if Base <> nil then
- begin
- Flush;
- if OwnsBase then
- Dispose(Base, Done);
- end;
- TStream.Done;
- end;
-
- function TFilter.GetPos : LongInt;
- begin
- if CheckStatus then
- begin
- GetPos := Base^.GetPos-Startofs;
- CheckBase;
- end;
- end;
-
- function TFilter.GetSize : LongInt;
- begin
- if CheckStatus then
- begin
- GetSize := Base^.GetSize-Startofs;
- CheckBase;
- end;
- end;
-
- procedure TFilter.Read(var Buf; Count : Word);
- begin
- if CheckStatus then
- begin
- Base^.Read(Buf, Count);
- CheckBase;
- end;
- end;
-
- procedure TFilter.Seek(Pos : LongInt);
- begin
- if CheckStatus then
- begin
- Base^.Seek(Pos+Startofs);
- CheckBase;
- end;
- end;
-
- procedure TFilter.Truncate;
- begin
- if CheckStatus then
- begin
- Base^.Truncate;
- CheckBase;
- end;
- end;
-
- procedure TFilter.Write(var Buf; Count : Word);
- begin
- if CheckStatus then
- begin
- Base^.Write(Buf, Count);
- CheckBase;
- end;
- end;
-
- procedure TFilter.Flush;
- begin
- if CheckStatus then
- begin
- Base^.Flush;
- CheckBase;
- end;
- end;
-
- function TFilter.CheckStatus : Boolean;
- begin
- if (Status = stOK) and (Base^.Status <> stOK) then
- Base^.Reset;
- CheckStatus := Status = stOK;
- end;
-
- procedure TFilter.CheckBase;
- begin
- if Base^.Status <> stOK then
- Error(stBaseError, Base^.Status);
- end;
-
- constructor TEncryptFilter.Init(Akey : LongInt; ABase : PStream);
- begin
- TFilter.Init(ABase);
- Key := Akey;
- end;
-
- procedure TEncryptFilter.Read(var Buf; Count : Word);
- var
- i : Word;
- SaveSeed : LongInt;
- Bytes : Tbyte_array absolute Buf;
- begin
- SaveSeed := RandSeed;
- RandSeed := Key;
- TFilter.Read(Buf, Count);
- for i := 0 to Count-1 do
- Bytes[i] := Bytes[i] xor Random(256);
- Key := RandSeed;
- RandSeed := SaveSeed;
- end;
-
- procedure CycleKey(Key, Cycles : LongInt);
- { For cycles > 0, mimics cycles calls to the TP random number generator.
- For cycles < 0, backs it up the given number of calls. }
- var
- i : LongInt;
- Junk : Integer;
- SaveSeed : LongInt;
- begin
- if Cycles > 0 then
- begin
- SaveSeed := RandSeed;
- RandSeed := Key;
- for i := 1 to Cycles do
- Junk := Random(0);
- Key := RandSeed;
- RandSeed := SaveSeed;
- end
- else
- for i := -1 downto Cycles do
- Key := (Key-1)*(-649090867);
- end;
-
- procedure TEncryptFilter.Seek(Pos : LongInt);
- var
- OldPos : LongInt;
- begin
- OldPos := GetPos;
- TFilter.Seek(Pos);
- CycleKey(Key, Pos-OldPos);
- end;
-
- procedure TEncryptFilter.Write(var Buf; Count : Word);
- var
- i : Word;
- SaveSeed : LongInt;
- BufPtr : Pointer;
- BufPtrOffset : Word absolute BufPtr;
- Buffer : array[0..255] of Byte;
- begin
- SaveSeed := RandSeed;
- RandSeed := Key;
- BufPtr := @Buf;
- while Count > 256 do
- begin
- Move(BufPtr^, Buffer, 256);
- for i := 0 to 255 do
- Buffer[i] := Buffer[i] xor Random(256);
- TFilter.Write(Buffer, 256);
- Dec(Count, 256);
- Inc(BufPtrOffset, 256);
- end;
- Move(BufPtr^, Buffer, Count);
- for i := 0 to Count-1 do
- Buffer[i] := Buffer[i] xor Random(256);
- TFilter.Write(Buffer, Count);
- Key := RandSeed;
- RandSeed := SaveSeed;
- end;
-
-
- { ******* LZW code ******* }
-
- {$L LZWSTREAM.OBJ}
-
- procedure Initialise(Tables : PLZWTables); External;
-
- function PutSignature(Tables : PLZWTables) : Boolean; External;
-
- function Crunch(InBufSize, OutBufSize : Word;
- var InBuffer, OutBuffer;
- Tables : PLZWTables) : Pointer; External;
-
- { Crunch some more text. Stops when Inbufsize bytes are used up, or
- output buffer is full. Returns bytes used in segment, bytes written
- in offset of result }
-
- function FlushLZW(var OutBuffer;
- Tables : PLZWTables) : Word; External;
- { Flush the remaining characters to signal EOF. Needs space for up to
- 3 characters. }
-
- function GetSignature(var InBuffer, Dummy;
- Tables : PLZWTables) : Boolean; External;
- { Initializes for reading, and checks for 'LZ' signature in start of compressed
- code. Inbuffer must contain at least 3 bytes. Dummy is just there to put the
- Inbuffer in the right spot }
-
- function Uncrunch(InBufSize, OutBufSize : Word;
- var InBuffer, OutBuffer;
- Tables : PLZWTables) : Pointer; External;
- { Uncrunch some text. Will stop when it has done Outbufsize worth or has
- exhausted Inbufsize worth. Returns bytes used in segment, bytes written
- in offset of result }
-
- constructor TLZWFilter.Init(ABase : PStream; AMode : TOpenMode);
- { Create new compressor stream, to use ABase as the source/destination
- for data. Mode must be stOpenRead or stOpenWrite. }
- var
- Buffer : array[1..3] of Byte;
- Info : Integer;
- begin
- Info := stBadMode;
- if (AMode = stOpenRead) or (AMode = stOpenWrite) then
- begin
- Info := stStreamFail;
- if TFilter.Init(ABase) then
- begin
- if Status = stOK then
- begin
- Info := stMemError;
- Startofs := Base^.GetPos;
- Position := 0;
- Mode := AMode;
-
- if MaxAvail >= SizeOf(TLZWTables) then
- begin
- Info := stSigError;
- GetMem(Tables, SizeOf(TLZWTables));
- Initialise(Tables);
- if Mode = stOpenRead then
- begin
- Base^.Read(Size, SizeOf(Size));
- Base^.Read(Buffer, 3);
- CheckBase;
- if GetSignature(Buffer, Buffer, Tables) then
- Exit; { Successfully opened for reading }
- end
- else if Mode = stOpenWrite then
- begin
- Size := 0;
- Base^.Write(Size, SizeOf(Size)); { Put a place holder }
- CheckBase;
- if PutSignature(Tables) then
- Exit; { Successful construction for writing! }
- end;
- end;
- end;
- end;
- end;
- Error(stInitError, Info);
- end;
-
- destructor TLZWFilter.Done;
- begin
- Flush;
- FreeMem(Tables, SizeOf(TLZWTables));
- TFilter.Done;
- end;
-
- procedure TLZWFilter.Write(var Buf; Count : Word);
- var
- Inbuf : array[0..65520] of Byte absolute Buf;
- Outbuf : array[0..255] of Byte;
- Inptr : Word;
- Sizes : record
- OutSize, UsedSize : Word;
- end;
- begin
- if CheckStatus then
- begin
- if Mode <> stOpenWrite then
- Error(stBadMode, Mode);
- Inptr := 0;
- repeat
- Pointer(Sizes) := Crunch(Count, SizeOf(Outbuf),
- Inbuf[Inptr], Outbuf, Tables);
- with Sizes do
- begin
- Base^.Write(Outbuf, OutSize);
-
- Dec(Count, UsedSize);
- Inc(Inptr, UsedSize);
- Inc(Size, UsedSize);
- Inc(Position, UsedSize);
- end;
- until Count = 0;
- CheckBase;
- end;
- end;
-
- procedure TLZWFilter.Flush;
- var
- Outbuf : array[0..255] of Byte;
- Sizes : record
- OutSize, UsedSize : Word;
- end;
- Pos : LongInt;
- begin
- if CheckStatus then
- begin
- if Mode = stOpenWrite then
- begin
- Pointer(Sizes) := Crunch(1, SizeOf(Outbuf), Outbuf, Outbuf, Tables);
- { Push one more character to match JA bug }
- with Sizes do
- begin
- Base^.Write(Outbuf, OutSize);
-
- OutSize := FlushLZW(Outbuf, Tables); { And flush }
- Base^.Write(Outbuf, OutSize);
- end;
- Pos := Base^.GetPos;
- Base^.Seek(Startofs);
- Base^.Write(Size, SizeOf(Size));
- Base^.Seek(Pos);
- end;
- Base^.Flush;
- Mode := 0;
- CheckBase;
- end;
- end;
-
- procedure TLZWFilter.Read(var Buf; Count : Word);
- var
- Outbuf : array[0..65520] of Byte absolute Buf;
- Inbuf : array[0..255] of Byte;
- OutPtr : Word;
- BlockSize : Word;
- Sizes : record
- OutSize, UsedSize : Word;
- end;
- BytesLeft : LongInt;
- begin
- if CheckStatus then
- begin
- if Mode <> stOpenRead then
- Error(stBadMode, Mode);
- OutPtr := 0;
- BlockSize := SizeOf(Inbuf);
- with Base^ do
- BytesLeft := GetSize-GetPos;
-
- if Position+Count > Size then
- begin
- Error(stReaderror, 0);
- FillChar(Buf, Count, 0);
- Exit;
- end;
-
- while Count > 0 do
- begin
- if BytesLeft < BlockSize then
- BlockSize := BytesLeft;
- Base^.Read(Inbuf, BlockSize);
- Pointer(Sizes) := Uncrunch(BlockSize, Count, Inbuf,
- Outbuf[OutPtr], Tables);
- with Sizes do
- begin
- if OutSize = 0 then
- begin
- Error(stReaderror, 0);
- FillChar(Outbuf[OutPtr], Count, 0);
- Exit;
- end;
- Dec(BytesLeft, UsedSize);
- Inc(Position, OutSize);
- Dec(Count, OutSize);
- Inc(OutPtr, OutSize);
- if UsedSize < BlockSize then
- with Base^ do { seek back to the first unused byte }
- Seek(GetPos-(BlockSize-UsedSize));
- end;
- end;
- CheckBase;
- end;
- end;
-
- procedure TLZWFilter.Seek(Pos : LongInt);
- var
- Buf : array[0..255] of Byte;
- Bytes : Word;
- begin
- if CheckStatus then
- begin
- if Mode <> stOpenRead then
- begin
- Error(stBadMode, Mode);
- Exit;
- end;
- if Pos < Position then
- begin
- Base^.Seek(Startofs);
- FreeMem(Tables, SizeOf(TLZWTables));
-
- TLZWFilter.Init(Base, Mode); { Re-initialize everything. Will this cause
- bugs in descendents? }
- end;
- while Pos > Position do
- begin
- if Pos-Position > SizeOf(Buf) then
- Bytes := SizeOf(Buf)
- else
- Bytes := Pos-Position;
- Read(Buf, Bytes);
- end;
- end;
- end;
-
- procedure TLZWFilter.Truncate;
- begin
- Error(stBadMode, Mode);
- end;
-
- function TLZWFilter.GetPos;
- begin
- GetPos := Position;
- end;
-
- function TLZWFilter.GetSize;
- begin
- GetSize := Size;
- end;
-
- { ***** Text Filter Code ******* }
-
- { These declarations are used both by TTextFilter and TLogFilter }
-
- type
- TFDDfunc = function(var F : Text) : Integer;
-
- PStreamTextRec = ^StreamTextRec;
- PSaveText = ^TSaveText;
- TSaveText =
- record { Used when logging for original data values }
- OpenFunc,
- InOutFunc,
- FlushFunc,
- CloseFunc : TFDDfunc;
- S : PLogFilter;
- SaveData : PSaveText;
- Next : PStreamTextRec;
- Data : array[13..16] of Byte;
- end;
-
- StreamTextRec =
- record
- Handle : Word;
- Mode : Word;
- BufSize : Word;
- private : Word;
- BufPos : Word;
- BufEnd : Word;
- BufPtr : Pbyte_array;
- OpenFunc,
- InOutFunc,
- FlushFunc,
- CloseFunc : TFDDfunc;
- S : PFilter; { This is a TTextFilter or a TLogFilter }
- SaveData : PSaveText;
- Next : PStreamTextRec;
- OtherData : array[13..16] of Byte;
- Name : array[0..79] of Char;
- Buffer : array[0..127] of Byte;
- end;
-
- function XLATstatus(var S:TStream):integer;
- const
- TextErrors : array[0..6] of integer = (0,5,5,100,101,212,212);
- var
- status : integer;
- begin
- status := S.status;
- if (status = stBaseError) or (status = stBase2Error) then
- status := S.errorinfo;
- if (-6 <= status) and (status <= 0) then
- XLATstatus := TextErrors[-status]
- else
- XLATstatus := 5;
- end;
-
- function TextIn(var F : Text) : Integer; Far;
- var
- savemode : word;
- begin
- with StreamTextRec(F), S^ do
- begin
- if Status = 0 then
- begin
- savemode := mode;
- mode := fmClosed; { This stops infinite loop }
- if GetSize-GetPos > BufSize then
- begin
- Read(BufPtr^, BufSize);
- BufEnd := BufSize;
- end
- else
- begin
- BufEnd := GetSize-GetPos;
- if BufEnd > 0 then
- Read(BufPtr^, BufEnd);
- end;
- BufPos := 0; { 1.3A bug fix }
- mode := savemode;
- end;
- TextIn := XLATStatus(S^);
- end;
- end;
-
- function TextOut(var F : Text) : Integer; Far;
- var
- savemode : word;
- begin
- with StreamTextRec(F), S^ do
- begin
- if Status = 0 then
- begin
- savemode := mode;
- mode := fmClosed;
- Write(BufPtr^, BufPos);
- mode := savemode;
- BufPos := 0;
- end;
- TextOut := XLATStatus(S^);
- end;
- end;
-
- function TextInFlush(var F : Text) : Integer; Far;
- begin
- TextInFlush := 0; { 1.3A bug fix }
- end;
-
- function TextOutFlush(var F : Text) : Integer; Far;
- begin
- TextOutFlush := TextOut(F);
- end;
-
- function TextClose(var F : Text) : Integer; Far;
- begin
- with StreamTextRec(F) do
- begin
- S^.Flush;
- TextClose := XLATStatus(S^);
- end;
- end;
-
- function TextOpen(var F : Text) : Integer; Far;
- var
- saveMode : word;
- begin
- with StreamTextRec(F) do
- begin
- case Mode of
- fmInOut :
- begin
- Mode := fmClosed;
- S^.Seek(S^.GetSize);
- Mode := fmOutput;
- end;
- fmInput,fmOutput :
- begin
- saveMode := Mode;
- Mode := fmClosed;
- S^.Seek(0);
- Mode := saveMode;
- end;
- end;
- case Mode of
- fmInput : begin
- InOutFunc := TextIn;
- FlushFunc := TextInFlush;
- end;
- fmOutput : begin
- InOutFunc := TextOut;
- FlushFunc := TextOutFlush;
- end;
- end;
- TextOpen := XLATStatus(S^);
- end;
- end;
-
- constructor TTextFilter.Init(ABase : PStream; AName : String);
- begin
- if not TFilter.Init(ABase) then
- Fail;
- TextPtr := nil;
- AssignStream(TextFile,AName);
- end;
-
- destructor TTextFilter.Done;
- begin
- if StreamTextRec(TextPtr^).Mode <> fmClosed then
- Close(Textptr^);
- TFilter.Done;
- end;
-
- procedure TTextFilter.AssignStream(var NewText:text;AName:string);
- begin
- if (TextPtr <> nil) and (StreamTextRec(TextPtr^).Mode <> fmClosed) then
- Close(TextPtr^);
- with StreamTextRec(NewText) do
- begin
- Mode := fmClosed;
- BufSize := SizeOf(Buffer);
- BufPtr := PByte_Array(@Buffer);
- OpenFunc := TextOpen;
- CloseFunc := TextClose;
- AName := Copy(AName, 1, 79);
- Move(AName[1], Name, Length(AName));
- Name[Length(AName)] := #0;
- S := @Self;
- end;
- TextPtr := @NewText;
- end;
-
- function TTextFilter.GetPos : LongInt;
- var
- result : longint;
- begin
- result := TFilter.GetPos;
- with StreamTextRec(Textptr^) do
- case Mode of
- fmInput : result := result - (BufEnd - BufPos);
- fmOutput : result := result + (BufPos);
- end;
- GetPos := Result;
- end;
-
- function TTextFilter.GetSize : LongInt;
- begin
- if StreamTextRec(Textptr^).Mode <> fmClosed then
- System.Flush(TextPtr^);
- GetSize := TFilter.GetSize;
- end;
-
- procedure TTextFilter.Flush;
- begin
- with StreamTextRec(TextPtr^) do
- begin
- case Mode of
- fmOutput : system.flush(TextPtr^);
- fmInput :
- begin
- TFilter.Seek(TFilter.GetPos - BufEnd + BufPos);
- BufPos := 0;
- BufEnd := 0;
- end;
- end;
- end;
- TFilter.Flush;
- end;
-
- procedure TTextFilter.Read(var Buf; Count : Word);
- begin
- Flush;
- TFilter.Read(Buf,Count);
- end;
-
- procedure TTextFilter.Seek(Pos : LongInt);
- begin
- Flush;
- TFilter.Seek(Pos);
- end;
-
- procedure TTextFilter.Truncate;
- begin
- Flush;
- TFilter.Truncate;
- end;
-
- procedure TTextFilter.Write(var Buf; Count : Word);
- begin
- Flush;
- TFilter.Write(Buf,Count);
- end;
-
- function DoOldCall(Func : TFDDfunc; var F : Text) : Integer;
- var
- Save : TSaveText;
- begin
- if @Func <> nil then
- with StreamTextRec(F) do
- begin
- Move(OpenFunc, Save, SizeOf(TSaveText));
- Move(SaveData^, OpenFunc, SizeOf(TSaveText)); { Now using old functions }
- DoOldCall := Func(F);
- Move(OpenFunc, Save.SaveData^, SizeOf(TSaveText)); { Save any changes }
- Move(Save, OpenFunc, SizeOf(TSaveText)); { Back to new ones }
- end;
- end;
-
- function LogIn(var F : Text) : Integer; Far;
- var
- Result : Integer;
- begin
- with StreamTextRec(F) do
- begin
- Result := DoOldCall(SaveData^.InOutFunc, F);
- if Result = 0 then
- S^.Write(BufPtr^, BufEnd); { Might want to record errors
- here }
- LogIn := Result;
- end;
- end;
-
- function LogOut(var F : Text) : Integer; Far;
- begin
- with StreamTextRec(F) do
- begin
- S^.Write(BufPtr^, BufPos);
- LogOut := DoOldCall(SaveData^.InOutFunc, F);
- end;
- end;
-
- function LogInFlush(var F : Text) : Integer; Far;
- begin
- with StreamTextRec(F) do
- LogInFlush := DoOldCall(SaveData^.FlushFunc, F);
- end;
-
- function LogOutFlush(var F : Text) : Integer; Far;
- var
- OldPos : Word;
- begin
- with StreamTextRec(F) do
- begin
- OldPos := BufPos;
- LogOutFlush := DoOldCall(SaveData^.FlushFunc, F);
- if BufPos = 0 then
- S^.Write(BufPtr^, OldPos);
- end;
- end;
-
- function LogClose(var F : Text) : Integer; Far;
- begin
- with StreamTextRec(F) do
- begin
- LogClose := DoOldCall(SaveData^.CloseFunc, F);
- if not PLogFilter(S)^.Unlog(F) then
- { Bug! } ;
- end;
- end;
-
- function LogOpen(var F : Text) : Integer; Far;
- begin
- with StreamTextRec(F) do
- begin
- LogOpen := DoOldCall(SaveData^.OpenFunc, F);
- case Mode of
- fmInOut, fmOutput : begin
- InOutFunc := LogOut;
- if @FlushFunc <> nil then
- FlushFunc := LogOutFlush;
- end;
- fmInput : begin
- InOutFunc := LogIn;
- if @FlushFunc <> nil then
- FlushFunc := LogInFlush;
- end;
- end;
- end;
- end;
-
- { ******* TLogFilter methods ******** }
-
- constructor TLogFilter.Init(Abase:PStream);
- begin
- if not TFilter.init(ABase) then
- fail;
- LogList := nil;
- end;
-
- destructor TLogFilter.Done;
- begin
- while (LogList <> nil) and Unlog(LogList^) do ;
- TFilter.Done;
- end;
-
- procedure TLogFilter.Log(var F : Text);
- var
- Save : PSaveText;
- OldOpen : TFDDfunc;
- Junk : Integer;
-
- begin
- New(Save);
- with StreamTextRec(F) do
- begin
- Move(OpenFunc, Save^, SizeOf(TSaveText)); { Save the original contents }
- S := @Self;
- SaveData := Save;
- Next := PStreamTextRec(LogList);
- LogList := @F; { Insert this file into the list of logged files }
- OldOpen := SaveData^.OpenFunc;
- Pointer(@SaveData^.OpenFunc) := nil; { Call LogOpen, but don't open. }
- Junk := LogOpen(F);
- SaveData^.OpenFunc := OldOpen;
- CloseFunc := LogClose;
- end;
- end;
-
- function TLogFilter.Unlog(var F : Text) : Boolean;
- var
- Save : PSaveText;
- Prev : PStreamTextRec;
- begin
- Unlog := False; { Assume failure }
- with StreamTextRec(F) do
- begin
- if S = PFilter(@Self) then
- begin
- { First, delete it from the list. }
- if LogList = @F then
- LogList := Pointer(Next)
- else
- begin
- Prev := PStreamTextRec(LogList);
- while (Prev^.Next <> nil) and (Prev^.Next <> PStreamTextRec(@F)) do
- Prev := Prev^.Next;
- if Prev^.Next <> PStreamTextRec(@F) then
- Exit; { Couldn't find it in the list!? }
- Prev^.Next := Next;
- end;
- Save := SaveData;
- Move(Save^, OpenFunc, SizeOf(TSaveText));
- Dispose(Save);
- Unlog := True;
- end;
- end;
- end;
-
- {$ifdef overlays}
-
- { ****** Overlay stream code ****** }
-
- type
- { This is the structure at the start of each "thunk" segment }
- POvrhead = ^TOvrhead;
- TOvrhead = record
- Signature : Word; { CD 3F - INT 3F call used on returns }
- Ret_Ofs : Word; { The offset to jump to when a return triggers a
- reload }
- Offset : LongInt; { The offset to the segment in the .OVR file }
- Code_Bytes, { Size of the code image }
- Reloc_Bytes, { Number of relocation fixups times 2 }
- Entry_Count, { The number of entry points }
- NextSeg, { Next overlay segment - add prefixseg + $10 to find
- thunks. List starts with System.ovrcodelist. }
- LoadSeg, { The segment at which the overlay is loaded, or 0 }
- Reprieve, { Set to 1 to if overlay used while on probation }
- NextLoaded : Word; { The segment of the next loaded overlay. List starts
- with System.ovrloadlist. Updated *after* call to
- ovrreadbuf. }
- case Integer of
- 1 : (EMSPage, { The EMS page where this overlay is stored }
- EMSOffset : Word); { The offset within the EMS page }
- 2 : (S : PStream; { The stream holding this segment's code }
- Soffset : LongInt); { The offset within S }
- end;
-
- var
- OldReadFunc : OvrReadFunc;
- OvrOldExitProc : Pointer;
- OvrStream : PStream;
- const
- OvrStreamInstalled : Boolean = False;
- OvrExitHandler : Boolean = False;
-
- function OvrPtr(Seg : Word) : POvrhead;
- { Convert map style segment number, as used by overlay manager, to
- pointer }
- begin
- OvrPtr := Ptr(Seg+PrefixSeg+$10, 0);
- end;
-
- function StdPtr(Seg : Word) : POvrhead;
- { Convert straight segment number to a pointer }
- begin
- StdPtr := Ptr(Seg, 0);
- end;
-
- function NewReadFunc(OvrSeg : Word) : Integer; Far;
- var
- Result : Integer;
- begin
- with StdPtr(OvrSeg)^ do
- begin
- if S = nil then
- begin { Segment not yet loaded }
- Result := OldReadFunc(OvrSeg);
- if Result = 0 then
- begin
- { Now copy the loaded code to our stream }
- Soffset := OvrStream^.GetSize;
- OvrStream^.Seek(Soffset);
- OvrStream^.Write(Ptr(LoadSeg, 0)^, Code_Bytes);
- Result := OvrStream^.Status;
- if Result = stOK then
- S := OvrStream
- else
- OvrStream^.Reset; { Something failed; hope we haven't messed
- up the stream too much }
- end;
- end
- else
- begin { Segment has been loaded into the stream }
- S^.Seek(Soffset);
- S^.Read(Ptr(LoadSeg, 0)^, Code_Bytes);
- Result := S^.Status;
- if Result <> stOK then
- begin
- S^.Reset; { Fix the stream, and try a standard load }
- Result := OldReadFunc(OvrSeg);
- end;
- end;
- end;
- NewReadFunc := Result;
- end;
-
- procedure OvrExitProc; Far;
- { Installed exit procedure; disposes of any streams that are still
- handling overlays. }
- begin
- ExitProc := OvrOldExitProc;
- OvrDisposeStreams;
- end;
-
- procedure OvrInitStream(S : PStream);
- begin
- if not OvrStreamInstalled then
- begin
- OldReadFunc := OvrReadBuf; { Install our reader function }
- OvrReadBuf := NewReadFunc;
- OvrStreamInstalled := True;
- end;
- if not OvrExitHandler then
- begin
- OvrOldExitProc := ExitProc;
- ExitProc := @OvrExitProc;
- OvrExitHandler := True;
- end;
- OvrStream := S; { And set stream to use }
- end;
-
- procedure OvrDetachStream(BadS : PStream);
- var
- OvrSeg : Word;
- begin
- if OvrStreamInstalled then
- begin
- if OvrStream = BadS then
- OvrStream := nil; { Detach default stream }
- OvrSeg := OvrCodeList;
- while OvrSeg <> 0 do { Walk the overlay list }
- with OvrPtr(OvrSeg)^ do
- begin
- if S <> nil then
- begin
- if S <> BadS then
- begin
- if OvrStream = nil then
- OvrStream := S; { Set default stream to first found }
- end
- else
- S := nil; { Blank out BadS references }
- end;
- OvrSeg := NextSeg;
- end;
- if OvrStream = nil then
- begin
- OvrStreamInstalled := False; { If we don't have a stream, better
- uninstall. }
- OvrReadBuf := OldReadFunc;
- end;
- end;
- end;
-
- procedure OvrDisposeStreams;
- var
- S : PStream;
- begin
- while OvrStreamInstalled and (OvrStream <> nil) do
- begin
- S := OvrStream;
- OvrDetachStream(S);
- Dispose(S, Done);
- end;
- end;
-
- function OvrSizeNeeded : LongInt;
- var
- OvrSeg : Word;
- Result : LongInt;
- begin
- OvrSeg := OvrCodeList;
- Result := 0;
- while OvrSeg <> 0 do { Walk the overlay list }
- with OvrPtr(OvrSeg)^ do
- begin
- if S = nil then
- Inc(Result, Code_Bytes);
- OvrSeg := NextSeg;
- end;
- OvrSizeNeeded := Result;
- end;
-
- function OvrLoadAll : Boolean;
- var
- OvrSeg : Word;
- Junk : Integer;
- begin
- if not OvrStreamInstalled then
- OvrLoadAll := False
- else
- begin
- OvrClearBuf;
- OvrSeg := OvrCodeList;
- while OvrSeg <> 0 do { Walk the overlay list }
- with OvrPtr(OvrSeg)^ do
- begin
- if S = nil then
- begin
- LoadSeg := OvrHeapOrg; { load at start of overlay buffer }
- Junk := NewReadFunc(OvrSeg+PrefixSeg+$10);
- LoadSeg := 0; { Don't really want it loaded yet }
- end;
- OvrSeg := NextSeg;
- end;
- OvrLoadAll := OvrStream^.Status = stOK;
- end;
- end;
-
- {$endif windows}
-
- { ****** Bit filter code ****** }
-
- constructor TBitFilter.Init(ABase : PStream);
- begin
- TFilter.Init(ABase);
- BitPos := 0;
- AtEnd := false;
- end;
-
- procedure TBitFilter.PrepareBuffer(ForRead : Boolean);
- begin
- if BitPos = 8 then { Buffer full on write }
- begin
- Base^.Write(Buffer, 1);
- BitPos := 0;
- end;
- if BitPos = 0 then { Buffer empty }
- begin
- if not AtEnd then
- begin
- if not ForRead then
- AtEnd := (Base^.GetPos >= Base^.GetSize);
- if (not AtEnd) or ForRead then
- begin
- Base^.Read(Buffer,1);
- BitPos := -8
- end;
- end;
- if AtEnd then
- Buffer := 0;
- Mask := 1;
- end;
- if (not ForRead) and (BitPos < 0) then
- begin
- Base^.Seek(Base^.GetPos-1);
- Inc(BitPos, 8);
- AtEnd := false;
- end;
- end;
-
- function TBitFilter.GetBit : TBit;
- begin
- if CheckStatus then
- begin
- PrepareBuffer(True);
- GetBit := TBit((Buffer and Mask) > 0);
- Mask := Mask shl 1;
- Inc(BitPos);
- CheckBase;
- end;
- end;
-
- function TBitFilter.GetBits(Count : Byte) : LongInt;
- var
- Result : LongInt;
- begin
- Result := 0;
- ReadBits(Result, Count);
- GetBits := Result;
- end;
-
- procedure TBitFilter.PutBit(ABit : TBit);
- begin
- if CheckStatus then
- begin
- PrepareBuffer(False);
- if ABit = 1 then
- Buffer := Buffer or Mask;
- Mask := Mask shl 1;
- Inc(BitPos);
- end;
- end;
-
- procedure TBitFilter.PutBits(ABits : LongInt; Count : Byte);
- begin
- WriteBits(ABits, Count);
- end;
-
- procedure TBitFilter.ReadBits(var Buf; Count : LongInt);
- var
- w : Word;
- b : array[1..2] of Byte absolute w;
- bBuf : TByte_Array absolute Buf;
- i, Bytes : Word;
- Shift : Word;
- begin
- if (Count > 0) and CheckStatus then
- begin
- PrepareBuffer(True);
- if BitPos > 0 then
- begin
- Base^.Write(Buffer, 1);
- Dec(BitPos, 8);
- end;
- Shift := BitPos+8; { the number of bits to shift by }
- Bytes := (Count+Shift-1) div 8; { Count of whole bytes to read }
- if Bytes > 0 then
- begin
- TFilter.Read(Buf, Bytes);
- b[1] := Buffer;
- for i := 0 to Pred(Bytes) do
- begin
- b[2] := bBuf[i];
- w := w shr Shift;
- bBuf[i] := b[1];
- w := w shr (8-Shift);
- end;
- Buffer := b[1];
- end;
- { Now fix up the last few bits }
- Dec(Count, 8*LongInt(Bytes));
- if Count > 0 then
- bBuf[Bytes] := (Buffer shr Shift) and not($FF shl Count)
- else
- if Count < 0 then
- bBuf[Bytes-1] := bBuf[Bytes-1] and not($FF shl (8+Count));
- BitPos := BitPos+Count;
- Mask := 1 shl (BitPos+8);
- end;
- end;
-
- procedure TBitFilter.WriteBits(var Buf; Count : LongInt);
- var
- w : Word;
- b : array[1..2] of Byte absolute w;
- bBuf : TByte_Array absolute Buf;
- i, Bytes : Word;
- Shift : Word;
- SaveBuf : Byte;
- SavePos : ShortInt;
- begin
- if CheckStatus then
- begin
- PrepareBuffer(False);
- Bytes := (Count+BitPos-1) div 8; { Count of whole bytes to write }
- Shift := 8-BitPos;
- if Bytes > 0 then
- begin
- if Shift < 8 then
- begin
- b[1] := Buffer shl Shift;
- for i := 0 to Pred(Bytes) do
- begin
- b[2] := bBuf[i];
- w := w shr Shift;
- Base^.Write(b[1], 1);
- w := w shr (8-Shift);
- end;
- Buffer := b[1] shr Shift;
- end
- else
- Base^.Write(Buf, Bytes);
- end;
- Dec(Count, 8*LongInt(Bytes));
- if Count > 0 then
- Buffer := (Buffer or (bBuf[Bytes] shl (8-Shift)));
- BitPos := BitPos+Count;
- if BitPos > 0 then { Fill in upper part of buffer }
- begin
- SaveBuf := Buffer;
- SavePos := BitPos;
- BitPos := 0; { signal empty buffer }
- PrepareBuffer(False); { and load it }
- Buffer := (Buffer and ($FF shl SavePos)) { old part }
- or (SaveBuf and not($FF shl SavePos)); { new part }
- BitPos := SavePos;
- end;
- Mask := 1 shl BitPos;
- CheckBase;
- end;
- end;
-
- procedure TBitFilter.Flush;
- begin
- if CheckStatus then
- begin
- if BitPos > 0 then
- Base^.Write(Buffer, 1);
- Dec(BitPos, 8);
- AtEnd := false;
- CheckBase;
- end;
- end;
-
- procedure TBitFilter.Seek(Pos : LongInt);
- begin
- if CheckStatus then
- begin
- Flush;
- TFilter.Seek(Pos);
- BitPos := 0;
- AtEnd := false;
- end;
- end;
-
- procedure TBitFilter.Read(var Buf; Count : Word);
- begin
- ReadBits(Buf, 8*LongInt(Count));
- end;
-
- procedure TBitFilter.Write(var Buf; Count : Word);
- begin
- WriteBits(Buf, 8*LongInt(Count));
- end;
-
- procedure TBitFilter.SeekBit(Pos : LongInt);
- var
- i : Byte;
- b : TBit;
- begin
- if CheckStatus then
- begin
- Seek(Pos div 8);
- for i := 1 to (Pos and 7) do
- b := GetBit;
- end;
- end;
-
- function TBitFilter.GetBitPos : LongInt;
- begin
- GetBitPos := 8*TFilter.GetPos+BitPos; { Need TFilter override in
- case descendants override
- GetPos }
- end;
-
- procedure TBitFilter.CopyBits(var S : TBitFilter; Count : LongInt);
- var
- localbuf : array[1..256] of Byte;
- begin
- while Count > 2048 do
- begin
- S.ReadBits(localbuf, 2048);
- WriteBits(localbuf, 2048);
- Dec(Count, 2048);
- end;
- if Count > 0 then
- begin
- S.ReadBits(localbuf, Count);
- WriteBits(localbuf, Count);
- end;
- end;
-
- procedure TBitFilter.ByteAlign;
- begin
- SeekBit((GetBitPos+7) and $FFFFFFF8);
- end;
-
- { ****** Duplicate filter code ****** }
-
- constructor TDupFilter.Init(ABase, ABase2 : PStream);
- { Initialize the filter with the given bases. }
- begin
- if not TFilter.Init(Abase) then
- fail;
- Base2 := ABase2;
- CheckBase2;
- if Status = stOK then
- Startofs2 := Base2^.GetPos;
- end;
-
- destructor TDupFilter.Done;
- { Flush filter, then dispose of both bases. }
- begin
- Flush;
- if Base2 <> nil then
- Dispose(Base2,done);
- TFilter.Done;
- end;
-
- function TDupFilter.MisMatch(var buf1,buf2;count:word):word;
- var
- i : word;
- bbuf1 : TByte_Array absolute buf1;
- bbuf2 : TByte_Array absolute buf2;
- begin
- for i := 0 to pred(count) do
- if bbuf1[i] <> bbuf2[i] then
- begin
- MisMatch := succ(i);
- exit;
- end;
- MisMatch := 0;
- end;
-
- procedure TDupFilter.Read(var Buf; Count : Word);
- var
- bpos : word;
- localbuf : array[0..255] of byte;
-
- procedure CompareBuffer(size:word);
- var
- epos : word;
- bbuf : TByte_Array absolute Buf;
- begin
- Base2^.Read(localbuf,size);
- dec(count,size);
- CheckBase2;
- if status = stOK then
- begin
- epos := MisMatch(bbuf[bpos],localbuf,size);
- if epos <> 0 then
- Error(stMismatch,bpos+epos);
- end;
- inc(bpos,size);
- end;
-
- begin
- TFilter.Read(buf, Count);
- bpos := 0;
- While (Status = stOK) and (Count >= sizeof(localbuf)) do
- CompareBuffer(Sizeof(localbuf));
- If (Status = stOK) and (Count > 0) then
- CompareBuffer(Count);
- { Be sure the bases are synchronized }
- Base2^.Seek(GetPos+StartOfs2);
- end;
-
- procedure TDupFilter.Seek(Pos : LongInt);
- begin
- TFilter.Seek(Pos);
- if Status = stOK then
- begin
- base2^.Seek(pos+startofs2);
- CheckBase2;
- end;
- end;
-
- procedure TDupFilter.Truncate;
- begin
- TFilter.Truncate;
- if Status = stOK then
- begin
- base2^.truncate;
- CheckBase2;
- end;
- end;
-
- procedure TDupFilter.Write(var Buf; Count : Word);
- begin
- TFilter.Write(buf,Count);
- if Status = stOK then
- begin
- Base2^.write(buf,Count);
- CheckBase2;
- end;
- end;
-
- procedure TDupFilter.Flush;
- begin
- TFilter.Flush;
- if Status = stOK then
- begin
- base2^.flush;
- CheckBase2;
- end;
- end;
-
- function TDupFilter.CheckStatus : Boolean;
- begin
- if TFilter.CheckStatus then
- if Base2^.Status <> stOK then
- Base2^.Reset;
- CheckStatus := Status = stOK;
- end;
-
- procedure TDupFilter.CheckBase2;
- begin
- if Base2^.status <> stOk then
- Error(stBase2Error,Base2^.status);
- end;
-
- { ****** Concatenating Filter code ****** }
-
- constructor TConcatFilter.Init(ABase, ABase2 : PStream);
- { Initialize the filter with the given bases. }
- begin
- if not TFilter.Init(ABase) then
- fail;
- Base2 := ABase2;
- CheckBase2;
- Base1Size := TFilter.GetSize;
- if Status = stOK then
- StartOfs2 := Base2^.GetPos;
- Position := Base1Size;
- end;
-
- destructor TConcatFilter.done;
- begin
- Flush;
- if Base2 <> nil then
- Dispose(Base2,done);
- if Base <> nil then
- Dispose(Base,Done); { Can't call TFilter.Done!!!! }
- TStream.done;
- end;
-
- function TConcatFilter.GetPos:longint;
- begin
- GetPos := Position;
- end;
-
- function TConcatFilter.GetSize:longint;
- begin
- if CheckStatus then
- begin
- GetSize := Base1Size + Base2^.GetSize;
- CheckBase2;
- end;
- end;
-
- procedure TConcatFilter.Read(var Buf; Count : Word);
- var
- Buffer : TByte_array absolute Buf;
- base1part : word;
- begin
- { First read the Base 1 portion }
- if Position < Base1Size then
- begin
- base1part := Count;
- if Position+base1part > Base1Size then
- base1part := Base1Size - Position;
- TFilter.Read(Buf, base1part);
- dec(Count,base1part);
- inc(Position,Base1part);
- if Count > 0 then
- Base2^.Seek(StartOfs2); { Be sure Base2 agrees with Pos now }
- end
- else
- base1part := 0;
- { Now read the Base 2 portion }
- if (Count > 0) and (status = stOK) then
- begin
- if Position = Base1Size then
- Base2^.Seek(StartOfs2);
- Base2^.Read(Buffer[base1part],Count);
- CheckBase2;
- inc(Position,count);
- end;
- end;
-
- procedure TConcatFilter.Seek(Pos : LongInt);
- begin
- if Pos < Base1Size then
- TFilter.Seek(Pos)
- else
- begin
- if CheckStatus then
- begin
- Base2^.Seek(Pos-Base1Size+StartOfs2);
- CheckBase2;
- end;
- end;
- if Status = stOK then
- Position := Pos;
- end;
-
- procedure TConcatFilter.Truncate;
- begin
- if Position < Base1Size then
- Error(stUnsupported,0) { We don't allow Base to be truncated, only
- Base2 }
- else
- if CheckStatus then
- begin
- Base2^.Truncate;
- CheckBase2;
- end;
- end;
-
- procedure TConcatFilter.Write(var Buf; Count : Word);
- var
- Buffer : TByte_array absolute Buf;
- base1part : word;
- begin
- { First write the Base 1 portion }
- if Position < Base1Size then
- begin
- base1part := Count;
- if Position+base1part > Base1Size then
- base1part := Base1Size - Position;
- TFilter.Write(Buf, base1part);
- dec(Count,base1part);
- inc(Position,Base1part);
- if Count > 0 then
- Base2^.Seek(StartOfs2); { Be sure Base2 agrees with Pos now }
- end
- else
- base1part := 0;
- { Now write the Base 2 portion }
- if (Count > 0) and (status = stOK) then
- begin
- Base2^.Write(Buffer[base1part],Count);
- CheckBase2;
- inc(Position,count);
- end;
- end;
-
- procedure TConcatFilter.Flush;
- begin
- TFilter.Flush;
- if status = stOK then
- begin
- Base2^.Flush;
- CheckBase2;
- end;
- end;
-
- function TConcatFilter.CheckStatus : Boolean;
- begin
- if TFilter.CheckStatus then
- if Base2^.Status <> stOK then
- Base2^.Reset;
- CheckStatus := Status = stOK;
- end;
-
- procedure TConcatFilter.CheckBase2;
- begin
- if Base2^.status <> stOk then
- Error(stBase2Error,Base2^.status);
- end;
-
- { ****** Limit Filter code *****}
-
- constructor TLimitFilter.init(ABase:PStream;ALoLimit,AHiLimit:longint);
- { Does the usual init, sets the limits, then does a Seek to ALoLimit
- if it is non-zero. }
- begin
- if not TFilter.Init(ABase) then
- fail;
- LoLimit := ALoLimit;
- HiLimit := AHiLimit;
- if ALoLimit <> 0 then
- Seek(ALoLimit);
- end;
-
- procedure TLimitFilter.Read(var Buf; Count : Word);
- begin
- if status = stOk then
- begin
- if GetPos + Count > HiLimit then
- begin
- Error(stReadError,0);
- Fillchar(Buf,Count,0);
- end
- else
- TFilter.Read(Buf,Count);
- end;
- end;
-
- procedure TLimitFilter.Seek(Pos : LongInt);
- begin
- if Status = stOK then
- begin
- if (Pos < LoLimit) or (Pos > HiLimit) then
- Error(stReadError,0)
- else
- TFilter.Seek(Pos);
- end;
- end;
-
- procedure TLimitFilter.Write(var Buf; Count : Word);
- begin
- if Status = stOk then
- begin
- if GetPos + Count > HiLimit then
- Error(stWriteError,0)
- else
- TFilter.Write(Buf,Count);
- end;
- end;
-
- function TLimitFilter.GetSize:longint;
- var
- result : longint;
- begin
- result := TFilter.GetSize;
- if result > HiLimit then
- GetSize := HiLimit
- else
- GetSize := result;
- end;
-
- { ****** Loop Filter code *****}
-
- procedure TLoopFilter.Read(var Buf; Count : Word);
- var
- buffer : TByte_Array absolute Buf;
- pos : word;
- begin
- if status = stOk then
- begin
- if GetPos + Count > HiLimit then
- begin
- pos := HiLimit - GetPos;
- TFilter.Read(Buf,pos);
- dec(count,pos);
- TFilter.Seek(LoLimit);
- Read(Buffer[pos],Count); { Recursive call! }
- end
- else
- Tfilter.Read(Buf,Count);
- end;
- end;
-
- procedure TLoopFilter.Seek(Pos : LongInt);
- var
- size : longint;
- begin
- size := HiLimit - LoLimit;
- if Pos < LoLimit then
- Pos := LoLimit + (Pos - LoLimit) mod Size + Size;
- TFilter.Seek(LoLimit + (Pos - LoLimit) mod Size);
- end;
-
- procedure TLoopFilter.Write(var Buf; Count : Word);
- var
- buffer : TByte_Array absolute Buf;
- pos : word;
- begin
- if status = stOk then
- begin
- if GetPos + Count > HiLimit then
- begin
- pos := HiLimit - GetPos;
- TFilter.Write(Buf,pos);
- dec(count,pos);
- TFilter.Seek(LoLimit);
- Write(Buffer[pos],Count); { Recursive call! }
- end
- else
- Tfilter.Write(Buf,Count);
- end;
- end;
-
- function TLoopFilter.GetSize:longint;
- var
- result : longint;
- begin
- result := TFilter.GetSize;
- if result > HiLimit then
- GetSize := HiLimit - LoLimit
- else
- GetSize := result - LoLimit;
- end;
-
- { ****** TReverseFilter code ******}
-
- constructor TReverseFilter.Init(ABase : PStream; AReverseBlocks:boolean);
- begin
- TFilter.Init(ABase);
- ReverseBlocks := AReverseBlocks;
- end;
-
- function TReverseFilter.GetPos:longint;
- begin
- GetPos := TFilter.GetSize-TFilter.GetPos;
- end;
-
- procedure TReverseFilter.Read(var Buf;Count : word);
- var
- curpos : longint;
- begin
- curpos := TFilter.GetPos; { We call the Tfilter methods to propagate errors }
- Base^.Seek(curpos-Count);
- Base^.Read(Buf,Count);
- if ReverseBlocks then
- ReverseBytes(Buf,Count);
- TFilter.Seek(curpos-Count);
- end;
-
- procedure TReverseFilter.Write(var Buf;Count : word);
- var
- curpos : longint;
- begin
- curpos := TFilter.GetPos; { We call the Tfilter methods to propagate errors }
- Base^.Seek(curpos-Count);
- if ReverseBlocks then
- ReverseBytes(Buf,Count);
- Base^.Write(Buf,Count);
- if ReverseBlocks then
- ReverseBytes(Buf,Count);
- TFilter.Seek(curpos-Count);
- end;
-
- procedure TReverseFilter.Seek(Pos:Longint);
- begin
- TFilter.Seek(TFilter.GetSize-Pos);
- end;
-
- procedure TReverseFilter.Truncate;
- begin
- Error(stUnsupported,0);
- end;
-
- procedure ReverseBytes(var Buf; Count:Word);
- var
- buffer : TByte_Array absolute Buf;
- i,j : word;
- t : byte;
- begin
- if Count > 1 then
- begin
- j := Count;
- for i:=0 to (Count div 2) - 1 do
- begin
- t := buffer[i];
- buffer[i] := buffer[j];
- buffer[j] := t;
- dec(j);
- end;
- end;
- end;
-
- { ****** Checksum/CRC code ******}
-
- Function UpdateChksum(initsum:word; var Inbuf; inlen:word):word;
- var
- i : word;
- bbuf : TByte_Array absolute inbuf;
- begin
- for i:=0 to pred(inlen) do
- inc(initsum,bbuf[i]);
- UpdateChksum := initsum;
- end;
-
- { From the original CRC.PAS: }
-
- { This unit provides three speed-optimized functions to compute (or continue
- computation of) a Cyclic Redundency Check (CRC). These routines are
- contributed to the public domain (with the limitations noted by the
- original authors in the TASM sources).
-
- Each function takes three parameters:
-
- InitCRC - The initial CRC value. This may be the recommended initialization
- value if this is the first or only block to be checked, or this may be
- a previously computed CRC value if this is a continuation.
-
- InBuf - An untyped parameter specifying the beginning of the memory area
- to be checked.
-
- InLen - A word indicating the length of the memory area to be checked. If
- InLen is zero, the function returns the value of InitCRC.
-
- The function result is the updated CRC. The input buffer is scanned under
- the limitations of the 8086 segmented architecture, so the result will be
- in error if InLen > 64k - Offset(InBuf).
-
- These conversions were done on 10-29-89 by:
-
- Edwin T. Floyd [76067,747]
- #9 Adams Park Court
- Columbus, GA 31909
- (404) 576-3305 (work)
- (404) 322-0076 (home)
- }
-
- Function UpdateCRC16(InitCRC : Word; Var InBuf; InLen : Word) : Word;
- external; {$L crc16.obj}
- { I believe this is the CRC used by the XModem protocol. The transmitting
- end should initialize with zero, UpdateCRC16 for the block, Continue the
- UpdateCRC16 for two nulls, and append the result (hi order byte first) to
- the transmitted block. The receiver should initialize with zero and
- UpdateCRC16 for the received block including the two byte CRC. The
- result will be zero (why?) if there were no transmission errors. (I have
- not tested this function with an actual XModem implementation, though I
- did verify the behavior just described. See TESTCRC.PAS.) }
-
-
- Function UpdateCRCArc(InitCRC : Word; Var InBuf; InLen : Word) : Word;
- external; {$L crcarc.obj}
- { This function computes the CRC used by SEA's ARC utility. Initialize
- with zero. }
-
- Function UpdateCRC32(InitCRC : LongInt; Var InBuf; InLen : Word) : LongInt;
- external; {$L crc32.obj}
- { This function computes the CRC used by PKZIP and Forsberg's ZModem.
- Initialize with high-values ($FFFFFFFF), and finish by inverting all bits
- (Not). }
-
- { ****** Sequential filter code ****** }
-
- procedure TSequential.Seek(pos:longint);
- begin
- Error(stUnsupported,0);
- end;
-
- { ****** Chksum filter code ******}
-
- constructor TChkSumFilter.init(ABase:PStream; AChksum:word);
- begin
- if not TSequential.init(ABase) then
- fail;
- Chksum := AChksum;
- end;
-
- procedure TChkSumFilter.Read(var buf; Count:word);
- begin
- TSequential.Read(buf,count);
- if status = stOK then
- ChkSum := UpdateChksum(ChkSum,buf,Count);
- end;
-
- procedure TChkSumFilter.Write(var buf; Count:word);
- begin
- TSequential.Write(buf,count);
- if status = stOk then
- ChkSum := UpdateChksum(ChkSum,buf,Count);
- end;
-
- { ***** CRC16 filter code ***** }
-
- constructor TCRC16Filter.init(ABase:PStream; ACRC16:word);
- begin
- if not TSequential.init(ABase) then
- fail;
- CRC16 := ACRC16;
- end;
-
- procedure TCRC16Filter.Read(var buf; Count:word);
- begin
- TSequential.Read(buf,count);
- if status = stOK then
- CRC16 := UpdateCRC16(CRC16,buf,count);
- end;
-
- procedure TCRC16Filter.Write(var buf; Count:word);
- begin
- TSequential.Write(buf,count);
- if status = stOk then
- CRC16 := UpdateCRC16(CRC16,buf,count);
- end;
-
- { ***** CRCARC filter code ***** }
-
- constructor TCRCARCFilter.init(ABase:PStream; ACRCARC:word);
- begin
- if not TSequential.init(ABase) then
- fail;
- CRCARC := ACRCARC;
- end;
-
- procedure TCRCARCFilter.Read(var buf; Count:word);
- begin
- TSequential.Read(buf,count);
- if status = stOK then
- CRCARC := UpdateCRCARC(CRCARC,buf,count);
- end;
-
- procedure TCRCARCFilter.Write(var buf; Count:word);
- begin
- TSequential.Write(buf,count);
- if status = stOk then
- CRCARC := UpdateCRCARC(CRCARC,buf,count);
- end;
-
- { ***** CRC32 filter code ***** }
-
- constructor TCRC32Filter.init(ABase:PStream; ACRC32:longint);
- begin
- if not TSequential.init(ABase) then
- fail;
- CRC32 := ACRC32;
- end;
-
- procedure TCRC32Filter.Read(var buf; Count:word);
- begin
- TSequential.Read(buf,count);
- if status = stOK then
- CRC32 := UpdateCRC32(CRC32,buf,count);
- end;
-
- procedure TCRC32Filter.Write(var buf; Count:word);
- begin
- TSequential.Write(buf,count);
- if status = stOk then
- CRC32 := UpdateCRC32(CRC32,buf,count);
- end;
-
- { ****** Null stream code ****** }
-
- constructor TNulStream.Init;
- begin
- TStream.Init;
- Position := 0;
- Value := AValue;
- end;
-
- function TNulStream.GetPos;
- begin
- GetPos := Position;
- end;
-
- function TNulStream.GetSize;
- begin
- GetSize := Position;
- end;
-
- procedure TNulStream.Read;
- begin
- FillChar(Buf, Count, Value);
- Inc(Position, Count);
- end;
-
- procedure TNulStream.Seek;
- begin
- Position := Pos;
- end;
-
- procedure TNulStream.Write;
- begin
- Inc(Position, Count);
- end;
-
- { ****** RAM stream code ****** }
-
- constructor TRAMStream.Init(Asize : Word);
- begin
- TStream.Init;
- Position := 0;
- Size := 0;
- Alloc := Asize;
- if MaxAvail < Alloc then
- Fail;
- GetMem(Buffer, Alloc);
- OwnMem := True;
- FillChar(Buffer^, Alloc, 0);
- end;
-
- constructor TRAMStream.UseBuf(ABuffer : Pointer; Asize : Word);
- begin
- TRAMStream.Init(0);
- Alloc := Asize;
- Size := Asize;
- Buffer := ABuffer;
- OwnMem := False;
- end;
-
- destructor TRAMStream.Done;
- begin
- if OwnMem then
- FreeMem(Buffer, Alloc);
- TStream.Done;
- end;
-
- function TRAMStream.GetPos;
- { begin Replaced with assembler for speed.
- GetPos := Position;
- end; }
- assembler;
- asm
- les di,self
- mov ax,es:di[Position];
- xor dx,dx
- end;
-
- function TRAMStream.GetSize;
- { begin Replaced with assembler for speed.
- GetSize := Size;
- end; }
- assembler;
- asm
- les di,self
- mov ax,es:di[size]
- xor dx,dx
- end;
-
- function CheckInc(var pos:word;count,limit:word):boolean; assembler;
- { Increments pos by count, returns false if limit is exceeded }
- asm
- les di,pos
- mov bx,count
- mov al,true
- add bx,es:[di]
- jc @1 { Carry means error }
- mov es:[di],bx
- sub bx,limit
- jbe @2
- @1:
- dec ax { Set AX to false }
- @2:
- end;
-
- procedure TRAMStream.Read;
- begin
- Move(Buffer^[Position], Buf, Count);
- if not CheckInc(Position,Count,Size) then
- begin
- Error(stReadError,0);
- Dec(Position,Count);
- FillChar(Buf,Count,0);
- end;
- end;
-
- procedure TRAMStream.Seek;
- begin
- if Pos > Size then
- Error(stReaderror, 0)
- else
- Position := Pos;
- end;
-
- procedure TRAMStream.Truncate;
- begin
- Size := Position;
- end;
-
- procedure TRAMStream.Write;
- begin
- if not CheckInc(Position,Count,Alloc) then
- Error(stWriteError, 0)
- else
- begin
- Move(Buf, Buffer^[Position-Count], Count);
- if Position > Size then
- Size := Position;
- end;
- end;
-
- { ***** XMS stream code ***** }
-
- {$I xmsstrm.inc}
-
- { ***** EMS size code ***** }
-
- function exist_ems:boolean;
- const
- ems_found : boolean = false; { Used as initialized var }
- var
- S : TEMSStream;
- begin
- if not ems_found then
- begin
- S.init(1,1);
- ems_found := S.status = stOk;
- S.done;
- end;
- exist_ems := ems_found;
- end;
-
- function ems_maxavail: longint;
- begin
- if not exist_ems then
- ems_maxavail:=0
- else
- asm
- mov ah,$42;
- int $67
- mov ax,16384
- mul bx
- mov word ptr @result,ax
- mov word ptr @result[2],dx
- end;
- end;
-
- function ems_memavail: longint;
- begin
- ems_memavail := ems_maxavail;
- end;
-
- function GetTempList:String;
- { Function to get the list of directories for temp files }
- var
- {$ifdef windows}
- p : PChar;
- {$endif}
- result : string;
- begin
- {$ifdef windows}
- p := GetEnvVar(@TempEnvVar[1]);
- if p <> nil then
- result := StrPas(p)
- else
- result := '';
- {$else}
- result := GetEnv(TempEnvVar);
- {$endif}
- if Length(result) = 0 then
- result := '.\';
- GetTempList := result;
- end;
-
- function GetTempDir(var TempList:string):string;
- { Strip one temp directory off the front of the list, and
- return it fully qualified, with a '\' at the end. }
- var
- Semicolon : byte;
- result : string;
- curdir : string;
- begin
- Semicolon := Pos(';',TempList);
- if Semicolon > 0 then
- begin
- result := Copy(TempList,1,Semicolon-1);
- TempList := Copy(TempList,Semicolon+1,255);
- end
- else
- begin
- result := TempList;
- TempList := '';
- end;
- if result[Length(result)] <> '\' then
- result := result+'\';
- if (length(result) < 2) or (result[2] <> ':') then
- GetDir(0,curdir)
- else
- begin
- GetDir(ord(upcase(result[1]))-ord('A')+1,curdir);
- result := copy(result,3,255);
- end;
- if (length(result) > 1) and (result[1] <> '\') then
- result := curdir + '\' + result
- else
- result := copy(curdir,1,2) + result;
- GetTempDir := result;
- end;
-
- function disk_maxavail: longint;
- var
- templist,tempname : string;
- result : longint;
- begin
- result := 0;
- templist := GetTempList;
- repeat
- tempname := GetTempDir(templist);
- result := MaxLong(result,
- DiskFree(ord(upcase(tempname[1]))-ord('A')+1))
- until templist = '';
- disk_maxavail := result;
- end;
-
- function disk_memavail: longint;
- var
- templist,tempname : string;
- result,space : longint;
- disk : byte;
- disks : array[1..32] of boolean;
- begin
- fillchar(disks,sizeof(disks),false);
- result := 0;
- templist := GetTempList;
- repeat
- tempname := GetTempDir(templist);
- disk := ord(upcase(tempname[1]))-ord('A')+1;
- if not disks[disk] then
- begin
- disks[disk] := true;
- space := DiskFree(disk);
- end
- else
- space := 0;
- if space > 0 then
- inc(result,space);
- until templist = '';
- disk_memavail := result;
- end;
-
- { ***** Named Buffered file stream code ***** }
-
- constructor TNamedBufStream.Init(Name : FNameStr; Mode : TOpenMode; ABufSize : Word);
- begin
- if TBufStream.Init(Name, Mode, ABufSize) then
- {$ifdef windows}
- filename := StrNew(name)
- {$else}
- Filename := NewStr(Name)
- {$endif}
- else
- Fail;
- end;
-
- destructor TNamedBufStream.Done;
- begin
- {$ifdef windows}
- StrDispose(filename);
- {$else}
- DisposeStr(Filename);
- {$endif}
- TBufStream.Done;
- end;
-
- constructor TTempBufStream.Init(ABufSize : Word;
- InitSize,MaxSize : Longint);
- var
- TempList,TempName : String;
- Okay : Boolean;
- NewHandle : Word;
- F : File;
- begin
- if not TStream.Init then
- Fail;
- if MaxAvail < ABufSize then
- Fail;
- BufSize := ABufSize;
- GetMem(Buffer, BufSize);
- MaxSize := MaxLong(MinLong(MaxSize,Disk_MaxAvail),InitSize);
- TempList := GetTempList;
- repeat
- TempName := GetTempDir(TempList);
- FillChar(TempName[Length(TempName)+1], 255-Length(TempName), #0);
- asm
- push ds
- push ss
- pop ds
- lea dx,TempName[1]
- mov ah, $5a
- xor cx,cx
- {$ifdef windows}
- call dos3call
- {$else}
- int $21 { Create temporary file. }
- {$endif}
- pop ds
- jc @failed
- mov Okay,True
- mov NewHandle,ax
- jmp @done
- @failed:
- mov Okay,False
- @done:
- end;
- if okay then
- begin
- Handle := NewHandle;
- while TempName[Length(TempName)+1] <> #0 do
- Inc(TempName[0]);
- {$ifdef windows}
- Filename := StrNew(StrPCopy(@tempname[1],tempname));
- {$else}
- Filename := NewStr(TempName);
- {$endif}
- Seek(MaxSize-1);
- Write(okay,1); { Write a 0 }
- Flush;
- Seek(InitSize);
- Truncate;
- okay := Status = stOK;
- if not okay and (TempList <> '') then
- begin
- asm
- mov ah,$3E
- mov bx,NewHandle
- int $21 { Close file }
- end;
- assign(F,filename^);
- Erase(F);
- Reset;
- {$ifdef windows}
- StrDispose(Filename);
- {$else}
- DisposeStr(Filename);
- {$endif}
- Filename := nil;
- end;
- end;
- until okay or (TempList = '');
- end;
-
- destructor TTempBufStream.Done;
- var
- F : file;
- begin
- {$ifdef windows}
- assign(f,StrPas(Filename));
- {$else}
- Assign(F, Filename^);
- {$endif}
- TNamedBufStream.Done;
- Erase(F);
- end;
-
- {******** TWorkStream code ******* }
-
- constructor TWorkStream.init(Allocator:TAllocator;ABlockmin,ABlockMax:Longint;
- APreference : TStreamRanking);
- begin
- TFilter.init(Allocator(ABlockmin,ABlockmax,APreference));
- Allocate := Allocator;
- Blockmin := ABlockmin;
- Blockmax := ABlockmax;
- Preference := APreference;
- BlockStart := 0;
- end;
-
- procedure TWorkStream.write(var Buf; Count:Word);
- var
- Buffer : TByte_array absolute Buf;
- firstpart : word;
- byteswritten : word;
- pos : longint;
- NewBase : PStream;
- saveStatus, saveInfo : integer;
- begin
- pos := GetPos;
- byteswritten := 0;
- if CheckStatus then
- repeat
- firstpart := Count;
- if (Pos < BlockStart+BlockMax) and (Pos+firstpart > BlockStart+BlockMax) then
- firstpart := BlockStart+BlockMax-Pos;
- TFilter.Write(Buffer[byteswritten], firstpart);
-
- { **** crummy code to get around problems with TBufStream **** }
- { The test is an efficiency hack - we don't want to flush every
- segment of the stream, just the last one. }
- if typeof(Base^) = typeof(TConcatFilter) then
- PConcatFilter(Base)^.Base2^.Flush
- else
- Base^.Flush; { Must flush all writes to see TBufStream
- errors immediately :-( }
- { **** end of crummy code :-) ***** }
- if Status = stOK then
- begin
- dec(Count,firstpart);
- inc(Pos,firstpart);
- inc(byteswritten,firstpart);
- end
- else
- begin
- saveStatus := Status;
- saveInfo := ErrorInfo;
- Reset;
- if Pos = GetSize then
- begin
- { If write failed at eof, allocate a new block }
- Seek(0);
- NewBase := Allocate(BlockMin,BlockMax,Preference);
- if (NewBase = nil) or (NewBase^.Status <> stOK) then
- begin
- error(stBaseError, stWriteError);
- exit;
- end;
- Base := New(PConcatFilter,init(Base,NewBase));
- BlockStart := Pos;
- end
- else { Some other kind of write failure; restore the error status }
- begin
- error(saveStatus,saveInfo);
- exit;
- end;
- end;
- until count = 0;
- end;
-
- { ***** Temp Stream Code ******* }
-
- function TempStream(InitSize, MaxSize : LongInt;
- Preference : TStreamRanking) : PStream;
- var
- Choice : Integer;
- Result : PStream;
- StreamType : TStreamType;
- Nulls : TNulStream;
- begin
- Result := nil;
- Nulls.Init(0);
- for Choice := 1 to NumTypes do
- begin
- StreamType := Preference[Choice];
- case StreamType of
- RAMStream :
- if MaxSize < $10000 then
- Result := New(PRAMStream, Init(MaxSize));
- EMSStream :
- if ems_MaxAvail >= MaxSize then
- Result := New(PEMSStream, Init(InitSize, MaxSize));
- XMSStream :
- if xms_MaxAvail >= MaxSize then
- Result := New(PXMSStream, Init(InitSize, MaxSize));
- FileStream :
- if disk_MaxAvail >= MaxSize then
- Result := New(PTempBufStream, Init(2048, InitSize, MaxSize));
- end;
- if (Result <> nil) and (Result^.Status = stOK) then
- begin
- Result^.Copyfrom(Nulls, InitSize);
- Result^.Seek(0);
- if Result^.Status = stOK then
- begin
- Nulls.Done;
- TempStream := Result;
- Exit;
- end;
- end;
- if Result <> nil then
- Dispose(Result, Done); { Clean up and start over } ;
- Result := nil;
- end;
- TempStream := nil;
- end;
-
- function StreamName(S:PStream):String;
- { This function is for debugging only! It links every single stream
- type into your .EXE. }
- var
- t : pointer;
- begin
- if S=nil then
- StreamName := 'nil'
- else
- begin
- t := typeof(S^);
- if t = typeof(TStream) then StreamName := 'TStream'
- else if t = typeof(TEMSStream) then StreamName := 'TEMSStream'
- else if t = typeof(TDOSStream) then StreamName := 'TDOSStream'
- else if t = typeof(TBufStream) then StreamName := 'TBufStream'
- else if t = typeof(TFilter) then StreamName := 'TFilter'
- else if t = typeof(TEncryptFilter) then StreamName := 'TEncryptFilter'
- else if t = typeof(TLZWFilter) then StreamName := 'TLZWFilter'
- else if t = typeof(TTextFilter) then StreamName := 'TTextFilter'
- else if t = typeof(TLogFilter) then StreamName := 'TLogFilter'
- else if t = typeof(TBitFilter) then StreamName := 'TBitFilter'
- else if t = typeof(TDupFilter) then StreamName := 'TDupFilter'
- else if t = typeof(TConcatFilter) then StreamName := 'TConcatFilter'
- else if t = typeof(TLimitFilter) then StreamName := 'TLimitFilter'
- else if t = typeof(TLoopFilter) then StreamName := 'TLoopFilter'
- else if t = typeof(TReverseFilter) then StreamName := 'TReverseFilter'
- else if t = typeof(TSequential) then StreamName := 'TSequential'
- else if t = typeof(TChksumFilter) then StreamName := 'TChksumFilter'
- else if t = typeof(TCRC16Filter) then StreamName := 'TCRC16Filter'
- else if t = typeof(TCRCARCFilter) then StreamName := 'TCRCARCFilter'
- else if t = typeof(TCRC32Filter) then StreamName := 'TCRC32Filter'
- else if t = typeof(TNulStream) then StreamName := 'TNulStream'
- else if t = typeof(TRAMStream) then StreamName := 'TRAMStream'
- else if t = typeof(TXMSStream) then StreamName := 'TXMSStream'
- else if t = typeof(TNamedBufStream) then StreamName := 'TNamedBufStream'
- else if t = typeof(TTempBufStream) then StreamName := 'TTempBufStream'
- else if t = typeof(TWorkStream) then StreamName := 'TWorkStream'
- else StreamName := 'Unknown (or uninitialized) stream';
- end;
- end;
-
- end.