OpenClonk
C4Playback Class Reference

#include <C4Record.h>

Public Member Functions

 C4Playback ()
 
 ~C4Playback ()
 
bool Open (C4Group &rGrp)
 
bool ReadBinary (const StdBuf &Buf)
 
bool ReadText (const StdStrBuf &Buf)
 
void NextChunk ()
 
bool NextSequentialChunk ()
 
StdStrBuf ReWriteText ()
 
StdBuf ReWriteBinary ()
 
void Strip ()
 
bool ExecuteControl (C4Control *pCtrl, int iFrame)
 
bool IsFinished ()
 
void Clear ()
 
void Check (C4RecordChunkType eType, const uint8_t *pData, int iSize)
 
void DebugRecError (const char *szError)
 

Static Public Member Functions

static bool StreamToRecord (const char *szStream, StdStrBuf *pRecord)
 

Detailed Description

Definition at line 285 of file C4Record.h.

Constructor & Destructor Documentation

◆ C4Playback()

C4Playback::C4Playback ( )
default

◆ ~C4Playback()

C4Playback::~C4Playback ( )

Definition at line 377 of file C4Record.cpp.

378 {
379  Clear();
380 }
void Clear()
Definition: C4Record.cpp:909

References Clear().

Here is the call graph for this function:

Member Function Documentation

◆ Check()

void C4Playback::Check ( C4RecordChunkType  eType,
const uint8_t *  pData,
int  iSize 
)

Definition at line 992 of file C4Record.cpp.

993 {
994  // only if enabled
995  if (DoNoDebugRec>0) return;
996  if (Game.FrameCounter < DEBUGREC_START_FRAME) return;
997 
998  C4PktDebugRec PktInReplay;
999  bool fHasPacketFromHead = false;
1001  {
1003  {
1004  // writing of external debugrec file
1005  DbgRecFile.Write(&eType, sizeof eType);
1006  int32_t iSize32 = iSize;
1007  DbgRecFile.Write(&iSize32, sizeof iSize32);
1008  DbgRecFile.Write(pData, iSize);
1009  return;
1010  }
1011  else
1012  {
1013  int32_t iSize32 = 0;
1014  C4RecordChunkType eTypeRec = RCT_Undefined;
1015  DbgRecFile.Read(&eTypeRec, sizeof eTypeRec);
1016  DbgRecFile.Read(&iSize32, sizeof iSize32);
1017  if (iSize32)
1018  {
1019  StdBuf buf;
1020  buf.SetSize(iSize32);
1021  DbgRecFile.Read(buf.getMData(), iSize32);
1022  PktInReplay = C4PktDebugRec(eTypeRec, buf);
1023  }
1024  }
1025  }
1026  else
1027  {
1028  // check debug rec in list
1029  C4IDPacket *pkt;
1030  if ((pkt = DebugRec.firstPkt()))
1031  {
1032  // copy from list
1033  PktInReplay = *static_cast<C4PktDebugRec *>(pkt->getPkt());
1034  DebugRec.Delete(pkt);
1035  }
1036  else
1037  {
1038  // special sync check skip...
1039  while (currChunk != chunks.end() && currChunk->Type == RCT_CtrlPkt)
1040  {
1041  C4IDPacket Packet(*currChunk->pPkt);
1042  C4ControlPacket *pCtrlPck = static_cast<C4ControlPacket *>(Packet.getPkt());
1043  assert(!pCtrlPck->Sync());
1044  ::Control.ExecControlPacket(Packet.getPktType(), pCtrlPck);
1045  NextChunk();
1046  }
1047  // record end?
1048  if (currChunk == chunks.end() || currChunk->Type == RCT_End || Finished)
1049  {
1050  Log("DebugRec end: All in sync!");
1051  ++DoNoDebugRec;
1052  return;
1053  }
1054  // unpack directly from head
1055  if (currChunk->Type != eType)
1056  {
1057  DebugRecError(FormatString("Playback type %x, this type %x", currChunk->Type, eType).getData());
1058  return;
1059  }
1060  if (currChunk->pDbg)
1061  PktInReplay = *currChunk->pDbg;
1062 
1063  fHasPacketFromHead = true;
1064  }
1065  }
1066  // record end?
1067  if (PktInReplay.getType() == RCT_End)
1068  {
1069  Log("DebugRec end: All in sync (2)!");
1070  ++DoNoDebugRec;
1071  return;
1072  }
1073  // replay packet is unpacked to PktInReplay now; check it
1074  if (PktInReplay.getType() != eType)
1075  {
1076  DebugRecError(FormatString("Type %s != %s", GetRecordChunkTypeName(PktInReplay.getType()), GetRecordChunkTypeName(eType)).getData());
1077  return;
1078  }
1079  if (PktInReplay.getSize() != unsigned(iSize))
1080  {
1081  DebugRecError(FormatString("Size %d != %d", (int) PktInReplay.getSize(), (int) iSize).getData());
1082  }
1083  // check packet data
1084  if (memcmp(PktInReplay.getData(), pData, iSize))
1085  {
1086  StdStrBuf sErr;
1087  sErr.Format("DbgRecPkt Type %s, size %d", GetRecordChunkTypeName(eType), iSize);
1088  sErr.Append(" Replay: ");
1089  StdBuf replay(PktInReplay.getData(), PktInReplay.getSize());
1090  sErr.Append(GetDbgRecPktData(eType, replay));
1091  sErr.Append(" Here: ");
1092  StdBuf here(pData, iSize);
1093  sErr.Append(GetDbgRecPktData(eType, here));
1094  DebugRecError(sErr.getData());
1095  }
1096  // packet is fine, jump over it
1097  if (fHasPacketFromHead)
1098  NextChunk();
1099 }
C4Config Config
Definition: C4Config.cpp:930
C4GameControl Control
C4Game Game
Definition: C4Globals.cpp:52
constexpr int DEBUGREC_START_FRAME
Definition: C4Include.h:31
bool Log(const char *szMessage)
Definition: C4Log.cpp:204
const char * GetRecordChunkTypeName(C4RecordChunkType eType)
Definition: C4Record.cpp:928
StdStrBuf GetDbgRecPktData(C4RecordChunkType eType, const StdBuf &RawData)
Definition: C4Record.cpp:977
int DoNoDebugRec
Definition: C4Record.cpp:30
CStdFile DbgRecFile
Definition: C4Record.cpp:29
C4RecordChunkType
Definition: C4Record.h:45
@ RCT_End
Definition: C4Record.h:49
@ RCT_Undefined
Definition: C4Record.h:90
@ RCT_CtrlPkt
Definition: C4Record.h:47
StdStrBuf FormatString(const char *szFmt,...)
Definition: StdBuf.cpp:270
int iSize
Definition: TstC4NetIO.cpp:32
char DebugRecExternalFile[_MAX_PATH_LEN]
Definition: C4Config.h:65
int32_t DebugRecWrite
Definition: C4Config.h:64
C4ConfigGeneral General
Definition: C4Config.h:255
virtual bool Sync() const
Definition: C4Control.h:55
void ExecControlPacket(C4PacketType eCtrlType, class C4ControlPacket *pPkt)
int32_t FrameCounter
Definition: C4Game.h:129
C4PacketBase * getPkt() const
Definition: C4PacketBase.h:260
C4IDPacket * firstPkt() const
Definition: C4PacketBase.h:282
void Delete(C4IDPacket *pPkt)
Definition: C4Packet2.cpp:348
const void * getData() const
Definition: C4PacketBase.h:235
size_t getSize() const
Definition: C4PacketBase.h:234
C4RecordChunkType getType() const
Definition: C4Record.h:238
void NextChunk()
Definition: C4Record.cpp:609
void DebugRecError(const char *szError)
Definition: C4Record.cpp:1101
bool Write(const void *pBuffer, int iSize)
Definition: CStdFile.cpp:240
bool Read(void *pBuffer, size_t iSize) override
Definition: CStdFile.h:60
Definition: StdBuf.h:30
void * getMData()
Definition: StdBuf.h:100
void SetSize(size_t inSize)
Definition: StdBuf.h:204
const char * getData() const
Definition: StdBuf.h:442
void Append(const char *pnData, size_t iChars)
Definition: StdBuf.h:519
void Format(const char *szFmt,...) GNUC_FORMAT_ATTRIBUTE_O
Definition: StdBuf.cpp:174

