OpenClonk
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
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 ( )

Definition at line 384 of file C4Record.cpp.

384  : Finished(true), fLoadSequential(false)
385 {
386 }
C4Playback::~C4Playback ( )

Definition at line 388 of file C4Record.cpp.

References Clear().

389 {
390  Clear();
391 }
void Clear()
Definition: C4Record.cpp:920

Here is the call graph for this function:

Member Function Documentation

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

Definition at line 1003 of file C4Record.cpp.

References StdStrBuf::Append(), Config, Control, DEBUGREC_START_FRAME, DebugRecError(), C4ConfigGeneral::DebugRecExternalFile, C4ConfigGeneral::DebugRecWrite, C4PacketList::Delete(), DoNoDebugRec, C4GameControl::ExecControlPacket(), C4PacketList::firstPkt(), StdStrBuf::Format(), FormatString(), C4Game::FrameCounter, Game, C4Config::General, C4PktBuf::getData(), StdStrBuf::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().

1004 {
1005  // only if enabled
1006  if (DoNoDebugRec>0) return;
1007  if (Game.FrameCounter < DEBUGREC_START_FRAME) return;
1008 
1009  C4PktDebugRec PktInReplay;
1010  bool fHasPacketFromHead = false;
1012  {
1014  {
1015  // writing of external debugrec file
1016  DbgRecFile.Write(&eType, sizeof eType);
1017  int32_t iSize32 = iSize;
1018  DbgRecFile.Write(&iSize32, sizeof iSize32);
1019  DbgRecFile.Write(pData, iSize);
1020  return;
1021  }
1022  else
1023  {
1024  int32_t iSize32 = 0;
1025  C4RecordChunkType eTypeRec = RCT_Undefined;
1026  DbgRecFile.Read(&eTypeRec, sizeof eTypeRec);
1027  DbgRecFile.Read(&iSize32, sizeof iSize32);
1028  if (iSize32)
1029  {
1030  StdBuf buf;
1031  buf.SetSize(iSize32);
1032  DbgRecFile.Read(buf.getMData(), iSize32);
1033  PktInReplay = C4PktDebugRec(eTypeRec, buf);
1034  }
1035  }
1036  }
1037  else
1038  {
1039  // check debug rec in list
1040  C4IDPacket *pkt;
1041  if ((pkt = DebugRec.firstPkt()))
1042  {
1043  // copy from list
1044  PktInReplay = *static_cast<C4PktDebugRec *>(pkt->getPkt());
1045  DebugRec.Delete(pkt);
1046  }
1047  else
1048  {
1049  // special sync check skip...
1050  while (currChunk != chunks.end() && currChunk->Type == RCT_CtrlPkt)
1051  {
1052  C4IDPacket Packet(*currChunk->pPkt);
1053  C4ControlPacket *pCtrlPck = static_cast<C4ControlPacket *>(Packet.getPkt());
1054  assert(!pCtrlPck->Sync());
1055  ::Control.ExecControlPacket(Packet.getPktType(), pCtrlPck);
1056  NextChunk();
1057  }
1058  // record end?
1059  if (currChunk == chunks.end() || currChunk->Type == RCT_End || Finished)
1060  {
1061  Log("DebugRec end: All in sync!");
1062  ++DoNoDebugRec;
1063  return;
1064  }
1065  // unpack directly from head
1066  if (currChunk->Type != eType)
1067  {
1068  DebugRecError(FormatString("Playback type %x, this type %x", currChunk->Type, eType).getData());
1069  return;
1070  }
1071  if (currChunk->pDbg)
1072  PktInReplay = *currChunk->pDbg;
1073 
1074  fHasPacketFromHead = true;
1075  }
1076  }
1077  // record end?
1078  if (PktInReplay.getType() == RCT_End)
1079  {
1080  Log("DebugRec end: All in sync (2)!");
1081  ++DoNoDebugRec;
1082  return;
1083  }
1084  // replay packet is unpacked to PktInReplay now; check it
1085  if (PktInReplay.getType() != eType)
1086  {
1087  DebugRecError(FormatString("Type %s != %s", GetRecordChunkTypeName(PktInReplay.getType()), GetRecordChunkTypeName(eType)).getData());
1088  return;
1089  }
1090  if (PktInReplay.getSize() != unsigned(iSize))
1091  {
1092  DebugRecError(FormatString("Size %d != %d", (int) PktInReplay.getSize(), (int) iSize).getData());
1093  }
1094  // check packet data
1095  if (memcmp(PktInReplay.getData(), pData, iSize))
1096  {
1097  StdStrBuf sErr;
1098  sErr.Format("DbgRecPkt Type %s, size %d", GetRecordChunkTypeName(eType), iSize);
1099  sErr.Append(" Replay: ");
1100  StdBuf replay(PktInReplay.getData(), PktInReplay.getSize());
1101  sErr.Append(GetDbgRecPktData(eType, replay));
1102  sErr.Append(" Here: ");
1103  StdBuf here(pData, iSize);
1104  sErr.Append(GetDbgRecPktData(eType, here));
1105  DebugRecError(sErr.getData());
1106  }
1107  // packet is fine, jump over it
1108  if (fHasPacketFromHead)
1109  NextChunk();
1110 }
const char * getData() const
Definition: StdBuf.h:450
C4RecordChunkType getType() const
Definition: C4Record.h:238
C4Config Config
Definition: C4Config.cpp:837
Definition: StdBuf.h:37
C4Game Game
Definition: C4Globals.cpp:52
C4ConfigGeneral General
Definition: C4Config.h:252
const void * getData() const
Definition: C4PacketBase.h:230
const char * GetRecordChunkTypeName(C4RecordChunkType eType)
Definition: C4Record.cpp:939
void ExecControlPacket(C4PacketType eCtrlType, class C4ControlPacket *pPkt)
char DebugRecExternalFile[_MAX_PATH+1]
Definition: C4Config.h:65
void Format(const char *szFmt,...) GNUC_FORMAT_ATTRIBUTE_O
Definition: StdBuf.cpp:181
CStdFile DbgRecFile
Definition: C4Record.cpp:33
void NextChunk()
Definition: C4Record.cpp:620
void SetSize(size_t inSize)
Definition: StdBuf.h:212
C4RecordChunkType
Definition: C4Record.h:44
int DoNoDebugRec
Definition: C4Record.cpp:34
int32_t FrameCounter
Definition: C4Game.h:130
bool Write(const void *pBuffer, int iSize)
Definition: CStdFile.cpp:244
C4GameControl Control
void Append(const char *pnData, size_t iChars)
Definition: StdBuf.h:527
StdStrBuf GetDbgRecPktData(C4RecordChunkType eType, const StdBuf &RawData)
Definition: C4Record.cpp:988
bool Read(void *pBuffer, size_t iSize) override
Definition: CStdFile.h:61
void Delete(C4IDPacket *pPkt)
Definition: C4Packet2.cpp:362
size_t getSize() const
Definition: C4PacketBase.h:229
bool Log(const char *szMessage)
Definition: C4Log.cpp:195
int32_t DebugRecWrite
Definition: C4Config.h:63
virtual bool Sync() const
Definition: C4Control.h:55
C4PacketBase * getPkt() const
Definition: C4PacketBase.h:255
void * getMData()
Definition: StdBuf.h:108
C4IDPacket * firstPkt() const
Definition: C4PacketBase.h:277
#define DEBUGREC_START_FRAME
Definition: C4Include.h:31
void DebugRecError(const char *szError)
Definition: C4Record.cpp:1112
int iSize
Definition: TstC4NetIO.cpp:35
StdStrBuf FormatString(const char *szFmt,...)
Definition: StdBuf.cpp:277

