//---------------------------------------------------------------------------------------------- // Protected : OnLine //---------------------------------------------------------------------------------------------- void CObjMesh::OnLine() { char *sToken = GetToken(); // It's possible to have an integer "modifier" appended to a command EOb2Command eCmd = ParseCommandString( sToken ); switch (eCmd) { case eVertexCoord: { float fX = GetFloatToken(); float fY = GetFloatToken(); float fZ = GetFloatToken(); OnVertexCoordinate( fX, fY, fZ ); } break; case eTextureCoord: { float fX = GetFloatToken(); float fY = GetFloatToken(); OnVertexTextureCoordinate( fX, fY ); } break; case eNormalCoord: { float fX = GetFloatToken(); float fY = GetFloatToken(); float fZ = GetFloatToken(); OnVertexNormal( fX, fY, fZ ); } break; case eFace: { int pVertexCoords[128]; int pTexCoords[128]; int pNormal[128]; // Initialize to -1 for (int i = 0; i < 128; i++) pNormal[i] = pTexCoords[i] = -1; // Tokens can be of the form <vertex>/<texture>/<normal>, or <vertex>/<texture> or <vertex> int nNumCoords = 0; while ( sToken = GetToken() ) { string sVertexString( sToken ); int nStart = 0; for ( int nNum = 0; nNum < 3; nNum++ ) { unsigned int nEnd = (int)sVertexString.find_first_of("/", nStart); bool bExit = false; if ( nEnd == string::npos) { nEnd = sVertexString.size(); bExit = true; } string sNumber = sVertexString.substr( nStart, nEnd-nStart ); if ( nNum == 0 ) pVertexCoords[nNumCoords] = atoi(sNumber.c_str()); else if ( nNum == 1 ) pTexCoords[nNumCoords] = atoi(sNumber.c_str()); else if ( nNum == 2 ) pNormal[nNumCoords] = atoi(sNumber.c_str()); if ( bExit ) break; nStart = nEnd + 1; } nNumCoords++; } OnFace( pVertexCoords, pTexCoords, pNormal, nNumCoords ); } break; } }
bool udtBaseParser::ParseServerMessage() { _outMsg.Init(_outMsgData, sizeof(_outMsgData)); _outMsg.SetHuffman(_outProtocol >= udtProtocol::Dm66); _inMsg.SetHuffman(_inProtocol >= udtProtocol::Dm66); // // Using the message sequence number as acknowledge number will help avoid // the command overflow playback error for dm3 and dm_48 demos converted to dm_68. // For reference in the Q3 source code: "Client command overflow". // s32 reliableSequenceAcknowledge = _inServerMessageSequence; if(_inProtocol > udtProtocol::Dm3) { const s32 sequAck = _inMsg.ReadLong(); // Reliable sequence acknowledge. if(_inProtocol >= udtProtocol::Dm68) { reliableSequenceAcknowledge = sequAck; } } _inReliableSequenceAcknowledge = reliableSequenceAcknowledge; if(ShouldWriteMessage()) { _outMsg.WriteLong(_inReliableSequenceAcknowledge); } if(EnablePlugIns && !PlugIns.IsEmpty()) { udtMessageBundleCallbackArg info; info.ReliableSequenceAcknowledge = reliableSequenceAcknowledge; for(u32 i = 0, count = PlugIns.GetSize(); i < count; ++i) { PlugIns[i]->ProcessMessageBundleStart(info, *this); } } for(;;) { if(_inMsg.Buffer.readcount > _inMsg.Buffer.cursize) { _context->LogError("udtBaseParser::ParseServerMessage: Read past the end of the server message (in file: %s)", GetFileNamePtr()); return false; } if(_inMsg.Buffer.readcount == _inMsg.Buffer.cursize) { break; } const s32 command = _inMsg.ReadByte(); if(command == svc_EOF || (_inProtocol <= udtProtocol::Dm48 && command == svc_bad)) { break; } // @NOTE: We don't write the command byte already, we leave that decision for later. switch(command) { case svc_nop: if(ShouldWriteMessage()) { _outMsg.WriteByte(svc_nop); } break; case svc_serverCommand: if(!ParseCommandString()) return false; break; case svc_gamestate: if(!ParseGamestate()) return false; break; case svc_snapshot: if(!ParseSnapshot()) return false; break; case svc_download: case svc_voip: default: _context->LogError("udtBaseParser::ParseServerMessage: Unrecognized server message command byte: %d (in file: %s)", command, GetFileNamePtr()); return false; } if(_inProtocol <= udtProtocol::Dm48) { _inMsg.GoToNextByte(); } } if(ShouldWriteMessage()) { _outMsg.WriteByte(svc_EOF); } if(EnablePlugIns && !PlugIns.IsEmpty()) { udtMessageBundleCallbackArg info; info.ReliableSequenceAcknowledge = reliableSequenceAcknowledge; for(u32 i = 0, count = PlugIns.GetSize(); i < count; ++i) { PlugIns[i]->ProcessMessageBundleEnd(info, *this); } } if(_cuts.GetSize() > 0) { const udtCutInfo cut = _cuts[0]; const s32 gameTime = _inServerTime; if(_inGameStateIndex == cut.GameStateIndex && !_outWriteMessage && gameTime >= cut.StartTimeMs && gameTime <= cut.EndTimeMs) { const bool wroteMessage = _outWriteMessage; _outWriteMessage = true; _outWriteFirstMessage = _outWriteMessage && !wroteMessage; } else if((_inGameStateIndex == cut.GameStateIndex && _outWriteMessage && gameTime > cut.EndTimeMs) || (_inGameStateIndex > cut.GameStateIndex && _outWriteMessage)) { WriteLastMessage(); _outWriteMessage = false; _outWriteFirstMessage = false; _outServerCommandSequence = 0; _outSnapshotsWritten = 0; _outFile.Close(); _cuts.Remove(0); if(_cuts.GetSize() == 0) { // It was the last cut, we're done parsing the file now. return false; } } } if(_outWriteFirstMessage) { udtCutInfo& cut = _cuts[0]; udtString filePath; if(cut.FilePath != NULL) { filePath = udtString::NewConstRef(cut.FilePath); } else { udtDemoStreamCreatorArg info; memset(&info, 0, sizeof(info)); info.StartTimeMs = cut.StartTimeMs; info.EndTimeMs = cut.EndTimeMs; info.Parser = this; info.VeryShortDesc = cut.VeryShortDesc; info.UserData = cut.UserData; info.TempAllocator = &_tempAllocator; info.FilePathAllocator = &_persistentAllocator; filePath = (*cut.StreamCreator)(info); } _outFile.Close(); if(_outFile.Open(filePath.GetPtr(), udtFileOpenMode::Write)) { _outFilePath = filePath; udtPath::GetFileName(_outFileName, _persistentAllocator, filePath); _outMsg.SetFileName(_outFileName); WriteFirstMessage(); _outWriteFirstMessage = false; } else { _cuts.Remove(0); } } else if(_outWriteMessage) { WriteNextMessage(); } return true; }