References StdStrBuf::Append(), Config, Control, DbgRecFile, DEBUGREC_START_FRAME, DebugRecError(), C4ConfigGeneral::DebugRecExternalFile, C4ConfigGeneral::DebugRecWrite, C4PacketList::Delete(), DoNoDebugRec, C4GameControl::ExecControlPacket(), C4PacketList::firstPkt(), StdStrBuf::Format(), FormatString(), C4Game::FrameCounter, Game, C4Config::General, StdStrBuf::getData(), C4PktBuf::getData(), GetDbgRecPktData(), StdBuf::getMData(), C4IDPacket::getPkt(), C4IDPacket::getPktType(), GetRecordChunkTypeName(), C4PktBuf::getSize(), C4PktDebugRec::getType(), iSize, Log(), NextChunk(), RCT_CtrlPkt, RCT_End, RCT_Undefined, CStdFile::Read(), StdBuf::SetSize(), C4ControlPacket::Sync(), and CStdFile::Write().

Referenced by C4GameControl::DbgRec().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ Clear()

void C4Playback::Clear ( )

Definition at line 909 of file C4Record.cpp.

910 {
911  // free stuff
912  for (auto & chunk : chunks) chunk.Delete();
913  chunks.clear(); currChunk = chunks.end();
914  playbackFile.Close();
915  sequentialBuffer.Clear();
916  fLoadSequential = false;
917  if (Config.General.DebugRec)
918  {
919  C4IDPacket *pkt;
920  while ((pkt = DebugRec.firstPkt())) DebugRec.Delete(pkt);
922  DbgRecFile.Close();
923  }
924  // done
925  Finished = true;
926 }
int32_t DebugRec
Definition: C4Config.h:63
bool Close(StdBuf **ppMemory=nullptr)
Definition: CStdFile.cpp:151
void Clear()
Definition: StdBuf.h:190

References StdBuf::Clear(), CStdFile::Close(), Config, DbgRecFile, C4ConfigGeneral::DebugRec, C4ConfigGeneral::DebugRecExternalFile, C4PacketList::Delete(), C4PacketList::firstPkt(), and C4Config::General.

Referenced by Open(), and ~C4Playback().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ DebugRecError()

void C4Playback::DebugRecError ( const char *  szError)

Definition at line 1101 of file C4Record.cpp.

1102 {
1103  LogF("Playback error: %s", szError);
1105 }
bool LogF(const char *strMessage,...)
Definition: C4Log.cpp:262
#define BREAKPOINT_HERE

References BREAKPOINT_HERE, and LogF().

Referenced by Check(), and ExecuteControl().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ ExecuteControl()

bool C4Playback::ExecuteControl ( C4Control pCtrl,
int  iFrame 
)

Definition at line 840 of file C4Record.cpp.

