Author Topic: Base 64 encoder / decoder  (Read 7412 times)

0 Members and 1 Guest are viewing this topic.

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Base 64 encoder / decoder
« on: December 17, 2020, 01:58:14 am »
Encodes  base 256  to base 64
Decodes base 64 to base 256

The code is more of an example of the math behind base to base conversions.

The earlier version had a bug  - now corrected.

Code: QB64: [Select]
  1. 'BASE 64 encoder / decoder
  2.  
  3. 'prints the string to be encoded
  4. 'prints the encoded base 64 string
  5. 'prints the decoded base 64 string
  6.  
  7. DIM string1 AS STRING
  8. DIM string2 AS STRING
  9.  
  10. string1 = "ASCII stands for American Standard Code for Information Interchange.."
  11.  
  12. PRINT "STRING1 "; string1
  13. FOR x% = 1 TO LEN(string1) STEP 3
  14.     a = ASC(MID$(string1, x%, 1))
  15.     b = ASC(MID$(string1, x% + 1, 1))
  16.     c = ASC(MID$(string1, x% + 2, 1))
  17.  
  18.  
  19.     s = a * 256 ^ 2 + b * 256 + c
  20.  
  21.  
  22.     IF s >= 64 ^ 3 THEN
  23.         i = INT(s / 64 ^ 3)
  24.         s = s - i * 64 ^ 3
  25.         e = CHR$(i + 48)
  26.     END IF
  27.  
  28.     IF s >= 64 ^ 2 THEN
  29.         i = INT(s / 64 ^ 2)
  30.         s = s - i * 64 ^ 2
  31.         f = CHR$(i + 48)
  32.     END IF
  33.  
  34.     IF s >= 64 ^ 1 THEN
  35.         i = INT(s / 64 ^ 1)
  36.         s = s - i * 64 ^ 1
  37.         g = CHR$(i + 48)
  38.     END IF
  39.  
  40.     IF s >= 64 ^ 0 THEN
  41.         i = INT(s / 64 ^ 0)
  42.         s = s - i * 64 ^ 0
  43.         h = CHR$(i + 48)
  44.     END IF
  45.  
  46.  
  47.     string2 = string2 + (e + f + g + h)
  48. NEXT x%
  49. PRINT "STRING2 "; string2
  50. string1 = ""
  51. '********************
  52. FOR x% = 1 TO LEN(string2) STEP 4
  53.     a = ASC(MID$(string2, x%, 1)) - 48
  54.     b = ASC(MID$(string2, x% + 1, 1)) - 48
  55.     c = ASC(MID$(string2, x% + 2, 1)) - 48
  56.     d = ASC(MID$(string2, x% + 3, 1)) - 48
  57.  
  58.  
  59.     s = a * 64 ^ 3 + b * 64 ^ 2 + c * 64 + d
  60.  
  61.     IF s >= 256 ^ 2 THEN
  62.         i = INT(s / 256 ^ 2)
  63.         s = s - i * 256 ^ 2
  64.         e = CHR$(i)
  65.     END IF
  66.  
  67.     IF s >= 256 ^ 1 THEN
  68.         i = INT(s / 256 ^ 1)
  69.         s = s - i * 256 ^ 1
  70.         f = CHR$(i)
  71.     END IF
  72.  
  73.     IF s >= 256 ^ 0 THEN
  74.         i = INT(s / 256 ^ 0)
  75.         s = s - i * 256 ^ 0
  76.         g = CHR$(i)
  77.     END IF
  78.  
  79.     string1 = string1 + (e + f + g)
  80.  
  81. NEXT x%
  82.  
  83. PRINT "STRING1 "; string1
  84.  
  85.  
  86.  








« Last Edit: December 17, 2020, 10:30:44 pm by NOVARSEG »

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Base 64 encoder
« Reply #1 on: December 17, 2020, 01:06:06 pm »
What's the POINT?

Wouldn't numbers be more handy for processing?

ColorNumber~& = _RGB32(red, green, blue, alpha)

And the reverse, simple handy SUB for analyzing a color:
Code: QB64: [Select]
  1. SUB cAnalysis (c AS _UNSIGNED LONG, outRed, outGrn, outBlu, outAlp)
  2.     outRed = _RED32(c): outGrn = _GREEN32(c): outBlu = _BLUE32(c): outAlp = _ALPHA32(c)
  3.  

