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
108 // they throws
112 // little endian
140 // big endian
170 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
172 {$ENDIF}
184 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
186 {$ENDIF}
197 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
199 {$ENDIF}
201 type
204 // returns formatted string if `writerCB` is `nil`, empty string otherwise
205 function formatstrf (const fmt: AnsiString; const args: array of const; writerCB: TFormatStrFCallback=nil): AnsiString;
212 // returns string in single or double quotes
213 // single quotes supports only pascal-style '' for single quote char
214 // double quotes supports c-style escapes
215 // function will select quote mode automatically
219 type
221 private
222 //type PItemT = ^ItemT;
225 public
226 type
228 private
232 public
239 private
243 private
250 public
254 //WARNING! don't change list contents in `for ... in`!
264 public
276 implementation
278 uses
279 xstreams;
282 // ////////////////////////////////////////////////////////////////////////// //
284 begin
289 begin
294 begin
299 // ////////////////////////////////////////////////////////////////////////// //
301 begin
308 begin
314 begin
319 // ////////////////////////////////////////////////////////////////////////// //
321 begin
329 begin
336 begin
342 begin
349 begin
356 begin
362 begin
369 begin
375 begin
381 var
383 begin
385 begin
395 var
397 begin
399 begin
406 var
408 begin
410 begin
413 end
414 else
415 begin
421 // ////////////////////////////////////////////////////////////////////////// //
422 var
427 // ////////////////////////////////////////////////////////////////////////// //
428 const
430 $0402,$0403,$201A,$0453,$201E,$2026,$2020,$2021,$20AC,$2030,$0409,$2039,$040A,$040C,$040B,$040F,
431 $0452,$2018,$2019,$201C,$201D,$2022,$2013,$2014,$003F,$2122,$0459,$203A,$045A,$045C,$045B,$045F,
432 $00A0,$040E,$045E,$0408,$00A4,$0490,$00A6,$00A7,$0401,$00A9,$0404,$00AB,$00AC,$00AD,$00AE,$0407,
433 $00B0,$00B1,$0406,$0456,$0491,$00B5,$00B6,$00B7,$0451,$2116,$0454,$00BB,$0458,$0405,$0455,$0457,
434 $0410,$0411,$0412,$0413,$0414,$0415,$0416,$0417,$0418,$0419,$041A,$041B,$041C,$041D,$041E,$041F,
435 $0420,$0421,$0422,$0423,$0424,$0425,$0426,$0427,$0428,$0429,$042A,$042B,$042C,$042D,$042E,$042F,
436 $0430,$0431,$0432,$0433,$0434,$0435,$0436,$0437,$0438,$0439,$043A,$043B,$043C,$043D,$043E,$043F,
438 );
442 var
444 begin
452 // ////////////////////////////////////////////////////////////////////////// //
453 // fast state-machine based UTF-8 decoder; using 8 bytes of memory
454 // code points from invalid range will never be valid, this is the property of the state machine
455 const
456 // see http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
458 // maps bytes to character classes
475 // maps a combination of a state of the automaton and a character class to a state
485 // ////////////////////////////////////////////////////////////////////////// //
486 constructor TUtf8DecoderFast.Create (v: Boolean{fuck you, fpc}); begin state := Accept; codepoint := 0; end;
492 function TUtf8DecoderFast.completeOrInvalid (): Boolean; inline; begin result := (state = Accept) or (state = Reject); end;
494 function TUtf8DecoderFast.decode (c: AnsiChar): Boolean; inline; overload; begin result := decode(Byte(c)); end;
497 var
499 begin
502 if (state <> Accept) then codepoint := (b and $3f) or (codepoint shl 6) else codepoint := ($ff shr tp) and b;
509 // ////////////////////////////////////////////////////////////////////////// //
511 begin
517 // ////////////////////////////////////////////////////////////////////////// //
519 var
522 begin
524 begin
526 begin
530 begin
533 exit;
541 var
545 begin
548 begin
550 end
552 begin
555 end
557 begin
561 end
563 begin
568 end
569 else
570 begin
575 begin
577 begin
579 begin
582 begin
584 begin
586 end
587 else
588 begin
592 exit;
599 // ////////////////////////////////////////////////////////////////////////// //
601 begin
606 begin
609 end
610 else
611 begin
620 // ////////////////////////////////////////////////////////////////////////// //
624 var
626 begin
629 begin
637 var
640 begin
643 begin
651 begin
654 end
656 begin
659 end
660 else
661 begin
668 var
671 begin
673 begin
681 // ////////////////////////////////////////////////////////////////////////// //
683 var
686 begin
689 begin
692 begin
694 exit;
704 var
707 begin
712 begin
724 var
727 begin
731 begin
734 begin
736 begin
738 end
739 else
740 begin
743 exit;
750 begin
757 // strips out name from `fn`, leaving trailing slash
759 var
762 begin
767 begin
776 // ends with '/' or '\'?
778 begin
780 begin
782 end
783 else
784 begin
790 // strips extra trailing slashes in `path, and extra leading slashes in `fn`
791 // will add slash to `path`, even if `fn` is empty!
793 var
795 begin
799 if (Length(result) > 0) and ((result[Length(result)] <> '/') and (result[Length(result)] <> '\')) then result += '/';
801 begin
803 //FIXME: make this faster!
804 while (Length(result) > 0) and ((result[Length(result)] = '/') or (result[Length(result)] = '\')) do
805 begin
814 var
816 begin
818 result := StrEquCI1251(ext, '.wad') or StrEquCI1251(ext, '.pk3') or StrEquCI1251(ext, '.zip') or StrEquCI1251(ext, '.dfz');
823 begin
830 begin
832 Result :=
833 (* ZIP *)
836 (* PACK *)
839 (* DFWAD *)
840 ((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))
845 var
848 begin
852 begin
854 begin
857 begin
859 if StrEquCI1251(s, '.wad') or StrEquCI1251(s, '.pk3') or StrEquCI1251(s, '.zip') or StrEquCI1251(s, '.dfz') then
860 begin
862 exit;
872 var
874 begin
878 begin
885 begin
887 begin
889 end
890 else
891 begin
893 begin
895 end
896 else
897 begin
909 begin
911 begin
913 end
914 else
915 begin
917 begin
919 end
920 else
921 begin
933 var
935 begin
944 var
947 begin
949 begin
951 begin
955 exit;
958 // nothing to do
963 // ////////////////////////////////////////////////////////////////////////// //
964 // utils
965 // `ch`: utf8 start
966 // -1: invalid utf8
968 begin
980 var
982 begin
986 begin
992 // check other sequence bytes
994 begin
1004 // ////////////////////////////////////////////////////////////////////////// //
1005 const
1007 $0402,$0403,$201A,$0453,$201E,$2026,$2020,$2021,$20AC,$2030,$0409,$2039,$040A,$040C,$040B,$040F,
1008 $0452,$2018,$2019,$201C,$201D,$2022,$2013,$2014,$003F,$2122,$0459,$203A,$045A,$045C,$045B,$045F,
1009 $00A0,$040E,$045E,$0408,$00A4,$0490,$00A6,$00A7,$0401,$00A9,$0404,$00AB,$00AC,$00AD,$00AE,$0407,
1010 $00B0,$00B1,$0406,$0456,$0491,$00B5,$00B6,$00B7,$0451,$2116,$0454,$00BB,$0458,$0405,$0455,$0457,
1011 $0410,$0411,$0412,$0413,$0414,$0415,$0416,$0417,$0418,$0419,$041A,$041B,$041C,$041D,$041E,$041F,
1012 $0420,$0421,$0422,$0423,$0424,$0425,$0426,$0427,$0428,$0429,$042A,$042B,$042C,$042D,$042E,$042F,
1013 $0430,$0431,$0432,$0433,$0434,$0435,$0436,$0437,$0438,$0439,$043A,$043B,$043C,$043D,$043E,$043F,
1014 $0440,$0441,$0442,$0443,$0444,$0445,$0446,$0447,$0448,$0449,$044A,$044B,$044C,$044D,$044E,$044F
1015 );
1019 var
1021 begin
1022 (* The following encodings are valid, except for the 5 and 6 byte
1023 * combinations:
1024 * 0xxxxxxx
1025 * 110xxxxx 10xxxxxx
1026 * 1110xxxx 10xxxxxx 10xxxxxx
1027 * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
1028 * 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
1029 * 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
1030 *)
1038 // mask out unused bits
1046 // now continue
1048 begin
1056 // done, try 1251
1058 // alas
1063 var
1065 begin
1069 begin
1080 // ////////////////////////////////////////////////////////////////////////// //
1081 // `pathname` will be modified if path is valid
1082 // `lastIsDir` should be `true` if we are searching for directory
1083 // nobody cares about shitdoze, so i'll use the same code path for it
1085 var
1093 begin
1098 begin
1099 // remove trailing slashes
1102 // extract name
1105 begin
1109 // remove trailing slashes again
1112 //writeln(Format('npt=[%s]; newname=[%s]; curname=[%s]; wantdir=%d', [npt, newname, curname, Integer(wantdir)]));
1113 // try the easiest case first
1116 begin
1118 begin
1119 // i found her!
1122 continue;
1125 //writeln(Format('npt=[%s]; newname=[%s]; curname=[%s]; wantdir=%d', [npt, newname, curname, Integer(wantdir)]));
1126 // alas, either not found, or invalid attributes
1128 try
1130 repeat
1132 begin
1133 // i found her!
1137 break;
1140 finally
1149 const fileExtensions: array [0..6] of AnsiString = ('.dfz', '.wad', '.dfwad', '.pk3', '.pak', '.zip', '.dfzip');
1152 var
1155 begin
1157 //writeln('findDiskWad00: fname=<', fname, '>');
1161 //writeln(' findDiskWad01: fname=<', fname, '>; origExt=<', origExt, '>');
1163 begin
1164 //writeln(' findDiskWad02: fname=<', fname, '>; origExt=<', origExt, '>; newExt=<', newExt, '>');
1166 begin
1167 //writeln(' SKIP');
1168 continue;
1178 begin
1179 if not findFileCI(pathname) then raise EFileNotFoundException.Create('can''t open file "'+pathname+'"');
1184 var
1186 begin
1189 begin
1190 if not findFileCI(path, true) then raise Exception.Create('can''t create file "'+pathname+'"');
1197 {$IFDEF ENDIAN_LITTLE}
1198 begin
1201 {$ELSE}
1202 var
1204 begin
1207 begin
1213 {$ENDIF}
1216 {$IFDEF ENDIAN_LITTLE}
1217 var
1219 begin
1222 begin
1228 {$ELSE}
1229 begin
1232 {$ENDIF}
1235 begin
1240 var
1243 begin
1246 begin
1248 begin
1251 end
1252 else
1253 begin
1255 begin
1274 procedure writeIntBE (st: TStream; v: ShortInt); overload; begin writeIntegerBE(st, @v, 1); end;
1276 procedure writeIntBE (st: TStream; v: SmallInt); overload; begin writeIntegerBE(st, @v, 2); end;
1277 procedure writeIntBE (st: TStream; v: LongWord); overload; begin writeIntegerBE(st, @v, 4); end;
1278 procedure writeIntBE (st: TStream; v: LongInt); overload; begin writeIntegerBE(st, @v, 4); end;
1287 begin
1289 if (maxlen <= 65535) then writeInt(st, Word(Length(str))) else writeInt(st, LongWord(Length(str)));
1294 var
1296 begin
1301 begin
1309 {$IFDEF ENDIAN_LITTLE}
1310 begin
1313 {$ELSE}
1314 var
1316 begin
1319 begin
1325 {$ENDIF}
1328 {$IFDEF ENDIAN_LITTLE}
1329 var
1331 begin
1334 begin
1340 {$ELSE}
1341 begin
1344 {$ENDIF}
1365 // ////////////////////////////////////////////////////////////////////////// //
1366 function nmin (a, b: Byte): Byte; inline; overload; begin if (a < b) then result := a else result := b; end;
1367 function nmin (a, b: ShortInt): ShortInt; inline; overload; begin if (a < b) then result := a else result := b; end;
1368 function nmin (a, b: Word): Word; inline; overload; begin if (a < b) then result := a else result := b; end;
1369 function nmin (a, b: SmallInt): SmallInt; inline; overload; begin if (a < b) then result := a else result := b; end;
1370 function nmin (a, b: LongWord): LongWord; inline; overload; begin if (a < b) then result := a else result := b; end;
1371 function nmin (a, b: LongInt): LongInt; inline; overload; begin if (a < b) then result := a else result := b; end;
1372 function nmin (a, b: Int64): Int64; inline; overload; begin if (a < b) then result := a else result := b; end;
1373 function nmin (a, b: UInt64): UInt64; inline; overload; begin if (a < b) then result := a else result := b; end;
1374 function nmin (a, b: Single): Single; inline; overload; begin if (a < b) then result := a else result := b; end;
1375 function nmin (a, b: Double): Double; inline; overload; begin if (a < b) then result := a else result := b; end;
1376 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
1377 function nmin (a, b: Extended): Extended; inline; overload; begin if (a < b) then result := a else result := b; end;
1378 {$ENDIF}
1380 function nmax (a, b: Byte): Byte; inline; overload; begin if (a > b) then result := a else result := b; end;
1381 function nmax (a, b: ShortInt): ShortInt; inline; overload; begin if (a > b) then result := a else result := b; end;
1382 function nmax (a, b: Word): Word; inline; overload; begin if (a > b) then result := a else result := b; end;
1383 function nmax (a, b: SmallInt): SmallInt; inline; overload; begin if (a > b) then result := a else result := b; end;
1384 function nmax (a, b: LongWord): LongWord; inline; overload; begin if (a > b) then result := a else result := b; end;
1385 function nmax (a, b: LongInt): LongInt; inline; overload; begin if (a > b) then result := a else result := b; end;
1386 function nmax (a, b: Int64): Int64; inline; overload; begin if (a > b) then result := a else result := b; end;
1387 function nmax (a, b: UInt64): UInt64; inline; overload; begin if (a > b) then result := a else result := b; end;
1388 function nmax (a, b: Single): Single; inline; overload; begin if (a > b) then result := a else result := b; end;
1389 function nmax (a, b: Double): Double; inline; overload; begin if (a > b) then result := a else result := b; end;
1390 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
1391 function nmax (a, b: Extended): Extended; inline; overload; begin if (a > b) then result := a else result := b; end;
1392 {$ENDIF}
1394 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;
1395 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;
1396 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;
1397 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;
1398 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;
1399 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;
1400 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;
1401 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;
1402 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;
1403 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;
1404 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
1405 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;
1406 {$ENDIF}
1408 // ////////////////////////////////////////////////////////////////////////// //
1409 {$IFDEF WINDOWS}
1410 function snprintf (buf: PAnsiChar; bufsize: SizeUInt; const fmt: PAnsiChar): SizeUInt; cdecl; varargs; external 'msvcrt.dll' name '_snprintf';
1411 {$ELSE}
1412 function snprintf (buf: PAnsiChar; bufsize: SizeUInt; const fmt: PAnsiChar): SizeUInt; cdecl; varargs; external 'libc' name 'snprintf';
1413 {$ENDIF}
1416 (*
1417 procedure conwriter (constref buf; len: SizeUInt);
1418 var
1419 ss: ShortString;
1420 slen: Integer;
1421 b: PByte;
1422 begin
1423 if (len < 1) then exit;
1424 b := PByte(@buf);
1425 while (len > 0) do
1426 begin
1427 if (len > 255) then slen := 255 else slen := Integer(len);
1428 Move(b^, ss[1], len);
1429 ss[0] := AnsiChar(slen);
1430 write(ss);
1431 b += slen;
1432 len -= slen;
1433 end;
1434 end;
1435 *)
1438 function formatstrf (const fmt: AnsiString; const args: array of const; writerCB: TFormatStrFCallback=nil): AnsiString;
1439 const
1441 PadZeroes: AnsiString = '00000000000000000000000000000000000000000000000000000000000000000000000';
1442 var
1458 var
1462 begin
1466 begin
1468 end
1469 else
1470 begin
1472 begin
1484 begin
1489 begin
1495 var
1497 begin
1503 var
1505 begin
1511 begin
1515 begin
1525 begin
1530 var
1533 begin
1535 begin
1539 end
1540 else
1541 begin
1546 repeat
1548 begin
1552 end
1553 else
1554 begin
1556 begin
1559 end
1571 var
1573 begin
1576 repeat
1578 begin
1582 end
1583 else
1584 begin
1586 begin
1589 end
1599 var
1601 begin
1603 begin
1611 var
1613 begin
1615 begin
1622 begin
1626 begin
1627 // print literal part
1630 // output literal part
1632 begin
1634 begin
1636 break;
1640 begin
1644 end
1645 else
1646 begin
1650 continue;
1652 // check if we have argument for this format string
1654 begin
1657 break;
1659 // skip percent
1663 // parse format; check for sign
1667 // parse width
1670 begin
1671 if (fmt[spos] < '0') or (fmt[spos] > '9') then begin xwrite('<INVALID FORMAT>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
1675 begin
1681 end
1682 else
1683 begin
1687 // parse precision
1690 begin
1693 if (fmt[spos] < '0') or (fmt[spos] > '9') then begin xwrite('<INVALID FORMAT>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
1696 begin
1703 // get format char
1707 // done parsing format, check for valid format chars
1708 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;
1709 // now write formatted string
1712 begin
1713 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;
1722 begin
1724 if args[curarg].VBoolean then strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], 'true')
1729 begin
1731 if args[curarg].VBoolean then strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], AnsiChar('t'))
1736 begin
1741 else
1742 begin
1745 break;
1751 begin
1758 begin
1763 else
1764 begin
1767 break;
1770 //vtWideChar: begin end; // args[curarg].VWideChar (WideChar)
1774 begin
1781 begin
1787 begin
1789 strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], Integer(trunc(args[curarg].VExtended^)));
1793 begin
1795 strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], LongWord(trunc(args[curarg].VExtended^)));
1798 else
1799 begin
1802 break;
1806 begin
1814 begin
1823 begin
1831 else
1832 begin
1835 break;
1840 begin
1844 end
1845 else
1846 begin
1854 begin
1855 if (args[curarg].VObject <> nil) then ccname := args[curarg].VObject.Classname else ccname := '<nil>';
1861 begin
1862 if (args[curarg].VClass <> nil) then ccname := args[curarg].VClass.Classname else ccname := '<nil>';
1867 //vtPWideChar: begin end; // args[curarg].VPWideChar (PWideChar)
1869 begin
1874 //vtCurrency: begin end; // args[curarg].VCurrency (PCurrency)
1875 //vtVariant: begin end; // args[curarg].VVariant^ (PVariant)
1876 //vtInterface: begin end; // args[curarg].VInterface (Pointer);
1877 //vtWideString: begin end; // args[curarg].VWideString (Pointer);
1879 begin
1884 else begin xwrite('<INVALID FORMAT CHAR>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
1889 begin
1891 begin
1893 begin
1898 end
1899 else
1900 begin
1903 end
1904 else
1905 begin
1913 begin
1918 else begin xwrite('<INVALID FORMAT CHAR>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
1922 if (sign <> '-') then begin if zeropad then indent0(width-pclen) else indent(width-pclen); end;
1926 else
1927 begin
1930 break;
1938 (*
1939 var
1940 ss: ShortString;
1941 ls: AnsiString;
1942 i64: Int64 = -$A000000000;
1943 ui64: UInt64 = $A000000000;
1944 begin
1945 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']);
1946 writef(conwriter, 'test float:<%s;%u;%f;%g>'#10, [666.6942, 666.6942, 666.6942, 666.6942]);
1947 ss := 'fuckit';
1948 ls := 'FUCKIT';
1949 writef(conwriter, 'test ss:<%5s;%040s>'#10, [ss, ss]);
1950 writef(conwriter, 'test ls:<%5s;%040s>'#10, [ls, ls]);
1951 writef(conwriter, 'test pointer:<%s;%x;%p>'#10, [@ss, @ss, @ss]);
1952 writef(conwriter, 'test i64:<%s;%x;%015d;%u;%X>'#10, [i64, i64, i64, i64, i64]);
1953 writef(conwriter, 'test ui64:<%s;%x;%15d;%015u;%X>'#10, [ui64, ui64, ui64, ui64, ui64]);
1954 *)