1#ifndef ENGINE_CLIENT_GHOST_H
2#define ENGINE_CLIENT_GHOST_H
3
4#include <engine/ghost.h>
5
6#include <cstdint>
7
8enum
9{
10 MAX_ITEM_SIZE = 128,
11 NUM_ITEMS_PER_CHUNK = 50,
12 MAX_CHUNK_SIZE = MAX_ITEM_SIZE * NUM_ITEMS_PER_CHUNK,
13};
14static_assert(MAX_CHUNK_SIZE % sizeof(uint32_t) == 0, "Chunk size must be aligned with uint32_t");
15
16// version 4-6
17struct CGhostHeader
18{
19 unsigned char m_aMarker[8];
20 unsigned char m_Version;
21 char m_aOwner[MAX_NAME_LENGTH];
22 char m_aMap[64];
23 unsigned char m_aZeroes[sizeof(int32_t)]; // Crc before version 6
24 unsigned char m_aNumTicks[sizeof(int32_t)];
25 unsigned char m_aTime[sizeof(int32_t)];
26 SHA256_DIGEST m_MapSha256;
27
28 int GetTicks() const;
29 int GetTime() const;
30 CGhostInfo ToGhostInfo() const;
31};
32
33class CGhostItem
34{
35public:
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
46class 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
62public:
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
74class CGhostLoader : public IGhostLoader
75{
76 IOHANDLE m_File;
77 char m_aFilename[IO_MAX_PATH_LENGTH];
78 class IStorage *m_pStorage;
79
80 CGhostHeader m_Header;
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 ReadHeader(CGhostHeader &Header, const char *pFilename, const char *pMap, const SHA256_DIGEST &MapSha256, unsigned MapCrc, bool LogMapMismatch) const;
94 bool ValidateHeader(const CGhostHeader &Header, const char *pFilename) const;
95 bool CheckHeaderMap(const CGhostHeader &Header, const char *pFilename, const char *pMap, const SHA256_DIGEST &MapSha256, unsigned MapCrc, bool LogMapMismatch) const;
96 bool ReadChunk(int *pType);
97
98public:
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