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 // convert number to strig with nice commas
91 // `true` if strings are equal; ignoring case for cp1251
98 // `pathname` will be modified if path is valid
99 // `lastIsDir` should be `true` if we are searching for directory
100 // nobody cares about shitdoze, so i'll use the same code path for it
103 // return fixed AnsiString or empty AnsiString
106 // they throws
110 // little endian
138 // big endian
168 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
170 {$ENDIF}
182 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
184 {$ENDIF}
195 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
197 {$ENDIF}
199 type
202 // returns formatted string if `writerCB` is `nil`, empty string otherwise
203 function formatstrf (const fmt: AnsiString; const args: array of const; writerCB: TFormatStrFCallback=nil): AnsiString;
210 // returns string in single or double quotes
211 // single quotes supports only pascal-style '' for single quote char
212 // double quotes supports c-style escapes
213 // function will select quote mode automatically
217 type
219 private
220 //type PItemT = ^ItemT;
223 public
224 type
226 private
230 public
237 private
241 private
248 public
252 //WARNING! don't change list contents in `for ... in`!
262 public
274 implementation
276 uses
277 xstreams;
280 // ////////////////////////////////////////////////////////////////////////// //
282 begin
287 begin
292 begin
297 // ////////////////////////////////////////////////////////////////////////// //
299 begin
306 begin
312 begin
317 // ////////////////////////////////////////////////////////////////////////// //
319 begin
327 begin
334 begin
340 begin
347 begin
354 begin
360 begin
367 begin
373 begin
379 var
381 begin
383 begin
393 var
395 begin
397 begin
404 var
406 begin
408 begin
411 end
412 else
413 begin
419 // ////////////////////////////////////////////////////////////////////////// //
420 var
425 // ////////////////////////////////////////////////////////////////////////// //
426 const
428 $0402,$0403,$201A,$0453,$201E,$2026,$2020,$2021,$20AC,$2030,$0409,$2039,$040A,$040C,$040B,$040F,
429 $0452,$2018,$2019,$201C,$201D,$2022,$2013,$2014,$003F,$2122,$0459,$203A,$045A,$045C,$045B,$045F,
430 $00A0,$040E,$045E,$0408,$00A4,$0490,$00A6,$00A7,$0401,$00A9,$0404,$00AB,$00AC,$00AD,$00AE,$0407,
431 $00B0,$00B1,$0406,$0456,$0491,$00B5,$00B6,$00B7,$0451,$2116,$0454,$00BB,$0458,$0405,$0455,$0457,
432 $0410,$0411,$0412,$0413,$0414,$0415,$0416,$0417,$0418,$0419,$041A,$041B,$041C,$041D,$041E,$041F,
433 $0420,$0421,$0422,$0423,$0424,$0425,$0426,$0427,$0428,$0429,$042A,$042B,$042C,$042D,$042E,$042F,
434 $0430,$0431,$0432,$0433,$0434,$0435,$0436,$0437,$0438,$0439,$043A,$043B,$043C,$043D,$043E,$043F,
436 );
440 var
442 begin
450 // ////////////////////////////////////////////////////////////////////////// //
451 // fast state-machine based UTF-8 decoder; using 8 bytes of memory
452 // code points from invalid range will never be valid, this is the property of the state machine
453 const
454 // see http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
456 // maps bytes to character classes
473 // maps a combination of a state of the automaton and a character class to a state
483 // ////////////////////////////////////////////////////////////////////////// //
484 constructor TUtf8DecoderFast.Create (v: Boolean{fuck you, fpc}); begin state := Accept; codepoint := 0; end;
490 function TUtf8DecoderFast.completeOrInvalid (): Boolean; inline; begin result := (state = Accept) or (state = Reject); end;
492 function TUtf8DecoderFast.decode (c: AnsiChar): Boolean; inline; overload; begin result := decode(Byte(c)); end;
495 var
497 begin
500 if (state <> Accept) then codepoint := (b and $3f) or (codepoint shl 6) else codepoint := ($ff shr tp) and b;
507 // ////////////////////////////////////////////////////////////////////////// //
509 begin
515 // ////////////////////////////////////////////////////////////////////////// //
517 var
520 begin
522 begin
524 begin
528 begin
531 exit;
539 var
543 begin
546 begin
548 end
550 begin
553 end
555 begin
559 end
561 begin
566 end
567 else
568 begin
573 begin
575 begin
577 begin
580 begin
582 begin
584 end
585 else
586 begin
590 exit;
597 // ////////////////////////////////////////////////////////////////////////// //
599 begin
604 begin
607 end
608 else
609 begin
618 // ////////////////////////////////////////////////////////////////////////// //
622 var
624 begin
627 begin
635 var
638 begin
641 begin
649 begin
652 end
654 begin
657 end
658 else
659 begin
666 var
669 begin
671 begin
679 // ////////////////////////////////////////////////////////////////////////// //
681 var
684 begin
687 begin
690 begin
692 exit;
702 var
705 begin
710 begin
722 var
725 begin
729 begin
732 begin
734 begin
736 end
737 else
738 begin
741 exit;
748 begin
755 // strips out name from `fn`, leaving trailing slash
757 var
760 begin
765 begin
774 // ends with '/' or '\'?
776 begin
778 begin
780 end
781 else
782 begin
788 // strips extra trailing slashes in `path, and extra leading slashes in `fn`
789 // will add slash to `path`, even if `fn` is empty!
791 var
793 begin
797 if (Length(result) > 0) and ((result[Length(result)] <> '/') and (result[Length(result)] <> '\')) then result += '/';
799 begin
801 //FIXME: make this faster!
802 while (Length(result) > 0) and ((result[Length(result)] = '/') or (result[Length(result)] = '\')) do
803 begin
812 var
814 begin
816 result := StrEquCI1251(ext, '.wad') or StrEquCI1251(ext, '.pk3') or StrEquCI1251(ext, '.zip') or StrEquCI1251(ext, '.dfz');
821 begin
828 var
831 begin
835 begin
837 begin
840 begin
842 if StrEquCI1251(s, '.wad') or StrEquCI1251(s, '.pk3') or StrEquCI1251(s, '.zip') or StrEquCI1251(s, '.dfz') then
843 begin
845 exit;
855 var
857 begin
861 begin
868 begin
870 begin
872 end
873 else
874 begin
876 begin
878 end
879 else
880 begin
892 begin
894 begin
896 end
897 else
898 begin
900 begin
902 end
903 else
904 begin
916 var
918 begin
927 var
930 begin
932 begin
934 begin
938 exit;
941 // nothing to do
946 // ////////////////////////////////////////////////////////////////////////// //
947 // utils
948 // `ch`: utf8 start
949 // -1: invalid utf8
951 begin
963 var
965 begin
969 begin
975 // check other sequence bytes
977 begin
987 // ////////////////////////////////////////////////////////////////////////// //
988 const
990 $0402,$0403,$201A,$0453,$201E,$2026,$2020,$2021,$20AC,$2030,$0409,$2039,$040A,$040C,$040B,$040F,
991 $0452,$2018,$2019,$201C,$201D,$2022,$2013,$2014,$003F,$2122,$0459,$203A,$045A,$045C,$045B,$045F,
992 $00A0,$040E,$045E,$0408,$00A4,$0490,$00A6,$00A7,$0401,$00A9,$0404,$00AB,$00AC,$00AD,$00AE,$0407,
993 $00B0,$00B1,$0406,$0456,$0491,$00B5,$00B6,$00B7,$0451,$2116,$0454,$00BB,$0458,$0405,$0455,$0457,
994 $0410,$0411,$0412,$0413,$0414,$0415,$0416,$0417,$0418,$0419,$041A,$041B,$041C,$041D,$041E,$041F,
995 $0420,$0421,$0422,$0423,$0424,$0425,$0426,$0427,$0428,$0429,$042A,$042B,$042C,$042D,$042E,$042F,
996 $0430,$0431,$0432,$0433,$0434,$0435,$0436,$0437,$0438,$0439,$043A,$043B,$043C,$043D,$043E,$043F,
998 );
1002 var
1004 begin
1005 (* The following encodings are valid, except for the 5 and 6 byte
1006 * combinations:
1007 * 0xxxxxxx
1008 * 110xxxxx 10xxxxxx
1009 * 1110xxxx 10xxxxxx 10xxxxxx
1010 * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
1011 * 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
1012 * 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
1013 *)
1021 // mask out unused bits
1029 // now continue
1031 begin
1039 // done, try 1251
1041 // alas
1046 var
1048 begin
1052 begin
1063 // ////////////////////////////////////////////////////////////////////////// //
1064 // `pathname` will be modified if path is valid
1065 // `lastIsDir` should be `true` if we are searching for directory
1066 // nobody cares about shitdoze, so i'll use the same code path for it
1068 var
1076 begin
1081 begin
1082 // remove trailing slashes
1085 // extract name
1088 begin
1092 // remove trailing slashes again
1095 //writeln(Format('npt=[%s]; newname=[%s]; curname=[%s]; wantdir=%d', [npt, newname, curname, Integer(wantdir)]));
1096 // try the easiest case first
1099 begin
1101 begin
1102 // i found her!
1105 continue;
1108 //writeln(Format('npt=[%s]; newname=[%s]; curname=[%s]; wantdir=%d', [npt, newname, curname, Integer(wantdir)]));
1109 // alas, either not found, or invalid attributes
1111 try
1113 repeat
1115 begin
1116 // i found her!
1120 break;
1123 finally
1132 const fileExtensions: array [0..6] of AnsiString = ('.dfz', '.wad', '.dfwad', '.pk3', '.pak', '.zip', '.dfzip');
1135 var
1138 begin
1140 //writeln('findDiskWad00: fname=<', fname, '>');
1144 //writeln(' findDiskWad01: fname=<', fname, '>; origExt=<', origExt, '>');
1146 begin
1147 //writeln(' findDiskWad02: fname=<', fname, '>; origExt=<', origExt, '>; newExt=<', newExt, '>');
1149 begin
1150 //writeln(' SKIP');
1151 continue;
1161 begin
1162 if not findFileCI(pathname) then raise EFileNotFoundException.Create('can''t open file "'+pathname+'"');
1167 var
1169 begin
1172 begin
1173 if not findFileCI(path, true) then raise Exception.Create('can''t create file "'+pathname+'"');
1180 {$IFDEF ENDIAN_LITTLE}
1181 begin
1184 {$ELSE}
1185 var
1187 begin
1190 begin
1196 {$ENDIF}
1199 {$IFDEF ENDIAN_LITTLE}
1200 var
1202 begin
1205 begin
1211 {$ELSE}
1212 begin
1215 {$ENDIF}
1218 begin
1223 var
1226 begin
1229 begin
1231 begin
1234 end
1235 else
1236 begin
1238 begin
1257 procedure writeIntBE (st: TStream; v: ShortInt); overload; begin writeIntegerBE(st, @v, 1); end;
1259 procedure writeIntBE (st: TStream; v: SmallInt); overload; begin writeIntegerBE(st, @v, 2); end;
1260 procedure writeIntBE (st: TStream; v: LongWord); overload; begin writeIntegerBE(st, @v, 4); end;
1261 procedure writeIntBE (st: TStream; v: LongInt); overload; begin writeIntegerBE(st, @v, 4); end;
1270 begin
1272 if (maxlen <= 65535) then writeInt(st, Word(Length(str))) else writeInt(st, LongWord(Length(str)));
1277 var
1279 begin
1284 begin
1292 {$IFDEF ENDIAN_LITTLE}
1293 begin
1296 {$ELSE}
1297 var
1299 begin
1302 begin
1308 {$ENDIF}
1311 {$IFDEF ENDIAN_LITTLE}
1312 var
1314 begin
1317 begin
1323 {$ELSE}
1324 begin
1327 {$ENDIF}
1348 // ////////////////////////////////////////////////////////////////////////// //
1349 function nmin (a, b: Byte): Byte; inline; overload; begin if (a < b) then result := a else result := b; end;
1350 function nmin (a, b: ShortInt): ShortInt; inline; overload; begin if (a < b) then result := a else result := b; end;
1351 function nmin (a, b: Word): Word; inline; overload; begin if (a < b) then result := a else result := b; end;
1352 function nmin (a, b: SmallInt): SmallInt; inline; overload; begin if (a < b) then result := a else result := b; end;
1353 function nmin (a, b: LongWord): LongWord; inline; overload; begin if (a < b) then result := a else result := b; end;
1354 function nmin (a, b: LongInt): LongInt; inline; overload; begin if (a < b) then result := a else result := b; end;
1355 function nmin (a, b: Int64): Int64; inline; overload; begin if (a < b) then result := a else result := b; end;
1356 function nmin (a, b: UInt64): UInt64; inline; overload; begin if (a < b) then result := a else result := b; end;
1357 function nmin (a, b: Single): Single; inline; overload; begin if (a < b) then result := a else result := b; end;
1358 function nmin (a, b: Double): Double; inline; overload; begin if (a < b) then result := a else result := b; end;
1359 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
1360 function nmin (a, b: Extended): Extended; inline; overload; begin if (a < b) then result := a else result := b; end;
1361 {$ENDIF}
1363 function nmax (a, b: Byte): Byte; inline; overload; begin if (a > b) then result := a else result := b; end;
1364 function nmax (a, b: ShortInt): ShortInt; inline; overload; begin if (a > b) then result := a else result := b; end;
1365 function nmax (a, b: Word): Word; inline; overload; begin if (a > b) then result := a else result := b; end;
1366 function nmax (a, b: SmallInt): SmallInt; inline; overload; begin if (a > b) then result := a else result := b; end;
1367 function nmax (a, b: LongWord): LongWord; inline; overload; begin if (a > b) then result := a else result := b; end;
1368 function nmax (a, b: LongInt): LongInt; inline; overload; begin if (a > b) then result := a else result := b; end;
1369 function nmax (a, b: Int64): Int64; inline; overload; begin if (a > b) then result := a else result := b; end;
1370 function nmax (a, b: UInt64): UInt64; inline; overload; begin if (a > b) then result := a else result := b; end;
1371 function nmax (a, b: Single): Single; inline; overload; begin if (a > b) then result := a else result := b; end;
1372 function nmax (a, b: Double): Double; inline; overload; begin if (a > b) then result := a else result := b; end;
1373 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
1374 function nmax (a, b: Extended): Extended; inline; overload; begin if (a > b) then result := a else result := b; end;
1375 {$ENDIF}
1377 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;
1378 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;
1379 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;
1380 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;
1381 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;
1382 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;
1383 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;
1384 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;
1385 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;
1386 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;
1387 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
1388 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;
1389 {$ENDIF}
1391 // ////////////////////////////////////////////////////////////////////////// //
1392 {$IFDEF WINDOWS}
1393 function snprintf (buf: PAnsiChar; bufsize: SizeUInt; const fmt: PAnsiChar): SizeUInt; cdecl; varargs; external 'msvcrt.dll' name '_snprintf';
1394 {$ELSE}
1395 function snprintf (buf: PAnsiChar; bufsize: SizeUInt; const fmt: PAnsiChar): SizeUInt; cdecl; varargs; external 'libc' name 'snprintf';
1396 {$ENDIF}
1399 (*
1400 procedure conwriter (constref buf; len: SizeUInt);
1401 var
1402 ss: ShortString;
1403 slen: Integer;
1404 b: PByte;
1405 begin
1406 if (len < 1) then exit;
1407 b := PByte(@buf);
1408 while (len > 0) do
1409 begin
1410 if (len > 255) then slen := 255 else slen := Integer(len);
1411 Move(b^, ss[1], len);
1412 ss[0] := AnsiChar(slen);
1413 write(ss);
1414 b += slen;
1415 len -= slen;
1416 end;
1417 end;
1418 *)
1421 function formatstrf (const fmt: AnsiString; const args: array of const; writerCB: TFormatStrFCallback=nil): AnsiString;
1422 const
1424 PadZeroes: AnsiString = '00000000000000000000000000000000000000000000000000000000000000000000000';
1425 var
1441 var
1445 begin
1449 begin
1451 end
1452 else
1453 begin
1455 begin
1467 begin
1472 begin
1478 var
1480 begin
1486 var
1488 begin
1494 begin
1498 begin
1508 begin
1513 var
1516 begin
1518 begin
1522 end
1523 else
1524 begin
1529 repeat
1531 begin
1535 end
1536 else
1537 begin
1539 begin
1542 end
1554 var
1556 begin
1559 repeat
1561 begin
1565 end
1566 else
1567 begin
1569 begin
1572 end
1582 var
1584 begin
1586 begin
1594 var
1596 begin
1598 begin
1605 begin
1609 begin
1610 // print literal part
1613 // output literal part
1615 begin
1617 begin
1619 break;
1623 begin
1627 end
1628 else
1629 begin
1633 continue;
1635 // check if we have argument for this format string
1637 begin
1640 break;
1642 // skip percent
1646 // parse format; check for sign
1650 // parse width
1653 begin
1654 if (fmt[spos] < '0') or (fmt[spos] > '9') then begin xwrite('<INVALID FORMAT>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
1658 begin
1664 end
1665 else
1666 begin
1670 // parse precision
1673 begin
1676 if (fmt[spos] < '0') or (fmt[spos] > '9') then begin xwrite('<INVALID FORMAT>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
1679 begin
1686 // get format char
1690 // done parsing format, check for valid format chars
1691 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;
1692 // now write formatted string
1695 begin
1696 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;
1705 begin
1707 if args[curarg].VBoolean then strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], 'true')
1712 begin
1714 if args[curarg].VBoolean then strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], AnsiChar('t'))
1719 begin
1724 else
1725 begin
1728 break;
1734 begin
1741 begin
1746 else
1747 begin
1750 break;
1753 //vtWideChar: begin end; // args[curarg].VWideChar (WideChar)
1757 begin
1764 begin
1770 begin
1772 strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], Integer(trunc(args[curarg].VExtended^)));
1776 begin
1778 strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], LongWord(trunc(args[curarg].VExtended^)));
1781 else
1782 begin
1785 break;
1789 begin
1797 begin
1806 begin
1814 else
1815 begin
1818 break;
1823 begin
1827 end
1828 else
1829 begin
1837 begin
1838 if (args[curarg].VObject <> nil) then ccname := args[curarg].VObject.Classname else ccname := '<nil>';
1844 begin
1845 if (args[curarg].VClass <> nil) then ccname := args[curarg].VClass.Classname else ccname := '<nil>';
1850 //vtPWideChar: begin end; // args[curarg].VPWideChar (PWideChar)
1852 begin
1857 //vtCurrency: begin end; // args[curarg].VCurrency (PCurrency)
1858 //vtVariant: begin end; // args[curarg].VVariant^ (PVariant)
1859 //vtInterface: begin end; // args[curarg].VInterface (Pointer);
1860 //vtWideString: begin end; // args[curarg].VWideString (Pointer);
1862 begin
1867 else begin xwrite('<INVALID FORMAT CHAR>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
1872 begin
1874 begin
1876 begin
1881 end
1882 else
1883 begin
1886 end
1887 else
1888 begin
1896 begin
1901 else begin xwrite('<INVALID FORMAT CHAR>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
1905 if (sign <> '-') then begin if zeropad then indent0(width-pclen) else indent(width-pclen); end;
1909 else
1910 begin
1913 break;
1921 (*
1922 var
1923 ss: ShortString;
1924 ls: AnsiString;
1925 i64: Int64 = -$A000000000;
1926 ui64: UInt64 = $A000000000;
1927 begin
1928 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']);
1929 writef(conwriter, 'test float:<%s;%u;%f;%g>'#10, [666.6942, 666.6942, 666.6942, 666.6942]);
1930 ss := 'fuckit';
1931 ls := 'FUCKIT';
1932 writef(conwriter, 'test ss:<%5s;%040s>'#10, [ss, ss]);
1933 writef(conwriter, 'test ls:<%5s;%040s>'#10, [ls, ls]);
1934 writef(conwriter, 'test pointer:<%s;%x;%p>'#10, [@ss, @ss, @ss]);
1935 writef(conwriter, 'test i64:<%s;%x;%015d;%u;%X>'#10, [i64, i64, i64, i64, i64]);
1936 writef(conwriter, 'test ui64:<%s;%x;%15d;%015u;%X>'#10, [ui64, ui64, ui64, ui64, ui64]);
1937 *)