76 case RCT_Ctrl:
delete pCtrl; pCtrl =
nullptr;
break;
77 case RCT_CtrlPkt:
delete pPkt; pPkt =
nullptr;
break;
80 case RCT_File:
delete pFileData;
break;
81 default:
delete pDbg; pDbg =
nullptr;
break;
107 if (fRecording)
return false;
118 char *pScenNameEnd = sScenName +
SLen(sScenName);
119 while (Inside<char>(*--pScenNameEnd,
'0',
'9'))
120 if (pScenNameEnd == sScenName)
140 if (!saveRec.
Save(sFilename.
getData()))
return false;
151 if (!CtrlRec.
Create(szCtrlRecFilename))
return false;
156 if (!LogRec.
Create(szLogRecFilename))
return false;
172 if (!fRecording)
return false;
190 CtrlRec.
Write(&Head,
sizeof(Head));
201 pRecordName->
Copy(sFilename);
213 if (!fRecording)
return false;
221 return Rec(iFrame, DecompileToBuf<StdCompilerBinWrite>(Cpy),
RCT_Ctrl);
226 if (!fRecording)
return false;
233 return Rec(iFrame, DecompileToBuf<StdCompilerBinWrite>(Pkt),
RCT_CtrlPkt);
239 while (iFrame >
int(iLastFrame + 0xff))
242 uint8_t iFrameDiff = std::max<uint8_t>(0, iFrame - iLastFrame);
243 iLastFrame += iFrameDiff;
247 CtrlRec.
Write(&Head,
sizeof(Head));
261 if (!fStreaming)
return;
262 StreamingData.
Append(&Head,
sizeof(Head));
268 if (!fRecording)
return false;
288 if (!StreamFile(szFile.
getData(), szAddAs))
292 if (szFile != szLocalFilename)
299 if (!RecordGrp.
Move(szLocalFilename, szAddAs))
304 if (!RecordGrp.
Add(szLocalFilename, szAddAs))
313 if (!fRecording)
return false;
314 if (fStreaming)
return false;
322 if (!saveRec.
Save(sTempFilename.
getData()))
return false;
341 iStreamingPos += iAmount;
342 if (iAmount == StreamingData.
getSize())
343 StreamingData.
Clear();
346 StreamingData.
Move(iAmount, StreamingData.
getSize() - iAmount);
356 bool C4Record::StreamFile(
const char *szLocalFilename,
const char *szAddAs)
365 StdBuf Packed = DecompileToBuf<StdCompilerBinWrite>(
370 Stream(Head, Packed);
386 iLastSequentialFrame = 0;
409 currChunk = chunks.end();
413 LogFatal(
"Record: Binary read error.");
460 LogFatal(
"Record: No control data found!");
476 currChunk = chunks.begin();
507 const StdBuf *pUseBuf; uint32_t iFrame = 0;
510 sequentialBuffer.
Append(Buf);
511 pUseBuf = &sequentialBuffer;
512 iFrame = iLastSequentialFrame;
517 size_t iPos = 0;
bool fFinished =
false;
555 if (pHead->
Type >= 0x80)
584 chunks.push_back(c); c.pPkt =
nullptr;
591 if (iPos >= sequentialBuffer.
getSize())
592 sequentialBuffer.
Clear();
595 sequentialBuffer.
Move(iPos, sequentialBuffer.
getSize() - iPos);
596 sequentialBuffer.
Shrink(iPos);
599 iLastSequentialFrame = iFrame;
611 assert(currChunk != chunks.end());
613 if (currChunk != chunks.end())
return;
615 if (!fLoadSequential)
return;
617 for (
auto & chunk : chunks) chunk.Delete();
618 chunks.clear(); currChunk = chunks.end();
624 StdBuf BinaryBuf;
size_t iRealSize;
630 playbackFile.
Read(BinaryBuf.
getMData(), 4096, &iRealSize);
631 if (!iRealSize)
return false;
637 currChunk = chunks.begin();
650 for (chunks_t::const_iterator i = chunks.begin(); i != chunks.end(); i++)
660 const int OUTPUT_GROW = 16 * 1024;
661 StdBuf Output;
int iPos = 0;
662 bool fFinished =
false;
664 for (chunks_t::const_iterator i = chunks.begin(); !fFinished && i != chunks.end(); i++)
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!");
676 Chunk = DecompileToBuf<StdCompilerBinWrite>(*i->pCtrl);
679 Chunk = DecompileToBuf<StdCompilerBinWrite>(*i->pPkt);
686 Chunk = DecompileToBuf<StdCompilerBinWrite>(*i->pDbg);
698 Output.
Grow(OUTPUT_GROW);
701 pHead->
Type = i->Type;
702 pHead->
iFrm = i->Frame - iFrame;
706 Output.
Write(Chunk, iPos);
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;
723 for (chunks_t::iterator i = chunks.begin(); i != chunks.end(); )
726 if (iEndFrame >= 0 && i->Frame > iEndFrame)
729 while (i != chunks.end())
736 EndChunk.
Frame = iEndFrame;
738 chunks.push_back(EndChunk);
751 switch (pPkt->getPktType())
771 if (fStripSyncChecks)
773 i->pCtrl->Remove(pPkt);
793 bool fStripThis=
false;
794 switch (i->pPkt->getPktType())
806 if (fStripSyncChecks) fStripThis =
true;
809 if (fStripMessages) fStripThis=
true;
843 if (currChunk == chunks.end())
return false;
844 if (Finished) { Finish();
return false; }
852 while (currChunk != chunks.end() && currChunk->Frame <= iFrame)
854 switch (currChunk->Type)
857 pCtrl->
Append(*currChunk->pCtrl);
881 currChunk->pDbg =
nullptr;
892 void C4Playback::Finish()
912 for (
auto & chunk : chunks) chunk.Delete();
913 chunks.clear(); currChunk = chunks.end();
914 playbackFile.
Close();
915 sequentialBuffer.
Clear();
916 fLoadSequential =
false;
985 for (
unsigned int i=0; i<RawData.
getSize(); ++i)
986 r.
AppendFormat(
"%02x ", (uint32_t) *getBufPtr<uint8_t>(RawData, i));
999 bool fHasPacketFromHead =
false;
1006 int32_t iSize32 =
iSize;
1013 int32_t iSize32 = 0;
1039 while (currChunk != chunks.end() && currChunk->Type ==
RCT_CtrlPkt)
1043 assert(!pCtrlPck->
Sync());
1048 if (currChunk == chunks.end() || currChunk->Type ==
RCT_End || Finished)
1050 Log(
"DebugRec end: All in sync!");
1055 if (currChunk->Type != eType)
1060 if (currChunk->pDbg)
1061 PktInReplay = *currChunk->pDbg;
1063 fHasPacketFromHead =
true;
1069 Log(
"DebugRec end: All in sync (2)!");
1074 if (PktInReplay.
getType() != eType)
1088 sErr.
Append(
" Replay: ");
1097 if (fHasPacketFromHead)
1103 LogF(
"Playback error: %s", szError);
1112 Log(
"Reading stream...");
1117 unsigned long iStreamSize = CompressedData.
getSize() * 5;
1118 StdBuf StreamData; StreamData.
New(iStreamSize);
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();
1131 if (inflateInit(&strm) != Z_OK)
1133 int ret = inflate(&strm, Z_FINISH);
1139 if (ret != Z_BUF_ERROR)
1143 iStreamSize = strm.total_out;
1144 if (strm.avail_in == 0)
1146 Log(
"Stream data incomplete, using as much data as possible");
1152 iStreamSize = StreamData.
getSize();
1154 StreamData.
SetSize(iStreamSize);
1159 LogF(
"Got %lu chunks from stream",
static_cast<unsigned long>(Playback.chunks.size()));
1162 chunks_t::iterator chunkIter = Playback.chunks.begin();
1163 if (chunkIter == Playback.chunks.end() || chunkIter->Type !=
RCT_File)
1167 StdBuf InitialData = *chunkIter->pFileData;
1178 if (!Grp.
Open(szInitial) ||
1179 !Initial.
Load(Grp) ||
1190 LogF(
"Original scenario is %s, creating %s.", szOrigin, szRecord);
1195 if (!Grp.
Open(szRecord) ||
1196 !Grp.
Merge(szInitial))
1200 chunkIter->Delete();
1201 chunkIter = Playback.chunks.erase(chunkIter);
1202 while (chunkIter != Playback.chunks.end())
1205 LogF(
"Inserting %s...", chunkIter->Filename.getData());
1208 if (!chunkIter->pFileData->SaveToFile(Temp.
getData()))
1210 if (!Grp.
Move(Temp.
getData(), chunkIter->Filename.getData()))
1212 chunkIter = Playback.chunks.erase(chunkIter);
1223 Log(
"Writing record file...");
1225 pRecordFile->
Copy(szRecord);
#define C4CFN_RecPlayerInfos
#define C4CFN_ScenarioFiles
#define C4CFN_CtrlRecText
bool C4Group_PackDirectory(const char *filename)
bool C4Group_CopyItem(const char *source, const char *target, bool no_sorting, bool reset_attributes)
bool C4Group_UnpackDirectory(const char *filename)
constexpr int DEBUGREC_START_FRAME
const char * LoadResStr(const char *id)
bool Log(const char *szMessage)
bool LogF(const char *strMessage,...)
bool LogFatal(const char *szMessage)
const char * GetRecordChunkTypeName(C4RecordChunkType eType)
void AddDbgRec(C4RecordChunkType eType, const void *pData, int iSize)
StdStrBuf GetDbgRecPktData(C4RecordChunkType eType, const StdBuf &RawData)
bool GetFileSHA1(const char *szFilename, BYTE *pSHA1)
void SCopy(const char *szSource, char *sTarget, size_t iMaxL)
bool SEqualNoCase(const char *szStr1, const char *szStr2, int iLen)
void SAppend(const char *szSource, char *szTarget, int iMaxL)
std::enable_if< std::is_pod< T >::value >::type ZeroMem(T *lpMem, size_t dwSize)
size_t SLen(const char *sptr)
StdInsertAdapt< T, I > mkInsertAdapt(T &&rObj, I &&rIns, bool fBefore=true)
StdSTLContainerAdapt< C > mkSTLContainerAdapt(C &rTarget, StdCompiler::Sep eSep=StdCompiler::SEP_SEP)
StdDecompileAdapt< T > mkDecompileAdapt(const T &rValue)
StdPtrAdapt< T > mkPtrAdaptNoNull(T *&rpObj)
StdIntAdapt< T > mkIntAdapt(T &rValue)
StdNamingAdapt< T > mkNamingAdapt(T &&rValue, const char *szName)
StdStrBuf FormatString(const char *szFmt,...)
bool EraseItem(const char *szItemName)
bool DirectoryExists(const char *szFilename)
char * GetExtension(char *szFilename)
bool WildcardMatch(const char *szWildcard, const char *szString)
const char * GetFilenameOnly(const char *strFilename)
void MakeTempFilename(char *szFilename)
bool CopyItem(const char *szSource, const char *szTarget, bool fResetAttributes)
bool EraseFile(const char *szFileName)
char DebugRecExternalFile[_MAX_PATH_LEN]
bool CreateSaveFolder(const char *directory, const char *language_title)
const char * AtUserDataPath(const char *filename)
bool UpdateHaltCtrls(bool fHalt)
void PreRec(C4Record *pRecord) const
C4IDPacket * firstPkt() const
void Append(const C4Control &Ctrl)
void Add(C4PacketType eType, C4ControlPacket *pCtrl)
void Copy(const C4Control &Ctrl)
C4IDPacket * nextPkt(C4IDPacket *pPkt) const
virtual void PreRec(C4Record *pRecord)
virtual bool Sync() const
void ExecControlPacket(C4PacketType eCtrlType, class C4ControlPacket *pPkt)
void DbgRec(C4RecordChunkType eType, const uint8_t *pData=nullptr, size_t iSize=0)
C4PlayerInfoList & PlayerInfos
C4GameParameters & Parameters
const char * getFile() const
bool SaveDesc(C4Group &hToGroup)
bool Save(const char *szFilename)
bool Merge(const char *folders)
StdStrBuf GetFullName() const
bool Add(const char *filename, const char *entry_name)
bool LoadEntry(const char *entry_name, char **buffer, size_t *size_info=nullptr, int zeros_to_append=0)
bool LoadEntryString(const char *entry_name, StdStrBuf *buffer)
bool Move(const char *filename, const char *entry_name)
bool FindEntry(const char *wildcard, StdStrBuf *filename=nullptr, size_t *size=nullptr)
bool Open(const char *group_name, bool do_create=false)
C4PacketType getPktType() const
C4PacketBase * getPkt() const
C4IDPacket * firstPkt() const
void Delete(C4IDPacket *pPkt)
void Add(C4IDPacket *pPkt)
const void * getData() const
void CompileFunc(StdCompiler *pComp) override
void CompileFunc(StdCompiler *pComp) override
C4RecordChunkType getType() const
void Check(C4RecordChunkType eType, const uint8_t *pData, int iSize)
bool ExecuteControl(C4Control *pCtrl, int iFrame)
bool ReadBinary(const StdBuf &Buf)
static bool StreamToRecord(const char *szStream, StdStrBuf *pRecord)
bool ReadText(const StdStrBuf &Buf)
void DebugRecError(const char *szError)
bool NextSequentialChunk()
static bool Strip(const char *szFilename, bool fAggressive)
bool Save(C4Group &hGroup, const char *szToFile)
bool StartStreaming(bool fInitial)
bool AddFile(const char *szLocalFilename, const char *szAddAs, bool fDelete=false)
bool Stop(StdStrBuf *pRecordName=nullptr, BYTE *pRecordSHA1=nullptr)
bool Rec(const C4Control &Ctrl, int iFrame)
void ClearStreamingBuf(unsigned int iAmount)
bool Start(bool fInitial)
bool Load(C4Group &hGroup, bool fLoadSection=false, bool suppress_errors=false)
bool Close(StdBuf **ppMemory=nullptr)
bool Create(const char *szFileName, bool fCompressed=false, bool fExecutable=false, bool fMemory=false)
bool Write(const void *pBuffer, int iSize)
bool Read(void *pBuffer, size_t iSize) override
bool Open(const char *szFileName, bool fCompressed=false)
void SetSize(size_t inSize)
bool SaveToFile(const char *szFile) const
void Shrink(size_t iShrink)
void Write(const void *pnData, size_t inSize, size_t iAt=0)
const void * getData() const
bool LoadFromFile(const char *szFile)
void Move(size_t iFrom, size_t inSize, size_t iTo=0)
void Append(const void *pnData, size_t inSize)
StdBuf getPart(size_t iStart, size_t inSize) const
StdStrBuf getPosition() const override
void Value(const T &rStruct)
void AppendFormat(const char *szFmt,...) GNUC_FORMAT_ATTRIBUTE_O
void Ref(const char *pnData)
const char * getData() const
void Append(const char *pnData, size_t iChars)
bool SaveToFile(const char *szFile) const
void Format(const char *szFmt,...) GNUC_FORMAT_ATTRIBUTE_O
virtual void CompileFunc(StdCompiler *pComp)