841 {
842  // still playbacking?
843  if (currChunk == chunks.end()) return false;
844  if (Finished) { Finish(); return false; }
845  if (Config.General.DebugRec)
846  {
847  if (DebugRec.firstPkt())
848  DebugRecError("Debug rec overflow!");
849  DebugRec.Clear();
850  }
851  // return all control until this frame
852  while (currChunk != chunks.end() && currChunk->Frame <= iFrame)
853  {
854  switch (currChunk->Type)
855  {
856  case RCT_Ctrl:
857  pCtrl->Append(*currChunk->pCtrl);
858  break;
859 
860  case RCT_CtrlPkt:
861  {
862  C4IDPacket Packet(*currChunk->pPkt);
863  pCtrl->Add(Packet.getPktType(), static_cast<C4ControlPacket *>(Packet.getPkt()));
864  Packet.Default();
865  break;
866  }
867 
868  case RCT_End:
869  // end of playback; stop it!
870  Finished=true;
871  break;
872 
873  default: // expect it to be debug rec
874  if (Config.General.DebugRec)
875  {
876  // append to debug rec buffer
877  if (currChunk->pDbg)
878  {
879  DebugRec.Add(CID_DebugRec, currChunk->pDbg);
880  // the debugrec buffer is now responsible for deleting the packet
881  currChunk->pDbg = nullptr;
882  }
883  break;
884  }
885  }
886  // next chunk
887  NextChunk();
888  }
889  return true;
890 }
@ CID_DebugRec
Definition: C4PacketBase.h:174
@ RCT_Ctrl
Definition: C4Record.h:46
void Append(const C4Control &Ctrl)
Definition: C4Control.h:85
void Add(C4PacketType eType, C4ControlPacket *pCtrl)
Definition: C4Control.h:82
void Clear()
Definition: C4Packet2.cpp:321
void Add(C4IDPacket *pPkt)
Definition: C4Packet2.cpp:283

References C4PacketList::Add(), C4Control::Add(), C4Control::Append(), CID_DebugRec, C4PacketList::Clear(), Config, C4ConfigGeneral::DebugRec, DebugRecError(), C4IDPacket::Default(), C4PacketList::firstPkt(), C4Config::General, C4IDPacket::getPkt(), C4IDPacket::getPktType(), NextChunk(), RCT_Ctrl, RCT_CtrlPkt, and RCT_End.

Referenced by C4GameControl::Execute().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ IsFinished()

bool C4Playback::IsFinished ( )
inline

Definition at line 311 of file C4Record.h.

311 { return Finished; }

◆ NextChunk()

void C4Playback::NextChunk ( )

Definition at line 609 of file C4Record.cpp.

610 {
611  assert(currChunk != chunks.end());
612  ++currChunk;
613  if (currChunk != chunks.end()) return;
614  // end of all chunks if not loading sequential here
615  if (!fLoadSequential) return;
616  // otherwise, get next few chunks
617  for (auto & chunk : chunks) chunk.Delete();
618  chunks.clear(); currChunk = chunks.end();
620 }
bool NextSequentialChunk()
Definition: C4Record.cpp:622

References NextSequentialChunk().

Referenced by Check(), and ExecuteControl().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ NextSequentialChunk()

bool C4Playback::NextSequentialChunk ( )

Definition at line 622 of file C4Record.cpp.

623 {
624  StdBuf BinaryBuf; size_t iRealSize;
625  BinaryBuf.New(4096);
626  // load data until a chunk could be filled
627  for (;;)
628  {
629  iRealSize = 0;
630  playbackFile.Read(BinaryBuf.getMData(), 4096, &iRealSize);
631  if (!iRealSize) return false;
632  BinaryBuf.SetSize(iRealSize);
633  if (!ReadBinary(BinaryBuf)) return false;
634  // okay, at least one chunk has been read!
635  if (chunks.size())
636  {
637  currChunk = chunks.begin();
638  return true;
639  }
640  }
641  // playback file reading failed - looks like we're done
642  return false;
643 }
bool ReadBinary(const StdBuf &Buf)
Definition: C4Record.cpp:504
void New(size_t inSize)
Definition: StdBuf.h:146

References StdBuf::getMData(), StdBuf::New(), CStdFile::Read(), ReadBinary(), and StdBuf::SetSize().

Referenced by NextChunk(), and Open().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ Open()

bool C4Playback::Open ( C4Group rGrp)

Definition at line 382 of file C4Record.cpp.

