DEADSOFTWARE

renamed "mapdesc.txt" to "mapdef.txt"
[d2df-sdl.git] / src / shared / utils.pas
index 712923e93b6c2a2fa2abefe95a1b282f51429ebc..36d59ff8bd1d5c22606d8b62e4b0727c0e389cba 100644 (file)
@@ -1,8 +1,27 @@
-{$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, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 a_modes.inc}
 unit utils;
 
 interface
 
+uses
+  SysUtils, Classes;
+
+
 // does filename have one of ".wad", ".pk3", ".zip" extensions?
 function hasWadExtension (fn: AnsiString): Boolean;
 
@@ -16,6 +35,7 @@ function addWadExtension (fn: AnsiString): AnsiString;
 function Int64ToStrComma (i: Int64): AnsiString;
 
 function UpCase1251 (ch: Char): Char;
+function LoCase1251 (ch: Char): Char;
 
 // `true` if strings are equal; ignoring case for cp1251
 function StrEquCI1251 (const s0, s1: AnsiString): Boolean;
@@ -29,11 +49,57 @@ function utf8to1251 (s: AnsiString): AnsiString;
 // nobody cares about shitdoze, so i'll use the same code path for it
 function findFileCI (var pathname: AnsiString; lastIsDir: Boolean=false): Boolean;
 
+// they throws
+function openDiskFileRO (pathname: AnsiString): TStream;
+function createDiskFile (pathname: AnsiString): TStream;
 
-implementation
+// little endian
+procedure writeInt (st: TStream; v: Byte); overload;
+procedure writeInt (st: TStream; v: ShortInt); overload;
+procedure writeInt (st: TStream; v: Word); overload;
+procedure writeInt (st: TStream; v: SmallInt); overload;
+procedure writeInt (st: TStream; v: LongWord); overload;
+procedure writeInt (st: TStream; v: LongInt); overload;
+procedure writeInt (st: TStream; v: Int64); overload;
+procedure writeInt (st: TStream; v: UInt64); overload;
 
-uses
-  SysUtils;
+function readByte (st: TStream): Byte;
+function readShortInt (st: TStream): ShortInt;
+function readWord (st: TStream): Word;
+function readSmallInt (st: TStream): SmallInt;
+function readLongWord (st: TStream): LongWord;
+function readLongInt (st: TStream): LongInt;
+function readInt64 (st: TStream): Int64;
+function readUInt64 (st: TStream): UInt64;
+
+// big endian
+procedure writeIntBE (st: TStream; v: Byte); overload;
+procedure writeIntBE (st: TStream; v: ShortInt); overload;
+procedure writeIntBE (st: TStream; v: Word); overload;
+procedure writeIntBE (st: TStream; v: SmallInt); overload;
+procedure writeIntBE (st: TStream; v: LongWord); overload;
+procedure writeIntBE (st: TStream; v: LongInt); overload;
+procedure writeIntBE (st: TStream; v: Int64); overload;
+procedure writeIntBE (st: TStream; v: UInt64); overload;
+
+function readByteBE (st: TStream): Byte;
+function readShortIntBE (st: TStream): ShortInt;
+function readWordBE (st: TStream): Word;
+function readSmallIntBE (st: TStream): SmallInt;
+function readLongWordBE (st: TStream): LongWord;
+function readLongIntBE (st: TStream): LongInt;
+function readInt64BE (st: TStream): Int64;
+function readUInt64BE (st: TStream): UInt64;
+
+
+type
+  TFormatStrFCallback = procedure (constref buf; len: SizeUInt);
+
+// returns formatted string if `writerCB` is `nil`, empty string otherwise
+function formatstrf (const fmt: AnsiString; args: array of const; writerCB: TFormatStrFCallback=nil): AnsiString;
+
+
+implementation
 
 
 function hasWadExtension (fn: AnsiString): Boolean;
@@ -111,6 +177,30 @@ begin
 end;
 
 
+function LoCase1251 (ch: Char): Char;
+begin
+  if ch < #128 then
+  begin
+    if (ch >= 'A') and (ch <= 'Z') then Inc(ch, 32);
+  end
+  else
+  begin
+    if (ch >= #192) and (ch <= #223) then
+    begin
+      Inc(ch, 32);
+    end
+    else
+    begin
+      case ch of
+        #168, #170, #175: Inc(ch, 16);
+        #161, #178: Inc(ch);
+      end;
+    end;
+  end;
+  result := ch;
+end;
+
+
 function StrEquCI1251 (const s0, s1: AnsiString): Boolean;
 var
   i: Integer;
@@ -308,4 +398,681 @@ begin
 end;
 
 
+function openDiskFileRO (pathname: AnsiString): TStream;
+begin
+  if not findFileCI(pathname) then raise Exception.Create('can''t open file "'+pathname+'"');
+  result := TFileStream.Create(pathname, fmOpenRead or {fmShareDenyWrite}fmShareDenyNone);
+end;
+
+function createDiskFile (pathname: AnsiString): TStream;
+var
+  path: AnsiString;
+begin
+  path := ExtractFilePath(pathname);
+  if length(path) > 0 then
+  begin
+    if not findFileCI(path, true) then raise Exception.Create('can''t create file "'+pathname+'"');
+  end;
+  result := TFileStream.Create(path+ExtractFileName(pathname), fmCreate);
+end;
+
+
+procedure writeIntegerLE (st: TStream; vp: Pointer; size: Integer);
+{$IFDEF ENDIAN_LITTLE}
+begin
+  st.writeBuffer(vp^, size);
+end;
+{$ELSE}
+var
+  p: PByte;
+begin
+  p := PByte(vp)+size-1;
+  while size > 0 do
+  begin
+    st.writeBuffer(p^, 1);
+    Dec(size);
+    Dec(p);
+  end;
+end;
+{$ENDIF}
+
+procedure writeIntegerBE (st: TStream; vp: Pointer; size: Integer);
+{$IFDEF ENDIAN_LITTLE}
+var
+  p: PByte;
+begin
+  p := PByte(vp)+size-1;
+  while size > 0 do
+  begin
+    st.writeBuffer(p^, 1);
+    Dec(size);
+    Dec(p);
+  end;
+end;
+{$ELSE}
+begin
+  st.writeBuffer(vp^, size);
+end;
+{$ENDIF}
+
+procedure writeInt (st: TStream; v: Byte); overload; begin writeIntegerLE(st, @v, 1); end;
+procedure writeInt (st: TStream; v: ShortInt); overload; begin writeIntegerLE(st, @v, 1); end;
+procedure writeInt (st: TStream; v: Word); overload; begin writeIntegerLE(st, @v, 2); end;
+procedure writeInt (st: TStream; v: SmallInt); overload; begin writeIntegerLE(st, @v, 2); end;
+procedure writeInt (st: TStream; v: LongWord); overload; begin writeIntegerLE(st, @v, 4); end;
+procedure writeInt (st: TStream; v: LongInt); overload; begin writeIntegerLE(st, @v, 4); end;
+procedure writeInt (st: TStream; v: Int64); overload; begin writeIntegerLE(st, @v, 8); end;
+procedure writeInt (st: TStream; v: UInt64); overload; begin writeIntegerLE(st, @v, 8); end;
+
+procedure writeIntBE (st: TStream; v: Byte); overload; begin writeIntegerBE(st, @v, 1); end;
+procedure writeIntBE (st: TStream; v: ShortInt); overload; begin writeIntegerBE(st, @v, 1); end;
+procedure writeIntBE (st: TStream; v: Word); overload; begin writeIntegerBE(st, @v, 2); end;
+procedure writeIntBE (st: TStream; v: SmallInt); overload; begin writeIntegerBE(st, @v, 2); end;
+procedure writeIntBE (st: TStream; v: LongWord); overload; begin writeIntegerBE(st, @v, 4); end;
+procedure writeIntBE (st: TStream; v: LongInt); overload; begin writeIntegerBE(st, @v, 4); end;
+procedure writeIntBE (st: TStream; v: Int64); overload; begin writeIntegerBE(st, @v, 8); end;
+procedure writeIntBE (st: TStream; v: UInt64); overload; begin writeIntegerBE(st, @v, 8); end;
+
+
+procedure readIntegerLE (st: TStream; vp: Pointer; size: Integer);
+{$IFDEF ENDIAN_LITTLE}
+begin
+  st.readBuffer(vp^, size);
+end;
+{$ELSE}
+var
+  p: PByte;
+begin
+  p := PByte(vp)+size-1;
+  while size > 0 do
+  begin
+    st.readBuffer(p^, 1);
+    Dec(size);
+    Dec(p);
+  end;
+end;
+{$ENDIF}
+
+procedure readIntegerBE (st: TStream; vp: Pointer; size: Integer);
+{$IFDEF ENDIAN_LITTLE}
+var
+  p: PByte;
+begin
+  p := PByte(vp)+size-1;
+  while size > 0 do
+  begin
+    st.readBuffer(p^, 1);
+    Dec(size);
+    Dec(p);
+  end;
+end;
+{$ELSE}
+begin
+  st.readBuffer(vp^, size);
+end;
+{$ENDIF}
+
+function readByte (st: TStream): Byte; begin readIntegerLE(st, @result, 1); end;
+function readShortInt (st: TStream): ShortInt; begin readIntegerLE(st, @result, 1); end;
+function readWord (st: TStream): Word; begin readIntegerLE(st, @result, 2); end;
+function readSmallInt (st: TStream): SmallInt; begin readIntegerLE(st, @result, 2); end;
+function readLongWord (st: TStream): LongWord; begin readIntegerLE(st, @result, 4); end;
+function readLongInt (st: TStream): LongInt; begin readIntegerLE(st, @result, 4); end;
+function readInt64 (st: TStream): Int64; begin readIntegerLE(st, @result, 8); end;
+function readUInt64 (st: TStream): UInt64; begin readIntegerLE(st, @result, 8); end;
+
+function readByteBE (st: TStream): Byte; begin readIntegerBE(st, @result, 1); end;
+function readShortIntBE (st: TStream): ShortInt; begin readIntegerBE(st, @result, 1); end;
+function readWordBE (st: TStream): Word; begin readIntegerBE(st, @result, 2); end;
+function readSmallIntBE (st: TStream): SmallInt; begin readIntegerBE(st, @result, 2); end;
+function readLongWordBE (st: TStream): LongWord; begin readIntegerBE(st, @result, 4); end;
+function readLongIntBE (st: TStream): LongInt; begin readIntegerBE(st, @result, 4); end;
+function readInt64BE (st: TStream): Int64; begin readIntegerBE(st, @result, 8); end;
+function readUInt64BE (st: TStream): UInt64; begin readIntegerBE(st, @result, 8); end;
+
+
+// ////////////////////////////////////////////////////////////////////////// //
+{$IFDEF WINDOWS}
+function snprintf (buf: PAnsiChar; bufsize: SizeUInt; const fmt: PAnsiChar): SizeUInt; cdecl; varargs; external 'msvcrt.dll' name '_snprintf';
+{$ELSE}
+function snprintf (buf: PAnsiChar; bufsize: SizeUInt; const fmt: PAnsiChar): SizeUInt; cdecl; varargs; external 'libc' name 'snprintf';
+{$ENDIF}
+
+
+(*
+procedure conwriter (constref buf; len: SizeUInt);
+var
+  ss: ShortString;
+  slen: Integer;
+  b: PByte;
+begin
+  if (len < 1) then exit;
+  b := PByte(@buf);
+  while (len > 0) do
+  begin
+    if (len > 255) then slen := 255 else slen := Integer(len);
+    Move(b^, ss[1], len);
+    ss[0] := AnsiChar(slen);
+    write(ss);
+    b += slen;
+    len -= slen;
+  end;
+end;
+*)
+
+
+function formatstrf (const fmt: AnsiString; args: array of const; writerCB: TFormatStrFCallback=nil): AnsiString;
+const
+  PadSpaces: AnsiString = '                                                                       ';
+  PadZeroes: AnsiString = '00000000000000000000000000000000000000000000000000000000000000000000000';
+var
+  curarg: Integer = 0; // current arg in `args`
+  sign, fmtch: AnsiChar;
+  zeropad: Boolean;
+  width, prec: Integer; // width and precision
+  spos, epos: Integer;
+  ch: AnsiChar;
+  strbuf: array[0..256] of AnsiChar;
+  strblen: SizeUInt;
+  fmtbuf: array[0..256] of AnsiChar;
+  fmtblen: Integer;
+  pclen: Integer;
+  pc: PAnsiChar;
+
+  procedure writer (constref buf; len: SizeUInt);
+  var
+    ss: ShortString;
+    slen: Integer;
+    b: PByte;
+  begin
+    if (len < 1) then exit;
+    b := PByte(@buf);
+    if assigned(writerCB) then
+    begin
+      writerCB(b^, len);
+    end
+    else
+    begin
+      while (len > 0) do
+      begin
+        if (len > 255) then slen := 255 else slen := Integer(len);
+        Move(b^, ss[1], len);
+        ss[0] := AnsiChar(slen);
+        result += ss;
+        b += slen;
+        len -= slen;
+      end;
+    end;
+  end;
+
+  procedure xwrite (const s: AnsiString);
+  begin
+    if (Length(s) > 0) then writer(PAnsiChar(s)^, Length(s));
+  end;
+
+  procedure putFmtChar (ch: AnsiChar);
+  begin
+    fmtbuf[fmtblen] := ch;
+    Inc(fmtblen);
+  end;
+
+  procedure putFmtInt (n: Integer);
+  var
+    len: SizeUInt;
+  begin
+    len := snprintf(@fmtbuf[fmtblen], Length(fmtbuf)-fmtblen, '%d', n);
+    if (len > 0) then Inc(fmtblen, len);
+  end;
+
+  procedure buildCFormat (const pfx: AnsiString='');
+  var
+    f: Integer;
+  begin
+    fmtblen := 0;
+    for f := 1 to Length(pfx) do putFmtChar(pfx[f]);
+    putFmtChar('%');
+    if (sign <> ' ') then putFmtChar(sign);
+    if (width >= 0) then
+    begin
+      if (zeropad) then putFmtChar('0');
+      putFmtInt(width);
+      if (prec >= 0) then
+      begin
+        putFmtChar('.');
+        putFmtInt(prec);
+      end;
+    end;
+    putFmtChar(fmtch);
+    fmtbuf[fmtblen] := #0;
+  end;
+
+  procedure writeStrBuf ();
+  begin
+    if (strblen > 0) then writer(strbuf, strblen);
+  end;
+
+  function i642str (n: Int64; hex: Boolean; hexup: Boolean): PAnsiChar;
+  var
+    neg: Boolean;
+    xpos: Integer;
+  begin
+    if (n = $8000000000000000) then
+    begin
+      if hex then snprintf(@strbuf[0], Length(strbuf), '-8000000000000000')
+      else snprintf(@strbuf[0], Length(strbuf), '-9223372036854775808');
+      result := @strbuf[0];
+    end
+    else
+    begin
+      neg := (n < 0);
+      if neg then n := -n;
+      xpos := High(strbuf);
+      strbuf[xpos] := #0; Dec(xpos);
+      repeat
+        if hex then
+        begin
+          strbuf[xpos] := AnsiChar((n mod 10)+48);
+          Dec(xpos);
+          n := n div 10;
+        end
+        else
+        begin
+          if (n mod 16 > 9) then
+          begin
+            strbuf[xpos] := AnsiChar((n mod 16)+48+7);
+            if not hexup then Inc(strbuf[xpos], 32);
+          end
+          else strbuf[xpos] := AnsiChar((n mod 16)+48);
+          Dec(xpos);
+          n := n div 16;
+        end;
+      until (n = 0);
+      if neg then begin strbuf[xpos] := '-'; Dec(xpos); end;
+      result := @strbuf[xpos+1];
+    end;
+  end;
+
+  function ui642str (n: UInt64; hex: Boolean; hexup: Boolean): PAnsiChar;
+  var
+    xpos: Integer;
+  begin
+    xpos := High(strbuf);
+    strbuf[xpos] := #0; Dec(xpos);
+    repeat
+      if hex then
+      begin
+        strbuf[xpos] := AnsiChar((n mod 10)+48);
+        Dec(xpos);
+        n := n div 10;
+      end
+      else
+      begin
+        if (n mod 16 > 9) then
+        begin
+          strbuf[xpos] := AnsiChar((n mod 16)+48+7);
+          if not hexup then Inc(strbuf[xpos], 32);
+        end
+        else strbuf[xpos] := AnsiChar((n mod 16)+48);
+        Dec(xpos);
+        n := n div 16;
+      end;
+    until (n = 0);
+    result := @strbuf[xpos+1];
+  end;
+
+  procedure indent (len: Integer);
+  var
+    ilen: Integer;
+  begin
+    while (len > 0) do
+    begin
+      if (len > Length(PadSpaces)) then ilen := Length(PadSpaces) else ilen := len;
+      writer(PAnsiChar(PadSpaces)^, ilen);
+      Dec(len, ilen);
+    end;
+  end;
+
+  procedure indent0 (len: Integer);
+  var
+    ilen: Integer;
+  begin
+    while (len > 0) do
+    begin
+      if (len > Length(PadZeroes)) then ilen := Length(PadZeroes) else ilen := len;
+      writer(PAnsiChar(PadZeroes)^, ilen);
+      Dec(len, ilen);
+    end;
+  end;
+
+begin
+  result := '';
+  spos := 1;
+  while (spos <= Length(fmt)) do
+  begin
+    // print literal part
+    epos := spos;
+    while (epos <= Length(fmt)) and (fmt[epos] <> '%') do Inc(epos);
+    // output literal part
+    if (epos > spos) then
+    begin
+      if (epos > Length(fmt)) then
+      begin
+        writer((PAnsiChar(fmt)+spos-1)^, epos-spos);
+        break;
+      end;
+           if (epos+1 > Length(fmt)) then Inc(epos) // last percent, output literally
+      else if (fmt[epos+1] = '%') then // special case
+      begin
+        Inc(epos);
+        writer((PAnsiChar(fmt)+spos-1)^, epos-spos);
+        spos := epos+1;
+      end
+      else
+      begin
+        writer((PAnsiChar(fmt)+spos-1)^, epos-spos);
+        spos := epos;
+      end;
+      continue;
+    end;
+    // check if we have argument for this format string
+    if (curarg > High(args)) then
+    begin
+      xwrite('<OUT OF ARGS>');
+      writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1);
+      break;
+    end;
+    // skip percent
+    if (spos+1 > Length(fmt)) then break; // oops
+    assert(fmt[spos] = '%');
+    Inc(spos);
+    // parse format; check for sign
+         if (fmt[spos] = '-') then begin sign := '-'; Inc(spos); end
+    else if (fmt[spos] = '+') then begin sign := '+'; Inc(spos); end
+    else sign := ' ';
+    // parse width
+    if (spos > Length(fmt)) then begin xwrite('<INVALID FORMAT>'); break; end;
+    if (sign <> ' ') or ((fmt[spos] >= '0') and (fmt[spos] <= '9')) then
+    begin
+      if (fmt[spos] < '0') or (fmt[spos] > '9') then begin xwrite('<INVALID FORMAT>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
+      zeropad := (fmt[spos] = '0');
+      width := 0;
+      while (spos <= Length(fmt)) do
+      begin
+        ch := fmt[spos];
+        if (ch < '0') or (ch > '9') then break;
+        width := width*10+Integer(ch)-48;
+        Inc(spos);
+      end;
+    end
+    else
+    begin
+      width := -1;
+      zeropad := false;
+    end;
+    // parse precision
+    prec := -1;
+    if (spos <= Length(fmt)) and (fmt[spos] = '.') then
+    begin
+      Inc(spos);
+      if (spos > Length(fmt)) then begin xwrite('<INVALID FORMAT>'); break; end;
+      if (fmt[spos] < '0') or (fmt[spos] > '9') then begin xwrite('<INVALID FORMAT>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
+      prec := 0;
+      while (spos <= Length(fmt)) do
+      begin
+        ch := fmt[spos];
+        if (ch < '0') or (ch > '9') then break;
+        prec := prec*10+Integer(ch)-48;
+        Inc(spos);
+      end;
+    end;
+    // get format char
+    if (spos > Length(fmt)) then begin xwrite('<INVALID FORMAT>'); break; end;
+    fmtch := fmt[spos];
+    Inc(spos);
+    // done parsing format, check for valid format chars
+    if not (fmtch in ['s','u','d','x','X','p','f','g','c']) then begin xwrite('<INVALID FORMAT CHAR>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
+    // now write formatted string
+    case args[curarg].VType of
+      vtInteger: // args[curarg].VInteger
+        begin
+          if not (fmtch in ['s','u','d','x','X']) then begin xwrite('<INVALID FORMAT CHAR>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
+          if (fmtch = 's') then fmtch := 'd';
+          buildCFormat();
+          strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], args[curarg].VInteger);
+          writeStrBuf();
+        end;
+      vtBoolean: // args[curarg].VBoolean
+        case fmtch of
+          's':
+            begin
+              buildCFormat();
+              if args[curarg].VBoolean then strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], 'true')
+              else strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], 'false');
+              writeStrBuf();
+            end;
+          'c':
+            begin
+              buildCFormat();
+              if args[curarg].VBoolean then strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], AnsiChar('t'))
+              else strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], AnsiChar('f'));
+              writeStrBuf();
+            end;
+          'u', 'd', 'x', 'X':
+            begin
+              buildCFormat();
+              strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], Integer(args[curarg].VBoolean));
+              writeStrBuf();
+            end;
+          else
+            begin
+              xwrite('<INVALID FORMAT CHAR>');
+              writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1);
+              break;
+            end;
+        end;
+      vtChar: // args[curarg].VChar
+        case fmtch of
+          's', 'c':
+            begin
+              fmtch := 'c';
+              buildCFormat();
+              strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], args[curarg].VChar);
+              writeStrBuf();
+            end;
+          'u', 'd', 'x', 'X':
+            begin
+              buildCFormat();
+              strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], Integer(args[curarg].VChar));
+              writeStrBuf();
+            end;
+          else
+            begin
+              xwrite('<INVALID FORMAT CHAR>');
+              writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1);
+              break;
+            end;
+        end;
+      //vtWideChar: begin end; // args[curarg].VWideChar (WideChar)
+      vtExtended: // args[curarg].VExtended^
+        case fmtch of
+          's', 'g':
+            begin
+              fmtch := 'g';
+              buildCFormat();
+              strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], Double(args[curarg].VExtended^));
+              writeStrBuf();
+            end;
+          'f':
+            begin
+              buildCFormat();
+              strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], Double(args[curarg].VExtended^));
+              writeStrBuf();
+            end;
+          'd':
+            begin
+              buildCFormat();
+              strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], Integer(trunc(args[curarg].VExtended^)));
+              writeStrBuf();
+            end;
+          'u', 'x', 'X':
+            begin
+              buildCFormat();
+              strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], LongWord(trunc(args[curarg].VExtended^)));
+              writeStrBuf();
+            end;
+          else
+            begin
+              xwrite('<INVALID FORMAT CHAR>');
+              writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1);
+              break;
+            end;
+        end;
+      vtString: // args[curarg].VString^ (PShortString)
+        begin
+          if (sign <> '-') then indent(width-Length(args[curarg].VString^));
+          writer(args[curarg].VString^[1], Length(args[curarg].VString^));
+          if (sign = '-') then indent(width-Length(args[curarg].VString^));
+        end;
+      vtPointer: // args[curarg].VPointer
+        case fmtch of
+          's':
+            begin
+              fmtch := 'x';
+              if (width < 8) then width := 8;
+              zeropad := true;
+              buildCFormat('0x');
+              strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], PtrUInt(args[curarg].VPointer));
+              writeStrBuf();
+            end;
+          'u', 'd', 'x', 'p', 'X':
+            begin
+              if (fmtch = 'p') then fmtch := 'x';
+              if (width < 8) then width := 8;
+              zeropad := true;
+              buildCFormat('0x');
+              strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], PtrUInt(args[curarg].VPointer));
+              writeStrBuf();
+            end;
+          else
+            begin
+              xwrite('<INVALID FORMAT CHAR>');
+              writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1);
+              break;
+            end;
+        end;
+      vtPChar: // args[curarg].VPChar
+        if (args[curarg].VPChar = nil) then
+        begin
+          if (sign <> '-') then indent(width-3);
+          xwrite('nil');
+          if (sign = '-') then indent(width-3);
+        end
+        else
+        begin
+          pclen := 0;
+          while (args[curarg].VPChar[pclen] <> #0) do Inc(pclen);
+          if (sign <> '-') then indent(width-pclen);
+          writer(args[curarg].VPChar^, pclen);
+          if (sign = '-') then indent(width-pclen);
+        end;
+      vtObject: // args[curarg].VObject.Classname (TObject)
+        begin
+          if (sign <> '-') then indent(width-Length(args[curarg].VObject.Classname));
+          xwrite(args[curarg].VObject.Classname);
+          if (sign = '-') then indent(width-Length(args[curarg].VObject.Classname));
+        end;
+      vtClass: // args[curarg].VClass.Classname (TClass)
+        begin
+          if (sign <> '-') then indent(width-Length(args[curarg].VClass.Classname));
+          xwrite(args[curarg].VClass.Classname);
+          if (sign = '-') then indent(width-Length(args[curarg].VClass.Classname));
+        end;
+      //vtPWideChar: begin end; // args[curarg].VPWideChar (PWideChar)
+      vtAnsiString: // AnsiString(args[curarg].VAnsiString) (Pointer)
+        begin
+          if (sign <> '-') then indent(width-Length(AnsiString(args[curarg].VAnsiString)));
+          xwrite(AnsiString(args[curarg].VAnsiString));
+          if (sign = '-') then indent(width-Length(AnsiString(args[curarg].VAnsiString)));
+        end;
+      //vtCurrency: begin end; // args[curarg].VCurrency (PCurrency)
+      //vtVariant: begin end; // args[curarg].VVariant^ (PVariant)
+      //vtInterface: begin end; // args[curarg].VInterface (Pointer);
+      //vtWideString: begin end; // args[curarg].VWideString (Pointer);
+      vtInt64: // args[curarg].VInt64^ (PInt64)
+        begin
+          case fmtch of
+            's','d','u': pc := i642str(args[curarg].VInt64^, false, false);
+            'x': pc := i642str(args[curarg].VInt64^, true, false);
+            'X': pc := i642str(args[curarg].VInt64^, true, true);
+            else begin xwrite('<INVALID FORMAT CHAR>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
+          end;
+          pclen := 0;
+          while (pc[pclen] <> #0) do Inc(pclen);
+          if (sign <> '-') and (width > pclen) then
+          begin
+            if zeropad then
+            begin
+              if (pc[0] = '-') or (pc[0] = '+') then
+              begin
+                writer(pc^, 1);
+                indent0(width-pclen-1);
+                Inc(pc);
+                Dec(pclen);
+              end
+              else
+              begin
+                indent0(width-pclen);
+              end;
+            end
+            else
+            begin
+              indent(width-pclen);
+            end;
+          end;
+          writer(pc^, pclen);
+          if (sign = '-') then indent(width-pclen);
+        end;
+      vtQWord: // args[curarg].VQWord^ (PQWord)
+        begin
+          case fmtch of
+            's','d','u': pc := ui642str(args[curarg].VInt64^, false, false);
+            'x': pc := ui642str(args[curarg].VInt64^, true, false);
+            'X': pc := ui642str(args[curarg].VInt64^, true, true);
+            else begin xwrite('<INVALID FORMAT CHAR>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
+          end;
+          pclen := 0;
+          while (pc[pclen] <> #0) do Inc(pclen);
+          if (sign <> '-') then begin if zeropad then indent0(width-pclen) else indent(width-pclen); end;
+          writer(pc^, pclen);
+          if (sign = '-') then indent(width-pclen);
+        end;
+      else
+        begin
+          xwrite('<INVALID TYPE>');
+          writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1);
+          break;
+        end;
+    end;
+    Inc(curarg);
+  end;
+end;
+
+
+(*
+var
+  ss: ShortString;
+  ls: AnsiString;
+  i64: Int64 = -$A000000000;
+  ui64: UInt64 = $A000000000;
+begin
+  writef(conwriter, 'test int:<%s> bool:<%s:%02d:%c> bool:<%s:%02d:%c>; char:<%2s;%c;%d>!'#10, [42, true, true, true, false, false, false, 'A', 'A', 'A']);
+  writef(conwriter, 'test float:<%s;%u;%f;%g>'#10, [666.6942, 666.6942, 666.6942, 666.6942]);
+  ss := 'fuckit';
+  ls := 'FUCKIT';
+  writef(conwriter, 'test ss:<%5s;%040s>'#10, [ss, ss]);
+  writef(conwriter, 'test ls:<%5s;%040s>'#10, [ls, ls]);
+  writef(conwriter, 'test pointer:<%s;%x;%p>'#10, [@ss, @ss, @ss]);
+  writef(conwriter, 'test i64:<%s;%x;%015d;%u;%X>'#10, [i64, i64, i64, i64, i64]);
+  writef(conwriter, 'test ui64:<%s;%x;%15d;%015u;%X>'#10, [ui64, ui64, ui64, ui64, ui64]);
+*)
 end.