867f0dcbef1befffb33b7d3a66cc40b57eeb360e
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
29 // ////////////////////////////////////////////////////////////////////////// //
30 type
32 public
37 private
40 public
43 public
52 // process one byte, return `true` if codepoint is ready
58 // ////////////////////////////////////////////////////////////////////////// //
63 // strips out name from `fn`, leaving trailing slash
66 // ends with '/' or '\'?
69 // strips extra trailing slashes in `path, and extra leading slashes in `fn`
70 // will add slash to `path`, even if `fn` is empty!
73 // does filename have one of ".wad", ".pk3", ".zip" extensions?
76 // does filepath have ".XXX:\" in it?
79 // adds ".wad" extension if filename doesn't have one of ".wad", ".pk3", ".zip"
82 // check wad signature
85 // convert number to strig with nice commas
93 // `true` if strings are equal; ignoring case for cp1251
100 // `pathname` will be modified if path is valid
101 // `lastIsDir` should be `true` if we are searching for directory
102 // nobody cares about shitdoze, so i'll use the same code path for it
105 // return fixed AnsiString or empty AnsiString
107 // slashes must be normalized!
110 // they throws
113 // creates file if necessary
116 // little endian
144 // big endian
174 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
176 {$ENDIF}
188 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
190 {$ENDIF}
201 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
203 {$ENDIF}
205 type
208 // returns formatted string if `writerCB` is `nil`, empty string otherwise
209 function formatstrf (const fmt: AnsiString; const args: array of const; writerCB: TFormatStrFCallback=nil): AnsiString;
216 // returns string in single or double quotes
217 // single quotes supports only pascal-style '' for single quote char
218 // double quotes supports c-style escapes
219 // function will select quote mode automatically
223 type
225 private
226 //type PItemT = ^ItemT;
229 public
230 type
232 private
236 public
243 private
247 private
254 public
258 //WARNING! don't change list contents in `for ... in`!
268 public
280 type
285 // not changed by info getter; used in other parts of the code
295 implementation
297 uses
298 xstreams;
307 '.dfzip'
308 );
311 // ////////////////////////////////////////////////////////////////////////// //
313 begin
318 begin
323 begin
328 // ////////////////////////////////////////////////////////////////////////// //
330 begin
337 begin
343 begin
348 // ////////////////////////////////////////////////////////////////////////// //
350 begin
358 begin
365 begin
371 begin
378 begin
385 begin
391 begin
398 begin
404 begin
410 var
412 begin
414 begin
424 var
426 begin
428 begin
435 var
437 begin
439 begin
442 end
443 else
444 begin
450 // ////////////////////////////////////////////////////////////////////////// //
451 var
456 // ////////////////////////////////////////////////////////////////////////// //
457 const
459 $0402,$0403,$201A,$0453,$201E,$2026,$2020,$2021,$20AC,$2030,$0409,$2039,$040A,$040C,$040B,$040F,
460 $0452,$2018,$2019,$201C,$201D,$2022,$2013,$2014,$003F,$2122,$0459,$203A,$045A,$045C,$045B,$045F,
461 $00A0,$040E,$045E,$0408,$00A4,$0490,$00A6,$00A7,$0401,$00A9,$0404,$00AB,$00AC,$00AD,$00AE,$0407,
462 $00B0,$00B1,$0406,$0456,$0491,$00B5,$00B6,$00B7,$0451,$2116,$0454,$00BB,$0458,$0405,$0455,$0457,
463 $0410,$0411,$0412,$0413,$0414,$0415,$0416,$0417,$0418,$0419,$041A,$041B,$041C,$041D,$041E,$041F,
464 $0420,$0421,$0422,$0423,$0424,$0425,$0426,$0427,$0428,$0429,$042A,$042B,$042C,$042D,$042E,$042F,
465 $0430,$0431,$0432,$0433,$0434,$0435,$0436,$0437,$0438,$0439,$043A,$043B,$043C,$043D,$043E,$043F,
467 );
471 var
473 begin
481 // ////////////////////////////////////////////////////////////////////////// //
482 // fast state-machine based UTF-8 decoder; using 8 bytes of memory
483 // code points from invalid range will never be valid, this is the property of the state machine
484 const
485 // see http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
487 // maps bytes to character classes
504 // maps a combination of a state of the automaton and a character class to a state
514 // ////////////////////////////////////////////////////////////////////////// //
515 constructor TUtf8DecoderFast.Create (v: Boolean{fuck you, fpc}); begin state := Accept; codepoint := 0; end;
521 function TUtf8DecoderFast.completeOrInvalid (): Boolean; inline; begin result := (state = Accept) or (state = Reject); end;
523 function TUtf8DecoderFast.decode (c: AnsiChar): Boolean; inline; overload; begin result := decode(Byte(c)); end;
526 var
528 begin
531 if (state <> Accept) then codepoint := (b and $3f) or (codepoint shl 6) else codepoint := ($ff shr tp) and b;
538 // ////////////////////////////////////////////////////////////////////////// //
540 begin
546 // ////////////////////////////////////////////////////////////////////////// //
548 var
551 begin
553 begin
555 begin
559 begin
562 exit;
570 var
574 begin
577 begin
579 end
581 begin
584 end
586 begin
590 end
592 begin
597 end
598 else
599 begin
604 begin
606 begin
608 begin
611 begin
613 begin
615 end
616 else
617 begin
621 exit;
628 // ////////////////////////////////////////////////////////////////////////// //
630 begin
635 begin
638 end
639 else
640 begin
649 // ////////////////////////////////////////////////////////////////////////// //
653 var
655 begin
658 begin
666 var
669 begin
672 begin
680 begin
683 end
685 begin
688 end
689 else
690 begin
697 var
700 begin
702 begin
710 // ////////////////////////////////////////////////////////////////////////// //
712 var
715 begin
718 begin
721 begin
723 exit;
733 var
736 begin
741 begin
753 var
756 begin
760 begin
763 begin
765 begin
767 end
768 else
769 begin
772 exit;
779 begin
786 // strips out name from `fn`, leaving trailing slash
788 var
791 begin
796 begin
805 // ends with '/' or '\'?
807 begin
809 begin
811 end
812 else
813 begin
819 // strips extra trailing slashes in `path, and extra leading slashes in `fn`
820 // will add slash to `path`, even if `fn` is empty!
822 var
824 begin
828 if (Length(result) > 0) and ((result[Length(result)] <> '/') and (result[Length(result)] <> '\')) then result += '/';
830 begin
832 //FIXME: make this faster!
833 while (Length(result) > 0) and ((result[Length(result)] = '/') or (result[Length(result)] = '\')) do
834 begin
843 var
845 begin
850 //result := StrEquCI1251(ext, '.wad') or StrEquCI1251(ext, '.pk3') or StrEquCI1251(ext, '.zip') or StrEquCI1251(ext, '.dfz');
855 begin
863 begin
865 Result :=
866 (* ZIP *)
869 (* PACK *)
872 (* DFWAD *)
873 ((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))
878 var
881 begin
885 begin
887 begin
890 begin
892 if StrEquCI1251(s, '.wad') or StrEquCI1251(s, '.pk3') or StrEquCI1251(s, '.zip') or StrEquCI1251(s, '.dfz') then
893 begin
895 exit;
905 var
907 begin
911 begin
918 begin
920 begin
922 end
923 else
924 begin
926 begin
928 end
929 else
930 begin
942 begin
944 begin
946 end
947 else
948 begin
950 begin
952 end
953 else
954 begin
966 var
968 begin
977 var
980 begin
982 begin
984 begin
988 exit;
991 // nothing to do
996 // ////////////////////////////////////////////////////////////////////////// //
997 // utils
998 // `ch`: utf8 start
999 // -1: invalid utf8
1001 begin
1013 var
1015 begin
1019 begin
1025 // check other sequence bytes
1027 begin
1037 // ////////////////////////////////////////////////////////////////////////// //
1038 const
1040 $0402,$0403,$201A,$0453,$201E,$2026,$2020,$2021,$20AC,$2030,$0409,$2039,$040A,$040C,$040B,$040F,
1041 $0452,$2018,$2019,$201C,$201D,$2022,$2013,$2014,$003F,$2122,$0459,$203A,$045A,$045C,$045B,$045F,
1042 $00A0,$040E,$045E,$0408,$00A4,$0490,$00A6,$00A7,$0401,$00A9,$0404,$00AB,$00AC,$00AD,$00AE,$0407,
1043 $00B0,$00B1,$0406,$0456,$0491,$00B5,$00B6,$00B7,$0451,$2116,$0454,$00BB,$0458,$0405,$0455,$0457,
1044 $0410,$0411,$0412,$0413,$0414,$0415,$0416,$0417,$0418,$0419,$041A,$041B,$041C,$041D,$041E,$041F,
1045 $0420,$0421,$0422,$0423,$0424,$0425,$0426,$0427,$0428,$0429,$042A,$042B,$042C,$042D,$042E,$042F,
1046 $0430,$0431,$0432,$0433,$0434,$0435,$0436,$0437,$0438,$0439,$043A,$043B,$043C,$043D,$043E,$043F,
1047 $0440,$0441,$0442,$0443,$0444,$0445,$0446,$0447,$0448,$0449,$044A,$044B,$044C,$044D,$044E,$044F
1048 );
1052 var
1054 begin
1055 (* The following encodings are valid, except for the 5 and 6 byte
1056 * combinations:
1057 * 0xxxxxxx
1058 * 110xxxxx 10xxxxxx
1059 * 1110xxxx 10xxxxxx 10xxxxxx
1060 * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
1061 * 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
1062 * 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
1063 *)
1071 // mask out unused bits
1079 // now continue
1081 begin
1089 // done, try 1251
1091 // alas
1096 var
1098 begin
1102 begin
1113 // ////////////////////////////////////////////////////////////////////////// //
1114 // `pathname` will be modified if path is valid
1115 // `lastIsDir` should be `true` if we are searching for directory
1116 // nobody cares about shitdoze, so i'll use the same code path for it
1118 var
1126 begin
1131 begin
1132 // remove trailing slashes
1135 // extract name
1138 begin
1142 // remove trailing slashes again
1145 //writeln(Format('npt=[%s]; newname=[%s]; curname=[%s]; wantdir=%d', [npt, newname, curname, Integer(wantdir)]));
1146 // try the easiest case first
1149 begin
1151 begin
1152 // i found her!
1155 continue;
1158 //writeln(Format('npt=[%s]; newname=[%s]; curname=[%s]; wantdir=%d', [npt, newname, curname, Integer(wantdir)]));
1159 // alas, either not found, or invalid attributes
1161 try
1163 repeat
1165 begin
1166 // i found her!
1170 break;
1173 finally
1183 var
1186 begin
1189 // check first ext
1192 for newExt in wadExtensions do if (StrEquCI1251(ext, newExt)) then begin found := true; break; end;
1194 // check second ext
1197 for newExt in wadExtensions do if (StrEquCI1251(ext, newExt)) then begin found := true; break; end;
1205 var
1208 begin
1210 //writeln('findDiskWad00: fname=<', fname, '>');
1214 //writeln(' findDiskWad01: fname=<', fname, '>; origExt=<', origExt, '>');
1216 begin
1217 //writeln(' findDiskWad02: fname=<', fname, '>; origExt=<', origExt, '>; newExt=<', newExt, '>');
1219 begin
1220 //writeln(' SKIP');
1221 continue;
1231 begin
1232 if not findFileCI(pathname) then raise EFileNotFoundException.Create('can''t open file "'+pathname+'"');
1237 var
1239 begin
1242 begin
1243 if not findFileCI(path, true) then raise Exception.Create('can''t create file "'+pathname+'"');
1250 var
1253 begin
1254 //writeln('*** TRYING R/W FILE "', pathname, '"');
1257 begin
1258 if not findFileCI(path, true) then raise Exception.Create('can''t create file "'+pathname+'"');
1262 begin
1263 //writeln('*** found old file "', oldname, '"');
1265 end
1266 else
1267 begin
1274 {$IFDEF ENDIAN_LITTLE}
1275 begin
1278 {$ELSE}
1279 var
1281 begin
1284 begin
1290 {$ENDIF}
1293 {$IFDEF ENDIAN_LITTLE}
1294 var
1296 begin
1299 begin
1305 {$ELSE}
1306 begin
1309 {$ENDIF}
1312 begin
1317 var
1320 begin
1323 begin
1325 begin
1328 end
1329 else
1330 begin
1332 begin
1351 procedure writeIntBE (st: TStream; v: ShortInt); overload; begin writeIntegerBE(st, @v, 1); end;
1353 procedure writeIntBE (st: TStream; v: SmallInt); overload; begin writeIntegerBE(st, @v, 2); end;
1354 procedure writeIntBE (st: TStream; v: LongWord); overload; begin writeIntegerBE(st, @v, 4); end;
1355 procedure writeIntBE (st: TStream; v: LongInt); overload; begin writeIntegerBE(st, @v, 4); end;
1364 begin
1366 if (maxlen <= 65535) then writeInt(st, Word(Length(str))) else writeInt(st, LongWord(Length(str)));
1371 var
1373 begin
1378 begin
1386 {$IFDEF ENDIAN_LITTLE}
1387 begin
1390 {$ELSE}
1391 var
1393 begin
1396 begin
1402 {$ENDIF}
1405 {$IFDEF ENDIAN_LITTLE}
1406 var
1408 begin
1411 begin
1417 {$ELSE}
1418 begin
1421 {$ENDIF}
1442 // ////////////////////////////////////////////////////////////////////////// //
1443 function nmin (a, b: Byte): Byte; inline; overload; begin if (a < b) then result := a else result := b; end;
1444 function nmin (a, b: ShortInt): ShortInt; inline; overload; begin if (a < b) then result := a else result := b; end;
1445 function nmin (a, b: Word): Word; inline; overload; begin if (a < b) then result := a else result := b; end;
1446 function nmin (a, b: SmallInt): SmallInt; inline; overload; begin if (a < b) then result := a else result := b; end;
1447 function nmin (a, b: LongWord): LongWord; inline; overload; begin if (a < b) then result := a else result := b; end;
1448 function nmin (a, b: LongInt): LongInt; inline; overload; begin if (a < b) then result := a else result := b; end;
1449 function nmin (a, b: Int64): Int64; inline; overload; begin if (a < b) then result := a else result := b; end;
1450 function nmin (a, b: UInt64): UInt64; inline; overload; begin if (a < b) then result := a else result := b; end;
1451 function nmin (a, b: Single): Single; inline; overload; begin if (a < b) then result := a else result := b; end;
1452 function nmin (a, b: Double): Double; inline; overload; begin if (a < b) then result := a else result := b; end;
1453 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
1454 function nmin (a, b: Extended): Extended; inline; overload; begin if (a < b) then result := a else result := b; end;
1455 {$ENDIF}
1457 function nmax (a, b: Byte): Byte; inline; overload; begin if (a > b) then result := a else result := b; end;
1458 function nmax (a, b: ShortInt): ShortInt; inline; overload; begin if (a > b) then result := a else result := b; end;
1459 function nmax (a, b: Word): Word; inline; overload; begin if (a > b) then result := a else result := b; end;
1460 function nmax (a, b: SmallInt): SmallInt; inline; overload; begin if (a > b) then result := a else result := b; end;
1461 function nmax (a, b: LongWord): LongWord; inline; overload; begin if (a > b) then result := a else result := b; end;
1462 function nmax (a, b: LongInt): LongInt; inline; overload; begin if (a > b) then result := a else result := b; end;
1463 function nmax (a, b: Int64): Int64; inline; overload; begin if (a > b) then result := a else result := b; end;
1464 function nmax (a, b: UInt64): UInt64; inline; overload; begin if (a > b) then result := a else result := b; end;
1465 function nmax (a, b: Single): Single; inline; overload; begin if (a > b) then result := a else result := b; end;
1466 function nmax (a, b: Double): Double; inline; overload; begin if (a > b) then result := a else result := b; end;
1467 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
1468 function nmax (a, b: Extended): Extended; inline; overload; begin if (a > b) then result := a else result := b; end;
1469 {$ENDIF}
1471 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;
1472 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;
1473 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;
1474 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;
1475 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;
1476 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;
1477 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;
1478 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;
1479 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;
1480 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;
1481 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
1482 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;
1483 {$ENDIF}
1485 // ////////////////////////////////////////////////////////////////////////// //
1486 {$IFDEF WINDOWS}
1487 function snprintf (buf: PAnsiChar; bufsize: SizeUInt; const fmt: PAnsiChar): SizeUInt; cdecl; varargs; external 'msvcrt.dll' name '_snprintf';
1488 {$ELSE}
1489 function snprintf (buf: PAnsiChar; bufsize: SizeUInt; const fmt: PAnsiChar): SizeUInt; cdecl; varargs; external 'libc' name 'snprintf';
1490 {$ENDIF}
1493 (*
1494 procedure conwriter (constref buf; len: SizeUInt);
1495 var
1496 ss: ShortString;
1497 slen: Integer;
1498 b: PByte;
1499 begin
1500 if (len < 1) then exit;
1501 b := PByte(@buf);
1502 while (len > 0) do
1503 begin
1504 if (len > 255) then slen := 255 else slen := Integer(len);
1505 Move(b^, ss[1], len);
1506 ss[0] := AnsiChar(slen);
1507 write(ss);
1508 b += slen;
1509 len -= slen;
1510 end;
1511 end;
1512 *)
1515 function formatstrf (const fmt: AnsiString; const args: array of const; writerCB: TFormatStrFCallback=nil): AnsiString;
1516 const
1518 PadZeroes: AnsiString = '00000000000000000000000000000000000000000000000000000000000000000000000';
1519 var
1535 var
1539 begin
1543 begin
1545 end
1546 else
1547 begin
1549 begin
1561 begin
1566 begin
1572 var
1574 begin
1580 var
1582 begin
1588 begin
1592 begin
1602 begin
1607 var
1610 begin
1612 begin
1616 end
1617 else
1618 begin
1623 repeat
1625 begin
1629 end
1630 else
1631 begin
1633 begin
1636 end
1648 var
1650 begin
1653 repeat
1655 begin
1659 end
1660 else
1661 begin
1663 begin
1666 end
1676 var
1678 begin
1680 begin
1688 var
1690 begin
1692 begin
1699 begin
1703 begin
1704 // print literal part
1707 // output literal part
1709 begin
1711 begin
1713 break;
1717 begin
1721 end
1722 else
1723 begin
1727 continue;
1729 // check if we have argument for this format string
1731 begin
1734 break;
1736 // skip percent
1740 // parse format; check for sign
1744 // parse width
1747 begin
1748 if (fmt[spos] < '0') or (fmt[spos] > '9') then begin xwrite('<INVALID FORMAT>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
1752 begin
1758 end
1759 else
1760 begin
1764 // parse precision
1767 begin
1770 if (fmt[spos] < '0') or (fmt[spos] > '9') then begin xwrite('<INVALID FORMAT>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
1773 begin
1780 // get format char
1784 // done parsing format, check for valid format chars
1785 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;
1786 // now write formatted string
1789 begin
1790 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;
1799 begin
1801 if args[curarg].VBoolean then strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], 'true')
1806 begin
1808 if args[curarg].VBoolean then strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], AnsiChar('t'))
1813 begin
1818 else
1819 begin
1822 break;
1828 begin
1835 begin
1840 else
1841 begin
1844 break;
1847 //vtWideChar: begin end; // args[curarg].VWideChar (WideChar)
1851 begin
1858 begin
1864 begin
1866 strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], Integer(trunc(args[curarg].VExtended^)));
1870 begin
1872 strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], LongWord(trunc(args[curarg].VExtended^)));
1875 else
1876 begin
1879 break;
1883 begin
1891 begin
1900 begin
1908 else
1909 begin
1912 break;
1917 begin
1921 end
1922 else
1923 begin
1931 begin
1932 if (args[curarg].VObject <> nil) then ccname := args[curarg].VObject.Classname else ccname := '<nil>';
1938 begin
1939 if (args[curarg].VClass <> nil) then ccname := args[curarg].VClass.Classname else ccname := '<nil>';
1944 //vtPWideChar: begin end; // args[curarg].VPWideChar (PWideChar)
1946 begin
1951 //vtCurrency: begin end; // args[curarg].VCurrency (PCurrency)
1952 //vtVariant: begin end; // args[curarg].VVariant^ (PVariant)
1953 //vtInterface: begin end; // args[curarg].VInterface (Pointer);
1954 //vtWideString: begin end; // args[curarg].VWideString (Pointer);
1956 begin
1961 else begin xwrite('<INVALID FORMAT CHAR>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
1966 begin
1968 begin
1970 begin
1975 end
1976 else
1977 begin
1980 end
1981 else
1982 begin
1990 begin
1995 else begin xwrite('<INVALID FORMAT CHAR>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
1999 if (sign <> '-') then begin if zeropad then indent0(width-pclen) else indent(width-pclen); end;
2003 else
2004 begin
2007 break;
2016 var
2020 begin
2024 // get age
2027 // get size
2033 // fill info
2041 (*
2042 var
2043 ss: ShortString;
2044 ls: AnsiString;
2045 i64: Int64 = -$A000000000;
2046 ui64: UInt64 = $A000000000;
2047 begin
2048 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']);
2049 writef(conwriter, 'test float:<%s;%u;%f;%g>'#10, [666.6942, 666.6942, 666.6942, 666.6942]);
2050 ss := 'fuckit';
2051 ls := 'FUCKIT';
2052 writef(conwriter, 'test ss:<%5s;%040s>'#10, [ss, ss]);
2053 writef(conwriter, 'test ls:<%5s;%040s>'#10, [ls, ls]);
2054 writef(conwriter, 'test pointer:<%s;%x;%p>'#10, [@ss, @ss, @ss]);
2055 writef(conwriter, 'test i64:<%s;%x;%015d;%u;%X>'#10, [i64, i64, i64, i64, i64]);
2056 writef(conwriter, 'test ui64:<%s;%x;%15d;%015u;%X>'#10, [ui64, ui64, ui64, ui64, ui64]);
2057 *)