bool C4Record::Rec(C4PacketType eCtrlType, C4ControlPacket *pCtrl, int iFrame) { if (!fRecording) return false; // create copy C4IDPacket Pkt = C4IDPacket(eCtrlType, pCtrl, false); if (!Pkt.getPkt()) return false; C4ControlPacket *pCtrlCpy = static_cast<C4ControlPacket *>(Pkt.getPkt()); // prepare for recording pCtrlCpy->PreRec(this); // record it return Rec(iFrame, DecompileToBuf<StdCompilerBinWrite>(Pkt), RCT_CtrlPkt); }
void C4PacketList::CompileFunc(StdCompiler *pComp) { // unpack packets if (pComp->isCompiler()) { // Read until no further sections available while (pComp->Name("IDPacket")) { // Read the packet C4IDPacket *pPkt = new C4IDPacket(); try { pComp->Value(*pPkt); pComp->NameEnd(); } catch (...) { delete pPkt; throw; } // Terminator? if (!pPkt->getPkt()) { delete pPkt; break; } // Add to list Add(pPkt); } pComp->NameEnd(); } else { // Write all packets for (C4IDPacket *pPkt = pFirst; pPkt; pPkt = pPkt->pNext) pComp->Value(mkNamingAdapt(*pPkt, "IDPacket")); // Terminate, if no naming is available if (!pComp->hasNaming()) { C4IDPacket Pkt; pComp->Value(mkNamingAdapt(Pkt, "IDPacket")); } } }
void C4Playback::Check(C4RecordChunkType eType, const uint8_t *pData, int iSize) { // only if enabled if (DoNoDebugRec > 0) return; if (Game.FrameCounter < DEBUGREC_START_FRAME) return; C4PktDebugRec PktInReplay; bool fHasPacketFromHead = false; #ifdef DEBUGREC_EXTFILE #ifdef DEBUGREC_EXTFILE_WRITE // writing of external debugrec file DbgRecFile.Write(&eType, sizeof eType); int32_t iSize32 = iSize; DbgRecFile.Write(&iSize32, sizeof iSize32); DbgRecFile.Write(pData, iSize); return; #else int32_t iSize32 = 0; C4RecordChunkType eTypeRec = RCT_Undefined; DbgRecFile.Read(&eTypeRec, sizeof eTypeRec); DbgRecFile.Read(&iSize32, sizeof iSize32); if (iSize32) { StdBuf buf; buf.SetSize(iSize32); DbgRecFile.Read(buf.getMData(), iSize32); PktInReplay = C4PktDebugRec(eTypeRec, buf); } #endif #else // check debug rec in list C4IDPacket *pkt; if (pkt = DebugRec.firstPkt()) { // copy from list PktInReplay = *static_cast<C4PktDebugRec *>(pkt->getPkt()); DebugRec.Delete(pkt); } else { // special sync check skip... while (currChunk != chunks.end() && currChunk->Type == RCT_CtrlPkt) { C4IDPacket Packet(*currChunk->pPkt); C4ControlPacket *pCtrlPck = static_cast<C4ControlPacket *>(Packet.getPkt()); assert(!pCtrlPck->Sync()); Game.Control.ExecControlPacket(Packet.getPktType(), pCtrlPck); NextChunk(); } // record end? if (currChunk == chunks.end() || currChunk->Type == RCT_End || Finished) { Log("DebugRec end: All in sync!"); ++DoNoDebugRec; return; } // unpack directly from head if (currChunk->Type != eType) { DebugRecError(FormatString("Playback type %x, this type %x", currChunk->Type, eType).getData()); return; } PktInReplay = *currChunk->pDbg; fHasPacketFromHead = true; } #endif // DEBUGREC_EXTFILE // record end? if (PktInReplay.getType() == RCT_End) { Log("DebugRec end: All in sync (2)!"); ++DoNoDebugRec; return; } // replay packet is unpacked to PktInReplay now; check it if (PktInReplay.getType() != eType) { DebugRecError(FormatString("Type %s != %s", GetRecordChunkTypeName(PktInReplay.getType()), GetRecordChunkTypeName(eType)).getData()); return; } if (PktInReplay.getSize() != iSize) { DebugRecError(FormatString("Size %d != %d", (int)PktInReplay.getSize(), (int)iSize).getData()); } // check packet data if (memcmp(PktInReplay.getData(), pData, iSize)) { StdStrBuf sErr; sErr.Format("DbgRecPkt Type %s, size %d", GetRecordChunkTypeName(eType), iSize); sErr.Append(" Replay: "); StdBuf replay(PktInReplay.getData(), PktInReplay.getSize()); sErr.Append(GetDbgRecPktData(eType, replay)); sErr.Append(" Here: "); StdBuf here(pData, iSize); sErr.Append(GetDbgRecPktData(eType, here)); DebugRecError(sErr.getData()); } // packet is fine, jump over it if (fHasPacketFromHead) NextChunk(); }
void C4Playback::Strip() { // Strip what? const bool fStripPlayers = false; const bool fStripSyncChecks = false; const bool fStripDebugRec = true; const bool fCheckCheat = false; const bool fStripMessages = true; // const bool fCheckEMControl = true; const int32_t iEndFrame = -1; // Iterate over chunk list for (chunks_t::iterator i = chunks.begin(); i != chunks.end();) { // Strip rest of record? if (iEndFrame >= 0 && i->Frame > iEndFrame) { // Remove this and all remaining chunks while (i != chunks.end()) { i->Delete(); i = chunks.erase(i); } // Push new End-Chunk C4RecordChunk EndChunk; EndChunk.Frame = iEndFrame; EndChunk.Type = RCT_End; chunks.push_back(EndChunk); // Done break; } switch (i->Type) { case RCT_Ctrl: { // Iterate over controls C4Control *pCtrl = i->pCtrl; for (C4IDPacket *pPkt = pCtrl->firstPkt(), *pNext; pPkt; pPkt = pNext) { pNext = pCtrl->nextPkt(pPkt); switch (pPkt->getPktType()) { // Player join: Strip player file (if possible) case CID_JoinPlr: if (fStripPlayers) { C4ControlJoinPlayer *pJoinPlr = static_cast<C4ControlJoinPlayer *>(pPkt->getPkt()); pJoinPlr->Strip(); } break; // EM commands: May be cheats, so log them case CID_Script: case CID_EMMoveObj: case CID_EMDrawTool: if (fCheckCheat) Log(DecompileToBuf<StdCompilerINIWrite>( mkNamingAdapt(*pPkt, FormatString("Frame %d", i->Frame) .getData())).getData()); break; // Strip sync check case CID_SyncCheck: if (fStripSyncChecks) { i->pCtrl->Remove(pPkt); } break; } } // Strip empty control lists (always) if (!pCtrl->firstPkt()) { i->Delete(); i = chunks.erase(i); } else i++; } break; case RCT_CtrlPkt: { bool fStripThis = false; switch (i->pPkt->getPktType()) { // EM commands: May be cheats, so log them case CID_Script: case CID_EMMoveObj: case CID_EMDrawTool: if (fCheckCheat) Log(DecompileToBuf<StdCompilerINIWrite>( mkNamingAdapt(*i->pPkt, FormatString("Frame %d", i->Frame) .getData())).getData()); break; // Strip some stuff case CID_SyncCheck: if (fStripSyncChecks) fStripThis = true; break; case CID_Message: if (fStripMessages) fStripThis = true; break; } if (fStripThis) { i->Delete(); i = chunks.erase(i); } else i++; } break; case RCT_End: i++; break; default: // Strip debugrec if (fStripDebugRec) { i->Delete(); i = chunks.erase(i); } else i++; } } }