When I learned QB64-PE allows resources to be loaded from memory it re-kindled my interest in encoding methods to put small files in BAS code, like Base64 is used in several programs found here. One step more space efficient than Base64 is Base85 (also called ASCII85) which outputs smaller encoded data and uses a larger character set, so I started trying to figure it out. It's been a real challenge as I am not very math savvy and there were no BASIC sources of Base85 out there I could find to study.
Here's what I have so far. This will take 4 bytes of binary data, convert it to an integer, divide that up and map them to a characterset, outputting 5 bytes of printable characters that can be included in BAS code. The decoder reverses the process. I have tested these functions on a few files, and they seem to encode/decode ok here - but they is VERY SLOW. If you would like to try them and/or add some improvements please do (Speed it up if you can). I'm also working on Base91 too, but it's not ready to post yet.
- Dav
Edit: Updated version. Now encode/decode much faster.
Here's what I have so far. This will take 4 bytes of binary data, convert it to an integer, divide that up and map them to a characterset, outputting 5 bytes of printable characters that can be included in BAS code. The decoder reverses the process. I have tested these functions on a few files, and they seem to encode/decode ok here - but they is VERY SLOW. If you would like to try them and/or add some improvements please do (Speed it up if you can). I'm also working on Base91 too, but it's not ready to post yet.
- Dav
Edit: Updated version. Now encode/decode much faster.
Code: (Select All)
'==========
'BASE85.BAS - v0.30
'==========
'Base85 Encoder/Decoder Functions.
'Coded by Dav for QB64-PE 3.8.0, SEP/2023
'CREDIT: Thanks to RhoSigma for helping me make these lightning fast!!
a1$ = Space$(1000000) ' make a 1MB string to test encoder/decoder speed
Print
Print "Original size:"; Len(a1$); "bytes."
Print
t1# = Timer
Print "Encoding"; Len(a1$); "bytes....";
a2$ = Base85Encode$(a1$)
Print Timer - t1#; "secs, output size:"; Len(a2$)
Print
t1# = Timer
Print "Decoding"; Len(a2$); "bytes....";
a3$ = Base85Decode$(a2$)
Print Timer - t1#; "secs, output:"; Len(a3$)
Print
If a1$ = a3$ Then
Print "Original Data and Decoded Data Match!"
Print Len(a1$), Len(a3$)
Else
Print "Original Data and Decoded DO NOT Match!"
Print Len(a1$), Len(a3$)
End If
Function Base85Encode$ (in$)
For i = 39 To 125 'Make 85 character set to use
If i <> 64 And i <> 96 Then c$ = c$ + Chr$(i)
Next
Dim v As _Unsigned Long
t$ = in$ 'make a working copy so in$ isn't changed
If Len(t$) Mod 4 > 0 Then 'pad needed bytes on end
a = 5 - Len(t$) Mod 4
t$ = t$ + Space$(a - 1)
End If
out$ = Space$(Len(t$) * 1.25): outb& = 1
For i& = 1 To Len(t$) Step 4
v = CVL(Mid$(t$, i&, 4))
For j& = 4 To 0 Step -1
p& = 85 ^ j&
r& = v \ p&
v = v Mod p&
Mid$(out$, outb&, 1) = Mid$(c$, r& + 1, 1)
outb& = outb& + 1
Next
Next
Base85Encode$ = LTrim$(RTrim$(Str$(a))) + out$
End Function
Function Base85Decode$ (in$)
For i = 39 To 125 'Make an 85 character set
If i <> 64 And i <> 96 Then c$ = c$ + Chr$(i)
Next
Dim v As _Unsigned Long
t$ = in$ 'use a working copy so in$ isn't changed
a = Val(Mid$(t$, 1, 1)) 'grab pad number
out$ = Space$(Len(t$) / 1.25 - 1): outb& = 1
For i& = 2 To Len(in$) Step 5: v = 0
For j& = 0 To 4: p& = 85 ^ (4 - j&)
cv& = InStr(c$, Mid$(t$, i& + j&, 1)) - 1
v = v + cv& * p&
Next
Mid$(out$, outb&, 4) = MKL$(v)
outb& = outb& + 4
Next
Base85Decode$ = Mid$(out$, 1, Len(out$) - a + 1)
End Function