Here is the call graph for this function:

Here is the caller graph for this function:

void C4Playback::Clear ( )

Definition at line 920 of file C4Record.cpp.

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

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

921 {
922  // free stuff
923  for (chunks_t::iterator i = chunks.begin(); i != chunks.end(); i++) i->Delete();
924  chunks.clear(); currChunk = chunks.end();
925  playbackFile.Close();
926  sequentialBuffer.Clear();
927  fLoadSequential = false;
928  if (Config.General.DebugRec)
929  {
930  C4IDPacket *pkt;
931  while ((pkt = DebugRec.firstPkt())) DebugRec.Delete(pkt);
933  DbgRecFile.Close();
934  }
935  // done
936  Finished = true;
937 }
bool Close(StdBuf **ppMemory=nullptr)
Definition: CStdFile.cpp:155
C4Config Config
Definition: C4Config.cpp:837
C4ConfigGeneral General
Definition: C4Config.h:252
void Clear()
Definition: StdBuf.h:198
char DebugRecExternalFile[_MAX_PATH+1]
Definition: C4Config.h:65
CStdFile DbgRecFile
Definition: C4Record.cpp:33
void Delete(C4IDPacket *pPkt)
Definition: C4Packet2.cpp:362
int32_t DebugRec
Definition: C4Config.h:61
C4IDPacket * firstPkt() const
Definition: C4PacketBase.h:277

