1/*
2 * LegacyClonk
3 *
4 * Copyright (c) RedWolf Design
5 * Copyright (c) 2001, Sven2
6 * Copyright (c) 2017-2021, The LegacyClonk Team and contributors
7 *
8 * Distributed under the terms of the ISC license; see accompanying file
9 * "COPYING" for details.
10 *
11 * "Clonk" is a registered trademark of Matthes Bender, used with permission.
12 * See accompanying file "TRADEMARK" for details.
13 *
14 * To redistribute this file separately, substitute the full license texts
15 * for the above references.
16 */
17
18// scenario record functionality
19
20#pragma once
21
22class C4Record;
23
24#include "C4Group.h"
25#include "C4Control.h"
26#include "C4Log.h"
27#include "CStdFile.h"
28#include "Fixed.h"
29
30#include <list>
31
32#ifdef DEBUGREC
33extern int DoNoDebugRec; // debugrec disable counter in C4Record.cpp
34
35#define DEBUGREC_OFF ++DoNoDebugRec;
36#define DEBUGREC_ON --DoNoDebugRec;
37
38#else
39#define DEBUGREC_OFF
40#define DEBUGREC_ON
41#endif
42
43// turn off debugrecs in current block
44class C4DebugRecOff
45{
46 bool fDoOff;
47
48public:
49 C4DebugRecOff();
50 C4DebugRecOff(bool fDoOff);
51 ~C4DebugRecOff();
52
53 void Clear();
54};
55
56enum C4RecordChunkType // record file chunk type
57{
58 RCT_Ctrl = 0x00, // control
59 RCT_CtrlPkt = 0x01, // control packet
60 RCT_Frame = 0x02, // beginning frame
61 RCT_End = 0x10, // --- the end ---
62 RCT_Log = 0x20, // log message
63 // Streaming
64 RCT_File = 0x30, // file data
65 // DEBUGREC
66 RCT_DbgFrame = 0x81,
67 RCT_Block = 0x82, // point in Game::Execute
68 RCT_SetPix = 0x83, // set landscape pixel
69 RCT_ExecObj = 0x84, // exec object
70 RCT_Random = 0x85, // Random()-call
71 RCT_Rn3 = 0x86, // Rn3()-call
72 RCT_MMC = 0x87, // create MassMover
73 RCT_MMD = 0x88, // destroy MassMover
74 RCT_CrObj = 0x89, // create object
75 RCT_DsObj = 0x8A, // remove object
76 RCT_GetPix = 0x8B, // get landscape pixel; let the Gigas flow!
77 RCT_RotVtx1 = 0x8C, // before shape is rotated
78 RCT_RotVtx2 = 0x8D, // after shape is rotated
79 RCT_ExecPXS = 0x8E, // execute pxs system
80 RCT_Sin = 0x8F, // sin by Shape-Rotation
81 RCT_Cos = 0x90, // cos by Shape-Rotation
82 RCT_Map = 0x91, // map dump
83 RCT_Ls = 0x92, // complete landscape dump!
84 RCT_MCT1 = 0x93, // MapCreatorS2: before transformation
85 RCT_MCT2 = 0x94, // MapCreatorS2: after transformation
86 RCT_AulFunc = 0x9A, // script function call
87 RCT_ObjCom = 0x9B, // object com
88 RCT_PlrCom = 0x9C, // player com
89 RCT_PlrInCom = 0x9D, // player InCom
90 RCT_MatScan = 0x9E, // landscape scan execute
91 RCT_MatScanDo = 0x9F, // landscape scan mat change
92 RCT_Area = 0xA0, // object area change
93 RCT_MenuAdd = 0xA1, // add menu item
94 RCT_MenuAddC = 0xA2, // add menu item: Following commands
95 RCT_OCF = 0xA3, // OCF setting of updating
96 RCT_DirectExec = 0xA4, // a DirectExec-script
97
98 RCT_Custom = 0xc0, // varies
99
100 RCT_Undefined = 0xff,
101};
102
103#ifdef DEBUGREC
104void AddDbgRec(C4RecordChunkType eType, const void *pData = nullptr, int iSize = 0); // record debug stuff
105#endif
106
107#pragma pack(1)
108
109struct C4RecordChunkHead // record file chunk head
110{
111 uint8_t iFrm; // frame
112 uint8_t Type; // chunk type
113};
114
115struct C4RecordChunk
116{
117 int32_t Frame;
118 uint8_t Type;
119 union
120 {
121 C4Control *pCtrl;
122 C4IDPacket *pPkt;
123 class C4PktDebugRec *pDbg;
124 class StdBuf *pFileData;
125 };
126 StdStrBuf Filename; // RCT_File only
127
128public:
129 C4RecordChunk();
130 void Delete();
131 virtual void CompileFunc(StdCompiler *pComp);
132 virtual ~C4RecordChunk() {}
133};
134
135struct C4RCSetPix
136{
137 int x, y; // pos
138 uint8_t clr; // new color
139};
140
141struct C4RCExecObj
142{
143 int Number; // object number
144 int id;
145 C4Fixed fx, fy, fr;
146};
147
148struct C4RCMassMover
149{
150 int x, y; // pos
151};
152
153struct C4RCRandom
154{
155 int Cnt; // index in seed
156 int Range; // random range query
157 int Val; // random value
158};
159
160struct C4RCCreateObj
161{
162 int oei;
163 int id;
164 int x, y, ownr;
165};
166
167struct C4RCRotVtx
168{
169 // shape size
170 int x, y, wdt, hgt, r;
171 // vertices
172 int VtxX[4], VtxY[4];
173};
174
175struct C4RCExecPXS
176{
177 // pos
178 C4Fixed x, y;
179 // mat
180 int32_t iMat;
181 // execution pos
182 int32_t pos;
183};
184
185struct C4RCTrf
186{
187 int x, y, Turbulence, Rotate;
188};
189
190struct C4RCPos
191{
192 int x, y;
193};
194
195struct C4RCObjectCom
196{
197 uint8_t com;
198 int32_t data;
199 int32_t o;
200};
201
202struct C4RCMatScan
203{
204 int32_t cx, cy, mat, conv_to, dir, mconvs;
205};
206
207struct C4RCArea
208{
209 char op;
210 int32_t obj;
211 int32_t x1, y1, xL, yL, dpitch;
212 bool out;
213};
214
215struct C4RCMenuAdd
216{
217 int32_t iObjNum;
218 int32_t iCount;
219 C4ID idID : 32;
220 bool fOwnValue;
221 int32_t iValue;
222 bool fIsSelectable;
223};
224
225struct C4RCOCF
226{
227 uint32_t dwOCFOld;
228 uint32_t dwOCFNew;
229 bool fUpdate;
230};
231
232#pragma pack()
233
234// debug record packet
235class C4PktDebugRec : public C4PacketBase
236{
237protected:
238 C4RecordChunkType eType;
239 StdBuf Data;
240
241public:
242 C4PktDebugRec() : eType(RCT_Undefined) {}
243 C4PktDebugRec(const C4PktDebugRec &rCopy) : Data(rCopy.Data), eType(rCopy.eType) {}
244 C4PktDebugRec(C4RecordChunkType eType, const StdBuf &rCpyData)
245 : Data(rCpyData), eType(eType) {}
246
247 C4RecordChunkType getType() const { return eType; }
248 size_t getSize() const { return Data.getSize(); }
249 const void *getData() const { return Data.getData(); }
250
251 virtual void CompileFunc(StdCompiler *pComp) override;
252};
253
254class C4Record // demo recording
255{
256private:
257 CStdFile CtrlRec; // control file handle
258 StdStrBuf sFilename; // recorded scenario file name
259 C4Group RecordGrp; // record scenario group
260 bool fRecording; // set if recording is active
261 uint32_t iLastFrame; // frame of last chunk written
262 bool fStreaming; // perdiodically sent new control to server
263 unsigned int iStreamingPos; // Position of current buffer in stream
264 StdBuf StreamingData; // accumulated control data since last stream sync
265
266public:
267 C4Record(); // creates control file etc
268 ~C4Record(); // close file; create demo scen
269 int Index;
270
271 unsigned int GetStreamingPos() const { return iStreamingPos; }
272 const StdBuf &GetStreamingBuf() const { return StreamingData; }
273
274 bool Start(bool fInitial);
275 bool Stop(StdStrBuf *pRecordName = nullptr, uint8_t *pRecordSHA1 = nullptr);
276
277 bool Rec(const C4Control &Ctrl, int iFrame); // record control
278 bool Rec(C4PacketType eCtrlType, C4ControlPacket *pCtrl, int iFrame); // record control packet
279 bool Rec(uint32_t iFrame, const StdBuf &sBuf, C4RecordChunkType eType);
280
281 bool AddFile(const char *szLocalFilename, const char *szAddAs, bool fDelete = false);
282
283 bool StartStreaming(bool fInitial);
284 void ClearStreamingBuf(unsigned int iAmount);
285 void StopStreaming();
286
287private:
288 void Stream(const C4RecordChunkHead &Head, const StdBuf &sBuf);
289 bool StreamFile(const char *szFilename, const char *szAddAs);
290};
291
292class C4Playback // demo playback
293{
294private:
295 std::shared_ptr<spdlog::logger> logger;
296
297 typedef std::list<C4RecordChunk> chunks_t;
298 chunks_t chunks;
299 chunks_t::iterator currChunk;
300 bool Finished; // if set, free playback in next frame
301 CStdFile playbackFile; // if open, try reading additional chunks from this file
302 bool fLoadSequential; // used for debugrecs: Sequential reading of files
303 StdBuf sequentialBuffer; // buffer to manage sequential reads
304 uint32_t iLastSequentialFrame; // frame number of last chunk read
305 void Finish(); // end playback
306#ifdef DEBUGREC
307 std::shared_ptr<spdlog::logger> loggerDebugRec;
308 C4PacketList DebugRec;
309#endif
310
311public:
312 C4Playback(std::shared_ptr<spdlog::logger> logger);
313 ~C4Playback();
314
315 bool Open(C4Group &rGrp);
316 bool ReadBinary(const StdBuf &Buf);
317 bool ReadText(const StdStrBuf &Buf);
318 void NextChunk(); // point to next prepared chunk in mem or read it
319 bool NextSequentialChunk(); // read from seq file until a new chunk has been filled
320 std::string ReWriteText();
321 StdBuf ReWriteBinary();
322 void Strip();
323 bool ExecuteControl(C4Control *pCtrl, int iFrame); // assign control
324 void Clear();
325#ifdef DEBUGREC
326 void Check(C4RecordChunkType eType, const uint8_t *pData, int iSize); // compare with debugrec
327 void DebugRecError(std::string_view error);
328#endif
329 static bool StreamToRecord(const char *szStream, StdStrBuf *pRecord);
330};
331
332C4LOGGERCONFIG_NAME_TYPE(C4Playback);
333