Demo test:
Code: QB64: [Select]
  1. red = 50: green = 100: blue = 200: alpha = 100
  2. ColorNumber~& = _RGBA32(red, green, blue, alpha)
  3. PRINT red, green, blue, alpha, ColorNumber~&
  4. cAnalysis ColorNumber~&, outR, outG, outB, outA
  5. PRINT outR, outG, outB, outA
  6.  
  7.  
  8. 'And the reverse, simple handy SUB for analyzing a color:
  9.  
  10. SUB cAnalysis (c AS _UNSIGNED LONG, outRed, outGrn, outBlu, outAlp)
  11.     outRed = _RED32(c): outGrn = _GREEN32(c): outBlu = _BLUE32(c): outAlp = _ALPHA32(c)
  12.  

Convert red, green, blue, alpha to Binary, Hex, Oct....

« Last Edit: December 17, 2020, 01:07:37 pm by bplus »

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Base 64 encoder
« Reply #2 on: December 17, 2020, 04:26:13 pm »
Here's my Base-128 converter:

Code: QB64: [Select]
  1. SCREEN _NEWIMAGE(640, 480, 32)
  2.  
  3. a$ = "  The Old Woman and the Wine-Jar" + CHR$(10)
  4. a$ = a$ + "AN OLD WOMAN found an empty jar which had lately been full of prime old wine and which still retained the fragrant smell of its former contents.  She greedily placed it several times to her nose, and drawing it backwards and forwards said, 'O most delicious! How nice must the Wine itself have been, when it leaves behind in the very vessel which contained it so sweet a perfume!'" + CHR$(10)
  5. a$ = a$ + "The memory of a good deed lives.     "
  6.  
  7. d$ = B256to128$(a$)
  8. i$ = B128to256$(d$)
  9. PRINT "-----------------"
  10. PRINT "-----------------"
  11. PRINT "-----------------"
  12. PRINT "Length of original:"; LEN(a$)
  13. PRINT "Length of 128-bit DATA ready conversion:"; LEN(d$)
  14. PRINT "Length of restored:"; LEN(i$)
  15.  
  16.  
  17.  
  18.  
  19.  
  20. FUNCTION B256to128$ (temp$)
  21.     text$ = _DEFLATE$(temp$)
  22.     l = 8 * LEN(text$)
  23.     'convert the text to the 8 bit array
  24.     FOR i = 1 TO LEN(text$)
  25.         b = ASC(text$, i)
  26.         p = (i - 1) * 8 + 1
  27.         FOR j = 0 TO 7
  28.             IF b AND (2 ^ j) THEN A(p + j) = 1 'ELSE A(p + j) = 0
  29.         NEXT
  30.     NEXT
  31.     'convert the array to 7bit strings
  32.     FOR i = 1 TO l STEP 7
  33.         b = 0
  34.         FOR j = 6 TO 0 STEP -1
  35.             IF i + j < l THEN
  36.                 IF A(i + j) THEN b = b + (2 ^ j)
  37.             END IF
  38.         NEXT
  39.         b = b + 59
  40.         t$ = t$ + CHR$(b)
  41.  
  42.     NEXT
  43.     B256to128 = t$
  44.  
  45. FUNCTION B128to256$ (text$)
  46.     l = 7 * LEN(text$)
  47.     'convert the text to the 8 bit array
  48.     FOR i = 1 TO LEN(text$)
  49.         b = ASC(text$, i) - 59
  50.         FOR j = 0 TO 6
  51.             p = p + 1: IF p > l THEN EXIT FOR
  52.             IF b AND (2 ^ j) THEN A(p) = 1 ELSE A(p) = 0
  53.         NEXT
  54.     NEXT
  55.     'convert the array to 8bit strings
  56.     p = 0
  57.     FOR i = 1 TO l STEP 8
  58.         b = 0
  59.         FOR j = 0 TO 7
  60.             p = p + 1
  61.             IF p > l THEN EXIT FOR
  62.             IF A(p) THEN b = b + (2 ^ j)
  63.         NEXT
  64.         t$ = t$ + CHR$(b)
  65.     NEXT
  66.     B128to256 = _INFLATE$(t$)

With this, I can take file assets such as images, sounds, or memblocks, and I can convert them down from BASE-256 characters to compressed BASE-128 characters, so that they'll paste into a DATA statement without any invalid characters causing problems.

For example, we can't have this in QB64:

DATA He said, "Foobar on your toolbar."

Those quotes and commas are problematic, as would a CHR$(0) or ":" be.  Not all 256 characters play nice with DATA statements, so I convert my data down to 128-bytes and place them into a program instead. 

