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 a_modes.inc}
18 interface
20 uses
24 // ////////////////////////////////////////////////////////////////////////// //
25 type
36 '.dfzip'
37 );
40 // ////////////////////////////////////////////////////////////////////////// //
41 type
43 public
48 private
51 public
54 public
63 // process one byte, return `true` if codepoint is ready
69 // ////////////////////////////////////////////////////////////////////////// //
74 // rewrites slashes to '/'
80 // strips out name from `fn`, leaving trailing slash
83 // ends with '/' or '\'?
86 // strips extra trailing slashes in `path, and extra leading slashes in `fn`
87 // will add slash to `path`, even if `fn` is empty!
90 // does filename have one of ".wad", ".pk3", ".zip" extensions?
93 // does filepath have ".XXX:\" in it?
96 // adds ".wad" extension if filename doesn't have one of ".wad", ".pk3", ".zip"
99 // check wad signature
102 // convert number to strig with nice commas
112 // `true` if strings are equal; ignoring case for cp1251
119 // findFileCI takes case-insensitive path, traverses it, and rewrites it to
120 // a case-sensetive one (using real on-disk names). return value means 'success'.
121 // if some dir or file wasn't found, pathname is undefined (destroyed, but not
122 // necessarily cleared).
123 // last name assumed to be a file, not directory (unless `lastIsDir` flag is set).
126 // findDiskWad tries to find the wad file using common wad extensions
127 // (see `wadExtensions` array).
128 // returns real on-disk filename, or empty string.
129 // original wad extension is used as a hint for the first try.
130 // also, this automatically performs `findFileCI()`.
132 // slashes must be normalized!
135 // they throws
138 // create file if necessary, but don't truncate the existing one
141 // little endian
169 // big endian
199 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
201 {$ENDIF}
213 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
215 {$ENDIF}
226 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
228 {$ENDIF}
230 type
233 // returns formatted string if `writerCB` is `nil`, empty string otherwise
234 function formatstrf (const fmt: AnsiString; const args: array of const; writerCB: TFormatStrFCallback=nil): AnsiString;
241 // returns string in single or double quotes
242 // single quotes supports only pascal-style '' for single quote char
243 // double quotes supports c-style escapes
244 // function will select quote mode automatically
248 type
250 private
251 //type PItemT = ^ItemT;
254 public
255 type
257 private
261 public
268 private
272 private
279 public
283 //WARNING! don't change list contents in `for ... in`!
293 public
305 type
310 // not changed by info getter; used in other parts of the code
320 implementation
322 uses
323 xstreams;
325 // ////////////////////////////////////////////////////////////////////////// //
327 begin
332 begin
337 begin
342 // ////////////////////////////////////////////////////////////////////////// //
343 // rewrites slashes to '/'
345 {$IFDEF WINDOWS}
346 var
348 {$ENDIF}
349 begin
351 {$IFDEF WINDOWS}
353 {$ENDIF}
358 begin
361 {$IFDEF WINDOWS}
363 if (length(s) > 2) and (s[2] = ':') and ((s[3] = '/') or (s[3] = '\')) then begin result := true; exit; end;
364 {$ELSE}
366 {$ENDIF}
371 begin
374 {$IFDEF WINDOWS}
376 if (length(s) = 3) and (s[2] = ':') and ((s[3] = '/') or (s[3] = '\')) then begin result := true; exit; end;
377 {$ELSE}
379 {$ENDIF}
383 // ////////////////////////////////////////////////////////////////////////// //
385 begin
392 begin
398 begin
403 // ////////////////////////////////////////////////////////////////////////// //
405 begin
413 begin
420 begin
426 begin
433 begin
440 begin
446 begin
453 begin
459 begin
465 var
467 begin
469 begin
479 var
481 begin
483 begin
490 var
492 begin
494 begin
497 end
498 else
499 begin
505 // ////////////////////////////////////////////////////////////////////////// //
506 var
511 // ////////////////////////////////////////////////////////////////////////// //
512 const
514 $0402,$0403,$201A,$0453,$201E,$2026,$2020,$2021,$20AC,$2030,$0409,$2039,$040A,$040C,$040B,$040F,
515 $0452,$2018,$2019,$201C,$201D,$2022,$2013,$2014,$FFFD,$2122,$0459,$203A,$045A,$045C,$045B,$045F,
516 $00A0,$040E,$045E,$0408,$00A4,$0490,$00A6,$00A7,$0401,$00A9,$0404,$00AB,$00AC,$00AD,$00AE,$0407,
517 $00B0,$00B1,$0406,$0456,$0491,$00B5,$00B6,$00B7,$0451,$2116,$0454,$00BB,$0458,$0405,$0455,$0457,
518 $0410,$0411,$0412,$0413,$0414,$0415,$0416,$0417,$0418,$0419,$041A,$041B,$041C,$041D,$041E,$041F,
519 $0420,$0421,$0422,$0423,$0424,$0425,$0426,$0427,$0428,$0429,$042A,$042B,$042C,$042D,$042E,$042F,
520 $0430,$0431,$0432,$0433,$0434,$0435,$0436,$0437,$0438,$0439,$043A,$043B,$043C,$043D,$043E,$043F,
522 );
526 var
528 begin
536 // ////////////////////////////////////////////////////////////////////////// //
537 // fast state-machine based UTF-8 decoder; using 8 bytes of memory
538 // code points from invalid range will never be valid, this is the property of the state machine
539 const
540 // see http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
542 // maps bytes to character classes
559 // maps a combination of a state of the automaton and a character class to a state
569 // ////////////////////////////////////////////////////////////////////////// //
570 constructor TUtf8DecoderFast.Create (v: Boolean{fuck you, fpc}); begin state := Accept; codepoint := 0; end;
576 function TUtf8DecoderFast.completeOrInvalid (): Boolean; inline; begin result := (state = Accept) or (state = Reject); end;
578 function TUtf8DecoderFast.decode (c: AnsiChar): Boolean; inline; overload; begin result := decode(Byte(c)); end;
581 var
583 begin
586 if (state <> Accept) then codepoint := (b and $3f) or (codepoint shl 6) else codepoint := ($ff shr tp) and b;
593 // ////////////////////////////////////////////////////////////////////////// //
595 begin
601 // ////////////////////////////////////////////////////////////////////////// //
603 var
606 begin
608 begin
610 begin
614 begin
617 exit;
625 var
629 begin
632 begin
634 end
636 begin
639 end
641 begin
645 end
647 begin
652 end
653 else
654 begin
659 begin
661 begin
663 begin
666 begin
668 begin
670 end
671 else
672 begin
676 exit;
683 // ////////////////////////////////////////////////////////////////////////// //
685 begin
690 begin
693 end
694 else
695 begin
704 // ////////////////////////////////////////////////////////////////////////// //
708 var
710 begin
713 begin
721 var
724 begin
727 begin
735 begin
738 end
740 begin
743 end
744 else
745 begin
752 var
755 begin
757 begin
765 // ////////////////////////////////////////////////////////////////////////// //
767 var
770 begin
773 begin
776 begin
778 exit;
788 var
791 begin
796 begin
808 var
811 begin
815 begin
818 begin
820 begin
822 end
823 else
824 begin
827 exit;
834 begin
841 // strips out name from `fn`, leaving trailing slash
843 var
846 begin
851 begin
860 // ends with '/' or '\'?
862 begin
864 begin
866 end
867 else
868 begin
874 // strips extra trailing slashes in `path, and extra leading slashes in `fn`
875 // will add slash to `path`, even if `fn` is empty!
877 var
879 begin
883 if (Length(result) > 0) and ((result[Length(result)] <> '/') and (result[Length(result)] <> '\')) then result += '/';
885 begin
887 //FIXME: make this faster!
888 while (Length(result) > 0) and ((result[Length(result)] = '/') or (result[Length(result)] = '\')) do
889 begin
898 var
900 begin
905 //result := StrEquCI1251(ext, '.wad') or StrEquCI1251(ext, '.pk3') or StrEquCI1251(ext, '.zip') or StrEquCI1251(ext, '.dfz');
910 begin
918 begin
920 Result :=
921 (* ZIP *)
924 (* PACK *)
927 (* DFWAD *)
928 ((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))
933 var
936 begin
940 begin
942 begin
945 begin
948 begin
950 begin
952 exit;
963 var
965 begin
969 begin
976 begin
978 begin
980 end
981 else
982 begin
984 begin
986 end
987 else
988 begin
1000 begin
1002 begin
1004 end
1005 else
1006 begin
1008 begin
1010 end
1011 else
1012 begin
1023 begin
1028 begin
1034 var
1036 begin
1045 var
1048 begin
1050 begin
1052 begin
1056 exit;
1059 // nothing to do
1064 // ////////////////////////////////////////////////////////////////////////// //
1065 // utils
1066 // `ch`: utf8 start
1067 // -1: invalid utf8
1069 begin
1081 var
1083 begin
1087 begin
1093 // check other sequence bytes
1095 begin
1105 // ////////////////////////////////////////////////////////////////////////// //
1106 const
1108 $0402,$0403,$201A,$0453,$201E,$2026,$2020,$2021,$20AC,$2030,$0409,$2039,$040A,$040C,$040B,$040F,
1109 $0452,$2018,$2019,$201C,$201D,$2022,$2013,$2014,$003F,$2122,$0459,$203A,$045A,$045C,$045B,$045F,
1110 $00A0,$040E,$045E,$0408,$00A4,$0490,$00A6,$00A7,$0401,$00A9,$0404,$00AB,$00AC,$00AD,$00AE,$0407,
1111 $00B0,$00B1,$0406,$0456,$0491,$00B5,$00B6,$00B7,$0451,$2116,$0454,$00BB,$0458,$0405,$0455,$0457,
1112 $0410,$0411,$0412,$0413,$0414,$0415,$0416,$0417,$0418,$0419,$041A,$041B,$041C,$041D,$041E,$041F,
1113 $0420,$0421,$0422,$0423,$0424,$0425,$0426,$0427,$0428,$0429,$042A,$042B,$042C,$042D,$042E,$042F,
1114 $0430,$0431,$0432,$0433,$0434,$0435,$0436,$0437,$0438,$0439,$043A,$043B,$043C,$043D,$043E,$043F,
1115 $0440,$0441,$0442,$0443,$0444,$0445,$0446,$0447,$0448,$0449,$044A,$044B,$044C,$044D,$044E,$044F
1116 );
1120 var
1122 begin
1123 (* The following encodings are valid, except for the 5 and 6 byte
1124 * combinations:
1125 * 0xxxxxxx
1126 * 110xxxxx 10xxxxxx
1127 * 1110xxxx 10xxxxxx 10xxxxxx
1128 * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
1129 * 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
1130 * 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
1131 *)
1139 // mask out unused bits
1147 // now continue
1149 begin
1157 // done, try 1251
1159 // alas
1164 var
1166 begin
1170 begin
1181 // ////////////////////////////////////////////////////////////////////////// //
1182 // findFileCI eats case-insensitive path, traverses it and rewrites it to a
1183 // case-sensetive. result value means success.
1184 // if file/dir not founded than pathname is in undefined state!
1186 var
1194 begin
1199 begin
1200 // remove trailing slashes
1203 // extract name
1206 begin
1210 // remove trailing slashes again
1213 //writeln(Format('npt=[%s]; newname=[%s]; curname=[%s]; wantdir=%d', [npt, newname, curname, Integer(wantdir)]));
1214 // try the easiest case first
1217 begin
1219 begin
1220 // i found her!
1223 continue;
1226 //writeln(Format('npt=[%s]; newname=[%s]; curname=[%s]; wantdir=%d', [npt, newname, curname, Integer(wantdir)]));
1227 // alas, either not found, or invalid attributes
1229 try
1231 repeat
1233 begin
1234 // i found her!
1238 break;
1241 finally
1251 var
1254 begin
1257 // check first ext
1260 for newExt in wadExtensions do if (StrEquCI1251(ext, newExt)) then begin found := true; break; end;
1262 // check second ext
1265 for newExt in wadExtensions do if (StrEquCI1251(ext, newExt)) then begin found := true; break; end;
1273 var
1276 begin
1278 //writeln('findDiskWad00: fname=<', fname, '>');
1282 //writeln(' findDiskWad01: fname=<', fname, '>; origExt=<', origExt, '>');
1284 begin
1285 //writeln(' findDiskWad02: fname=<', fname, '>; origExt=<', origExt, '>; newExt=<', newExt, '>');
1287 begin
1288 //writeln(' SKIP');
1289 continue;
1299 begin
1300 if not findFileCI(pathname) then raise EFileNotFoundException.Create('can''t open file "'+pathname+'"');
1305 var
1307 begin
1310 begin
1311 if not findFileCI(path, true) then raise Exception.Create('can''t create file "'+pathname+'"');
1318 var
1321 begin
1322 //writeln('*** TRYING R/W FILE "', pathname, '"');
1325 begin
1326 if not findFileCI(path, true) then raise Exception.Create('can''t create file "'+pathname+'"');
1330 begin
1331 //writeln('*** found old file "', oldname, '"');
1333 end
1334 else
1335 begin
1342 {$IFDEF ENDIAN_LITTLE}
1343 begin
1346 {$ELSE}
1347 var
1349 begin
1352 begin
1358 {$ENDIF}
1361 {$IFDEF ENDIAN_LITTLE}
1362 var
1364 begin
1367 begin
1373 {$ELSE}
1374 begin
1377 {$ENDIF}
1380 begin
1385 var
1388 begin
1391 begin
1393 begin
1396 end
1397 else
1398 begin
1400 begin
1419 procedure writeIntBE (st: TStream; v: ShortInt); overload; begin writeIntegerBE(st, @v, 1); end;
1421 procedure writeIntBE (st: TStream; v: SmallInt); overload; begin writeIntegerBE(st, @v, 2); end;
1422 procedure writeIntBE (st: TStream; v: LongWord); overload; begin writeIntegerBE(st, @v, 4); end;
1423 procedure writeIntBE (st: TStream; v: LongInt); overload; begin writeIntegerBE(st, @v, 4); end;
1432 begin
1434 if (maxlen <= 65535) then writeInt(st, Word(Length(str))) else writeInt(st, LongWord(Length(str)));
1439 var
1441 begin
1446 begin
1454 {$IFDEF ENDIAN_LITTLE}
1455 begin
1458 {$ELSE}
1459 var
1461 begin
1464 begin
1470 {$ENDIF}
1473 {$IFDEF ENDIAN_LITTLE}
1474 var
1476 begin
1479 begin
1485 {$ELSE}
1486 begin
1489 {$ENDIF}
1510 // ////////////////////////////////////////////////////////////////////////// //
1511 function nmin (a, b: Byte): Byte; inline; overload; begin if (a < b) then result := a else result := b; end;
1512 function nmin (a, b: ShortInt): ShortInt; inline; overload; begin if (a < b) then result := a else result := b; end;
1513 function nmin (a, b: Word): Word; inline; overload; begin if (a < b) then result := a else result := b; end;
1514 function nmin (a, b: SmallInt): SmallInt; inline; overload; begin if (a < b) then result := a else result := b; end;
1515 function nmin (a, b: LongWord): LongWord; inline; overload; begin if (a < b) then result := a else result := b; end;
1516 function nmin (a, b: LongInt): LongInt; inline; overload; begin if (a < b) then result := a else result := b; end;
1517 function nmin (a, b: Int64): Int64; inline; overload; begin if (a < b) then result := a else result := b; end;
1518 function nmin (a, b: UInt64): UInt64; inline; overload; begin if (a < b) then result := a else result := b; end;
1519 function nmin (a, b: Single): Single; inline; overload; begin if (a < b) then result := a else result := b; end;
1520 function nmin (a, b: Double): Double; inline; overload; begin if (a < b) then result := a else result := b; end;
1521 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
1522 function nmin (a, b: Extended): Extended; inline; overload; begin if (a < b) then result := a else result := b; end;
1523 {$ENDIF}
1525 function nmax (a, b: Byte): Byte; inline; overload; begin if (a > b) then result := a else result := b; end;
1526 function nmax (a, b: ShortInt): ShortInt; inline; overload; begin if (a > b) then result := a else result := b; end;
1527 function nmax (a, b: Word): Word; inline; overload; begin if (a > b) then result := a else result := b; end;
1528 function nmax (a, b: SmallInt): SmallInt; inline; overload; begin if (a > b) then result := a else result := b; end;
1529 function nmax (a, b: LongWord): LongWord; inline; overload; begin if (a > b) then result := a else result := b; end;
1530 function nmax (a, b: LongInt): LongInt; inline; overload; begin if (a > b) then result := a else result := b; end;
1531 function nmax (a, b: Int64): Int64; inline; overload; begin if (a > b) then result := a else result := b; end;
1532 function nmax (a, b: UInt64): UInt64; inline; overload; begin if (a > b) then result := a else result := b; end;
1533 function nmax (a, b: Single): Single; inline; overload; begin if (a > b) then result := a else result := b; end;
1534 function nmax (a, b: Double): Double; inline; overload; begin if (a > b) then result := a else result := b; end;
1535 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
1536 function nmax (a, b: Extended): Extended; inline; overload; begin if (a > b) then result := a else result := b; end;
1537 {$ENDIF}
1539 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;
1540 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;
1541 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;
1542 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;
1543 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;
1544 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;
1545 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;
1546 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;
1547 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;
1548 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;
1549 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
1550 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;
1551 {$ENDIF}
1553 // ////////////////////////////////////////////////////////////////////////// //
1554 {$IFDEF WINDOWS}
1555 function snprintf (buf: PAnsiChar; bufsize: SizeUInt; const fmt: PAnsiChar): SizeUInt; cdecl; varargs; external 'msvcrt.dll' name '_snprintf';
1556 {$ELSE}
1557 function snprintf (buf: PAnsiChar; bufsize: SizeUInt; const fmt: PAnsiChar): SizeUInt; cdecl; varargs; external 'libc' name 'snprintf';
1558 {$ENDIF}
1561 (*
1562 procedure conwriter (constref buf; len: SizeUInt);
1563 var
1564 ss: ShortString;
1565 slen: Integer;
1566 b: PByte;
1567 begin
1568 if (len < 1) then exit;
1569 b := PByte(@buf);
1570 while (len > 0) do
1571 begin
1572 if (len > 255) then slen := 255 else slen := Integer(len);
1573 Move(b^, ss[1], len);
1574 ss[0] := AnsiChar(slen);
1575 write(ss);
1576 b += slen;
1577 len -= slen;
1578 end;
1579 end;
1580 *)
1583 function formatstrf (const fmt: AnsiString; const args: array of const; writerCB: TFormatStrFCallback=nil): AnsiString;
1584 const
1586 PadZeroes: AnsiString = '00000000000000000000000000000000000000000000000000000000000000000000000';
1587 var
1603 var
1607 begin
1611 begin
1613 end
1614 else
1615 begin
1617 begin
1629 begin
1634 begin
1640 var
1642 begin
1648 var
1650 begin
1656 begin
1660 begin
1670 begin
1675 var
1678 begin
1680 begin
1684 end
1685 else
1686 begin
1691 repeat
1693 begin
1697 end
1698 else
1699 begin
1701 begin
1704 end
1716 var
1718 begin
1721 repeat
1723 begin
1727 end
1728 else
1729 begin
1731 begin
1734 end
1744 var
1746 begin
1748 begin
1756 var
1758 begin
1760 begin
1767 begin
1771 begin
1772 // print literal part
1775 // output literal part
1777 begin
1779 begin
1781 break;
1785 begin
1789 end
1790 else
1791 begin
1795 continue;
1797 // check if we have argument for this format string
1799 begin
1802 break;
1804 // skip percent
1808 // parse format; check for sign
1812 // parse width
1815 begin
1816 if (fmt[spos] < '0') or (fmt[spos] > '9') then begin xwrite('<INVALID FORMAT>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
1820 begin
1826 end
1827 else
1828 begin
1832 // parse precision
1835 begin
1838 if (fmt[spos] < '0') or (fmt[spos] > '9') then begin xwrite('<INVALID FORMAT>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
1841 begin
1848 // get format char
1852 // done parsing format, check for valid format chars
1853 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;
1854 // now write formatted string
1857 begin
1858 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;
1867 begin
1869 if args[curarg].VBoolean then strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], 'true')
1874 begin
1876 if args[curarg].VBoolean then strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], AnsiChar('t'))
1881 begin
1886 else
1887 begin
1890 break;
1896 begin
1903 begin
1908 else
1909 begin
1912 break;
1915 //vtWideChar: begin end; // args[curarg].VWideChar (WideChar)
1919 begin
1926 begin
1932 begin
1934 strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], Integer(trunc(args[curarg].VExtended^)));
1938 begin
1940 strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], LongWord(trunc(args[curarg].VExtended^)));
1943 else
1944 begin
1947 break;
1951 begin
1959 begin
1968 begin
1976 else
1977 begin
1980 break;
1985 begin
1989 end
1990 else
1991 begin
1999 begin
2000 if (args[curarg].VObject <> nil) then ccname := args[curarg].VObject.Classname else ccname := '<nil>';
2006 begin
2007 if (args[curarg].VClass <> nil) then ccname := args[curarg].VClass.Classname else ccname := '<nil>';
2012 //vtPWideChar: begin end; // args[curarg].VPWideChar (PWideChar)
2014 begin
2019 //vtCurrency: begin end; // args[curarg].VCurrency (PCurrency)
2020 //vtVariant: begin end; // args[curarg].VVariant^ (PVariant)
2021 //vtInterface: begin end; // args[curarg].VInterface (Pointer);
2022 //vtWideString: begin end; // args[curarg].VWideString (Pointer);
2024 begin
2029 else begin xwrite('<INVALID FORMAT CHAR>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
2034 begin
2036 begin
2038 begin
2043 end
2044 else
2045 begin
2048 end
2049 else
2050 begin
2058 begin
2063 else begin xwrite('<INVALID FORMAT CHAR>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
2067 if (sign <> '-') then begin if zeropad then indent0(width-pclen) else indent(width-pclen); end;
2071 else
2072 begin
2075 break;
2084 var
2088 begin
2092 // get age
2095 // get size
2101 // fill info
2109 (*
2110 var
2111 ss: ShortString;
2112 ls: AnsiString;
2113 i64: Int64 = -$A000000000;
2114 ui64: UInt64 = $A000000000;
2115 begin
2116 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']);
2117 writef(conwriter, 'test float:<%s;%u;%f;%g>'#10, [666.6942, 666.6942, 666.6942, 666.6942]);
2118 ss := 'fuckit';
2119 ls := 'FUCKIT';
2120 writef(conwriter, 'test ss:<%5s;%040s>'#10, [ss, ss]);
2121 writef(conwriter, 'test ls:<%5s;%040s>'#10, [ls, ls]);
2122 writef(conwriter, 'test pointer:<%s;%x;%p>'#10, [@ss, @ss, @ss]);
2123 writef(conwriter, 'test i64:<%s;%x;%015d;%u;%X>'#10, [i64, i64, i64, i64, i64]);
2124 writef(conwriter, 'test ui64:<%s;%x;%15d;%015u;%X>'#10, [ui64, ui64, ui64, ui64, ui64]);
2125 *)