DEADSOFTWARE

no more old mapreader: use textmap reader both for text and for binary maps
[d2df-sdl.git] / src / shared / xparser.pas
index fe17d0c8a700aa1dc656c899534901f151d390ad..7263b7db60037eef69dba6c49ffa9b6cac5a3108 100644 (file)
@@ -102,14 +102,21 @@ type
 // ////////////////////////////////////////////////////////////////////////// //
 type
   TFileTextParser = class(TTextParser)
+  private
+    const BufSize = 65536;
+
   private
     mFile: TStream;
+    mBuffer: PChar;
+    mBufLen: Integer;
+    mBufPos: Integer;
 
   protected
     procedure loadNextChar (); override; // loads next char into mNextChar; #0 means 'eof'
 
   public
     constructor Create (const fname: AnsiString; loadToken: Boolean=true);
+    constructor Create (st: TStream; loadToken: Boolean=true); // will take ownership on st
     destructor Destroy (); override;
   end;
 
@@ -170,6 +177,11 @@ uses
   SysUtils, utils;
 
 
+// ////////////////////////////////////////////////////////////////////////// //
+function StrEqu (const a, b: AnsiString): Boolean; inline; begin result := (a = b); end;
+
+
+// ////////////////////////////////////////////////////////////////////////// //
 var
   wc2shitmap: array[0..65535] of AnsiChar;
   wc2shitmapInited: Boolean = false;
@@ -521,7 +533,7 @@ end;
 
 procedure TTextParser.expectId (const aid: AnsiString);
 begin
-  if (mTokType <> TTId) or (CompareText(mTokStr, aid) <> 0) then raise Exception.Create('identifier '''+aid+''' expected');
+  if (mTokType <> TTId) or (not StrEqu(mTokStr, aid)) then raise Exception.Create('identifier '''+aid+''' expected');
   skipToken();
 end;
 
@@ -529,7 +541,7 @@ end;
 function TTextParser.eatId (const aid: AnsiString): Boolean;
 begin
   result := false;
-  if (mTokType <> TTId) or (CompareText(mTokStr, aid) <> 0) then exit;
+  if (mTokType <> TTId) or (not StrEqu(mTokStr, aid)) then exit;
   result := true;
   skipToken();
 end;
@@ -586,24 +598,49 @@ end;
 // ////////////////////////////////////////////////////////////////////////// //
 constructor TFileTextParser.Create (const fname: AnsiString; loadToken: Boolean=true);
 begin
+  mBuffer := nil;
   mFile := openDiskFileRO(fname);
+  GetMem(mBuffer, BufSize);
+  mBufPos := 0;
+  mBufLen := mFile.Read(mBuffer^, BufSize);
+  if (mBufLen < 0) then raise Exception.Create('TFileTextParser: read error');
+  inherited Create(loadToken);
+end;
+
+
+constructor TFileTextParser.Create (st: TStream; loadToken: Boolean=true);
+begin
+  if (st = nil) then raise Exception.Create('cannot create parser for nil stream');
+  mFile := st;
+  GetMem(mBuffer, BufSize);
+  mBufPos := 0;
+  mBufLen := mFile.Read(mBuffer^, BufSize);
+  if (mBufLen < 0) then raise Exception.Create('TFileTextParser: read error');
   inherited Create(loadToken);
 end;
 
 
 destructor TFileTextParser.Destroy ();
 begin
+  if (mBuffer <> nil) then FreeMem(mBuffer);
   mFile.Free();
   inherited;
 end;
 
 
 procedure TFileTextParser.loadNextChar ();
-var
-  rd: Integer;
 begin
-  rd := mFile.Read(mNextChar, 1);
-  if (rd = 0) then begin mNextChar := #0; exit; end;
+  if (mBufLen = 0) then begin mNextChar := #0; exit; end;
+  if (mBufPos >= mBufLen) then
+  begin
+    mBufLen := mFile.Read(mBuffer^, BufSize);
+    if (mBufLen < 0) then raise Exception.Create('TFileTextParser: read error');
+    if (mBufLen = 0) then begin mNextChar := #0; exit; end;
+    mBufPos := 0;
+  end;
+  assert(mBufPos < mBufLen);
+  mNextChar := mBuffer[mBufPos];
+  Inc(mBufPos);
   if (mNextChar = #0) then mNextChar := ' ';
 end;