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