Note: Routines above haven't been optimized for speed, so they may take a few seconds to convert and restore your data, in the case of large image or sound files.  I should honestly fix that sometime soon, but since I usually only use these routines for small assets (such as storing internal data for such things such as character information), it's never been too much of an issue for me.  Just be aware:  Large files can take a while to process with this method -- especially on an older, slower machine.
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Re: Base 64 encoder
« Reply #3 on: December 17, 2020, 06:50:44 pm »
with base 256 to base 64 conversion, it will  require 4 base 64 characters to encode 3 bytes.

with base 256 to base 128 conversion, it will require 4 base 128 characters to encode 3 bytes.

4 characters are required for both methods

base 256 to base 64 conversion is exactly efficient because 256^3 = 64^4

Any encoded characters like "," or quotes, can be mapped to other characters which is much easier since there are only 64 characters in the encoding set.
« Last Edit: December 17, 2020, 07:03:34 pm by NOVARSEG »

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Base 64 encoder
« Reply #4 on: December 17, 2020, 07:48:21 pm »
with base 256 to base 64 conversion, it will  require 4 base 64 characters to encode 3 bytes.

with base 256 to base 128 conversion, it will require 4 base 128 characters to encode 3 bytes.

4 characters are required for both methods

base 256 to base 64 conversion is exactly efficient because 256^3 = 64^4

Any encoded characters like "," or quotes, can be mapped to other characters which is much easier since there are only 64 characters in the encoding set.

Base 64 lets you pack 6 bits of data per character.
Base 128 lets you pack 7 bits of data per character.

So 7 bytes of ASCII data is 56-BITS.

In base-64, you'll need 56/6 = 10 characters to hold your data.  (Round up, as you don't have fractional characters.)

In base-128, you'll need 56/7 = 8 characters to hold your data. 

It's simple to see how base-128 packs more data into 7-bits than base-64 can pack into 6-bits.

How often are you converting ONLY 3 bytes?  Why not convert a whole row of pixels at once?  Or the whole screen?  No need to do pixel by pixel, when you can do image by image.
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Re: Base 64 encoder / decoder
« Reply #5 on: December 17, 2020, 10:00:41 pm »
SMcNeill

Hi
In the previous example I'm processing 3 bytes at a time.

For longer strings say 21 bytes * 8 = 168 bits

base 64 encoding would require 28 characters

Putting the 21 bytes into 7 bit bytes would require  only 24 characters

So that is 14.2% smaller encoding for the 7 bit method.

***************
In a convertor that Dav posted the encoded data looks to be base 64. Here is a bit of code from his convertor.

   
Quote
A$ = A$ + "8d;ai7=B`OhkhbAWZm^?Fbe6f0W?h7feD]G466?ShkeZZW3K;QCaWihI9W;b"
    A$ = A$ + "m7>F^WijPf?n<[cniJbW=]JNo=`efa5IkHO:\:hK`cP[1WSWQlL;oH2edC1C"
    A$ = A$ + "RWe`X`^Ah<df0J?9fHHG>Uf:aCK9X=SfEH9oghnoIT6[dl==i<0]513JdFZF"
    A$ = A$ + "J[dL\g7hoVf;;GYSbeoo[Y#?aO#e;iaGn9YJgl`V>EKUKf^17RH4R69RI0a3"
    A$ = A$ + "H\V;AOG;C;FShWNlA?7?O^oJcanNNR=IRbe6ZEii?g0BFQ6TLg9jEnYO__Ul"
    A$ = A$ + "gn7mk:O_?ok;OQGlnb[Om;:_be?_l_jCoZbgi;nITk<aPfkdn?oI9;MJ?SaW"
    A$ = A$ + "Xo1n;K6coLUIm\CAVcDWZ\\i?OIQcIfR?;HnbbFlRlDlI9b9>jQTSOT38[Ni"
    A$ = A$ + ";FFa2V[\YE^2eN[E9Kc_EJfd6PZKJk:_Jo[OEbagF0VV2G<c;#]W>g4>Q5cK"
    A$ = A$ + "\IRo3Q]0Wa]=n<[CaC<g?VIK5U2lVS`XYiYm14eGQUh\KS2eOMjUH9n?1MGH"
    A$ = A$ + "38<#HB=DXfnH9I;iYJb8_3Yn1e=XnbPeGaTnN9Ge>WJb#]W9g<dg=EKLR?:k"
    A$ = A$ + "H;8fCJea:9aH\SSmAMdRC8JS=DJBaJJJZ;kd\j`lIHMiYf7Q=<dFNmh_jdRc"
    A$ = A$ + "N_7MED7ZV<CdhB`Thc`ZQiRo\QMbkEh?9>g3DGijcg`<o9ae0^XSS`faGC\M"

