(*****************************************************************************)
(* MODULE: BTRBLOB                                                           *)
(* RIGHTS: Reggatta Systems - COPYRIGHT 1995 - 2002                          *)
(* DESCR.: This module contains Blob routines for MIME encoded blobs         *)
(*****************************************************************************)
Unit BtrBlob;
{$I BSE.INC}
{$I BAPI.INC}
interface
Uses
  Classes;

Procedure StreamtoBase64Stream( InStream, OutStream : TStream );
Procedure Base64StreamToStream( InStream, OutStream : TStream );

implementation
Uses
  SysUtils,
  BDE,

  BseUtils,
  BseErrs;

Const
  COMPRESS_CHAR = '!';
  PAD_CHAR      = '=';
  SIGNATURE_STR  : Array[1..3] of Char = '###';  { Format #1 for Titan Blobs }


Function MapBytetoBase64( ch : Byte ) : Byte;
begin
  Case Ch of
     0..25 : Result := Ch + Ord( 'A' );
    26..51 : Result := ( Ch - 26 ) + Ord( 'a' );
    52..61 : Result := ( Ch - 52 ) + Ord( '0' );
    62     : Result := Ord( '+' );
    63     : Result := Ord( '/' );
  else
    Raise EConvertError.Create('MapByteBase64 - Unexpected value: ' + Chr( ch ) );
  end;
end;


Function MapBase64toByte( ch : Byte ) : Byte;
begin
  Case Chr( Ch ) of
    'A'..'Z' : Result := Ch - Ord( 'A' );
    'a'..'z' : Result := 26 + ( Ch - Ord( 'a' ) );
    '0'..'9' : Result := 52 + ( Ch - Ord( '0' ) );
    '+'      : Result := 62;
    '/'      : Result := 63;
    '='      : Result := 0;
  else
    Raise EConvertError.Create('MapBase64toByte - Unexpected value: ' + Chr( ch ) );
  end;
  Result := Result and $3F; { And off high bits }
end;


Procedure StreamtoBase64Stream( InStream, OutStream : TStream );
Const
  BYTESTOREAD = 3;
Type
  MemBuff = Array[1..( BYTESTOREAD + ( BYTESTOREAD Mod 2 ) ) ] of Byte;
  BldBuff = Array[1..4] of Byte;
Var
  BytesRead : LongInt;
  Buffer    : MemBuff;
  OutBuff   : BldBuff;
  LastBuff  : BldBuff;
begin
  FillChar( LastBuff, SizeOf( LastBuff ), PAD_CHAR );
  OutStream.Seek( 0, soFROMBEGINNING );
  OutStream.Write( SIGNATURE_STR, SizeOf( SIGNATURE_STR ) );
  InStream.Seek(  0, soFROMBEGINNING );
  Repeat
    FillChar( Buffer, SizeOf( Buffer ), #0 );
    BytesRead := InStream.Read( Buffer, BYTESTOREAD );
    if ( BytesRead > 0 ) then
    begin
      { First }
      {11111111|11111111|11111111}
      {11111100|00000000|00000000}
      OutBuff[ 1 ] := MapByteToBase64( Buffer[ 1 ] shr 2 );

      { Second }
      {11111111|11111111|11111111}
      {00000011|11110000|00000000}
      OutBuff[ 2 ] := MapByteToBase64( ( ( Buffer[ 1 ] and $03 ) shl 4 ) or ( Buffer[ 2 ] shr 4 ) );

      If ( BytesRead > 1 ) then
      begin
        { Third }
        {11111111|11111111|11111111}
        {00000000|00001111|11000000}
        OutBuff[ 3 ] := MapByteToBase64( ( ( Buffer[ 2 ] and $F ) shl 2 ) or ( Buffer[ 3 ] shr 6 ) );

        If ( BytesRead > 2 ) then
        begin
          { Fourth }
          {11111111|11111111|11111111}
          {00000000|00000000|00111111}
          OutBuff[ 4 ] := MapByteToBase64( Buffer[ 3 ] and $3F );
        end
        else
          OutBuff[ 4 ] := Ord( PAD_CHAR );
      end
      else
      begin
        OutBuff[ 3 ] := Ord( PAD_CHAR );
        OutBuff[ 4 ] := Ord( PAD_CHAR );
      end;
      If ( BytesRead = BYTESTOREAD ) and ( LongInt( LastBuff ) = LongInt( OutBuff ) ) then
      begin
        OutBuff[ 1 ] := Ord( COMPRESS_CHAR );
        OutStream.Write( OutBuff, SizeOf( OutBuff[ 1 ] ) );
      end
      else
      begin
        OutStream.Write( OutBuff, SizeOf( OutBuff ) );
        Move( OutBuff, LastBuff, SizeOf( LastBuff ) );
      end;
    end;
  Until ( BytesRead < BYTESTOREAD );
end;


Procedure Base64StreamToStream( InStream, OutStream : TStream );
Const
  BYTESTOREAD = 4;
Var
  InBuff       : Array[1..BYTESTOREAD] of Byte;
  LastBuff     : Array[1..3] of Byte;
  OutBuff      : Array[1..3] of Byte;
  BytesRead    : LongInt;
  BytesToWrite : LongInt;
  i            : Integer;
  Signature    : Array[1..3] of Char;
begin
  FillChar( LastBuff, SizeOf( LastBuff ), PAD_CHAR );
  OutStream.Seek( 0, soFROMBEGINNING );
  InStream.Seek(  0, soFROMBEGINNING );
  InStream.Read(  Signature, SizeOf( Signature ) );
  If ( Compare( Signature, SIGNATURE_STR, SizeOf( SIGNATURE_STR ) ) <> 0 ) then
    ThrowException( DBIERR_BLOBVERSION );
  Repeat
    BytesRead := InStream.Read( InBuff, BYTESTOREAD );
    if ( BytesRead > 0 ) then
    begin
      If ( InBuff[ 1 ] = Ord( COMPRESS_CHAR ) ) then
      begin
        OutStream.Write( LastBuff, Sizeof( LastBuff ) );
        InStream.Seek( -3, soFROMCURRENT ); { Rewind }
        Continue;
      end;

      BytesToWrite := 1;
      If ( InBuff[ 3 ] <> Ord( PAD_CHAR ) ) then
        Inc( BytesToWrite );
      If ( InBuff[ 4 ] <> Ord( PAD_CHAR ) ) then
        Inc( BytesToWrite );

      For i := 1 to BYTESTOREAD do
        InBuff[ i ] := MapBase64toByte( InBuff[ i ] );

      { First }
      Outbuff[ 1 ] := ( InBuff[ 1 ] Shl 2 ) or ( InBuff[ 2 ] shr 4 );

      If ( BytesToWrite > 1 ) then
      begin
        { Second }
        Outbuff[ 2 ] := Byte( ( InBuff[ 2 ] Shl 4 ) or ( InBuff[ 3 ] shr 2 ) );

        If ( BytestoWrite > 2  ) then
        begin
          { Third }
          Outbuff[ 3 ] := Byte( ( InBuff[ 3 ] Shl 6 ) or ( InBuff[ 4 ] ) );
        end
      end;

      OutStream.Write( OutBuff, BytesToWrite );
      Move( OutBuff, LastBuff, SizeOf( LastBuff ) );

      If ( BytesToWrite < 3 ) then
        Break;
    end;
  Until ( BytesRead < BYTESTOREAD );
end;


end.
