DEADSOFTWARE

add default dirs for linux and android
[d2df-sdl.git] / src / engine / e_log.pas
index 472bd5762544126e0b60850d48eaf5ae6b7d7bac..7159c18445614beaff3ba4fc3f534df33d2cf474 100644 (file)
@@ -1,4 +1,18 @@
-{$MODE DELPHI}
+(* Copyright (C)  Doom 2D: Forever Developers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3 of the License ONLY.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *)
+{$INCLUDE ../shared/a_modes.inc}
 {$R-}
 { $DEFINE CBLOG}
 unit e_log;
@@ -10,27 +24,33 @@ uses
 
 type
   TWriteMode = (WM_NEWFILE,  WM_OLDFILE);
-  TRecordCategory = (MSG_FATALERROR, MSG_WARNING, MSG_NOTIFY);
+  TMsgType = (Fatal, Warning, Notify);
 
 
 procedure e_InitLog (fFileName: String; fWriteMode: TWriteMode);
-procedure e_WriteLog (TextLine: String; RecordCategory: TRecordCategory; WriteTime: Boolean=True);
-function DecodeIPV4 (ip: LongWord): string;
+procedure e_DeinitLog ();
+
+procedure e_SetSafeSlowLog (slowAndSafe: Boolean);
+
+procedure e_WriteLog (TextLine: String; RecordCategory: TMsgType; WriteTime: Boolean=True);
 
+function DecodeIPV4 (ip: LongWord): string;
 
 // start Write/WriteLn driver. it will write everything to cbuf.
 procedure e_InitWritelnDriver ();
 
+procedure e_LogWritefln (const fmt: AnsiString; args: array of const; category: TMsgType=TMsgType.Notify; writeTime: Boolean=true; writeConsole: Boolean=true);
+procedure e_LogWriteln (const s: AnsiString; category: TMsgType=TMsgType.Notify; writeTime: Boolean=true);
 
-var
-  e_WriteToStdOut: Boolean = False;
-
+procedure e_WriteStackTrace (const msg: AnsiString);
 
 implementation
 
 uses
-  conbuf;
-
+  {$IFDEF ANDROID}
+    SDL2,
+  {$ENDIF}
+  conbuf, utils;
 
 var
   FirstRecord: Boolean;
@@ -44,66 +64,225 @@ begin
 end;
 
 
-procedure e_WriteLog (TextLine: String; RecordCategory: TRecordCategory; WriteTime: Boolean=True);
+function consoleAllow (const s: String): Boolean;
+begin
+  Result := False;
+  if Pos('[Chat] ', s) = 1 then
+    Exit;
+  Result := True;
+end;
+
+
+procedure e_WriteLog (TextLine: String; RecordCategory: TMsgType; WriteTime: Boolean=True);
+begin
+  e_LogWritefln('%s', [TextLine], RecordCategory, WriteTime, consoleAllow(TextLine));
+end;
+
+
+procedure e_LogWriteln (const s: AnsiString; category: TMsgType=TMsgType.Notify; writeTime: Boolean=true);
+begin
+  e_LogWritefln('%s', [s], category, writeTime, consoleAllow(s));
+end;
+
+
+// returns formatted string if `writerCB` is `nil`, empty string otherwise
+//function formatstrf (const fmt: AnsiString; args: array of const; writerCB: TFormatStrFCallback=nil): AnsiString;
+//TFormatStrFCallback = procedure (constref buf; len: SizeUInt);
+
+procedure conwriter (constref buf; len: SizeUInt);
 var
