void Crypto::Encrypt( const SimpleString& Plaintext, const Array< SimpleString >& Keys, Array< char >& OutCiphertext ) { Array< char > PlaintextArray; Array< char > KeyArray; Array< char > CiphertextArray; Plaintext.FillArray( PlaintextArray ); ConstructKeyFromStrings( Keys, KeyArray ); Encrypt( PlaintextArray, KeyArray, OutCiphertext ); }
SimpleString SimpleString::URLEncodeUTF8() const { // First, encode all the usual stuff... const SimpleString BasicEncoded = URLEncode(); Array<char> EncodedArray; BasicEncoded.FillArray( EncodedArray, true ); #define PERCENT_ENCODE_CHAR \ { \ const char c = EncodedArray[ CharIter ]; \ const uint8 UnsignedChar = static_cast<uint8>( c ); \ const SimpleString Encoded = PrintF( "%%%02X", UnsignedChar ); \ const char* const pEncoded = Encoded.CStr(); \ EncodedArray[ CharIter ] = *pEncoded; \ EncodedArray.Insert( *( pEncoded + 1 ), CharIter + 1 ); \ EncodedArray.Insert( *( pEncoded + 2 ), CharIter + 2 ); \ CharIter += 3; \ } for( uint CharIter = 0; CharIter < EncodedArray.Size(); ) { const char c = EncodedArray[ CharIter ]; if( ( c & UTF8_2BYTEMASKHIGH ) == UTF8_2BYTEMARKER ) { // Encode c and following 2 byte PERCENT_ENCODE_CHAR; PERCENT_ENCODE_CHAR; } else if( ( c & UTF8_3BYTEMASKHIGH ) == UTF8_3BYTEMARKER ) { // Encode c and following 2 bytes PERCENT_ENCODE_CHAR; PERCENT_ENCODE_CHAR; PERCENT_ENCODE_CHAR; } else if( ( c & UTF8_4BYTEMASKHIGH ) == UTF8_4BYTEMARKER ) { // Encode c and following 3 bytes PERCENT_ENCODE_CHAR; PERCENT_ENCODE_CHAR; PERCENT_ENCODE_CHAR; PERCENT_ENCODE_CHAR; } else { ++CharIter; } } SimpleString RetVal = EncodedArray; return RetVal; }
void ConfigParser::Parse( const IDataStream& Stream ) { Array< SToken > Tokens; SToken Token; Token.m_TokenType = SToken::ET_Name; char StrMark = 0; // Stores the character (either ' or ") that opened a string SToken MacroToken; List<int> ArrayCounters; Array<char> CounterCharArray; int LineCount = 1; for(;;) { char c = Stream.ReadInt8(); // Skip the UTF-8 byte order mark if present. if( UTF8_BOM_0 == static_cast<byte>( c ) ) { CHECK( UTF8_BOM_1 == static_cast<byte>( Stream.ReadInt8() ) ); CHECK( UTF8_BOM_2 == static_cast<byte>( Stream.ReadInt8() ) ); c = Stream.ReadInt8(); } if( Stream.EOS() ) { if( Token.m_TokenString.Empty() ) { Token.m_TokenType = SToken::ET_None; Tokens.PushBack( Token ); DEBUGCATPRINTF( "Core", 2, "%s\n", SToken::m_TokenNames[ Token.m_TokenType ] ); } else { Token.m_TokenString.PushBack( '\0' ); Tokens.PushBack( Token ); DEBUGCATPRINTF( "Core", 2, "%s: %s\n", SToken::m_TokenNames[ Token.m_TokenType ], Token.m_TokenString.GetData() ); Token.m_TokenString.Clear(); } break; } if( c == '&' ) { if( Token.m_TokenType == SToken::ET_Name ) { // Increment the current counter and add it to the current name. ASSERT( ArrayCounters.Size() > 0 ); List<int>::Iterator CounterIter = ArrayCounters.Back(); ( *CounterIter )++; SimpleString CounterString = SimpleString::PrintF( "%d", *CounterIter ); CounterCharArray.Clear(); CounterString.FillArray( CounterCharArray ); Token.m_TokenString.Append( CounterCharArray ); } else if( Token.m_TokenType == SToken::ET_None ) { // Add a new counter // Push a counter token that will be replaced with the count int later. ArrayCounters.PushBack( -1 ); Token.m_TokenType = SToken::ET_Counter; } else if( Token.m_TokenType == SToken::ET_String ) { Token.m_TokenString.PushBack( c ); } else { WARNDESC( "Unexpected character '&' in token." ); } } else if( c == '^' ) { if( Token.m_TokenType == SToken::ET_Name ) { // Add the current counter to the current name. ASSERT( ArrayCounters.Size() > 0 ); List<int>::Iterator CounterIter = ArrayCounters.Back(); ASSERT( ( *CounterIter ) >= 0 ); SimpleString CounterString = SimpleString::PrintF( "%d", *CounterIter ); CounterCharArray.Clear(); CounterString.FillArray( CounterCharArray ); Token.m_TokenString.Append( CounterCharArray ); } else if( Token.m_TokenType == SToken::ET_String ) { Token.m_TokenString.PushBack( c ); } else { WARNDESC( "Unexpected character '^' in token." ); } } else if( c == ' ' || c == '\t' ) { switch( Token.m_TokenType ) { case SToken::ET_None: case SToken::ET_Equals: // Ignore whitespace break; case SToken::ET_Name: case SToken::ET_Context: case SToken::ET_Macro: if( Token.m_TokenString.Empty() ) { // If the name is empty, ignore whitespace (before the name) } else { // Close current token, push it, and expect an equals // If we're closing a macro, save it as such if( Token.m_TokenType == SToken::ET_Macro ) { MacroToken = Token; } Token.m_TokenString.PushBack( '\0' ); Tokens.PushBack( Token ); DEBUGCATPRINTF( "Core", 2, "%s: %s\n", SToken::m_TokenNames[ Token.m_TokenType ], Token.m_TokenString.GetData() ); Token.m_TokenString.Clear(); if( Token.m_TokenType == SToken::ET_Name ) { Token.m_TokenType = SToken::ET_Equals; } else if( Token.m_TokenType == SToken::ET_Context || Token.m_TokenType == SToken::ET_Macro ) { Token.m_TokenType = SToken::ET_Name; } } break; case SToken::ET_Bool: case SToken::ET_Int: case SToken::ET_Float: // Close current token, push it, and expect nothing Token.m_TokenString.PushBack( '\0' ); Tokens.PushBack( Token ); DEBUGCATPRINTF( "Core", 2, "%s: %s\n", SToken::m_TokenNames[ Token.m_TokenType ], Token.m_TokenString.GetData() ); Token.m_TokenString.Clear(); Token.m_TokenType = SToken::ET_None; break; case SToken::ET_String: Token.m_TokenString.PushBack( c ); break; default: WARNDESC( "Unexpected token" ); break; } } else if( c == '=' ) { switch( Token.m_TokenType ) { case SToken::ET_Name: // Close current token, push it and an equals Token.m_TokenString.PushBack( '\0' ); Tokens.PushBack( Token ); DEBUGCATPRINTF( "Core", 2, "%s: %s\n", SToken::m_TokenNames[ Token.m_TokenType ], Token.m_TokenString.GetData() ); Token.m_TokenString.Clear(); Token.m_TokenType = SToken::ET_Equals; DEBUGCATPRINTF( "Core", 2, "%s\n", SToken::m_TokenNames[ Token.m_TokenType ] ); Tokens.PushBack( Token ); Token.m_TokenType = SToken::ET_None; break; case SToken::ET_Equals: // Already expecting =, just push it Tokens.PushBack( Token ); DEBUGCATPRINTF( "Core", 2, "%s\n", SToken::m_TokenNames[ Token.m_TokenType ] ); Token.m_TokenType = SToken::ET_None; break; case SToken::ET_String: Token.m_TokenString.PushBack( c ); break; default: WARNDESC( "Unexpected token" ); break; } } else if( c == '#' ) { // # starts a comment // Allow # inside a string if( Token.m_TokenType == SToken::ET_String ) { Token.m_TokenString.PushBack( c ); } else { c = Stream.ReadInt8(); if( c == '!' ) // #! and !# indicate the start and end of block comments { while( !Stream.EOS() ) { if( Stream.ReadInt8() == '!' && Stream.ReadInt8() == '#' ) { break; } } } else { // Read to end of line while( c != '\n' && c!= '\v' && !Stream.EOS() ) // Vertical tab stupidity again { c = Stream.ReadInt8(); } // Change the context because we're on a new line Token.m_TokenType = SToken::ET_Name; ++LineCount; } } } else if( c == '\\' && Token.m_TokenType == SToken::ET_String ) { // Escape sequence, intended to insert linebreaks (and maybe other things in the future) // Config string escape sequences are not the same as C++ escape sequences. They can be // \\, \n, \?, \", or \xx (where x are hex digits, to specify any character by hex). char next = Stream.ReadInt8(); if( next == 'n' ) { Token.m_TokenString.PushBack( '\n' ); } else if( next == '\"' ) { Token.m_TokenString.PushBack( '\"' ); } else if( next == '\\' ) { Token.m_TokenString.PushBack( '\\' ); } else if( next == 'x' ) { char Hex = 0; for( uint HexIndex = 0; HexIndex < 2; ++HexIndex ) { next = Stream.ReadInt8(); ASSERT( IsHex( next ) ); Hex = ( Hex << 4 ) | GetHex( next ); } Token.m_TokenString.PushBack( Hex ); } else if( next == 'u' ) { // First, extract a unicode code point (e.g. \u00d7 for U+00D7) // NOTE: This only support the first Unicode plane, and is strict about // using four characters, so \ud7 is not a valid substitute for \u00d7. unicode_t CodePoint = 0; for( uint UnicodeIndex = 0; UnicodeIndex < 4; ++UnicodeIndex ) { next = Stream.ReadInt8(); ASSERT( IsHex( next ) ); CodePoint = ( CodePoint << 4 ) | GetHex( next ); } // Then convert the two-byte code point to UTF-8. Array<unicode_t> CodePointArray; CodePointArray.PushBack( CodePoint ); const SimpleString UTF8String = SimpleString::SetUTF8( CodePointArray ); for( uint CharIndex = 0; CharIndex < UTF8String.Length(); ++CharIndex ) { const char NextChar = UTF8String.GetChar( CharIndex ); Token.m_TokenString.PushBack( NextChar ); } } else { PRINTF( "Unrecognized escape sequence \\%c at line %d\n", next, LineCount ); WARNDESC( "Unrecognized escape sequence" ); } } else if( c == 0x0d ) { // DOS linebreak is 0D 0A, so ignore and expect \n to follow } else if( c == '\0' ) { // Don't know how these are getting in either, but ignore them } else if( c == '\n' || c == '\v' ) { if( Token.m_TokenType == SToken::ET_Macro ) { MacroToken = Token; } // Dunno how vertical tabs are getting in, but treat them as linebreaks if( Token.m_TokenString.Empty() ) { if( Token.m_TokenType != SToken::ET_Counter ) { Token.m_TokenType = SToken::ET_None; } Tokens.PushBack( Token ); DEBUGCATPRINTF( "Core", 2, "%s\n", SToken::m_TokenNames[ Token.m_TokenType ] ); Token.m_TokenType = SToken::ET_Name; } else { Token.m_TokenString.PushBack( '\0' ); Tokens.PushBack( Token ); DEBUGCATPRINTF( "Core", 2, "%s: %s\n", SToken::m_TokenNames[ Token.m_TokenType ], Token.m_TokenString.GetData() ); Token.m_TokenString.Clear(); Token.m_TokenType = SToken::ET_Name; } ++LineCount; } else if( c == '[' ) { if( Token.m_TokenType == SToken::ET_String ) { Token.m_TokenString.PushBack( c ); } else { // We should only ever open a context when we're expecting a name ASSERT( Token.m_TokenType == SToken::ET_Name ); Token.m_TokenType = SToken::ET_Context; // Opening a new context, clear the macro token. MacroToken = SToken(); } } else if( c == ']' ) { // If we've already closed the context, ignore; else, push token if( Token.m_TokenType == SToken::ET_String ) { Token.m_TokenString.PushBack( c ); } else { ASSERT( Token.m_TokenType == SToken::ET_Context ); Token.m_TokenString.PushBack( '\0' ); Tokens.PushBack( Token ); DEBUGCATPRINTF( "Core", 2, "%s: %s\n", SToken::m_TokenNames[ Token.m_TokenType ], Token.m_TokenString.GetData() ); Token.m_TokenString.Clear(); } } else if( c == '@' ) { if( Token.m_TokenType == SToken::ET_String ) { Token.m_TokenString.PushBack( c ); } else { // We should only ever declare or insert a macro when we're expecting a name ASSERT( Token.m_TokenType == SToken::ET_Name ); c = Stream.ReadInt8(); if( c == '@' ) { // @@... means we're inserting the current macro into a name // Make sure there is a current macro. If this fails, a macro probably // wasn't opened in the current context. ASSERT( MacroToken.m_TokenString.Size() > 0 ); const uint MacroLength = MacroToken.m_TokenString.Size(); for( uint MacroIndex = 0; MacroIndex < MacroLength; ++MacroIndex ) { Token.m_TokenString.PushBack( MacroToken.m_TokenString[ MacroIndex ] ); } } else { // @... means we're declaring a new macro Token.m_TokenType = SToken::ET_Macro; if( c == ' ' || c == '\t' ) { // Ignore whitespace at the front of macro } else { Token.m_TokenString.PushBack( c ); } } } } else { bool ClosedString = false; InnerParse( c, StrMark, Token.m_TokenString, LineCount, Token.m_TokenType, &ClosedString ); if( ClosedString ) { Tokens.PushBack( Token ); DEBUGCATPRINTF( "Core", 2, "%s: %s\n", SToken::m_TokenNames[ Token.m_TokenType ], Token.m_TokenString.GetData() ); Token.m_TokenString.Clear(); Token.m_TokenType = SToken::ET_None; } } } SimpleString Context = ""; // Tokens are made, now create config vars for( uint i = 0; i < Tokens.Size(); ++i ) { SToken& NameToken = Tokens[i]; SimpleString Name = ""; const char* ValueString = NULL; if( NameToken.m_TokenType == SToken::ET_Name ) { Name = NameToken.m_TokenString.GetData(); ASSERT( Tokens[ i + 1 ].m_TokenType == SToken::ET_Equals ); SToken& ValueToken = Tokens[ i + 2 ]; ValueString = ValueToken.m_TokenString.GetData(); if( Context != "" ) { CATPRINTF( "Core", 2, "%s:", Context.CStr() ); } CATPRINTF( "Core", 2, "%s: %s: %s\n", Name.CStr(), SToken::m_TokenNames[ ValueToken.m_TokenType ], ValueString ); switch( ValueToken.m_TokenType ) { case SToken::ET_Bool: { // Just use the first character to determine truth bool Value = false; char first = ValueString[0]; if( first == 't' || first == 'T' ) { Value = true; } ConfigManager::SetBool( Name, Value, Context ); } break; case SToken::ET_Int: { int Value = atoi( ValueString ); ConfigManager::SetInt( Name, Value, Context ); } break; case SToken::ET_Counter: { List<int>::Iterator NextCounterIter = ArrayCounters.Front(); ( *NextCounterIter )++; // Add one to the last value we incremented, and that's the total for this array ConfigManager::SetInt( Name, *NextCounterIter, Context ); ArrayCounters.PopFront(); } break; case SToken::ET_Float: { float Value = (float)atof( ValueString ); ConfigManager::SetFloat( Name, Value, Context ); } break; case SToken::ET_String: { // Make a permanent copy of the string uint Length = (uint)strlen( ValueString ); char* pString = new char[ Length + 1]; memcpy_s( pString, Length + 1, ValueString, Length ); pString[ Length ] = '\0'; StringManager::AddString( StringManager::ESL_Permanent, pString ); ConfigManager::SetString( Name, pString, Context ); } break; default: WARNDESC( "Unexpected token" ); break; } i += 2; } else if( NameToken.m_TokenType == SToken::ET_Context ) { Context = NameToken.m_TokenString.GetData(); //CATPRINTF( "Core", 2, "Pushed context %s\n", Context.CStr() ); } else { DEBUGCATPRINTF( "Core", 2, "Skipped unexpected token %s (expected ET_Name)\n", SToken::m_TokenNames[ NameToken.m_TokenType ] ); } } // Clean up for( uint i = 0; i < Tokens.Size(); ++i ) { Tokens[i].m_TokenString.Clear(); } Tokens.Clear(); }