Bug in the _base64Decode and proposed fix

Howdy,

I ran into a bit of a bug with the _base64Decode function and found a solution that could be easily integrated. I was saving unit data to XML by converting a piece of data to base64 and appending it to the the end of larger base64 string. Using base64 and the data was going in fine, and could sometimes be decoded fine, but other times the loading data would be gibberish or nothing at all. The problem was that some data would save with an = or an symbol in the middle of a set of data. The = symbol causes the _base64Decode function to leave the loop and stop decoding and check where the = an error or a base64 encoding feature. To solve the problem, when the function detects an = sign, the function does halt, but the check on what to do with the = sign is rolled into an if statement. This keeps the loop going so that appended data can be read properly. This is REALLY useful for saving date to XML using libxml2 and Cocos2d-x and avoiding to have to resort to platform specific save routines.

Modified _base64Decode code:

int _base64Decode( unsigned char *input, unsigned int input_len, unsigned char *output, unsigned int *output_len )
{
static char inalphabet[256], decoder[256];
int i, bits, c, char_count, errors = 0;
unsigned int input_idx = 0;
unsigned int output_idx = 0;

for (i = (sizeof alphabet) - 1; i \>= 0 ; i--) {
    inalphabet[alphabet[i]] = 1;
    decoder[alphabet[i]] = i;
}
char\_count = 0;
bits = 0;
for( input\_idx=0; input\_idx \< input\_len ; input\_idx++ ) {
    c = input[ input\_idx ];
    if (c  ‘=’){

if(char_count 1){
std::fprintf(stderr, “base64Decode: encoding incomplete: at least 2 bits missing”);
errors++;
break;
}
if(char_count 2){
output[ output_idx*+ ] = ;
}
if{
output[ output_idx*+ ] = ( bits >> 16 );
output[ output_idx*+ ] = & 0xff);
}
char_count = 0;
}else{
if
continue;
bits*= decoder[c];
char_count**;
if {
output[ output_idx** ] = (bits >> 16);
output[ output_idx*+ ] = & 0xff);
output[ output_idx*+ ] = ( bits & 0xff);
bits = 0;
char_count = 0;
} else {
bits <<= 6;
}
}
}
if ( input_idx < input_len ) {
if (char_count) {
std::fprintf(stderr, “base64 encoding incomplete: at least %d bits truncated”,
((4 - char_count) * 6));
errors++;
}
}
*output_len = output_idx;
return errors;
}

In my opinion, a correct base64 buffer will not contain ‘=’ or ‘==’ symbol except at the end. How can make this issue to be reappeared?

James Chen wrote:

In my opinion, a correct base64 buffer will not contain ‘=’ or ‘==’ symbol except at the end. How can make this issue to be reappeared?

Hi James,

From Wikipedia:

After encoding the non-padded data, if two octets of the 24-bit buffer are padded-zeros, two “=” characters are appended to the output; if one octet of the 24-bit buffer is filled with padded-zeros, one “=” character is appended. This signals the decoder that the zero bits added due to padding should be excluded from the reconstructed data. This also guarantees that the encoded output length is a multiple of 4 bytes.

As the code exists, the encoder returns an error and stops encoding if the first symbol is = and it is also the first character, which is correct, as this should not happen. However, if the an = or symbol appears at the end of a base 64 string, it just means that the data that was encoded had to be 0 padded, but the encoder treats this as an end-of-string event and halts the decoding algorithm.

It is possible to append multiple base64 strings together. For example, I have unit data that I want to save in a class:

class UnitDataToSave{
public:
char name[50];
int xLocation;
int yLocation;
int Life;
};

If I have a vector of n units each with a UnitDataToSave class, it is possible to:
1.) loop through the vector and encode each element into base64
2.) just append the strings together into one long base64 string.
3.) Upon decoding, prepare a buffer for UnitDataToSave which is n elements long (new UnitDataToSave[n]).
4.) The base64 encoder will, the the edits above, properly decode the appended string and put the data properly back in the buffer, so each element can be accessed

The problem with the original code was that one of the UnitDataToSave might have been encoded with an = or an in step 1 which would halt the decoder in step 4. The solution is to treat the = and == as a valid character, preform the bit shifting and padding, but simply reset the char_count to 0 so that the decoder can continue along.

Could the base64 decoder also be exported from the Cocos2-x library? This technique is incredibly useful for writing binary data to disk on ANY platform in XML format.

Thanks for taking the time to work with me on this issue,
Austin

thank you.

Hi, Austin McElroy,
Thanks for your feedback, I did the test you offer above. Your patch code work very well, good job,
but I think base64 in the cocos2d-x is just used for cocos2dx engine, not for the game. So we don’t consider to merge your patch.
Thank you all the same.:slight_smile:

this code is not well formatted please give proper