X-Git-Url: https://deadsoftware.ru/gitweb?a=blobdiff_plain;f=src%2Fshared%2Futils.pas;h=d8b6e85c266dcf6fb2142759b53e8b5e35e913aa;hb=939cb39acd39a1ca2bf11a0e4a8488c05baf7358;hp=d73d25766e0734e972f0cf5a8a099d099d2a586e;hpb=d884101780ae73c5f1dea0627007fa839f0a8ad8;p=d2df-sdl.git diff --git a/src/shared/utils.pas b/src/shared/utils.pas index d73d257..d8b6e85 100644 --- a/src/shared/utils.pas +++ b/src/shared/utils.pas @@ -52,14 +52,28 @@ type // ////////////////////////////////////////////////////////////////////////// // +function getFilenameExt (const fn: AnsiString): AnsiString; +function setFilenameExt (const fn, ext: AnsiString): AnsiString; +function forceFilenameExt (const fn, ext: AnsiString): AnsiString; + +// strips out name from `fn`, leaving trailing slash +function getFilenamePath (const fn: AnsiString): AnsiString; + +// ends with '/' or '\'? +function isFilenamePath (const fn: AnsiString): Boolean; + +// strips extra trailing slashes in `path, and extra leading slashes in `fn` +// will add slash to `path`, even if `fn` is empty! +function filenameConcat (const path, fn: AnsiString): AnsiString; + // does filename have one of ".wad", ".pk3", ".zip" extensions? -function hasWadExtension (fn: AnsiString): Boolean; +function hasWadExtension (const fn: AnsiString): Boolean; // does filepath have ".XXX:\" in it? -function isWadPath (fn: AnsiString): Boolean; +function isWadPath (const fn: AnsiString): Boolean; // adds ".wad" extension if filename doesn't have one of ".wad", ".pk3", ".zip" -function addWadExtension (fn: AnsiString): AnsiString; +function addWadExtension (const fn: AnsiString): AnsiString; // convert number to strig with nice commas function Int64ToStrComma (i: Int64): AnsiString; @@ -67,6 +81,8 @@ function Int64ToStrComma (i: Int64): AnsiString; function UpCase1251 (ch: Char): Char; function LoCase1251 (ch: Char): Char; +function toLowerCase1251 (const s: AnsiString): AnsiString; + // `true` if strings are equal; ignoring case for cp1251 function StrEquCI1251 (const s0, s1: AnsiString): Boolean; @@ -122,11 +138,48 @@ function readInt64BE (st: TStream): Int64; function readUInt64BE (st: TStream): UInt64; +function nmin (a, b: Byte): Byte; inline; overload; +function nmin (a, b: ShortInt): ShortInt; inline; overload; +function nmin (a, b: Word): Word; inline; overload; +function nmin (a, b: SmallInt): SmallInt; inline; overload; +function nmin (a, b: LongWord): LongWord; inline; overload; +function nmin (a, b: LongInt): LongInt; inline; overload; +function nmin (a, b: Int64): Int64; inline; overload; +function nmin (a, b: UInt64): UInt64; inline; overload; +function nmin (a, b: Single): Single; inline; overload; +function nmin (a, b: Double): Double; inline; overload; +function nmin (a, b: Extended): Extended; inline; overload; + +function nmax (a, b: Byte): Byte; inline; overload; +function nmax (a, b: ShortInt): ShortInt; inline; overload; +function nmax (a, b: Word): Word; inline; overload; +function nmax (a, b: SmallInt): SmallInt; inline; overload; +function nmax (a, b: LongWord): LongWord; inline; overload; +function nmax (a, b: LongInt): LongInt; inline; overload; +function nmax (a, b: Int64): Int64; inline; overload; +function nmax (a, b: UInt64): UInt64; inline; overload; +function nmax (a, b: Single): Single; inline; overload; +function nmax (a, b: Double): Double; inline; overload; +function nmax (a, b: Extended): Extended; inline; overload; + +function nclamp (v, a, b: Byte): Byte; inline; overload; +function nclamp (v, a, b: ShortInt): ShortInt; inline; overload; +function nclamp (v, a, b: Word): Word; inline; overload; +function nclamp (v, a, b: SmallInt): SmallInt; inline; overload; +function nclamp (v, a, b: LongWord): LongWord; inline; overload; +function nclamp (v, a, b: LongInt): LongInt; inline; overload; +function nclamp (v, a, b: Int64): Int64; inline; overload; +function nclamp (v, a, b: UInt64): UInt64; inline; overload; +function nclamp (v, a, b: Single): Single; inline; overload; +function nclamp (v, a, b: Double): Double; inline; overload; +function nclamp (v, a, b: Extended): Extended; inline; overload; + + type TFormatStrFCallback = procedure (constref buf; len: SizeUInt); // returns formatted string if `writerCB` is `nil`, empty string otherwise -function formatstrf (const fmt: AnsiString; args: array of const; writerCB: TFormatStrFCallback=nil): AnsiString; +function formatstrf (const fmt: AnsiString; const args: array of const; writerCB: TFormatStrFCallback=nil): AnsiString; function wchar2win (wc: WideChar): AnsiChar; inline; function utf2win (const s: AnsiString): AnsiString; @@ -143,24 +196,25 @@ function quoteStr (const s: AnsiString): AnsiString; type generic TSimpleList = class private - type PItemT = ^ItemT; + //type PItemT = ^ItemT; + type TItemArr = array of ItemT; public type TEnumerator = record private - mItems: PItemT; + mItems: TItemArr; mCount: Integer; mCurrent: Integer; public - constructor Create (aitems: PItemT; acount: Integer); + constructor Create (const aitems: TItemArr; acount: Integer); function MoveNext: Boolean; function getCurrent (): ItemT; property Current: ItemT read getCurrent; end; private - mItems: array of ItemT; + mItems: TItemArr; mCount: Integer; // can be less than `mItems` size private @@ -181,6 +235,8 @@ type procedure clear (); inline; procedure append (constref it: ItemT); inline; + procedure delete (idx: Integer); inline; + function remove (idx: Integer): ItemT; inline; public property count: Integer read mCount; @@ -193,11 +249,11 @@ implementation // ////////////////////////////////////////////////////////////////////////// // -constructor TSimpleList.TEnumerator.Create (aitems: PItemT; acount: Integer); +constructor TSimpleList.TEnumerator.Create (const aitems: TItemArr; acount: Integer); begin mItems := aitems; - mCount := acount; mCurrent := -1; + mCount := acount; end; function TSimpleList.TEnumerator.MoveNext: Boolean; @@ -243,7 +299,7 @@ end; function TSimpleList.GetEnumerator (): TEnumerator; begin - if (Length(mItems) > 0) then result := TEnumerator.Create(@mItems[0], mCount) + if (Length(mItems) > 0) then result := TEnumerator.Create(mItems, mCount) else result := TEnumerator.Create(nil, -1); end; @@ -274,16 +330,46 @@ end; procedure TSimpleList.append (constref it: ItemT); inline; +var + newsz: Integer; begin - if (mCount = Length(mItems)) then + if (mCount >= Length(mItems)) then begin - if (mCount = 0) then SetLength(mItems, 128) else SetLength(mItems, mCount*2); + newsz := mCount+(mCount div 3)+128; + SetLength(mItems, newsz); end; mItems[mCount] := it; Inc(mCount); end; +procedure TSimpleList.delete (idx: Integer); inline; +var + f: Integer; +begin + if (idx >= 0) and (idx < mCount) then + begin + for f := idx+1 to mCount-1 do mItems[f-1] := mItems[f]; + end; +end; + + +function TSimpleList.remove (idx: Integer): ItemT; inline; +var + f: Integer; +begin + if (idx >= 0) and (idx < mCount) then + begin + result := mItems[idx]; + for f := idx+1 to mCount-1 do mItems[f-1] := mItems[f]; + end + else + begin + result := Default(ItemT); + end; +end; + + // ////////////////////////////////////////////////////////////////////////// // var wc2shitmap: array[0..65535] of AnsiChar; @@ -545,40 +631,176 @@ end; // ////////////////////////////////////////////////////////////////////////// // -function hasWadExtension (fn: AnsiString): Boolean; +function getFilenameExt (const fn: AnsiString): AnsiString; +var + pos: Integer; + ch: AnsiChar; +begin + pos := Length(fn); + while (pos > 0) do + begin + ch := fn[pos]; + if (ch = '.') then + begin + if (pos = Length(fn)) then result := '' else result := Copy(fn, pos, Length(fn)-pos+1); + exit; + end; + if (ch = '/') or (ch = '\') then break; + Dec(pos); + end; + result := ''; // no extension +end; + + +function setFilenameExt (const fn, ext: AnsiString): AnsiString; +var + pos: Integer; + ch: AnsiChar; +begin + result := fn; + if (Length(ext) = 0) or (ext = '.') then exit; + pos := Length(fn); + while (pos > 0) do + begin + ch := fn[pos]; + if (ch = '.') then exit; + if (ch = '/') or (ch = '\') then break; + Dec(pos); + end; + if (ext[1] <> '.') then result += '.'; + result += ext; +end; + + +function forceFilenameExt (const fn, ext: AnsiString): AnsiString; +var + pos: Integer; + ch: AnsiChar; +begin + result := fn; + pos := Length(fn); + while (pos > 0) do + begin + ch := fn[pos]; + if (ch = '.') then + begin + if (Length(ext) = 0) or (ext = '.') then + begin + result := Copy(fn, 1, pos-1); + end + else + begin + if (ext[1] = '.') then result := Copy(fn, 1, pos-1) else result := Copy(fn, 1, pos); + result += ext; + exit; + end; + end; + if (ch = '/') or (ch = '\') then break; + Dec(pos); + end; + if (Length(ext) > 0) then + begin + if (ext[1] <> '.') then result += '.'; + result += ext; + end; +end; + + +// strips out name from `fn`, leaving trailing slash +function getFilenamePath (const fn: AnsiString): AnsiString; +var + pos: Integer; + ch: AnsiChar; +begin + if (Length(fn) = 0) then begin result := './'; exit; end; + if (fn[Length(fn)] = '/') or (fn[Length(fn)] = '\') then begin result := fn; exit; end; + pos := Length(fn); + while (pos > 0) do + begin + ch := fn[pos]; + if (ch = '/') or (ch = '\') then begin result := Copy(fn, 1, pos); exit; end; + Dec(pos); + end; + result := './'; // no path -> current dir +end; + + +// ends with '/' or '\'? +function isFilenamePath (const fn: AnsiString): Boolean; +begin + if (Length(fn) = 0) then + begin + result := false; + end + else + begin + result := (fn[Length(fn)] = '/') or (fn[Length(fn)] = '\'); + end; +end; + + +// strips extra trailing slashes in `path, and extra leading slashes in `fn` +// will add slash to `path`, even if `fn` is empty! +function filenameConcat (const path, fn: AnsiString): AnsiString; +var + pos: Integer; +begin + pos := 1; + while (pos <= Length(fn)) and ((fn[pos] = '/') or (fn[pos] = '\')) do Inc(pos); + result := path; + if (Length(result) > 0) and ((result[Length(result)] <> '/') and (result[Length(result)] <> '\')) then result += '/'; + if (pos <= Length(fn)) then + begin + result += Copy(fn, pos, Length(fn)-pos+1); + //FIXME: make this faster! + while (Length(result) > 0) and ((result[Length(result)] = '/') or (result[Length(result)] = '\')) do + begin + Delete(result, Length(result), 1); + end; + if (fn[Length(fn)] = '/') or (fn[Length(fn)] = '\') then result += '/'; + end; +end; + + +function hasWadExtension (const fn: AnsiString): Boolean; +var + ext: AnsiString; begin - fn := ExtractFileExt(fn); - result := StrEquCI1251(fn, '.wad') or StrEquCI1251(fn, '.pk3') or StrEquCI1251(fn, '.zip'); + ext := getFilenameExt(fn); + result := StrEquCI1251(ext, '.wad') or StrEquCI1251(ext, '.pk3') or StrEquCI1251(ext, '.zip'); end; -function addWadExtension (fn: AnsiString): AnsiString; +function addWadExtension (const fn: AnsiString): AnsiString; begin result := fn; if not hasWadExtension(result) then result := result+'.wad'; end; -function isWadPath (fn: AnsiString): Boolean; +function isWadPath (const fn: AnsiString): Boolean; var - p: Integer; + pos: Integer; s: AnsiString; begin result := false; - while true do + pos := 1; + while (pos <= Length(fn)) do begin - p := Pos(':', fn); - if (p = 0) or (length(fn)-p < 1) then break; - if (p-4 > 1) and (fn[p-4] = '.') and ((fn[p+1] = '\') or (fn[p+1] = '/')) then + if (fn[pos] = ':') then begin - s := Copy(fn, p-4, 4); - if StrEquCI1251(s, '.wad') or StrEquCI1251(s, '.pk3') or StrEquCI1251(s, '.zip') then + if (Length(fn)-pos < 1) then break; + if (pos-4 > 1) and (fn[pos-4] = '.') and ((fn[pos+1] = '\') or (fn[pos+1] = '/')) then begin - result := true; - exit; + s := Copy(fn, pos-4, 4); + if StrEquCI1251(s, '.wad') or StrEquCI1251(s, '.pk3') or StrEquCI1251(s, '.zip') then + begin + result := true; + exit; + end; end; end; - Delete(fn, 1, p); + Inc(pos); end; end; @@ -655,6 +877,26 @@ begin end; +function toLowerCase1251 (const s: AnsiString): AnsiString; +var + f: Integer; + ch: AnsiChar; +begin + for ch in s do + begin + if (ch <> LoCase1251(ch)) then + begin + result := ''; + SetLength(result, Length(s)); + for f := 1 to Length(s) do result[f] := LoCase1251(s[f]); + exit; + end; + end; + // nothing to do + result := s; +end; + + // ////////////////////////////////////////////////////////////////////////// // // utils // `ch`: utf8 start @@ -974,6 +1216,44 @@ function readInt64BE (st: TStream): Int64; begin readIntegerBE(st, @result, 8); function readUInt64BE (st: TStream): UInt64; begin readIntegerBE(st, @result, 8); end; +// ////////////////////////////////////////////////////////////////////////// // +function nmin (a, b: Byte): Byte; inline; overload; begin if (a < b) then result := a else result := b; end; +function nmin (a, b: ShortInt): ShortInt; inline; overload; begin if (a < b) then result := a else result := b; end; +function nmin (a, b: Word): Word; inline; overload; begin if (a < b) then result := a else result := b; end; +function nmin (a, b: SmallInt): SmallInt; inline; overload; begin if (a < b) then result := a else result := b; end; +function nmin (a, b: LongWord): LongWord; inline; overload; begin if (a < b) then result := a else result := b; end; +function nmin (a, b: LongInt): LongInt; inline; overload; begin if (a < b) then result := a else result := b; end; +function nmin (a, b: Int64): Int64; inline; overload; begin if (a < b) then result := a else result := b; end; +function nmin (a, b: UInt64): UInt64; inline; overload; begin if (a < b) then result := a else result := b; end; +function nmin (a, b: Single): Single; inline; overload; begin if (a < b) then result := a else result := b; end; +function nmin (a, b: Double): Double; inline; overload; begin if (a < b) then result := a else result := b; end; +function nmin (a, b: Extended): Extended; inline; overload; begin if (a < b) then result := a else result := b; end; + +function nmax (a, b: Byte): Byte; inline; overload; begin if (a > b) then result := a else result := b; end; +function nmax (a, b: ShortInt): ShortInt; inline; overload; begin if (a > b) then result := a else result := b; end; +function nmax (a, b: Word): Word; inline; overload; begin if (a > b) then result := a else result := b; end; +function nmax (a, b: SmallInt): SmallInt; inline; overload; begin if (a > b) then result := a else result := b; end; +function nmax (a, b: LongWord): LongWord; inline; overload; begin if (a > b) then result := a else result := b; end; +function nmax (a, b: LongInt): LongInt; inline; overload; begin if (a > b) then result := a else result := b; end; +function nmax (a, b: Int64): Int64; inline; overload; begin if (a > b) then result := a else result := b; end; +function nmax (a, b: UInt64): UInt64; inline; overload; begin if (a > b) then result := a else result := b; end; +function nmax (a, b: Single): Single; inline; overload; begin if (a > b) then result := a else result := b; end; +function nmax (a, b: Double): Double; inline; overload; begin if (a > b) then result := a else result := b; end; +function nmax (a, b: Extended): Extended; inline; overload; begin if (a > b) then result := a else result := b; end; + +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; +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; +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; +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; +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; +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; +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; +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; +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; +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; +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; + + // ////////////////////////////////////////////////////////////////////////// // {$IFDEF WINDOWS} function snprintf (buf: PAnsiChar; bufsize: SizeUInt; const fmt: PAnsiChar): SizeUInt; cdecl; varargs; external 'msvcrt.dll' name '_snprintf'; @@ -1004,7 +1284,7 @@ end; *) -function formatstrf (const fmt: AnsiString; args: array of const; writerCB: TFormatStrFCallback=nil): AnsiString; +function formatstrf (const fmt: AnsiString; const args: array of const; writerCB: TFormatStrFCallback=nil): AnsiString; const PadSpaces: AnsiString = ' '; PadZeroes: AnsiString = '00000000000000000000000000000000000000000000000000000000000000000000000'; @@ -1021,6 +1301,7 @@ var fmtblen: Integer; pclen: Integer; pc: PAnsiChar; + ccname: ShortString; procedure writer (constref buf; len: SizeUInt); var @@ -1039,7 +1320,7 @@ var while (len > 0) do begin if (len > 255) then slen := 255 else slen := Integer(len); - Move(b^, ss[1], len); + Move(b^, ss[1], slen); ss[0] := AnsiChar(slen); result += ss; b += slen; @@ -1112,7 +1393,7 @@ var xpos := High(strbuf); strbuf[xpos] := #0; Dec(xpos); repeat - if hex then + if not hex then begin strbuf[xpos] := AnsiChar((n mod 10)+48); Dec(xpos); @@ -1142,7 +1423,7 @@ var xpos := High(strbuf); strbuf[xpos] := #0; Dec(xpos); repeat - if hex then + if not hex then begin strbuf[xpos] := AnsiChar((n mod 10)+48); Dec(xpos); @@ -1420,15 +1701,17 @@ begin end; vtObject: // args[curarg].VObject.Classname (TObject) begin - if (sign <> '-') then indent(width-Length(args[curarg].VObject.Classname)); - xwrite(args[curarg].VObject.Classname); - if (sign = '-') then indent(width-Length(args[curarg].VObject.Classname)); + if (args[curarg].VObject <> nil) then ccname := args[curarg].VObject.Classname else ccname := ''; + if (sign <> '-') then indent(width-Length(ccname)); + xwrite(ccname); + if (sign = '-') then indent(width-Length(ccname)); end; vtClass: // args[curarg].VClass.Classname (TClass) begin - if (sign <> '-') then indent(width-Length(args[curarg].VClass.Classname)); - xwrite(args[curarg].VClass.Classname); - if (sign = '-') then indent(width-Length(args[curarg].VClass.Classname)); + if (args[curarg].VClass <> nil) then ccname := args[curarg].VClass.Classname else ccname := ''; + if (sign <> '-') then indent(width-Length(ccname)); + xwrite(ccname); + if (sign = '-') then indent(width-Length(ccname)); end; //vtPWideChar: begin end; // args[curarg].VPWideChar (PWideChar) vtAnsiString: // AnsiString(args[curarg].VAnsiString) (Pointer)