DEADSOFTWARE

the game is able to read text maps now (WARNING! the feature is still experimental!)
[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, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *)
16 {$INCLUDE ../shared/a_modes.inc}
17 {$R-}
18 { $DEFINE CBLOG}
19 unit e_log;
21 interface
23 uses
24 SysUtils;
26 type
27 TWriteMode = (WM_NEWFILE, WM_OLDFILE);
28 TRecordCategory = (MSG_FATALERROR, MSG_WARNING, MSG_NOTIFY);
31 procedure e_InitLog (fFileName: String; fWriteMode: TWriteMode);
32 procedure e_DeinitLog ();
34 procedure e_SetSafeSlowLog (slowAndSafe: Boolean);
36 procedure e_WriteLog (TextLine: String; RecordCategory: TRecordCategory; WriteTime: Boolean=True);
38 function DecodeIPV4 (ip: LongWord): string;
40 // start Write/WriteLn driver. it will write everything to cbuf.
41 procedure e_InitWritelnDriver ();
43 procedure e_LogWritefln (const fmt: AnsiString; args: array of const; category: TRecordCategory=MSG_NOTIFY; writeTime: Boolean=true);
46 var
47 e_WriteToStdOut: Boolean = False;
50 implementation
52 uses
53 conbuf, utils;
55 var
56 FirstRecord: Boolean;
57 FileName: String;
58 driverInited: Boolean = false;
61 function DecodeIPV4 (ip: LongWord): string;
62 begin
63 Result := Format('%d.%d.%d.%d', [ip and $FF, (ip shr 8) and $FF, (ip shr 16) and $FF, (ip shr 24)]);
64 end;
67 procedure e_WriteLog (TextLine: String; RecordCategory: TRecordCategory; WriteTime: Boolean=True);
68 begin
69 e_LogWritefln('%s', [TextLine], RecordCategory, WriteTime);
70 end;
71 (*
72 var
73 LogFile: TextFile;
74 Prefix: ShortString = '';
75 OutStr: String;
76 begin
77 if driverInited and (length(TextLine) > 0) then
78 begin
79 case RecordCategory of
80 MSG_FATALERROR: write('FATAL: ');
81 MSG_WARNING: write('WARNING: ');
82 end;
83 writeln(TextLine);
84 end;
86 if FileName = '' then Exit;
88 Assign(LogFile, FileName);
89 try
90 if FileExists(FileName) then
91 Append(LogFile)
92 else
93 Rewrite(LogFile);
94 try
95 if FirstRecord then
96 begin
97 Writeln(LogFile, '--- Log started at '+TimeToStr(Time)+' ---');
98 FirstRecord := False;
99 end;
100 case RecordCategory of
101 MSG_FATALERROR: Prefix := '!!!';
102 MSG_WARNING: Prefix := '! ';
103 MSG_NOTIFY: Prefix := '***';
104 end;
105 if WriteTime then
106 OutStr := '['+TimeToStr(Time)+'] '+Prefix+' '+TextLine
107 else
108 OutStr := Prefix+' '+TextLine;
109 Writeln(LogFile, OutStr);
110 if e_WriteToStdOut then
111 Writeln(OutStr);
112 finally
113 Close(LogFile);
114 end;
115 except // sorry
116 end;
117 end;
118 *)
121 // returns formatted string if `writerCB` is `nil`, empty string otherwise
122 //function formatstrf (const fmt: AnsiString; args: array of const; writerCB: TFormatStrFCallback=nil): AnsiString;
123 //TFormatStrFCallback = procedure (constref buf; len: SizeUInt);
125 procedure conwriter (constref buf; len: SizeUInt);
126 var
127 ss: ShortString;
128 slen: Integer;
129 b: PByte;
130 begin
131 if (len < 1) then exit;
132 b := PByte(@buf);
133 while (len > 0) do
134 begin
135 if (len > 255) then slen := 255 else slen := Integer(len);
136 Move(b^, ss[1], slen);
137 ss[0] := AnsiChar(slen);
138 write(ss);
139 b += slen;
140 len -= slen;
141 end;
142 end;
145 var
146 xlogFile: TextFile;
147 xlogFileOpened: Boolean = false;
148 xlogPrefix: AnsiString;
149 xlogLastWasEOL: Boolean = false;
150 xlogWantSpace: Boolean = false;
151 xlogSlowAndSafe: Boolean = false;
154 procedure e_SetSafeSlowLog (slowAndSafe: Boolean);
155 begin
156 xlogSlowAndSafe := slowAndSafe;
157 if xlogSlowAndSafe and xlogFileOpened then
158 begin
159 CloseFile(xlogFile);
160 xlogFileOpened := false;
161 end;
162 end;
165 procedure logwriter (constref buf; len: SizeUInt);
166 var
167 ss: ShortString;
168 slen: Integer;
169 b: PByte;
170 begin
171 if (len < 1) then exit;
172 b := PByte(@buf);
173 if xlogLastWasEOL then
174 begin
175 write(xlogFile, xlogPrefix);
176 xlogLastWasEOL := false;
177 xlogWantSpace := true;
178 end;
179 while (len > 0) do
180 begin
181 slen := 0;
182 while (slen < len) and (b[slen] <> 13) and (b[slen] <> 10) do Inc(slen);
183 if (slen > 255) then slen := 255;
184 // print string
185 if (slen > 0) then
186 begin
187 if xlogWantSpace then begin write(xlogFile, ' '); xlogWantSpace := false; end;
188 Move(b^, ss[1], slen);
189 ss[0] := AnsiChar(slen);
190 write(xlogFile, ss);
191 b += slen;
192 len -= slen;
193 continue;
194 end;
195 // process newline
196 if (len > 0) and ((b[0] = 13) or (b[0] = 10)) then
197 begin
198 if (b[0] = 13) then begin len -= 1; b += 1; end;
199 if (len > 0) and (b[0] = 10) then begin len -= 1; b += 1; end;
200 xlogLastWasEOL := false;
201 writeln(xlogFile, '');
202 write(xlogFile, xlogPrefix);
203 end;
204 end;
205 end;
208 procedure e_LogWritefln (const fmt: AnsiString; args: array of const; category: TRecordCategory=MSG_NOTIFY; writeTime: Boolean=true);
210 procedure xwrite (const s: AnsiString);
211 begin
212 if (Length(s) = 0) then exit;
213 logwriter(PAnsiChar(s)^, Length(s));
214 end;
216 begin
217 if driverInited and (length(fmt) > 0) then
218 begin
219 case category of
220 MSG_FATALERROR: write('FATAL: ');
221 MSG_WARNING: write('WARNING: ');
222 end;
223 formatstrf(fmt, args, conwriter);
224 writeln;
225 end;
227 if (FileName = '') then exit;
229 if not xlogFileOpened then
230 begin
231 AssignFile(xlogFile, FileName);
232 try
233 if FileExists(FileName) then Append(xlogFile) else Rewrite(xlogFile);
234 xlogFileOpened := true;
235 except // sorry
236 exit;
237 end;
238 end;
240 if FirstRecord then
241 begin
242 writeln(xlogFile, '--- Log started at ', TimeToStr(Time), ' ---');
243 FirstRecord := false;
244 end;
246 xlogPrefix := '';
247 if writeTime then begin xlogPrefix += '['; xlogPrefix += TimeToStr(Time); xlogPrefix += '] '; end;
248 case category of
249 MSG_FATALERROR: xlogPrefix += '!!!';
250 MSG_WARNING: xlogPrefix += '! ';
251 MSG_NOTIFY: xlogPrefix += '***';
252 end;
253 xlogLastWasEOL := true; // to output prefix
254 xlogWantSpace := true; // after prefix
255 formatstrf(fmt, args, logwriter);
256 if not xlogLastWasEOL then writeln(xlogFile, '') else writeln(xlogFile, xlogPrefix);
258 if xlogSlowAndSafe and xlogFileOpened then
259 begin
260 CloseFile(xlogFile);
261 xlogFileOpened := false;
262 end;
264 //if fopened then CloseFile(xlogFile);
265 end;
268 procedure e_InitLog (fFileName: String; fWriteMode: TWriteMode);
269 begin
270 if xlogFileOpened then CloseFile(xlogFile);
271 xlogFileOpened := false;
272 FileName := fFileName;
273 if (fWriteMode = WM_NEWFILE) then
274 begin
275 try
276 if FileExists(FileName) then DeleteFile(FileName);
277 except // sorry
278 end;
279 end;
280 FirstRecord := true;
281 end;
284 procedure e_DeinitLog ();
285 begin
286 if xlogFileOpened then CloseFile(xlogFile);
287 xlogFileOpened := false;
288 end;
291 // ////////////////////////////////////////////////////////////////////////// //
292 (* Write/WriteLn driver *)
293 //
294 // control codes:
295 // CR, LF, BS
296 // TAB: tab space = 4
297 //
298 // userData[1]: current x (for tabs)
299 // userData[2]: #13 was eaten, we should skip next #10
300 //
301 type
302 TDevFunc = function (var f: TTextRec): Integer;
304 const
305 udX = 1;
306 udWasCR = 2;
309 procedure ProcessOutput (var tf: TTextRec; buf: PChar; count: Integer);
310 var
311 wcr: Boolean;
312 ep: PChar;
313 f, x: Integer;
314 ch: Char;
315 begin
316 x := tf.userData[udX];
317 wcr := (tf.userData[udWasCR] <> 0);
318 while count > 0 do
319 begin
320 // look for some special char
321 ep := buf;
322 f := 0;
323 while f < count do
324 begin
325 ch := ep^;
326 if (ch = #13) or (ch = #10) or (ch = #9) or (ch = #8) then break;
327 Inc(ep);
328 Inc(f);
329 {$IFDEF CBLOG}
330 write(stderr, ch);
331 {$ENDIF}
332 end;
333 if f > 0 then
334 begin
335 wcr := false;
336 cbufPutChars(buf, f);
337 Inc(buf, f);
338 Dec(count, f);
339 Inc(x, f);
340 continue;
341 end;
342 // process special chars
343 ch := buf^;
344 Inc(buf);
345 Dec(count);
346 // tab
347 if ch = #9 then
348 begin
349 {$IFDEF CBLOG}
350 write(stderr, ch);
351 {$ENDIF}
352 repeat
353 cbufPut(' ');
354 Inc(x);
355 until (x mod 4) = 0;
356 continue;
357 end;
358 // cr, lf
359 if (ch = #13) or (ch = #10) then
360 begin
361 {$IFDEF CBLOG}
362 writeln(stderr);
363 {$ENDIF}
364 if not wcr or (ch <> #10) then
365 begin
366 wcr := (ch = #13);
367 x := 0;
368 cbufPut(#10);
369 end;
370 continue;
371 end;
372 end;
373 tf.userData[udX] := x;
374 tf.userData[udWasCR] := ord(wcr);
375 end;
378 function DevOpen (var f: TTextRec): Integer;
379 begin
380 f.userData[udX] := 0;
381 f.userData[udWasCR] := 0;
382 f.bufPos := 0;
383 f.bufEnd := 0;
384 result := 0;
385 end;
387 function DevInOut (var f: TTextRec): Integer;
388 var
389 buf: PChar;
390 sz: Integer;
391 begin
392 result := 0;
393 buf := Pointer(f.BufPtr);
394 sz := f.BufPos;
395 if sz > 0 then ProcessOutput(f, buf, sz);
396 f.bufPos := 0;
397 f.bufEnd := 0;
398 end;
400 function DevFlush (var f: TTextRec): Integer;
401 begin
402 result := DevInOut(f);
403 end;
405 function DevClose (var f: TTextRec): Integer;
406 begin
407 result := 0;
408 end;
411 procedure e_InitWritelnDriver ();
412 begin
413 if not driverInited then
414 begin
415 driverInited := true;
416 with TTextRec(output) do
417 begin
418 Mode := fmClosed;
419 if BufPtr = nil then
420 begin
421 BufSize := SizeOf(Buffer);
422 BufPtr := @Buffer;
423 end;
424 OpenFunc := @DevOpen;
425 InOutFunc := @DevInOut;
426 FlushFunc := @DevFlush;
427 CloseFunc := @DevClose;
428 Name[0] := #0;
429 end;
430 Rewrite(output);
431 end;
432 end;
435 begin
436 //e_InitWritelnDriver();
437 end.