The encoded bytes range from ASC 48  "0"  to ASC 111 "o".  111 - 48  = 63 (64 characters). In that range, the comma and quotes are avoided.

However, the base 128 encoding packs in more data.

« Last Edit: December 17, 2020, 10:04:22 pm by NOVARSEG »

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Base 64 encoder / decoder
« Reply #6 on: December 17, 2020, 10:13:41 pm »
If you're curious, here's where Petr and I had a nice discussion over Base64 encoding/decoding: https://www.qb64.org/forum/index.php?topic=2017.msg112422#msg112422
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Re: Base 64 encoder / decoder
« Reply #7 on: December 17, 2020, 11:06:14 pm »
I just fixed major bugs in my code up top.  It works now.

I read the link you posted. 

 7 bytes = 8  (7 bit) bytes Ok I will try that.



« Last Edit: December 18, 2020, 01:04:16 am by NOVARSEG »

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Re: Base 64 encoder / decoder
« Reply #8 on: December 18, 2020, 01:01:25 am »
Here is a  base 256 to base 128 encoder.
Does not decode yet.   Encodes 7 bytes at a time.

Code: QB64: [Select]
  1.  'BASE 128 encoder
  2.  
  3. DIM b8, b7, b6, b5, b4, b3, b2, b1 AS INTEGER
  4.  
  5.  
  6. DIM string1 AS STRING 'input string
  7. DIM string2 AS STRING 'output string
  8. '
  9. string1 = "ASCII stands for American Standard Code for Information Interchange..."
  10. PRINT LEN(string1) 'must be a multiple of 7
  11.  
  12. PRINT "STRING1 "; string1
  13.  
  14. n = 45 'offset into ASCII table
  15.  
  16.  
  17. FOR x% = 1 TO LEN(string1) STEP 7
  18.     b7 = ASC(MID$(string1, x%, 1))
  19.     b6 = ASC(MID$(string1, x% + 1, 1))
  20.     b5 = ASC(MID$(string1, x% + 2, 1))
  21.     b4 = ASC(MID$(string1, x% + 3, 1))
  22.     b3 = ASC(MID$(string1, x% + 4, 1))
  23.     b2 = ASC(MID$(string1, x% + 5, 1))
  24.     b1 = ASC(MID$(string1, x% + 6, 1))
  25.  
  26.  
  27.     IF b7 >= 128 THEN
  28.         b8 = 64
  29.         b7 = b7 - 128
  30.     END IF
  31.     s7 = CHR$(b7 + n)
  32.  
  33.     IF b6 >= 128 THEN
  34.         b8 = b8 + 32
  35.         b6 = b6 - 128
  36.     END IF
  37.     s6 = CHR$(b6 + n)
  38.  
  39.     IF b5 >= 128 THEN
  40.         b8 = b8 + 16
  41.         b5 = b5 - 128
  42.     END IF
  43.     s5 = CHR$(b5 + n)
  44.  
  45.     IF b4 >= 128 THEN
  46.         b8 = b8 + 8
  47.         b4 = b4 - 128
  48.     END IF
  49.     s4 = CHR$(b4 + n)
  50.  
  51.     IF b3 >= 128 THEN
  52.         b8 = b8 + 4
  53.         b3 = b3 - 128
  54.     END IF
  55.     s3 = CHR$(b3 + n)
  56.  
  57.     IF b2 >= 128 THEN
  58.         b8 = b8 + 2
  59.         b2 = b2 - 128
  60.     END IF
  61.     s2 = CHR$(b2 + n)
  62.  
  63.     IF b1 >= 128 THEN
  64.         b8 = b8 + 1
  65.         b1 = b1 - 128
  66.     END IF
  67.     s1 = CHR$(b1 + n)
  68.  
  69.     s8 = CHR$(b8+n)
  70.    
  71.     string2 = string2 + (s8 + s7 + s6 + s5 + s4 + s3 + s2 + s1)
  72. NEXT x%
  73.  
  74.  
  75. PRINT string2
  76.  
  77.  

« Last Edit: December 18, 2020, 02:20:56 am by NOVARSEG »

Offline SpriggsySpriggs

  • Forum Resident
  • Posts: 1145
  • Larger than life
    • View Profile
    • GitHub
Re: Base 64 encoder / decoder
« Reply #9 on: December 18, 2020, 08:28:02 am »
@NOVARSEG

My API collection has WinAPI code for encoding and decoding in Base 64. Feel free to check it out.
Shuwatch!

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Base 64 encoder / decoder
« Reply #10 on: December 18, 2020, 09:53:50 am »
@NOVARSEG