Here is the call graph for this function:

Here is the caller graph for this function:

void C4Playback::DebugRecError ( const char *  szError)

Definition at line 1112 of file C4Record.cpp.

References BREAKPOINT_HERE, and LogF().

Referenced by Check(), and ExecuteControl().

1113 {
1114  LogF("Playback error: %s", szError);
1116 }
#define BREAKPOINT_HERE
bool LogF(const char *strMessage,...)
Definition: C4Log.cpp:253

Here is the call graph for this function:

Here is the caller graph for this function:

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

Definition at line 851 of file C4Record.cpp.

References C4Control::Add(), C4PacketList::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().

852 {
853  // still playbacking?
854  if (currChunk == chunks.end()) return false;
855  if (Finished) { Finish(); return false; }
856  if (Config.General.DebugRec)
857  {
858  if (DebugRec.firstPkt())
859  DebugRecError("Debug rec overflow!");
860  DebugRec.Clear();
861  }
862  // return all control until this frame
863  while (currChunk != chunks.end() && currChunk->Frame <= iFrame)
864  {
865  switch (currChunk->Type)
866  {
867  case RCT_Ctrl:
868  pCtrl->Append(*currChunk->pCtrl);
869  break;
870 
871  case RCT_CtrlPkt:
872  {
873  C4IDPacket Packet(*currChunk->pPkt);
874  pCtrl->Add(Packet.getPktType(), static_cast<C4ControlPacket *>(Packet.getPkt()));
875  Packet.Default();
876  break;
877  }
878 
879  case RCT_End:
880  // end of playback; stop it!
881  Finished=true;
882  break;
883 
884  default: // expect it to be debug rec
885  if (Config.General.DebugRec)
886  {
887  // append to debug rec buffer
888  if (currChunk->pDbg)
889  {
890  DebugRec.Add(CID_DebugRec, currChunk->pDbg);
891  // the debugrec buffer is now responsible for deleting the packet
892  currChunk->pDbg = nullptr;
893  }
894  break;
895  }
896  }
897  // next chunk
898  NextChunk();
899  }
900  return true;
901 }
C4Config Config
Definition: C4Config.cpp:837
void Add(C4IDPacket *pPkt)
Definition: C4Packet2.cpp:297
C4ConfigGeneral General
Definition: C4Config.h:252
void Add(C4PacketType eType, C4ControlPacket *pCtrl)
Definition: C4Control.h:82
void NextChunk()
Definition: C4Record.cpp:620
void Clear()
Definition: C4Packet2.cpp:335
void Append(const C4Control &Ctrl)
Definition: C4Control.h:85
int32_t DebugRec
Definition: C4Config.h:61
C4IDPacket * firstPkt() const
Definition: C4PacketBase.h:277
void DebugRecError(const char *szError)
Definition: C4Record.cpp:1112

Here is the call graph for this function:

Here is the caller graph for this function:

bool C4Playback::IsFinished ( )
inline

Definition at line 311 of file C4Record.h.

311 { return Finished; }
void C4Playback::NextChunk ( )

Definition at line 620 of file C4Record.cpp.

References NextSequentialChunk().

Referenced by Check(), and ExecuteControl().

