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
114 // little endian
142 // big endian
172 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
174 {$ENDIF}
186 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
188 {$ENDIF}
199 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
201 {$ENDIF}
203 type
206 // returns formatted string if `writerCB` is `nil`, empty string otherwise
207 function formatstrf (const fmt: AnsiString; const args: array of const; writerCB: TFormatStrFCallback=nil): AnsiString;
214 // returns string in single or double quotes
215 // single quotes supports only pascal-style '' for single quote char
216 // double quotes supports c-style escapes
217 // function will select quote mode automatically
221 type
223 private
224 //type PItemT = ^ItemT;
227 public
228 type
230 private
234 public
241 private
245 private
252 public
256 //WARNING! don't change list contents in `for ... in`!
266 public
278 implementation
280 uses
281 xstreams;
284 // ////////////////////////////////////////////////////////////////////////// //
286 begin
291 begin
296 begin
301 // ////////////////////////////////////////////////////////////////////////// //
303 begin
310 begin
316 begin
321 // ////////////////////////////////////////////////////////////////////////// //
323 begin
331 begin
338 begin
344 begin
351 begin
358 begin
364 begin
371 begin
377 begin
383 var
385 begin
387 begin
397 var
399 begin
401 begin
408 var
410 begin
412 begin
415 end
416 else
417 begin
423 // ////////////////////////////////////////////////////////////////////////// //
424 var
429 // ////////////////////////////////////////////////////////////////////////// //
430 const
432 $0402,$0403,$201A,$0453,$201E,$2026,$2020,$2021,$20AC,$2030,$0409,$2039,$040A,$040C,$040B,$040F,
433 $0452,$2018,$2019,$201C,$201D,$2022,$2013,$2014,$003F,$2122,$0459,$203A,$045A,$045C,$045B,$045F,
434 $00A0,$040E,$045E,$0408,$00A4,$0490,$00A6,$00A7,$0401,$00A9,$0404,$00AB,$00AC,$00AD,$00AE,$0407,
435 $00B0,$00B1,$0406,$0456,$0491,$00B5,$00B6,$00B7,$0451,$2116,$0454,$00BB,$0458,$0405,$0455,$0457,
436 $0410,$0411,$0412,$0413,$0414,$0415,$0416,$0417,$0418,$0419,$041A,$041B,$041C,$041D,$041E,$041F,
437 $0420,$0421,$0422,$0423,$0424,$0425,$0426,$0427,$0428,$0429,$042A,$042B,$042C,$042D,$042E,$042F,
438 $0430,$0431,$0432,$0433,$0434,$0435,$0436,$0437,$0438,$0439,$043A,$043B,$043C,$043D,$043E,$043F,
440 );
444 var
446 begin
454 // ////////////////////////////////////////////////////////////////////////// //
455 // fast state-machine based UTF-8 decoder; using 8 bytes of memory
456 // code points from invalid range will never be valid, this is the property of the state machine
457 const
458 // see http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
460 // maps bytes to character classes
477 // maps a combination of a state of the automaton and a character class to a state
487 // ////////////////////////////////////////////////////////////////////////// //
488 constructor TUtf8DecoderFast.Create (v: Boolean{fuck you, fpc}); begin state := Accept; codepoint := 0; end;
494 function TUtf8DecoderFast.completeOrInvalid (): Boolean; inline; begin result := (state = Accept) or (state = Reject); end;
496 function TUtf8DecoderFast.decode (c: AnsiChar): Boolean; inline; overload; begin result := decode(Byte(c)); end;
499 var
501 begin
504 if (state <> Accept) then codepoint := (b and $3f) or (codepoint shl 6) else codepoint := ($ff shr tp) and b;
511 // ////////////////////////////////////////////////////////////////////////// //
513 begin
519 // ////////////////////////////////////////////////////////////////////////// //
521 var
524 begin
526 begin
528 begin
532 begin
535 exit;
543 var
547 begin
550 begin
552 end
554 begin
557 end
559 begin
563 end
565 begin
570 end
571 else
572 begin
577 begin
579 begin
581 begin
584 begin
586 begin
588 end
589 else
590 begin
594 exit;
601 // ////////////////////////////////////////////////////////////////////////// //
603 begin
608 begin
611 end
612 else
613 begin
622 // ////////////////////////////////////////////////////////////////////////// //
626 var
628 begin
631 begin
639 var
642 begin
645 begin
653 begin
656 end
658 begin
661 end
662 else
663 begin
670 var
673 begin
675 begin
683 // ////////////////////////////////////////////////////////////////////////// //
685 var
688 begin
691 begin
694 begin
696 exit;
706 var
709 begin
714 begin
726 var
729 begin
733 begin
736 begin
738 begin
740 end
741 else
742 begin
745 exit;
752 begin
759 // strips out name from `fn`, leaving trailing slash
761 var
764 begin
769 begin
778 // ends with '/' or '\'?
780 begin
782 begin
784 end
785 else
786 begin
792 // strips extra trailing slashes in `path, and extra leading slashes in `fn`
793 // will add slash to `path`, even if `fn` is empty!
795 var
797 begin
801 if (Length(result) > 0) and ((result[Length(result)] <> '/') and (result[Length(result)] <> '\')) then result += '/';
803 begin
805 //FIXME: make this faster!
806 while (Length(result) > 0) and ((result[Length(result)] = '/') or (result[Length(result)] = '\')) do
807 begin
816 var
818 begin
820 result := StrEquCI1251(ext, '.wad') or StrEquCI1251(ext, '.pk3') or StrEquCI1251(ext, '.zip') or StrEquCI1251(ext, '.dfz');
825 begin
832 begin
834 Result :=
835 (* ZIP *)
838 (* PACK *)
841 (* DFWAD *)
842 ((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))
847 var
850 begin
854 begin
856 begin
859 begin
861 if StrEquCI1251(s, '.wad') or StrEquCI1251(s, '.pk3') or StrEquCI1251(s, '.zip') or StrEquCI1251(s, '.dfz') then
862 begin
864 exit;
874 var
876 begin
880 begin
887 begin
889 begin
891 end
892 else
893 begin
895 begin
897 end
898 else
899 begin
911 begin
913 begin
915 end
916 else
917 begin
919 begin
921 end
922 else
923 begin
935 var
937 begin
946 var
949 begin
951 begin
953 begin
957 exit;
960 // nothing to do
965 // ////////////////////////////////////////////////////////////////////////// //
966 // utils
967 // `ch`: utf8 start
968 // -1: invalid utf8
970 begin
982 var
984 begin
988 begin
994 // check other sequence bytes
996 begin
1006 // ////////////////////////////////////////////////////////////////////////// //
1007 const
1009 $0402,$0403,$201A,$0453,$201E,$2026,$2020,$2021,$20AC,$2030,$0409,$2039,$040A,$040C,$040B,$040F,
1010 $0452,$2018,$2019,$201C,$201D,$2022,$2013,$2014,$003F,$2122,$0459,$203A,$045A,$045C,$045B,$045F,
1011 $00A0,$040E,$045E,$0408,$00A4,$0490,$00A6,$00A7,$0401,$00A9,$0404,$00AB,$00AC,$00AD,$00AE,$0407,
1012 $00B0,$00B1,$0406,$0456,$0491,$00B5,$00B6,$00B7,$0451,$2116,$0454,$00BB,$0458,$0405,$0455,$0457,
1013 $0410,$0411,$0412,$0413,$0414,$0415,$0416,$0417,$0418,$0419,$041A,$041B,$041C,$041D,$041E,$041F,
1014 $0420,$0421,$0422,$0423,$0424,$0425,$0426,$0427,$0428,$0429,$042A,$042B,$042C,$042D,$042E,$042F,
1015 $0430,$0431,$0432,$0433,$0434,$0435,$0436,$0437,$0438,$0439,$043A,$043B,$043C,$043D,$043E,$043F,
1016 $0440,$0441,$0442,$0443,$0444,$0445,$0446,$0447,$0448,$0449,$044A,$044B,$044C,$044D,$044E,$044F
1017 );
1021 var
1023 begin
1024 (* The following encodings are valid, except for the 5 and 6 byte
1025 * combinations:
1026 * 0xxxxxxx
1027 * 110xxxxx 10xxxxxx
1028 * 1110xxxx 10xxxxxx 10xxxxxx
1029 * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
1030 * 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
1031 * 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
1032 *)
1040 // mask out unused bits
1048 // now continue
1050 begin
1058 // done, try 1251
1060 // alas
1065 var
1067 begin
1071 begin
1082 // ////////////////////////////////////////////////////////////////////////// //
1083 // `pathname` will be modified if path is valid
1084 // `lastIsDir` should be `true` if we are searching for directory
1085 // nobody cares about shitdoze, so i'll use the same code path for it
1087 var
1095 begin
1100 begin
1101 // remove trailing slashes
1104 // extract name
1107 begin
1111 // remove trailing slashes again
1114 //writeln(Format('npt=[%s]; newname=[%s]; curname=[%s]; wantdir=%d', [npt, newname, curname, Integer(wantdir)]));
1115 // try the easiest case first
1118 begin
1120 begin
1121 // i found her!
1124 continue;
1127 //writeln(Format('npt=[%s]; newname=[%s]; curname=[%s]; wantdir=%d', [npt, newname, curname, Integer(wantdir)]));
1128 // alas, either not found, or invalid attributes
1130 try
1132 repeat
1134 begin
1135 // i found her!
1139 break;
1142 finally
1151 const fileExtensions: array [0..6] of AnsiString = ('.dfz', '.wad', '.dfwad', '.pk3', '.pak', '.zip', '.dfzip');
1154 var
1157 begin
1160 // check first ext
1163 for newExt in fileExtensions do if (StrEquCI1251(ext, newExt)) then begin found := true; break; end;
1165 // check second ext
1168 for newExt in fileExtensions do if (StrEquCI1251(ext, newExt)) then begin found := true; break; end;
1176 var
1179 begin
1181 //writeln('findDiskWad00: fname=<', fname, '>');
1185 //writeln(' findDiskWad01: fname=<', fname, '>; origExt=<', origExt, '>');
1187 begin
1188 //writeln(' findDiskWad02: fname=<', fname, '>; origExt=<', origExt, '>; newExt=<', newExt, '>');
1190 begin
1191 //writeln(' SKIP');
1192 continue;
1202 begin
1203 if not findFileCI(pathname) then raise EFileNotFoundException.Create('can''t open file "'+pathname+'"');
1208 var
1210 begin
1213 begin
1214 if not findFileCI(path, true) then raise Exception.Create('can''t create file "'+pathname+'"');
1221 {$IFDEF ENDIAN_LITTLE}
1222 begin
1225 {$ELSE}
1226 var
1228 begin
1231 begin
1237 {$ENDIF}
1240 {$IFDEF ENDIAN_LITTLE}
1241 var
1243 begin
1246 begin
1252 {$ELSE}
1253 begin
1256 {$ENDIF}
1259 begin
1264 var
1267 begin
1270 begin
1272 begin
1275 end
1276 else
1277 begin
1279 begin
1298 procedure writeIntBE (st: TStream; v: ShortInt); overload; begin writeIntegerBE(st, @v, 1); end;
1300 procedure writeIntBE (st: TStream; v: SmallInt); overload; begin writeIntegerBE(st, @v, 2); end;
1301 procedure writeIntBE (st: TStream; v: LongWord); overload; begin writeIntegerBE(st, @v, 4); end;
1302 procedure writeIntBE (st: TStream; v: LongInt); overload; begin writeIntegerBE(st, @v, 4); end;
1311 begin
1313 if (maxlen <= 65535) then writeInt(st, Word(Length(str))) else writeInt(st, LongWord(Length(str)));
1318 var
1320 begin
1325 begin
1333 {$IFDEF ENDIAN_LITTLE}
1334 begin
1337 {$ELSE}
1338 var
1340 begin
1343 begin
1349 {$ENDIF}
1352 {$IFDEF ENDIAN_LITTLE}
1353 var
1355 begin
1358 begin
1364 {$ELSE}
1365 begin
1368 {$ENDIF}
1389 // ////////////////////////////////////////////////////////////////////////// //
1390 function nmin (a, b: Byte): Byte; inline; overload; begin if (a < b) then result := a else result := b; end;
1391 function nmin (a, b: ShortInt): ShortInt; inline; overload; begin if (a < b) then result := a else result := b; end;
1392 function nmin (a, b: Word): Word; inline; overload; begin if (a < b) then result := a else result := b; end;
1393 function nmin (a, b: SmallInt): SmallInt; inline; overload; begin if (a < b) then result := a else result := b; end;
1394 function nmin (a, b: LongWord): LongWord; inline; overload; begin if (a < b) then result := a else result := b; end;
1395 function nmin (a, b: LongInt): LongInt; inline; overload; begin if (a < b) then result := a else result := b; end;
1396 function nmin (a, b: Int64): Int64; inline; overload; begin if (a < b) then result := a else result := b; end;
1397 function nmin (a, b: UInt64): UInt64; inline; overload; begin if (a < b) then result := a else result := b; end;
1398 function nmin (a, b: Single): Single; inline; overload; begin if (a < b) then result := a else result := b; end;
1399 function nmin (a, b: Double): Double; inline; overload; begin if (a < b) then result := a else result := b; end;
1400 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
1401 function nmin (a, b: Extended): Extended; inline; overload; begin if (a < b) then result := a else result := b; end;
1402 {$ENDIF}
1404 function nmax (a, b: Byte): Byte; inline; overload; begin if (a > b) then result := a else result := b; end;
1405 function nmax (a, b: ShortInt): ShortInt; inline; overload; begin if (a > b) then result := a else result := b; end;
1406 function nmax (a, b: Word): Word; inline; overload; begin if (a > b) then result := a else result := b; end;
1407 function nmax (a, b: SmallInt): SmallInt; inline; overload; begin if (a > b) then result := a else result := b; end;
1408 function nmax (a, b: LongWord): LongWord; inline; overload; begin if (a > b) then result := a else result := b; end;
1409 function nmax (a, b: LongInt): LongInt; inline; overload; begin if (a > b) then result := a else result := b; end;
1410 function nmax (a, b: Int64): Int64; inline; overload; begin if (a > b) then result := a else result := b; end;
1411 function nmax (a, b: UInt64): UInt64; inline; overload; begin if (a > b) then result := a else result := b; end;
1412 function nmax (a, b: Single): Single; inline; overload; begin if (a > b) then result := a else result := b; end;
1413 function nmax (a, b: Double): Double; inline; overload; begin if (a > b) then result := a else result := b; end;
1414 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
1415 function nmax (a, b: Extended): Extended; inline; overload; begin if (a > b) then result := a else result := b; end;
1416 {$ENDIF}
1418 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;
1419 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;
1420 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;
1421 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;
1422 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;
1423 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;
1424 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;
1425 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;
1426 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;
1427 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;
1428 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
1429 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;
1430 {$ENDIF}
1432 // ////////////////////////////////////////////////////////////////////////// //
1433 {$IFDEF WINDOWS}
1434 function snprintf (buf: PAnsiChar; bufsize: SizeUInt; const fmt: PAnsiChar): SizeUInt; cdecl; varargs; external 'msvcrt.dll' name '_snprintf';
1435 {$ELSE}
1436 function snprintf (buf: PAnsiChar; bufsize: SizeUInt; const fmt: PAnsiChar): SizeUInt; cdecl; varargs; external 'libc' name 'snprintf';
1437 {$ENDIF}
1440 (*
1441 procedure conwriter (constref buf; len: SizeUInt);
1442 var
1443 ss: ShortString;
1444 slen: Integer;
1445 b: PByte;
1446 begin
1447 if (len < 1) then exit;
1448 b := PByte(@buf);
1449 while (len > 0) do
1450 begin
1451 if (len > 255) then slen := 255 else slen := Integer(len);
1452 Move(b^, ss[1], len);
1453 ss[0] := AnsiChar(slen);
1454 write(ss);
1455 b += slen;
1456 len -= slen;
1457 end;
1458 end;
1459 *)
1462 function formatstrf (const fmt: AnsiString; const args: array of const; writerCB: TFormatStrFCallback=nil): AnsiString;
1463 const
1465 PadZeroes: AnsiString = '00000000000000000000000000000000000000000000000000000000000000000000000';
1466 var
1482 var
1486 begin
1490 begin
1492 end
1493 else
1494 begin
1496 begin
1508 begin
1513 begin
1519 var
1521 begin
1527 var
1529 begin
1535 begin
1539 begin
1549 begin
1554 var
1557 begin
1559 begin
1563 end
1564 else
1565 begin
1570 repeat
1572 begin
1576 end
1577 else
1578 begin
1580 begin
1583 end
1595 var
1597 begin
1600 repeat
1602 begin
1606 end
1607 else
1608 begin
1610 begin
1613 end
1623 var
1625 begin
1627 begin
1635 var
1637 begin
1639 begin
1646 begin
1650 begin
1651 // print literal part
1654 // output literal part
1656 begin
1658 begin
1660 break;
1664 begin
1668 end
1669 else
1670 begin
1674 continue;
1676 // check if we have argument for this format string
1678 begin
1681 break;
1683 // skip percent
1687 // parse format; check for sign
1691 // parse width
1694 begin
1695 if (fmt[spos] < '0') or (fmt[spos] > '9') then begin xwrite('<INVALID FORMAT>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
1699 begin
1705 end
1706 else
1707 begin
1711 // parse precision
1714 begin
1717 if (fmt[spos] < '0') or (fmt[spos] > '9') then begin xwrite('<INVALID FORMAT>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
1720 begin
1727 // get format char
1731 // done parsing format, check for valid format chars
1732 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;
1733 // now write formatted string
1736 begin
1737 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;
1746 begin
1748 if args[curarg].VBoolean then strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], 'true')
1753 begin
1755 if args[curarg].VBoolean then strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], AnsiChar('t'))
1760 begin
1765 else
1766 begin
1769 break;
1775 begin
1782 begin
1787 else
1788 begin
1791 break;
1794 //vtWideChar: begin end; // args[curarg].VWideChar (WideChar)
1798 begin
1805 begin
1811 begin
1813 strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], Integer(trunc(args[curarg].VExtended^)));
1817 begin
1819 strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], LongWord(trunc(args[curarg].VExtended^)));
1822 else
1823 begin
1826 break;
1830 begin
1838 begin
1847 begin
1855 else
1856 begin
1859 break;
1864 begin
1868 end
1869 else
1870 begin
1878 begin
1879 if (args[curarg].VObject <> nil) then ccname := args[curarg].VObject.Classname else ccname := '<nil>';
1885 begin
1886 if (args[curarg].VClass <> nil) then ccname := args[curarg].VClass.Classname else ccname := '<nil>';
1891 //vtPWideChar: begin end; // args[curarg].VPWideChar (PWideChar)
1893 begin
1898 //vtCurrency: begin end; // args[curarg].VCurrency (PCurrency)
1899 //vtVariant: begin end; // args[curarg].VVariant^ (PVariant)
1900 //vtInterface: begin end; // args[curarg].VInterface (Pointer);
1901 //vtWideString: begin end; // args[curarg].VWideString (Pointer);
1903 begin
1908 else begin xwrite('<INVALID FORMAT CHAR>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
1913 begin
1915 begin
1917 begin
1922 end
1923 else
1924 begin
1927 end
1928 else
1929 begin
1937 begin
1942 else begin xwrite('<INVALID FORMAT CHAR>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
1946 if (sign <> '-') then begin if zeropad then indent0(width-pclen) else indent(width-pclen); end;
1950 else
1951 begin
1954 break;
1962 (*
1963 var
1964 ss: ShortString;
1965 ls: AnsiString;
1966 i64: Int64 = -$A000000000;
1967 ui64: UInt64 = $A000000000;
1968 begin
1969 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']);
1970 writef(conwriter, 'test float:<%s;%u;%f;%g>'#10, [666.6942, 666.6942, 666.6942, 666.6942]);
1971 ss := 'fuckit';
1972 ls := 'FUCKIT';
1973 writef(conwriter, 'test ss:<%5s;%040s>'#10, [ss, ss]);
1974 writef(conwriter, 'test ls:<%5s;%040s>'#10, [ls, ls]);
1975 writef(conwriter, 'test pointer:<%s;%x;%p>'#10, [@ss, @ss, @ss]);
1976 writef(conwriter, 'test i64:<%s;%x;%015d;%u;%X>'#10, [i64, i64, i64, i64, i64]);
1977 writef(conwriter, 'test ui64:<%s;%x;%15d;%015u;%X>'#10, [ui64, ui64, ui64, ui64, ui64]);
1978 *)