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
279 const
280 {$IFDEF GO32V2}
282 {$ELSE}
284 {$ENDIF}
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 += DirSep;
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 var
837 begin
841 begin
843 begin
846 begin
848 if StrEquCI1251(s, '.wad') or StrEquCI1251(s, '.pk3') or StrEquCI1251(s, '.zip') or StrEquCI1251(s, '.dfz') then
849 begin
851 exit;
861 var
863 begin
867 begin
874 begin
876 begin
878 end
879 else
880 begin
882 begin
884 end
885 else
886 begin
898 begin
900 begin
902 end
903 else
904 begin
906 begin
908 end
909 else
910 begin
922 var
924 begin
933 var
936 begin
938 begin
940 begin
944 exit;
947 // nothing to do
952 // ////////////////////////////////////////////////////////////////////////// //
953 // utils
954 // `ch`: utf8 start
955 // -1: invalid utf8
957 begin
969 var
971 begin
975 begin
981 // check other sequence bytes
983 begin
993 // ////////////////////////////////////////////////////////////////////////// //
994 const
996 $0402,$0403,$201A,$0453,$201E,$2026,$2020,$2021,$20AC,$2030,$0409,$2039,$040A,$040C,$040B,$040F,
997 $0452,$2018,$2019,$201C,$201D,$2022,$2013,$2014,$003F,$2122,$0459,$203A,$045A,$045C,$045B,$045F,
998 $00A0,$040E,$045E,$0408,$00A4,$0490,$00A6,$00A7,$0401,$00A9,$0404,$00AB,$00AC,$00AD,$00AE,$0407,
999 $00B0,$00B1,$0406,$0456,$0491,$00B5,$00B6,$00B7,$0451,$2116,$0454,$00BB,$0458,$0405,$0455,$0457,
1000 $0410,$0411,$0412,$0413,$0414,$0415,$0416,$0417,$0418,$0419,$041A,$041B,$041C,$041D,$041E,$041F,
1001 $0420,$0421,$0422,$0423,$0424,$0425,$0426,$0427,$0428,$0429,$042A,$042B,$042C,$042D,$042E,$042F,
1002 $0430,$0431,$0432,$0433,$0434,$0435,$0436,$0437,$0438,$0439,$043A,$043B,$043C,$043D,$043E,$043F,
1003 $0440,$0441,$0442,$0443,$0444,$0445,$0446,$0447,$0448,$0449,$044A,$044B,$044C,$044D,$044E,$044F
1004 );
1008 var
1010 begin
1011 (* The following encodings are valid, except for the 5 and 6 byte
1012 * combinations:
1013 * 0xxxxxxx
1014 * 110xxxxx 10xxxxxx
1015 * 1110xxxx 10xxxxxx 10xxxxxx
1016 * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
1017 * 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
1018 * 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
1019 *)
1027 // mask out unused bits
1035 // now continue
1037 begin
1045 // done, try 1251
1047 // alas
1052 var
1054 begin
1058 begin
1069 // ////////////////////////////////////////////////////////////////////////// //
1070 // `pathname` will be modified if path is valid
1071 // `lastIsDir` should be `true` if we are searching for directory
1072 // nobody cares about shitdoze, so i'll use the same code path for it
1074 var
1082 begin
1087 begin
1088 // remove trailing slashes
1091 // extract name
1094 begin
1098 // remove trailing slashes again
1101 //e_LogWritefln('npt=[%s]; newname=[%s]; curname=[%s]; wantdir=%d', [npt, newname, curname, Integer(wantdir)]);
1102 // try the easiest case first
1105 begin
1107 begin
1108 // i found her!
1111 continue;
1114 //e_LogWritefLn('npt=[%s]; newname=[%s]; curname=[%s]; wantdir=%d', [npt, newname, curname, Integer(wantdir)]);
1115 // alas, either not found, or invalid attributes
1117 try
1119 repeat
1121 begin
1122 // i found her!
1126 break;
1129 finally
1138 (** Replace slashes to backslashes for DOS **)
1140 begin
1141 {$IFDEF GO32V2}
1143 {$ELSE}
1144 Result := filename
1145 {$ENDIF}
1149 const fileExtensions: array [0..6] of AnsiString = ('.wad', '.dfzip', '.dfwad', '.pk3', '.pak', '.zip', '.dfz');
1152 var
1155 begin
1157 {$IFDEF GO32V2}
1158 // FIXIT: it didn't work under MSDOS for some reason, so i just cut extension replacement
1160 {$ELSE}
1161 //e_LogWriteLn('findDiskWad00: fname=<' + fname + '>');
1165 //e_LogWriteLn(' findDiskWad01: fname=<' + fname + '>; origExt=<' + origExt + '>');
1167 begin
1168 //e_LogWriteLn(' findDiskWad02: fname=<' + fname + '>; origExt=<' + origExt + '>; newExt=<' + newExt + '>');
1170 begin
1171 //e_LogWriteLn(' SKIP');
1172 continue;
1178 {$ENDIF}
1182 begin
1189 var
1191 begin
1195 begin
1196 if not findFileCI(path, true) then raise Exception.Create('can''t create file "'+pathname+'"');
1203 {$IFDEF ENDIAN_LITTLE}
1204 begin
1207 {$ELSE}
1208 var
1210 begin
1213 begin
1219 {$ENDIF}
1222 {$IFDEF ENDIAN_LITTLE}
1223 var
1225 begin
1228 begin
1234 {$ELSE}
1235 begin
1238 {$ENDIF}
1241 begin
1246 var
1249 begin
1252 begin
1254 begin
1257 end
1258 else
1259 begin
1261 begin
1280 procedure writeIntBE (st: TStream; v: ShortInt); overload; begin writeIntegerBE(st, @v, 1); end;
1282 procedure writeIntBE (st: TStream; v: SmallInt); overload; begin writeIntegerBE(st, @v, 2); end;
1283 procedure writeIntBE (st: TStream; v: LongWord); overload; begin writeIntegerBE(st, @v, 4); end;
1284 procedure writeIntBE (st: TStream; v: LongInt); overload; begin writeIntegerBE(st, @v, 4); end;
1293 begin
1295 if (maxlen <= 65535) then writeInt(st, Word(Length(str))) else writeInt(st, LongWord(Length(str)));
1300 var
1302 begin
1307 begin
1315 {$IFDEF ENDIAN_LITTLE}
1316 begin
1319 {$ELSE}
1320 var
1322 begin
1325 begin
1331 {$ENDIF}
1334 {$IFDEF ENDIAN_LITTLE}
1335 var
1337 begin
1340 begin
1346 {$ELSE}
1347 begin
1350 {$ENDIF}
1371 // ////////////////////////////////////////////////////////////////////////// //
1372 function nmin (a, b: Byte): Byte; inline; overload; begin if (a < b) then result := a else result := b; end;
1373 function nmin (a, b: ShortInt): ShortInt; inline; overload; begin if (a < b) then result := a else result := b; end;
1374 function nmin (a, b: Word): Word; inline; overload; begin if (a < b) then result := a else result := b; end;
1375 function nmin (a, b: SmallInt): SmallInt; inline; overload; begin if (a < b) then result := a else result := b; end;
1376 function nmin (a, b: LongWord): LongWord; inline; overload; begin if (a < b) then result := a else result := b; end;
1377 function nmin (a, b: LongInt): LongInt; inline; overload; begin if (a < b) then result := a else result := b; end;
1378 function nmin (a, b: Int64): Int64; inline; overload; begin if (a < b) then result := a else result := b; end;
1379 function nmin (a, b: UInt64): UInt64; inline; overload; begin if (a < b) then result := a else result := b; end;
1380 function nmin (a, b: Single): Single; inline; overload; begin if (a < b) then result := a else result := b; end;
1381 function nmin (a, b: Double): Double; inline; overload; begin if (a < b) then result := a else result := b; end;
1382 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
1383 function nmin (a, b: Extended): Extended; inline; overload; begin if (a < b) then result := a else result := b; end;
1384 {$ENDIF}
1386 function nmax (a, b: Byte): Byte; inline; overload; begin if (a > b) then result := a else result := b; end;
1387 function nmax (a, b: ShortInt): ShortInt; inline; overload; begin if (a > b) then result := a else result := b; end;
1388 function nmax (a, b: Word): Word; inline; overload; begin if (a > b) then result := a else result := b; end;
1389 function nmax (a, b: SmallInt): SmallInt; inline; overload; begin if (a > b) then result := a else result := b; end;
1390 function nmax (a, b: LongWord): LongWord; inline; overload; begin if (a > b) then result := a else result := b; end;
1391 function nmax (a, b: LongInt): LongInt; inline; overload; begin if (a > b) then result := a else result := b; end;
1392 function nmax (a, b: Int64): Int64; inline; overload; begin if (a > b) then result := a else result := b; end;
1393 function nmax (a, b: UInt64): UInt64; inline; overload; begin if (a > b) then result := a else result := b; end;
1394 function nmax (a, b: Single): Single; inline; overload; begin if (a > b) then result := a else result := b; end;
1395 function nmax (a, b: Double): Double; inline; overload; begin if (a > b) then result := a else result := b; end;
1396 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
1397 function nmax (a, b: Extended): Extended; inline; overload; begin if (a > b) then result := a else result := b; end;
1398 {$ENDIF}
1400 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;
1401 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;
1402 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;
1403 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;
1404 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;
1405 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;
1406 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;
1407 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;
1408 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;
1409 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;
1410 {$IF DEFINED(CPU386) OR DEFINED(CPUAMD64)}
1411 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;
1412 {$ENDIF}
1414 // ////////////////////////////////////////////////////////////////////////// //
1415 {$IFDEF WINDOWS}
1416 function snprintf (buf: PAnsiChar; bufsize: SizeUInt; const fmt: PAnsiChar): SizeUInt; cdecl; varargs; external 'msvcrt.dll' name '_snprintf';
1417 {$ELSE}
1418 {$IFDEF GO32V2}
1419 function snprintf (buf: PAnsiChar; bufsize: SizeUInt; const fmt: PAnsiChar): SizeUInt; cdecl; varargs; external;
1420 {$ELSE}
1421 function snprintf (buf: PAnsiChar; bufsize: SizeUInt; const fmt: PAnsiChar): SizeUInt; cdecl; varargs; external 'libc' name 'snprintf';
1422 {$ENDIF}
1423 {$ENDIF}
1426 (*
1427 procedure conwriter (constref buf; len: SizeUInt);
1428 var
1429 ss: ShortString;
1430 slen: Integer;
1431 b: PByte;
1432 begin
1433 if (len < 1) then exit;
1434 b := PByte(@buf);
1435 while (len > 0) do
1436 begin
1437 if (len > 255) then slen := 255 else slen := Integer(len);
1438 Move(b^, ss[1], len);
1439 ss[0] := AnsiChar(slen);
1440 write(ss);
1441 b += slen;
1442 len -= slen;
1443 end;
1444 end;
1445 *)
1448 function formatstrf (const fmt: AnsiString; const args: array of const; writerCB: TFormatStrFCallback=nil): AnsiString;
1449 const
1451 PadZeroes: AnsiString = '00000000000000000000000000000000000000000000000000000000000000000000000';
1452 var
1468 var
1472 begin
1476 begin
1478 end
1479 else
1480 begin
1482 begin
1494 begin
1499 begin
1505 var
1507 begin
1513 var
1515 begin
1521 begin
1525 begin
1535 begin
1540 var
1543 begin
1545 begin
1549 end
1550 else
1551 begin
1556 repeat
1558 begin
1562 end
1563 else
1564 begin
1566 begin
1569 end
1581 var
1583 begin
1586 repeat
1588 begin
1592 end
1593 else
1594 begin
1596 begin
1599 end
1609 var
1611 begin
1613 begin
1621 var
1623 begin
1625 begin
1632 begin
1636 begin
1637 // print literal part
1640 // output literal part
1642 begin
1644 begin
1646 break;
1650 begin
1654 end
1655 else
1656 begin
1660 continue;
1662 // check if we have argument for this format string
1664 begin
1667 break;
1669 // skip percent
1673 // parse format; check for sign
1677 // parse width
1680 begin
1681 if (fmt[spos] < '0') or (fmt[spos] > '9') then begin xwrite('<INVALID FORMAT>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
1685 begin
1691 end
1692 else
1693 begin
1697 // parse precision
1700 begin
1703 if (fmt[spos] < '0') or (fmt[spos] > '9') then begin xwrite('<INVALID FORMAT>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
1706 begin
1713 // get format char
1717 // done parsing format, check for valid format chars
1718 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;
1719 // now write formatted string
1722 begin
1723 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;
1732 begin
1734 if args[curarg].VBoolean then strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], 'true')
1739 begin
1741 if args[curarg].VBoolean then strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], AnsiChar('t'))
1746 begin
1751 else
1752 begin
1755 break;
1761 begin
1768 begin
1773 else
1774 begin
1777 break;
1780 //vtWideChar: begin end; // args[curarg].VWideChar (WideChar)
1784 begin
1791 begin
1797 begin
1799 strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], Integer(trunc(args[curarg].VExtended^)));
1803 begin
1805 strblen := snprintf(@strbuf[0], Length(strbuf), @fmtbuf[0], LongWord(trunc(args[curarg].VExtended^)));
1808 else
1809 begin
1812 break;
1816 begin
1824 begin
1833 begin
1841 else
1842 begin
1845 break;
1850 begin
1854 end
1855 else
1856 begin
1864 begin
1865 if (args[curarg].VObject <> nil) then ccname := args[curarg].VObject.Classname else ccname := '<nil>';
1871 begin
1872 if (args[curarg].VClass <> nil) then ccname := args[curarg].VClass.Classname else ccname := '<nil>';
1877 //vtPWideChar: begin end; // args[curarg].VPWideChar (PWideChar)
1879 begin
1884 //vtCurrency: begin end; // args[curarg].VCurrency (PCurrency)
1885 //vtVariant: begin end; // args[curarg].VVariant^ (PVariant)
1886 //vtInterface: begin end; // args[curarg].VInterface (Pointer);
1887 //vtWideString: begin end; // args[curarg].VWideString (Pointer);
1889 begin
1894 else begin xwrite('<INVALID FORMAT CHAR>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
1899 begin
1901 begin
1903 begin
1908 end
1909 else
1910 begin
1913 end
1914 else
1915 begin
1923 begin
1928 else begin xwrite('<INVALID FORMAT CHAR>'); writer((PAnsiChar(fmt)+spos-1)^, Length(fmt)-spos+1); break; end;
1932 if (sign <> '-') then begin if zeropad then indent0(width-pclen) else indent(width-pclen); end;
1936 else
1937 begin
1940 break;
1948 (*
1949 var
1950 ss: ShortString;
1951 ls: AnsiString;
1952 i64: Int64 = -$A000000000;
1953 ui64: UInt64 = $A000000000;
1954 begin
1955 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']);
1956 writef(conwriter, 'test float:<%s;%u;%f;%g>'#10, [666.6942, 666.6942, 666.6942, 666.6942]);
1957 ss := 'fuckit';
1958 ls := 'FUCKIT';
1959 writef(conwriter, 'test ss:<%5s;%040s>'#10, [ss, ss]);
1960 writef(conwriter, 'test ls:<%5s;%040s>'#10, [ls, ls]);
1961 writef(conwriter, 'test pointer:<%s;%x;%p>'#10, [@ss, @ss, @ss]);
1962 writef(conwriter, 'test i64:<%s;%x;%015d;%u;%X>'#10, [i64, i64, i64, i64, i64]);
1963 writef(conwriter, 'test ui64:<%s;%x;%15d;%015u;%X>'#10, [ui64, ui64, ui64, ui64, ui64]);
1964 *)