unit ATLLib;

interface

{$I KOLDEF.inc}

uses Windows, Kol;

function Trim( const S : string): string; //Для устранения ошибки в asm версии KOL
function SwapOrder32(Value: DWord): DWord;
function SwapOrder64(Value: Int64): Int64;
function Str2IntDef(const S: string; Default: Integer): Integer;

function OpenFileRead(AFileName: KOLString): PStream;
function OpenFileWrite(AFileName: KOLString): PStream;
function CreateFile(AFileName: KOLString): PStream;

type
  PBitsStream = ^TBitsStream;
  TBitsStream = object(TObj)
  private
    FStream: PStream;
    FBitsPos, FLastByte: Byte;
  public
    constructor Create(AStream: PStream);
    destructor Destroy; virtual;

    function ReadBits(var Value: Cardinal; ASize: Byte): Boolean;
    function Seek(MoveTo: DWord; MoveMethod: TMoveMethod): DWord;
  end;
  function NewBitsStream(AStream: PStream): PBitsStream;

{$IFNDEF _D6orHigher}
type
  UTF8String = type string;
  PUTF8String = ^UTF8String;

// UnicodeToUTF8(3):
// UTF8ToUnicode(3):
// Scans the source data to find the null terminator, up to MaxBytes
// Dest must have MaxBytes available in Dest.
// MaxDestBytes includes the null terminator (last char in the buffer will be set to null)
// Function result includes the null terminator.
function UnicodeToUtf8(Dest: PChar; Source: PWideChar; MaxBytes: Integer): Integer; overload;
function Utf8ToUnicode(Dest: PWideChar; Source: PChar; MaxChars: Integer): Integer; overload;

// UnicodeToUtf8(4):
// UTF8ToUnicode(4):
// MaxDestBytes includes the null terminator (last char in the buffer will be set to null)
// Function result includes the null terminator.
// Nulls in the source data are not considered terminators - SourceChars must be accurate
function UnicodeToUtf8(Dest: PChar; MaxDestBytes: Cardinal; Source: PWideChar; SourceChars: Cardinal): Cardinal; overload;
function Utf8ToUnicode(Dest: PWideChar; MaxDestChars: Cardinal; Source: PChar; SourceBytes: Cardinal): Cardinal; overload;

{ WideString <-> UTF8 conversion }

function UTF8Encode(const WS: WideString): UTF8String;
function UTF8Decode(const S: UTF8String): WideString;
function AnsiToUtf8(const S: string): UTF8String;
function Utf8ToAnsi(const S: UTF8String): string;
{$ENDIF}

implementation

function TrimRight(const S: string): string;
var
  I: Integer;
begin
  I := Length(S);
  while (I > 0) and (S[I] <= ' ') do Dec(I);
  Result := Copy(S, 1, I);
end;

function TrimLeft(const S: string): string;
var
  I, L: Integer;
begin
  L := Length(S);
  I := 1;
  while (I <= L) and (S[I] <= ' ') do Inc(I);
  Result := Copy(S, I, Maxint);
end;

function Trim( const S : string): string;
begin
   Result := TrimLeft( TrimRight( S ) );
end;

function SwapOrder32(Value: DWord): DWord;
type Tab_3 = array[0..3]of Byte;
begin
    Tab_3(Result)[0]:= Tab_3(Value)[3];
    Tab_3(Result)[1]:= Tab_3(Value)[2];
    Tab_3(Result)[2]:= Tab_3(Value)[1];
    Tab_3(Result)[3]:= Tab_3(Value)[0];
end;

function SwapOrder64(Value: Int64): Int64;
type Tab_7 = array[0..7]of Byte;
begin
    Tab_7(Result)[0]:= Tab_7(Value)[7];
    Tab_7(Result)[1]:= Tab_7(Value)[6];
    Tab_7(Result)[2]:= Tab_7(Value)[5];
    Tab_7(Result)[3]:= Tab_7(Value)[4];
    Tab_7(Result)[4]:= Tab_7(Value)[3];
    Tab_7(Result)[5]:= Tab_7(Value)[2];
    Tab_7(Result)[6]:= Tab_7(Value)[1];
    Tab_7(Result)[7]:= Tab_7(Value)[0];
