20706c661dae397f8a0b4914ab00e9caa765a252
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
95 // `true` if strings are equal; ignoring case for cp1251
102 // `pathname` will be modified if path is valid
103 // `lastIsDir` should be `true` if we are searching for directory
104 // nobody cares about shitdoze, so i'll use the same code path for it
107 // return fixed AnsiString or empty AnsiString
109 // slashes must be normalized!
112 // they throws
115 // creates file if necessary
118 // little endian
146 // big endian
176 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
178 {$ENDIF}
190 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
192 {$ENDIF}
203 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
205 {$ENDIF}
207 type
210 // returns formatted string if `writerCB` is `nil`, empty string otherwise
211 function formatstrf (const fmt: AnsiString; const args: array of const; writerCB: TFormatStrFCallback=nil): AnsiString;
218 // returns string in single or double quotes
219 // single quotes supports only pascal-style '' for single quote char
220 // double quotes supports c-style escapes
221 // function will select quote mode automatically
225 type
227 private
228 //type PItemT = ^ItemT;
231 public
232 type
234 private
238 public
245 private
249 private
256 public
260 //WARNING! don't change list contents in `for ... in`!
270 public
282 type
287 // not changed by info getter; used in other parts of the code
297 implementation
299 uses
300 xstreams;
309 '.dfzip'
310 );
313 // ////////////////////////////////////////////////////////////////////////// //
315 begin
320 begin
325 begin
330 // ////////////////////////////////////////////////////////////////////////// //
332 begin
339 begin
345 begin
350 // ////////////////////////////////////////////////////////////////////////// //
352 begin
360 begin
367 begin
373 begin
380 begin
387 begin
393 begin
400 begin
406 begin
412 var
414 begin
416 begin
426 var
428 begin
430 begin
437 var
439 begin
441 begin
444 end
445 else
446 begin
452 // ////////////////////////////////////////////////////////////////////////// //
453 var
458 // ////////////////////////////////////////////////////////////////////////// //
459 const
461 $0402,$0403,$201A,$0453,$201E,$2026,$2020,$2021,$20AC,$2030,$0409,$2039,$040A,$040C,$040B,$040F,
462 $0452,$2018,$2019,$201C,$201D,$2022,$2013,$2014,$003F,$2122,$0459,$203A,$045A,$045C,$045B,$045F,
463 $00A0,$040E,$045E,$0408,$00A4,$0490,$00A6,$00A7,$0401,$00A9,$0404,$00AB,$00AC,$00AD,$00AE,$0407,
464 $00B0,$00B1,$0406,$0456,$0491,$00B5,$00B6,$00B7,$0451,$2116,$0454,$00BB,$0458,$0405,$0455,$0457,
465 $0410,$0411,$0412,$0413,$0414,$0415,$0416,$0417,$0418,$0419,$041A,$041B,$041C,$041D,$041E,$041F,
466 $0420,$0421,$0422,$0423,$0424,$0425,$0426,$0427,$0428,$0429,$042A,$042B,$042C,$042D,$042E,$042F,
467 $0430,$0431,$0432,$0433,$0434,$0435,$0436,$0437,$0438,$0439,$043A,$043B,$043C,$043D,$043E,$043F,
469 );
473 var
475 begin
483 // ////////////////////////////////////////////////////////////////////////// //
484 // fast state-machine based UTF-8 decoder; using 8 bytes of memory
485 // code points from invalid range will never be valid, this is the property of the state machine
486 const
487 // see http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
489 // maps bytes to character classes
506 // maps a combination of a state of the automaton and a character class to a state
516 // ////////////////////////////////////////////////////////////////////////// //
517 constructor TUtf8DecoderFast.Create (v: Boolean{fuck you, fpc}); begin state := Accept; codepoint := 0; end;
523 function TUtf8DecoderFast.completeOrInvalid (): Boolean; inline; begin result := (state = Accept) or (state = Reject); end;
525 function TUtf8DecoderFast.decode (c: AnsiChar): Boolean; inline; overload; begin result := decode(Byte(c)); end;
528 var
530 begin
533 if (state <> Accept) then codepoint := (b and $3f) or (codepoint shl 6) else codepoint := ($ff shr tp) and b;
540 // ////////////////////////////////////////////////////////////////////////// //
542 begin
548 // ////////////////////////////////////////////////////////////////////////// //
550 var
553 begin
555 begin
557 begin
561 begin
564 exit;
572 var
576 begin
579 begin
581 end
583 begin
586 end
588 begin
592 end
594 begin
599 end
600 else
601 begin
606 begin
608 begin
610 begin
613 begin
615 begin
617 end
618 else
619 begin
623 exit;
630 // ////////////////////////////////////////////////////////////////////////// //
632 begin
637 begin
640 end
641 else
642 begin
651 // ////////////////////////////////////////////////////////////////////////// //
655 var
657 begin
660 begin
668 var
671 begin
674 begin
682 begin
685 end
687 begin
690 end
691 else
692 begin
699 var
702 begin
704 begin
712 // ////////////////////////////////////////////////////////////////////////// //
714 var
717 begin
720 begin
723 begin
725 exit;
735 var
738 begin
743 begin
755 var
758 begin
762 begin
765 begin
767 begin
769 end
770 else
771 begin
774 exit;
781 begin
788 // strips out name from `fn`, leaving trailing slash
790 var
793 begin
798 begin
807 // ends with '/' or '\'?
809 begin
811 begin
813 end
814 else
815 begin
821 // strips extra trailing slashes in `path, and extra leading slashes in `fn`
822 // will add slash to `path`, even if `fn` is empty!
824 var
826 begin
830 if (Length(result) > 0) and ((result[Length(result)] <> '/') and (result[Length(result)] <> '\')) then result += '/';
832 begin
834 //FIXME: make this faster!
835 while (Length(result) > 0) and ((result[Length(result)] = '/') or (result[Length(result)] = '\')) do
836 begin
845 var
847 begin
852 //result := StrEquCI1251(ext, '.wad') or StrEquCI1251(ext, '.pk3') or StrEquCI1251(ext, '.zip') or StrEquCI1251(ext, '.dfz');
857 begin
865 begin
867 Result :=
868 (* ZIP *)
871 (* PACK *)
874 (* DFWAD *)
875 ((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))
880 var
883 begin
887 begin
889 begin
892 begin
894 if StrEquCI1251(s, '.wad') or StrEquCI1251(s, '.pk3') or StrEquCI1251(s, '.zip') or StrEquCI1251(s, '.dfz') then
895 begin
897 exit;
907 var
909 begin
913 begin
920 begin
922 begin
924 end
925 else
926 begin
928 begin
930 end
931 else
932 begin
944 begin
946 begin
948 end
949 else
950 begin
952 begin
954 end
955 else
956 begin
967 begin
972 begin
978 var
980 begin
989 var
992 begin
994 begin
996 begin
1000 exit;
1003 // nothing to do
1008 // ////////////////////////////////////////////////////////////////////////// //
1009 // utils
1010 // `ch`: utf8 start
1011 // -1: invalid utf8
1013 begin
1025 var
1027 begin
1031 begin
1037 // check other sequence bytes
1039 begin
1049 // ////////////////////////////////////////////////////////////////////////// //
1050 const
1052 $0402,$0403,$201A,$0453,$201E,$2026,$2020,$2021,$20AC,$2030,$0409,$2039,$040A,$040C,$040B,$040F,
1053 $0452,$2018,$2019,$201C,$201D,$2022,$2013,$2014,$003F,$2122,$0459,$203A,$045A,$045C,$045B,$045F,
1054 $00A0,$040E,$045E,$0408,$00A4,$0490,$00A6,$00A7,$0401,$00A9,$0404,$00AB,$00AC,$00AD,$00AE,$0407,
1055 $00B0,$00B1,$0406,$0456,$0491,$00B5,$00B6,$00B7,$0451,$2116,$0454,$00BB,$0458,$0405,$0455,$0457,
1056 $0410,$0411,$0412,$0413,$0414,$0415,$0416,$0417,$0418,$0419,$041A,$041B,$041C,$041D,$041E,$041F,
1057 $0420,$0421,$0422,$0423,$0424,$0425,$0426,$0427,$0428,$0429,$042A,$042B,$042C,$042D,$042E,$042F,
1058 $0430,$0431,$0432,$0433,$0434,$0435,$0436,$0437,$0438,$0439,$043A,$043B,$043C,$043D,$043E,$043F,
1059 $0440,$0441,$0442,$0443,$0444,$0445,$0446,$0447,$0448,$0449,$044A,$044B,$044C,$044D,$044E,$044F
1060 );
1064 var
1066 begin
1067 (* The following encodings are valid, except for the 5 and 6 byte
1068 * combinations:
1069 * 0xxxxxxx
1070 * 110xxxxx 10xxxxxx
1071 * 1110xxxx 10xxxxxx 10xxxxxx
1072 * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
1073 * 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
1074 * 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
1075 *)
1083 // mask out unused bits
1091 // now continue
1093 begin
1101 // done, try 1251
1103 // alas
1108 var
1110 begin
1114 begin
1125 // ////////////////////////////////////////////////////////////////////////// //
1126 // `pathname` will be modified if path is valid
1127 // `lastIsDir` should be `true` if we are searching for directory
1128 // nobody cares about shitdoze, so i'll use the same code path for it
1130 var
1138 begin
1143 begin
1144 // remove trailing slashes
1147 // extract name
1150 begin
1154 // remove trailing slashes again
1157 //writeln(Format('npt=[%s]; newname=[%s]; curname=[%s]; wantdir=%d', [npt, newname, curname, Integer(wantdir)]));
1158 // try the easiest case first
1161 begin
1163 begin
1164 // i found her!
1167 continue;
1170 //writeln(Format('npt=[%s]; newname=[%s]; curname=[%s]; wantdir=%d', [npt, newname, curname, Integer(wantdir)]));
1171 // alas, either not found, or invalid attributes
1173 try
1175 repeat
1177 begin
1178 // i found her!
1182 break;
1185 finally
1195 var
1198 begin
1201 // check first ext
1204 for newExt in wadExtensions do if (StrEquCI1251(ext, newExt)) then begin found := true; break; end;
1206 // check second ext
1209 for newExt in wadExtensions do if (StrEquCI1251(ext, newExt)) then begin found := true; break; end;
1217 var
1220 begin
1222 //writeln('findDiskWad00: fname=<', fname, '>');
1226 //writeln(' findDiskWad01: fname=<', fname, '>; origExt=<', origExt, '>');
1228 begin
1229 //writeln(' findDiskWad02: fname=<', fname, '>; origExt=<', origExt, '>; newExt=<', newExt, '>');
1231 begin
1232 //writeln(' SKIP');
1233 continue;
1243 begin
1244 if not findFileCI(pathname) then raise EFileNotFoundException.Create('can''t open file "'+pathname+'"');
1249 var
1251 begin
1254 begin
1255 if not findFileCI(path, true) then raise Exception.Create('can''t create file "'+pathname+'"');
1262 var
1265 begin
1266 //writeln('*** TRYING R/W FILE "', pathname, '"');
1269 begin
1270 if not findFileCI(path, true) then raise Exception.Create('can''t create file "'+pathname+'"');
1274 begin
1275 //writeln('*** found old file "', oldname, '"');
1277 end
1278 else
1279 begin
1286 {$IFDEF ENDIAN_LITTLE}
1287 begin
1290 {$ELSE}
1291 var
1293 begin
1296 begin
1302 {$ENDIF}
1305 {$IFDEF ENDIAN_LITTLE}
1306 var
1308 begin
1311 begin
1317 {$ELSE}
1318 begin
1321 {$ENDIF}
1324 begin
1329 var
1332 begin
1335 begin
1337 begin
1340 end
1341 else
1342 begin
1344 begin
1363 procedure writeIntBE (st: TStream; v: ShortInt); overload; begin writeIntegerBE(st, @v, 1); end;
1365 procedure writeIntBE (st: TStream; v: SmallInt); overload; begin writeIntegerBE(st, @v, 2); end;
1366 procedure writeIntBE (st: TStream; v: LongWord); overload; begin writeIntegerBE(st, @v, 4); end;
1367 procedure writeIntBE (st: TStream; v: LongInt); overload; begin writeIntegerBE(st, @v, 4); end;
1376 begin
1378 if (maxlen <= 65535) then writeInt(st, Word(Length(str))) else writeInt(st, LongWord(Length(str)));
1383 var
1385 begin
1390 begin
1398 {$IFDEF ENDIAN_LITTLE}
1399 begin
1402 {$ELSE}
1403 var
1405 begin
1408 begin
1414 {$ENDIF}
1417 {$IFDEF ENDIAN_LITTLE}
1418 var
1420 begin
1423 begin
1429 {$ELSE}
1430 begin
1433 {$ENDIF}
1454 // ////////////////////////////////////////////////////////////////////////// //
1455 function nmin (a, b: Byte): Byte; inline; overload; begin if (a < b) then result := a else result := b; end;
1456 function nmin (a, b: ShortInt): ShortInt; inline; overload; begin if (a < b) then result := a else result := b; end;
1457 function nmin (a, b: Word): Word; inline; overload; begin if (a < b) then result := a else result := b; end;
1458 function nmin (a, b: SmallInt): SmallInt; inline; overload; begin if (a < b) then result := a else result := b; end;
1459 function nmin (a, b: LongWord): LongWord; inline; overload; begin if (a < b) then result := a else result := b; end;
1460 function nmin (a, b: LongInt): LongInt; inline; overload; begin if (a < b) then result := a else result := b; end;
1461 function nmin (a, b: Int64): Int64; inline; overload; begin if (a < b) then result := a else result := b; end;
1462 function nmin (a, b: UInt64): UInt64; inline; overload; begin if (a < b) then result := a else result := b; end;
1463 function nmin (a, b: Single): Single; inline; overload; begin if (a < b) then result := a else result := b; end;
1464 function nmin (a, b: Double): Double; inline; overload; begin if (a < b) then result := a else result := b; end;
1465 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
1466 function nmin (a, b: Extended): Extended; inline; overload; begin if (a < b) then result := a else result := b; end;
1467 {$ENDIF}
1469 function nmax (a, b: Byte): Byte; inline; overload; begin if (a > b) then result := a else result := b; end;
1470 function nmax (a, b: ShortInt): ShortInt; inline; overload; begin if (a > b) then result := a else result := b; end;
1471 function nmax (a, b: Word): Word; inline; overload; begin if (a > b) then result := a else result := b; end;
1472 function nmax (a, b: SmallInt): SmallInt; inline; overload; begin if (a > b) then result := a else result := b; end;
1473 function nmax (a, b: LongWord): LongWord; inline; overload; begin if (a > b) then result := a else result := b; end;
1474 function nmax (a, b: LongInt): LongInt; inline; overload; begin if (a > b) then result := a else result := b; end;
1475 function nmax (a, b: Int64): Int64; inline; overload; begin if (a > b) then result := a else result := b; end;
1476 function nmax (a, b: UInt64): UInt64; inline; overload; begin if (a > b) then result := a else result := b; end;
1477 function nmax (a, b: Single): Single; inline; overload; begin if (a > b) then result := a else result := b; end;
1478 function nmax (a, b: Double): Double; inline; overload; begin if (a > b) then result := a else result := b; end;
1479 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
1480 function nmax (a, b: Extended): Extended; inline; overload; begin if (a > b) then result := a else result := b; end;
1481 {$ENDIF}
1483 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;
1484 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;
1485 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;
1486 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;
1487 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;
1488 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;
1489 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;
1490 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;
1491 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;
1492 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;
1493 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
1494 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;
1495 {$ENDIF}
1497 // ////////////////////////////////////////////////////////////////////////// //
1498 {$IFDEF WINDOWS}
1499 function snprintf (buf: PAnsiChar; bufsize: SizeUInt; const fmt: PAnsiChar): SizeUInt; cdecl; varargs; external 'msvcrt.dll' name '_snprintf';
1500 {$ELSE}
1501 function snprintf (buf: PAnsiChar; bufsize: SizeUInt; const fmt: PAnsiChar): SizeUInt; cdecl; varargs; external 'libc' name 'snprintf';
1502 {$ENDIF}
1505 (*
1506 procedure conwriter (constref buf; len: SizeUInt);
1507 var
1508 ss: ShortString;
1509 slen: Integer;
1510 b: PByte;
1511 begin
1512 if (len < 1) then exit;
1513 b := PByte(@buf);
1514 while (len > 0) do
1515 begin
1516 if (len > 255) then slen := 255 else slen := Integer(len);
1517 Move(b^, ss[1], len);
1518 ss[0] := AnsiChar(slen);
1519 write(ss);
1520 b += slen;
1521 len -= slen;
1522 end;
1523 end;
1524 *)
1527 function formatstrf (const fmt: AnsiString; const args: array of const; writerCB: TFormatStrFCallback=nil): AnsiString;
1528 const
1530 PadZeroes: AnsiString = '00000000000000000000000000000000000000000000000000000000000000000000000';
1531 var
1547 var
1551 begin
1555 begin
1557 end
1558 else
1559 begin
1561 begin
1573 begin
1578 begin
1584 var
1586 begin
1592 var
1594 begin
1600 begin
1604 begin
1614 begin
1619 var
1622 begin
1624 begin
1628 end
1629 else
1630 begin
1635 repeat
1637 begin
1641 end
1642 else
1643 begin
1645 begin
1648 end
1660 var
1662 begin
1665 repeat
1667 begin
1671 end
1672 else
1673 begin
1675 begin
1678 end
1688 var
1690 begin
1692 begin
1700 var
1702 begin
1704 begin
1711 begin
1715 begin
1716 // print literal part
1719 // output literal part
1721 begin
1723 begin
1725 break;
1729 begin
1733 end
1734 else
1735 begin
1739 continue;
1741 // check if we have argument for this format string
1743 begin
1746 break;
1748 // skip percent
1752 // parse format; check for sign
1756 // parse width
1759 begin
1760 if (fmt[spos] < '0') or (fmt[spos] > '9') then begin xwrite('<INVALID FORMAT>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
1764 begin
1770 end
1771 else
1772 begin
1776 // parse precision
1779 begin
1782 if (fmt[spos] < '0') or (fmt[spos] > '9') then begin xwrite('<INVALID FORMAT>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
1785 begin
1792 // get format char
1796 // done parsing format, check for valid format chars
1797 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;
1798 // now write formatted string
1801 begin
1802 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;
1811 begin
1813 if args[curarg].VBoolean then strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], 'true')
1818 begin
1820 if args[curarg].VBoolean then strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], AnsiChar('t'))
1825 begin
1830 else
1831 begin
1834 break;
1840 begin
1847 begin
1852 else
1853 begin
1856 break;
1859 //vtWideChar: begin end; // args[curarg].VWideChar (WideChar)
1863 begin
1870 begin
1876 begin
1878 strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], Integer(trunc(args[curarg].VExtended^)));
1882 begin
1884 strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], LongWord(trunc(args[curarg].VExtended^)));
1887 else
1888 begin
1891 break;
1895 begin
1903 begin
1912 begin
1920 else
1921 begin
1924 break;
1929 begin
1933 end
1934 else
1935 begin
1943 begin
1944 if (args[curarg].VObject <> nil) then ccname := args[curarg].VObject.Classname else ccname := '<nil>';
1950 begin
1951 if (args[curarg].VClass <> nil) then ccname := args[curarg].VClass.Classname else ccname := '<nil>';
1956 //vtPWideChar: begin end; // args[curarg].VPWideChar (PWideChar)
1958 begin
1963 //vtCurrency: begin end; // args[curarg].VCurrency (PCurrency)
1964 //vtVariant: begin end; // args[curarg].VVariant^ (PVariant)
1965 //vtInterface: begin end; // args[curarg].VInterface (Pointer);
1966 //vtWideString: begin end; // args[curarg].VWideString (Pointer);
1968 begin
1973 else begin xwrite('<INVALID FORMAT CHAR>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
1978 begin
1980 begin
1982 begin
1987 end
1988 else
1989 begin
1992 end
1993 else
1994 begin
2002 begin
2007 else begin xwrite('<INVALID FORMAT CHAR>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
2011 if (sign <> '-') then begin if zeropad then indent0(width-pclen) else indent(width-pclen); end;
2015 else
2016 begin
2019 break;
2028 var
2032 begin
2036 // get age
2039 // get size
2045 // fill info
2053 (*
2054 var
2055 ss: ShortString;
2056 ls: AnsiString;
2057 i64: Int64 = -$A000000000;
2058 ui64: UInt64 = $A000000000;
2059 begin
2060 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']);
2061 writef(conwriter, 'test float:<%s;%u;%f;%g>'#10, [666.6942, 666.6942, 666.6942, 666.6942]);
2062 ss := 'fuckit';
2063 ls := 'FUCKIT';
2064 writef(conwriter, 'test ss:<%5s;%040s>'#10, [ss, ss]);
2065 writef(conwriter, 'test ls:<%5s;%040s>'#10, [ls, ls]);
2066 writef(conwriter, 'test pointer:<%s;%x;%p>'#10, [@ss, @ss, @ss]);
2067 writef(conwriter, 'test i64:<%s;%x;%015d;%u;%X>'#10, [i64, i64, i64, i64, i64]);
2068 writef(conwriter, 'test ui64:<%s;%x;%15d;%015u;%X>'#10, [ui64, ui64, ui64, ui64, ui64]);
2069 *)