| 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 | |
| 22 | class 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 |
| 33 | extern 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 |
| 44 | class C4DebugRecOff |
| 45 | { |
| 46 | bool fDoOff; |
| 47 | |
| 48 | public: |
| 49 | C4DebugRecOff(); |
| 50 | C4DebugRecOff(bool fDoOff); |
| 51 | ~C4DebugRecOff(); |
| 52 | |
| 53 | void Clear(); |
| 54 | }; |
| 55 | |
| 56 | enum 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 | = 0xA1, // add menu item |
| 94 | = 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 |
| 104 | void AddDbgRec(C4RecordChunkType eType, const void *pData = nullptr, int iSize = 0); // record debug stuff |
| 105 | #endif |
| 106 | |
| 107 | #pragma pack(1) |
| 108 | |
| 109 | struct C4RecordChunkHead // record file chunk head |
| 110 | { |
| 111 | uint8_t iFrm; // frame |
| 112 | uint8_t Type; // chunk type |
| 113 | }; |
| 114 | |
| 115 | struct 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 | |
| 128 | public: |
| 129 | C4RecordChunk(); |
| 130 | void Delete(); |
| 131 | virtual void CompileFunc(StdCompiler *pComp); |
| 132 | virtual ~C4RecordChunk() {} |
| 133 | }; |
| 134 | |
| 135 | struct C4RCSetPix |
| 136 | { |
| 137 | int x, y; // pos |
| 138 | uint8_t clr; // new color |
| 139 | }; |
| 140 | |
| 141 | struct C4RCExecObj |
| 142 | { |
| 143 | int Number; // object number |
| 144 | int id; |
| 145 | C4Fixed fx, fy, fr; |
| 146 | }; |
| 147 | |
| 148 | struct C4RCMassMover |
| 149 | { |
| 150 | int x, y; // pos |
| 151 | }; |
| 152 | |
| 153 | struct C4RCRandom |
| 154 | { |
| 155 | int Cnt; // index in seed |
| 156 | int Range; // random range query |
| 157 | int Val; // random value |
| 158 | }; |
| 159 | |
| 160 | struct C4RCCreateObj |
| 161 | { |
| 162 | int oei; |
| 163 | int id; |
| 164 | int x, y, ownr; |
| 165 | }; |
| 166 | |
| 167 | struct C4RCRotVtx |
| 168 | { |
| 169 | // shape size |
| 170 | int x, y, wdt, hgt, r; |
| 171 | // vertices |
| 172 | int VtxX[4], VtxY[4]; |
| 173 | }; |
| 174 | |
| 175 | struct C4RCExecPXS |
| 176 | { |
| 177 | // pos |
| 178 | C4Fixed x, y; |
| 179 | // mat |
| 180 | int32_t iMat; |
| 181 | // execution pos |
| 182 | int32_t pos; |
| 183 | }; |
| 184 | |
| 185 | struct C4RCTrf |
| 186 | { |
| 187 | int x, y, Turbulence, Rotate; |
| 188 | }; |
| 189 | |
| 190 | struct C4RCPos |
| 191 | { |
| 192 | int x, y; |
| 193 | }; |
| 194 | |
| 195 | struct C4RCObjectCom |
| 196 | { |
| 197 | uint8_t com; |
| 198 | int32_t data; |
| 199 | int32_t o; |
| 200 | }; |
| 201 | |
| 202 | struct C4RCMatScan |
| 203 | { |
| 204 | int32_t cx, cy, mat, conv_to, dir, mconvs; |
| 205 | }; |
| 206 | |
| 207 | struct C4RCArea |
| 208 | { |
| 209 | char op; |
| 210 | int32_t obj; |
| 211 | int32_t x1, y1, xL, yL, dpitch; |
| 212 | bool out; |
| 213 | }; |
| 214 | |
| 215 | struct |
| 216 | { |
| 217 | int32_t ; |
| 218 | int32_t ; |
| 219 | C4ID : 32; |
| 220 | bool ; |
| 221 | int32_t ; |
| 222 | bool ; |
| 223 | }; |
| 224 | |
| 225 | struct C4RCOCF |
| 226 | { |
| 227 | uint32_t dwOCFOld; |
| 228 | uint32_t dwOCFNew; |
| 229 | bool fUpdate; |
| 230 | }; |
| 231 | |
| 232 | #pragma pack() |
| 233 | |
| 234 | // debug record packet |
| 235 | class C4PktDebugRec : public C4PacketBase |
| 236 | { |
| 237 | protected: |
| 238 | C4RecordChunkType eType; |
| 239 | StdBuf Data; |
| 240 | |
| 241 | public: |
| 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 | |
| 254 | class C4Record // demo recording |
| 255 | { |
| 256 | private: |
| 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 | |
| 266 | public: |
| 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 | |
| 287 | private: |
| 288 | void Stream(const C4RecordChunkHead &Head, const StdBuf &sBuf); |
| 289 | bool StreamFile(const char *szFilename, const char *szAddAs); |
| 290 | }; |
| 291 | |
| 292 | class C4Playback // demo playback |
| 293 | { |
| 294 | private: |
| 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 | |
| 311 | public: |
| 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 | |
| 332 | C4LOGGERCONFIG_NAME_TYPE(C4Playback); |
| 333 | |