end;

function OpenFileRead(AFileName: KOLString): PStream;
begin
    Result:= NewFileStream(AFileName, ofOpenRead or ofOpenExisting or ofShareDenyNone);
end;

function OpenFileWrite(AFileName: KOLString): PStream;
begin
    Result:= NewFileStream(AFileName, ofOpenReadWrite or ofOpenExisting or ofShareDenyNone);
end;

function CreateFile(AFileName: KOLString): PStream;
begin
    Result:= NewFileStream(AFileName, ofCreateAlways or ofOpenReadWrite or ofShareDenyNone);
end;

function Str2IntDef(const S: string; Default: Integer): Integer;
var E: Integer;
begin
    Val(S, Result, E);
    if E <> 0 then Result := Default;
end;

{$IFNDEF _D6orHigher}
function UnicodeToUtf8(Dest: PChar; Source: PWideChar; MaxBytes: Integer): Integer;
var len: Cardinal;
begin
    len := 0;
    if Source <> nil then while Source[len] <> #0 do Inc(len);
    Result := UnicodeToUtf8(Dest, MaxBytes, Source, len);
end;

function UnicodeToUtf8(Dest: PChar; MaxDestBytes: Cardinal; Source: PWideChar; SourceChars: Cardinal): Cardinal;
var i, count: Cardinal;
    c: Cardinal;
begin
    Result := 0;
    if Source = nil then Exit;
    count := 0;
    i := 0;
    if Dest <> nil then begin
      while (i < SourceChars) and (count < MaxDestBytes) do begin
        c := Cardinal(Source[i]);
        Inc(i);
        if c <= $7F then begin
          Dest[count] := Char(c);
          Inc(count);
        end
        else if c > $7FF then
        begin
          if count + 3 > MaxDestBytes then
            break;
          Dest[count] := Char($E0 or (c shr 12));
          Dest[count+1] := Char($80 or ((c shr 6) and $3F));
          Dest[count+2] := Char($80 or (c and $3F));
          Inc(count,3);
        end
        else //  $7F < Source[i] <= $7FF
        begin
          if count + 2 > MaxDestBytes then
            break;
          Dest[count] := Char($C0 or (c shr 6));
          Dest[count+1] := Char($80 or (c and $3F));
          Inc(count,2);
        end;
      end;
      if count >= MaxDestBytes then count := MaxDestBytes-1;
      Dest[count] := #0;
    end
    else
    begin
      while i < SourceChars do
      begin
        c := Integer(Source[i]);
        Inc(i);
        if c > $7F then
        begin
          if c > $7FF then
            Inc(count);
          Inc(count);
        end;
        Inc(count);
      end;
    end;
    Result := count+1;  // convert zero based index to byte count
end;

function Utf8ToUnicode(Dest: PWideChar; Source: PChar; MaxChars: Integer): Integer;
var
  len: Cardinal;
begin
  len := 0;
  if Source <> nil then
    while Source[len] <> #0 do
      Inc(len);
  Result := Utf8ToUnicode(Dest, MaxChars, Source, len);
end;

function Utf8ToUnicode(Dest: PWideChar; MaxDestChars: Cardinal; Source: PChar; SourceBytes: Cardinal): Cardinal;
var
  i, count: Cardinal;
  c: Byte;
  wc: Cardinal;