383 {
384  // clean up
385  Clear();
386  iLastSequentialFrame = 0;
387  bool fStrip = false;
388 
389  // open group? Then do some sequential reading for large files
390  // Can't do this when a dump is forced, because the dump needs all data
391  // Also can't do this when stripping is desired
392  fLoadSequential = !rGrp.IsPacked() && !Game.RecordDumpFile.getLength() && !fStrip;
393 
394  // get text record file
395  StdStrBuf TextBuf;
396  if (rGrp.LoadEntryString(C4CFN_CtrlRecText, &TextBuf))
397  {
398  if (!ReadText(TextBuf))
399  return false;
400  }
401  else
402  {
403  // get record file
404  if (fLoadSequential)
405  {
406  if (!rGrp.FindEntry(C4CFN_CtrlRec)) return false;
407  if (!playbackFile.Open(FormatString("%s%c%s", rGrp.GetFullName().getData(), (char) DirectorySeparator, (const char *) C4CFN_CtrlRec).getData())) return false;
408  // forcing first chunk to be read; will call ReadBinary
409  currChunk = chunks.end();
410  if (!NextSequentialChunk())
411  {
412  // empty replay??!
413  LogFatal("Record: Binary read error.");
414  return false;
415  }
416  }
417  else
418  {
419  // non-sequential reading: Just read as a whole
420  StdBuf BinaryBuf;
421  if (rGrp.LoadEntry(C4CFN_CtrlRec, &BinaryBuf))
422  {
423  if (!ReadBinary(BinaryBuf))
424  return false;
425  }
426  else
427  {
428  // file too large? Try sequential loading and parsing
429  /* size_t iSize;
430  if (rGrp.AccessEntry(C4CFN_CtrlRec, &iSize))
431  {
432  CStdFile fOut; fOut.Create(Game.RecordDumpFile.getData());
433  fLoadSequential = true;
434  const size_t iChunkSize = 1024*1024*16; // 16M
435  while (iSize)
436  {
437  size_t iLoadSize = std::min<size_t>(iChunkSize, iSize);
438  BinaryBuf.SetSize(iLoadSize);
439  if (!rGrp.Read(BinaryBuf.getMData(), iLoadSize))
440  {
441  LogFatal("Record: Binary load error!");
442  return false;
443  }
444  iSize -= iLoadSize;
445  if (!ReadBinary(BinaryBuf)) return false;
446  LogF("%d binary remaining", iSize);
447  currChunk = chunks.begin();
448  if (fStrip) Strip();
449  StdStrBuf s(ReWriteText());
450  fOut.WriteString(s.getData());
451  LogF("Wrote %d text bytes (%d binary remaining)", s.getLength(), iSize);
452  chunks.clear();
453  }
454  fOut.Close();
455  fLoadSequential = false;
456  }
457  else*/
458  {
459  // no control data?
460  LogFatal("Record: No control data found!");
461  return false;
462  }
463  }
464  }
465  }
466  // rewrite record
467  if (fStrip) Strip();
469  {
472  else
474  }
475  // reset status
476  currChunk = chunks.begin();
477  Finished = false;
478  // external debugrec file
480  {
482  {
484  {
485  LogFatal(FormatString(R"(DbgRec: Creation of external file "%s" failed!)", Config.General.DebugRecExternalFile).getData());
486  return false;
487  }
488  else LogF(R"(DbgRec: Writing to "%s"...)", Config.General.DebugRecExternalFile);
489  }
490  else
491  {
493  {
494  LogFatal(FormatString(R"(DbgRec: Opening of external file "%s" failed!)", Config.General.DebugRecExternalFile).getData());
495  return false;
496  }
497  else LogF(R"(DbgRec: Checking against "%s"...)", Config.General.DebugRecExternalFile);
498  }
499  }
500  // ok
501  return true;
502 }
#define C4CFN_CtrlRec
Definition: C4Components.h:77
#define C4CFN_CtrlRecText
Definition: C4Components.h:78
bool LogFatal(const char *szMessage)
Definition: C4Log.cpp:239
#define DirectorySeparator
bool SEqualNoCase(const char *szStr1, const char *szStr2, int iLen)
Definition: Standard.cpp:213
char * GetExtension(char *szFilename)
Definition: StdFile.cpp:118
StdStrBuf RecordDumpFile
Definition: C4Game.h:125
bool IsPacked() const
Definition: C4Group.cpp:2541
StdStrBuf GetFullName() const
Definition: C4Group.cpp:2638
bool LoadEntry(const char *entry_name, char **buffer, size_t *size_info=nullptr, int zeros_to_append=0)
Definition: C4Group.cpp:2375
bool LoadEntryString(const char *entry_name, StdStrBuf *buffer)
Definition: C4Group.cpp:2430
bool FindEntry(const char *wildcard, StdStrBuf *filename=nullptr, size_t *size=nullptr)
Definition: C4Group.cpp:2211
StdBuf ReWriteBinary()
Definition: C4Record.cpp:658
StdStrBuf ReWriteText()
Definition: C4Record.cpp:645
void Strip()
Definition: C4Record.cpp:713
bool ReadText(const StdStrBuf &Buf)
Definition: C4Record.cpp:604
bool Create(const char *szFileName, bool fCompressed=false, bool fExecutable=false, bool fMemory=false)
Definition: CStdFile.cpp:49
bool Open(const char *szFileName, bool fCompressed=false)
Definition: CStdFile.cpp:95
bool SaveToFile(const char *szFile) const
Definition: StdBuf.cpp:53
bool SaveToFile(const char *szFile) const
Definition: StdBuf.cpp:94
size_t getLength() const
Definition: StdBuf.h:445

References C4CFN_CtrlRec, C4CFN_CtrlRecText, Clear(), Config, CStdFile::Create(), DbgRecFile, C4ConfigGeneral::DebugRec, C4ConfigGeneral::DebugRecExternalFile, C4ConfigGeneral::DebugRecWrite, DirectorySeparator, C4Group::FindEntry(), FormatString(), Game, C4Config::General, StdStrBuf::getData(), GetExtension(), C4Group::GetFullName(), StdStrBuf::getLength(), C4Group::IsPacked(), C4Group::LoadEntry(), C4Group::LoadEntryString(), LogF(), LogFatal(), NextSequentialChunk(), CStdFile::Open(), ReadBinary(), ReadText(), C4Game::RecordDumpFile, ReWriteBinary(), ReWriteText(), StdBuf::SaveToFile(), StdStrBuf::SaveToFile(), SEqualNoCase(), and Strip().

Referenced by C4GameControl::InitReplay().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ ReadBinary()

bool C4Playback::ReadBinary ( const StdBuf Buf)

Definition at line 504 of file C4Record.cpp.

