
{$A+,B-,D+,E-,F-,G+,I+,L+,N-,O+,P+,Q+,R+,S+,T-,V-,X+,Y+}
{.$DEFINE OPRO}
{
  This unit adds an XMS-memory stream to TStream or IdStream
  depending on the define above.
  (c) 1994 Helge Olav Helgesen
  If you have any comments, please leave them in the Pascal
  conference on Rime or U'NI, or on InterNet to me at
  helge.helgesen@midnight.powertech.no
}
{$IFNDEF MSDOS}
  !! This unit must be compiled under real mode !!
{$ENDIF}
Unit Xms;

interface

uses
{$IFDEF OPRO}
  OpRoot,
{$ELSE}
  Objects,
{$ENDIF}
  OpDos, OpXms;

type
  PXmsStream = ^TXmsStream; { pointer to TXmsStream }
  TXmsStream = object({$IFDEF OPRO}IdStream{$ELSE}TStream{$ENDIF})
    XmsSizeInK, { allocated size in kilobytes }
    XmsHandle: word; { XMS Handle }
    TotalSize, { total size in bytes }
    CurOfs, { current offset into the stream }
    UsedSize: longint; { size of used stream }

    constructor Init(MemNeeded: word); { allocate ext. memory and init vars }
    destructor  Done; virtual; { deallocate ext. memory }

    procedure   Seek(WhereTo: longint); virtual; { seek within stream }
    function    GetPos: longint; virtual; { get curret offset }
    function    GetSize: longint; virtual; { get used size of stream }
    procedure   SetPos(Ofs: longint; Mode: byte); virtual; { seek using POS mode
 }

    procedure   Truncate; virtual; { truncate stream to current size }

    procedure   Write(var Buf; Count: Word); virtual; { writes Buf to the stream
 }
    procedure   Read(var Buf; Count: Word); virtual; { reads Buf from the stream
 }
  end; { TXmsStream }

{$IFNDEF OPRO}
var
  InitStatus: byte; { detailed error code from last Init or Done }
{$ENDIF}

const
  RealMemHandle = 0; { handle for Real Memory }
{$IFNDEF OPRO}
  PosAbs     = 0;               {Relative to beginning}
  PosCur     = 1;               {Relative to current position}
  PosEnd     = 2;               {Relative to end}
{$ENDIF}

{$IFDEF OPRO}
procedure SaveStream(const FileName: string; var S: IdStream);
  { Saves a stream to disk, old file is erased! }
procedure LoadStream(const FileName: string; var S: IdStream);
  { Loads a stream from disk }
{$ELSE}
procedure SaveStream(const FileName: string; var S: TStream);
  { Saves a stream to disk, old file is erased! }
procedure LoadStream(const FileName: string; var S: TStream);
  { Loads a stream from disk }
{$ENDIF}

implementation

constructor TXmsStream.Init;
  { You should already have tested if XMS is installed! }
begin
  if not inherited Init then Fail;
  InitStatus:=AllocateExtMem(MemNeeded, XmsHandle);
  if InitStatus>0 then Fail;
  XmsSizeInK:=MemNeeded;
  TotalSize:=LongInt(MemNeeded)*LongInt(1024);
  UsedSize:=0;
  CurOfs:=0;
end; { TXmsStream }

destructor TXmsStream.Done;
begin
  FreeExtMem(XmsHandle);
  inherited Done;
end; { TXmsStream.Done }

procedure TXmsStream.Seek;
begin
{$IFDEF OPRO}
  if idStatus=0 then
{$ELSE}
  if Status=stOk then
{$ENDIF}
  CurOfs:=WhereTo;
end; { TXmsStream }

function TXmsStream.GetPos;
begin
{$IFDEF OPRO}
  if idStatus=0 then
{$ELSE}
  if Status=stOk then
{$ENDIF}
  GetPos:=CurOfs else GetPos:=-1;
end; { TXmsStream.GetPos }

function TXmsStream.GetSize;
begin
{$IFDEF OPRO}
  if idStatus=0 then
{$ELSE}
  if Status=stOk then
{$ENDIF}
  GetSize:=UsedSize else GetSize:=-1;
end; { TXmsStream.GetSize }

procedure TXmsStream.Truncate;
begin
{$IFDEF OPRO}
  if idStatus=0 then
{$ELSE}
  if Status=stOk then
{$ENDIF}
  UsedSize:=CurOfs;
end; { TXmsStream.Truncate }

procedure TXmsStream.Write;
var
  NumberisOdd: boolean;
  x: word;
  Source, Dest: ExtMemPtr;