My API collection has WinAPI code for encoding and decoding in Base 64. Feel free to check it out.

@SpriggsySpriggs -- I was looking at your encoder/decoder...  Are you certain you need to add a CHR$(0) to the code?

        encode = encode + CHR$(0)

This isn't a null-terminated C-function; you have to specify the length of the string for it to work properly.  As it is, you're adding characters to your conversion routines, as below:

Code: QB64: [Select]
  1.  
  2. DIM encode AS STRING
  3. PRINT "My name is Inigo Montoya", LEN("My name is Inigo Montoya")
  4. encode = encodeBase64("My name is Inigo Montoya")
  5.  
  6. PRINT encode, LEN(encode)
  7.  
  8. DIM decode AS STRING
  9. decode = decodeBase64(encode)
  10.  
  11. PRINT decode, LEN(decode)
  12.  
  13.  
  14. $IF WIN THEN
  15.     'DECLARE LIBRARY ".\pipecom"
  16.     '    FUNCTION pipecom$ (cmd AS STRING)
  17.     'END DECLARE
  18.     'FUNCTION encodeBase64$ (encode AS STRING)
  19.     '    DIM encoded AS STRING
  20.     '    DIM encodefile AS INTEGER
  21.     '    encodefile = FREEFILE
  22.     '    OPEN "3nc0deb64" FOR OUTPUT AS #encodefile
  23.     '    PRINT #encodefile, encode
  24.     '    CLOSE #encodefile
  25.     '    encoded = pipecom("certutil -encode 3nc0deb64 3nc0dedb64 && type 3nc0dedb64 && del 3nc0dedb64 && del 3nc0deb64")
  26.     '    encoded = MID$(encoded, INSTR(encoded, "-----BEGIN CERTIFICATE-----") + LEN("-----BEGIN CERTIFICATE-----") + 1)
  27.     '    encoded = MID$(encoded, 1, INSTR(encoded, "-----END CERTIFICATE-----") - 2)
  28.     '    encodeBase64 = encoded
  29.     'END FUNCTION
  30.  
  31.     'FUNCTION decodeBase64$ (decode AS STRING)
  32.     '    DIM decoded AS STRING
  33.     '    DIM decodefile AS INTEGER
  34.     '    decodefile = FREEFILE
  35.     '    OPEN "d3c0deb64" FOR OUTPUT AS #decodefile
  36.     '    PRINT #decodefile, decode
  37.     '    CLOSE #decodefile
  38.     '    decoded = pipecom("certutil -decode d3c0deb64 d3c0dedb64 && type d3c0dedb64 && del d3c0deb64 && del d3c0dedb64")
  39.     '    decoded = MID$(decoded, _INSTRREV(decoded, "successfully.") + 1 + LEN("successfully."))
  40.     '    decoded = MID$(decoded, 1, LEN(decoded) - 1)
  41.     '    decodeBase64 = decoded
  42.     'END FUNCTION
  43.  
  44.     DECLARE DYNAMIC LIBRARY "Crypt32"
  45.         FUNCTION CryptBinaryToString%% ALIAS CryptBinaryToStringA (BYVAL pbBinary AS _OFFSET, BYVAL cbBinary AS LONG, BYVAL dwFlags AS LONG, BYVAL pszString AS _OFFSET, BYVAL pcchString AS _OFFSET)
  46.         FUNCTION CryptStringToBinary%% ALIAS CryptStringToBinaryA (BYVAL pszString AS _OFFSET, BYVAL cchString AS LONG, BYVAL dwFlags AS LONG, BYVAL pbBinary AS _OFFSET, BYVAL pcbBinary AS _OFFSET, BYVAL pdwSkip AS _OFFSET, BYVAL pdwFlags AS _OFFSET)
  47.     END DECLARE
  48.  
  49.     FUNCTION encodeBase64$ (encode AS STRING)
  50.         CONST CRYPT_STRING_NOCRLF = &H40000000
  51.         CONST CRYPT_STRING_BASE64 = &H00000001
  52.  
  53.         DIM a AS _BYTE
  54.         DIM lengthencode AS LONG
  55.         DIM encoded AS STRING
  56.         DIM lengthencoded AS LONG
  57.  
  58.         encode = encode + CHR$(0)
  59.         lengthencode = LEN(encode)
  60.  
  61.         a = CryptBinaryToString(_OFFSET(encode), lengthencode, CRYPT_STRING_BASE64 OR CRYPT_STRING_NOCRLF, 0, _OFFSET(lengthencoded))
  62.         'Calculate buffer length
  63.         IF a <> 0 THEN
  64.             encoded = SPACE$(lengthencoded)
  65.         ELSE
  66.             encodeBase64 = ""
  67.             EXIT FUNCTION
  68.         END IF
  69.         a = CryptBinaryToString(_OFFSET(encode), lengthencode, CRYPT_STRING_BASE64 OR CRYPT_STRING_NOCRLF, _OFFSET(encoded), _OFFSET(lengthencoded))
  70.         'Acual conversion
  71.         IF a <> 0 THEN
  72.             encodeBase64 = encoded
  73.         ELSE
  74.             encodeBase64 = ""
  75.         END IF
  76.  
  77.     FUNCTION decodeBase64$ (decode AS STRING)
  78.         CONST CRYPT_STRING_BASE64_ANY = &H00000006
  79.  
  80.         DIM a AS _BYTE
  81.         DIM lengthdecode AS LONG
  82.         DIM decoded AS STRING
  83.         DIM lengthdecoded AS LONG
  84.  
  85.         decode = decode + CHR$(0)
  86.         lengthdecode = LEN(decode) - 1
  87.         a = CryptStringToBinary(_OFFSET(decode), lengthdecode, CRYPT_STRING_BASE64_ANY, 0, _OFFSET(lengthdecoded), 0, 0)
  88.         'Calculate buffer length
  89.         IF a <> 0 THEN
  90.             decoded = SPACE$(lengthdecoded)
  91.         ELSE
  92.             decodeBase64 = ""
  93.             EXIT FUNCTION
  94.         END IF
  95.         a = CryptStringToBinary(_OFFSET(decode), lengthdecode, CRYPT_STRING_BASE64_ANY, _OFFSET(decoded), _OFFSET(lengthdecoded), 0, 0)
  96.         'Actual conversion
  97.         IF a <> 0 THEN
  98.             decodeBase64 = decoded
  99.         ELSE
  100.             decodeBase64 = ""
  101.         END IF
  102.     DECLARE LIBRARY "./pipecom"
  103.     FUNCTION pipecom$ (cmd AS STRING)
  104.     END DECLARE
  105.  
  106.     FUNCTION encodeBase64$ (encode AS STRING)
  107.     DIM encoded AS STRING
  108.     DIM encodefile AS INTEGER
  109.     encodefile = FREEFILE
  110.     OPEN "3nc0deb64" FOR OUTPUT AS #encodefile
  111.     PRINT #encodefile, encode
  112.     CLOSE #encodefile
  113.     encoded = pipecom("base64 3nc0deb64")
  114.     KILL "3nc0deb64"
  115.     encodeBase64 = MID$(encoded, 1, LEN(encoded) - 1)
  116.  
  117.     FUNCTION decodeBase64$ (decode AS STRING)
  118.     DIM decoded AS STRING
  119.     DIM decodefile AS INTEGER
  120.     decodefile = FREEFILE
  121.     OPEN "d3c0deb64" FOR OUTPUT AS #decodefile
  122.     PRINT #decodefile, decode
  123.     CLOSE #decodefile
  124.     decoded = pipecom("base64 -d d3c0deb64")
  125.     KILL "d3c0deb64"
  126.     decodeBase64 = decoded

