'===========================================================================
' Subject: BASE 64 ENCODING                   Date: 05-12-96 (14:15)       
'  Author: G. Balla                           Code: VBDOS                  
'  Origin: artvil@ix.netcom.com             Packet: VB.ABC
'===========================================================================
'-------------------------------------------------------------------------
'This code has been placed in public domain.
'G Balla 
'--------------------------------------------------------------------------
Option Explicit

Dim Shared ic_ChopMask  As Integer  ' Constant 8-bit mask (Faster than using string constants)
Dim Shared ic_BitShift  As Integer  ' Constant shift mask (Faster than using string constants)
Dim Shared ic_StartMask As Integer  ' Initial mask value  (Faster than using string constants)

Dim Shared i_RollOver   As Integer  ' Decoded Roll over value
Dim Shared i_HighMask   As Integer  ' Mask high bits of each char
Dim Shared i_Shift      As Integer  ' Multiplier shift value
Dim Shared i_LowShift   As Integer  ' Mask low bits of each char

Dim Shared sz_Alphabet  As String   ' Decode/Encode Lookup Table
Dim Shared sz_Temp      As String   ' Working string

'----------------------------------------------------------------------------------------------
' Name:         DECODE_64$
' Type:         String function
' Description:  decodes a string from BASE64 to binary format
'
' Arguements:   Source string to decode (sz_Encoded)
'               Last string in file to decode flag (i_EndOfText)
' Returns:      Un-Encoded binary string.
'
' Errors:       Generates an Error via ERROR statement.
' Error code:   253 = No string to decode
'                   -----------------------------------------------
' NOTES:    Must call INIT_DECODE_64 before sending 1st string of an encoded file.
'
'           A DOEVENTS occurs near the end of the FOR/NEXT loop
'                   -----------------------------------------------
' Global variables: sz_Alphabet         ' Decode/Encode lookup table
'                   sz_Temp             ' Working string
'
'                   i_RollOver          ' Decode/Encode rollover value
'                   i_HighMask          ' High bit mask
'                   i_Shift             ' Multiplier value
'
'
' Local variables:  i_Char              ' Ascii Char value from Encoded string
'                   i_Ptr               ' Lookup table pointer
'                   i_Counter           ' FOR/NEXT counter
'
'----------------------------------------------------------------------------------------------
Function DECODE_64$ (sz_Encoded As String, i_EndOfText As Integer)

' Create variables
    Dim i_Ptr       As Integer
    Dim i_Char      As Integer
    Dim i_Counter As Integer

' Check if empty decoded string.
' If Empty, return NUL and Generate error 254
    If Len(sz_Encoded) = 0 Then
        DECODE_64$ = ""
        Error 253
        Exit Function
    End If

' Initialize working string
    sz_Temp = ""

' Begin Decoding
    For i_Counter = 1 To Len(sz_Encoded)

' Get next alphabet
        i_Char = Asc(Mid$(sz_Encoded, i_Counter, 1))

' Get Decoded value
        i_Ptr = InStr(sz_Alphabet, Chr$(i_Char)) - 1

' Check if character is valid
        If i_Ptr >= 0 Then

' Char is valid, process it
            If i_Shift = ic_BitShift Then

' 1st char in block of 4, keep high part of character
                i_RollOver = (i_Ptr * i_Shift) And ic_ChopMask

' Reset masks for next character
                i_HighMask = &H30
                i_LowShift = ic_StartMask
                i_Shift = ic_StartMask

            Else

' Start saving decoded character
                sz_Temp = sz_Temp + Chr$(i_RollOver Or ((i_Ptr And i_HighMask) / i_LowShift))

' Calculate next mask and shift values
                i_RollOver = (i_Ptr * i_Shift) And ic_ChopMask
                i_Shift = i_Shift * ic_BitShift
                i_HighMask = (i_HighMask \ ic_BitShift) Or &H30
                i_LowShift = i_LowShift / ic_BitShift

                If i_Shift > 256 Then
                    i_Shift = ic_BitShift
                    i_LowShift = 0
                    DoEvents
                End If
            End If

        End If
    Next

' Concat last character if required
    If (i_Shift > ic_BitShift And i_Shift < 256) Then

' Character remaining in    i_RollOver
        If i_EndOfText Then

' Last string to decode in file
            sz_Temp = sz_Temp + Chr$(i_RollOver)
        End If
    End If

' Exit wth decoded string
    DECODE_64$ = sz_Temp
End Function

