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
94 // `true` if strings are equal; ignoring case for cp1251
101 // `pathname` will be modified if path is valid
102 // `lastIsDir` should be `true` if we are searching for directory
103 // nobody cares about shitdoze, so i'll use the same code path for it
106 // return fixed AnsiString or empty AnsiString
108 // slashes must be normalized!
111 // they throws
114 // creates file if necessary
117 // little endian
145 // big endian
175 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
177 {$ENDIF}
189 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
191 {$ENDIF}
202 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
204 {$ENDIF}
206 type
209 // returns formatted string if `writerCB` is `nil`, empty string otherwise
210 function formatstrf (const fmt: AnsiString; const args: array of const; writerCB: TFormatStrFCallback=nil): AnsiString;
217 // returns string in single or double quotes
218 // single quotes supports only pascal-style '' for single quote char
219 // double quotes supports c-style escapes
220 // function will select quote mode automatically
224 type
226 private
227 //type PItemT = ^ItemT;
230 public
231 type
233 private
237 public
244 private
248 private
255 public
259 //WARNING! don't change list contents in `for ... in`!
269 public
281 type
286 // not changed by info getter; used in other parts of the code
296 implementation
298 uses
299 xstreams;
308 '.dfzip'
309 );
312 // ////////////////////////////////////////////////////////////////////////// //
314 begin
319 begin
324 begin
329 // ////////////////////////////////////////////////////////////////////////// //
331 begin
338 begin
344 begin
349 // ////////////////////////////////////////////////////////////////////////// //
351 begin
359 begin
366 begin
372 begin
379 begin
386 begin
392 begin
399 begin
405 begin
411 var
413 begin
415 begin
425 var
427 begin
429 begin
436 var
438 begin
440 begin
443 end
444 else
445 begin
451 // ////////////////////////////////////////////////////////////////////////// //
452 var
457 // ////////////////////////////////////////////////////////////////////////// //
458 const
460 $0402,$0403,$201A,$0453,$201E,$2026,$2020,$2021,$20AC,$2030,$0409,$2039,$040A,$040C,$040B,$040F,
461 $0452,$2018,$2019,$201C,$201D,$2022,$2013,$2014,$003F,$2122,$0459,$203A,$045A,$045C,$045B,$045F,
462 $00A0,$040E,$045E,$0408,$00A4,$0490,$00A6,$00A7,$0401,$00A9,$0404,$00AB,$00AC,$00AD,$00AE,$0407,
463 $00B0,$00B1,$0406,$0456,$0491,$00B5,$00B6,$00B7,$0451,$2116,$0454,$00BB,$0458,$0405,$0455,$0457,
464 $0410,$0411,$0412,$0413,$0414,$0415,$0416,$0417,$0418,$0419,$041A,$041B,$041C,$041D,$041E,$041F,
465 $0420,$0421,$0422,$0423,$0424,$0425,$0426,$0427,$0428,$0429,$042A,$042B,$042C,$042D,$042E,$042F,
466 $0430,$0431,$0432,$0433,$0434,$0435,$0436,$0437,$0438,$0439,$043A,$043B,$043C,$043D,$043E,$043F,
468 );
472 var
474 begin
482 // ////////////////////////////////////////////////////////////////////////// //
483 // fast state-machine based UTF-8 decoder; using 8 bytes of memory
484 // code points from invalid range will never be valid, this is the property of the state machine
485 const
486 // see http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
488 // maps bytes to character classes
505 // maps a combination of a state of the automaton and a character class to a state
515 // ////////////////////////////////////////////////////////////////////////// //
516 constructor TUtf8DecoderFast.Create (v: Boolean{fuck you, fpc}); begin state := Accept; codepoint := 0; end;
522 function TUtf8DecoderFast.completeOrInvalid (): Boolean; inline; begin result := (state = Accept) or (state = Reject); end;
524 function TUtf8DecoderFast.decode (c: AnsiChar): Boolean; inline; overload; begin result := decode(Byte(c)); end;
527 var
529 begin
532 if (state <> Accept) then codepoint := (b and $3f) or (codepoint shl 6) else codepoint := ($ff shr tp) and b;
539 // ////////////////////////////////////////////////////////////////////////// //
541 begin
547 // ////////////////////////////////////////////////////////////////////////// //
549 var
552 begin
554 begin
556 begin
560 begin
563 exit;
571 var
575 begin
578 begin
580 end
582 begin
585 end
587 begin
591 end
593 begin
598 end
599 else
600 begin
605 begin
607 begin
609 begin
612 begin
614 begin
616 end
617 else
618 begin
622 exit;
629 // ////////////////////////////////////////////////////////////////////////// //
631 begin
636 begin
639 end
640 else
641 begin
650 // ////////////////////////////////////////////////////////////////////////// //
654 var
656 begin
659 begin
667 var
670 begin
673 begin
681 begin
684 end
686 begin
689 end
690 else
691 begin
698 var
701 begin
703 begin
711 // ////////////////////////////////////////////////////////////////////////// //
713 var
716 begin
719 begin
722 begin
724 exit;
734 var
737 begin
742 begin
754 var
757 begin
761 begin
764 begin
766 begin
768 end
769 else
770 begin
773 exit;
780 begin
787 // strips out name from `fn`, leaving trailing slash
789 var
792 begin
797 begin
806 // ends with '/' or '\'?
808 begin
810 begin
812 end
813 else
814 begin
820 // strips extra trailing slashes in `path, and extra leading slashes in `fn`
821 // will add slash to `path`, even if `fn` is empty!
823 var
825 begin
829 if (Length(result) > 0) and ((result[Length(result)] <> '/') and (result[Length(result)] <> '\')) then result += '/';
831 begin
833 //FIXME: make this faster!
834 while (Length(result) > 0) and ((result[Length(result)] = '/') or (result[Length(result)] = '\')) do
835 begin
844 var
846 begin
851 //result := StrEquCI1251(ext, '.wad') or StrEquCI1251(ext, '.pk3') or StrEquCI1251(ext, '.zip') or StrEquCI1251(ext, '.dfz');
856 begin
864 begin
866 Result :=
867 (* ZIP *)
870 (* PACK *)
873 (* DFWAD *)
874 ((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))
879 var
882 begin
886 begin
888 begin
891 begin
893 if StrEquCI1251(s, '.wad') or StrEquCI1251(s, '.pk3') or StrEquCI1251(s, '.zip') or StrEquCI1251(s, '.dfz') then
894 begin
896 exit;
906 var
908 begin
912 begin
919 begin
921 begin
923 end
924 else
925 begin
927 begin
929 end
930 else
931 begin
943 begin
945 begin
947 end
948 else
949 begin
951 begin
953 end
954 else
955 begin
966 begin
972 var
974 begin
983 var
986 begin
988 begin
990 begin
994 exit;
997 // nothing to do
1002 // ////////////////////////////////////////////////////////////////////////// //
1003 // utils
1004 // `ch`: utf8 start
1005 // -1: invalid utf8
1007 begin
1019 var
1021 begin
1025 begin
1031 // check other sequence bytes
1033 begin
1043 // ////////////////////////////////////////////////////////////////////////// //
1044 const
1046 $0402,$0403,$201A,$0453,$201E,$2026,$2020,$2021,$20AC,$2030,$0409,$2039,$040A,$040C,$040B,$040F,
1047 $0452,$2018,$2019,$201C,$201D,$2022,$2013,$2014,$003F,$2122,$0459,$203A,$045A,$045C,$045B,$045F,
1048 $00A0,$040E,$045E,$0408,$00A4,$0490,$00A6,$00A7,$0401,$00A9,$0404,$00AB,$00AC,$00AD,$00AE,$0407,
1049 $00B0,$00B1,$0406,$0456,$0491,$00B5,$00B6,$00B7,$0451,$2116,$0454,$00BB,$0458,$0405,$0455,$0457,
1050 $0410,$0411,$0412,$0413,$0414,$0415,$0416,$0417,$0418,$0419,$041A,$041B,$041C,$041D,$041E,$041F,
1051 $0420,$0421,$0422,$0423,$0424,$0425,$0426,$0427,$0428,$0429,$042A,$042B,$042C,$042D,$042E,$042F,
1052 $0430,$0431,$0432,$0433,$0434,$0435,$0436,$0437,$0438,$0439,$043A,$043B,$043C,$043D,$043E,$043F,
1053 $0440,$0441,$0442,$0443,$0444,$0445,$0446,$0447,$0448,$0449,$044A,$044B,$044C,$044D,$044E,$044F
1054 );
1058 var
1060 begin
1061 (* The following encodings are valid, except for the 5 and 6 byte
1062 * combinations:
1063 * 0xxxxxxx
1064 * 110xxxxx 10xxxxxx
1065 * 1110xxxx 10xxxxxx 10xxxxxx
1066 * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
1067 * 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
1068 * 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
1069 *)
1077 // mask out unused bits
1085 // now continue
1087 begin
1095 // done, try 1251
1097 // alas
1102 var
1104 begin
1108 begin
1119 // ////////////////////////////////////////////////////////////////////////// //
1120 // `pathname` will be modified if path is valid
1121 // `lastIsDir` should be `true` if we are searching for directory
1122 // nobody cares about shitdoze, so i'll use the same code path for it
1124 var
1132 begin
1137 begin
1138 // remove trailing slashes
1141 // extract name
1144 begin
1148 // remove trailing slashes again
1151 //writeln(Format('npt=[%s]; newname=[%s]; curname=[%s]; wantdir=%d', [npt, newname, curname, Integer(wantdir)]));
1152 // try the easiest case first
1155 begin
1157 begin
1158 // i found her!
1161 continue;
1164 //writeln(Format('npt=[%s]; newname=[%s]; curname=[%s]; wantdir=%d', [npt, newname, curname, Integer(wantdir)]));
1165 // alas, either not found, or invalid attributes
1167 try
1169 repeat
1171 begin
1172 // i found her!
1176 break;
1179 finally
1189 var
1192 begin
1195 // check first ext
1198 for newExt in wadExtensions do if (StrEquCI1251(ext, newExt)) then begin found := true; break; end;
1200 // check second ext
1203 for newExt in wadExtensions do if (StrEquCI1251(ext, newExt)) then begin found := true; break; end;
1211 var
1214 begin
1216 //writeln('findDiskWad00: fname=<', fname, '>');
1220 //writeln(' findDiskWad01: fname=<', fname, '>; origExt=<', origExt, '>');
1222 begin
1223 //writeln(' findDiskWad02: fname=<', fname, '>; origExt=<', origExt, '>; newExt=<', newExt, '>');
1225 begin
1226 //writeln(' SKIP');
1227 continue;
1237 begin
1238 if not findFileCI(pathname) then raise EFileNotFoundException.Create('can''t open file "'+pathname+'"');
1243 var
1245 begin
1248 begin
1249 if not findFileCI(path, true) then raise Exception.Create('can''t create file "'+pathname+'"');
1256 var
1259 begin
1260 //writeln('*** TRYING R/W FILE "', pathname, '"');
1263 begin
1264 if not findFileCI(path, true) then raise Exception.Create('can''t create file "'+pathname+'"');
1268 begin
1269 //writeln('*** found old file "', oldname, '"');
1271 end
1272 else
1273 begin
1280 {$IFDEF ENDIAN_LITTLE}
1281 begin
1284 {$ELSE}
1285 var
1287 begin
1290 begin
1296 {$ENDIF}
1299 {$IFDEF ENDIAN_LITTLE}
1300 var
1302 begin
1305 begin
1311 {$ELSE}
1312 begin
1315 {$ENDIF}
1318 begin
1323 var
1326 begin
1329 begin
1331 begin
1334 end
1335 else
1336 begin
1338 begin
1357 procedure writeIntBE (st: TStream; v: ShortInt); overload; begin writeIntegerBE(st, @v, 1); end;
1359 procedure writeIntBE (st: TStream; v: SmallInt); overload; begin writeIntegerBE(st, @v, 2); end;
1360 procedure writeIntBE (st: TStream; v: LongWord); overload; begin writeIntegerBE(st, @v, 4); end;
1361 procedure writeIntBE (st: TStream; v: LongInt); overload; begin writeIntegerBE(st, @v, 4); end;
1370 begin
1372 if (maxlen <= 65535) then writeInt(st, Word(Length(str))) else writeInt(st, LongWord(Length(str)));
1377 var
1379 begin
1384 begin
1392 {$IFDEF ENDIAN_LITTLE}
1393 begin
1396 {$ELSE}
1397 var
1399 begin
1402 begin
1408 {$ENDIF}
1411 {$IFDEF ENDIAN_LITTLE}
1412 var
1414 begin
1417 begin
1423 {$ELSE}
1424 begin
1427 {$ENDIF}
1448 // ////////////////////////////////////////////////////////////////////////// //
1449 function nmin (a, b: Byte): Byte; inline; overload; begin if (a < b) then result := a else result := b; end;
1450 function nmin (a, b: ShortInt): ShortInt; inline; overload; begin if (a < b) then result := a else result := b; end;
1451 function nmin (a, b: Word): Word; inline; overload; begin if (a < b) then result := a else result := b; end;
1452 function nmin (a, b: SmallInt): SmallInt; inline; overload; begin if (a < b) then result := a else result := b; end;
1453 function nmin (a, b: LongWord): LongWord; inline; overload; begin if (a < b) then result := a else result := b; end;
1454 function nmin (a, b: LongInt): LongInt; inline; overload; begin if (a < b) then result := a else result := b; end;
1455 function nmin (a, b: Int64): Int64; inline; overload; begin if (a < b) then result := a else result := b; end;
1456 function nmin (a, b: UInt64): UInt64; inline; overload; begin if (a < b) then result := a else result := b; end;
1457 function nmin (a, b: Single): Single; inline; overload; begin if (a < b) then result := a else result := b; end;
1458 function nmin (a, b: Double): Double; inline; overload; begin if (a < b) then result := a else result := b; end;
1459 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
1460 function nmin (a, b: Extended): Extended; inline; overload; begin if (a < b) then result := a else result := b; end;
1461 {$ENDIF}
1463 function nmax (a, b: Byte): Byte; inline; overload; begin if (a > b) then result := a else result := b; end;
1464 function nmax (a, b: ShortInt): ShortInt; inline; overload; begin if (a > b) then result := a else result := b; end;
1465 function nmax (a, b: Word): Word; inline; overload; begin if (a > b) then result := a else result := b; end;
1466 function nmax (a, b: SmallInt): SmallInt; inline; overload; begin if (a > b) then result := a else result := b; end;
1467 function nmax (a, b: LongWord): LongWord; inline; overload; begin if (a > b) then result := a else result := b; end;
1468 function nmax (a, b: LongInt): LongInt; inline; overload; begin if (a > b) then result := a else result := b; end;
1469 function nmax (a, b: Int64): Int64; inline; overload; begin if (a > b) then result := a else result := b; end;
1470 function nmax (a, b: UInt64): UInt64; inline; overload; begin if (a > b) then result := a else result := b; end;
1471 function nmax (a, b: Single): Single; inline; overload; begin if (a > b) then result := a else result := b; end;
1472 function nmax (a, b: Double): Double; inline; overload; begin if (a > b) then result := a else result := b; end;
1473 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
1474 function nmax (a, b: Extended): Extended; inline; overload; begin if (a > b) then result := a else result := b; end;
1475 {$ENDIF}
1477 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;
1478 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;
1479 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;
1480 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;
1481 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;
1482 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;
1483 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;
1484 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;
1485 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;
1486 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;
1487 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
1488 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;
1489 {$ENDIF}
1491 // ////////////////////////////////////////////////////////////////////////// //
1492 {$IFDEF WINDOWS}
1493 function snprintf (buf: PAnsiChar; bufsize: SizeUInt; const fmt: PAnsiChar): SizeUInt; cdecl; varargs; external 'msvcrt.dll' name '_snprintf';
1494 {$ELSE}
1495 function snprintf (buf: PAnsiChar; bufsize: SizeUInt; const fmt: PAnsiChar): SizeUInt; cdecl; varargs; external 'libc' name 'snprintf';
1496 {$ENDIF}
1499 (*
1500 procedure conwriter (constref buf; len: SizeUInt);
1501 var
1502 ss: ShortString;
1503 slen: Integer;
1504 b: PByte;
1505 begin
1506 if (len < 1) then exit;
1507 b := PByte(@buf);
1508 while (len > 0) do
1509 begin
1510 if (len > 255) then slen := 255 else slen := Integer(len);
1511 Move(b^, ss[1], len);
1512 ss[0] := AnsiChar(slen);
1513 write(ss);
1514 b += slen;
1515 len -= slen;
1516 end;
1517 end;
1518 *)
1521 function formatstrf (const fmt: AnsiString; const args: array of const; writerCB: TFormatStrFCallback=nil): AnsiString;
1522 const
1524 PadZeroes: AnsiString = '00000000000000000000000000000000000000000000000000000000000000000000000';
1525 var
1541 var
1545 begin
1549 begin
1551 end
1552 else
1553 begin
1555 begin
1567 begin
1572 begin
1578 var
1580 begin
1586 var
1588 begin
1594 begin
1598 begin
1608 begin
1613 var
1616 begin
1618 begin
1622 end
1623 else
1624 begin
1629 repeat
1631 begin
1635 end
1636 else
1637 begin
1639 begin
1642 end
1654 var
1656 begin
1659 repeat
1661 begin
1665 end
1666 else
1667 begin
1669 begin
1672 end
1682 var
1684 begin
1686 begin
1694 var
1696 begin
1698 begin
1705 begin
1709 begin
1710 // print literal part
1713 // output literal part
1715 begin
1717 begin
1719 break;
1723 begin
1727 end
1728 else
1729 begin
1733 continue;
1735 // check if we have argument for this format string
1737 begin
1740 break;
1742 // skip percent
1746 // parse format; check for sign
1750 // parse width
1753 begin
1754 if (fmt[spos] < '0') or (fmt[spos] > '9') then begin xwrite('<INVALID FORMAT>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
1758 begin
1764 end
1765 else
1766 begin
1770 // parse precision
1773 begin
1776 if (fmt[spos] < '0') or (fmt[spos] > '9') then begin xwrite('<INVALID FORMAT>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
1779 begin
1786 // get format char
1790 // done parsing format, check for valid format chars
1791 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;
1792 // now write formatted string
1795 begin
1796 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;
1805 begin
1807 if args[curarg].VBoolean then strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], 'true')
1812 begin
1814 if args[curarg].VBoolean then strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], AnsiChar('t'))
1819 begin
1824 else
1825 begin
1828 break;
1834 begin
1841 begin
1846 else
1847 begin
1850 break;
1853 //vtWideChar: begin end; // args[curarg].VWideChar (WideChar)
1857 begin
1864 begin
1870 begin
1872 strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], Integer(trunc(args[curarg].VExtended^)));
1876 begin
1878 strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], LongWord(trunc(args[curarg].VExtended^)));
1881 else
1882 begin
1885 break;
1889 begin
1897 begin
1906 begin
1914 else
1915 begin
1918 break;
1923 begin
1927 end
1928 else
1929 begin
1937 begin
1938 if (args[curarg].VObject <> nil) then ccname := args[curarg].VObject.Classname else ccname := '<nil>';
1944 begin
1945 if (args[curarg].VClass <> nil) then ccname := args[curarg].VClass.Classname else ccname := '<nil>';
1950 //vtPWideChar: begin end; // args[curarg].VPWideChar (PWideChar)
1952 begin
1957 //vtCurrency: begin end; // args[curarg].VCurrency (PCurrency)
1958 //vtVariant: begin end; // args[curarg].VVariant^ (PVariant)
1959 //vtInterface: begin end; // args[curarg].VInterface (Pointer);
1960 //vtWideString: begin end; // args[curarg].VWideString (Pointer);
1962 begin
1967 else begin xwrite('<INVALID FORMAT CHAR>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
1972 begin
1974 begin
1976 begin
1981 end
1982 else
1983 begin
1986 end
1987 else
1988 begin
1996 begin
2001 else begin xwrite('<INVALID FORMAT CHAR>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
2005 if (sign <> '-') then begin if zeropad then indent0(width-pclen) else indent(width-pclen); end;
2009 else
2010 begin
2013 break;
2022 var
2026 begin
2030 // get age
2033 // get size
2039 // fill info
2047 (*
2048 var
2049 ss: ShortString;
2050 ls: AnsiString;
2051 i64: Int64 = -$A000000000;
2052 ui64: UInt64 = $A000000000;
2053 begin
2054 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']);
2055 writef(conwriter, 'test float:<%s;%u;%f;%g>'#10, [666.6942, 666.6942, 666.6942, 666.6942]);
2056 ss := 'fuckit';
2057 ls := 'FUCKIT';
2058 writef(conwriter, 'test ss:<%5s;%040s>'#10, [ss, ss]);
2059 writef(conwriter, 'test ls:<%5s;%040s>'#10, [ls, ls]);
2060 writef(conwriter, 'test pointer:<%s;%x;%p>'#10, [@ss, @ss, @ss]);
2061 writef(conwriter, 'test i64:<%s;%x;%015d;%u;%X>'#10, [i64, i64, i64, i64, i64]);
2062 writef(conwriter, 'test ui64:<%s;%x;%15d;%015u;%X>'#10, [ui64, ui64, ui64, ui64, ui64]);
2063 *)