Sunday, February 2, 2014

Very Simple Base64 Reader

I've written this very simple Base64 reader. I wrote this as part of my loader for Tiled Map Editor files (see mapeditor.org). The code is very simple, but it works. The out_binary_data parameter is unsigned char * for simplicity as the code works in 8 bit chunks. Tiled actually uses 32 bit int variables so the pointer will need to be cast to int *. If you have anymore questions about the code, just leave a comment.


// converts a 6 bit value into the Base64 ASCII equivalent
unsigned char ASCIITo_6BitVal(char ch)
{
 if((ch >= 'A') && (ch <= 'Z')) return ch - 'A';
 else if((ch >= 'a') && (ch <= 'z')) return (ch - 'a') + 26;
 else if((ch >= '0') && (ch <= '9')) return (ch - '0') + 52;
 else if(ch == '+') return 62;
 else if(ch == '/') return 63;
 else if(ch == '=') return 64;
 else return 65;
}

// converts binary data to Base64, If the data will be used on a platform with a different endianness, the
// data 32 bit integers or the decoder will not be able to switch the endianness automatically
int Base64Binary(const char *in_base64_data, unsigned int in_base64_data_length,
                 unsigned char *out_binary_data, unsigned int in_binary_buffer_length, unsigned int &out_binary_data_length)
{
 out_binary_data_length = 0;

 for(unsigned int i = 0; i < in_base64_data_length; i += 4)
 {
  bool end_found = false;
  unsigned char b64_byte1 = ASCIITo_6BitVal(in_base64_data[i]);
  if(b64_byte1 > 63) return 1; // stop processing
  unsigned char b64_byte2 = ASCIITo_6BitVal(in_base64_data[i+1]);
  if(b64_byte2 > 63) return -1;
  unsigned char b64_byte3 = ASCIITo_6BitVal(in_base64_data[i+2]);
  if(b64_byte3 > 64) return -1; // this could be the end of the stream so it could also be '='
  unsigned char b64_byte4 = ASCIITo_6BitVal(in_base64_data[i+3]);
  if(b64_byte4 > 64) return -1; // this could be the end of the stream so it could also be '='

  int size = 3;
  // check for the end
  if(b64_byte4 == 64)
  {
   size = 2;
   end_found = true;
   b64_byte4 = 0;
  }
  if(b64_byte3 == 64)
  {
   size = 1;
   end_found = true;
   b64_byte3 = 0;
  }

  if(in_binary_buffer_length < (out_binary_data_length + size))
  {
   // the buffer is too small
   return -2;
  }

  out_binary_data[out_binary_data_length + 0] = (b64_byte1 << 2) | ((0x30 & b64_byte2) >> 4);
  if(size > 1)out_binary_data[out_binary_data_length + 1] = ((0xF & b64_byte2) << 4) | ((0x3C & b64_byte3) >> 2);
  if(size > 2)out_binary_data[out_binary_data_length + 2] = ((0x3 & b64_byte3) << 6) | b64_byte4;

  out_binary_data_length += size;

  if(end_found) break;
 }

 return 0;
}