-  LogFile: TextFile;
-  Prefix: ShortString = '';
-  OutStr: String;
+  ss: ShortString;
+  slen: Integer;
+  b: PByte;
+{$IFDEF ANDROID}
+  cstr: PChar;
+{$ENDIF}
+begin
+  if (len < 1) then exit;
+  b := PByte(@buf);
+
+{$IFDEF ANDROID}
+  cstr := GetMem(len + 1);
+  for slen := 0 to len - 1 do
+    cstr[slen] := Chr(b[slen]);
+  cstr[len] := #0;
+  SDL_Log(cstr, []);
+  Dispose(cstr);
+{$ENDIF}
+
+  while (len > 0) do
+  begin
+    if (len > 255) then slen := 255 else slen := Integer(len);
+    Move(b^, ss[1], slen);
+    ss[0] := AnsiChar(slen);
+    write(ss);
+    b += slen;
+    len -= slen;
+  end;
+end;
+
+
+var
+  xlogFile: TextFile;
+  xlogFileOpened: Boolean = false;
+  xlogPrefix: AnsiString;
+  xlogLastWasEOL: Boolean = false;
+  xlogWantSpace: Boolean = false;
+  xlogSlowAndSafe: Boolean = false;
+
+
+procedure e_SetSafeSlowLog (slowAndSafe: Boolean);
+begin
+  xlogSlowAndSafe := slowAndSafe;
+  if xlogSlowAndSafe and xlogFileOpened then
+  begin
+    CloseFile(xlogFile);
+    xlogFileOpened := false;
+  end;
+end;
+
+
+procedure logwriter (constref buf; len: SizeUInt);
+var
+  ss: ShortString;
+  slen: Integer;
+  b: PByte;
+begin
+  if (len < 1) then exit;
+  b := PByte(@buf);
+  if xlogLastWasEOL then
+  begin
+    write(xlogFile, xlogPrefix);
+    xlogLastWasEOL := false;
+    xlogWantSpace := true;
+  end;
+  while (len > 0) do
+  begin
+    slen := 0;
+    while (slen < len) and (b[slen] <> 13) and (b[slen] <> 10) do Inc(slen);
+    if (slen > 255) then slen := 255;
+    // print string
+    if (slen > 0) then
+    begin
+      if xlogWantSpace then begin write(xlogFile, ' '); xlogWantSpace := false; end;
+      Move(b^, ss[1], slen);
+      ss[0] := AnsiChar(slen);
+      write(xlogFile, ss);
+      b += slen;
+      len -= slen;
+      continue;
+    end;
+    // process newline
+    if (len > 0) and ((b[0] = 13) or (b[0] = 10)) then
+    begin
+      if (b[0] = 13) then begin len -= 1; b += 1; end;
+      if (len > 0) and (b[0] = 10) then begin len -= 1; b += 1; end;
+      xlogLastWasEOL := false;
+      writeln(xlogFile, '');
+      write(xlogFile, xlogPrefix);
+    end;
+  end;
+end;
+
+
+procedure e_LogWritefln (const fmt: AnsiString; args: array of const; category: TMsgType=TMsgType.Notify; writeTime: Boolean=true; writeConsole: Boolean=true);
+
+  procedure xwrite (const s: AnsiString);
+  begin
+    if (Length(s) = 0) then exit;
+    logwriter(PAnsiChar(s)^, Length(s));
+  end;
+
 begin
-  if driverInited and (length(TextLine) > 0) then
+  if driverInited and (length(fmt) > 0) and writeConsole then
   begin
-    case RecordCategory of
-      MSG_FATALERROR: write('FATAL: ');
-      MSG_WARNING: write('WARNINIG: ');
+    case category of
+      TMsgType.Fatal: write('FATAL: ');
+      TMsgType.Warning: write('WARNING: ');
     end;
-    writeln(TextLine);
+    formatstrf(fmt, args, conwriter);
+    writeln;
   end;
 
-  if FileName = '' then Exit;
+  if (FileName = '') then exit;
 
-  Assign(LogFile, FileName);
-  try
-    if FileExists(FileName) then
-      Append(LogFile)
-    else
-      Rewrite(LogFile);
+  if not xlogFileOpened then
+  begin
+    AssignFile(xlogFile, FileName);
     try
-      if FirstRecord then
-      begin
-        Writeln(LogFile, '--- Log started at '+TimeToStr(Time)+' ---');
-        FirstRecord := False;
-      end;
-      case RecordCategory of
-        MSG_FATALERROR: Prefix := '!!!';
-        MSG_WARNING:    Prefix := '!  ';
-        MSG_NOTIFY:     Prefix := '***';
-      end;
-      if WriteTime then
-        OutStr := '['+TimeToStr(Time)+'] '+Prefix+' '+TextLine
-      else
-        OutStr := Prefix+' '+TextLine;
-      Writeln(LogFile, OutStr);
-      if e_WriteToStdOut then
-        Writeln(OutStr);
-    finally
-      Close(LogFile);
+      if FileExists(FileName) then Append(xlogFile) else Rewrite(xlogFile);
+      xlogFileOpened := true;
+    except // sorry
+      exit;
     end;