505 {
506  // sequential reading: Take over rest from last buffer
507  const StdBuf *pUseBuf; uint32_t iFrame = 0;
508  if (fLoadSequential)
509  {
510  sequentialBuffer.Append(Buf);
511  pUseBuf = &sequentialBuffer;
512  iFrame = iLastSequentialFrame;
513  }
514  else
515  pUseBuf = &Buf;
516  // get buffer data
517  size_t iPos = 0; bool fFinished = false;
518  do
519  {
520  // unpack header
521  if (pUseBuf->getSize() - iPos < sizeof(C4RecordChunkHead)) break;
522  const C4RecordChunkHead *pHead = getBufPtr<C4RecordChunkHead>(*pUseBuf, iPos);
523  // get chunk
524  iPos += sizeof(C4RecordChunkHead);
525  StdBuf Chunk = pUseBuf->getPart(iPos, pUseBuf->getSize() - iPos);
526  // Create entry
527  C4RecordChunk c;
528  c.Frame = (iFrame + pHead->iFrm);
529  c.Type = pHead->Type;
530  // Unpack data
531  try
532  {
533  // Initialize compiler
534  StdCompilerBinRead Compiler;
535  Compiler.setInput(Chunk.getRef());
536  Compiler.Begin();
537  // Read chunk
538  switch (pHead->Type)
539  {
540  case RCT_Ctrl:
541  Compiler.Value(mkPtrAdaptNoNull(c.pCtrl));
542  break;
543  case RCT_CtrlPkt:
544  Compiler.Value(mkPtrAdaptNoNull(c.pPkt));
545  break;
546  case RCT_End:
547  fFinished = true;
548  break;
549  case RCT_File:
550  Compiler.Value(c.Filename);
551  Compiler.Value(mkPtrAdaptNoNull(c.pFileData));
552  break;
553  default:
554  // debugrec
555  if (pHead->Type >= 0x80)
556  Compiler.Value(mkPtrAdaptNoNull(c.pDbg));
557  }
558  // Advance over data
559  Compiler.End();
560  iPos += Compiler.getPosition();
561  }
562  catch (StdCompiler::EOFException *pEx)
563  {
564  // This is to be expected for sequential reading
565  if (fLoadSequential)
566  {
567  iPos -= sizeof(C4RecordChunkHead);
568  delete pEx;
569  break;
570  }
571  LogF("Record: Binary unpack error: %s", pEx->Msg.getData());
572  c.Delete();
573  delete pEx;
574  return false;
575  }
576  catch (StdCompiler::Exception *pEx)
577  {
578  LogF("Record: Binary unpack error: %s", pEx->Msg.getData());
579  c.Delete();
580  delete pEx;
581  return false;
582  }
583  // Add to list
584  chunks.push_back(c); c.pPkt = nullptr;
585  iFrame = c.Frame;
586  }
587  while (!fFinished);
588  // erase everything but the trailing part from sequential buffer
589  if (fLoadSequential)
590  {
591  if (iPos >= sequentialBuffer.getSize())
592  sequentialBuffer.Clear();
593  else if (iPos)
594  {
595  sequentialBuffer.Move(iPos, sequentialBuffer.getSize() - iPos);
596  sequentialBuffer.Shrink(iPos);
597  }
598  // remember frame
599  iLastSequentialFrame = iFrame;
600  }
601  return true;
602 }
@ RCT_File
Definition: C4Record.h:52
uint8_t iFrm
Definition: C4Record.h:99
StdPtrAdapt< T > mkPtrAdaptNoNull(T *&rpObj)
Definition: StdAdaptors.h:638
size_t getSize() const
Definition: StdBuf.h:101
void Shrink(size_t iShrink)
Definition: StdBuf.h:180
StdBuf getRef() const
Definition: StdBuf.h:271
void Move(size_t iFrom, size_t inSize, size_t iTo=0)
Definition: StdBuf.h:159
void Append(const void *pnData, size_t inSize)
Definition: StdBuf.h:254
StdBuf getPart(size_t iStart, size_t inSize) const
Definition: StdBuf.h:107
void setInput(InT &&In)
Definition: StdCompiler.h:491
StdStrBuf getPosition() const override
void Begin() override
void Value(const T &rStruct)
Definition: StdCompiler.h:161
virtual void End()
Definition: StdCompiler.h:156
void Delete()
Definition: C4Record.cpp:72
int32_t Frame
Definition: C4Record.h:105
uint8_t Type
Definition: C4Record.h:106
StdCopyStrBuf Filename
Definition: C4Record.h:114

References StdBuf::Append(), StdCompilerBinRead::Begin(), StdBuf::Clear(), C4RecordChunk::Delete(), StdCompiler::End(), C4RecordChunk::Filename, C4RecordChunk::Frame, StdStrBuf::getData(), StdBuf::getPart(), StdCompilerBinRead::getPosition(), StdBuf::getRef(), StdBuf::getSize(), C4RecordChunkHead::iFrm, LogF(), mkPtrAdaptNoNull(), StdBuf::Move(), StdCompiler::Exception::Msg, RCT_Ctrl, RCT_CtrlPkt, RCT_End, RCT_File, StdCompilerBinRead::setInput(), StdBuf::Shrink(), C4RecordChunkHead::Type, C4RecordChunk::Type, and StdCompiler::Value().

Referenced by NextSequentialChunk(), Open(), and StreamToRecord().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ ReadText()

bool C4Playback::ReadText ( const StdStrBuf Buf)

Definition at line 604 of file C4Record.cpp.