begin
{$IFDEF OPRO}
  if idStatus<>0 then
{$ELSE}
  if Status<>stOk then
{$ENDIF}
  Exit;
  if LongInt(Count)+LongInt(CurOfs)>LongInt(TotalSize) then
  begin
{$IFDEF OPRO}
    Error(101); { disk write error }
{$ELSE}
    Error(stWriteError, 0);
{$ENDIF}
    Exit;
  end; { if }
  NumberIsOdd:=Odd(Count);
  if NumberIsOdd then Dec(Count);
  Source.RealPtr:=@Buf;
  Dest.ProtectedPtr:=CurOfs;
  if Count>0 then
  x:=MoveExtMemBlock(Count, RealMemHandle, Source, { source data }
                     XmsHandle, Dest) { dest data }
  else x:=0;
  if x>0 then { new error }
  begin
{$IFDEF OPRO}
    Error(101); { disk write error }
{$ELSE}
    Error(stWriteError, x);
{$ENDIF}
    Exit;
  end; { if }
  Inc(CurOfs, Count); { adjust current offset }
  if CurOfs>UsedSize then UsedSize:=CurOfs;
  if not NumberisOdd then Exit;
  asm { get last byte to transfer }
    les  di, Buf
    mov  bx, Count
    mov  ax, es:[di+bx]
    inc  Count
    mov  x, ax
  end; { asm }
  Source.RealPtr:=@x;
  Inc(Dest.ProtectedPtr, Count-1);
  Count:=2;
  x:=MoveExtMemBlock(Count, RealMemHandle, Source, { source data }
                     XmsHandle, Dest); { dest data }
  if x>0 then { new error }
  begin
{$IFDEF OPRO}
    Error(101); { disk write error }
{$ELSE}
    Error(stWriteError, x);
{$ENDIF}
    Exit;
  end; { if }
  Inc(CurOfs);
  if CurOfs>UsedSize then UsedSize:=CurOfs;
end; { TXmsStream.Write }

procedure TXmsStream.Read;
var
  NumberisOdd: boolean;
  x: word;
  Source, Dest: ExtMemPtr;
begin
{$IFDEF OPRO}
  if idStatus<>0 then
{$ELSE}
  if Status<>stOk then
{$ENDIF}
  Exit;
  if LongInt(CurOfs)+LongInt(Count)>LongInt(UsedSize) then
  begin { read error }
{$IFDEF OPRO}
    Error(100); { read error }
{$ELSE}
    Error(stReadError, 0);
{$ENDIF}
    Exit;
  end; { if }
  NumberisOdd:=Odd(Count);
  if NumberisOdd then Inc(Count);
  Source.ProtectedPtr:=CurOfs;
  Dest.RealPtr:=@Buf;
  x:=MoveExtMemBlock(Count, XmsHandle, Source, { source data }
                     RealMemHandle, Dest); { dest data }
  if x>0 then
  begin
{$IFDEF OPRO}
    Error(100); { read error }
{$ELSE}
    Error(stReadError, x);
{$ENDIF}
    Exit;
  end; { if }
  if NumberisOdd then Dec(Count);
  Inc(CurOfs, Count);
end; { TXmsStream.Read }

procedure TXmsStream.SetPos;
begin
  case Mode of
    PosAbs: Seek(Ofs);
    PosCur: Seek(LongInt(Ofs)+LongInt(CurOfs));
    PosEnd: Seek(LongInt(UsedSize)-LongInt(Ofs));
  end; { case }
end; { TXmsStream.SetPos }

procedure SaveStream;
{
  Saves the stream to disk. No errorchecking is done
}
var
  Buf: pointer;
  x, BufSize: word;
  f: file;
  OldPos, l: longint;
begin
  Assign(f, FileName);
  Rewrite(f, 1);
  if S.GetSize=0 then
  begin
    Close(f);
    Exit;
  end; { if }
  if MaxAvail>65520 then BufSize:=65520 else BufSize:=MaxAvail;
  GetMem(Buf, BufSize);
  OldPos:=S.GetPos;
  l:=S.GetSize;
  S.Seek(0);
  while l<>0 do
  begin
    if l>BufSize then x:=BufSize else x:=l;
    S.Read(Buf^, x);
{$IFDEF OPRO}
    if S.PeekStatus<>0 then
{$ELSE}
    if S.Status<>0 then
{$ENDIF}
    begin
      Close(f);
      Exit;
    end; { if }
    BlockWrite(f, Buf^, x);
    Dec(l, x);
  end; { while }
  Close(f);
  FreeMem(Buf, BufSize);
  S.Seek(OldPos);
end; { SaveStream }

procedure LoadStream;
{
  Loads the stream from disk. No errorchecking is done, you must allocate
  enough memory yourself! Any old contents of the stream is erased.
}
var
  f: file;
  BufSize, x: word;
  l: longint;
  Buf: pointer;
begin
  if not ExistFile(FileName) then Exit;
  Assign(f, FileName);
  Reset(f, 1);
  S.Seek(0);
  S.Truncate;
  l:=FileSize(f);
  if l>0 then
  begin
    if MaxAvail>65520 then BufSize:=65520 else BufSize:=MaxAvail;
    GetMem(Buf, BufSize);
    while l<>0 do
    begin
      BlockRead(f, Buf^, BufSize, x);
      S.Write(Buf^, x);
{$IFDEF OPRO}
      if S.PeekStatus<>0 then
{$ELSE}
      if S.Status<>0 then
{$ENDIF}
      begin
        Close(f);
        Exit;
      end; { if }
      Dec(l, x);
    end; { while }
    FreeMem(Buf, BufSize);
  end; { if }
  Close(f);
  S.Seek(0);
end; { LoadStream }

end.