If you notice, you've tacked a stray CHR$(0) onto the end of the decoded results. 
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline SpriggsySpriggs

  • Forum Resident
  • Posts: 1145
  • Larger than life
    • View Profile
    • GitHub
Re: Base 64 encoder / decoder
« Reply #11 on: December 18, 2020, 09:58:07 am »
@SMcNeill

https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptstringtobinarya

What I should have done instead is make the length argument be zero or just not do a CHR$(0). I think there might have been issues and that's why I did that but feel free to comment that one line out.
Shuwatch!

Offline SpriggsySpriggs

  • Forum Resident
  • Posts: 1145
  • Larger than life
    • View Profile
    • GitHub
Re: Base 64 encoder / decoder
« Reply #12 on: December 18, 2020, 10:04:36 am »
@SMcNeill

Yeah, I just got used to having to do null-terminated strings. I'll fix the code in the zip folder momentarily.

 
Screenshot 2020-12-18 100337.png


Code: QB64: [Select]
  1. 'DIM encode AS STRING
  2. 'encode = encodeBase64("My name is Inigo Montoya")
  3. DIM encoded AS STRING
  4. 'PRINT encode
  5.     LINE INPUT "Input Base64 String: ", encoded
  6. LOOP UNTIL encoded <> ""
  7. DIM decode AS STRING
  8. decode = decodeBase64(encoded)
  9.  
  10. PRINT decode
  11.  
  12. $IF WIN THEN
  13.     'DECLARE LIBRARY ".\pipecom"
  14.     '    FUNCTION pipecom$ (cmd AS STRING)
  15.     'END DECLARE
  16.     'FUNCTION encodeBase64$ (encode AS STRING)
  17.     '    DIM encoded AS STRING
  18.     '    DIM encodefile AS INTEGER
  19.     '    encodefile = FREEFILE
  20.     '    OPEN "3nc0deb64" FOR OUTPUT AS #encodefile
  21.     '    PRINT #encodefile, encode
  22.     '    CLOSE #encodefile
  23.     '    encoded = pipecom("certutil -encode 3nc0deb64 3nc0dedb64 && type 3nc0dedb64 && del 3nc0dedb64 && del 3nc0deb64")
  24.     '    encoded = MID$(encoded, INSTR(encoded, "-----BEGIN CERTIFICATE-----") + LEN("-----BEGIN CERTIFICATE-----") + 1)
  25.     '    encoded = MID$(encoded, 1, INSTR(encoded, "-----END CERTIFICATE-----") - 2)
  26.     '    encodeBase64 = encoded
  27.     'END FUNCTION
  28.  
  29.     'FUNCTION decodeBase64$ (decode AS STRING)
  30.     '    DIM decoded AS STRING
  31.     '    DIM decodefile AS INTEGER
  32.     '    decodefile = FREEFILE
  33.     '    OPEN "d3c0deb64" FOR OUTPUT AS #decodefile
  34.     '    PRINT #decodefile, decode
  35.     '    CLOSE #decodefile
  36.     '    decoded = pipecom("certutil -decode d3c0deb64 d3c0dedb64 && type d3c0dedb64 && del d3c0deb64 && del d3c0dedb64")
  37.     '    decoded = MID$(decoded, _INSTRREV(decoded, "successfully.") + 1 + LEN("successfully."))
  38.     '    decoded = MID$(decoded, 1, LEN(decoded) - 1)
  39.     '    decodeBase64 = decoded
  40.     'END FUNCTION
  41.  
  42.     DECLARE DYNAMIC LIBRARY "Crypt32"
  43.         FUNCTION CryptBinaryToString%% ALIAS CryptBinaryToStringA (BYVAL pbBinary AS _OFFSET, BYVAL cbBinary AS LONG, BYVAL dwFlags AS LONG, BYVAL pszString AS _OFFSET, BYVAL pcchString AS _OFFSET)
  44.         FUNCTION CryptStringToBinary%% ALIAS CryptStringToBinaryA (BYVAL pszString AS _OFFSET, BYVAL cchString AS LONG, BYVAL dwFlags AS LONG, BYVAL pbBinary AS _OFFSET, BYVAL pcbBinary AS _OFFSET, BYVAL pdwSkip AS _OFFSET, BYVAL pdwFlags AS _OFFSET)
  45.     END DECLARE
  46.  
  47.     FUNCTION encodeBase64$ (encode AS STRING)
  48.         CONST CRYPT_STRING_NOCRLF = &H40000000
  49.         CONST CRYPT_STRING_BASE64 = &H00000001
  50.  
  51.         DIM a AS _BYTE
  52.         DIM lengthencode AS LONG
  53.         DIM encoded AS STRING
  54.         DIM lengthencoded AS LONG
  55.  
  56.         lengthencode = LEN(encode)
  57.  
  58.         a = CryptBinaryToString(_OFFSET(encode), lengthencode, CRYPT_STRING_BASE64 OR CRYPT_STRING_NOCRLF, 0, _OFFSET(lengthencoded))
  59.         'Calculate buffer length
  60.         IF a <> 0 THEN
  61.             encoded = SPACE$(lengthencoded)
  62.         ELSE
  63.             encodeBase64 = ""
  64.             EXIT FUNCTION
  65.         END IF
  66.         a = CryptBinaryToString(_OFFSET(encode), lengthencode, CRYPT_STRING_BASE64 OR CRYPT_STRING_NOCRLF, _OFFSET(encoded), _OFFSET(lengthencoded))
  67.         'Acual conversion
  68.         IF a <> 0 THEN
  69.             encodeBase64 = encoded
  70.         ELSE
  71.             encodeBase64 = ""
  72.         END IF
  73.  
  74.     FUNCTION decodeBase64$ (decode AS STRING)
  75.         CONST CRYPT_STRING_BASE64_ANY = &H00000006
  76.  
  77.         DIM a AS _BYTE
  78.         DIM lengthdecode AS LONG
  79.         DIM decoded AS STRING
  80.         DIM lengthdecoded AS LONG
  81.  
  82.         lengthdecode = LEN(decode)
  83.         a = CryptStringToBinary(_OFFSET(decode), lengthdecode, CRYPT_STRING_BASE64_ANY, 0, _OFFSET(lengthdecoded), 0, 0)
  84.         'Calculate buffer length
  85.         IF a <> 0 THEN
  86.             decoded = SPACE$(lengthdecoded)
  87.         ELSE
  88.             decodeBase64 = ""
  89.             EXIT FUNCTION
  90.         END IF
  91.         a = CryptStringToBinary(_OFFSET(decode), lengthdecode, CRYPT_STRING_BASE64_ANY, _OFFSET(decoded), _OFFSET(lengthdecoded), 0, 0)
  92.         'Actual conversion
  93.         IF a <> 0 THEN
  94.             decodeBase64 = decoded
  95.         ELSE
  96.             decodeBase64 = ""
  97.         END IF
  98.     DECLARE LIBRARY "./pipecom"
  99.     FUNCTION pipecom$ (cmd AS STRING)
  100.     END DECLARE
  101.  
  102.     FUNCTION encodeBase64$ (encode AS STRING)
  103.     DIM encoded AS STRING
  104.     DIM encodefile AS INTEGER
  105.     encodefile = FREEFILE
  106.     OPEN "3nc0deb64" FOR OUTPUT AS #encodefile
  107.     PRINT #encodefile, encode
  108.     CLOSE #encodefile
  109.     encoded = pipecom("base64 3nc0deb64")
  110.     KILL "3nc0deb64"
  111.     encodeBase64 = MID$(encoded, 1, LEN(encoded) - 1)
  112.  
  113.     FUNCTION decodeBase64$ (decode AS STRING)
  114.     DIM decoded AS STRING
  115.     DIM decodefile AS INTEGER
  116.     decodefile = FREEFILE
  117.     OPEN "d3c0deb64" FOR OUTPUT AS #decodefile
  118.     PRINT #decodefile, decode
  119.     CLOSE #decodefile
  120.     decoded = pipecom("base64 -d d3c0deb64")
  121.     KILL "d3c0deb64"
  122.     decodeBase64 = decoded
