home *** CD-ROM | disk | FTP | other *** search
/ Black Box 4 / BlackBox.cdr / w3_prog / stream11.arj / STREAMS.PAS < prev   
Encoding:
Pascal/Delphi Source File  |  1992-03-31  |  39.8 KB  |  1,443 lines

  1. unit Streams;
  2.  
  3. { Unit to provide enhancements to TV Objects unit streams in the form
  4.   of several filters, i.e. stream clients, and other streams. }
  5.  
  6. {$O-}
  7.   { Don't overlay this unit; it contains code that needs to participate
  8.          in overlay management. }
  9.  
  10. {  Hierarchy:
  11.  
  12.    TStream                  (from Objects)
  13.      TFilter                Base type for filters
  14.        TEncryptFilter       Encrypts as it writes; decrypts as it reads
  15.        TLZWFilter           Compresses as it writes; expands as it reads
  16.        TTextFilter          Provides text file interface to stream
  17.        TLogFilter           Provides logging of text file activity
  18.      TRAMStream             Stream in memory
  19.      TDOSStream             (from Objects)
  20.        TBufStream           (from Objects)
  21.          TNamedBufStream    Buffered file stream that knows its name
  22.            TTempBufStream   Buffered file stream that erases itself when done
  23.  
  24.    Procedures & functions:
  25.  
  26.    TempStream      allocates a temporary stream
  27.    OvrInitStream   like OvrInitEMS, but buffers overlays on a stream
  28.                    May be called several times to buffer different
  29.                    segments on different streams.
  30.    OvrDetachStream detaches stream from overlay system
  31.    OvrDisposeStreams detaches all streams from overlay system and disposes of
  32.                    them
  33.    OvrSizeNeeded   Calculates the size needed to load the rest of the segments
  34.                    to a stream
  35.    OvrLoadAll      immediately copies as many overlay segments to the stream
  36.                    as will fit
  37.  
  38. }
  39.  
  40. interface
  41.  
  42. {$ifdef windows}
  43. uses strings,windos,winprocs,wobjects;
  44. {$else}
  45. uses DOS, Overlay, Objects;
  46. {$endif}
  47.  
  48. const
  49.   stBadMode = 1;                  { Bad mode for stream - operation not supported }
  50.   stStreamFail = 2;               { Stream init failed }
  51.   stBaseError = 3;                { Error in base stream }
  52.   stMemError = 4;                 { Not enough memory for operation }
  53.   stSigError = 5;                 { Problem with LZ file signature }
  54.  
  55. type
  56.   TOpenMode = $3C00..$3DFF;       { Allowable DOS stream open modes }
  57.   {$ifdef windows}
  58.   FNameStr = PChar;            { To make streams take names as in the manual }
  59.   {$endif}
  60.  
  61.   PFilter = ^TFilter;
  62.   TFilter =
  63.     object(TStream)
  64.     { Generic object to filter another stream.  TFilter just passes everything
  65.       through, and mirrors the status of the base stream }
  66.  
  67.       Base : PStream;
  68.       { Pointer to the base stream. }
  69.  
  70.       Startofs : LongInt;
  71.       { The offset of the start of the filter in the base stream. }
  72.  
  73.       constructor Init(ABase : PStream);
  74.         { Initialize the filter with the given base. }
  75.  
  76.       destructor Done; virtual;
  77.         { Dispose of base. }
  78.  
  79.       function GetPos : LongInt; virtual;
  80.       function GetSize : LongInt; virtual;
  81.       procedure Read(var Buf; Count : Word); virtual;
  82.       procedure Seek(Pos : LongInt); virtual;
  83.       procedure Truncate; virtual;
  84.       procedure Write(var Buf; Count : Word); virtual;
  85.  
  86.       function CheckStatus : Boolean; virtual;
  87.     { Return true if status is stOK.
  88.       If status is stOK, but base is not, then reset the base.  This is a poor
  89.       substitute for a virtual Reset method. }
  90.  
  91.       procedure CheckBase;
  92.         { Check base stream for error, and copy status using own Error method. }
  93.     end;
  94.  
  95.   PEncryptFilter = ^TEncryptFilter;
  96.   TEncryptFilter =
  97.     object(TFilter)
  98.   { Filter which encrypts text going in or out; encrypting twice with the same
  99.     key decrypts. Not very sophisticated encryption. }
  100.  
  101.       Key : LongInt;
  102.       { Key is used as a Randseed replacement }
  103.  
  104.       constructor Init(Akey : LongInt; ABase : PStream);
  105.         { Init with a given key }
  106.  
  107.       procedure Read(var Buf; Count : Word); virtual;
  108.       procedure Seek(Pos : LongInt); virtual;
  109.       procedure Write(var Buf; Count : Word); virtual;
  110.     end;
  111.  
  112. const
  113.   MaxStack = 4096;                { must match lzwstream.asm declaration! }
  114.  
  115. type
  116.   Plzwtables = ^TLZWTables;
  117.   TLZWTables =
  118.     record
  119.       Collision : array[0..MaxStack-1] of Byte; { Hash table entries }
  120.       PrefixTable : array[0..MaxStack-1] of Word; { Code for preceding stringf }
  121.       SuffixTable : array[0..MaxStack-1] of Byte; { Code for current character }
  122.       ChildTable : array[0..MaxStack-1] of Word; { Next duplicate in collision
  123.                                                  list }
  124.       CharStack : array[0..MaxStack-1] of Byte; { Decompression stack }
  125.       StackPtr : Word;            { Decompression stack depth }
  126.       Prefix : Word;              { Previous code string }
  127.       TableUsed : Word;           { # string table entries used }
  128.       InputPos : Word;            { Index in input buffer }
  129.       OutputPos : Word;           { Index in output buffer }
  130.       LastHit : Word;             { Last empty slot in collision
  131.                                                  table }
  132.       CodeBuf : Word;
  133.       SaveIP : Word;
  134.       SaveAX : Word;
  135.       SaveCX : Word;
  136.       SaveDX : Word;
  137.  
  138.       NotFound : Byte;            { Character combination found
  139.                                                  flag }
  140.     end;
  141.  
  142.   PLZWFilter = ^TLZWFilter;
  143.   TLZWFilter =
  144.     object(TFilter)
  145.       Mode : Word;                { Either stOpenRead or stOpenWrite. }
  146.       Size,                       { The size of the expanded stream. }
  147.       Position : LongInt;         { The current position in the expanded stream }
  148.       Tables : Plzwtables;        { Tables holding the compressor state. }
  149.  
  150.       constructor Init(ABase : PStream; AMode : TOpenMode);
  151.     {  Create new compressor stream, to use ABase as the source/destination
  152.        for data.  Mode must be stOpenRead or stOpenWrite. }
  153.  
  154.       destructor Done; virtual;
  155.     {  Flushes all data to the stream, and writes the uncompressed
  156.        filesize to the head of it before calling TFilter.done. }
  157.  
  158.       procedure Flush; virtual;
  159.       function GetPos : LongInt; virtual;
  160.       function GetSize : LongInt; virtual;
  161.       procedure Read(var Buf; Count : Word); virtual;
  162.  
  163.       procedure Seek(Pos : LongInt); virtual;
  164.     {  Seek is not supported at all in Write mode.  In Read mode, it is
  165.        slow for seeking forwards, and very slow for seeking backwards:
  166.        it rewinds the file to the start and seeks forward from there. }
  167.  
  168.       procedure Truncate; virtual;
  169.     {  Truncate is not supported in either mode, and always causes a
  170.        call to Error. }
  171.  
  172.       procedure Write(var Buf; Count : Word); virtual;
  173.     end;
  174.  
  175. type
  176.   PTextFilter = ^TTextFilter;
  177.   TTextFilter =
  178.     object(TFilter)
  179.   { A filter to provide ReadLn/WriteLn interface to a stream.  First
  180.     open the stream and position it, then pass it to this filter;
  181.     then Reset, Rewrite, or Append the Textfile variable, and do all
  182.     reads and writes to it; they'll go to the stream through a TFDD. }
  183.  
  184.       Textfile : Text;
  185.       { The fake text file to use with Read(ln)/Write(ln) }
  186.  
  187.       constructor Init(ABase : PStream; AName : String);
  188.     { Initialize the interface to ABase; stores AName in the name field of
  189.       Textfile. }
  190.  
  191.       destructor Done; virtual;
  192.         { Flushes the Textfile, then closes and disposes of the base stream. }
  193.     end;
  194.  
  195.   PLogFilter = ^TLogFilter;
  196.   TLogFilter =
  197.     object(TFilter)
  198.       { A filter to log activity on a text file. }
  199.  
  200.       LogList : ^Text;            { A pointer to the first logged file }
  201.  
  202.       destructor Done; virtual;
  203.         { Stops logging all files, and closes & disposes of the base stream }
  204.  
  205.       procedure Log(var F : Text);
  206.     { Logs all input and output to F to the stream.  You must do the Assign to
  207.       F first, and not do another Assign without closing F. }
  208.  
  209.       function Unlog(var F : Text) : Boolean;
  210.     { Stops logging of F.  Called automatically if file is closed. Returns
  211.       false and does nothing on error. }
  212.     end;
  213.  
  214.   Pbyte_array = ^Tbyte_array;
  215.   Tbyte_array = array[0..65520] of Byte; { Type used as a buffer. }
  216.  
  217.   PRAMStream = ^TRAMStream;
  218.   TRAMStream =
  219.     object(TStream)
  220.       CP : Word;    { The current pointer for the stream. }
  221.  
  222.       Size : Word;  { The current size of the stream. }
  223.       Alloc : Word; { The size of the allocated block of memory. }
  224.  
  225.       Buffer : Pbyte_array;
  226.       { A pointer to the block of memory holding the stream data. }
  227.  
  228.       constructor Init(Asize : Word);
  229.     { Attempt to initialize the stream to a block size of Asize;
  230.        initial stream size and position are 0. }
  231.  
  232.       destructor Done; virtual;
  233.         { Dispose of the stream. }
  234.  
  235.       function GetPos : LongInt; virtual;
  236.       function GetSize : LongInt; virtual;
  237.       procedure Read(var Buf; Count : Word); virtual;
  238.       procedure Seek(Pos : LongInt); virtual;
  239.       procedure Truncate; virtual;
  240.       procedure Write(var Buf; Count : Word); virtual;
  241.     end;
  242.  
  243.   PNamedBufStream = ^TNamedBufStream;
  244.   TNamedBufStream =
  245.     object(TBufStream)
  246.       { A simple descendant of TBufStream which knows its own name. }
  247.  
  248.     {$ifdef windows}
  249.     filename : PChar;
  250.     {$else}
  251.       Filename : PString;
  252.     {$endif}
  253.       { The name of the stream. }
  254.  
  255.       constructor Init(Name : FNameStr; Mode : TOpenMode; ABufSize : Word);
  256.         { Open the file with the given name, and save the name. }
  257.  
  258.       destructor Done; virtual;
  259.         { Close the file. }
  260.  
  261.     end;
  262.  
  263.   PTempBufStream = ^TTempBufStream;
  264.   TTempBufStream =
  265.     object(TNamedBufStream)
  266.       { A temporary buffered file stream, which deletes itself when done.}
  267.  
  268.       constructor Init(ABufSize : Word);
  269.   { Create a temporary file with a unique name, in the directory
  270.     pointed to by the environment varable TEMP or in the current
  271.     directory, and open it in read/write mode.   }
  272.  
  273.       destructor Done; virtual;
  274.         { Close and delete the temporary file. }
  275.  
  276.     end;
  277.  
  278. type
  279.   TStreamType = (NoStream, RAMStream, EMSStream, FileStream);
  280.   { The type of stream that a tempstream might be. }
  281.  
  282. const
  283.   NumTypes = Ord(FileStream);
  284.   BufSize : Word = 2048;          { Buffer size if buffered stream is used. }
  285.  
  286. type
  287.   TStreamRanking = array[1..NumTypes] of TStreamType;
  288.   { A ranking of preference for a type of stream, from most to least preferred }
  289.  
  290. const ForSpeed : TStreamRanking = (RAMStream, EMSStream, FileStream);
  291.   { Streams ordered for speed }
  292.  
  293. const ForSize : TStreamRanking = (FileStream, EMSStream, RAMStream);
  294.   { Streams ordered for low impact on the heap }
  295.  
  296. const ForSizeInMem : TStreamRanking = (EMSStream, RAMStream, NoStream);
  297.   { Streams in memory only, ordered as #ForSize#. }
  298.  
  299. const ForOverlays : TStreamRanking = (EMSStream, FileStream, NoStream);
  300.   { Streams ordered for speed, but never in RAM. }
  301.  
  302. function TempStream(InitSize, MaxSize : LongInt;
  303.                     Preference : TStreamRanking) : PStream;
  304.  
  305. {      This procedure returns a pointer to a temporary stream from a
  306.        choice of 3, specified in the Preference array.  The first stream
  307.        type listed in the Preference array which can be successfully
  308.        created with the given sizes will be returned, or Nil if none can
  309.        be made. }
  310.  
  311. procedure OvrInitStream(S : PStream);
  312. { Copies overlay segment code to S as new segments are loaded,
  313.   and does reloads from there.  Allows multiple calls, to buffer
  314.   different segments on different streams. }
  315.  
  316. procedure OvrDetachStream(BadS : PStream);
  317.   { Makes sure that the overlay system makes no references to BadS. }
  318.  
  319. procedure OvrDisposeStreams;
  320.   { Detaches and disposes of all streams being used by the overlay system }
  321.  
  322. function OvrSizeNeeded : LongInt;
  323. { Returns the size required to load any segments which still haven't
  324.   been loaded to a stream. }
  325.  
  326. function OvrLoadAll : Boolean;
  327. { Forces all overlay segments to be copied into the stream; if successful
  328.   (true) then no more references to the overlay file will be made. }
  329.  
  330. implementation
  331.  
  332.   constructor TFilter.Init(ABase : PStream);
  333.   begin
  334.     TStream.Init;
  335.     Base := ABase;
  336.     CheckBase;
  337.     if Status = stOK then
  338.       Startofs := Base^.GetPos;
  339.   end;
  340.  
  341.   destructor TFilter.Done;
  342.   begin
  343.     if Base <> nil then
  344.       Dispose(Base, Done);
  345.     TStream.Done;
  346.   end;
  347.  
  348.   function TFilter.GetPos : LongInt;
  349.   begin
  350.     if CheckStatus then
  351.     begin
  352.       GetPos := Base^.GetPos-Startofs;
  353.       CheckBase;
  354.     end;
  355.   end;
  356.  
  357.   function TFilter.GetSize : LongInt;
  358.   begin
  359.     if CheckStatus then
  360.     begin
  361.       GetSize := Base^.GetSize-Startofs;
  362.       CheckBase;
  363.     end;
  364.   end;
  365.  
  366.   procedure TFilter.Read(var Buf; Count : Word);
  367.   begin
  368.     if CheckStatus then
  369.     begin
  370.       Base^.Read(Buf, Count);
  371.       CheckBase;
  372.     end;
  373.   end;
  374.  
  375.   procedure TFilter.Seek(Pos : LongInt);
  376.   begin
  377.     if CheckStatus then
  378.     begin
  379.       Base^.Seek(Pos+Startofs);
  380.       CheckBase;
  381.     end;
  382.   end;
  383.  
  384.   procedure TFilter.Truncate;
  385.   begin
  386.     if CheckStatus then
  387.     begin
  388.       Base^.Truncate;
  389.       CheckBase;
  390.     end;
  391.   end;
  392.  
  393.   procedure TFilter.Write(var Buf; Count : Word);
  394.   begin
  395.     if CheckStatus then
  396.     begin
  397.       Base^.Write(Buf, Count);
  398.       CheckBase;
  399.     end;
  400.   end;
  401.  
  402.   function TFilter.CheckStatus : Boolean;
  403.   begin
  404.     if (Status = stOK) and (Base^.Status <> stOK) then
  405.       Base^.Reset;
  406.     CheckStatus := Status = stOK;
  407.   end;
  408.  
  409.   procedure TFilter.CheckBase;
  410.   begin
  411.     if Base^.Status <> stOK then
  412.       Error(stBaseError, Base^.Status);
  413.   end;
  414.  
  415.   constructor TEncryptFilter.Init(Akey : LongInt; ABase : PStream);
  416.   begin
  417.     TFilter.Init(ABase);
  418.     Key := Akey;
  419.   end;
  420.  
  421.   procedure TEncryptFilter.Read(var Buf; Count : Word);
  422.   var
  423.     i : Word;
  424.     SaveSeed : LongInt;
  425.     Bytes : Tbyte_array absolute Buf;
  426.   begin
  427.     SaveSeed := RandSeed;
  428.     RandSeed := Key;
  429.     TFilter.Read(Buf, Count);
  430.     for i := 0 to Count-1 do
  431.       Bytes[i] := Bytes[i] xor Random(256);
  432.     Key := RandSeed;
  433.     RandSeed := SaveSeed;
  434.   end;
  435.  
  436.   procedure CycleKey(Key, Cycles : LongInt);
  437. { For cycles > 0, mimics cycles calls to the TP random number generator.
  438.   For cycles < 0, backs it up the given number of calls. }
  439.   var
  440.     i : LongInt;
  441.     Junk : Integer;
  442.     SaveSeed : LongInt;
  443.   begin
  444.     if Cycles > 0 then
  445.     begin
  446.       SaveSeed := RandSeed;
  447.       RandSeed := Key;
  448.       for i := 1 to Cycles do
  449.         Junk := Random(0);
  450.       Key := RandSeed;
  451.       RandSeed := Key;
  452.     end
  453.     else
  454.       for i := -1 downto Cycles do
  455.         Key := (Key-1)*(-649090867);
  456.   end;
  457.  
  458.   procedure TEncryptFilter.Seek(Pos : LongInt);
  459.   var
  460.     OldPos : LongInt;
  461.   begin
  462.     OldPos := GetPos;
  463.     TFilter.Seek(Pos);
  464.     CycleKey(Key, Pos-OldPos);
  465.   end;
  466.  
  467.   procedure TEncryptFilter.Write(var Buf; Count : Word);
  468.   var
  469.     i : Word;
  470.     SaveSeed : LongInt;
  471.     BufPtr : ^Byte;
  472.     BufPtrOffset : Word absolute BufPtr;
  473.     Buffer : array[0..255] of Byte;
  474.   begin
  475.     SaveSeed := RandSeed;
  476.     RandSeed := Key;
  477.     BufPtr := @Buf;
  478.     while Count > 256 do
  479.     begin
  480.       Move(BufPtr^, Buffer, 256);
  481.       for i := 0 to 255 do
  482.         Buffer[i] := Buffer[i] xor Random(256);
  483.       TFilter.Write(Buffer, 256);
  484.       Dec(Count, 256);
  485.       Inc(BufPtrOffset, 256);
  486.     end;
  487.     Move(BufPtr^, Buffer, Count);
  488.     for i := 0 to Count-1 do
  489.       Buffer[i] := Buffer[i] xor Random(256);
  490.     TFilter.Write(Buffer, Count);
  491.     Key := RandSeed;
  492.     RandSeed := SaveSeed;
  493.   end;
  494.  
  495.  
  496.   { ******* LZW code ******* }
  497.  
  498. {$L LZWSTREAM.OBJ}
  499.  
  500.   procedure Initialise(Tables : Plzwtables); External;
  501.  
  502.   function PutSignature(Tables : Plzwtables) : Boolean; External;
  503.  
  504.   function Crunch(InBufSize, OutBufSize : Word;
  505.                   var InBuffer, OutBuffer;
  506.   Tables : Plzwtables) : Pointer; External;
  507.  
  508. {  Crunch some more text.  Stops when Inbufsize bytes are used up, or
  509.    output buffer is full.   Returns bytes used in segment, bytes written
  510.    in offset of result }
  511.  
  512.   function FlushLZW(var OutBuffer;
  513.   Tables : Plzwtables) : Word; External;
  514. {  Flush the remaining characters to signal EOF.  Needs space for up to
  515.    3 characters. }
  516.  
  517.   function GetSignature(var InBuffer, Dummy;
  518.   Tables : Plzwtables) : Boolean; External;
  519. { Initializes for reading, and checks for 'LZ' signature in start of compressed
  520.   code.  Inbuffer must contain at least 3 bytes.  Dummy is just there to put the
  521.   Inbuffer in the right spot }
  522.  
  523.   function Uncrunch(InBufSize, OutBufSize : Word;
  524.                     var InBuffer, OutBuffer;
  525.   Tables : Plzwtables) : Pointer; External;
  526. {  Uncrunch some text.  Will stop when it has done Outbufsize worth or has
  527.    exhausted Inbufsize worth.  Returns bytes used in segment, bytes written
  528.    in offset of result }
  529.  
  530.   constructor TLZWFilter.Init(ABase : PStream; AMode : TOpenMode);
  531.     {  Create new compressor stream, to use ABase as the source/destination
  532.        for data.  Mode must be stOpenRead or stOpenWrite. }
  533.   var
  534.     Out : LongInt;
  535.     Buffer : array[1..3] of Byte;
  536.     Info : Integer;
  537.   begin
  538.     Info := stBadMode;
  539.     if (AMode = stOpenRead) or (AMode = stOpenWrite) then
  540.     begin
  541.       Info := stStreamFail;
  542.       if TFilter.Init(ABase) then
  543.       begin
  544.         if Status = stOK then
  545.         begin
  546.           Info := stMemError;
  547.           Startofs := Base^.GetPos;
  548.           Position := 0;
  549.           Mode := AMode;
  550.  
  551.           if MaxAvail >= SizeOf(TLZWTables) then
  552.           begin
  553.             Info := stSigError;
  554.             GetMem(Tables, SizeOf(TLZWTables));
  555.             Initialise(Tables);
  556.             if Mode = stOpenRead then
  557.             begin
  558.               Base^.Read(Size, SizeOf(Size));
  559.               Base^.Read(Buffer, 3);
  560.               CheckBase;
  561.               if GetSignature(Buffer, Buffer, Tables) then
  562.                 Exit;             { Successfully opened for reading }
  563.             end
  564.             else if Mode = stOpenWrite then
  565.             begin
  566.               Size := 0;
  567.               Base^.Write(Size, SizeOf(Size)); { Put a place holder }
  568.               CheckBase;
  569.               if PutSignature(Tables) then
  570.                 Exit;             { Successful construction for writing! }
  571.             end;
  572.           end;
  573.         end;
  574.       end;
  575.     end;
  576.     Error(stInitError, Info);
  577.   end;
  578.  
  579.   destructor TLZWFilter.Done;
  580.   var
  581.     Pos : LongInt;
  582.   begin
  583.     if CheckStatus and (Mode = stOpenWrite) then
  584.       Flush;
  585.     FreeMem(Tables, SizeOf(TLZWTables));
  586.     TFilter.Done;
  587.   end;
  588.  
  589.   procedure TLZWFilter.Write(var Buf; Count : Word);
  590.   var
  591.     Inbuf : array[0..65520] of Byte absolute Buf;
  592.     Outbuf : array[0..255] of Byte;
  593.     Inptr : Word;
  594.     Sizes : record
  595.               OutSize, UsedSize : Word;
  596.             end;
  597.   begin
  598.     if CheckStatus then
  599.     begin
  600.       if Mode <> stOpenWrite then
  601.         Error(stBadMode, Mode);
  602.       Inptr := 0;
  603.       repeat
  604.         Pointer(Sizes) := Crunch(Count, SizeOf(Outbuf),
  605.                                  Inbuf[Inptr], Outbuf, Tables);
  606.         with Sizes do
  607.         begin
  608.           Base^.Write(Outbuf, OutSize);
  609.  
  610.           Dec(Count, UsedSize);
  611.           Inc(Inptr, UsedSize);
  612.           Inc(Size, UsedSize);
  613.           Inc(Position, UsedSize);
  614.         end;
  615.       until Count = 0;
  616.       CheckBase;
  617.     end;
  618.   end;
  619.  
  620.   procedure TLZWFilter.Flush;
  621.   var
  622.     Outbuf : array[0..255] of Byte;
  623.     OutSize : Word;
  624.     Sizes : record
  625.               OutSize, UsedSize : Word;
  626.             end;
  627.     Pos   : longint;
  628.   begin
  629.     if CheckStatus then
  630.     begin
  631.       if Mode = stOpenWrite then
  632.       begin
  633.         Pointer(Sizes) := Crunch(1, SizeOf(Outbuf), Outbuf, Outbuf, Tables);
  634.         { Push one more character to match JA bug }
  635.         with Sizes do
  636.         begin
  637.           Base^.Write(Outbuf, OutSize);
  638.  
  639.           OutSize := FlushLZW(Outbuf, Tables); { And flush }
  640.           Base^.Write(Outbuf, OutSize);
  641.         end;
  642.         Pos := Base^.GetPos;
  643.         Base^.Seek(Startofs);
  644.         Base^.Write(Size, SizeOf(Size));
  645.         Base^.Seek(Pos);
  646.       end;
  647.       Base^.Flush;
  648.       Mode := 0;
  649.       CheckBase;
  650.     end;
  651.   end;
  652.  
  653.   procedure TLZWFilter.Read(var Buf; Count : Word);
  654.   var
  655.     Outbuf : array[0..65520] of Byte absolute Buf;
  656.     Inbuf : array[0..255] of Byte;
  657.     OutPtr : Word;
  658.     BlockSize : Word;
  659.     Sizes : record
  660.               OutSize, UsedSize : Word;
  661.             end;
  662.     BytesLeft : LongInt;
  663.   begin
  664.     if CheckStatus then
  665.     begin
  666.       if Mode <> stOpenRead then
  667.         Error(stBadMode, Mode);
  668.       OutPtr := 0;
  669.       BlockSize := SizeOf(Inbuf);
  670.       with Base^ do
  671.         BytesLeft := GetSize-GetPos;
  672.  
  673.       if Position+Count > Size then
  674.       begin
  675.         Error(stReaderror, 0);
  676.         FillChar(Buf, Count, 0);
  677.         Exit;
  678.       end;
  679.  
  680.       while Count > 0 do
  681.       begin
  682.         if BytesLeft < BlockSize then
  683.           BlockSize := BytesLeft;
  684.         Base^.Read(Inbuf, BlockSize);
  685.         Pointer(Sizes) := Uncrunch(BlockSize, Count, Inbuf,
  686.                                    Outbuf[OutPtr], Tables);
  687.         with Sizes do
  688.         begin
  689.           if OutSize = 0 then
  690.           begin
  691.             Error(stReaderror, 0);
  692.             FillChar(Outbuf[OutPtr], Count, 0);
  693.             Exit;
  694.           end;
  695.           Dec(BytesLeft, UsedSize);
  696.           Inc(Position, OutSize);
  697.           Dec(Count, OutSize);
  698.           Inc(OutPtr, OutSize);
  699.           if UsedSize < BlockSize then
  700.             with Base^ do         { seek back to the first unused byte }
  701.               Seek(GetPos-(BlockSize-UsedSize));
  702.         end;
  703.       end;
  704.       CheckBase;
  705.     end;
  706.   end;
  707.  
  708.   procedure TLZWFilter.Seek(Pos : LongInt);
  709.   var
  710.     Buf : array[0..255] of Byte;
  711.     Bytes : Word;
  712.   begin
  713.     if CheckStatus then
  714.     begin
  715.       if Mode <> stOpenRead then
  716.       begin
  717.         Error(stBadMode, Mode);
  718.         Exit;
  719.       end;
  720.       if Pos < Position then
  721.       begin
  722.         Base^.Seek(Startofs);
  723.         FreeMem(Tables, SizeOf(TLZWTables));
  724.  
  725.         TLZWFilter.Init(Base, Mode); { Re-initialize everything.  Will this cause
  726.                                      bugs in descendents? }
  727.       end;
  728.       while Pos > Position do
  729.       begin
  730.         if Pos-Position > SizeOf(Buf) then
  731.           Bytes := SizeOf(Buf)
  732.         else
  733.           Bytes := Pos-Position;
  734.         Read(Buf, Bytes);
  735.       end;
  736.     end;
  737.   end;
  738.  
  739.   procedure TLZWFilter.Truncate;
  740.   begin
  741.     Error(stBadMode, Mode);
  742.   end;
  743.  
  744.   function TLZWFilter.GetPos;
  745.   begin
  746.     GetPos := Position;
  747.   end;
  748.  
  749.   function TLZWFilter.GetSize;
  750.   begin
  751.     GetSize := Size;
  752.   end;
  753.  
  754.   { ***** Text Filter Code ******* }
  755.  
  756.   { These declarations are used both by TTextFilter and TLogFilter }
  757.  
  758. type
  759.   TFDDfunc = function(var F : Text) : Integer;
  760.  
  761.   PStreamTextRec = ^StreamTextRec;
  762.   PSaveText = ^TSaveText;
  763.   TSaveText =
  764.     record                        { Used when logging for original data values }
  765.       OpenFunc,
  766.       InOutFunc,
  767.       FlushFunc,
  768.       CloseFunc : TFDDfunc;
  769.       S : PLogFilter;
  770.       SaveData : PSaveText;
  771.       Next : PStreamTextRec;
  772.       Data : array[13..16] of Byte;
  773.     end;
  774.  
  775.   StreamTextRec =
  776.     record
  777.       Handle : Word;
  778.       Mode : Word;
  779.       BufSize : Word;
  780.       private : Word;
  781.       BufPos : Word;
  782.       BufEnd : Word;
  783.       BufPtr : Pbyte_array;
  784.       OpenFunc,
  785.       InOutFunc,
  786.       FlushFunc,
  787.       CloseFunc : TFDDfunc;
  788.       S : PFilter;                { This is a TTextFilter or a TLogFilter }
  789.       SaveData : PSaveText;
  790.       Next : PStreamTextRec;
  791.       OtherData : array[13..16] of Byte;
  792.       Name : array[0..79] of Char;
  793.       Buffer : array[0..127] of Byte;
  794.     end;
  795.  
  796.  
  797.   function TextIn(var F : Text) : Integer; Far;
  798.   begin
  799.     with StreamTextRec(F), S^ do
  800.     begin
  801.       if Status = 0 then
  802.       begin
  803.         if GetSize-GetPos > BufSize then
  804.         begin
  805.           Read(BufPtr^, BufSize);
  806.           BufEnd := BufSize;
  807.         end
  808.         else
  809.         begin
  810.           BufEnd := GetSize-GetPos;
  811.           if BufEnd > 0 then
  812.             Read(BufPtr^, BufEnd);
  813.         end;
  814.       end;
  815.       TextIn := Status;
  816.     end;
  817.   end;
  818.  
  819.   function TextOut(var F : Text) : Integer; Far;
  820.   begin
  821.     with StreamTextRec(F), S^ do
  822.     begin
  823.       if Status = 0 then
  824.       begin
  825.         Write(BufPtr^, BufPos);
  826.         BufPos := 0;
  827.       end;
  828.       TextOut := Status;
  829.     end;
  830.   end;
  831.  
  832.   function TextInFlush(var F : Text) : Integer; Far;
  833.   begin
  834.   end;
  835.  
  836.   function TextOutFlush(var F : Text) : Integer; Far;
  837.   begin
  838.     TextOutFlush := TextOut(F);
  839.   end;
  840.  
  841.   function TextClose(var F : Text) : Integer; Far;
  842.   begin
  843.     TextClose := StreamTextRec(F).S^.Status;
  844.   end;
  845.  
  846.   function TextOpen(var F : Text) : Integer; Far;
  847.   begin
  848.     with StreamTextRec(F) do
  849.     begin
  850.       case Mode of
  851.         fmInOut : Mode := fmOutput;
  852.         fmOutput : S^.Seek(S^.Startofs);
  853.       end;
  854.       case Mode of
  855.         fmInput : begin
  856.                     InOutFunc := TextIn;
  857.                     FlushFunc := TextInFlush;
  858.                   end;
  859.         fmOutput : begin
  860.                      InOutFunc := TextOut;
  861.                      FlushFunc := TextOutFlush;
  862.                    end;
  863.       end;
  864.       TextOpen := S^.Status;
  865.     end;
  866.   end;
  867.  
  868.   constructor TTextFilter.Init(ABase : PStream; AName : String);
  869.   begin
  870.     if not TFilter.Init(ABase) then
  871.       Fail;
  872.     with StreamTextRec(Textfile) do
  873.     begin
  874.       Mode := fmClosed;
  875.       BufSize := SizeOf(Buffer);
  876.       BufPtr := @Buffer;
  877.       OpenFunc := TextOpen;
  878.       CloseFunc := TextClose;
  879.       AName := Copy(AName, 1, 79);
  880.       Move(AName[1], Name, Length(AName));
  881.       Name[Length(AName)] := #0;
  882.       S := @Self;
  883.     end;
  884.   end;
  885.  
  886.   destructor TTextFilter.Done;
  887.   begin
  888.     if StreamTextRec(Textfile).Mode <> fmClosed then
  889.       Close(Textfile);
  890.     TFilter.Done;
  891.   end;
  892.  
  893.   function DoOldCall(Func : TFDDfunc; var F : Text) : Integer;
  894.   var
  895.     Save : TSaveText;
  896.   begin
  897.     if @Func <> nil then
  898.       with StreamTextRec(F) do
  899.       begin
  900.         Move(OpenFunc, Save, SizeOf(TSaveText));
  901.         Move(SaveData^, OpenFunc, SizeOf(TSaveText)); { Now using old functions }
  902.         DoOldCall := Func(F);
  903.         Move(OpenFunc, Save.SaveData^, SizeOf(TSaveText)); { Save any changes }
  904.         Move(Save, OpenFunc, SizeOf(TSaveText)); { Back to new ones }
  905.       end;
  906.   end;
  907.  
  908.   function LogIn(var F : Text) : Integer; Far;
  909.   var
  910.     Result : Integer;
  911.   begin
  912.     with StreamTextRec(F) do
  913.     begin
  914.       Result := DoOldCall(SaveData^.InOutFunc, F);
  915.       if Result = 0 then
  916.         S^.Write(BufPtr^, BufEnd); { Might want to record errors
  917.                                                here }
  918.       LogIn := Result;
  919.     end;
  920.   end;
  921.  
  922.   function LogOut(var F : Text) : Integer; Far;
  923.   begin
  924.     with StreamTextRec(F) do
  925.     begin
  926.       S^.Write(BufPtr^, BufPos);
  927.       LogOut := DoOldCall(SaveData^.InOutFunc, F);
  928.     end;
  929.   end;
  930.  
  931.   function LogInFlush(var F : Text) : Integer; Far;
  932.   begin
  933.     with StreamTextRec(F) do
  934.       LogInFlush := DoOldCall(SaveData^.FlushFunc, F);
  935.   end;
  936.  
  937.   function LogOutFlush(var F : Text) : Integer; Far;
  938.   var
  939.     OldPos : Word;
  940.   begin
  941.     with StreamTextRec(F) do
  942.     begin
  943.       OldPos := BufPos;
  944.       LogOutFlush := DoOldCall(SaveData^.FlushFunc, F);
  945.       if BufPos = 0 then
  946.         S^.Write(BufPtr^, OldPos);
  947.     end;
  948.   end;
  949.  
  950.   function LogClose(var F : Text) : Integer; Far;
  951.   begin
  952.     with StreamTextRec(F) do
  953.     begin
  954.       LogClose := DoOldCall(SaveData^.CloseFunc, F);
  955.       if not PLogFilter(S)^.Unlog(F) then
  956.         { Bug! } ;
  957.     end;
  958.   end;
  959.  
  960.   function LogOpen(var F : Text) : Integer; Far;
  961.   begin
  962.     with StreamTextRec(F) do
  963.     begin
  964.       LogOpen := DoOldCall(SaveData^.OpenFunc, F);
  965.       case Mode of
  966.         fmInOut, fmOutput : begin
  967.                               InOutFunc := LogOut;
  968.                               if @FlushFunc <> nil then
  969.                                 FlushFunc := LogOutFlush;
  970.                             end;
  971.         fmInput : begin
  972.                     InOutFunc := LogIn;
  973.                     if @FlushFunc <> nil then
  974.                       FlushFunc := LogInFlush;
  975.                   end;
  976.       end;
  977.     end;
  978.   end;
  979.  
  980.   { ******* TLogFilter methods ******** }
  981.  
  982.   destructor TLogFilter.Done;
  983.   begin
  984.     while (LogList <> nil) and Unlog(LogList^) do ;
  985.     TFilter.Done;
  986.   end;
  987.  
  988.   procedure TLogFilter.Log(var F : Text);
  989.   var
  990.     Save : PSaveText;
  991.     OldOpen : TFDDfunc;
  992.     Junk : Integer;
  993.  
  994.   begin
  995.     New(Save);
  996.     with StreamTextRec(F) do
  997.     begin
  998.       Move(OpenFunc, Save^, SizeOf(TSaveText)); { Save the original contents }
  999.       S := @Self;
  1000.       SaveData := Save;
  1001.       Next := PStreamTextRec(LogList);
  1002.       LogList := @F;              { Insert this file into the list of logged files }
  1003.       OldOpen := SaveData^.OpenFunc;
  1004.       Pointer(@SaveData^.OpenFunc) := nil; { Call LogOpen, but don't open. }
  1005.       Junk := LogOpen(F);
  1006.       SaveData^.OpenFunc := OldOpen;
  1007.       CloseFunc := LogClose;
  1008.     end;
  1009.   end;
  1010.  
  1011.   function TLogFilter.Unlog(var F : Text) : Boolean;
  1012.   var
  1013.     Save : PSaveText;
  1014.     Prev : PStreamTextRec;
  1015.   begin
  1016.     Unlog := False;               { Assume failure }
  1017.     with StreamTextRec(F) do
  1018.     begin
  1019.       if S = @Self then
  1020.       begin
  1021.         { First, delete it from the list. }
  1022.         if LogList = @F then
  1023.           LogList := Pointer(Next)
  1024.         else
  1025.         begin
  1026.           Prev := PStreamTextRec(LogList);
  1027.           while (Prev^.Next <> nil) and (Prev^.Next <> @F) do
  1028.             Prev := Prev^.Next;
  1029.           if Prev^.Next <> @F then
  1030.             Exit;                 { Couldn't find it in the list!? }
  1031.           Prev^.Next := Next;
  1032.         end;
  1033.         Save := SaveData;
  1034.         Move(Save^, OpenFunc, SizeOf(TSaveText));
  1035.         Dispose(Save);
  1036.         Unlog := True;
  1037.       end;
  1038.     end;
  1039.   end;
  1040.  
  1041.   { ****** Overlay stream code ****** }
  1042.  
  1043. type
  1044.   { This is the structure at the start of each "thunk" segment }
  1045.   Povrhead = ^TOvrhead;
  1046.   TOvrhead = record
  1047.                Signature : Word;  { CD 3F  - INT 3F call used on returns }
  1048.                Ret_Ofs : Word;    { The offset to jump to when a return triggers a
  1049.                             reload }
  1050.                Offset : LongInt;  { The offset to the segment in the .OVR file }
  1051.                Code_Bytes,        { Size of the code image }
  1052.                Reloc_Bytes,       { Number of relocation fixups times 2 }
  1053.                Entry_Count,       { The number of entry points }
  1054.                NextSeg,           { Next overlay segment - add prefixseg + $10 to find
  1055.                             thunks.  List starts with System.ovrcodelist. }
  1056.                LoadSeg,           { The segment at which the overlay is loaded, or 0 }
  1057.                Reprieve,          { Set to 1 to if overlay used while on probation }
  1058.                NextLoaded : Word; { The segment of the next loaded overlay.  List starts
  1059.                             with System.ovrloadlist.  Updated *after* call to
  1060.                             ovrreadbuf. }
  1061.                case Integer of
  1062.                  1 : (EMSPage,    { The EMS page where this overlay is stored }
  1063.                       EMSOffset : Word); { The offset within the EMS page }
  1064.                  2 : (S : PStream; { The stream holding this segment's code }
  1065.                       Soffset : LongInt); { The offset within S }
  1066.              end;
  1067.  
  1068. var
  1069.   OldReadFunc : OvrReadFunc;
  1070.   OvrOldExitProc : Pointer;
  1071.   OvrStream : PStream;
  1072. const
  1073.   OvrStreamInstalled : Boolean = False;
  1074.   OvrExitHandler : Boolean = False;
  1075.  
  1076.   function OvrPtr(Seg : Word) : Povrhead;
  1077. { Convert map style segment number, as used by overlay manager, to
  1078.   pointer }
  1079.   begin
  1080.     OvrPtr := Ptr(Seg+PrefixSeg+$10, 0);
  1081.   end;
  1082.  
  1083.   function StdPtr(Seg : Word) : Povrhead;
  1084.     { Convert straight segment number to a pointer }
  1085.   begin
  1086.     StdPtr := Ptr(Seg, 0);
  1087.   end;
  1088.  
  1089.   function NewReadFunc(OvrSeg : Word) : Integer; Far;
  1090.   var
  1091.     Result : Integer;
  1092.   begin
  1093.     with StdPtr(OvrSeg)^ do
  1094.     begin
  1095.       if S = nil then
  1096.       begin                       { Segment not yet loaded }
  1097.         Result := OldReadFunc(OvrSeg);
  1098.         if Result = 0 then
  1099.         begin
  1100.           { Now copy the loaded code to our stream }
  1101.           Soffset := OvrStream^.GetSize;
  1102.           OvrStream^.Seek(Soffset);
  1103.           OvrStream^.Write(Ptr(LoadSeg, 0)^, Code_Bytes);
  1104.           Result := OvrStream^.Status;
  1105.           if Result = stOK then
  1106.             S := OvrStream
  1107.           else
  1108.             OvrStream^.Reset;     { Something failed; hope we haven't messed
  1109.                               up the stream too much }
  1110.         end;
  1111.       end
  1112.       else
  1113.       begin                       { Segment has been loaded into the stream }
  1114.         S^.Seek(Soffset);
  1115.         S^.Read(Ptr(LoadSeg, 0)^, Code_Bytes);
  1116.         Result := S^.Status;
  1117.         if Result <> stOK then
  1118.         begin
  1119.           S^.Reset;               { Fix the stream, and try a standard load }
  1120.           Result := OldReadFunc(OvrSeg);
  1121.         end;
  1122.       end;
  1123.     end;
  1124.     NewReadFunc := Result;
  1125.   end;
  1126.  
  1127.   procedure OvrExitProc; Far;
  1128. { Installed exit procedure; disposes of any streams that are still
  1129.   handling overlays. }
  1130.   begin
  1131.     ExitProc := OvrOldExitProc;
  1132.     OvrDisposeStreams;
  1133.   end;
  1134.  
  1135.   procedure OvrInitStream(S : PStream);
  1136.   begin
  1137.     if not OvrStreamInstalled then
  1138.     begin
  1139.       OldReadFunc := OvrReadBuf;  { Install our reader function }
  1140.       OvrReadBuf := NewReadFunc;
  1141.       OvrStreamInstalled := True;
  1142.     end;
  1143.     if not OvrExitHandler then
  1144.     begin
  1145.       OvrOldExitProc := ExitProc;
  1146.       ExitProc := @OvrExitProc;
  1147.       OvrExitHandler := True;
  1148.     end;
  1149.     OvrStream := S;               { And set stream to use }
  1150.   end;
  1151.  
  1152.   procedure OvrDetachStream(BadS : PStream);
  1153.   var
  1154.     OvrSeg : Word;
  1155.   begin
  1156.     if OvrStreamInstalled then
  1157.     begin
  1158.       if OvrStream = BadS then
  1159.         OvrStream := nil;         { Detach default stream }
  1160.       OvrSeg := OvrCodeList;
  1161.       while OvrSeg <> 0 do        { Walk the overlay list }
  1162.         with OvrPtr(OvrSeg)^ do
  1163.         begin
  1164.           if S <> nil then
  1165.           begin
  1166.             if S <> BadS then
  1167.             begin
  1168.               if OvrStream = nil then
  1169.                 OvrStream := S;   { Set default stream to first found }
  1170.             end
  1171.             else
  1172.               S := nil;           { Blank out BadS references }
  1173.           end;
  1174.           OvrSeg := NextSeg;
  1175.         end;
  1176.       if OvrStream = nil then
  1177.       begin
  1178.         OvrStreamInstalled := False; { If we don't have a stream, better
  1179.                                           uninstall. }
  1180.         OvrReadBuf := OldReadFunc;
  1181.       end;
  1182.     end;
  1183.   end;
  1184.  
  1185.   procedure OvrDisposeStreams;
  1186.   var
  1187.     S : PStream;
  1188.   begin
  1189.     while OvrStreamInstalled and (OvrStream <> nil) do
  1190.     begin
  1191.       S := OvrStream;
  1192.       OvrDetachStream(S);
  1193.       Dispose(S, Done);
  1194.     end;
  1195.   end;
  1196.  
  1197.   function OvrSizeNeeded : LongInt;
  1198.   var
  1199.     OvrSeg : Word;
  1200.     Result : LongInt;
  1201.   begin
  1202.     OvrSeg := OvrCodeList;
  1203.     Result := 0;
  1204.     while OvrSeg <> 0 do          { Walk the overlay list }
  1205.       with OvrPtr(OvrSeg)^ do
  1206.       begin
  1207.         if S = nil then
  1208.           Inc(Result, Code_Bytes);
  1209.         OvrSeg := NextSeg;
  1210.       end;
  1211.     OvrSizeNeeded := Result;
  1212.   end;
  1213.  
  1214.   function OvrLoadAll : Boolean;
  1215.   var
  1216.     OvrSeg : Word;
  1217.     Junk : Integer;
  1218.   begin
  1219.     if not OvrStreamInstalled then
  1220.       OvrLoadAll := False
  1221.     else
  1222.     begin
  1223.       OvrClearBuf;
  1224.       OvrSeg := OvrCodeList;
  1225.       while OvrSeg <> 0 do        { Walk the overlay list }
  1226.         with OvrPtr(OvrSeg)^ do
  1227.         begin
  1228.           if S = nil then
  1229.           begin
  1230.             LoadSeg := OvrHeapOrg; { load at start of overlay buffer }
  1231.             Junk := NewReadFunc(OvrSeg+PrefixSeg+$10);
  1232.             LoadSeg := 0;         { Don't really want it loaded yet }
  1233.           end;
  1234.           OvrSeg := NextSeg;
  1235.         end;
  1236.       OvrLoadAll := OvrStream^.Status = stOK;
  1237.     end;
  1238.   end;
  1239.  
  1240.   { ****** RAM stream code ****** }
  1241.  
  1242.   constructor TRAMStream.Init(Asize : Word);
  1243.   begin
  1244.     TStream.Init;
  1245.     CP := 0;
  1246.     Size := 0;
  1247.     Alloc := Asize;
  1248.     if MaxAvail < Alloc then
  1249.       Fail;
  1250.     GetMem(Buffer, Alloc);
  1251.     FillChar(Buffer^, Alloc, 0);
  1252.   end;
  1253.  
  1254.   destructor TRAMStream.Done;
  1255.   begin
  1256.     FreeMem(Buffer, Alloc);
  1257.     TStream.Done;
  1258.   end;
  1259.  
  1260.   function TRAMStream.GetPos;
  1261.   begin
  1262.     GetPos := CP;
  1263.   end;
  1264.  
  1265.   function TRAMStream.GetSize;
  1266.   begin
  1267.     GetSize := Size;
  1268.   end;
  1269.  
  1270.   procedure TRAMStream.Read;
  1271.   begin
  1272.     if CP+Count > Size then
  1273.     begin
  1274.       Error(stReaderror, 0);
  1275.       FillChar(Buf, Count, 0);
  1276.     end
  1277.     else
  1278.     begin
  1279.       Move(Buffer^[CP], Buf, Count);
  1280.       Inc(CP, Count);
  1281.     end;
  1282.   end;
  1283.  
  1284.   procedure TRAMStream.Seek;
  1285.   begin
  1286.     if Pos > Size then
  1287.       Error(stReaderror, 0)
  1288.     else
  1289.       CP := Pos;
  1290.   end;
  1291.  
  1292.   procedure TRAMStream.Truncate;
  1293.   begin
  1294.     Size := CP;
  1295.   end;
  1296.  
  1297.   procedure TRAMStream.Write;
  1298.   begin
  1299.     if CP+Count > Alloc then
  1300.       Error(stWriteError, 0)
  1301.     else
  1302.     begin
  1303.       Move(Buf, Buffer^[CP], Count);
  1304.       Inc(CP, Count);
  1305.       if CP > Size then
  1306.         Size := CP;
  1307.     end;
  1308.   end;
  1309.  
  1310.   { ***** Named Buffered file stream code ***** }
  1311.  
  1312.   constructor TNamedBufStream.Init(Name : FNameStr; Mode : TOpenMode; ABufSize : Word);
  1313.   begin
  1314.     if TBufStream.Init(Name, Mode, BufSize) then
  1315.     {$ifdef windows}
  1316.     filename := StrNew(name)
  1317.     {$else}
  1318.       Filename := NewStr(Name)
  1319.     {$endif}
  1320.     else
  1321.       Fail;
  1322.   end;
  1323.  
  1324.   destructor TNamedBufStream.Done;
  1325.   begin
  1326.   {$ifdef windows}
  1327.   StrDispose(filename);
  1328.   {$else}
  1329.     DisposeStr(Filename);
  1330.   {$endif}
  1331.     TBufStream.Done;
  1332.   end;
  1333.  
  1334.   constructor TTempBufStream.Init(ABufSize : Word);
  1335.   var
  1336.     p : Pchar;
  1337.     TempName : String;
  1338.     Okay : Boolean;
  1339.     NewHandle : Word;
  1340.   begin
  1341.     if not TStream.Init then
  1342.       Fail;
  1343.     if MaxAvail < ABufSize then
  1344.       Fail;
  1345.     BufSize := ABufSize;
  1346.     GetMem(Buffer, BufSize);
  1347.  
  1348.   {$ifdef windows}
  1349.   p := GetEnvVar('TEMP');
  1350.   if p <> nil then
  1351.     tempname := StrPas(p)
  1352.   else
  1353.     tempname := '';
  1354.   {$else}
  1355.     TempName := GetEnv('TEMP');
  1356.   {$endif}
  1357.     if Length(TempName) = 0 then
  1358.       TempName := '.\';
  1359.     if TempName[Length(TempName)] <> '\' then
  1360.       TempName := TempName+'\';
  1361.     FillChar(TempName[Length(TempName)+1], 255-Length(TempName), #0);
  1362.     asm
  1363.       push    ds
  1364.       push    ss
  1365.       pop     ds
  1366.       lea     dx,TempName[1]
  1367.       mov     ah, $5a
  1368.       xor     cx,cx
  1369.     {$ifdef windows}
  1370.     call dos3call
  1371.     {$else}
  1372.       int     $21                 { Create temporary file. }
  1373.     {$endif}
  1374.       pop     ds
  1375.       jc      @failed
  1376.       mov     Okay,True
  1377.       mov     NewHandle,ax
  1378.       jmp     @done
  1379. @failed:
  1380.       mov     Okay,False
  1381. @done:
  1382.     end;
  1383.     if not Okay then
  1384.       Fail;
  1385.     Handle := NewHandle;
  1386.     while TempName[Length(TempName)+1] <> #0 do
  1387.       Inc(TempName[0]);
  1388.   {$ifdef windows}
  1389.   filename := StrNew(StrPCopy(@tempname,tempname));
  1390.   {$else}
  1391.     Filename := NewStr(TempName);
  1392.   {$endif}
  1393.   end;
  1394.  
  1395.   destructor TTempBufStream.Done;
  1396.   var
  1397.     F : file;
  1398.   begin
  1399.   {$ifdef windows}
  1400.   assign(f,StrPas(Filename));
  1401.   {$else}
  1402.     Assign(F, Filename^);
  1403.   {$endif}
  1404.     TNamedBufStream.Done;
  1405.     Erase(F);
  1406.   end;
  1407.  
  1408.   { ***** Temp Stream Code ******* }
  1409.  
  1410.   function TempStream(InitSize, MaxSize : LongInt;
  1411.                       Preference : TStreamRanking) : PStream;
  1412.   var
  1413.     Choice : Integer;
  1414.     i : Integer;
  1415.     Result : PStream;
  1416.     StreamType : TStreamType;
  1417.   begin
  1418.     Result := nil;
  1419.     for Choice := 1 to NumTypes do
  1420.     begin
  1421.       StreamType := Preference[Choice];
  1422.       case StreamType of
  1423.         RAMStream :
  1424.           if MaxSize < $10000 then
  1425.             Result := New(PRAMStream, Init(MaxSize));
  1426.         EMSStream :
  1427.           Result := New(PEMSStream, Init(InitSize, MaxSize));
  1428.         FileStream :
  1429.           Result := New(PTempBufStream, Init(2048));
  1430.       end;
  1431.       if (Result <> nil) and (Result^.Status = stOK) then
  1432.       begin
  1433.         TempStream := Result;
  1434.         Exit;
  1435.       end;
  1436.       if Result <> nil then
  1437.         Dispose(Result, Done); { Clean up and start over } ;
  1438.       Result := nil;
  1439.     end;
  1440.   end;
  1441.  
  1442. end.
  1443.