605 {
606  return CompileFromBuf_LogWarn<StdCompilerINIRead>(mkNamingAdapt(mkSTLContainerAdapt(chunks), "Rec"), Buf, C4CFN_CtrlRecText);
607 }
StdSTLContainerAdapt< C > mkSTLContainerAdapt(C &rTarget, StdCompiler::Sep eSep=StdCompiler::SEP_SEP)
Definition: StdAdaptors.h:713
StdNamingAdapt< T > mkNamingAdapt(T &&rValue, const char *szName)
Definition: StdAdaptors.h:92

References C4CFN_CtrlRecText, mkNamingAdapt(), and mkSTLContainerAdapt().

Referenced by Open().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ ReWriteBinary()

StdBuf C4Playback::ReWriteBinary ( )

Definition at line 658 of file C4Record.cpp.

659 {
660  const int OUTPUT_GROW = 16 * 1024;
661  StdBuf Output; int iPos = 0;
662  bool fFinished = false;
663  int32_t iFrame = 0;
664  for (chunks_t::const_iterator i = chunks.begin(); !fFinished && i != chunks.end(); i++)
665  {
666  // Check frame difference
667  if (i->Frame - iFrame < 0 || i->Frame - iFrame > 0xff)
668  LogF("ERROR: Invalid frame difference between chunks (0-255 allowed)! Data will be invalid!");
669  // Pack data
670  StdBuf Chunk;
671  try
672  {
673  switch (i->Type)
674  {
675  case RCT_Ctrl:
676  Chunk = DecompileToBuf<StdCompilerBinWrite>(*i->pCtrl);
677  break;
678  case RCT_CtrlPkt:
679  Chunk = DecompileToBuf<StdCompilerBinWrite>(*i->pPkt);
680  break;
681  case RCT_End:
682  fFinished = true;
683  break;
684  default: // debugrec
685  if (i->pDbg)
686  Chunk = DecompileToBuf<StdCompilerBinWrite>(*i->pDbg);
687  break;
688  }
689  }
690  catch (StdCompiler::Exception *pEx)
691  {
692  LogF("Record: Binary unpack error: %s", pEx->Msg.getData());
693  delete pEx;
694  return StdBuf();
695  }
696  // Grow output
697  while (Output.getSize() - iPos < sizeof(C4RecordChunkHead) + Chunk.getSize())
698  Output.Grow(OUTPUT_GROW);
699  // Write header
700  C4RecordChunkHead *pHead = getMBufPtr<C4RecordChunkHead>(Output, iPos);
701  pHead->Type = i->Type;
702  pHead->iFrm = i->Frame - iFrame;
703  iPos += sizeof(C4RecordChunkHead);
704  iFrame = i->Frame;
705  // Write chunk
706  Output.Write(Chunk, iPos);
707  iPos += Chunk.getSize();
708  }
709  Output.SetSize(iPos);
710  return Output;
711 }
void Grow(size_t iGrow)
Definition: StdBuf.h:171
void Write(const void *pnData, size_t inSize, size_t iAt=0)
Definition: StdBuf.h:153

References StdStrBuf::getData(), StdBuf::getSize(), StdBuf::Grow(), C4RecordChunkHead::iFrm, LogF(), StdCompiler::Exception::Msg, RCT_Ctrl, RCT_CtrlPkt, RCT_End, StdBuf::SetSize(), C4RecordChunkHead::Type, and StdBuf::Write().

Referenced by Open(), and StreamToRecord().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ ReWriteText()

StdStrBuf C4Playback::ReWriteText ( )

Definition at line 645 of file C4Record.cpp.

646 {
647  // Would work, too, but is currently too slow due to bad buffering inside StdCompilerINIWrite:
648  // return DecompileToBuf<StdCompilerINIWrite>(mkNamingAdapt(mkSTLContainerAdapt(chunks), "Rec"));
649  StdStrBuf Output;
650  for (chunks_t::const_iterator i = chunks.begin(); i != chunks.end(); i++)
651  {
652  Output.Append(static_cast<const StdStrBuf&>(DecompileToBuf<StdCompilerINIWrite>(mkNamingAdapt(mkDecompileAdapt(*i), "Rec"))));
653  Output.Append("\n\n");
654  }
655  return Output;
656 }
StdDecompileAdapt< T > mkDecompileAdapt(const T &rValue)
Definition: StdAdaptors.h:153

References StdStrBuf::Append(), mkDecompileAdapt(), and mkNamingAdapt().

Referenced by Open().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ StreamToRecord()

bool C4Playback::StreamToRecord ( const char *  szStream,
StdStrBuf pRecord 
)
static

Definition at line 1107 of file C4Record.cpp.