Shuwatch!

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Base 64 encoder / decoder
« Reply #13 on: December 18, 2020, 10:13:36 am »
One more pointer, which might be of interest to you:  When passing pointers, you can just declare it without BYVAL.

Instead of:
  FUNCTION CryptBinaryToString%% ALIAS CryptBinaryToStringA (BYVAL pbBinary AS _OFFSET....

You can go with:
  FUNCTION CryptBinaryToString%% ALIAS CryptBinaryToStringA (pbBinary AS STRING....

Then when you call the function, instead of this:
a = CryptBinaryToString(_OFFSET(encode)....

It'd be:
a = CryptBinaryToString(encode....




It might be a trick to help simplify your libraries a bit, and cut down on some typing for you.  (Not just on strings, but all cases where pointers to something is required, rather than the actual data value itself.)

Not that what you're doing is wrong; I just thought you might not realize WHY that BYVAL is there, and what we do when it's not.  ;)

With BYVAL, we pass the value.
Without it, we pass the offset to whatever.
« Last Edit: December 18, 2020, 10:15:48 am by SMcNeill »
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline SpriggsySpriggs

  • Forum Resident
  • Posts: 1145
  • Larger than life
    • View Profile
    • GitHub
Re: Base 64 encoder / decoder
« Reply #14 on: December 18, 2020, 10:21:31 am »
@SMcNeill

Yeah, I know I can. I typically pass _OFFSETs unless it is more convenient for myself to just pass it as text rather than a variable (usually it isn't, due to the number of arguments, so I just write a wrapper that does all that B.S.). So instead of foo(_OFFSET(stringvalue$)) I would just do foo("this is my string"). I try to stay consistent with my code. Since I've done almost all of my code using _OFFSET rather than passing the string itself I just go ahead and keep using _OFFSETs unless absolutely necessary. I don't mind the extra typing. I passed this variable as a string for this particular function (different topic) because it didn't require a null character at the end and would be too much work for a 3 character string.

Code: QB64: [Select]
  1. GetDiskFreeSpace("L:\", 0, 0, _OFFSET(totalFreeBytesOnDisk))

But yeah, I typically don't pass a string to a function without _OFFSET unless I pass it as plain text.
« Last Edit: December 18, 2020, 10:23:30 am by SpriggsySpriggs »
Shuwatch!