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, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *)
16 {$INCLUDE a_modes.inc}
19 interface
21 uses
25 // ////////////////////////////////////////////////////////////////////////// //
26 type
30 // ////////////////////////////////////////////////////////////////////////// //
31 type
33 public
38 private
41 public
44 public
53 // process one byte, return `true` if codepoint is ready
59 // ////////////////////////////////////////////////////////////////////////// //
64 // strips out name from `fn`, leaving trailing slash
67 // ends with '/' or '\'?
70 // strips extra trailing slashes in `path, and extra leading slashes in `fn`
71 // will add slash to `path`, even if `fn` is empty!
74 // does filename have one of ".wad", ".pk3", ".zip" extensions?
77 // does filepath have ".XXX:\" in it?
80 // adds ".wad" extension if filename doesn't have one of ".wad", ".pk3", ".zip"
83 // check wad signature
86 // 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
109 // they throws
113 // little endian
141 // big endian
171 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
173 {$ENDIF}
185 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
187 {$ENDIF}
198 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
200 {$ENDIF}
202 type
205 // returns formatted string if `writerCB` is `nil`, empty string otherwise
206 function formatstrf (const fmt: AnsiString; const args: array of const; writerCB: TFormatStrFCallback=nil): AnsiString;
213 // returns string in single or double quotes
214 // single quotes supports only pascal-style '' for single quote char
215 // double quotes supports c-style escapes
216 // function will select quote mode automatically
220 type
222 private
223 //type PItemT = ^ItemT;
226 public
227 type
229 private
233 public
240 private
244 private
251 public
255 //WARNING! don't change list contents in `for ... in`!
265 public
277 implementation
279 uses
280 xstreams;
283 // ////////////////////////////////////////////////////////////////////////// //
285 begin
290 begin
295 begin
300 // ////////////////////////////////////////////////////////////////////////// //
302 begin
309 begin
315 begin
320 // ////////////////////////////////////////////////////////////////////////// //
322 begin
330 begin
337 begin
343 begin
350 begin
357 begin
363 begin
370 begin
376 begin
382 var
384 begin
386 begin
396 var
398 begin
400 begin
407 var
409 begin
411 begin
414 end
415 else
416 begin
422 // ////////////////////////////////////////////////////////////////////////// //
423 var
428 // ////////////////////////////////////////////////////////////////////////// //
429 const
431 $0402,$0403,$201A,$0453,$201E,$2026,$2020,$2021,$20AC,$2030,$0409,$2039,$040A,$040C,$040B,$040F,
432 $0452,$2018,$2019,$201C,$201D,$2022,$2013,$2014,$003F,$2122,$0459,$203A,$045A,$045C,$045B,$045F,
433 $00A0,$040E,$045E,$0408,$00A4,$0490,$00A6,$00A7,$0401,$00A9,$0404,$00AB,$00AC,$00AD,$00AE,$0407,
434 $00B0,$00B1,$0406,$0456,$0491,$00B5,$00B6,$00B7,$0451,$2116,$0454,$00BB,$0458,$0405,$0455,$0457,
435 $0410,$0411,$0412,$0413,$0414,$0415,$0416,$0417,$0418,$0419,$041A,$041B,$041C,$041D,$041E,$041F,
436 $0420,$0421,$0422,$0423,$0424,$0425,$0426,$0427,$0428,$0429,$042A,$042B,$042C,$042D,$042E,$042F,
437 $0430,$0431,$0432,$0433,$0434,$0435,$0436,$0437,$0438,$0439,$043A,$043B,$043C,$043D,$043E,$043F,
439 );
443 var
445 begin
453 // ////////////////////////////////////////////////////////////////////////// //
454 // fast state-machine based UTF-8 decoder; using 8 bytes of memory
455 // code points from invalid range will never be valid, this is the property of the state machine
456 const
457 // see http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
459 // maps bytes to character classes
476 // maps a combination of a state of the automaton and a character class to a state
486 // ////////////////////////////////////////////////////////////////////////// //
487 constructor TUtf8DecoderFast.Create (v: Boolean{fuck you, fpc}); begin state := Accept; codepoint := 0; end;
493 function TUtf8DecoderFast.completeOrInvalid (): Boolean; inline; begin result := (state = Accept) or (state = Reject); end;
495 function TUtf8DecoderFast.decode (c: AnsiChar): Boolean; inline; overload; begin result := decode(Byte(c)); end;
498 var
500 begin
503 if (state <> Accept) then codepoint := (b and $3f) or (codepoint shl 6) else codepoint := ($ff shr tp) and b;
510 // ////////////////////////////////////////////////////////////////////////// //
512 begin
518 // ////////////////////////////////////////////////////////////////////////// //
520 var
523 begin
525 begin
527 begin
531 begin
534 exit;
542 var
546 begin
549 begin
551 end
553 begin
556 end
558 begin
562 end
564 begin
569 end
570 else
571 begin
576 begin
578 begin
580 begin
583 begin
585 begin
587 end
588 else
589 begin
593 exit;
600 // ////////////////////////////////////////////////////////////////////////// //
602 begin
607 begin
610 end
611 else
612 begin
621 // ////////////////////////////////////////////////////////////////////////// //
625 var
627 begin
630 begin
638 var
641 begin
644 begin
652 begin
655 end
657 begin
660 end
661 else
662 begin
669 var
672 begin
674 begin
682 // ////////////////////////////////////////////////////////////////////////// //
684 var
687 begin
690 begin
693 begin
695 exit;
705 var
708 begin
713 begin
725 var
728 begin
732 begin
735 begin
737 begin
739 end
740 else
741 begin
744 exit;
751 begin
758 // strips out name from `fn`, leaving trailing slash
760 var
763 begin
768 begin
777 // ends with '/' or '\'?
779 begin
781 begin
783 end
784 else
785 begin
791 // strips extra trailing slashes in `path, and extra leading slashes in `fn`
792 // will add slash to `path`, even if `fn` is empty!
794 var
796 begin
800 if (Length(result) > 0) and ((result[Length(result)] <> '/') and (result[Length(result)] <> '\')) then result += '/';
802 begin
804 //FIXME: make this faster!
805 while (Length(result) > 0) and ((result[Length(result)] = '/') or (result[Length(result)] = '\')) do
806 begin
815 var
817 begin
819 result := StrEquCI1251(ext, '.wad') or StrEquCI1251(ext, '.pk3') or StrEquCI1251(ext, '.zip') or StrEquCI1251(ext, '.dfz');
824 begin
831 begin
833 Result :=
834 (* ZIP *)
837 (* PACK *)
840 (* DFWAD *)
841 ((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))
846 var
849 begin
853 begin
855 begin
858 begin
860 if StrEquCI1251(s, '.wad') or StrEquCI1251(s, '.pk3') or StrEquCI1251(s, '.zip') or StrEquCI1251(s, '.dfz') then
861 begin
863 exit;
873 var
875 begin
879 begin
886 begin
888 begin
890 end
891 else
892 begin
894 begin
896 end
897 else
898 begin
910 begin
912 begin
914 end
915 else
916 begin
918 begin
920 end
921 else
922 begin
934 var
936 begin
945 var
948 begin
950 begin
952 begin
956 exit;
959 // nothing to do
964 // ////////////////////////////////////////////////////////////////////////// //
965 // utils
966 // `ch`: utf8 start
967 // -1: invalid utf8
969 begin
981 var
983 begin
987 begin
993 // check other sequence bytes
995 begin
1005 // ////////////////////////////////////////////////////////////////////////// //
1006 const
1008 $0402,$0403,$201A,$0453,$201E,$2026,$2020,$2021,$20AC,$2030,$0409,$2039,$040A,$040C,$040B,$040F,
1009 $0452,$2018,$2019,$201C,$201D,$2022,$2013,$2014,$003F,$2122,$0459,$203A,$045A,$045C,$045B,$045F,
1010 $00A0,$040E,$045E,$0408,$00A4,$0490,$00A6,$00A7,$0401,$00A9,$0404,$00AB,$00AC,$00AD,$00AE,$0407,
1011 $00B0,$00B1,$0406,$0456,$0491,$00B5,$00B6,$00B7,$0451,$2116,$0454,$00BB,$0458,$0405,$0455,$0457,
1012 $0410,$0411,$0412,$0413,$0414,$0415,$0416,$0417,$0418,$0419,$041A,$041B,$041C,$041D,$041E,$041F,
1013 $0420,$0421,$0422,$0423,$0424,$0425,$0426,$0427,$0428,$0429,$042A,$042B,$042C,$042D,$042E,$042F,
1014 $0430,$0431,$0432,$0433,$0434,$0435,$0436,$0437,$0438,$0439,$043A,$043B,$043C,$043D,$043E,$043F,
1015 $0440,$0441,$0442,$0443,$0444,$0445,$0446,$0447,$0448,$0449,$044A,$044B,$044C,$044D,$044E,$044F
1016 );
1020 var
1022 begin
1023 (* The following encodings are valid, except for the 5 and 6 byte
1024 * combinations:
1025 * 0xxxxxxx
1026 * 110xxxxx 10xxxxxx
1027 * 1110xxxx 10xxxxxx 10xxxxxx
1028 * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
1029 * 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
1030 * 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
1031 *)
1039 // mask out unused bits
1047 // now continue
1049 begin
1057 // done, try 1251
1059 // alas
1064 var
1066 begin
1070 begin
1081 // ////////////////////////////////////////////////////////////////////////// //
1082 // `pathname` will be modified if path is valid
1083 // `lastIsDir` should be `true` if we are searching for directory
1084 // nobody cares about shitdoze, so i'll use the same code path for it
1086 var
1094 begin
1099 begin
1100 // remove trailing slashes
1103 // extract name
1106 begin
1110 // remove trailing slashes again
1113 //writeln(Format('npt=[%s]; newname=[%s]; curname=[%s]; wantdir=%d', [npt, newname, curname, Integer(wantdir)]));
1114 // try the easiest case first
1117 begin
1119 begin
1120 // i found her!
1123 continue;
1126 //writeln(Format('npt=[%s]; newname=[%s]; curname=[%s]; wantdir=%d', [npt, newname, curname, Integer(wantdir)]));
1127 // alas, either not found, or invalid attributes
1129 try
1131 repeat
1133 begin
1134 // i found her!
1138 break;
1141 finally
1150 const fileExtensions: array [0..6] of AnsiString = ('.dfz', '.wad', '.dfwad', '.pk3', '.pak', '.zip', '.dfzip');
1153 var
1156 begin
1158 //writeln('findDiskWad00: fname=<', fname, '>');
1162 //writeln(' findDiskWad01: fname=<', fname, '>; origExt=<', origExt, '>');
1164 begin
1165 //writeln(' findDiskWad02: fname=<', fname, '>; origExt=<', origExt, '>; newExt=<', newExt, '>');
1167 begin
1168 //writeln(' SKIP');
1169 continue;
1179 begin
1180 if not findFileCI(pathname) then raise EFileNotFoundException.Create('can''t open file "'+pathname+'"');
1185 var
1187 begin
1190 begin
1191 if not findFileCI(path, true) then raise Exception.Create('can''t create file "'+pathname+'"');
1198 {$IFDEF ENDIAN_LITTLE}
1199 begin
1202 {$ELSE}
1203 var
1205 begin
1208 begin
1214 {$ENDIF}
1217 {$IFDEF ENDIAN_LITTLE}
1218 var
1220 begin
1223 begin
1229 {$ELSE}
1230 begin
1233 {$ENDIF}
1236 begin
1241 var
1244 begin
1247 begin
1249 begin
1252 end
1253 else
1254 begin
1256 begin
1275 procedure writeIntBE (st: TStream; v: ShortInt); overload; begin writeIntegerBE(st, @v, 1); end;
1277 procedure writeIntBE (st: TStream; v: SmallInt); overload; begin writeIntegerBE(st, @v, 2); end;
1278 procedure writeIntBE (st: TStream; v: LongWord); overload; begin writeIntegerBE(st, @v, 4); end;
1279 procedure writeIntBE (st: TStream; v: LongInt); overload; begin writeIntegerBE(st, @v, 4); end;
1288 begin
1290 if (maxlen <= 65535) then writeInt(st, Word(Length(str))) else writeInt(st, LongWord(Length(str)));
1295 var
1297 begin
1302 begin
1310 {$IFDEF ENDIAN_LITTLE}
1311 begin
1314 {$ELSE}
1315 var
1317 begin
1320 begin
1326 {$ENDIF}
1329 {$IFDEF ENDIAN_LITTLE}
1330 var
1332 begin
1335 begin
1341 {$ELSE}
1342 begin
1345 {$ENDIF}
1366 // ////////////////////////////////////////////////////////////////////////// //
1367 function nmin (a, b: Byte): Byte; inline; overload; begin if (a < b) then result := a else result := b; end;
1368 function nmin (a, b: ShortInt): ShortInt; inline; overload; begin if (a < b) then result := a else result := b; end;
1369 function nmin (a, b: Word): Word; inline; overload; begin if (a < b) then result := a else result := b; end;
1370 function nmin (a, b: SmallInt): SmallInt; inline; overload; begin if (a < b) then result := a else result := b; end;
1371 function nmin (a, b: LongWord): LongWord; inline; overload; begin if (a < b) then result := a else result := b; end;
1372 function nmin (a, b: LongInt): LongInt; inline; overload; begin if (a < b) then result := a else result := b; end;
1373 function nmin (a, b: Int64): Int64; inline; overload; begin if (a < b) then result := a else result := b; end;
1374 function nmin (a, b: UInt64): UInt64; inline; overload; begin if (a < b) then result := a else result := b; end;
1375 function nmin (a, b: Single): Single; inline; overload; begin if (a < b) then result := a else result := b; end;
1376 function nmin (a, b: Double): Double; inline; overload; begin if (a < b) then result := a else result := b; end;
1377 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
1378 function nmin (a, b: Extended): Extended; inline; overload; begin if (a < b) then result := a else result := b; end;
1379 {$ENDIF}
1381 function nmax (a, b: Byte): Byte; inline; overload; begin if (a > b) then result := a else result := b; end;
1382 function nmax (a, b: ShortInt): ShortInt; inline; overload; begin if (a > b) then result := a else result := b; end;
1383 function nmax (a, b: Word): Word; inline; overload; begin if (a > b) then result := a else result := b; end;
1384 function nmax (a, b: SmallInt): SmallInt; inline; overload; begin if (a > b) then result := a else result := b; end;
1385 function nmax (a, b: LongWord): LongWord; inline; overload; begin if (a > b) then result := a else result := b; end;
1386 function nmax (a, b: LongInt): LongInt; inline; overload; begin if (a > b) then result := a else result := b; end;
1387 function nmax (a, b: Int64): Int64; inline; overload; begin if (a > b) then result := a else result := b; end;
1388 function nmax (a, b: UInt64): UInt64; inline; overload; begin if (a > b) then result := a else result := b; end;
1389 function nmax (a, b: Single): Single; inline; overload; begin if (a > b) then result := a else result := b; end;
1390 function nmax (a, b: Double): Double; inline; overload; begin if (a > b) then result := a else result := b; end;
1391 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
1392 function nmax (a, b: Extended): Extended; inline; overload; begin if (a > b) then result := a else result := b; end;
1393 {$ENDIF}
1395 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;
1396 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;
1397 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;
1398 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;
1399 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;
1400 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;
1401 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;
1402 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;
1403 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;
1404 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;
1405 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
1406 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;
1407 {$ENDIF}
1409 // ////////////////////////////////////////////////////////////////////////// //
1410 {$IFDEF WINDOWS}
1411 function snprintf (buf: PAnsiChar; bufsize: SizeUInt; const fmt: PAnsiChar): SizeUInt; cdecl; varargs; external 'msvcrt.dll' name '_snprintf';
1412 {$ELSE}
1413 function snprintf (buf: PAnsiChar; bufsize: SizeUInt; const fmt: PAnsiChar): SizeUInt; cdecl; varargs; external 'libc' name 'snprintf';
1414 {$ENDIF}
1417 (*
1418 procedure conwriter (constref buf; len: SizeUInt);
1419 var
1420 ss: ShortString;
1421 slen: Integer;
1422 b: PByte;
1423 begin
1424 if (len < 1) then exit;
1425 b := PByte(@buf);
1426 while (len > 0) do
1427 begin
1428 if (len > 255) then slen := 255 else slen := Integer(len);
1429 Move(b^, ss[1], len);
1430 ss[0] := AnsiChar(slen);
1431 write(ss);
1432 b += slen;
1433 len -= slen;
1434 end;
1435 end;
1436 *)
1439 function formatstrf (const fmt: AnsiString; const args: array of const; writerCB: TFormatStrFCallback=nil): AnsiString;
1440 const
1442 PadZeroes: AnsiString = '00000000000000000000000000000000000000000000000000000000000000000000000';
1443 var
1459 var
1463 begin
1467 begin
1469 end
1470 else
1471 begin
1473 begin
1485 begin
1490 begin
1496 var
1498 begin
1504 var
1506 begin
1512 begin
1516 begin
1526 begin
1531 var
1534 begin
1536 begin
1540 end
1541 else
1542 begin
1547 repeat
1549 begin
1553 end
1554 else
1555 begin
1557 begin
1560 end
1572 var
1574 begin
1577 repeat
1579 begin
1583 end
1584 else
1585 begin
1587 begin
1590 end
1600 var
1602 begin
1604 begin
1612 var
1614 begin
1616 begin
1623 begin
1627 begin
1628 // print literal part
1631 // output literal part
1633 begin
1635 begin
1637 break;
1641 begin
1645 end
1646 else
1647 begin
1651 continue;
1653 // check if we have argument for this format string
1655 begin
1658 break;
1660 // skip percent
1664 // parse format; check for sign
1668 // parse width
1671 begin
1672 if (fmt[spos] < '0') or (fmt[spos] > '9') then begin xwrite('<INVALID FORMAT>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
1676 begin
1682 end
1683 else
1684 begin
1688 // parse precision
1691 begin
1694 if (fmt[spos] < '0') or (fmt[spos] > '9') then begin xwrite('<INVALID FORMAT>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
1697 begin
1704 // get format char
1708 // done parsing format, check for valid format chars
1709 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;
1710 // now write formatted string
1713 begin
1714 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;
1723 begin
1725 if args[curarg].VBoolean then strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], 'true')
1730 begin
1732 if args[curarg].VBoolean then strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], AnsiChar('t'))
1737 begin
1742 else
1743 begin
1746 break;
1752 begin
1759 begin
1764 else
1765 begin
1768 break;
1771 //vtWideChar: begin end; // args[curarg].VWideChar (WideChar)
1775 begin
1782 begin
1788 begin
1790 strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], Integer(trunc(args[curarg].VExtended^)));
1794 begin
1796 strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], LongWord(trunc(args[curarg].VExtended^)));
1799 else
1800 begin
1803 break;
1807 begin
1815 begin
1824 begin
1832 else
1833 begin
1836 break;
1841 begin
1845 end
1846 else
1847 begin
1855 begin
1856 if (args[curarg].VObject <> nil) then ccname := args[curarg].VObject.Classname else ccname := '<nil>';
1862 begin
1863 if (args[curarg].VClass <> nil) then ccname := args[curarg].VClass.Classname else ccname := '<nil>';
1868 //vtPWideChar: begin end; // args[curarg].VPWideChar (PWideChar)
1870 begin
1875 //vtCurrency: begin end; // args[curarg].VCurrency (PCurrency)
1876 //vtVariant: begin end; // args[curarg].VVariant^ (PVariant)
1877 //vtInterface: begin end; // args[curarg].VInterface (Pointer);
1878 //vtWideString: begin end; // args[curarg].VWideString (Pointer);
1880 begin
1885 else begin xwrite('<INVALID FORMAT CHAR>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
1890 begin
1892 begin
1894 begin
1899 end
1900 else
1901 begin
1904 end
1905 else
1906 begin
1914 begin
1919 else begin xwrite('<INVALID FORMAT CHAR>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
1923 if (sign <> '-') then begin if zeropad then indent0(width-pclen) else indent(width-pclen); end;
1927 else
1928 begin
1931 break;
1939 (*
1940 var
1941 ss: ShortString;
1942 ls: AnsiString;
1943 i64: Int64 = -$A000000000;
1944 ui64: UInt64 = $A000000000;
1945 begin
1946 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']);
1947 writef(conwriter, 'test float:<%s;%u;%f;%g>'#10, [666.6942, 666.6942, 666.6942, 666.6942]);
1948 ss := 'fuckit';
1949 ls := 'FUCKIT';
1950 writef(conwriter, 'test ss:<%5s;%040s>'#10, [ss, ss]);
1951 writef(conwriter, 'test ls:<%5s;%040s>'#10, [ls, ls]);
1952 writef(conwriter, 'test pointer:<%s;%x;%p>'#10, [@ss, @ss, @ss]);
1953 writef(conwriter, 'test i64:<%s;%x;%015d;%u;%X>'#10, [i64, i64, i64, i64, i64]);
1954 writef(conwriter, 'test ui64:<%s;%x;%15d;%015u;%X>'#10, [ui64, ui64, ui64, ui64, ui64]);
1955 *)