1108 {
1109 
1110  // Load data
1111  StdBuf CompressedData;
1112  Log("Reading stream...");
1113  if (!CompressedData.LoadFromFile(szStream))
1114  return false;
1115 
1116  // Decompress
1117  unsigned long iStreamSize = CompressedData.getSize() * 5;
1118  StdBuf StreamData; StreamData.New(iStreamSize);
1119  while (true)
1120  {
1121 
1122  // Initialize stream
1123  z_stream strm;
1124  ZeroMem(&strm, sizeof strm);
1125  strm.next_in = getMBufPtr<BYTE>(CompressedData);
1126  strm.avail_in = CompressedData.getSize();
1127  strm.next_out = getMBufPtr<BYTE>(StreamData);
1128  strm.avail_out = StreamData.getSize();
1129 
1130  // Decompress
1131  if (inflateInit(&strm) != Z_OK)
1132  return false;
1133  int ret = inflate(&strm, Z_FINISH);
1134  if (ret == Z_OK)
1135  {
1136  inflateEnd(&strm);
1137  break;
1138  }
1139  if (ret != Z_BUF_ERROR)
1140  return false;
1141 
1142  // All input consumed?
1143  iStreamSize = strm.total_out;
1144  if (strm.avail_in == 0)
1145  {
1146  Log("Stream data incomplete, using as much data as possible");
1147  break;
1148  }
1149 
1150  // Larger buffer needed
1151  StreamData.Grow(CompressedData.getSize());
1152  iStreamSize = StreamData.getSize();
1153  }
1154  StreamData.SetSize(iStreamSize);
1155 
1156  // Parse
1157  C4Playback Playback;
1158  Playback.ReadBinary(StreamData);
1159  LogF("Got %lu chunks from stream", static_cast<unsigned long>(Playback.chunks.size()));
1160 
1161  // Get first chunk, which must contain the initial
1162  chunks_t::iterator chunkIter = Playback.chunks.begin();
1163  if (chunkIter == Playback.chunks.end() || chunkIter->Type != RCT_File)
1164  return false;
1165 
1166  // Get initial chunk, go over file name
1167  StdBuf InitialData = *chunkIter->pFileData;
1168 
1169  // Put to temporary file and unpack
1170  char szInitial[_MAX_PATH_LEN] = "~initial.tmp";
1171  MakeTempFilename(szInitial);
1172  if (!InitialData.SaveToFile(szInitial) ||
1173  !C4Group_UnpackDirectory(szInitial))
1174  return false;
1175 
1176  // Load Scenario.txt from Initial
1177  C4Group Grp; C4Scenario Initial;
1178  if (!Grp.Open(szInitial) ||
1179  !Initial.Load(Grp) ||
1180  !Grp.Close())
1181  return false;
1182 
1183  // Copy original scenario
1184  const char *szOrigin = Initial.Head.Origin.getData();
1185  char szRecord[_MAX_PATH_LEN];
1186  SCopy(szStream, szRecord, _MAX_PATH);
1187  if (GetExtension(szRecord))
1188  *(GetExtension(szRecord) - 1) = 0;
1189  SAppend(".ocs", szRecord, _MAX_PATH);
1190  LogF("Original scenario is %s, creating %s.", szOrigin, szRecord);
1191  if (!C4Group_CopyItem(szOrigin, szRecord, false, false))
1192  return false;
1193 
1194  // Merge initial
1195  if (!Grp.Open(szRecord) ||
1196  !Grp.Merge(szInitial))
1197  return false;
1198 
1199  // Process other files in stream
1200  chunkIter->Delete();
1201  chunkIter = Playback.chunks.erase(chunkIter);
1202  while (chunkIter != Playback.chunks.end())
1203  if (chunkIter->Type == RCT_File)
1204  {
1205  LogF("Inserting %s...", chunkIter->Filename.getData());
1206  StdStrBuf Temp; Temp.Copy(chunkIter->Filename);
1207  MakeTempFilename(&Temp);
1208  if (!chunkIter->pFileData->SaveToFile(Temp.getData()))
1209  return false;
1210  if (!Grp.Move(Temp.getData(), chunkIter->Filename.getData()))
1211  return false;
1212  chunkIter = Playback.chunks.erase(chunkIter);
1213  }
1214  else
1215  chunkIter++;
1216 
1217  // Write record data
1218  StdBuf RecordData = Playback.ReWriteBinary();
1219  if (!Grp.Add(C4CFN_CtrlRec, RecordData, false, true))
1220  return false;
1221 
1222  // Done
1223  Log("Writing record file...");
1224  Grp.Close();
1225  pRecordFile->Copy(szRecord);
1226  return true;
1227 }
bool C4Group_CopyItem(const char *source, const char *target, bool no_sorting, bool reset_attributes)
Definition: C4Group.cpp:115
bool C4Group_UnpackDirectory(const char *filename)
Definition: C4Group.cpp:401
#define _MAX_PATH
#define _MAX_PATH_LEN
void SCopy(const char *szSource, char *sTarget, size_t iMaxL)
Definition: Standard.cpp:152
void SAppend(const char *szSource, char *szTarget, int iMaxL)
Definition: Standard.cpp:263
std::enable_if< std::is_pod< T >::value >::type ZeroMem(T *lpMem, size_t dwSize)
Definition: Standard.h:60
void MakeTempFilename(char *szFilename)
Definition: StdFile.cpp:320
bool Merge(const char *folders)
Definition: C4Group.cpp:1534
bool Add(const char *filename, const char *entry_name)
Definition: C4Group.cpp:1621
bool Close()
Definition: C4Group.cpp:971
bool Move(const char *filename, const char *entry_name)
Definition: C4Group.cpp:1633
bool Open(const char *group_name, bool do_create=false)
Definition: C4Group.cpp:660
StdCopyStrBuf Origin
Definition: C4Scenario.h:80
C4SHead Head
Definition: C4Scenario.h:232
bool Load(C4Group &hGroup, bool fLoadSection=false, bool suppress_errors=false)
Definition: C4Scenario.cpp:92
bool LoadFromFile(const char *szFile)
Definition: StdBuf.cpp:32
void Copy()
Definition: StdBuf.h:467

References _MAX_PATH, _MAX_PATH_LEN, C4Group::Add(), C4CFN_CtrlRec, C4Group_CopyItem(), C4Group_UnpackDirectory(), C4Group::Close(), StdStrBuf::Copy(), StdStrBuf::getData(), GetExtension(), StdBuf::getSize(), StdBuf::Grow(), C4Scenario::Head, C4Scenario::Load(), StdBuf::LoadFromFile(), Log(), LogF(), MakeTempFilename(), C4Group::Merge(), C4Group::Move(), StdBuf::New(), C4Group::Open(), C4SHead::Origin, RCT_File, ReadBinary(), ReWriteBinary(), SAppend(), StdBuf::SaveToFile(), SCopy(), StdBuf::SetSize(), and ZeroMem().