begin
  if Source = nil then
  begin
    Result := 0;
    Exit;
  end;
  Result := Cardinal(-1);
  count := 0;
  i := 0;
  if Dest <> nil then
  begin
    while (i < SourceBytes) and (count < MaxDestChars) do
    begin
      wc := Cardinal(Source[i]);
      Inc(i);
      if (wc and $80) <> 0 then
      begin
        if i >= SourceBytes then Exit;          // incomplete multibyte char
        wc := wc and $3F;
        if (wc and $20) <> 0 then
        begin
          c := Byte(Source[i]);
          Inc(i);
          if (c and $C0) <> $80 then Exit;      // malformed trail byte or out of range char
          if i >= SourceBytes then Exit;        // incomplete multibyte char
          wc := (wc shl 6) or (c and $3F);
        end;
        c := Byte(Source[i]);
        Inc(i);
        if (c and $C0) <> $80 then Exit;       // malformed trail byte

        Dest[count] := WideChar((wc shl 6) or (c and $3F));
      end
      else
        Dest[count] := WideChar(wc);
      Inc(count);
    end;
    if count >= MaxDestChars then count := MaxDestChars-1;
    Dest[count] := #0;
  end
  else
  begin
    while (i < SourceBytes) do
    begin
      c := Byte(Source[i]);
      Inc(i);
      if (c and $80) <> 0 then
      begin
        if i >= SourceBytes then Exit;          // incomplete multibyte char
        c := c and $3F;
        if (c and $20) <> 0 then
        begin
          c := Byte(Source[i]);
          Inc(i);
          if (c and $C0) <> $80 then Exit;      // malformed trail byte or out of range char
          if i >= SourceBytes then Exit;        // incomplete multibyte char
        end;
        c := Byte(Source[i]);
        Inc(i);
        if (c and $C0) <> $80 then Exit;       // malformed trail byte
      end;
      Inc(count);
    end;
  end;
  Result := count+1;
end;
  

function Utf8Encode(const WS: WideString): UTF8String;
var
  L: Integer;
  Temp: UTF8String;
begin
  Result := '';
  if WS = '' then Exit;
  SetLength(Temp, Length(WS) * 3); // SetLength includes space for null terminator

  L := UnicodeToUtf8(PChar(Temp), Length(Temp)+1, PWideChar(WS), Length(WS));
  if L > 0 then
    SetLength(Temp, L-1)
  else
    Temp := '';
  Result := Temp;
end;

function Utf8Decode(const S: UTF8String): WideString;
var
  L: Integer;
  Temp: WideString;
begin
  Result := '';
  if S = '' then Exit;
  SetLength(Temp, Length(S));

  L := Utf8ToUnicode(PWideChar(Temp), Length(Temp)+1, PChar(S), Length(S));
  if L > 0 then
    SetLength(Temp, L-1)
  else
    Temp := '';
  Result := Temp;
end;

function AnsiToUtf8(const S: string): UTF8String;
begin
  Result := Utf8Encode(S);
end;

function Utf8ToAnsi(const S: UTF8String): string;
begin
  Result := Utf8Decode(S);
end;

{$ENDIF}

{ TBitStream }

function NewBitsStream(AStream: PStream): PBitsStream;
begin
    New(Result, Create(AStream));
end;

constructor TBitsStream.Create(AStream: PStream);
begin
    FStream:= AStream;
    FBitsPos:= 0;
end;

destructor TBitsStream.Destroy;
begin
    inherited;
end;

function TBitsStream.ReadBits(var Value: Cardinal; ASize: Byte): Boolean;
var CountBit, B: Byte;
begin
    Result:= ASize <= 32; Value:= 0;
    repeat
      if FBitsPos = 0 then begin Result:= FStream.Read(FLastByte, 1) = 1; FBitsPos:= 0; end;
      CountBit:= Min(ASize, 8-FBitsPos);
      B:= (FLastByte shl FBitsPos);
      B:= B shr (8-CountBit);
      Value:= (Value shl CountBit) or B;
      FBitsPos:= FBitsPos + CountBit;
      if FBitsPos = 8 then FBitsPos:= 0;
      ASize:= ASize - CountBit;
    until ASize=0;
end;

function TBitsStream.Seek(MoveTo: DWord; MoveMethod: TMoveMethod): DWord;
begin
    Result:= FStream.Seek(MoveTo, MoveMethod);
    FBitsPos:= 0;
end;

end.
