| 1 | #ifndef ENGINE_CLIENT_GHOST_H |
| 2 | #define ENGINE_CLIENT_GHOST_H |
| 3 | |
| 4 | #include <engine/ghost.h> |
| 5 | |
| 6 | #include <cstdint> |
| 7 | |
| 8 | enum |
| 9 | { |
| 10 | MAX_ITEM_SIZE = 128, |
| 11 | NUM_ITEMS_PER_CHUNK = 50, |
| 12 | MAX_CHUNK_SIZE = MAX_ITEM_SIZE * NUM_ITEMS_PER_CHUNK, |
| 13 | }; |
| 14 | static_assert(MAX_CHUNK_SIZE % sizeof(uint32_t) == 0, "Chunk size must be aligned with uint32_t" ); |
| 15 | |
| 16 | // version 4-6 |
| 17 | struct |
| 18 | { |
| 19 | unsigned char [8]; |
| 20 | unsigned char ; |
| 21 | char [MAX_NAME_LENGTH]; |
| 22 | char [64]; |
| 23 | unsigned char [sizeof(int32_t)]; // Crc before version 6 |
| 24 | unsigned char [sizeof(int32_t)]; |
| 25 | unsigned char [sizeof(int32_t)]; |
| 26 | SHA256_DIGEST ; |
| 27 | |
| 28 | int () const; |
| 29 | int () const; |
| 30 | CGhostInfo () const; |
| 31 | }; |
| 32 | |
| 33 | class CGhostItem |
| 34 | { |
| 35 | public: |
| 36 | alignas(uint32_t) unsigned char m_aData[MAX_ITEM_SIZE]; |
| 37 | int m_Type; |
| 38 | |
| 39 | CGhostItem() : |
| 40 | m_Type(-1) {} |
| 41 | CGhostItem(int Type) : |
| 42 | m_Type(Type) {} |
| 43 | void Reset() { m_Type = -1; } |
| 44 | }; |
| 45 | |
| 46 | class CGhostRecorder : public IGhostRecorder |
| 47 | { |
| 48 | IOHANDLE m_File; |
| 49 | char m_aFilename[IO_MAX_PATH_LENGTH]; |
| 50 | class IStorage *m_pStorage; |
| 51 | |
| 52 | alignas(uint32_t) char m_aBuffer[MAX_CHUNK_SIZE]; |
| 53 | alignas(uint32_t) char m_aBufferTemp[MAX_CHUNK_SIZE]; |
| 54 | char *m_pBufferPos; |
| 55 | const char *m_pBufferEnd; |
| 56 | int m_BufferNumItems; |
| 57 | CGhostItem m_LastItem; |
| 58 | |
| 59 | void ResetBuffer(); |
| 60 | void FlushChunk(); |
| 61 | |
| 62 | public: |
| 63 | CGhostRecorder(); |
| 64 | |
| 65 | void Init(); |
| 66 | |
| 67 | int Start(const char *pFilename, const char *pMap, const SHA256_DIGEST &MapSha256, const char *pName) override; |
| 68 | void Stop(int Ticks, int Time) override; |
| 69 | |
| 70 | void WriteData(int Type, const void *pData, size_t Size) override; |
| 71 | bool IsRecording() const override { return m_File != nullptr; } |
| 72 | }; |
| 73 | |
| 74 | class CGhostLoader : public IGhostLoader |
| 75 | { |
| 76 | IOHANDLE m_File; |
| 77 | char m_aFilename[IO_MAX_PATH_LENGTH]; |
| 78 | class IStorage *m_pStorage; |
| 79 | |
| 80 | CGhostHeader ; |
| 81 | CGhostInfo m_Info; |
| 82 | |
| 83 | alignas(uint32_t) char m_aBuffer[MAX_CHUNK_SIZE]; |
| 84 | alignas(uint32_t) char m_aBufferTemp[MAX_CHUNK_SIZE]; |
| 85 | char *m_pBufferPos; |
| 86 | const char *m_pBufferEnd; |
| 87 | int m_BufferNumItems; |
| 88 | int m_BufferCurItem; |
| 89 | int m_BufferPrevItem; |
| 90 | CGhostItem m_LastItem; |
| 91 | |
| 92 | void ResetBuffer(); |
| 93 | IOHANDLE (CGhostHeader &, const char *pFilename, const char *pMap, const SHA256_DIGEST &MapSha256, unsigned MapCrc, bool LogMapMismatch) const; |
| 94 | bool (const CGhostHeader &, const char *pFilename) const; |
| 95 | bool (const CGhostHeader &, const char *pFilename, const char *pMap, const SHA256_DIGEST &MapSha256, unsigned MapCrc, bool LogMapMismatch) const; |
| 96 | bool ReadChunk(int *pType); |
| 97 | |
| 98 | public: |
| 99 | CGhostLoader(); |
| 100 | |
| 101 | void Init(); |
| 102 | |
| 103 | bool Load(const char *pFilename, const char *pMap, const SHA256_DIGEST &MapSha256, unsigned MapCrc) override; |
| 104 | void Close() override; |
| 105 | const CGhostInfo *GetInfo() const override { return &m_Info; } |
| 106 | |
| 107 | bool ReadNextType(int *pType) override; |
| 108 | bool ReadData(int Type, void *pData, size_t Size) override; |
| 109 | |
| 110 | bool GetGhostInfo(const char *pFilename, CGhostInfo *pGhostInfo, const char *pMap, const SHA256_DIGEST &MapSha256, unsigned MapCrc) override; |
| 111 | }; |
| 112 | #endif |
| 113 | |