215e60a8115bfb5373da9329867f78804a001139
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 implementation
282 uses
283 xstreams;
286 // ////////////////////////////////////////////////////////////////////////// //
288 begin
293 begin
298 begin
303 // ////////////////////////////////////////////////////////////////////////// //
305 begin
312 begin
318 begin
323 // ////////////////////////////////////////////////////////////////////////// //
325 begin
333 begin
340 begin
346 begin
353 begin
360 begin
366 begin
373 begin
379 begin
385 var
387 begin
389 begin
399 var
401 begin
403 begin
410 var
412 begin
414 begin
417 end
418 else
419 begin
425 // ////////////////////////////////////////////////////////////////////////// //
426 var
431 // ////////////////////////////////////////////////////////////////////////// //
432 const
434 $0402,$0403,$201A,$0453,$201E,$2026,$2020,$2021,$20AC,$2030,$0409,$2039,$040A,$040C,$040B,$040F,
435 $0452,$2018,$2019,$201C,$201D,$2022,$2013,$2014,$003F,$2122,$0459,$203A,$045A,$045C,$045B,$045F,
436 $00A0,$040E,$045E,$0408,$00A4,$0490,$00A6,$00A7,$0401,$00A9,$0404,$00AB,$00AC,$00AD,$00AE,$0407,
437 $00B0,$00B1,$0406,$0456,$0491,$00B5,$00B6,$00B7,$0451,$2116,$0454,$00BB,$0458,$0405,$0455,$0457,
438 $0410,$0411,$0412,$0413,$0414,$0415,$0416,$0417,$0418,$0419,$041A,$041B,$041C,$041D,$041E,$041F,
439 $0420,$0421,$0422,$0423,$0424,$0425,$0426,$0427,$0428,$0429,$042A,$042B,$042C,$042D,$042E,$042F,
440 $0430,$0431,$0432,$0433,$0434,$0435,$0436,$0437,$0438,$0439,$043A,$043B,$043C,$043D,$043E,$043F,
442 );
446 var
448 begin
456 // ////////////////////////////////////////////////////////////////////////// //
457 // fast state-machine based UTF-8 decoder; using 8 bytes of memory
458 // code points from invalid range will never be valid, this is the property of the state machine
459 const
460 // see http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
462 // maps bytes to character classes
479 // maps a combination of a state of the automaton and a character class to a state
489 // ////////////////////////////////////////////////////////////////////////// //
490 constructor TUtf8DecoderFast.Create (v: Boolean{fuck you, fpc}); begin state := Accept; codepoint := 0; end;
496 function TUtf8DecoderFast.completeOrInvalid (): Boolean; inline; begin result := (state = Accept) or (state = Reject); end;
498 function TUtf8DecoderFast.decode (c: AnsiChar): Boolean; inline; overload; begin result := decode(Byte(c)); end;
501 var
503 begin
506 if (state <> Accept) then codepoint := (b and $3f) or (codepoint shl 6) else codepoint := ($ff shr tp) and b;
513 // ////////////////////////////////////////////////////////////////////////// //
515 begin
521 // ////////////////////////////////////////////////////////////////////////// //
523 var
526 begin
528 begin
530 begin
534 begin
537 exit;
545 var
549 begin
552 begin
554 end
556 begin
559 end
561 begin
565 end
567 begin
572 end
573 else
574 begin
579 begin
581 begin
583 begin
586 begin
588 begin
590 end
591 else
592 begin
596 exit;
603 // ////////////////////////////////////////////////////////////////////////// //
605 begin
610 begin
613 end
614 else
615 begin
624 // ////////////////////////////////////////////////////////////////////////// //
628 var
630 begin
633 begin
641 var
644 begin
647 begin
655 begin
658 end
660 begin
663 end
664 else
665 begin
672 var
675 begin
677 begin
685 // ////////////////////////////////////////////////////////////////////////// //
687 var
690 begin
693 begin
696 begin
698 exit;
708 var
711 begin
716 begin
728 var
731 begin
735 begin
738 begin
740 begin
742 end
743 else
744 begin
747 exit;
754 begin
761 // strips out name from `fn`, leaving trailing slash
763 var
766 begin
771 begin
780 // ends with '/' or '\'?
782 begin
784 begin
786 end
787 else
788 begin
794 // strips extra trailing slashes in `path, and extra leading slashes in `fn`
795 // will add slash to `path`, even if `fn` is empty!
797 var
799 begin
803 if (Length(result) > 0) and ((result[Length(result)] <> '/') and (result[Length(result)] <> '\')) then result += '/';
805 begin
807 //FIXME: make this faster!
808 while (Length(result) > 0) and ((result[Length(result)] = '/') or (result[Length(result)] = '\')) do
809 begin
818 var
820 begin
822 result := StrEquCI1251(ext, '.wad') or StrEquCI1251(ext, '.pk3') or StrEquCI1251(ext, '.zip') or StrEquCI1251(ext, '.dfz');
827 begin
834 begin
836 Result :=
837 (* ZIP *)
840 (* PACK *)
843 (* DFWAD *)
844 ((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))
849 var
852 begin
856 begin
858 begin
861 begin
863 if StrEquCI1251(s, '.wad') or StrEquCI1251(s, '.pk3') or StrEquCI1251(s, '.zip') or StrEquCI1251(s, '.dfz') then
864 begin
866 exit;
876 var
878 begin
882 begin
889 begin
891 begin
893 end
894 else
895 begin
897 begin
899 end
900 else
901 begin
913 begin
915 begin
917 end
918 else
919 begin
921 begin
923 end
924 else
925 begin
937 var
939 begin
948 var
951 begin
953 begin
955 begin
959 exit;
962 // nothing to do
967 // ////////////////////////////////////////////////////////////////////////// //
968 // utils
969 // `ch`: utf8 start
970 // -1: invalid utf8
972 begin
984 var
986 begin
990 begin
996 // check other sequence bytes
998 begin
1008 // ////////////////////////////////////////////////////////////////////////// //
1009 const
1011 $0402,$0403,$201A,$0453,$201E,$2026,$2020,$2021,$20AC,$2030,$0409,$2039,$040A,$040C,$040B,$040F,
1012 $0452,$2018,$2019,$201C,$201D,$2022,$2013,$2014,$003F,$2122,$0459,$203A,$045A,$045C,$045B,$045F,
1013 $00A0,$040E,$045E,$0408,$00A4,$0490,$00A6,$00A7,$0401,$00A9,$0404,$00AB,$00AC,$00AD,$00AE,$0407,
1014 $00B0,$00B1,$0406,$0456,$0491,$00B5,$00B6,$00B7,$0451,$2116,$0454,$00BB,$0458,$0405,$0455,$0457,
1015 $0410,$0411,$0412,$0413,$0414,$0415,$0416,$0417,$0418,$0419,$041A,$041B,$041C,$041D,$041E,$041F,
1016 $0420,$0421,$0422,$0423,$0424,$0425,$0426,$0427,$0428,$0429,$042A,$042B,$042C,$042D,$042E,$042F,
1017 $0430,$0431,$0432,$0433,$0434,$0435,$0436,$0437,$0438,$0439,$043A,$043B,$043C,$043D,$043E,$043F,
1018 $0440,$0441,$0442,$0443,$0444,$0445,$0446,$0447,$0448,$0449,$044A,$044B,$044C,$044D,$044E,$044F
1019 );
1023 var
1025 begin
1026 (* The following encodings are valid, except for the 5 and 6 byte
1027 * combinations:
1028 * 0xxxxxxx
1029 * 110xxxxx 10xxxxxx
1030 * 1110xxxx 10xxxxxx 10xxxxxx
1031 * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
1032 * 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
1033 * 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
1034 *)
1042 // mask out unused bits
1050 // now continue
1052 begin
1060 // done, try 1251
1062 // alas
1067 var
1069 begin
1073 begin
1084 // ////////////////////////////////////////////////////////////////////////// //
1085 // `pathname` will be modified if path is valid
1086 // `lastIsDir` should be `true` if we are searching for directory
1087 // nobody cares about shitdoze, so i'll use the same code path for it
1089 var
1097 begin
1102 begin
1103 // remove trailing slashes
1106 // extract name
1109 begin
1113 // remove trailing slashes again
1116 //writeln(Format('npt=[%s]; newname=[%s]; curname=[%s]; wantdir=%d', [npt, newname, curname, Integer(wantdir)]));
1117 // try the easiest case first
1120 begin
1122 begin
1123 // i found her!
1126 continue;
1129 //writeln(Format('npt=[%s]; newname=[%s]; curname=[%s]; wantdir=%d', [npt, newname, curname, Integer(wantdir)]));
1130 // alas, either not found, or invalid attributes
1132 try
1134 repeat
1136 begin
1137 // i found her!
1141 break;
1144 finally
1153 const fileExtensions: array [0..6] of AnsiString = ('.dfz', '.wad', '.dfwad', '.pk3', '.pak', '.zip', '.dfzip');
1156 var
1159 begin
1162 // check first ext
1165 for newExt in fileExtensions do if (StrEquCI1251(ext, newExt)) then begin found := true; break; end;
1167 // check second ext
1170 for newExt in fileExtensions do if (StrEquCI1251(ext, newExt)) then begin found := true; break; end;
1178 var
1181 begin
1183 //writeln('findDiskWad00: fname=<', fname, '>');
1187 //writeln(' findDiskWad01: fname=<', fname, '>; origExt=<', origExt, '>');
1189 begin
1190 //writeln(' findDiskWad02: fname=<', fname, '>; origExt=<', origExt, '>; newExt=<', newExt, '>');
1192 begin
1193 //writeln(' SKIP');
1194 continue;
1204 begin
1205 if not findFileCI(pathname) then raise EFileNotFoundException.Create('can''t open file "'+pathname+'"');
1210 var
1212 begin
1215 begin
1216 if not findFileCI(path, true) then raise Exception.Create('can''t create file "'+pathname+'"');
1223 var
1226 begin
1227 //writeln('*** TRYING R/W FILE "', pathname, '"');
1230 begin
1231 if not findFileCI(path, true) then raise Exception.Create('can''t create file "'+pathname+'"');
1235 begin
1236 //writeln('*** found old file "', oldname, '"');
1238 end
1239 else
1240 begin
1247 {$IFDEF ENDIAN_LITTLE}
1248 begin
1251 {$ELSE}
1252 var
1254 begin
1257 begin
1263 {$ENDIF}
1266 {$IFDEF ENDIAN_LITTLE}
1267 var
1269 begin
1272 begin
1278 {$ELSE}
1279 begin
1282 {$ENDIF}
1285 begin
1290 var
1293 begin
1296 begin
1298 begin
1301 end
1302 else
1303 begin
1305 begin
1324 procedure writeIntBE (st: TStream; v: ShortInt); overload; begin writeIntegerBE(st, @v, 1); end;
1326 procedure writeIntBE (st: TStream; v: SmallInt); overload; begin writeIntegerBE(st, @v, 2); end;
1327 procedure writeIntBE (st: TStream; v: LongWord); overload; begin writeIntegerBE(st, @v, 4); end;
1328 procedure writeIntBE (st: TStream; v: LongInt); overload; begin writeIntegerBE(st, @v, 4); end;
1337 begin
1339 if (maxlen <= 65535) then writeInt(st, Word(Length(str))) else writeInt(st, LongWord(Length(str)));
1344 var
1346 begin
1351 begin
1359 {$IFDEF ENDIAN_LITTLE}
1360 begin
1363 {$ELSE}
1364 var
1366 begin
1369 begin
1375 {$ENDIF}
1378 {$IFDEF ENDIAN_LITTLE}
1379 var
1381 begin
1384 begin
1390 {$ELSE}
1391 begin
1394 {$ENDIF}
1415 // ////////////////////////////////////////////////////////////////////////// //
1416 function nmin (a, b: Byte): Byte; inline; overload; begin if (a < b) then result := a else result := b; end;
1417 function nmin (a, b: ShortInt): ShortInt; inline; overload; begin if (a < b) then result := a else result := b; end;
1418 function nmin (a, b: Word): Word; inline; overload; begin if (a < b) then result := a else result := b; end;
1419 function nmin (a, b: SmallInt): SmallInt; inline; overload; begin if (a < b) then result := a else result := b; end;
1420 function nmin (a, b: LongWord): LongWord; inline; overload; begin if (a < b) then result := a else result := b; end;
1421 function nmin (a, b: LongInt): LongInt; inline; overload; begin if (a < b) then result := a else result := b; end;
1422 function nmin (a, b: Int64): Int64; inline; overload; begin if (a < b) then result := a else result := b; end;
1423 function nmin (a, b: UInt64): UInt64; inline; overload; begin if (a < b) then result := a else result := b; end;
1424 function nmin (a, b: Single): Single; inline; overload; begin if (a < b) then result := a else result := b; end;
1425 function nmin (a, b: Double): Double; inline; overload; begin if (a < b) then result := a else result := b; end;
1426 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
1427 function nmin (a, b: Extended): Extended; inline; overload; begin if (a < b) then result := a else result := b; end;
1428 {$ENDIF}
1430 function nmax (a, b: Byte): Byte; inline; overload; begin if (a > b) then result := a else result := b; end;
1431 function nmax (a, b: ShortInt): ShortInt; inline; overload; begin if (a > b) then result := a else result := b; end;
1432 function nmax (a, b: Word): Word; inline; overload; begin if (a > b) then result := a else result := b; end;
1433 function nmax (a, b: SmallInt): SmallInt; inline; overload; begin if (a > b) then result := a else result := b; end;
1434 function nmax (a, b: LongWord): LongWord; inline; overload; begin if (a > b) then result := a else result := b; end;
1435 function nmax (a, b: LongInt): LongInt; inline; overload; begin if (a > b) then result := a else result := b; end;
1436 function nmax (a, b: Int64): Int64; inline; overload; begin if (a > b) then result := a else result := b; end;
1437 function nmax (a, b: UInt64): UInt64; inline; overload; begin if (a > b) then result := a else result := b; end;
1438 function nmax (a, b: Single): Single; inline; overload; begin if (a > b) then result := a else result := b; end;
1439 function nmax (a, b: Double): Double; inline; overload; begin if (a > b) then result := a else result := b; end;
1440 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
1441 function nmax (a, b: Extended): Extended; inline; overload; begin if (a > b) then result := a else result := b; end;
1442 {$ENDIF}
1444 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;
1445 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;
1446 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;
1447 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;
1448 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;
1449 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;
1450 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;
1451 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;
1452 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;
1453 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;
1454 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
1455 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;
1456 {$ENDIF}
1458 // ////////////////////////////////////////////////////////////////////////// //
1459 {$IFDEF WINDOWS}
1460 function snprintf (buf: PAnsiChar; bufsize: SizeUInt; const fmt: PAnsiChar): SizeUInt; cdecl; varargs; external 'msvcrt.dll' name '_snprintf';
1461 {$ELSE}
1462 function snprintf (buf: PAnsiChar; bufsize: SizeUInt; const fmt: PAnsiChar): SizeUInt; cdecl; varargs; external 'libc' name 'snprintf';
1463 {$ENDIF}
1466 (*
1467 procedure conwriter (constref buf; len: SizeUInt);
1468 var
1469 ss: ShortString;
1470 slen: Integer;
1471 b: PByte;
1472 begin
1473 if (len < 1) then exit;
1474 b := PByte(@buf);
1475 while (len > 0) do
1476 begin
1477 if (len > 255) then slen := 255 else slen := Integer(len);
1478 Move(b^, ss[1], len);
1479 ss[0] := AnsiChar(slen);
1480 write(ss);
1481 b += slen;
1482 len -= slen;
1483 end;
1484 end;
1485 *)
1488 function formatstrf (const fmt: AnsiString; const args: array of const; writerCB: TFormatStrFCallback=nil): AnsiString;
1489 const
1491 PadZeroes: AnsiString = '00000000000000000000000000000000000000000000000000000000000000000000000';
1492 var
1508 var
1512 begin
1516 begin
1518 end
1519 else
1520 begin
1522 begin
1534 begin
1539 begin
1545 var
1547 begin
1553 var
1555 begin
1561 begin
1565 begin
1575 begin
1580 var
1583 begin
1585 begin
1589 end
1590 else
1591 begin
1596 repeat
1598 begin
1602 end
1603 else
1604 begin
1606 begin
1609 end
1621 var
1623 begin
1626 repeat
1628 begin
1632 end
1633 else
1634 begin
1636 begin
1639 end
1649 var
1651 begin
1653 begin
1661 var
1663 begin
1665 begin
1672 begin
1676 begin
1677 // print literal part
1680 // output literal part
1682 begin
1684 begin
1686 break;
1690 begin
1694 end
1695 else
1696 begin
1700 continue;
1702 // check if we have argument for this format string
1704 begin
1707 break;
1709 // skip percent
1713 // parse format; check for sign
1717 // parse width
1720 begin
1721 if (fmt[spos] < '0') or (fmt[spos] > '9') then begin xwrite('<INVALID FORMAT>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
1725 begin
1731 end
1732 else
1733 begin
1737 // parse precision
1740 begin
1743 if (fmt[spos] < '0') or (fmt[spos] > '9') then begin xwrite('<INVALID FORMAT>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
1746 begin
1753 // get format char
1757 // done parsing format, check for valid format chars
1758 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;
1759 // now write formatted string
1762 begin
1763 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;
1772 begin
1774 if args[curarg].VBoolean then strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], 'true')
1779 begin
1781 if args[curarg].VBoolean then strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], AnsiChar('t'))
1786 begin
1791 else
1792 begin
1795 break;
1801 begin
1808 begin
1813 else
1814 begin
1817 break;
1820 //vtWideChar: begin end; // args[curarg].VWideChar (WideChar)
1824 begin
1831 begin
1837 begin
1839 strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], Integer(trunc(args[curarg].VExtended^)));
1843 begin
1845 strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], LongWord(trunc(args[curarg].VExtended^)));
1848 else
1849 begin
1852 break;
1856 begin
1864 begin
1873 begin
1881 else
1882 begin
1885 break;
1890 begin
1894 end
1895 else
1896 begin
1904 begin
1905 if (args[curarg].VObject <> nil) then ccname := args[curarg].VObject.Classname else ccname := '<nil>';
1911 begin
1912 if (args[curarg].VClass <> nil) then ccname := args[curarg].VClass.Classname else ccname := '<nil>';
1917 //vtPWideChar: begin end; // args[curarg].VPWideChar (PWideChar)
1919 begin
1924 //vtCurrency: begin end; // args[curarg].VCurrency (PCurrency)
1925 //vtVariant: begin end; // args[curarg].VVariant^ (PVariant)
1926 //vtInterface: begin end; // args[curarg].VInterface (Pointer);
1927 //vtWideString: begin end; // args[curarg].VWideString (Pointer);
1929 begin
1934 else begin xwrite('<INVALID FORMAT CHAR>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
1939 begin
1941 begin
1943 begin
1948 end
1949 else
1950 begin
1953 end
1954 else
1955 begin
1963 begin
1968 else begin xwrite('<INVALID FORMAT CHAR>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
1972 if (sign <> '-') then begin if zeropad then indent0(width-pclen) else indent(width-pclen); end;
1976 else
1977 begin
1980 break;
1988 (*
1989 var
1990 ss: ShortString;
1991 ls: AnsiString;
1992 i64: Int64 = -$A000000000;
1993 ui64: UInt64 = $A000000000;
1994 begin
1995 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']);
1996 writef(conwriter, 'test float:<%s;%u;%f;%g>'#10, [666.6942, 666.6942, 666.6942, 666.6942]);
1997 ss := 'fuckit';
1998 ls := 'FUCKIT';
1999 writef(conwriter, 'test ss:<%5s;%040s>'#10, [ss, ss]);
2000 writef(conwriter, 'test ls:<%5s;%040s>'#10, [ls, ls]);
2001 writef(conwriter, 'test pointer:<%s;%x;%p>'#10, [@ss, @ss, @ss]);
2002 writef(conwriter, 'test i64:<%s;%x;%015d;%u;%X>'#10, [i64, i64, i64, i64, i64]);
2003 writef(conwriter, 'test ui64:<%s;%x;%15d;%015u;%X>'#10, [ui64, ui64, ui64, ui64, ui64]);
2004 *)