621 {
622  assert(currChunk != chunks.end());
623  ++currChunk;
624  if (currChunk != chunks.end()) return;
625  // end of all chunks if not loading sequential here
626  if (!fLoadSequential) return;
627  // otherwise, get next few chunks
628  for (chunks_t::iterator i = chunks.begin(); i != chunks.end(); i++) i->Delete();
629  chunks.clear(); currChunk = chunks.end();
631 }
bool NextSequentialChunk()
Definition: C4Record.cpp:633

Here is the call graph for this function:

Here is the caller graph for this function:

bool C4Playback::NextSequentialChunk ( )

Definition at line 633 of file C4Record.cpp.

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

Referenced by NextChunk(), and Open().

634 {
635  StdBuf BinaryBuf; size_t iRealSize;
636  BinaryBuf.New(4096);
637  // load data until a chunk could be filled
638  for (;;)
639  {
640  iRealSize = 0;
641  playbackFile.Read(BinaryBuf.getMData(), 4096, &iRealSize);
642  if (!iRealSize) return false;
643  BinaryBuf.SetSize(iRealSize);
644  if (!ReadBinary(BinaryBuf)) return false;
645  // okay, at least one chunk has been read!
646  if (chunks.size())
647  {
648  currChunk = chunks.begin();
649  return true;
650  }
651  }
652  // playback file reading failed - looks like we're done
653  return false;
654 }
Definition: StdBuf.h:37
void SetSize(size_t inSize)
Definition: StdBuf.h:212
bool Read(void *pBuffer, size_t iSize) override
Definition: CStdFile.h:61
void New(size_t inSize)
Definition: StdBuf.h:154
bool ReadBinary(const StdBuf &Buf)
Definition: C4Record.cpp:515
void * getMData()
Definition: StdBuf.h:108

Here is the call graph for this function:

Here is the caller graph for this function:

bool C4Playback::Open ( C4Group rGrp)

Definition at line 393 of file C4Record.cpp.

References C4CFN_CtrlRec, C4CFN_CtrlRecText, Clear(), Config, CStdFile::Create(), 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().

