/* * Modification History * * 2003-August-22 Jason Rohrer * Created. * * 2003-September-22 Jason Rohrer * Added base64 encoding. * * 2004-March-21 Jason Rohrer * Fixed a variable scoping and redefinition bug pointed out by Benjamin Meyer. */ #include "encodingUtils.h" #include "minorGems/util/SimpleVector.h" #include #include char fourBitIntToHex( int inInt ) { char outChar[2]; if( inInt < 10 ) { sprintf( outChar, "%d", inInt ); } else { switch( inInt ) { case 10: outChar[0] = 'A'; break; case 11: outChar[0] = 'B'; break; case 12: outChar[0] = 'C'; break; case 13: outChar[0] = 'D'; break; case 14: outChar[0] = 'E'; break; case 15: outChar[0] = 'F'; break; default: outChar[0] = '0'; break; } } return outChar[0]; } // returns -1 if inHex is not a valid hex character int hexToFourBitInt( char inHex ) { int returnInt; switch( inHex ) { case '0': returnInt = 0; break; case '1': returnInt = 1; break; case '2': returnInt = 2; break; case '3': returnInt = 3; break; case '4': returnInt = 4; break; case '5': returnInt = 5; break; case '6': returnInt = 6; break; case '7': returnInt = 7; break; case '8': returnInt = 8; break; case '9': returnInt = 9; break; case 'A': case 'a': returnInt = 10; break; case 'B': case 'b': returnInt = 11; break; case 'C': case 'c': returnInt = 12; break; case 'D': case 'd': returnInt = 13; break; case 'E': case 'e': returnInt = 14; break; case 'F': case 'f': returnInt = 15; break; default: returnInt = -1; break; } return returnInt; } char *hexEncode( unsigned char *inData, int inDataLength ) { char *resultHexString = new char[ inDataLength * 2 + 1 ]; int hexStringIndex = 0; for( int i=0; i> 4 ); int lowBits = 0xF & ( currentByte ); resultHexString[ hexStringIndex ] = fourBitIntToHex( highBits ); hexStringIndex++; resultHexString[ hexStringIndex ] = fourBitIntToHex( lowBits ); hexStringIndex++; } resultHexString[ hexStringIndex ] = '\0'; return resultHexString; } unsigned char *hexDecode( char *inHexString ) { int hexLength = strlen( inHexString ); if( hexLength % 2 != 0 ) { // hex strings must be even in length return NULL; } int dataLength = hexLength / 2; unsigned char *rawData = new unsigned char[ dataLength ]; for( int i=0; i *encodingVector = new SimpleVector(); int numInLine = 0; // take groups of 3 data bytes and map them to 4 base64 digits for( int i=0; i> 18 ); unsigned int digitB = 0x3F & ( block >> 12 ); unsigned int digitC = 0x3F & ( block >> 6 ); unsigned int digitD = 0x3F & ( block ); encodingVector->push_back( binaryToAscii[ digitA ] ); encodingVector->push_back( binaryToAscii[ digitB ] ); encodingVector->push_back( binaryToAscii[ digitC ] ); encodingVector->push_back( binaryToAscii[ digitD ] ); numInLine += 4; if( inBreakLines && numInLine == 76 ) { // break the line encodingVector->push_back( '\r' ); encodingVector->push_back( '\n' ); numInLine = 0; } } else { // at end int numLeft = inDataLength - i; switch( numLeft ) { case 0: // no padding break; case 1: { // two digits, two pads unsigned int block = inData[i] << 16 | 0; unsigned int digitA = 0x3F & ( block >> 18 ); unsigned int digitB = 0x3F & ( block >> 12 ); encodingVector->push_back( binaryToAscii[ digitA ] ); encodingVector->push_back( binaryToAscii[ digitB ] ); encodingVector->push_back( '=' ); encodingVector->push_back( '=' ); break; } case 2: { // three digits, one pad unsigned int block = inData[i] << 16 | inData[i+1] << 8 | 0; // base64 digits, with digitA at left unsigned int digitA = 0x3F & ( block >> 18 ); unsigned int digitB = 0x3F & ( block >> 12 ); unsigned int digitC = 0x3F & ( block >> 6 ); encodingVector->push_back( binaryToAscii[ digitA ] ); encodingVector->push_back( binaryToAscii[ digitB ] ); encodingVector->push_back( binaryToAscii[ digitC ] ); encodingVector->push_back( '=' ); break; } default: break; } // done with all data i = inDataLength; } } char *returnString = encodingVector->getElementString(); delete encodingVector; return returnString; } unsigned char *base64Decode( char *inBase64String, int *outDataLength ) { SimpleVector *decodedVector = new SimpleVector(); int encodingLength = strlen( inBase64String ); SimpleVector *binaryEncodingVector = new SimpleVector(); int i; for( i=0; ipush_back( currentBinary ); } } int binaryEncodingLength = binaryEncodingVector->size(); unsigned char *binaryEncoding = binaryEncodingVector->getElementArray(); delete binaryEncodingVector; int blockCount = binaryEncodingLength / 4; if( binaryEncodingLength % 4 != 0 ) { // extra, 0-padded block blockCount += 1; } // take groups of 4 encoded digits and map them to 3 data bytes for( i=0; i> 16 ); unsigned int digitB = 0xFF & ( block >> 8 ); unsigned int digitC = 0xFF & ( block ); decodedVector->push_back( digitA ); decodedVector->push_back( digitB ); decodedVector->push_back( digitC ); } else { // at end int numLeft = binaryEncodingLength - i; switch( numLeft ) { case 0: // no padding break; case 1: { // impossible break; } case 2: { // two base64 digits, one data byte unsigned int block = binaryEncoding[i] << 18 | binaryEncoding[i+1] << 12 | 0; // data byte digits, with digitA at left unsigned int digitA = 0xFF & ( block >> 16 ); decodedVector->push_back( digitA ); break; } case 3: { // three base64 digits, two data bytes unsigned int block = binaryEncoding[i] << 18 | binaryEncoding[i+1] << 12 | binaryEncoding[i+2] << 6 | 0; // data byte digits, with digitA at left unsigned int digitA = 0xFF & ( block >> 16 ); unsigned int digitB = 0xFF & ( block >> 8 ); decodedVector->push_back( digitA ); decodedVector->push_back( digitB ); break; } default: break; } // done with all data i = binaryEncodingLength; } } delete [] binaryEncoding; *outDataLength = decodedVector->size(); unsigned char* returnData = decodedVector->getElementArray(); delete decodedVector; return returnData; }