Referenced by C4Game::OpenScenario().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ Strip()

void C4Playback::Strip ( )

Definition at line 713 of file C4Record.cpp.

714 {
715  // Strip what?
716  const bool fStripPlayers = false;
717  const bool fStripSyncChecks = false;
718  const bool fStripDebugRec = true;
719  const bool fCheckCheat = false;
720  const bool fStripMessages = true;
721  const int32_t iEndFrame = -1;
722  // Iterate over chunk list
723  for (chunks_t::iterator i = chunks.begin(); i != chunks.end(); )
724  {
725  // Strip rest of record?
726  if (iEndFrame >= 0 && i->Frame > iEndFrame)
727  {
728  // Remove this and all remaining chunks
729  while (i != chunks.end())
730  {
731  i->Delete();
732  i = chunks.erase(i);
733  }
734  // Push new End-Chunk
735  C4RecordChunk EndChunk;
736  EndChunk.Frame = iEndFrame;
737  EndChunk.Type = RCT_End;
738  chunks.push_back(EndChunk);
739  // Done
740  break;
741  }
742  switch (i->Type)
743  {
744  case RCT_Ctrl:
745  {
746  // Iterate over controls
747  C4Control *pCtrl = i->pCtrl;
748  for (C4IDPacket *pPkt = pCtrl->firstPkt(), *pNext; pPkt; pPkt = pNext)
749  {
750  pNext = pCtrl->nextPkt(pPkt);
751  switch (pPkt->getPktType())
752  {
753  // Player join: Strip player file (if possible)
754  case CID_JoinPlr:
755  if (fStripPlayers)
756  {
757  C4ControlJoinPlayer *pJoinPlr = static_cast<C4ControlJoinPlayer *>(pPkt->getPkt());
758  pJoinPlr->Strip();
759  }
760  break;
761  // EM commands: May be cheats, so log them
762  case CID_Script:
763  case CID_EMMoveObj:
764  case CID_EMDrawTool:
765  case CID_ReInitScenario:
766  case CID_EditGraph:
767  if (fCheckCheat) Log(DecompileToBuf<StdCompilerINIWrite>(mkNamingAdapt(*pPkt, FormatString("Frame %d", i->Frame).getData())).getData());
768  break;
769  // Strip sync check
770  case CID_SyncCheck:
771  if (fStripSyncChecks)
772  {
773  i->pCtrl->Remove(pPkt);
774  }
775  break;
776  default:
777  // TODO
778  break;
779  }
780  }
781  // Strip empty control lists (always)
782  if (!pCtrl->firstPkt())
783  {
784  i->Delete();
785  i = chunks.erase(i);
786  }
787  else
788  i++;
789  }
790  break;
791  case RCT_CtrlPkt:
792  {
793  bool fStripThis=false;
794  switch (i->pPkt->getPktType())
795  {
796  // EM commands: May be cheats, so log them
797  case CID_Script:
798  case CID_EMMoveObj:
799  case CID_EMDrawTool:
800  case CID_ReInitScenario:
801  case CID_EditGraph:
802  if (fCheckCheat) Log(DecompileToBuf<StdCompilerINIWrite>(mkNamingAdapt(*i->pPkt, FormatString("Frame %d", i->Frame).getData())).getData());
803  break;
804  // Strip some stuff
805  case CID_SyncCheck:
806  if (fStripSyncChecks) fStripThis = true;
807  break;
808  case CID_Message:
809  if (fStripMessages) fStripThis=true;
810  break;
811  default:
812  // TODO
813  break;
814  }
815  if (fStripThis)
816  {
817  i->Delete();
818  i = chunks.erase(i);
819  }
820  else i++;
821  }
822  break;
823  case RCT_End:
824  i++;
825  break;
826  default:
827  // Strip debugrec
828  if (fStripDebugRec)
829  {
830  i->Delete();
831  i = chunks.erase(i);
832  }
833  else
834  i++;
835  }
836  }
837 }
@ CID_ReInitScenario
Definition: C4PacketBase.h:171
@ CID_JoinPlr
Definition: C4PacketBase.h:159
@ CID_SyncCheck
Definition: C4PacketBase.h:151
@ CID_Message
Definition: C4PacketBase.h:165
@ CID_EMMoveObj
Definition: C4PacketBase.h:169
@ CID_EMDrawTool
Definition: C4PacketBase.h:170
@ CID_EditGraph
Definition: C4PacketBase.h:172
@ CID_Script
Definition: C4PacketBase.h:154
C4IDPacket * firstPkt() const
Definition: C4Control.h:78
C4IDPacket * nextPkt(C4IDPacket *pPkt) const
Definition: C4Control.h:79

References CID_EditGraph, CID_EMDrawTool, CID_EMMoveObj, CID_JoinPlr, CID_Message, CID_ReInitScenario, CID_Script, CID_SyncCheck, C4Control::firstPkt(), FormatString(), C4RecordChunk::Frame, StdStrBuf::getData(), Log(), mkNamingAdapt(), C4Control::nextPkt(), RCT_Ctrl, RCT_CtrlPkt, RCT_End, C4ControlJoinPlayer::Strip(), and C4RecordChunk::Type.

Referenced by Open().

Here is the call graph for this function:
Here is the caller graph for this function:

The documentation for this class was generated from the following files: