DEADSOFTWARE

relaxed map reader, so "bloodlust", for example, is working now
[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;
73 // returns formatted string if `writerCB` is `nil`, empty string otherwise
74 //function formatstrf (const fmt: AnsiString; args: array of const; writerCB: TFormatStrFCallback=nil): AnsiString;
75 //TFormatStrFCallback = procedure (constref buf; len: SizeUInt);
77 procedure conwriter (constref buf; len: SizeUInt);
78 var
79 ss: ShortString;
80 slen: Integer;
81 b: PByte;
82 begin
83 if (len < 1) then exit;
84 b := PByte(@buf);
85 while (len > 0) do
86 begin
87 if (len > 255) then slen := 255 else slen := Integer(len);
88 Move(b^, ss[1], slen);
89 ss[0] := AnsiChar(slen);
90 write(ss);
91 b += slen;
92 len -= slen;
93 end;
94 end;
97 var
98 xlogFile: TextFile;
99 xlogFileOpened: Boolean = false;
100 xlogPrefix: AnsiString;
101 xlogLastWasEOL: Boolean = false;
102 xlogWantSpace: Boolean = false;
103 xlogSlowAndSafe: Boolean = false;
106 procedure e_SetSafeSlowLog (slowAndSafe: Boolean);
107 begin
108 xlogSlowAndSafe := slowAndSafe;
109 if xlogSlowAndSafe and xlogFileOpened then
110 begin
111 CloseFile(xlogFile);
112 xlogFileOpened := false;
113 end;
114 end;
117 procedure logwriter (constref buf; len: SizeUInt);
118 var
119 ss: ShortString;
120 slen: Integer;
121 b: PByte;
122 begin
123 if (len < 1) then exit;
124 b := PByte(@buf);
125 if xlogLastWasEOL then
126 begin
127 write(xlogFile, xlogPrefix);
128 xlogLastWasEOL := false;
129 xlogWantSpace := true;
130 end;
131 while (len > 0) do
132 begin
133 slen := 0;
134 while (slen < len) and (b[slen] <> 13) and (b[slen] <> 10) do Inc(slen);
135 if (slen > 255) then slen := 255;
136 // print string
137 if (slen > 0) then
138 begin
139 if xlogWantSpace then begin write(xlogFile, ' '); xlogWantSpace := false; end;
140 Move(b^, ss[1], slen);
141 ss[0] := AnsiChar(slen);
142 write(xlogFile, ss);
143 b += slen;
144 len -= slen;
145 continue;
146 end;
147 // process newline
148 if (len > 0) and ((b[0] = 13) or (b[0] = 10)) then
149 begin
150 if (b[0] = 13) then begin len -= 1; b += 1; end;
151 if (len > 0) and (b[0] = 10) then begin len -= 1; b += 1; end;
152 xlogLastWasEOL := false;
153 writeln(xlogFile, '');
154 write(xlogFile, xlogPrefix);
155 end;
156 end;
157 end;
160 procedure e_LogWritefln (const fmt: AnsiString; args: array of const; category: TRecordCategory=MSG_NOTIFY; writeTime: Boolean=true);
162 procedure xwrite (const s: AnsiString);
163 begin
164 if (Length(s) = 0) then exit;
165 logwriter(PAnsiChar(s)^, Length(s));
166 end;
168 begin
169 if driverInited and (length(fmt) > 0) then
170 begin
171 case category of
172 MSG_FATALERROR: write('FATAL: ');
173 MSG_WARNING: write('WARNING: ');
174 end;
175 formatstrf(fmt, args, conwriter);
176 writeln;
177 end;
179 if (FileName = '') then exit;
181 if not xlogFileOpened then
182 begin
183 AssignFile(xlogFile, FileName);
184 try
185 if FileExists(FileName) then Append(xlogFile) else Rewrite(xlogFile);
186 xlogFileOpened := true;
187 except // sorry
188 exit;
189 end;
190 end;
192 if FirstRecord then
193 begin
194 writeln(xlogFile, '--- Log started at ', TimeToStr(Time), ' ---');
195 FirstRecord := false;
196 end;
198 xlogPrefix := '';
199 if writeTime then begin xlogPrefix += '['; xlogPrefix += TimeToStr(Time); xlogPrefix += '] '; end;
200 case category of
201 MSG_FATALERROR: xlogPrefix += '!!!';
202 MSG_WARNING: xlogPrefix += '! ';
203 MSG_NOTIFY: xlogPrefix += '***';
204 end;
205 xlogLastWasEOL := true; // to output prefix
206 xlogWantSpace := true; // after prefix
207 formatstrf(fmt, args, logwriter);
208 if not xlogLastWasEOL then writeln(xlogFile, '') else writeln(xlogFile, xlogPrefix);
210 if xlogSlowAndSafe and xlogFileOpened then
211 begin
212 CloseFile(xlogFile);
213 xlogFileOpened := false;
214 end;
216 //if fopened then CloseFile(xlogFile);
217 end;
220 procedure e_InitLog (fFileName: String; fWriteMode: TWriteMode);
221 begin
222 if xlogFileOpened then CloseFile(xlogFile);
223 xlogFileOpened := false;
224 FileName := fFileName;
225 if (fWriteMode = WM_NEWFILE) then
226 begin
227 try
228 if FileExists(FileName) then DeleteFile(FileName);
229 except // sorry
230 end;
231 end;
232 FirstRecord := true;
233 end;
236 procedure e_DeinitLog ();
237 begin
238 if xlogFileOpened then CloseFile(xlogFile);
239 xlogFileOpened := false;
240 end;
243 // ////////////////////////////////////////////////////////////////////////// //
244 (* Write/WriteLn driver *)
245 //
246 // control codes:
247 // CR, LF, BS
248 // TAB: tab space = 4
249 //
250 // userData[1]: current x (for tabs)
251 // userData[2]: #13 was eaten, we should skip next #10
252 //
253 type
254 TDevFunc = function (var f: TTextRec): Integer;
256 const
257 udX = 1;
258 udWasCR = 2;
261 procedure ProcessOutput (var tf: TTextRec; buf: PChar; count: Integer);
262 var
263 wcr: Boolean;
264 ep: PChar;
265 f, x: Integer;
266 ch: Char;
267 begin
268 x := tf.userData[udX];
269 wcr := (tf.userData[udWasCR] <> 0);
270 while count > 0 do
271 begin
272 // look for some special char
273 ep := buf;
274 f := 0;
275 while f < count do
276 begin
277 ch := ep^;
278 if (ch = #13) or (ch = #10) or (ch = #9) or (ch = #8) then break;
279 Inc(ep);
280 Inc(f);
281 {$IFDEF CBLOG}
282 write(stderr, ch);
283 {$ENDIF}
284 end;
285 if f > 0 then
286 begin
287 wcr := false;
288 cbufPutChars(buf, f);
289 Inc(buf, f);
290 Dec(count, f);
291 Inc(x, f);
292 continue;
293 end;
294 // process special chars
295 ch := buf^;
296 Inc(buf);
297 Dec(count);
298 // tab
299 if ch = #9 then
300 begin
301 {$IFDEF CBLOG}
302 write(stderr, ch);
303 {$ENDIF}
304 repeat
305 cbufPut(' ');
306 Inc(x);
307 until (x mod 4) = 0;
308 continue;
309 end;
310 // cr, lf
311 if (ch = #13) or (ch = #10) then
312 begin
313 {$IFDEF CBLOG}
314 writeln(stderr);
315 {$ENDIF}
316 if not wcr or (ch <> #10) then
317 begin
318 wcr := (ch = #13);
319 x := 0;
320 cbufPut(#10);
321 end;
322 continue;
323 end;
324 end;
325 tf.userData[udX] := x;
326 tf.userData[udWasCR] := ord(wcr);
327 end;
330 function DevOpen (var f: TTextRec): Integer;
331 begin
332 f.userData[udX] := 0;
333 f.userData[udWasCR] := 0;
334 f.bufPos := 0;
335 f.bufEnd := 0;
336 result := 0;
337 end;
339 function DevInOut (var f: TTextRec): Integer;
340 var
341 buf: PChar;
342 sz: Integer;
343 begin
344 result := 0;
345 buf := Pointer(f.BufPtr);
346 sz := f.BufPos;
347 if sz > 0 then ProcessOutput(f, buf, sz);
348 f.bufPos := 0;
349 f.bufEnd := 0;
350 end;
352 function DevFlush (var f: TTextRec): Integer;
353 begin
354 result := DevInOut(f);
355 end;
357 function DevClose (var f: TTextRec): Integer;
358 begin
359 result := 0;
360 end;
363 procedure e_InitWritelnDriver ();
364 begin
365 if not driverInited then
366 begin
367 driverInited := true;
368 with TTextRec(output) do
369 begin
370 Mode := fmClosed;
371 if BufPtr = nil then
372 begin
373 BufSize := SizeOf(Buffer);
374 BufPtr := @Buffer;
375 end;
376 OpenFunc := @DevOpen;
377 InOutFunc := @DevInOut;
378 FlushFunc := @DevFlush;
379 CloseFunc := @DevClose;
380 Name[0] := #0;
381 end;
382 Rewrite(output);
383 end;
384 end;
387 begin
388 //e_InitWritelnDriver();
389 end.