394 {
395  // clean up
396  Clear();
397  iLastSequentialFrame = 0;
398  bool fStrip = false;
399 
400  // open group? Then do some sequential reading for large files
401  // Can't do this when a dump is forced, because the dump needs all data
402  // Also can't do this when stripping is desired
403  fLoadSequential = !rGrp.IsPacked() && !Game.RecordDumpFile.getLength() && !fStrip;
404 
405  // get text record file
406  StdStrBuf TextBuf;
407  if (rGrp.LoadEntryString(C4CFN_CtrlRecText, &TextBuf))
408  {
409  if (!ReadText(TextBuf))
410  return false;
411  }
412  else
413  {
414  // get record file
415  if (fLoadSequential)
416  {
417  if (!rGrp.FindEntry(C4CFN_CtrlRec)) return false;
418  if (!playbackFile.Open(FormatString("%s%c%s", rGrp.GetFullName().getData(), (char) DirectorySeparator, (const char *) C4CFN_CtrlRec).getData())) return false;
419  // forcing first chunk to be read; will call ReadBinary
420  currChunk = chunks.end();
421  if (!NextSequentialChunk())
422  {
423  // empty replay??!
424  LogFatal("Record: Binary read error.");
425  return false;
426  }
427  }
428  else
429  {
430  // non-sequential reading: Just read as a whole
431  StdBuf BinaryBuf;
432  if (rGrp.LoadEntry(C4CFN_CtrlRec, &BinaryBuf))
433  {
434  if (!ReadBinary(BinaryBuf))
435  return false;
436  }
437  else
438  {
439  // file too large? Try sequential loading and parsing
440  /* size_t iSize;
441  if (rGrp.AccessEntry(C4CFN_CtrlRec, &iSize))
442  {
443  CStdFile fOut; fOut.Create(Game.RecordDumpFile.getData());
444  fLoadSequential = true;
445  const size_t iChunkSize = 1024*1024*16; // 16M
446  while (iSize)
447  {
448  size_t iLoadSize = std::min<size_t>(iChunkSize, iSize);
449  BinaryBuf.SetSize(iLoadSize);
450  if (!rGrp.Read(BinaryBuf.getMData(), iLoadSize))
451  {
452  LogFatal("Record: Binary load error!");
453  return false;
454  }
455  iSize -= iLoadSize;
456  if (!ReadBinary(BinaryBuf)) return false;
457  LogF("%d binary remaining", iSize);
458  currChunk = chunks.begin();
459  if (fStrip) Strip();
460  StdStrBuf s(ReWriteText());
461  fOut.WriteString(s.getData());
462  LogF("Wrote %d text bytes (%d binary remaining)", s.getLength(), iSize);
463  chunks.clear();
464  }
465  fOut.Close();
466  fLoadSequential = false;
467  }
468  else*/
469  {
470  // no control data?
471  LogFatal("Record: No control data found!");
472  return false;
473  }
474  }
475  }
476  }
477  // rewrite record
478  if (fStrip) Strip();
480  {
483  else
485  }
486  // reset status
487  currChunk = chunks.begin();
488  Finished = false;
489  // external debugrec file
491  {
493  {
495  {
496  LogFatal(FormatString("DbgRec: Creation of external file \"%s\" failed!", Config.General.DebugRecExternalFile).getData());
497  return false;
498  }
499  else LogF("DbgRec: Writing to \"%s\"...", Config.General.DebugRecExternalFile);
500  }
501  else
502  {
504  {
505  LogFatal(FormatString("DbgRec: Opening of external file \"%s\" failed!", Config.General.DebugRecExternalFile).getData());
506  return false;
507  }
508  else LogF("DbgRec: Checking against \"%s\"...", Config.General.DebugRecExternalFile);
509  }
510  }
511  // ok
512  return true;
513 }
const char * getData() const
Definition: StdBuf.h:450
bool FindEntry(const char *szWildCard, StdStrBuf *sFileName=nullptr, size_t *iSize=nullptr)
Definition: C4Group.cpp:1774
C4Config Config
Definition: C4Config.cpp:837
Definition: StdBuf.h:37
StdStrBuf RecordDumpFile
Definition: C4Game.h:126
C4Game Game
Definition: C4Globals.cpp:52
bool Create(const char *szFileName, bool fCompressed=false, bool fExecutable=false, bool fMemory=false)
Definition: CStdFile.cpp:53
C4ConfigGeneral General
Definition: C4Config.h:252
bool SEqualNoCase(const char *szStr1, const char *szStr2, int iLen)
Definition: Standard.cpp:177
StdBuf ReWriteBinary()
Definition: C4Record.cpp:669
bool LoadEntry(const char *szEntryName, char **lpbpBuf, size_t *ipSize=nullptr, int iAppendZeros=0)
Definition: C4Group.cpp:1893
char DebugRecExternalFile[_MAX_PATH+1]
Definition: C4Config.h:65
CStdFile DbgRecFile
Definition: C4Record.cpp:33
bool ReadText(const StdStrBuf &Buf)
Definition: C4Record.cpp:615
bool SaveToFile(const char *szFile) const
Definition: StdBuf.cpp:101
StdStrBuf GetFullName() const
Definition: C4Group.cpp:2078
StdStrBuf ReWriteText()
Definition: C4Record.cpp:656
#define C4CFN_CtrlRecText
Definition: C4Components.h:78
void Clear()
Definition: C4Record.cpp:920
bool LogFatal(const char *szMessage)
Definition: C4Log.cpp:230
char * GetExtension(char *szFilename)
Definition: StdFile.cpp:131
bool SaveToFile(const char *szFile) const
Definition: StdBuf.cpp:60
void Strip()
Definition: C4Record.cpp:724
bool LoadEntryString(const char *szEntryName, StdStrBuf *Buf)
Definition: C4Group.cpp:1932
bool Open(const char *szFileName, bool fCompressed=false)
Definition: CStdFile.cpp:99
int32_t DebugRecWrite
Definition: C4Config.h:63
bool IsPacked() const
Definition: C4Group.cpp:2009
size_t getLength() const
Definition: StdBuf.h:453
int32_t DebugRec
Definition: C4Config.h:61
bool NextSequentialChunk()
Definition: C4Record.cpp:633
#define C4CFN_CtrlRec
Definition: C4Components.h:77
bool ReadBinary(const StdBuf &Buf)
Definition: C4Record.cpp:515
#define DirectorySeparator
bool LogF(const char *strMessage,...)
Definition: C4Log.cpp:253
StdStrBuf FormatString(const char *szFmt,...)
Definition: StdBuf.cpp:277

