--------------------------------------------------------------------------------------------------- -- -= Base64 =- --------------------------------------------------------------------------------------------------- -- Make some functions easier to write local floor = math.floor local sub = string.sub local gsub = string.gsub local rem = table.remove --------------------------------------------------------------------------------------------------- -- Our base64 value table local base64 = { ['A']=0,['B']=1,['C']=2,['D']=3,['E']=4,['F']=5,['G']=6,['H']=7,['I']=8, ['J']=9,['K']=10,['L']=11,['M']=12,['N']=13,['O']=14,['P']=15,['Q']=16, ['R']=17,['S']=18,['T']=19,['U']=20,['V']=21,['W']=22,['X']=23,['Y']=24, ['Z']=25,['a']=26,['b']=27,['c']=28,['d']=29,['e']=30,['f']=31,['g']=32, ['h']=33,['i']=34,['j']=35,['k']=36,['l']=37,['m']=38,['n']=39,['o']=40, ['p']=41,['q']=42,['r']=43,['s']=44,['t']=45,['u']=46,['v']=47,['w']=48, ['x']=49,['y']=50,['z']=51,['0']=52,['1']=53,['2']=54,['3']=55,['4']=56, ['5']=57,['6']=58,['7']=59,['8']=60,['9']=61,['+']=62,['/']=63,['=']=nil} --------------------------------------------------------------------------------------------------- -- Decimal values for binary digits local bin ={} local mult = 1 for i = 1,40 do bin[i] = mult mult = mult*2 end --------------------------------------------------------------------------------------------------- -- A buffer we will use to process the bits local buffer = 0 local pos = 0 local function clearBuffer() buffer = 0 pos = 1 end --------------------------------------------------------------------------------------------------- -- Shift all of the bits up in the buffer and put the base64 number on the bottom local function pushBase64(n) if base64[n] == nil then return end buffer = buffer * bin[7] + base64[n] pos = pos + 6 end --------------------------------------------------------------------------------------------------- -- Get an int out of the buffer. This is tricky. The byte order is in little endian so we're going -- to have to isolate and cut the bytes out and then move them around. local function getInt() -- If our buffer isn't filled all the way then fill it with zeros while pos < 33 do buffer = buffer * bin[2] pos = pos + 1 end -- Move the buffer position to just below the integer. pos = pos - 32 -- Swap the first and forth byte and then the second and third. local tmp = floor((buffer%bin[33+pos-1])/bin[25+pos-1]) + floor((buffer%bin[25+pos-1])/bin[17+pos-1])*bin[9] + floor((buffer%bin[17+pos-1])/bin[9+pos-1])*bin[17] + floor((buffer%bin[9+pos-1])/bin[pos])*bin[25] -- We've got our integer so let's cut that portion out of the buffer buffer = buffer % bin[pos] -- Return the int return tmp end --------------------------------------------------------------------------------------------------- -- Get a byte out of the buffer local function getByte() -- If our buffer isn't filled all the way then fill it with zeros while pos < 9 do buffer = buffer * bin[2] pos = pos + 1 end -- Move the buffer position to just below the byte. pos = pos - 8 -- Cut out the byte local tmp = floor((buffer%bin[9+pos-1])/bin[pos]) -- Delete the byte from the buffer buffer = buffer % bin[pos] -- Return the byte return tmp end --------------------------------------------------------------------------------------------------- -- Glues together an integer from four bytes. Little endian local function glueInt(b1, b2, b3, b4) return b1%bin[9] + b2%bin[9]*bin[9] + b3%bin[9]*bin[17] + b4%bin[9]*bin[25] end --------------------------------------------------------------------------------------------------- -- A Lua set that will filter out characters that aren't in the base64 table local set = "[^%a%d%+%/%=]" --------------------------------------------------------------------------------------------------- -- Decodes a base64 string into the given type local function decode(mode, raw) -- Make sure the mode is supported assert(mode=="string" or mode=="int" or mode=="byte", "Base64 decode - Invalid mode: " .. mode) -- Clear the buffer clearBuffer() -- Filters undefined characters out of the string raw = gsub(raw, set, "") local size = 0 -- Size of the returned type in bits local val = {} -- A table containing the data to be returned local raw_pos = 1 -- The position of the progress through the raw base64 string local raw_size = #raw -- The size of the base64 string local char = "" -- The current base64 character to be processed -- If we're expected to return an int then the bit size is 32, otherwise it's 8 if mode == "int" then size = 32 else size = 8 end -- While we still have input while raw_pos <= raw_size do -- Fill the buffer until we have enough bits while pos <= size and raw_pos <= raw_size do char = sub(raw,raw_pos,raw_pos) pushBase64( char ) raw_pos = raw_pos + 1 end -- If a nil character is encountered the end the loop if char == "=" then break end -- Get data from the buffer depending on the type if mode == "string" then val[#val+1] = string.char( getByte() ) end if mode == "byte" then val[#val+1] = getByte() end if mode == "int" then val[#val+1] = getInt() end end if mode == "string" then return table.concat(val) end return val end --------------------------------------------------------------------------------------------------- -- Encodes a table of ints into a base64 string. local function encode(ints) -- Clear the buffer clearBuffer() local size = 0 -- Size of the returned type in bits local val = {} -- A table containing the data to be returned local char = "" -- The current base64 character to be processed -- If we're expected to return an int then the bit size is 32, otherwise it's 8 if mode == "int" then size = 32 else size = 8 end -- While we still have input while raw_pos <= raw_size do -- Fill the buffer until we have enough bits while pos <= size and raw_pos <= raw_size do char = sub(raw,raw_pos,raw_pos) pushBase64( char ) raw_pos = raw_pos + 1 end -- If a nil character is encountered the end the loop if char == "=" then break end -- Get data from the buffer depending on the type if mode == "string" then val[#val+1] = string.char( getByte() ) end if mode == "byte" then val[#val+1] = getByte() end if mode == "int" then val[#val+1] = getInt() end end if mode == "string" then return table.concat(val) end return val end --------------------------------------------------------------------------------------------------- -- Returns the functions return {decode = decode, glueInt = glueInt, base64 = base64}