BYTEARRAY CPUBProtocol :: SendAuthAccept( string login, string pass, string key, deque<CBotData> BotList ) { BYTEARRAY packet; packet.push_back( PUB_HEADER_CONSTANT ); // Auth header 1 byte packet.push_back( PUB_AUTH_ACCEPT ); // 1 byte packet.push_back( 0 ); // assign later packet.push_back( 0 ); // assign later packet.push_back( login.size( ) ); // 1 byte UTIL_AppendByteArray(packet, UTIL_CreateByteArray( (unsigned char *)login.c_str( ), login.size( ) )); // packet.push_back( BotList.size() ); // 1 byte for (uint32_t i = 0; i < BotList.size(); i++) { packet.push_back(BotList[i].bot_ip.size( )); UTIL_AppendByteArray(packet, UTIL_CreateByteArray( (unsigned char *)BotList[i].bot_ip.c_str( ), BotList[i].bot_ip.size( ) )); UTIL_AppendByteArray(packet, UTIL_CreateByteArray( (uint16_t)(0) , false)); UTIL_AppendByteArray(packet, UTIL_CreateByteArray( (uint16_t)(BotList[i].bot_gameport) , false)); } packet.push_back( key.size( ) ); // 1 byte UTIL_AppendByteArray(packet, UTIL_CreateByteArray( (unsigned char *)key.c_str( ), key.size( ) )); AssignLength(packet); return packet; }
BYTEARRAY CPotentialPlayer :: GetExternalIP( ) { unsigned char Zeros[] = { 0, 0, 0, 0 }; BYTEARRAY IP; if( m_Socket ) { bool local=false; IP= m_Socket->GetIP( ); if (IP.size()>=2) { if (IP[0]==10 && IP[1]==0) local=true; if (IP[0]==10 && IP[1]==1) local=true; if (IP[0]==192 && IP[1]==168) local=true; if (IP[0]==169 && IP[1]==254) local=true; if (IP[0]==8 && IP[1]==0) local=true; if (IP[0]==5) local=true; } if (local && !m_Game->m_Config->m_ExternalIP.empty()) { IP=UTIL_CreateByteArray(m_Game->m_GHost->m_ExternalIPL,true); } return IP; } return UTIL_CreateByteArray( Zeros, 4 ); }
BYTEARRAY CPotentialPlayer :: GetGarenaIP( ) { if( m_IncomingGarenaUser == NULL ) { return UTIL_CreateByteArray( (uint32_t) 0, true ); } else { return UTIL_CreateByteArray( m_IncomingGarenaUser->GetIP( ), true ); } }
bool CBNCSUtilInterface :: HELP_SID_AUTH_CHECK( bool TFT, string war3Path, string keyROC, string keyTFT, string valueStringFormula, string mpqFileName, BYTEARRAY clientToken, BYTEARRAY serverToken ) { // set m_EXEVersion, m_EXEVersionHash, m_EXEInfo, m_InfoROC, m_InfoTFT string FileWar3EXE = war3Path + "war3.exe"; string FileStormDLL = war3Path + "Storm.dll"; if( !UTIL_FileExists( FileStormDLL ) ) FileStormDLL = war3Path + "storm.dll"; string FileGameDLL = war3Path + "game.dll"; bool ExistsWar3EXE = UTIL_FileExists( FileWar3EXE ); bool ExistsStormDLL = UTIL_FileExists( FileStormDLL ); bool ExistsGameDLL = UTIL_FileExists( FileGameDLL ); if( ExistsWar3EXE && ExistsStormDLL && ExistsGameDLL ) { // todotodo: check getExeInfo return value to ensure 1024 bytes was enough char buf[1024]; uint32_t EXEVersion; getExeInfo( FileWar3EXE.c_str( ), (char *)&buf, 1024, (uint32_t *)&EXEVersion, BNCSUTIL_PLATFORM_X86 ); m_EXEInfo = buf; m_EXEVersion = UTIL_CreateByteArray( EXEVersion, false ); uint32_t EXEVersionHash; checkRevisionFlat( valueStringFormula.c_str( ), FileWar3EXE.c_str( ), FileStormDLL.c_str( ), FileGameDLL.c_str( ), extractMPQNumber( mpqFileName.c_str( ) ), (unsigned long *)&EXEVersionHash ); m_EXEVersionHash = UTIL_CreateByteArray( EXEVersionHash, false ); m_KeyInfoROC = CreateKeyInfo( keyROC, UTIL_ByteArrayToUInt32( clientToken, false ), UTIL_ByteArrayToUInt32( serverToken, false ) ); if( TFT ) m_KeyInfoTFT = CreateKeyInfo( keyTFT, UTIL_ByteArrayToUInt32( clientToken, false ), UTIL_ByteArrayToUInt32( serverToken, false ) ); if( m_KeyInfoROC.size( ) == 36 && ( !TFT || m_KeyInfoTFT.size( ) == 36 ) ) return true; else { if( m_KeyInfoROC.size( ) != 36 ) CONSOLE_Print( "[BNCSUI] unable to create ROC key info - invalid ROC key" ); if( TFT && m_KeyInfoTFT.size( ) != 36 ) CONSOLE_Print( "[BNCSUI] unable to create TFT key info - invalid TFT key" ); } } else { if( !ExistsWar3EXE ) CONSOLE_Print( "[BNCSUI] unable to open [" + FileWar3EXE + "]" ); if( !ExistsStormDLL ) CONSOLE_Print( "[BNCSUI] unable to open [" + FileStormDLL + "]" ); if( !ExistsGameDLL ) CONSOLE_Print( "[BNCSUI] unable to open [" + FileGameDLL + "]" ); } return false; }
void CBNLSClient :: ExtractPackets( ) { string *RecvBuffer = m_Socket->GetBytes( ); BYTEARRAY Bytes = UTIL_CreateByteArray( (unsigned char *)RecvBuffer->c_str( ), RecvBuffer->size( ) ); while( Bytes.size( ) >= 3 ) { uint16_t Length = UTIL_ByteArrayToUInt16( Bytes, false ); if( Length >= 3 ) { if( Bytes.size( ) >= Length ) { m_Packets.push( new CCommandPacket( 0, Bytes[2], BYTEARRAY( Bytes.begin( ), Bytes.begin( ) + Length ) ) ); *RecvBuffer = RecvBuffer->substr( Length ); Bytes = BYTEARRAY( Bytes.begin( ) + Length, Bytes.end( ) ); } else return; } else { CONSOLE_Print( "[BNLSC: " + m_Server + ":" + UTIL_ToString( m_Port ) + ":C" + UTIL_ToString( m_WardenCookie ) + "] error - received invalid packet from BNLS server (bad length), disconnecting" ); m_Socket->Disconnect( ); return; } } }
BYTEARRAY CGameProtocol :: SEND_W3GS_MAPPART( unsigned char fromPID, unsigned char toPID, uint32_t start, string *mapData ) { unsigned char Unknown[] = { 1, 0, 0, 0 }; BYTEARRAY packet; if( start < mapData->size( ) ) { packet.push_back( W3GS_HEADER_CONSTANT ); // W3GS header constant packet.push_back( W3GS_MAPPART ); // W3GS_MAPPART packet.push_back( 0 ); // packet length will be assigned later packet.push_back( 0 ); // packet length will be assigned later packet.push_back( toPID ); // to PID packet.push_back( fromPID ); // from PID UTIL_AppendByteArray( packet, Unknown, 4 ); // ??? UTIL_AppendByteArray( packet, start, false ); // start position // calculate end position (don't send more than 1442 map bytes in one packet) uint32_t End = start + 1442; if( End > mapData->size( ) ) End = mapData->size( ); // calculate crc CCRC32* m_CRC = new CCRC32( ); m_CRC->Initialize( ); BYTEARRAY crc32 = UTIL_CreateByteArray( m_CRC->FullCRC( (unsigned char *)mapData->c_str( ) + start, End - start ), false ); UTIL_AppendByteArray( packet, crc32 ); delete m_CRC; // map data BYTEARRAY Data = UTIL_CreateByteArray( (unsigned char *)mapData->c_str( ) + start, End - start ); UTIL_AppendByteArray( packet, Data ); AssignLength( packet ); } else CONSOLE_Print( "[GAMEPROTO] invalid parameters passed to SEND_W3GS_MAPPART" ); // DEBUG_Print( "SENT W3GS_MAPPART" ); // DEBUG_Print( packet ); return packet; }
bool CPotentialPlayer :: Update( void *fd ) { if( m_DeleteMe ) return true; if( !m_Socket ) return false; m_Socket->DoRecv( (fd_set *)fd ); // extract as many packets as possible from the socket's receive buffer and process them string *RecvBuffer = m_Socket->GetBytes( ); BYTEARRAY Bytes = UTIL_CreateByteArray( (unsigned char *)RecvBuffer->c_str( ), RecvBuffer->size( ) ); // a packet is at least 4 bytes so loop as long as the buffer contains 4 bytes while( Bytes.size( ) >= 4 ) { if( Bytes[0] == W3GS_HEADER_CONSTANT || Bytes[0] == GPS_HEADER_CONSTANT ) { // bytes 2 and 3 contain the length of the packet uint16_t Length = UTIL_ByteArrayToUInt16( Bytes, false, 2 ); if( Length >= 4 ) { if( Bytes.size( ) >= Length ) { if( Bytes[0] == W3GS_HEADER_CONSTANT && Bytes[1] == CGameProtocol :: W3GS_REQJOIN ) { delete m_IncomingJoinPlayer; m_IncomingJoinPlayer = m_Protocol->RECEIVE_W3GS_REQJOIN( BYTEARRAY( Bytes.begin( ), Bytes.begin( ) + Length ) ); if( m_IncomingJoinPlayer ) m_Game->EventPlayerJoined( this, m_IncomingJoinPlayer ); // this is the packet which interests us for now, the remaining is left for CGamePlayer *RecvBuffer = RecvBuffer->substr( Length ); Bytes = BYTEARRAY( Bytes.begin( ) + Length, Bytes.end( ) ); break; } *RecvBuffer = RecvBuffer->substr( Length ); Bytes = BYTEARRAY( Bytes.begin( ) + Length, Bytes.end( ) ); } else break; } } } // don't call DoSend here because some other players may not have updated yet and may generate a packet for this player // also m_Socket may have been set to NULL during ProcessPackets but we're banking on the fact that m_DeleteMe has been set to true as well so it'll short circuit before dereferencing return m_DeleteMe || !m_Socket->GetConnected( ) || m_Socket->HasError( ); }
bool CBNCSUtilInterface :: HELP_PvPGNPasswordHash( string userPassword ) { // set m_PvPGNPasswordHash char buf[20]; hashPassword( userPassword.c_str( ), buf ); m_PvPGNPasswordHash = UTIL_CreateByteArray( (unsigned char *)buf, 20 ); return true; }
BYTEARRAY CPotentialPlayer :: GetExternalIP( ) { unsigned char Zeros[] = { 0, 0, 0, 0 }; if( m_Socket ) return m_Socket->GetIP( ); return UTIL_CreateByteArray( Zeros, 4 ); }
bool CBNCSUtilInterface :: HELP_SID_AUTH_ACCOUNTLOGONPROOF( BYTEARRAY salt, BYTEARRAY serverKey ) { // set m_M1 char buf[20]; // nls_get_M1( (nls_t *)m_nls, buf, string( serverKey.begin( ), serverKey.end( ) ).c_str( ), string( salt.begin( ), salt.end( ) ).c_str( ) ); ( (NLS *)m_NLS )->getClientSessionKey( buf, string( salt.begin( ), salt.end( ) ).c_str( ), string( serverKey.begin( ), serverKey.end( ) ).c_str( ) ); m_M1 = UTIL_CreateByteArray( (unsigned char *)buf, 20 ); return true; }
bool CBNCSUtilInterface :: HELP_SID_AUTH_ACCOUNTLOGON( ) { // set m_ClientKey char buf[32]; // nls_get_A( (nls_t *)m_nls, buf ); ( (NLS *)m_NLS )->getPublicKey( buf ); m_ClientKey = UTIL_CreateByteArray( (unsigned char *)buf, 32 ); return true; }
BYTEARRAY CPUBProtocol :: SendRealName( const string login, const string& key ) { BYTEARRAY packet; packet.push_back( PUB_HEADER_CONSTANT ); // Auth header 1 byte packet.push_back( PUB_AUTH_NAME ); // 1 byte packet.push_back( 0 ); // 1 byte packet.push_back( 0 ); // 1 byte packet.push_back( login.size() ); UTIL_AppendByteArray(packet, UTIL_CreateByteArray( (unsigned char *)login.c_str( ), login.size( ) )); packet.push_back( key.size() ); UTIL_AppendByteArray(packet, UTIL_CreateByteArray( (unsigned char *)key.c_str( ), key.size( ) )); AssignLength(packet); return packet; }
void CTCPSocket :: DoRecv( fd_set *fd ) { if( m_Socket == INVALID_SOCKET || m_HasError || !m_Connected ) return; if( FD_ISSET( m_Socket, fd ) ) { // data is waiting, receive it char buffer[1024]; int c = recv( m_Socket, buffer, 1024, 0 ); if( c == SOCKET_ERROR && GetLastError( ) != EWOULDBLOCK ) { // receive error m_HasError = true; m_Error = GetLastError( ); if (m_isConsolePrint) CONSOLE_Print( "[TCPSOCKET] error (recv) - " + GetErrorString( ) ); return; } else if( c == 0 ) { // the other end closed the connection if (m_isConsolePrint) CONSOLE_Print( "[TCPSOCKET] closed by remote host" ); m_Connected = false; } else if( c > 0 ) { // success! add the received data to the buffer if( !m_LogFile.empty( ) ) { ofstream Log; Log.open( m_LogFile.c_str( ), ios :: app ); if( !Log.fail( ) ) { Log << " RECEIVE <<< " << UTIL_ByteArrayToHexString( UTIL_CreateByteArray( (unsigned char *)buffer, c ) ) << endl; Log.close( ); } } m_RecvBuffer += string( buffer, c ); m_LastRecv = GetTime( ); } } }
BYTEARRAY CBNCSUtilInterface :: CreateKeyInfo( string key, uint32_t clientToken, uint32_t serverToken ) { unsigned char Zeros[] = { 0, 0, 0, 0 }; BYTEARRAY KeyInfo; CDKeyDecoder Decoder( key.c_str( ), key.size( ) ); if( Decoder.isKeyValid( ) ) { UTIL_AppendByteArray( KeyInfo, UTIL_CreateByteArray( (uint32_t)key.size( ), false ) ); UTIL_AppendByteArray( KeyInfo, UTIL_CreateByteArray( Decoder.getProduct( ), false ) ); UTIL_AppendByteArray( KeyInfo, UTIL_CreateByteArray( Decoder.getVal1( ), false ) ); UTIL_AppendByteArray( KeyInfo, UTIL_CreateByteArray( Zeros, 4 ) ); size_t Length = Decoder.calculateHash( clientToken, serverToken ); char *buf = new char[Length]; Length = Decoder.getHash( buf ); UTIL_AppendByteArray( KeyInfo, UTIL_CreateByteArray( (unsigned char *)buf, Length ) ); delete [] buf; } return KeyInfo; }
string sha1(const string& input) { SHA1.Reset(); SHA1.Update((unsigned char *)input.c_str(), input.size()); SHA1.Final(); unsigned char uResult[20]; SHA1.GetHash(uResult); BYTEARRAY MapSHA1 = UTIL_CreateByteArray( uResult, 20 ); string sResult = UTIL_ByteArrayToHexStringWithoutSpaces(MapSHA1); return sResult; }
BYTEARRAY CPotentialPlayer :: GetExternalIP( ) { unsigned char Zeros[] = { 0, 0, 0, 0 }; if( m_Socket ) { if( m_IncomingGarenaUser != NULL ) return GetGarenaIP( ); else return m_Socket->GetIP( ); } return UTIL_CreateByteArray( Zeros, 4 ); }
void CGamePlayer :: ExtractPackets( ) { if( !m_Socket ) return; // extract as many packets as possible from the socket's receive buffer and put them in the m_Packets queue string *RecvBuffer = m_Socket->GetBytes( ); BYTEARRAY Bytes = UTIL_CreateByteArray( (unsigned char *)RecvBuffer->c_str( ), RecvBuffer->size( ) ); // a packet is at least 4 bytes so loop as long as the buffer contains 4 bytes while( Bytes.size( ) >= 4 ) { if( Bytes[0] == W3GS_HEADER_CONSTANT || Bytes[0] == GPS_HEADER_CONSTANT || Bytes[0] == GCBI_HEADER_CONSTANT ) { // bytes 2 and 3 contain the length of the packet uint16_t Length = UTIL_ByteArrayToUInt16( Bytes, false, 2 ); if( Length >= 4 ) { if( Bytes.size( ) >= Length ) { m_Packets.push( new CCommandPacket( Bytes[0], Bytes[1], BYTEARRAY( Bytes.begin( ), Bytes.begin( ) + Length ) ) ); if( Bytes[0] == W3GS_HEADER_CONSTANT ) ++m_TotalPacketsReceived; *RecvBuffer = RecvBuffer->substr( Length ); Bytes = BYTEARRAY( Bytes.begin( ) + Length, Bytes.end( ) ); } else return; } else { m_Error = true; m_ErrorString = "received invalid packet from player (bad length)"; return; } } else { m_Error = true; m_ErrorString = "received invalid packet from player (bad header constant)"; return; } } }
vector<string> CDotaItems::GetItems( ) { vector<string> ret; for (int i = 0; i < 6; i++) { if (m_Items[i].value > 0) { BYTEARRAY b = UTIL_CreateByteArray( m_Items[i].value, false ); ret.push_back(string(b.rbegin(), b.rend())); } else ret.push_back(string( )); // CONSOLE_Print( "[CDotaItems:GetItem] slot["+UTIL_ToString(i)+"], item["+m_Items[i].name+"]."); } return ret; };
bool CGCBIProtocol :: AssignLength( BYTEARRAY &content ) { // insert the actual length of the content array into bytes 3 and 4 (indices 2 and 3) BYTEARRAY LengthBytes; if( content.size( ) >= 4 && content.size( ) <= 65535 ) { LengthBytes = UTIL_CreateByteArray( (uint16_t)content.size( ), false ); content[2] = LengthBytes[0]; content[3] = LengthBytes[1]; return true; } return false; }
bool CBNLSProtocol :: AssignLength( BYTEARRAY &content ) { // insert the actual length of the content array into bytes 1 and 2 (indices 0 and 1) BYTEARRAY LengthBytes; if( content.size( ) >= 2 && content.size( ) <= 65535 ) { LengthBytes = UTIL_CreateByteArray( (uint16_t)content.size( ), false ); content[0] = LengthBytes[0]; content[1] = LengthBytes[1]; return true; } return false; }
void CReplay :: AddChatMessage( unsigned char PID, unsigned char flags, uint32_t chatMode, string message ) { BYTEARRAY Block; Block.push_back( REPLAY_CHATMESSAGE ); Block.push_back( PID ); UTIL_AppendByteArray( Block, (uint16_t)0, false ); Block.push_back( flags ); UTIL_AppendByteArray( Block, chatMode, false ); UTIL_AppendByteArrayFast( Block, message ); // assign length BYTEARRAY LengthBytes = UTIL_CreateByteArray( (uint16_t)( Block.size( ) - 4 ), false ); Block[2] = LengthBytes[0]; Block[3] = LengthBytes[1]; m_CompiledBlocks += string( Block.begin( ), Block.end( ) ); }
BYTEARRAY CGameProtocol :: SEND_W3GS_INCOMING_ACTION2( queue<CIncomingAction *> actions ) { BYTEARRAY packet; packet.push_back( W3GS_HEADER_CONSTANT ); // W3GS header constant packet.push_back( W3GS_INCOMING_ACTION2 ); // W3GS_INCOMING_ACTION2 packet.push_back( 0 ); // packet length will be assigned later packet.push_back( 0 ); // packet length will be assigned later packet.push_back( 0 ); // ??? (send interval?) packet.push_back( 0 ); // ??? (send interval?) // create subpacket if( !actions.empty( ) ) { BYTEARRAY subpacket; while( !actions.empty( ) ) { CIncomingAction *Action = actions.front( ); actions.pop( ); subpacket.push_back( Action->GetPID( ) ); UTIL_AppendByteArray( subpacket, (uint16_t)Action->GetAction( )->size( ), false ); UTIL_AppendByteArray( subpacket, *Action->GetAction( ) ); } // calculate crc (we only care about the first 2 bytes though) CCRC32* m_CRC = new CCRC32( ); m_CRC->Initialize(); BYTEARRAY crc32 = UTIL_CreateByteArray( m_CRC->FullCRC( (unsigned char *)string( subpacket.begin( ), subpacket.end( ) ).c_str( ), subpacket.size( ) ), false ); crc32.resize( 2 ); delete m_CRC; // finish subpacket UTIL_AppendByteArray( packet, crc32 ); // crc UTIL_AppendByteArray( packet, subpacket ); // subpacket } AssignLength( packet ); // DEBUG_Print( "SENT W3GS_INCOMING_ACTION2" ); // DEBUG_Print( packet ); return packet; }
void CReplay :: AddTimeSlot2( queue<CIncomingAction *> actions ) { BYTEARRAY Block; Block.push_back( REPLAY_TIMESLOT2 ); UTIL_AppendByteArray( Block, (uint16_t)0, false ); UTIL_AppendByteArray( Block, (uint16_t)0, false ); while( !actions.empty( ) ) { CIncomingAction *Action = actions.front( ); actions.pop( ); Block.push_back( Action->GetPID( ) ); UTIL_AppendByteArray( Block, (uint16_t)Action->GetAction( )->size( ), false ); UTIL_AppendByteArrayFast( Block, *Action->GetAction( ) ); } // assign length BYTEARRAY LengthBytes = UTIL_CreateByteArray( (uint16_t)( Block.size( ) - 3 ), false ); Block[1] = LengthBytes[0]; Block[2] = LengthBytes[1]; m_CompiledBlocks += string( Block.begin( ), Block.end( ) ); }
void CReplay :: AddTimeSlot( uint16_t timeIncrement, queue<CIncomingAction *> actions ) { BYTEARRAY Block; Block.push_back( REPLAY_TIMESLOT ); UTIL_AppendByteArray( Block, (uint16_t)0, false ); UTIL_AppendByteArray( Block, timeIncrement, false ); while( !actions.empty( ) ) { CIncomingAction *Action = actions.front( ); actions.pop( ); BYTEARRAY ActionData = Action->GetAction( ); Block.push_back( Action->GetPID( ) ); UTIL_AppendByteArray( Block, (uint16_t)ActionData.size( ), false ); UTIL_AppendByteArray( Block, ActionData ); } // assign length BYTEARRAY LengthBytes = UTIL_CreateByteArray( (uint16_t)( Block.size( ) - 3 ), false ); Block[1] = LengthBytes[0]; Block[2] = LengthBytes[1]; m_Blocks.push( Block ); }
void CSaveGame :: ParseSaveGame( ) { m_MapPath.clear( ); m_Slots.clear( ); m_MagicNumber.clear( ); istringstream ISS( m_Decompressed ); // savegame format figured out by Varlock: // string -> map path // 0 (string?) -> ??? (no idea what this is) // string -> original game name // 0 (string?) -> ??? (maybe original game password) // string -> stat string // 4 bytes -> ??? (seems to be # of slots) // 4 bytes -> ??? (seems to be 0x01 0x28 0x49 0x00 on both of the savegames examined) // 2 bytes -> ??? (no idea what this is) // slot structure // 4 bytes -> magic number string GarbageString; unsigned char NumSlots; uint32_t MagicNumber; getline( ISS, m_MapPath, '\0' ); // map path getline( ISS, GarbageString, '\0' ); // ??? getline( ISS, GarbageString, '\0' ); // original game name getline( ISS, GarbageString, '\0' ); // ??? getline( ISS, GarbageString, '\0' ); // stat string ISS.seekg( 4, ios :: cur ); // ??? ISS.seekg( 4, ios :: cur ); // ??? ISS.seekg( 2, ios :: cur ); // ??? ISS.read( (char *)&NumSlots, 1 ); // number of slots if( NumSlots > 12 ) { CONSOLE_Print( "[SAVEGAME] too many slots in decompressed data" ); m_Valid = false; return; } CONSOLE_Print( "[SAVEGAME] found " + UTIL_ToString( NumSlots ) + " slots" ); for( unsigned char i = 0; i < NumSlots; i++ ) { unsigned char SlotData[9]; ISS.read( (char *)SlotData, 9 ); // slot data m_Slots.push_back( CGameSlot( SlotData[0], SlotData[1], SlotData[2], SlotData[3], SlotData[4], SlotData[5], SlotData[6], SlotData[7], SlotData[8] ) ); } ISS.seekg( 4, ios :: cur ); // GetTicks ISS.seekg( 1, ios :: cur ); // GameType ISS.seekg( 1, ios :: cur ); // number of player slots (non observer) ISS.read( (char *)&MagicNumber, 4 ); // magic number if( ISS.fail( ) ) { CONSOLE_Print( "[SAVEGAME] failed to parse decompressed data" ); m_Valid = false; return; } m_MagicNumber = UTIL_CreateByteArray( MagicNumber, false ); CONSOLE_Print( "[SAVEGAME] found map path [" + m_MapPath + "]" ); CONSOLE_Print( "[SAVEGAME] found magic number [" + UTIL_ToString( m_MagicNumber[0] ) + " " + UTIL_ToString( m_MagicNumber[1] ) + " " + UTIL_ToString( m_MagicNumber[2] ) + " " + UTIL_ToString( m_MagicNumber[3] ) + "]" ); }
BYTEARRAY CMap :: GetMapGameFlags( ) { /* Speed: (mask 0x00000003) cannot be combined 0x00000000 - Slow game speed 0x00000001 - Normal game speed 0x00000002 - Fast game speed Visibility: (mask 0x00000F00) cannot be combined 0x00000100 - Hide terrain 0x00000200 - Map explored 0x00000400 - Always visible (no fog of war) 0x00000800 - Default Observers/Referees: (mask 0x40003000) cannot be combined 0x00000000 - No Observers 0x00002000 - Observers on Defeat 0x00003000 - Additional players as observer allowed 0x40000000 - Referees Teams/Units/Hero/Race: (mask 0x07064000) can be combined 0x00004000 - Teams Together (team members are placed at neighbored starting locations) 0x00060000 - Fixed teams 0x01000000 - Unit share 0x02000000 - Random hero 0x04000000 - Random races */ uint32_t GameFlags = 0; // speed if( m_MapSpeed == MAPSPEED_SLOW ) GameFlags = 0x00000000; else if( m_MapSpeed == MAPSPEED_NORMAL ) GameFlags = 0x00000001; else GameFlags = 0x00000002; // visibility if( m_MapVisibility == MAPVIS_HIDETERRAIN ) GameFlags |= 0x00000100; else if( m_MapVisibility == MAPVIS_EXPLORED ) GameFlags |= 0x00000200; else if( m_MapVisibility == MAPVIS_ALWAYSVISIBLE ) GameFlags |= 0x00000400; else GameFlags |= 0x00000800; // observers if( m_MapObservers == MAPOBS_ONDEFEAT ) GameFlags |= 0x00002000; else if( m_MapObservers == MAPOBS_ALLOWED ) GameFlags |= 0x00003000; else if( m_MapObservers == MAPOBS_REFEREES ) GameFlags |= 0x40000000; // teams/units/hero/race if( m_MapFlags & MAPFLAG_TEAMSTOGETHER ) GameFlags |= 0x00004000; if( m_MapFlags & MAPFLAG_FIXEDTEAMS ) GameFlags |= 0x00060000; if( m_MapFlags & MAPFLAG_UNITSHARE ) GameFlags |= 0x01000000; if( m_MapFlags & MAPFLAG_RANDOMHERO ) GameFlags |= 0x02000000; if( m_MapFlags & MAPFLAG_RANDOMRACES ) GameFlags |= 0x04000000; return UTIL_CreateByteArray( GameFlags, false ); }
void CMap :: Load( CConfig *CFG, string nCFGFile ) { m_Valid = true; m_CFGFile = nCFGFile; // load the map data m_MapLocalPath = CFG->GetString( "map_localpath", string( ) ); m_MapData.clear( ); if( !m_MapLocalPath.empty( ) ) m_MapData = UTIL_FileRead( m_GHost->m_MapPath + m_MapLocalPath ); // load the map MPQ string MapMPQFileName = m_GHost->m_MapPath + m_MapLocalPath; HANDLE MapMPQ; bool MapMPQReady = false; if( SFileOpenArchive( MapMPQFileName.c_str( ), 0, MPQ_OPEN_FORCE_MPQ_V1, &MapMPQ ) ) { CONSOLE_Print( "[MAP] loading MPQ file [" + MapMPQFileName + "]" ); MapMPQReady = true; } else CONSOLE_Print( "[MAP] warning - unable to load MPQ file [" + MapMPQFileName + "]" ); // try to calculate map_size, map_info, map_crc, map_sha1 BYTEARRAY MapSize; BYTEARRAY MapInfo; BYTEARRAY MapCRC; BYTEARRAY MapSHA1; if( !m_MapData.empty( ) ) { m_GHost->m_SHA->Reset( ); // calculate map_size MapSize = UTIL_CreateByteArray( (uint32_t)m_MapData.size( ), false ); CONSOLE_Print( "[MAP] calculated map_size = " + UTIL_ByteArrayToDecString( MapSize ) ); // calculate map_info (this is actually the CRC) MapInfo = UTIL_CreateByteArray( (uint32_t)m_GHost->m_CRC->FullCRC( (unsigned char *)m_MapData.c_str( ), m_MapData.size( ) ), false ); CONSOLE_Print( "[MAP] calculated map_info = " + UTIL_ByteArrayToDecString( MapInfo ) ); // calculate map_crc (this is not the CRC) and map_sha1 // a big thank you to Strilanc for figuring the map_crc algorithm out string CommonJ = UTIL_FileRead( m_GHost->m_MapCFGPath + "common.j" ); if( CommonJ.empty( ) ) CONSOLE_Print( "[MAP] unable to calculate map_crc/sha1 - unable to read file [" + m_GHost->m_MapCFGPath + "common.j]" ); else { string BlizzardJ = UTIL_FileRead( m_GHost->m_MapCFGPath + "blizzard.j" ); if( BlizzardJ.empty( ) ) CONSOLE_Print( "[MAP] unable to calculate map_crc/sha1 - unable to read file [" + m_GHost->m_MapCFGPath + "blizzard.j]" ); else { uint32_t Val = 0; // update: it's possible for maps to include their own copies of common.j and/or blizzard.j // this code now overrides the default copies if required bool OverrodeCommonJ = false; bool OverrodeBlizzardJ = false; if( MapMPQReady ) { HANDLE SubFile; // override common.j if( SFileOpenFileEx( MapMPQ, "Scripts\\common.j", 0, &SubFile ) ) { uint32_t FileLength = SFileGetFileSize( SubFile, NULL ); if( FileLength > 0 && FileLength != 0xFFFFFFFF ) { char *SubFileData = new char[FileLength]; DWORD BytesRead = 0; if( SFileReadFile( SubFile, SubFileData, FileLength, &BytesRead ) ) { CONSOLE_Print( "[MAP] overriding default common.j with map copy while calculating map_crc/sha1" ); OverrodeCommonJ = true; Val = Val ^ XORRotateLeft( (unsigned char *)SubFileData, BytesRead ); m_GHost->m_SHA->Update( (unsigned char *)SubFileData, BytesRead ); } delete [] SubFileData; } SFileCloseFile( SubFile ); } } if( !OverrodeCommonJ ) { Val = Val ^ XORRotateLeft( (unsigned char *)CommonJ.c_str( ), CommonJ.size( ) ); m_GHost->m_SHA->Update( (unsigned char *)CommonJ.c_str( ), CommonJ.size( ) ); } if( MapMPQReady ) { HANDLE SubFile; // override blizzard.j if( SFileOpenFileEx( MapMPQ, "Scripts\\blizzard.j", 0, &SubFile ) ) { uint32_t FileLength = SFileGetFileSize( SubFile, NULL ); if( FileLength > 0 && FileLength != 0xFFFFFFFF ) { char *SubFileData = new char[FileLength]; DWORD BytesRead = 0; if( SFileReadFile( SubFile, SubFileData, FileLength, &BytesRead ) ) { CONSOLE_Print( "[MAP] overriding default blizzard.j with map copy while calculating map_crc/sha1" ); OverrodeBlizzardJ = true; Val = Val ^ XORRotateLeft( (unsigned char *)SubFileData, BytesRead ); m_GHost->m_SHA->Update( (unsigned char *)SubFileData, BytesRead ); } delete [] SubFileData; } SFileCloseFile( SubFile ); } } if( !OverrodeBlizzardJ ) { Val = Val ^ XORRotateLeft( (unsigned char *)BlizzardJ.c_str( ), BlizzardJ.size( ) ); m_GHost->m_SHA->Update( (unsigned char *)BlizzardJ.c_str( ), BlizzardJ.size( ) ); } Val = ROTL( Val, 3 ); Val = ROTL( Val ^ 0x03F1379E, 3 ); m_GHost->m_SHA->Update( (unsigned char *)"\x9E\x37\xF1\x03", 4 ); if( MapMPQReady ) { vector<string> FileList; FileList.push_back( "war3map.j" ); FileList.push_back( "scripts\\war3map.j" ); FileList.push_back( "war3map.w3e" ); FileList.push_back( "war3map.wpm" ); FileList.push_back( "war3map.doo" ); FileList.push_back( "war3map.w3u" ); FileList.push_back( "war3map.w3b" ); FileList.push_back( "war3map.w3d" ); FileList.push_back( "war3map.w3a" ); FileList.push_back( "war3map.w3q" ); bool FoundScript = false; for( vector<string> :: iterator i = FileList.begin( ); i != FileList.end( ); i++ ) { // don't use scripts\war3map.j if we've already used war3map.j (yes, some maps have both but only war3map.j is used) if( FoundScript && *i == "scripts\\war3map.j" ) continue; HANDLE SubFile; if( SFileOpenFileEx( MapMPQ, (*i).c_str( ), 0, &SubFile ) ) { uint32_t FileLength = SFileGetFileSize( SubFile, NULL ); if( FileLength > 0 && FileLength != 0xFFFFFFFF ) { char *SubFileData = new char[FileLength]; DWORD BytesRead = 0; if( SFileReadFile( SubFile, SubFileData, FileLength, &BytesRead ) ) { if( *i == "war3map.j" || *i == "scripts\\war3map.j" ) FoundScript = true; Val = ROTL( Val ^ XORRotateLeft( (unsigned char *)SubFileData, BytesRead ), 3 ); m_GHost->m_SHA->Update( (unsigned char *)SubFileData, BytesRead ); // DEBUG_Print( "*** found: " + *i ); } delete [] SubFileData; } SFileCloseFile( SubFile ); } else { // DEBUG_Print( "*** not found: " + *i ); } } if( !FoundScript ) CONSOLE_Print( "[MAP] couldn't find war3map.j or scripts\\war3map.j in MPQ file, calculated map_crc/sha1 is probably wrong" ); MapCRC = UTIL_CreateByteArray( Val, false ); CONSOLE_Print( "[MAP] calculated map_crc = " + UTIL_ByteArrayToDecString( MapCRC ) ); m_GHost->m_SHA->Final( ); unsigned char SHA1[20]; memset( SHA1, 0, sizeof( unsigned char ) * 20 ); m_GHost->m_SHA->GetHash( SHA1 ); MapSHA1 = UTIL_CreateByteArray( SHA1, 20 ); CONSOLE_Print( "[MAP] calculated map_sha1 = " + UTIL_ByteArrayToDecString( MapSHA1 ) ); } else CONSOLE_Print( "[MAP] unable to calculate map_crc/sha1 - map MPQ file not loaded" ); } } } else CONSOLE_Print( "[MAP] no map data available, using config file for map_size, map_info, map_crc, map_sha1" ); // try to calculate map_width, map_height, map_slot<x>, map_numplayers, map_numteams uint32_t MapOptions = 0; BYTEARRAY MapWidth; BYTEARRAY MapHeight; uint32_t MapNumPlayers = 0; uint32_t MapNumTeams = 0; vector<CGameSlot> Slots; if( !m_MapData.empty( ) ) { if( MapMPQReady ) { HANDLE SubFile; if( SFileOpenFileEx( MapMPQ, "war3map.w3i", 0, &SubFile ) ) { uint32_t FileLength = SFileGetFileSize( SubFile, NULL ); if( FileLength > 0 && FileLength != 0xFFFFFFFF ) { char *SubFileData = new char[FileLength]; DWORD BytesRead = 0; if( SFileReadFile( SubFile, SubFileData, FileLength, &BytesRead ) ) { istringstream ISS( string( SubFileData, BytesRead ) ); // war3map.w3i format found at http://www.wc3campaigns.net/tools/specs/index.html by Zepir/PitzerMike string GarbageString; uint32_t FileFormat; uint32_t RawMapWidth; uint32_t RawMapHeight; uint32_t RawMapFlags; uint32_t RawMapNumPlayers; uint32_t RawMapNumTeams; ISS.read( (char *)&FileFormat, 4 ); // file format (18 = ROC, 25 = TFT) if( FileFormat == 18 || FileFormat == 25 ) { ISS.seekg( 4, ios :: cur ); // number of saves ISS.seekg( 4, ios :: cur ); // editor version getline( ISS, GarbageString, '\0' ); // map name getline( ISS, GarbageString, '\0' ); // map author getline( ISS, GarbageString, '\0' ); // map description getline( ISS, GarbageString, '\0' ); // players recommended ISS.seekg( 32, ios :: cur ); // camera bounds ISS.seekg( 16, ios :: cur ); // camera bounds complements ISS.read( (char *)&RawMapWidth, 4 ); // map width ISS.read( (char *)&RawMapHeight, 4 ); // map height ISS.read( (char *)&RawMapFlags, 4 ); // flags ISS.seekg( 1, ios :: cur ); // map main ground type if( FileFormat == 18 ) ISS.seekg( 4, ios :: cur ); // campaign background number else if( FileFormat == 25 ) { ISS.seekg( 4, ios :: cur ); // loading screen background number getline( ISS, GarbageString, '\0' ); // path of custom loading screen model } getline( ISS, GarbageString, '\0' ); // map loading screen text getline( ISS, GarbageString, '\0' ); // map loading screen title getline( ISS, GarbageString, '\0' ); // map loading screen subtitle if( FileFormat == 18 ) ISS.seekg( 4, ios :: cur ); // map loading screen number else if( FileFormat == 25 ) { ISS.seekg( 4, ios :: cur ); // used game data set getline( ISS, GarbageString, '\0' ); // prologue screen path } getline( ISS, GarbageString, '\0' ); // prologue screen text getline( ISS, GarbageString, '\0' ); // prologue screen title getline( ISS, GarbageString, '\0' ); // prologue screen subtitle if( FileFormat == 25 ) { ISS.seekg( 4, ios :: cur ); // uses terrain fog ISS.seekg( 4, ios :: cur ); // fog start z height ISS.seekg( 4, ios :: cur ); // fog end z height ISS.seekg( 4, ios :: cur ); // fog density ISS.seekg( 1, ios :: cur ); // fog red value ISS.seekg( 1, ios :: cur ); // fog green value ISS.seekg( 1, ios :: cur ); // fog blue value ISS.seekg( 1, ios :: cur ); // fog alpha value ISS.seekg( 4, ios :: cur ); // global weather id getline( ISS, GarbageString, '\0' ); // custom sound environment ISS.seekg( 1, ios :: cur ); // tileset id of the used custom light environment ISS.seekg( 1, ios :: cur ); // custom water tinting red value ISS.seekg( 1, ios :: cur ); // custom water tinting green value ISS.seekg( 1, ios :: cur ); // custom water tinting blue value ISS.seekg( 1, ios :: cur ); // custom water tinting alpha value } ISS.read( (char *)&RawMapNumPlayers, 4 ); // number of players uint32_t ClosedSlots = 0; for( uint32_t i = 0; i < RawMapNumPlayers; i++ ) { CGameSlot Slot( 0, 255, SLOTSTATUS_OPEN, 0, 0, 1, SLOTRACE_RANDOM ); uint32_t Colour; uint32_t Status; uint32_t Race; ISS.read( (char *)&Colour, 4 ); // colour Slot.SetColour( Colour ); ISS.read( (char *)&Status, 4 ); // status if( Status == 1 ) Slot.SetSlotStatus( SLOTSTATUS_OPEN ); else if( Status == 2 ) { Slot.SetSlotStatus( SLOTSTATUS_OCCUPIED ); Slot.SetComputer( 1 ); Slot.SetComputerType( SLOTCOMP_NORMAL ); } else { Slot.SetSlotStatus( SLOTSTATUS_CLOSED ); ClosedSlots++; } ISS.read( (char *)&Race, 4 ); // race if( Race == 1 ) Slot.SetRace( SLOTRACE_HUMAN ); else if( Race == 2 ) Slot.SetRace( SLOTRACE_ORC ); else if( Race == 3 ) Slot.SetRace( SLOTRACE_UNDEAD ); else if( Race == 4 ) Slot.SetRace( SLOTRACE_NIGHTELF ); else Slot.SetRace( SLOTRACE_RANDOM ); ISS.seekg( 4, ios :: cur ); // fixed start position getline( ISS, GarbageString, '\0' ); // player name ISS.seekg( 4, ios :: cur ); // start position x ISS.seekg( 4, ios :: cur ); // start position y ISS.seekg( 4, ios :: cur ); // ally low priorities ISS.seekg( 4, ios :: cur ); // ally high priorities if( Slot.GetSlotStatus( ) != SLOTSTATUS_CLOSED ) Slots.push_back( Slot ); } ISS.read( (char *)&RawMapNumTeams, 4 ); // number of teams for( uint32_t i = 0; i < RawMapNumTeams; i++ ) { uint32_t Flags; uint32_t PlayerMask; ISS.read( (char *)&Flags, 4 ); // flags ISS.read( (char *)&PlayerMask, 4 ); // player mask for( unsigned char j = 0; j < 12; j++ ) { if( PlayerMask & 1 ) { for( vector<CGameSlot> :: iterator k = Slots.begin( ); k != Slots.end( ); k++ ) { if( (*k).GetColour( ) == j ) (*k).SetTeam( i ); } } PlayerMask >>= 1; } getline( ISS, GarbageString, '\0' ); // team name } // the bot only cares about the following options: melee, fixed player settings, custom forces // let's not confuse the user by displaying erroneous map options so zero them out now MapOptions = RawMapFlags & ( MAPOPT_MELEE | MAPOPT_FIXEDPLAYERSETTINGS | MAPOPT_CUSTOMFORCES ); CONSOLE_Print( "[MAP] calculated map_options = " + UTIL_ToString( MapOptions ) ); MapWidth = UTIL_CreateByteArray( (uint16_t)RawMapWidth, false ); CONSOLE_Print( "[MAP] calculated map_width = " + UTIL_ByteArrayToDecString( MapWidth ) ); MapHeight = UTIL_CreateByteArray( (uint16_t)RawMapHeight, false ); CONSOLE_Print( "[MAP] calculated map_height = " + UTIL_ByteArrayToDecString( MapHeight ) ); MapNumPlayers = RawMapNumPlayers - ClosedSlots; CONSOLE_Print( "[MAP] calculated map_numplayers = " + UTIL_ToString( MapNumPlayers ) ); MapNumTeams = RawMapNumTeams; CONSOLE_Print( "[MAP] calculated map_numteams = " + UTIL_ToString( MapNumTeams ) ); uint32_t SlotNum = 1; for( vector<CGameSlot> :: iterator i = Slots.begin( ); i != Slots.end( ); i++ ) { CONSOLE_Print( "[MAP] calculated map_slot" + UTIL_ToString( SlotNum ) + " = " + UTIL_ByteArrayToDecString( (*i).GetByteArray( ) ) ); SlotNum++; } if( MapOptions & MAPOPT_MELEE ) { CONSOLE_Print( "[MAP] found melee map, initializing slots" ); // give each slot a different team and set the race to random unsigned char Team = 0; for( vector<CGameSlot> :: iterator i = Slots.begin( ); i != Slots.end( ); i++ ) { (*i).SetTeam( Team++ ); (*i).SetRace( SLOTRACE_RANDOM ); } } if( !( MapOptions & MAPOPT_FIXEDPLAYERSETTINGS ) ) { // make races selectable for( vector<CGameSlot> :: iterator i = Slots.begin( ); i != Slots.end( ); i++ ) (*i).SetRace( (*i).GetRace( ) | SLOTRACE_SELECTABLE ); } } } else
BYTEARRAY CSocket :: GetPort( ) { return UTIL_CreateByteArray( m_SIN.sin_port, false ); }
BYTEARRAY CSocket :: GetIP( ) { return UTIL_CreateByteArray( (uint32_t)m_SIN.sin_addr.s_addr, false ); }
CBNETProtocol :: CBNETProtocol( ) { unsigned char ClientToken[] = { 220, 1, 203, 7 }; m_ClientToken = UTIL_CreateByteArray( ClientToken, 4 ); }