Here is the call graph for this function:

Here is the caller graph for this function:

bool C4Playback::ReadBinary ( const StdBuf Buf)

Definition at line 515 of file C4Record.cpp.

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().

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

Here is the call graph for this function:

Here is the caller graph for this function:

bool C4Playback::ReadText ( const StdStrBuf Buf)

Definition at line 615 of file C4Record.cpp.

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

Referenced by Open().

616 {
617  return CompileFromBuf_LogWarn<StdCompilerINIRead>(mkNamingAdapt(mkSTLContainerAdapt(chunks), "Rec"), Buf, C4CFN_CtrlRecText);
618 }
StdNamingAdapt< T > mkNamingAdapt(T &&rValue, const char *szName)
Definition: StdAdaptors.h:93
#define C4CFN_CtrlRecText
Definition: C4Components.h:78
StdSTLContainerAdapt< C > mkSTLContainerAdapt(C &rTarget, StdCompiler::Sep eSep=StdCompiler::SEP_SEP)
Definition: StdAdaptors.h:679

Here is the call graph for this function:

Here is the caller graph for this function:

StdBuf C4Playback::ReWriteBinary ( )

Definition at line 669 of file C4Record.cpp.

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

Referenced by Open(), and StreamToRecord().

670 {
671  const int OUTPUT_GROW = 16 * 1024;
672  StdBuf Output; int iPos = 0;
673  bool fFinished = false;
674  int32_t iFrame = 0;
675  for (chunks_t::const_iterator i = chunks.begin(); !fFinished && i != chunks.end(); i++)
676  {
677  // Check frame difference
678  if (i->Frame - iFrame < 0 || i->Frame - iFrame > 0xff)
679  LogF("ERROR: Invalid frame difference between chunks (0-255 allowed)! Data will be invalid!");
680  // Pack data
681  StdBuf Chunk;
682  try
683  {
684  switch (i->Type)
685  {
686  case RCT_Ctrl:
687  Chunk = DecompileToBuf<StdCompilerBinWrite>(*i->pCtrl);
688  break;
689  case RCT_CtrlPkt:
690  Chunk = DecompileToBuf<StdCompilerBinWrite>(*i->pPkt);
691  break;
692  case RCT_End:
693  fFinished = true;
694  break;
695  default: // debugrec
696  if (i->pDbg)
697  Chunk = DecompileToBuf<StdCompilerBinWrite>(*i->pDbg);
698  break;
699  }
700  }
701  catch (StdCompiler::Exception *pEx)
702  {
703  LogF("Record: Binary unpack error: %s", pEx->Msg.getData());
704  delete pEx;
705  return StdBuf();
706  }
707  // Grow output
708  while (Output.getSize() - iPos < sizeof(C4RecordChunkHead) + Chunk.getSize())
709  Output.Grow(OUTPUT_GROW);
710  // Write header
711  C4RecordChunkHead *pHead = getMBufPtr<C4RecordChunkHead>(Output, iPos);
712  pHead->Type = i->Type;
713  pHead->iFrm = i->Frame - iFrame;
714  iPos += sizeof(C4RecordChunkHead);
715  iFrame = i->Frame;
716  // Write chunk
717  Output.Write(Chunk, iPos);
718  iPos += Chunk.getSize();
719  }
720  Output.SetSize(iPos);
721  return Output;
722 }
const char * getData() const
Definition: StdBuf.h:450
Definition: StdBuf.h:37
void Grow(size_t iGrow)
Definition: StdBuf.h:179
size_t getSize() const
Definition: StdBuf.h:109
bool LogF(const char *strMessage,...)
Definition: C4Log.cpp:253
uint8_t iFrm
Definition: C4Record.h:99

Here is the call graph for this function:

Here is the caller graph for this function:

StdStrBuf C4Playback::ReWriteText ( )

Definition at line 656 of file C4Record.cpp.

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

Referenced by Open().