'----------------------------------------------------------------------------------------------
' Name:         ENCODE_64$
' Type:         String function
' Description:  Encodes a string into a BASE64 coded string
'
' Arguement:    Source string to encode (sz_UnEncoded)
' Returns:      Encoded BASE64 string.
'
' Errors:       Generates an Error via ERROR statement.
' Error code:   254 = No string to encode
'                       returns NUL string
'                   -----------------------------------------------
' NOTES:    If sending more than 1 string per file, ensure that each source string
'               is a multiple of 3 bytes or premature padded characters are added.
'               The last string of a file is exempt from this rule.
'
'           Each string can be 16383 bytes in length to satisfy VB's string capacity limitation.
'
'           Encoded blocks should be written as sub block lengths of about 76 characters and
'               added CR/LF added to each sub-block.
'               This is what WINCODE does and should satisfy most UUE browsers.
'
'           A DOEVENT occurs near the end of the FOR/NEXT loop
'
'                   -----------------------------------------------
' Global variables: sz_Alphabet     ' Encoding/Decoding lookup table
'                   sz_Temp         ' Working string
'                   ic_ChopMask     ' Constant 8-bit mask for speed
'                   ic_BitShift     ' Constant shift mask for speed
'
'                   i_RollOver      ' Decode/Encode rollover value
'                   i_HighMask      ' High bit mask
'                   i_Shift         ' Multiplier value
'
' Local variables:  ic_LowFill      ' Constant Low 2-bit fill value for speed
'
'                   i_LowMask       ' Low bits mask
'                   i_Char          ' Ascii char value from Un coded string
'                   i_Ptr           ' Lookup table pointer
'
'                   i_Counter       ' For/Next loop counter
'----------------------------------------------------------------------------------------------
Function ENCODE_64$ (sz_UnEncoded As String)

' Create variables
    Dim ic_LowFill  As Integer

    Dim i_Char      As Integer
    Dim i_LowMask   As Integer
    Dim i_Ptr       As Integer

    Dim i_Counter As Integer


' Check if empty decoded string.
' If Empty, return NUL and Generate error 254
    If Len(sz_UnEncoded) = 0 Then
        ENCODE_64$ = ""
        Error 254
        Exit Function
    End If

' Initialize lookup dictionary and constants
    sz_Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"

    ic_BitShift = 4
    ic_ChopMask = 255
    ic_LowFill = 3


' Initialize Masks
    sz_Temp = ""

    i_HighMask = &HFC
    i_LowMask = &H3
    i_Shift = &H10
    i_RollOver = 0


' Begin Encoding process
    For i_Counter = 1 To Len(sz_UnEncoded)

' Fetch ascii character in decoded string
        i_Char = Asc(Mid$(sz_UnEncoded, i_Counter, 1))

' Calculate Alphabet lookup pointer
        i_Ptr = ((i_Char And i_HighMask) \ (i_LowMask + 1)) Or i_RollOver

' Roll bit patterns
        i_RollOver = (i_Char And i_LowMask) * i_Shift

' Concatenate encoded character to working encoded string
        sz_Temp = sz_Temp + Mid$(sz_Alphabet, i_Ptr + 1, 1)

' Adjust masks
        i_HighMask = (i_HighMask * ic_BitShift) And ic_ChopMask
        i_LowMask = i_LowMask * ic_BitShift + ic_LowFill
        i_Shift = i_Shift \ ic_BitShift

' If last character in block, concat last RollOver and
'   reset masks
        If i_HighMask = 0 Then
            sz_Temp = sz_Temp + Mid$(sz_Alphabet, i_RollOver + 1, 1)
            i_RollOver = 0
            i_HighMask = &HFC
            i_LowMask = &H3
            i_Shift = &H10
            DoEvents
        End If

    Next i_Counter

' If RollOver remains, concat it to the working string
    If i_Shift < &H10 Then
        sz_Temp = sz_Temp + Mid$(sz_Alphabet, i_RollOver + 1, 1)
    End If

' Pad encoded string with required '=' pad characters
    i_Ptr = (Len(sz_Temp) Mod 4)
    If i_Ptr Then sz_Temp = sz_Temp + String$(4 - i_Ptr, "=")

' Return encoded string
    ENCODE_64$ = sz_Temp
End Function

'----------------------------------------------------------------------------------------------
' Name:         INIT_DECODE_64
' Type:         Sub procedure
' Description:  Initializes local shared variables for DECODE_64$
'                   Required because many encoded files cannot be decoded in a single string
'
' Used:         Before decoding a BASE64 file.  (DECODE_64$)
' Arguements:   None
' Returns:      Nothing
'
' Errors:       None
'
' Global Variables used:
'               sz_Alphabet             Encode/Decode Table lookup
'
'               ic_ChopMask             Constant value in variable to speed process
'               ic_BitShift             Constant value in variable to speed process
'               ic_StartMask            Constant value in variable to speed process
'
'               i_Shift                 Multiplier shift value
'               i_LowShift              Mask Low bits of each char
'               i_RollOver              Decoded Roll over value from prev char in string
'               i_HighMask              Mask high bits of each char
'----------------------------------------------------------------------------------------------
Sub INIT_DECODE_64 ()

' Initialize 2nd encoding pass lookup dictionary
    sz_Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"

' Initialize Constants
    ic_ChopMask = 255
    ic_BitShift = 4
    ic_StartMask = &H10

' Initialize Masks
    i_Shift = ic_BitShift
    i_LowShift = 0
    i_RollOver = 0
    i_HighMask = -1
End Sub