-  except // sorry
   end;
+
+  if FirstRecord then
+  begin
+    writeln(xlogFile, '--- Log started at ', TimeToStr(Time), ' ---');
+    FirstRecord := false;
+  end;
+
+  xlogPrefix := '';
+  if writeTime then begin xlogPrefix += '['; xlogPrefix += TimeToStr(Time); xlogPrefix += '] '; end;
+  case category of
+    TMsgType.Fatal: xlogPrefix += '!!!';
+    TMsgType.Warning: xlogPrefix += '!  ';
+    TMsgType.Notify: xlogPrefix += '***';
+  end;
+  xlogLastWasEOL := true; // to output prefix
+  xlogWantSpace := true; // after prefix
+  formatstrf(fmt, args, logwriter);
+  if not xlogLastWasEOL then writeln(xlogFile, '') else writeln(xlogFile, xlogPrefix);
+
+  if xlogSlowAndSafe and xlogFileOpened then
+  begin
+    CloseFile(xlogFile);
+    xlogFileOpened := false;
+  end;
+
+  //if fopened then CloseFile(xlogFile);
 end;
 
 
 procedure e_InitLog (fFileName: String; fWriteMode: TWriteMode);
 begin
- FileName := fFileName;
- if fWriteMode = WM_NEWFILE then
- begin
-   try
-     if FileExists(FileName) then DeleteFile(FileName);
-   except // sorry
-   end;
- end;
- FirstRecord := True;
+  if xlogFileOpened then CloseFile(xlogFile);
+  xlogFileOpened := false;
+  FileName := fFileName;
+  if (fWriteMode = TWriteMode.WM_NEWFILE) then
+  begin
+    try
+      if FileExists(FileName) then DeleteFile(FileName);
+    except // sorry
+    end;
+  end;
+  FirstRecord := true;
+end;
+
+
+{$I-}
+procedure e_WriteStackTrace (const msg: AnsiString);
+var
+  tfo: TextFile;
+begin
+  e_LogWriteln(msg, TMsgType.Fatal);
+  if (Length(FileName) > 0) then
+  begin
+    if xlogFileOpened then CloseFile(xlogFile);
+    xlogFileOpened := false;
+    AssignFile(tfo, FileName);
+    Append(tfo);
+    if (IOResult <> 0) then Rewrite(tfo);
+    if (IOResult = 0) then begin writeln(tfo, '====================='); DumpExceptionBackTrace(tfo); CloseFile(tfo); end;
+  end;
+end;
+
+
+procedure e_DeinitLog ();
+begin
+  if xlogFileOpened then CloseFile(xlogFile);
+  xlogFileOpened := false;
 end;
 
 
@@ -136,11 +315,6 @@ begin
   wcr := (tf.userData[udWasCR] <> 0);
   while count > 0 do
   begin
-    if wcr then
-    begin
-      wcr := false;
-      if buf^ = #10 then continue;
-    end;
     // look for some special char
     ep := buf;
     f := 0;
@@ -156,6 +330,7 @@ begin
     end;
     if f > 0 then
     begin
+      wcr := false;
       cbufPutChars(buf, f);
       Inc(buf, f);
       Dec(count, f);
@@ -184,9 +359,12 @@ begin
 {$IFDEF CBLOG}
       writeln(stderr);
 {$ENDIF}
-      wcr := (ch = #13);
-      x := 0;
-      cbufPut(#10);
+      if not wcr or (ch <> #10) then
+      begin
+        wcr := (ch = #13);
+        x := 0;
+        cbufPut(#10);
+      end;
       continue;
     end;
   end;
@@ -253,5 +431,5 @@ end;
 
 
 begin
-  e_InitWritelnDriver();
+  //e_InitWritelnDriver();
 end.