X-Git-Url: https://deadsoftware.ru/gitweb?a=blobdiff_plain;f=src%2Fengine%2Fe_log.pas;h=7159c18445614beaff3ba4fc3f534df33d2cf474;hb=96433b6928f0d3de3a7bb69b97008b17bff22b58;hp=d45f86efd10cb1d10e453a7d754ecb9d7a4f8e49;hpb=5d49aa6a925e49d43993305b3e4071fd70286a90;p=d2df-sdl.git diff --git a/src/engine/e_log.pas b/src/engine/e_log.pas index d45f86e..7159c18 100644 --- a/src/engine/e_log.pas +++ b/src/engine/e_log.pas @@ -1,4 +1,20 @@ +(* 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 . + *) +{$INCLUDE ../shared/a_modes.inc} {$R-} +{ $DEFINE CBLOG} unit e_log; interface @@ -7,70 +23,413 @@ uses SysUtils; type - TWriteMode=(WM_NEWFILE, WM_OLDFILE); - TRecordCategory=(MSG_FATALERROR, MSG_WARNING, MSG_NOTIFY); + TWriteMode = (WM_NEWFILE, WM_OLDFILE); + 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_InitLog (fFileName: String; fWriteMode: TWriteMode); +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); + +procedure e_WriteStackTrace (const msg: AnsiString); implementation +uses + {$IFDEF ANDROID} + SDL2, + {$ENDIF} + conbuf, utils; + var FirstRecord: Boolean; FileName: String; + driverInited: Boolean = false; -{ TLog } -function DecodeIPV4(ip: LongWord): string; +function DecodeIPV4 (ip: LongWord): string; begin Result := Format('%d.%d.%d.%d', [ip and $FF, (ip shr 8) and $FF, (ip shr 16) and $FF, (ip shr 24)]); 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 = ''; + ss: ShortString; + slen: Integer; + b: PByte; +{$IFDEF ANDROID} + cstr: PChar; +{$ENDIF} begin - if FileName = '' then Exit; + 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} - Assign(LogFile, FileName); - try - if FileExists(FileName) then - Append(LogFile) - else - Rewrite(LogFile); + 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(fmt) > 0) and writeConsole then + begin + case category of + TMsgType.Fatal: write('FATAL: '); + TMsgType.Warning: write('WARNING: '); + end; + formatstrf(fmt, args, conwriter); + writeln; + end; + + if (FileName = '') then exit; + + if not xlogFileOpened then + begin + AssignFile(xlogFile, FileName); try - if FirstRecord then + if FileExists(FileName) then Append(xlogFile) else Rewrite(xlogFile); + xlogFileOpened := true; + except // sorry + exit; + end; + 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 + 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; + + +// ////////////////////////////////////////////////////////////////////////// // +(* Write/WriteLn driver *) +// +// control codes: +// CR, LF, BS +// TAB: tab space = 4 +// +// userData[1]: current x (for tabs) +// userData[2]: #13 was eaten, we should skip next #10 +// +type + TDevFunc = function (var f: TTextRec): Integer; + +const + udX = 1; + udWasCR = 2; + + +procedure ProcessOutput (var tf: TTextRec; buf: PChar; count: Integer); +var + wcr: Boolean; + ep: PChar; + f, x: Integer; + ch: Char; +begin + x := tf.userData[udX]; + wcr := (tf.userData[udWasCR] <> 0); + while count > 0 do + begin + // look for some special char + ep := buf; + f := 0; + while f < count do + begin + ch := ep^; + if (ch = #13) or (ch = #10) or (ch = #9) or (ch = #8) then break; + Inc(ep); + Inc(f); +{$IFDEF CBLOG} + write(stderr, ch); +{$ENDIF} + end; + if f > 0 then + begin + wcr := false; + cbufPutChars(buf, f); + Inc(buf, f); + Dec(count, f); + Inc(x, f); + continue; + end; + // process special chars + ch := buf^; + Inc(buf); + Dec(count); + // tab + if ch = #9 then + begin +{$IFDEF CBLOG} + write(stderr, ch); +{$ENDIF} + repeat + cbufPut(' '); + Inc(x); + until (x mod 4) = 0; + continue; + end; + // cr, lf + if (ch = #13) or (ch = #10) then + begin +{$IFDEF CBLOG} + writeln(stderr); +{$ENDIF} + if not wcr or (ch <> #10) then begin - Writeln(LogFile, '--- Log started at '+TimeToStr(Time)+' ---'); - FirstRecord := False; - end; - case RecordCategory of - MSG_FATALERROR: Prefix := '!!!'; - MSG_WARNING: Prefix := '! '; - MSG_NOTIFY: Prefix := '***'; + wcr := (ch = #13); + x := 0; + cbufPut(#10); end; - if WriteTime then - Writeln(LogFile, '['+TimeToStr(Time)+'] '+Prefix+' '+TextLine) - else - Writeln(LogFile, Prefix+' '+TextLine); - finally - Close(LogFile); + continue; end; - except // sorry end; + tf.userData[udX] := x; + tf.userData[udWasCR] := ord(wcr); +end; + + +function DevOpen (var f: TTextRec): Integer; +begin + f.userData[udX] := 0; + f.userData[udWasCR] := 0; + f.bufPos := 0; + f.bufEnd := 0; + result := 0; +end; + +function DevInOut (var f: TTextRec): Integer; +var + buf: PChar; + sz: Integer; +begin + result := 0; + buf := Pointer(f.BufPtr); + sz := f.BufPos; + if sz > 0 then ProcessOutput(f, buf, sz); + f.bufPos := 0; + f.bufEnd := 0; +end; + +function DevFlush (var f: TTextRec): Integer; +begin + result := DevInOut(f); +end; + +function DevClose (var f: TTextRec): Integer; +begin + result := 0; end; -procedure e_InitLog(fFileName: String; fWriteMode: TWriteMode); + +procedure e_InitWritelnDriver (); begin - FileName := fFileName; - if fWriteMode = WM_NEWFILE then - if FileExists(FileName) then - DeleteFile(FileName); - FirstRecord := True; + if not driverInited then + begin + driverInited := true; + with TTextRec(output) do + begin + Mode := fmClosed; + if BufPtr = nil then + begin + BufSize := SizeOf(Buffer); + BufPtr := @Buffer; + end; + OpenFunc := @DevOpen; + InOutFunc := @DevInOut; + FlushFunc := @DevFlush; + CloseFunc := @DevClose; + Name[0] := #0; + end; + Rewrite(output); + end; end; + +begin + //e_InitWritelnDriver(); end.