LocFile* LocFileNew( char* path, unsigned int flags ) { FILE* fp = fopen( path, "rb" ); if( fp == NULL ) { INFO("Cannot open file %s (file does not exist?)..\n", path ); return NULL; } struct stat st; if( stat( path, &st ) < 0 ) { INFO( "Cannot stat file.\n" ); fclose( fp ); return NULL; } if( S_ISDIR( st.st_mode ) ) { INFO( "Is a directory. Can not open.\n" ); fclose( fp ); return NULL; } LocFile* fo = (LocFile*) calloc( 1, sizeof(LocFile) ); if( fo != NULL ) { int len = strlen( path ); fo->lf_Path = StringDuplicateN( path, len ); fo->lf_Filename = GetFileNamePtr( path, len ); fo->fp = fp; fseek( fp, 0L, SEEK_END ); fo->filesize = ftell( fp ); if( flags & FILE_READ_NOW ) { LocFileRead( fo, 0, fo->filesize ); } } else { ERROR("Cannot allocate memory for LocFile\n"); } return fo; }
bool udtBaseParser::ParseSnapshot() { // // Read in the new snapshot to a temporary buffer // We will only save it if it is valid. // We will have read any new server commands in this // message before we got to svc_snapshot. // if(_inProtocol == udtProtocol::Dm3) { _inMsg.ReadLong(); // Client command sequence. } _inServerTime = _inMsg.ReadLong(); idLargestClientSnapshot newSnap; Com_Memset(&newSnap, 0, sizeof(newSnap)); newSnap.serverCommandNum = _inServerCommandSequence; newSnap.serverTime = _inServerTime; newSnap.messageNum = _inServerMessageSequence; s32 deltaNum = _inMsg.ReadByte(); if(!deltaNum) { newSnap.deltaNum = -1; } else { newSnap.deltaNum = newSnap.messageNum - deltaNum; } newSnap.snapFlags = _inMsg.ReadByte(); // // If the frame is delta compressed from data that we // no longer have available, we must suck up the rest of // the frame, but not use it, then ask for a non-compressed // message. // idClientSnapshotBase* oldSnap; if(newSnap.deltaNum <= 0) { newSnap.valid = true; // Uncompressed frame. oldSnap = NULL; } else { if(deltaNum >= PACKET_BACKUP) { _context->LogWarning("udtBaseParser::ParseSnapshot: deltaNum %d invalid.", deltaNum); } if(newSnap.deltaNum > _inServerMessageSequence) { _context->LogWarning("udtBaseParser::ParseSnapshot: Need delta from read ahead."); } oldSnap = GetClientSnapshot(newSnap.deltaNum & PACKET_MASK); if(!oldSnap->valid) { // Should never happen. _context->LogWarning("udtBaseParser::ParseSnapshot: Delta from invalid frame %d (not supposed to happen!).", deltaNum); } else if(oldSnap->messageNum != newSnap.deltaNum) { // The frame that the server did the delta from // is too old, so we can't reconstruct it properly. _context->LogWarning("udtBaseParser::ParseSnapshot: Delta frame %d too old.", deltaNum); } else if(_inParseEntitiesNum - oldSnap->parseEntitiesNum > ID_MAX_PARSE_ENTITIES - 128) { _context->LogWarning("udtBaseParser::ParseSnapshot: Delta parseEntitiesNum %d too old.", _inParseEntitiesNum); } else { newSnap.valid = true; } } // // Read the area mask. // const s32 areaMaskLength = _inMsg.ReadByte(); if(areaMaskLength > (s32)sizeof(newSnap.areamask)) { _context->LogError("udtBaseParser::ParseSnapshot: Invalid size %d for areamask (in file: %s)", areaMaskLength, GetFileNamePtr()); return false; } _inMsg.ReadData(&newSnap.areamask, areaMaskLength); // Read the player info. if(!_inMsg.ReadDeltaPlayer(oldSnap ? GetPlayerState(oldSnap, _inProtocol) : NULL, GetPlayerState(&newSnap, _inProtocol))) { return false; } // Read in all entities. if(!ParsePacketEntities(_inMsg, oldSnap, &newSnap)) { return false; } // Did we write enough snapshots already? const bool noDelta = _outSnapshotsWritten < deltaNum; if(noDelta) { deltaNum = 0; oldSnap = NULL; } // If not valid, dump the entire thing now that // it has been properly read. if(!newSnap.valid) { return true; } // // Clear the valid flags of any snapshots between the last // received and this one, so if there was a dropped packet // it won't look like something valid to delta from next // time we wrap around in the buffer. // s32 oldMessageNum = _inSnapshot.messageNum + 1; if(newSnap.messageNum - oldMessageNum >= PACKET_BACKUP) { oldMessageNum = newSnap.messageNum - (PACKET_BACKUP - 1); } for(; oldMessageNum < newSnap.messageNum; ++oldMessageNum) { GetClientSnapshot(oldMessageNum & PACKET_MASK)->valid = false; } // Copy to the current good spot. _inSnapshot = newSnap; // Save the frame off in the backup array for later delta comparisons. Com_Memcpy(GetClientSnapshot(_inSnapshot.messageNum & PACKET_MASK), &_inSnapshot, (size_t)_inProtocolSizeOfClientSnapshot); // Don't give the same stuff to the plug-ins more than once. if(newSnap.messageNum == _inLastSnapshotMessageNumber) { return true; } _inLastSnapshotMessageNumber = newSnap.messageNum; // // Process plug-ins now so that modifiers can alter the snapshots. // if(EnablePlugIns && !PlugIns.IsEmpty()) { udtSnapshotCallbackArg info; info.ServerTime = _inServerTime; info.SnapshotArrayIndex = _inSnapshot.messageNum & PACKET_MASK; info.Snapshot = &newSnap; info.OldSnapshot = oldSnap; info.Entities = _inChangedEntities.GetStartAddress(); info.EntityCount = _inChangedEntities.GetSize(); info.RemovedEntities = _inRemovedEntities.GetStartAddress(); info.RemovedEntityCount = _inRemovedEntities.GetSize(); info.CommandNumber = newSnap.serverCommandNum; info.MessageNumber = newSnap.messageNum; for(u32 i = 0, count = PlugIns.GetSize(); i < count; ++i) { PlugIns[i]->ProcessSnapshotMessage(info, *this); } } // // Write to the output message. // if(ShouldWriteMessage()) { _outMsg.WriteByte(svc_snapshot); _outMsg.WriteLong(newSnap.serverTime); _outMsg.WriteByte(deltaNum); _outMsg.WriteByte(newSnap.snapFlags); _outMsg.WriteByte(areaMaskLength); _outMsg.WriteData(&newSnap.areamask, areaMaskLength); _protocolConverter->StartSnapshot(newSnap.serverTime); if(_outProtocol == _inProtocol) { _outMsg.WriteDeltaPlayer(oldSnap ? GetPlayerState(oldSnap, _outProtocol) : NULL, GetPlayerState(&newSnap, _outProtocol)); EmitPacketEntities(deltaNum ? oldSnap : NULL, &newSnap); } else { idLargestClientSnapshot oldSnapOutProto; idLargestClientSnapshot newSnapOutProto; if(oldSnap) { _protocolConverter->ConvertSnapshot(oldSnapOutProto, *oldSnap); } _protocolConverter->ConvertSnapshot(newSnapOutProto, newSnap); _outMsg.WriteDeltaPlayer(oldSnap ? GetPlayerState(&oldSnapOutProto, _outProtocol) : NULL, GetPlayerState(&newSnapOutProto, _outProtocol)); EmitPacketEntities(deltaNum ? &oldSnapOutProto : NULL, &newSnapOutProto); } ++_outSnapshotsWritten; } return true; }
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; }
bool udtBaseParser::ParseGamestate() { // @TODO: Reset some data, but not for the 1st gamestate message. ResetForGamestateMessage(); // A game state always marks a server command sequence. _inServerCommandSequence = _inMsg.ReadLong(); // // Parse all the config strings and baselines. // for(;;) { const s32 command = _inMsg.ReadByte(); if(_inProtocol <= udtProtocol::Dm48 && command == svc_bad) { break; } if(command == svc_EOF) { break; } if(command == svc_configstring) { const s32 index = _inMsg.ReadShort(); if(index < 0 || index >= MAX_CONFIGSTRINGS) { _context->LogError("udtBaseParser::ParseGamestate: Config string index out of range: %d (in file: %s)", index, GetFileNamePtr()); return false; } s32 configStringLength = 0; const char* const configStringTemp = _inMsg.ReadBigString(configStringLength); // Copy the string to a safe location. _inConfigStrings[index] = udtString::NewClone(_configStringAllocator, configStringTemp, configStringLength); } else if(command == svc_baseline) { const s32 newIndex = _inMsg.ReadBits(GENTITYNUM_BITS); if(newIndex < 0 || newIndex >= MAX_GENTITIES) { _context->LogError("udtBaseParser::ParseGamestate: Baseline number out of range: %d (in file: %s)", newIndex, GetFileNamePtr()); return false; } idLargestEntityState nullState; idEntityStateBase* const newState = GetBaseline(newIndex); // We delta from the null state because we read a full entity. Com_Memset(&nullState, 0, sizeof(nullState)); bool addedOrChanged = false; if(!_inMsg.ReadDeltaEntity(addedOrChanged, &nullState, newState, newIndex)) { return false; } } else { _context->LogError("udtBaseParser::ParseGamestate: Unrecognized command byte: %d (in file: %s)", command, GetFileNamePtr()); return false; } } if(_inProtocol >= udtProtocol::Dm66) { _inClientNum = _inMsg.ReadLong(); _inChecksumFeed = _inMsg.ReadLong(); } else { _inClientNum = -1; _inChecksumFeed = 0; } if(EnablePlugIns && !PlugIns.IsEmpty()) { udtGamestateCallbackArg info; info.ServerCommandSequence = _inServerCommandSequence; info.ClientNum = _inClientNum; info.ChecksumFeed = _inChecksumFeed; for(u32 i = 0, count = PlugIns.GetSize(); i < count; ++i) { PlugIns[i]->ProcessGamestateMessage(info, *this); } } ++_inGameStateIndex; _inGameStateFileOffsets.Add(_inFileOffset); return true; }
/** * Create new LocFile structure and read file from provided path * * @param path pointer to char table with path * @param flags additional flags used to open file * @return pointer to new LocFile when success, otherwise NULL */ LocFile* LocFileNew( char* path, unsigned int flags ) { if( path == NULL ) { FERROR("File path is null\n"); return NULL; } FILE* fp = fopen( path, "rb" ); if( fp == NULL ) { FERROR("Cannot open file %s (file does not exist?)..\n", path ); return NULL; } struct stat st; if( stat( path, &st ) < 0 ) { FERROR( "Cannot stat file: '%s'.\n", path ); fclose( fp ); return NULL; } if( S_ISDIR( st.st_mode ) ) { FERROR( "'%s' is a directory. Can not open.\n", path ); fclose( fp ); return NULL; } DEBUG("Read local file %s\n", path ); LocFile* fo = (LocFile*) FCalloc( 1, sizeof(LocFile) ); if( fo != NULL ) { fo->lf_PathLength = strlen( path ); fo->lf_Path = StringDuplicateN( path, fo->lf_PathLength ); fo->lf_Filename = StringDuplicate( GetFileNamePtr( path, fo->lf_PathLength ) ); MURMURHASH3( fo->lf_Path, fo->lf_PathLength, fo->hash ); DEBUG("PATH: %s\n", fo->lf_Path ); memcpy( &(fo->lf_Info), &st, sizeof( struct stat) ); fseek( fp, 0, SEEK_END ); long fsize = ftell( fp ); fseek( fp, 0, SEEK_SET ); //same as rewind(f); fo->lf_FileSize = fsize;// st.st_size; //ftell( fp ); if( flags & FILE_READ_NOW ) { #if LOCFILE_USE_MMAP == 0 LocFileRead( fo, fp, 0, fo->lf_FileSize ); #else fo->lf_Buffer = mmap(NULL/*address can be anywhere*/, fo->lf_FileSize/*map whole file*/, PROT_READ, MAP_SHARED | MAP_POPULATE, fileno(fp), 0/*beginning of file*/); //DEBUG("***************** Mapping length: %d at %p\n", fo->lf_FileSize, fo->lf_Buffer); #endif } } else { FERROR("Cannot allocate memory for LocFile\n"); } #if LOCFILE_USE_MMAP == 0 fclose( fp ); #endif return fo; }