DEADSOFTWARE

Make autoswitch server-side. Add option to skip empty weapons by travi$
[d2df-sdl.git] / src / engine / e_log.pas
1 (* Copyright (C) Doom 2D: Forever Developers
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, version 3 of the License ONLY.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 *)
15 {$INCLUDE ../shared/a_modes.inc}
16 {$R-}
17 { $DEFINE CBLOG}
18 unit e_log;
20 interface
22 uses
23 SysUtils;
25 type
26 TWriteMode = (WM_NEWFILE, WM_OLDFILE);
27 TMsgType = (Fatal, Warning, Notify);
30 procedure e_InitLog (fFileName: String; fWriteMode: TWriteMode);
31 procedure e_DeinitLog ();
33 procedure e_SetSafeSlowLog (slowAndSafe: Boolean);
35 procedure e_WriteLog (TextLine: String; RecordCategory: TMsgType; WriteTime: Boolean=True);
37 function DecodeIPV4 (ip: LongWord): string;
39 // start Write/WriteLn driver. it will write everything to cbuf.
40 procedure e_InitWritelnDriver ();
42 procedure e_LogWritefln (const fmt: AnsiString; args: array of const; category: TMsgType=TMsgType.Notify; writeTime: Boolean=true; writeConsole: Boolean=true);
43 procedure e_LogWriteln (const s: AnsiString; category: TMsgType=TMsgType.Notify; writeTime: Boolean=true);
45 procedure e_WriteStackTrace (const msg: AnsiString);
47 implementation
49 uses
50 {$IFDEF ANDROID}
51 SDL2,
52 {$ENDIF}
53 conbuf, utils;
55 var
56 FirstRecord: Boolean;
57 FileName: String;
58 driverInited: Boolean = false;
61 function DecodeIPV4 (ip: LongWord): string;
62 begin
63 {$IFDEF FPC_LITTLE_ENDIAN}
64 Result := Format('%d.%d.%d.%d', [ip and $FF, (ip shr 8) and $FF, (ip shr 16) and $FF, (ip shr 24)]);
65 {$ELSE}
66 Result := Format('%d.%d.%d.%d', [(ip shr 24), (ip shr 16) and $FF, (ip shr 8) and $FF, ip and $FF]);
67 {$ENDIF}
68 end;
71 function consoleAllow (const s: String): Boolean;
72 begin
73 Result := False;
74 if Pos('[Chat] ', s) = 1 then
75 Exit;
76 Result := True;
77 end;
80 procedure e_WriteLog (TextLine: String; RecordCategory: TMsgType; WriteTime: Boolean=True);
81 begin
82 e_LogWritefln('%s', [TextLine], RecordCategory, WriteTime, consoleAllow(TextLine));
83 end;
86 procedure e_LogWriteln (const s: AnsiString; category: TMsgType=TMsgType.Notify; writeTime: Boolean=true);
87 begin
88 e_LogWritefln('%s', [s], category, writeTime, consoleAllow(s));
89 end;
92 // returns formatted string if `writerCB` is `nil`, empty string otherwise
93 //function formatstrf (const fmt: AnsiString; args: array of const; writerCB: TFormatStrFCallback=nil): AnsiString;
94 //TFormatStrFCallback = procedure (constref buf; len: SizeUInt);
96 procedure conwriter (constref buf; len: SizeUInt);
97 var
98 ss: ShortString;
99 slen: Integer;
100 b: PByte;
101 {$IFDEF ANDROID}
102 cstr: PChar;
103 {$ENDIF}
104 begin
105 if (len < 1) then exit;
106 b := PByte(@buf);
108 {$IFDEF ANDROID}
109 cstr := GetMem(len + 1);
110 for slen := 0 to len - 1 do
111 cstr[slen] := Chr(b[slen]);
112 cstr[len] := #0;
113 SDL_Log(cstr, []);
114 Dispose(cstr);
115 {$ENDIF}
117 while (len > 0) do
118 begin
119 if (len > 255) then slen := 255 else slen := Integer(len);
120 Move(b^, ss[1], slen);
121 ss[0] := AnsiChar(slen);
122 write(ss);
123 b += slen;
124 len -= slen;
125 end;
126 end;
129 var
130 xlogFile: TextFile;
131 xlogFileOpened: Boolean = false;
132 xlogPrefix: AnsiString;
133 xlogLastWasEOL: Boolean = false;
134 xlogWantSpace: Boolean = false;
135 xlogSlowAndSafe: Boolean = false;
138 procedure e_SetSafeSlowLog (slowAndSafe: Boolean);
139 begin
140 xlogSlowAndSafe := slowAndSafe;
141 if xlogSlowAndSafe and xlogFileOpened then
142 begin
143 CloseFile(xlogFile);
144 xlogFileOpened := false;
145 end;
146 end;
149 procedure logwriter (constref buf; len: SizeUInt);
150 var
151 ss: ShortString;
152 slen: Integer;
153 b: PByte;
154 begin
155 if (len < 1) then exit;
156 b := PByte(@buf);
157 if xlogLastWasEOL then
158 begin
159 write(xlogFile, xlogPrefix);
160 xlogLastWasEOL := false;
161 xlogWantSpace := true;
162 end;
163 while (len > 0) do
164 begin
165 slen := 0;
166 while (slen < len) and (b[slen] <> 13) and (b[slen] <> 10) do Inc(slen);
167 if (slen > 255) then slen := 255;
168 // print string
169 if (slen > 0) then
170 begin
171 if xlogWantSpace then begin write(xlogFile, ' '); xlogWantSpace := false; end;
172 Move(b^, ss[1], slen);
173 ss[0] := AnsiChar(slen);
174 write(xlogFile, ss);
175 b += slen;
176 len -= slen;
177 continue;
178 end;
179 // process newline
180 if (len > 0) and ((b[0] = 13) or (b[0] = 10)) then
181 begin
182 if (b[0] = 13) then begin len -= 1; b += 1; end;
183 if (len > 0) and (b[0] = 10) then begin len -= 1; b += 1; end;
184 xlogLastWasEOL := false;
185 writeln(xlogFile, '');
186 write(xlogFile, xlogPrefix);
187 end;
188 end;
189 end;
192 procedure e_LogWritefln (const fmt: AnsiString; args: array of const; category: TMsgType=TMsgType.Notify; writeTime: Boolean=true; writeConsole: Boolean=true);
194 procedure xwrite (const s: AnsiString);
195 begin
196 if (Length(s) = 0) then exit;
197 logwriter(PAnsiChar(s)^, Length(s));
198 end;
200 begin
201 if driverInited and (length(fmt) > 0) and writeConsole then
202 begin
203 case category of
204 TMsgType.Fatal: write('FATAL: ');
205 TMsgType.Warning: write('WARNING: ');
206 end;
207 formatstrf(fmt, args, conwriter);
208 writeln;
209 end;
211 if (FileName = '') then exit;
213 if not xlogFileOpened then
214 begin
215 AssignFile(xlogFile, FileName);
216 try
217 if FileExists(FileName) then Append(xlogFile) else Rewrite(xlogFile);
218 xlogFileOpened := true;
219 except // sorry
220 exit;
221 end;
222 end;
224 if FirstRecord then
225 begin
226 writeln(xlogFile, '--- Log started at ', TimeToStr(Time), ' ---');
227 FirstRecord := false;
228 end;
230 xlogPrefix := '';
231 if writeTime then begin xlogPrefix += '['; xlogPrefix += TimeToStr(Time); xlogPrefix += '] '; end;
232 case category of
233 TMsgType.Fatal: xlogPrefix += '!!!';
234 TMsgType.Warning: xlogPrefix += '! ';
235 TMsgType.Notify: xlogPrefix += '***';
236 end;
237 xlogLastWasEOL := true; // to output prefix
238 xlogWantSpace := true; // after prefix
239 formatstrf(fmt, args, logwriter);
240 if not xlogLastWasEOL then writeln(xlogFile, '') else writeln(xlogFile, xlogPrefix);
242 if xlogSlowAndSafe and xlogFileOpened then
243 begin
244 CloseFile(xlogFile);
245 xlogFileOpened := false;
246 end;
248 //if fopened then CloseFile(xlogFile);
249 end;
252 procedure e_InitLog (fFileName: String; fWriteMode: TWriteMode);
253 begin
254 if xlogFileOpened then CloseFile(xlogFile);
255 xlogFileOpened := false;
256 FileName := fFileName;
257 if (fWriteMode = TWriteMode.WM_NEWFILE) then
258 begin
259 try
260 if FileExists(FileName) then DeleteFile(FileName);
261 except // sorry
262 end;
263 end;
264 FirstRecord := true;
265 end;
268 {$I-}
269 procedure e_WriteStackTrace (const msg: AnsiString);
270 var
271 tfo: TextFile;
272 begin
273 e_LogWriteln(msg, TMsgType.Fatal);
274 if (Length(FileName) > 0) then
275 begin
276 if xlogFileOpened then CloseFile(xlogFile);
277 xlogFileOpened := false;
278 AssignFile(tfo, FileName);
279 Append(tfo);
280 if (IOResult <> 0) then Rewrite(tfo);
281 if (IOResult = 0) then begin writeln(tfo, '====================='); DumpExceptionBackTrace(tfo); CloseFile(tfo); end;
282 end;
283 end;
286 procedure e_DeinitLog ();
287 begin
288 if xlogFileOpened then CloseFile(xlogFile);
289 xlogFileOpened := false;
290 end;
293 // ////////////////////////////////////////////////////////////////////////// //
294 (* Write/WriteLn driver *)
295 //
296 // control codes:
297 // CR, LF, BS
298 // TAB: tab space = 4
299 //
300 // userData[1]: current x (for tabs)
301 // userData[2]: #13 was eaten, we should skip next #10
302 //
303 type
304 TDevFunc = function (var f: TTextRec): Integer;
306 const
307 udX = 1;
308 udWasCR = 2;
311 procedure ProcessOutput (var tf: TTextRec; buf: PChar; count: Integer);
312 var
313 wcr: Boolean;
314 ep: PChar;
315 f, x: Integer;
316 ch: Char;
317 begin
318 x := tf.userData[udX];
319 wcr := (tf.userData[udWasCR] <> 0);
320 while count > 0 do
321 begin
322 // look for some special char
323 ep := buf;
324 f := 0;
325 while f < count do
326 begin
327 ch := ep^;
328 if (ch = #13) or (ch = #10) or (ch = #9) or (ch = #8) then break;
329 Inc(ep);
330 Inc(f);
331 {$IFDEF CBLOG}
332 write(stderr, ch);
333 {$ENDIF}
334 end;
335 if f > 0 then
336 begin
337 wcr := false;
338 cbufPutChars(buf, f);
339 Inc(buf, f);
340 Dec(count, f);
341 Inc(x, f);
342 continue;
343 end;
344 // process special chars
345 ch := buf^;
346 Inc(buf);
347 Dec(count);
348 // tab
349 if ch = #9 then
350 begin
351 {$IFDEF CBLOG}
352 write(stderr, ch);
353 {$ENDIF}
354 repeat
355 cbufPut(' ');
356 Inc(x);
357 until (x mod 4) = 0;
358 continue;
359 end;
360 // cr, lf
361 if (ch = #13) or (ch = #10) then
362 begin
363 {$IFDEF CBLOG}
364 writeln(stderr);
365 {$ENDIF}
366 if not wcr or (ch <> #10) then
367 begin
368 wcr := (ch = #13);
369 x := 0;
370 cbufPut(#10);
371 end;
372 continue;
373 end;
374 end;
375 tf.userData[udX] := x;
376 tf.userData[udWasCR] := ord(wcr);
377 end;
380 function DevOpen (var f: TTextRec): Integer;
381 begin
382 f.userData[udX] := 0;
383 f.userData[udWasCR] := 0;
384 f.bufPos := 0;
385 f.bufEnd := 0;
386 result := 0;
387 end;
389 function DevInOut (var f: TTextRec): Integer;
390 var
391 buf: PChar;
392 sz: Integer;
393 begin
394 result := 0;
395 buf := Pointer(f.BufPtr);
396 sz := f.BufPos;
397 if sz > 0 then ProcessOutput(f, buf, sz);
398 f.bufPos := 0;
399 f.bufEnd := 0;
400 end;
402 function DevFlush (var f: TTextRec): Integer;
403 begin
404 result := DevInOut(f);
405 end;
407 function DevClose (var f: TTextRec): Integer;
408 begin
409 result := 0;
410 end;
413 procedure e_InitWritelnDriver ();
414 begin
415 if not driverInited then
416 begin
417 driverInited := true;
418 with TTextRec(output) do
419 begin
420 Mode := fmClosed;
421 if BufPtr = nil then
422 begin
423 BufSize := SizeOf(Buffer);
424 BufPtr := @Buffer;
425 end;
426 OpenFunc := @DevOpen;
427 InOutFunc := @DevInOut;
428 FlushFunc := @DevFlush;
429 CloseFunc := @DevClose;
430 Name[0] := #0;
431 end;
432 Rewrite(output);
433 end;
434 end;
437 begin
438 //e_InitWritelnDriver();
439 end.