657 {
658  // Would work, too, but is currently too slow due to bad buffering inside StdCompilerINIWrite:
659  // return DecompileToBuf<StdCompilerINIWrite>(mkNamingAdapt(mkSTLContainerAdapt(chunks), "Rec"));
660  StdStrBuf Output;
661  for (chunks_t::const_iterator i = chunks.begin(); i != chunks.end(); i++)
662  {
663  Output.Append(static_cast<const StdStrBuf&>(DecompileToBuf<StdCompilerINIWrite>(mkNamingAdapt(mkDecompileAdapt(*i), "Rec"))));
664  Output.Append("\n\n");
665  }
666  return Output;
667 }
StdNamingAdapt< T > mkNamingAdapt(T &&rValue, const char *szName)
Definition: StdAdaptors.h:93
StdDecompileAdapt< T > mkDecompileAdapt(const T &rValue)
Definition: StdAdaptors.h:154
void Append(const char *pnData, size_t iChars)
Definition: StdBuf.h:527

Here is the call graph for this function:

Here is the caller graph for this function:

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

Definition at line 1118 of file C4Record.cpp.

References _MAX_PATH, 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().

1119 {
1120 
1121  // Load data
1122  StdBuf CompressedData;
1123  Log("Reading stream...");
1124  if (!CompressedData.LoadFromFile(szStream))
1125  return false;
1126 
1127  // Decompress
1128  unsigned long iStreamSize = CompressedData.getSize() * 5;
1129  StdBuf StreamData; StreamData.New(iStreamSize);
1130  while (true)
1131  {
1132 
1133  // Initialize stream
1134  z_stream strm;
1135  ZeroMem(&strm, sizeof strm);
1136  strm.next_in = getMBufPtr<BYTE>(CompressedData);
1137  strm.avail_in = CompressedData.getSize();
1138  strm.next_out = getMBufPtr<BYTE>(StreamData);
1139  strm.avail_out = StreamData.getSize();
1140 
1141  // Decompress
1142  if (inflateInit(&strm) != Z_OK)
1143  return false;
1144  int ret = inflate(&strm, Z_FINISH);
1145  if (ret == Z_OK)
1146  {
1147  inflateEnd(&strm);
1148  break;
1149  }
1150  if (ret != Z_BUF_ERROR)
1151  return false;
1152 
1153  // All input consumed?
1154  iStreamSize = strm.total_out;
1155  if (strm.avail_in == 0)
1156  {
1157  Log("Stream data incomplete, using as much data as possible");
1158  break;
1159  }
1160 
1161  // Larger buffer needed
1162  StreamData.Grow(CompressedData.getSize());
1163  iStreamSize = StreamData.getSize();
1164  }
1165  StreamData.SetSize(iStreamSize);
1166 
1167  // Parse
1168  C4Playback Playback;
1169  Playback.ReadBinary(StreamData);
1170  LogF("Got %lu chunks from stream", static_cast<unsigned long>(Playback.chunks.size()));
1171 
1172  // Get first chunk, which must contain the initial
1173  chunks_t::iterator chunkIter = Playback.chunks.begin();
1174  if (chunkIter == Playback.chunks.end() || chunkIter->Type != RCT_File)
1175  return false;
1176 
1177  // Get initial chunk, go over file name
1178  StdBuf InitialData = *chunkIter->pFileData;
1179 
1180  // Put to temporary file and unpack
1181  char szInitial[_MAX_PATH+1] = "~initial.tmp";
1182  MakeTempFilename(szInitial);
1183  if (!InitialData.SaveToFile(szInitial) ||
1184  !C4Group_UnpackDirectory(szInitial))
1185  return false;
1186 
1187  // Load Scenario.txt from Initial
1188  C4Group Grp; C4Scenario Initial;
1189  if (!Grp.Open(szInitial) ||
1190  !Initial.Load(Grp) ||
1191  !Grp.Close())
1192  return false;
1193 
1194  // Copy original scenario
1195  const char *szOrigin = Initial.Head.Origin.getData();
1196  char szRecord[_MAX_PATH + 1];
1197  SCopy(szStream, szRecord, _MAX_PATH);
1198  if (GetExtension(szRecord))
1199  *(GetExtension(szRecord) - 1) = 0;
1200  SAppend(".ocs", szRecord, _MAX_PATH);
1201  LogF("Original scenario is %s, creating %s.", szOrigin, szRecord);
1202  if (!C4Group_CopyItem(szOrigin, szRecord, false, false))
1203  return false;
1204 
1205  // Merge initial
1206  if (!Grp.Open(szRecord) ||
1207  !Grp.Merge(szInitial))
1208  return false;
1209 
1210  // Process other files in stream
1211  chunkIter->Delete();
1212  chunkIter = Playback.chunks.erase(chunkIter);
1213  while (chunkIter != Playback.chunks.end())
1214  if (chunkIter->Type == RCT_File)
1215  {
1216  LogF("Inserting %s...", chunkIter->Filename.getData());
1217  StdStrBuf Temp; Temp.Copy(chunkIter->Filename);
1218  MakeTempFilename(&Temp);
1219  if (!chunkIter->pFileData->SaveToFile(Temp.getData()))
1220  return false;
1221  if (!Grp.Move(Temp.getData(), chunkIter->Filename.getData()))
1222  return false;
1223  chunkIter = Playback.chunks.erase(chunkIter);
1224  }
1225  else
1226  chunkIter++;
1227 
1228  // Write record data
1229  StdBuf RecordData = Playback.ReWriteBinary();
1230  if (!Grp.Add(C4CFN_CtrlRec, RecordData, false, true))
1231  return false;
1232 
1233  // Done
1234  Log("Writing record file...");
1235  Grp.Close();
1236  pRecordFile->Copy(szRecord);
1237  return true;
1238 }
const char * getData() const
Definition: StdBuf.h:450
StdCopyStrBuf Origin
Definition: C4Scenario.h:80
Definition: StdBuf.h:37
void SCopy(const char *szSource, char *sTarget, size_t iMaxL)
Definition: Standard.cpp:122
void SAppend(const char *szSource, char *szTarget, int iMaxL)
Definition: Standard.cpp:227
bool Merge(const char *szFolders)
Definition: C4Group.cpp:1229
bool LoadFromFile(const char *szFile)
Definition: StdBuf.cpp:39
StdBuf ReWriteBinary()
Definition: C4Record.cpp:669
bool C4Group_UnpackDirectory(const char *szFilename)
Definition: C4Group.cpp:306
#define _MAX_PATH
void Grow(size_t iGrow)
Definition: StdBuf.h:179
size_t getSize() const
Definition: StdBuf.h:109
void SetSize(size_t inSize)
Definition: StdBuf.h:212
bool Move(const char *szFile, const char *szAddAs)
Definition: C4Group.cpp:1325
bool Open(const char *szGroupName, bool fCreate=false)
Definition: C4Group.cpp:514
bool Load(C4Group &hGroup, bool fLoadSection=false, bool suppress_errors=false)
Definition: C4Scenario.cpp:92
bool C4Group_CopyItem(const char *szSource, const char *szTarget1, bool fNoSort, bool fResetAttributes)
Definition: C4Group.cpp:100
bool Close()
Definition: C4Group.cpp:755
char * GetExtension(char *szFilename)
Definition: StdFile.cpp:131
void New(size_t inSize)
Definition: StdBuf.h:154
bool SaveToFile(const char *szFile) const
Definition: StdBuf.cpp:60
bool Log(const char *szMessage)
Definition: C4Log.cpp:195
bool Add(const char *szFile, const char *szAddAs)
Definition: C4Group.cpp:1316
#define C4CFN_CtrlRec
Definition: C4Components.h:77
std::enable_if< std::is_pod< T >::value >::type ZeroMem(T *lpMem, size_t dwSize)
Definition: Standard.h:63
C4SHead Head
Definition: C4Scenario.h:230
void MakeTempFilename(char *szFilename)
Definition: StdFile.cpp:333
bool ReadBinary(const StdBuf &Buf)
Definition: C4Record.cpp:515
bool LogF(const char *strMessage,...)
Definition: C4Log.cpp:253
void Copy()
Definition: StdBuf.h:475

Here is the call graph for this function:

Here is the caller graph for this function:

void C4Playback::Strip ( )

Definition at line 724 of file C4Record.cpp.

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().

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

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: