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 {$DEFINE D2DF_FORCE_OBJFPC}
16 {$INCLUDE a_modes.inc}
19 interface
21 uses
25 // ////////////////////////////////////////////////////////////////////////// //
26 type
30 const
32 InvalidUnicodeCodepoint = $FFFD; // Unicode REPLACEMENT CHARACTER used to replace an unknown, unrecognised, or unrepresentable character
41 '.dfzip'
42 );
44 {$IF DEFINED(FREEBSD) OR DEFINED(DARWIN)}
46 {$ELSE}
48 {$ENDIF}
51 // ////////////////////////////////////////////////////////////////////////// //
52 type
54 public
59 private
62 public
65 public
74 // process one byte, return `true` if codepoint is ready
80 // ////////////////////////////////////////////////////////////////////////// //
85 // rewrites slashes to '/'
88 // replaces all the shitty characters with '_'
89 // (everything except alphanumerics, '_', '.')
95 // strips out name from `fn`, leaving trailing slash
98 // ends with '/' or '\'?
101 // strips extra trailing slashes in `path, and extra leading slashes in `fn`
102 // will add slash to `path`, even if `fn` is empty!
105 // does filename have one of ".wad", ".pk3", ".zip" extensions?
108 // does filepath have ".XXX:\" in it?
111 // adds ".wad" extension if filename doesn't have one of ".wad", ".pk3", ".zip"
114 // check wad signature
117 // convert number to strig with nice commas
127 // `true` if strings are equal; ignoring case for cp1251
134 // findFileCI takes case-insensitive path, traverses it, and rewrites it to
135 // a case-sensetive one (using real on-disk names). return value means 'success'.
136 // if some dir or file wasn't found, pathname is undefined (destroyed, but not
137 // necessarily cleared).
138 // last name assumed to be a file, not directory (unless `lastIsDir` flag is set).
142 // findDiskWad tries to find the wad file using common wad extensions
143 // (see `wadExtensions` array).
144 // returns real on-disk filename, or empty string.
145 // original wad extension is used as a hint for the first try.
146 // also, this automatically performs `findFileCI()`.
148 // slashes must be normalized!
151 // they throws
154 // create file if necessary, but don't truncate the existing one
157 // little endian
185 // big endian
216 {$IFDEF FPC_HAS_TYPE_EXTENDED}
218 {$ENDIF}
230 {$IFDEF FPC_HAS_TYPE_EXTENDED}
232 {$ENDIF}
243 {$IFDEF FPC_HAS_TYPE_EXTENDED}
245 {$ENDIF}
247 type
250 // returns formatted string if `writerCB` is `nil`, empty string otherwise
251 function formatstrf (const fmt: AnsiString; const args: array of const; writerCB: TFormatStrFCallback=nil): AnsiString;
258 // returns string in single or double quotes
259 // single quotes supports only pascal-style '' for single quote char
260 // double quotes supports c-style escapes
261 // function will select quote mode automatically
263 // separate single-quote and double-quote escape functions
268 type
270 private
271 //type PItemT = ^ItemT;
274 public
275 type
277 private
281 public
288 private
292 private
299 public
303 //WARNING! don't change list contents in `for ... in`!
313 public
325 type
330 // not changed by info getter; used in other parts of the code
340 implementation
342 //uses
343 // xstreams;
345 // ////////////////////////////////////////////////////////////////////////// //
347 begin
352 begin
357 begin
362 // ////////////////////////////////////////////////////////////////////////// //
363 // rewrites slashes to '/'
365 {$IFDEF WINDOWS}
366 var
368 {$ENDIF}
369 begin
371 {$IFDEF WINDOWS}
373 {$ENDIF}
376 // replaces all the shitty characters with '_'
377 // (everything except alphanumerics, '_', '.')
379 var
381 const
384 begin
392 begin
395 {$IFDEF WINDOWS}
397 if (length(s) > 2) and (s[2] = ':') and ((s[3] = '/') or (s[3] = '\')) then begin result := true; exit; end;
398 {$ELSE}
400 {$ENDIF}
405 begin
408 {$IFDEF WINDOWS}
410 if (length(s) = 3) and (s[2] = ':') and ((s[3] = '/') or (s[3] = '\')) then begin result := true; exit; end;
411 {$ELSE}
413 {$ENDIF}
417 // ////////////////////////////////////////////////////////////////////////// //
419 begin
426 begin
432 begin
437 // ////////////////////////////////////////////////////////////////////////// //
439 begin
447 begin
454 begin
460 begin
467 begin
474 begin
480 begin
487 begin
493 begin
499 var
501 begin
503 begin
513 var
515 begin
517 begin
524 var
526 begin
528 begin
531 end
532 else
533 begin
539 // ////////////////////////////////////////////////////////////////////////// //
540 var
545 // ////////////////////////////////////////////////////////////////////////// //
546 const
548 $0402,$0403,$201A,$0453,$201E,$2026,$2020,$2021,$20AC,$2030,$0409,$2039,$040A,$040C,$040B,$040F,
549 $0452,$2018,$2019,$201C,$201D,$2022,$2013,$2014,InvalidUnicodeCodepoint,$2122,$0459,$203A,$045A,$045C,$045B,$045F,
550 $00A0,$040E,$045E,$0408,$00A4,$0490,$00A6,$00A7,$0401,$00A9,$0404,$00AB,$00AC,$00AD,$00AE,$0407,
551 $00B0,$00B1,$0406,$0456,$0491,$00B5,$00B6,$00B7,$0451,$2116,$0454,$00BB,$0458,$0405,$0455,$0457,
552 $0410,$0411,$0412,$0413,$0414,$0415,$0416,$0417,$0418,$0419,$041A,$041B,$041C,$041D,$041E,$041F,
553 $0420,$0421,$0422,$0423,$0424,$0425,$0426,$0427,$0428,$0429,$042A,$042B,$042C,$042D,$042E,$042F,
554 $0430,$0431,$0432,$0433,$0434,$0435,$0436,$0437,$0438,$0439,$043A,$043B,$043C,$043D,$043E,$043F,
556 );
560 var
562 begin
570 // ////////////////////////////////////////////////////////////////////////// //
571 // fast state-machine based UTF-8 decoder; using 8 bytes of memory
572 // code points from invalid range will never be valid, this is the property of the state machine
573 const
574 // see http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
576 // maps bytes to character classes
593 // maps a combination of a state of the automaton and a character class to a state
603 // ////////////////////////////////////////////////////////////////////////// //
604 constructor TUtf8DecoderFast.Create (v: Boolean{fuck you, fpc}); begin state := Accept; codepoint := 0; end;
610 function TUtf8DecoderFast.completeOrInvalid (): Boolean; inline; begin result := (state = Accept) or (state = Reject); end;
612 function TUtf8DecoderFast.decode (c: AnsiChar): Boolean; inline; overload; begin result := decode(Byte(c)); end;
615 var
617 begin
620 if (state <> Accept) then codepoint := (b and $3f) or (codepoint shl 6) else codepoint := ($ff shr tp) and b;
627 // ////////////////////////////////////////////////////////////////////////// //
629 begin
631 if (LongWord(wc) > 65535) then result := Invalid1251Char else result := wc2shitmap[LongWord(wc)];
635 // ////////////////////////////////////////////////////////////////////////// //
637 var
640 begin
642 begin
644 begin
648 begin
651 exit;
659 var
663 begin
667 begin
669 end
671 begin
674 end
676 begin
680 end
682 begin
690 begin
692 begin
694 begin
697 begin
699 begin
701 end
702 else
703 begin
707 exit;
714 // ////////////////////////////////////////////////////////////////////////// //
716 begin
721 begin
724 end
725 else
726 begin
735 // ////////////////////////////////////////////////////////////////////////// //
737 var
739 begin
742 begin
750 var
753 begin
756 begin
764 begin
767 end
769 begin
772 end
773 else
774 begin
782 var
785 begin
787 begin
795 // ////////////////////////////////////////////////////////////////////////// //
797 var
800 begin
803 begin
806 begin
808 exit;
818 var
821 begin
826 begin
838 var
841 begin
845 begin
848 begin
850 begin
852 end
853 else
854 begin
857 exit;
864 begin
871 // strips out name from `fn`, leaving trailing slash
873 var
876 begin
881 begin
890 // ends with '/' or '\'?
892 begin
894 begin
896 end
897 else
898 begin
904 // strips extra trailing slashes in `path, and extra leading slashes in `fn`
905 // will add slash to `path`, even if `fn` is empty!
907 var
909 begin
913 if (Length(result) > 0) and ((result[Length(result)] <> '/') and (result[Length(result)] <> '\')) then result += '/';
915 begin
917 //FIXME: make this faster!
918 while (Length(result) > 0) and ((result[Length(result)] = '/') or (result[Length(result)] = '\')) do
919 begin
928 var
930 begin
935 //result := StrEquCI1251(ext, '.wad') or StrEquCI1251(ext, '.pk3') or StrEquCI1251(ext, '.zip') or StrEquCI1251(ext, '.dfz');
940 begin
948 begin
950 Result :=
951 (* ZIP *)
954 (* PACK *)
957 (* DFWAD *)
958 ((len > 5) and (p[0] = 'D') and (p[1] = 'F') and (p[2] = 'W') and (p[3] = 'A') and (p[4] = 'D') and (p[5] = #01))
963 var
966 begin
970 begin
972 begin
975 begin
978 begin
980 begin
982 exit;
993 var
995 begin
999 begin
1006 begin
1008 begin
1010 end
1011 else
1012 begin
1014 begin
1016 end
1017 else
1018 begin
1030 begin
1032 begin
1034 end
1035 else
1036 begin
1038 begin
1040 end
1041 else
1042 begin
1053 begin
1058 begin
1064 var
1066 begin
1075 var
1078 begin
1080 begin
1082 begin
1086 exit;
1089 // nothing to do
1094 // ////////////////////////////////////////////////////////////////////////// //
1095 // utils
1096 // `ch`: utf8 start
1097 // -1: invalid utf8
1099 begin
1111 var
1113 begin
1117 begin
1123 // check other sequence bytes
1125 begin
1135 // ////////////////////////////////////////////////////////////////////////// //
1136 const
1138 $0402,$0403,$201A,$0453,$201E,$2026,$2020,$2021,$20AC,$2030,$0409,$2039,$040A,$040C,$040B,$040F,
1139 $0452,$2018,$2019,$201C,$201D,$2022,$2013,$2014,InvalidUnicodeCodepoint,$2122,$0459,$203A,$045A,$045C,$045B,$045F,
1140 $00A0,$040E,$045E,$0408,$00A4,$0490,$00A6,$00A7,$0401,$00A9,$0404,$00AB,$00AC,$00AD,$00AE,$0407,
1141 $00B0,$00B1,$0406,$0456,$0491,$00B5,$00B6,$00B7,$0451,$2116,$0454,$00BB,$0458,$0405,$0455,$0457,
1142 $0410,$0411,$0412,$0413,$0414,$0415,$0416,$0417,$0418,$0419,$041A,$041B,$041C,$041D,$041E,$041F,
1143 $0420,$0421,$0422,$0423,$0424,$0425,$0426,$0427,$0428,$0429,$042A,$042B,$042C,$042D,$042E,$042F,
1144 $0430,$0431,$0432,$0433,$0434,$0435,$0436,$0437,$0438,$0439,$043A,$043B,$043C,$043D,$043E,$043F,
1145 $0440,$0441,$0442,$0443,$0444,$0445,$0446,$0447,$0448,$0449,$044A,$044B,$044C,$044D,$044E,$044F
1146 );
1150 var
1152 begin
1153 (* The following encodings are valid, except for the 5 and 6 byte
1154 * combinations:
1155 * 0xxxxxxx
1156 * 110xxxxx 10xxxxxx
1157 * 1110xxxx 10xxxxxx 10xxxxxx
1158 * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
1159 * 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
1160 * 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
1161 *)
1169 // mask out unused bits
1177 // now continue
1179 begin
1187 // done, try 1251
1189 // alas
1194 var
1196 begin
1200 begin
1211 // ////////////////////////////////////////////////////////////////////////// //
1212 // findFileCI eats case-insensitive path, traverses it and rewrites it to a
1213 // case-sensetive. result value means success.
1214 // if file/dir not founded than pathname is in undefined state!
1216 var
1224 begin
1229 begin
1230 // remove trailing slashes
1233 // extract name
1236 begin
1240 // remove trailing slashes again
1243 //writeln(Format('npt=[%s]; newname=[%s]; curname=[%s]; wantdir=%d', [npt, newname, curname, Integer(wantdir)]));
1244 // try the easiest case first
1247 begin
1249 begin
1250 // i found her!
1253 continue;
1256 //writeln(Format('npt=[%s]; newname=[%s]; curname=[%s]; wantdir=%d', [npt, newname, curname, Integer(wantdir)]));
1257 // alas, either not found, or invalid attributes
1259 try
1261 repeat
1263 begin
1264 // i found her!
1268 break;
1271 finally
1281 begin
1289 var
1292 begin
1295 // check first ext
1298 for newExt in wadExtensions do if (StrEquCI1251(ext, newExt)) then begin found := true; break; end;
1300 // check second ext
1303 for newExt in wadExtensions do if (StrEquCI1251(ext, newExt)) then begin found := true; break; end;
1311 var
1314 begin
1316 //writeln('findDiskWad00: fname=<', fname, '>');
1320 //writeln(' findDiskWad01: fname=<', fname, '>; origExt=<', origExt, '>');
1322 begin
1323 //writeln(' findDiskWad02: fname=<', fname, '>; origExt=<', origExt, '>; newExt=<', newExt, '>');
1325 begin
1326 //writeln(' SKIP');
1327 continue;
1337 begin
1338 if not findFileCI(pathname) then raise EFileNotFoundException.Create('can''t open file "'+pathname+'"');
1343 var
1345 begin
1348 begin
1349 if not findFileCI(path, true) then raise Exception.Create('can''t create file "'+pathname+'"');
1356 var
1359 begin
1360 //writeln('*** TRYING R/W FILE "', pathname, '"');
1363 begin
1364 if not findFileCI(path, true) then raise Exception.Create('can''t create file "'+pathname+'"');
1368 begin
1369 //writeln('*** found old file "', oldname, '"');
1371 end
1372 else
1373 begin
1380 {$IFDEF ENDIAN_LITTLE}
1381 begin
1384 {$ELSE}
1385 var
1387 begin
1390 begin
1396 {$ENDIF}
1399 {$IFDEF ENDIAN_LITTLE}
1400 var
1402 begin
1405 begin
1411 {$ELSE}
1412 begin
1415 {$ENDIF}
1418 begin
1423 var
1426 begin
1429 begin
1431 begin
1434 end
1435 else
1436 begin
1438 begin
1457 procedure writeIntBE (st: TStream; v: ShortInt); overload; begin writeIntegerBE(st, @v, 1); end;
1459 procedure writeIntBE (st: TStream; v: SmallInt); overload; begin writeIntegerBE(st, @v, 2); end;
1460 procedure writeIntBE (st: TStream; v: LongWord); overload; begin writeIntegerBE(st, @v, 4); end;
1461 procedure writeIntBE (st: TStream; v: LongInt); overload; begin writeIntegerBE(st, @v, 4); end;
1470 begin
1472 if (maxlen <= 65535) then writeInt(st, Word(Length(str))) else writeInt(st, LongWord(Length(str)));
1477 var
1479 begin
1484 begin
1492 {$IFDEF ENDIAN_LITTLE}
1493 begin
1496 {$ELSE}
1497 var
1499 begin
1502 begin
1508 {$ENDIF}
1511 {$IFDEF ENDIAN_LITTLE}
1512 var
1514 begin
1517 begin
1523 {$ELSE}
1524 begin
1527 {$ENDIF}
1548 // ////////////////////////////////////////////////////////////////////////// //
1549 function nlerp (a, b: Integer; t: Single): Integer; inline; begin result := round((1.0 - t) * a + t * b); end;
1551 function nmin (a, b: Byte): Byte; inline; overload; begin if (a < b) then result := a else result := b; end;
1552 function nmin (a, b: ShortInt): ShortInt; inline; overload; begin if (a < b) then result := a else result := b; end;
1553 function nmin (a, b: Word): Word; inline; overload; begin if (a < b) then result := a else result := b; end;
1554 function nmin (a, b: SmallInt): SmallInt; inline; overload; begin if (a < b) then result := a else result := b; end;
1555 function nmin (a, b: LongWord): LongWord; inline; overload; begin if (a < b) then result := a else result := b; end;
1556 function nmin (a, b: LongInt): LongInt; inline; overload; begin if (a < b) then result := a else result := b; end;
1557 function nmin (a, b: Int64): Int64; inline; overload; begin if (a < b) then result := a else result := b; end;
1558 function nmin (a, b: UInt64): UInt64; inline; overload; begin if (a < b) then result := a else result := b; end;
1559 function nmin (a, b: Single): Single; inline; overload; begin if (a < b) then result := a else result := b; end;
1560 function nmin (a, b: Double): Double; inline; overload; begin if (a < b) then result := a else result := b; end;
1561 {$IFDEF FPC_HAS_TYPE_EXTENDED}
1562 function nmin (a, b: Extended): Extended; inline; overload; begin if (a < b) then result := a else result := b; end;
1563 {$ENDIF}
1565 function nmax (a, b: Byte): Byte; inline; overload; begin if (a > b) then result := a else result := b; end;
1566 function nmax (a, b: ShortInt): ShortInt; inline; overload; begin if (a > b) then result := a else result := b; end;
1567 function nmax (a, b: Word): Word; inline; overload; begin if (a > b) then result := a else result := b; end;
1568 function nmax (a, b: SmallInt): SmallInt; inline; overload; begin if (a > b) then result := a else result := b; end;
1569 function nmax (a, b: LongWord): LongWord; inline; overload; begin if (a > b) then result := a else result := b; end;
1570 function nmax (a, b: LongInt): LongInt; inline; overload; begin if (a > b) then result := a else result := b; end;
1571 function nmax (a, b: Int64): Int64; inline; overload; begin if (a > b) then result := a else result := b; end;
1572 function nmax (a, b: UInt64): UInt64; inline; overload; begin if (a > b) then result := a else result := b; end;
1573 function nmax (a, b: Single): Single; inline; overload; begin if (a > b) then result := a else result := b; end;
1574 function nmax (a, b: Double): Double; inline; overload; begin if (a > b) then result := a else result := b; end;
1575 {$IFDEF FPC_HAS_TYPE_EXTENDED}
1576 function nmax (a, b: Extended): Extended; inline; overload; begin if (a > b) then result := a else result := b; end;
1577 {$ENDIF}
1579 function nclamp (v, a, b: Byte): Byte; inline; overload; begin if (v < a) then result := a else if (v > b) then result := b else result := v; end;
1580 function nclamp (v, a, b: ShortInt): ShortInt; inline; overload; begin if (v < a) then result := a else if (v > b) then result := b else result := v; end;
1581 function nclamp (v, a, b: Word): Word; inline; overload; begin if (v < a) then result := a else if (v > b) then result := b else result := v; end;
1582 function nclamp (v, a, b: SmallInt): SmallInt; inline; overload; begin if (v < a) then result := a else if (v > b) then result := b else result := v; end;
1583 function nclamp (v, a, b: LongWord): LongWord; inline; overload; begin if (v < a) then result := a else if (v > b) then result := b else result := v; end;
1584 function nclamp (v, a, b: LongInt): LongInt; inline; overload; begin if (v < a) then result := a else if (v > b) then result := b else result := v; end;
1585 function nclamp (v, a, b: Int64): Int64; inline; overload; begin if (v < a) then result := a else if (v > b) then result := b else result := v; end;
1586 function nclamp (v, a, b: UInt64): UInt64; inline; overload; begin if (v < a) then result := a else if (v > b) then result := b else result := v; end;
1587 function nclamp (v, a, b: Single): Single; inline; overload; begin if (v < a) then result := a else if (v > b) then result := b else result := v; end;
1588 function nclamp (v, a, b: Double): Double; inline; overload; begin if (v < a) then result := a else if (v > b) then result := b else result := v; end;
1589 {$IFDEF FPC_HAS_TYPE_EXTENDED}
1590 function nclamp (v, a, b: Extended): Extended; inline; overload; begin if (v < a) then result := a else if (v > b) then result := b else result := v; end;
1591 {$ENDIF}
1593 // ////////////////////////////////////////////////////////////////////////// //
1594 {$IFDEF WINDOWS}
1595 function snprintf (buf: PAnsiChar; bufsize: SizeUInt; const fmt: PAnsiChar): SizeUInt; cdecl; varargs; external 'msvcrt.dll' name '_snprintf';
1596 {$ELSE}
1597 function snprintf (buf: PAnsiChar; bufsize: SizeUInt; const fmt: PAnsiChar): SizeUInt; cdecl; varargs; external 'libc' name 'snprintf';
1598 {$ENDIF}
1601 (*
1602 procedure conwriter (constref buf; len: SizeUInt);
1603 var
1604 ss: ShortString;
1605 slen: Integer;
1606 b: PByte;
1607 begin
1608 if (len < 1) then exit;
1609 b := PByte(@buf);
1610 while (len > 0) do
1611 begin
1612 if (len > 255) then slen := 255 else slen := Integer(len);
1613 Move(b^, ss[1], len);
1614 ss[0] := AnsiChar(slen);
1615 write(ss);
1616 b += slen;
1617 len -= slen;
1618 end;
1619 end;
1620 *)
1623 function formatstrf (const fmt: AnsiString; const args: array of const; writerCB: TFormatStrFCallback=nil): AnsiString;
1624 const
1626 PadZeroes: AnsiString = '00000000000000000000000000000000000000000000000000000000000000000000000';
1627 var
1643 var
1647 begin
1651 begin
1653 end
1654 else
1655 begin
1657 begin
1669 begin
1674 begin
1680 var
1682 begin
1688 var
1690 begin
1696 begin
1700 begin
1710 begin
1715 var
1718 begin
1720 begin
1724 end
1725 else
1726 begin
1731 repeat
1733 begin
1737 end
1738 else
1739 begin
1741 begin
1744 end
1756 var
1758 begin
1761 repeat
1763 begin
1767 end
1768 else
1769 begin
1771 begin
1774 end
1784 var
1786 begin
1788 begin
1796 var
1798 begin
1800 begin
1807 begin
1811 begin
1812 // print literal part
1815 // output literal part
1817 begin
1819 begin
1821 break;
1825 begin
1829 end
1830 else
1831 begin
1835 continue;
1837 // check if we have argument for this format string
1839 begin
1842 break;
1844 // skip percent
1848 // parse format; check for sign
1852 // parse width
1855 begin
1856 if (fmt[spos] < '0') or (fmt[spos] > '9') then begin xwrite('<INVALID FORMAT>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
1860 begin
1866 end
1867 else
1868 begin
1872 // parse precision
1875 begin
1878 if (fmt[spos] < '0') or (fmt[spos] > '9') then begin xwrite('<INVALID FORMAT>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
1881 begin
1888 // get format char
1892 // done parsing format, check for valid format chars
1893 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;
1894 // now write formatted string
1897 begin
1898 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;
1907 begin
1909 if args[curarg].VBoolean then strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], 'true')
1914 begin
1916 if args[curarg].VBoolean then strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], AnsiChar('t'))
1921 begin
1926 else
1927 begin
1930 break;
1936 begin
1943 begin
1948 else
1949 begin
1952 break;
1955 //vtWideChar: begin end; // args[curarg].VWideChar (WideChar)
1959 begin
1966 begin
1972 begin
1974 strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], Integer(trunc(args[curarg].VExtended^)));
1978 begin
1980 strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], LongWord(trunc(args[curarg].VExtended^)));
1983 else
1984 begin
1987 break;
1991 begin
1999 begin
2008 begin
2016 else
2017 begin
2020 break;
2025 begin
2029 end
2030 else
2031 begin
2039 begin
2040 if (args[curarg].VObject <> nil) then ccname := args[curarg].VObject.Classname else ccname := '<nil>';
2046 begin
2047 if (args[curarg].VClass <> nil) then ccname := args[curarg].VClass.Classname else ccname := '<nil>';
2052 //vtPWideChar: begin end; // args[curarg].VPWideChar (PWideChar)
2054 begin
2059 //vtCurrency: begin end; // args[curarg].VCurrency (PCurrency)
2060 //vtVariant: begin end; // args[curarg].VVariant^ (PVariant)
2061 //vtInterface: begin end; // args[curarg].VInterface (Pointer);
2062 //vtWideString: begin end; // args[curarg].VWideString (Pointer);
2064 begin
2069 else begin xwrite('<INVALID FORMAT CHAR>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
2074 begin
2076 begin
2078 begin
2083 end
2084 else
2085 begin
2088 end
2089 else
2090 begin
2098 begin
2103 else begin xwrite('<INVALID FORMAT CHAR>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
2107 if (sign <> '-') then begin if zeropad then indent0(width-pclen) else indent(width-pclen); end;
2111 else
2112 begin
2115 break;
2124 var
2128 begin
2132 // get age
2135 // get size
2141 // fill info
2149 (*
2150 var
2151 ss: ShortString;
2152 ls: AnsiString;
2153 i64: Int64 = -$A000000000;
2154 ui64: UInt64 = $A000000000;
2155 begin
2156 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']);
2157 writef(conwriter, 'test float:<%s;%u;%f;%g>'#10, [666.6942, 666.6942, 666.6942, 666.6942]);
2158 ss := 'fuckit';
2159 ls := 'FUCKIT';
2160 writef(conwriter, 'test ss:<%5s;%040s>'#10, [ss, ss]);
2161 writef(conwriter, 'test ls:<%5s;%040s>'#10, [ls, ls]);
2162 writef(conwriter, 'test pointer:<%s;%x;%p>'#10, [@ss, @ss, @ss]);
2163 writef(conwriter, 'test i64:<%s;%x;%015d;%u;%X>'#10, [i64, i64, i64, i64, i64]);
2164 writef(conwriter, 'test ui64:<%s;%x;%15d;%015u;%X>'#10, [ui64, ui64, ui64, ui64, ui64]);
2165 *)