Update ghost format to v6: Replace CRC with SHA256

To be friendly to other implementors of the format, the CRC field is now
zeroed out instead of completely gone.

Drop support for v2 and v3 of the ghost format, the ghost files should
have been automatically converted by the client already.
This commit is contained in:
heinrich5991 2020-10-10 22:58:33 +02:00
parent 6ff53358d3
commit 070504b5c8
8 changed files with 138 additions and 272 deletions

View file

@ -6,6 +6,7 @@
#include "graphics.h"
#include "message.h"
#include <base/hash.h>
#include <engine/friends.h>
struct SWarning;
@ -193,7 +194,8 @@ public:
virtual const char *GetCurrentMap() = 0;
virtual const char *GetCurrentMapPath() = 0;
virtual unsigned GetMapCrc() = 0;
virtual SHA256_DIGEST GetCurrentMapSha256() = 0;
virtual unsigned GetCurrentMapCrc() = 0;
virtual int GetCurrentRaceTime() = 0;

View file

@ -4371,7 +4371,12 @@ const char *CClient::GetCurrentMapPath()
return m_aCurrentMapPath;
}
unsigned CClient::GetMapCrc()
SHA256_DIGEST CClient::GetCurrentMapSha256()
{
return m_pMap->Sha256();
}
unsigned CClient::GetCurrentMapCrc()
{
return m_pMap->Crc();
}

View file

@ -471,7 +471,8 @@ public:
virtual const char *GetCurrentMap();
virtual const char *GetCurrentMapPath();
virtual unsigned GetMapCrc();
virtual SHA256_DIGEST GetCurrentMapSha256();
virtual unsigned GetCurrentMapCrc();
virtual void RaceRecord_Start(const char *pFilename);
virtual void RaceRecord_Stop();

View file

@ -1,31 +1,19 @@
#ifndef ENGINE_GHOST_H
#define ENGINE_GHOST_H
#include <base/hash.h>
#include <engine/map.h>
#include <engine/shared/protocol.h>
#include "kernel.h"
class CGhostHeader
class CGhostInfo
{
public:
unsigned char m_aMarker[8];
unsigned char m_Version;
char m_aOwner[MAX_NAME_LENGTH];
char m_aMap[64];
unsigned char m_aCrc[4];
unsigned char m_aNumTicks[4];
unsigned char m_aTime[4];
int GetTime() const
{
return (m_aTime[0] << 24) | (m_aTime[1] << 16) | (m_aTime[2] << 8) | (m_aTime[3]);
}
int GetTicks() const
{
return (m_aNumTicks[0] << 24) | (m_aNumTicks[1] << 16) | (m_aNumTicks[2] << 8) | (m_aNumTicks[3]);
}
int m_NumTicks;
int m_Time;
};
class IGhostRecorder : public IInterface
@ -34,7 +22,7 @@ class IGhostRecorder : public IInterface
public:
virtual ~IGhostRecorder() {}
virtual int Start(const char *pFilename, const char *pMap, unsigned MapCrc, const char *pName) = 0;
virtual int Start(const char *pFilename, const char *pMap, SHA256_DIGEST MapSha256, const char *pName) = 0;
virtual int Stop(int Ticks, int Time) = 0;
virtual void WriteData(int Type, const void *pData, int Size) = 0;
@ -47,15 +35,15 @@ class IGhostLoader : public IInterface
public:
virtual ~IGhostLoader() {}
virtual int Load(const char *pFilename, const char *pMap, unsigned Crc) = 0;
virtual int Load(const char *pFilename, const char *pMap, SHA256_DIGEST MapSha256, unsigned MapCrc) = 0;
virtual void Close() = 0;
virtual const CGhostHeader *GetHeader() const = 0;
virtual const CGhostInfo *GetInfo() const = 0;
virtual bool ReadNextType(int *pType) = 0;
virtual bool ReadData(int Type, void *pData, int Size) = 0;
virtual bool GetGhostInfo(const char *pFilename, CGhostHeader *pGhostHeader, const char *pMap, unsigned Crc) = 0;
virtual bool GetGhostInfo(const char *pFilename, CGhostInfo *pInfo, const char *pMap, SHA256_DIGEST MapSha256, unsigned MapCrc) = 0;
};
#endif

View file

@ -8,7 +8,7 @@
#include "network.h"
static const unsigned char gs_aHeaderMarker[8] = {'T', 'W', 'G', 'H', 'O', 'S', 'T', 0};
static const unsigned char gs_ActVersion = 5;
static const unsigned char gs_CurVersion = 6;
static const int gs_NumTicksOffset = 93;
CGhostRecorder::CGhostRecorder()
@ -24,7 +24,7 @@ void CGhostRecorder::Init()
}
// Record
int CGhostRecorder::Start(const char *pFilename, const char *pMap, unsigned Crc, const char *pName)
int CGhostRecorder::Start(const char *pFilename, const char *pMap, SHA256_DIGEST MapSha256, const char *pName)
{
m_File = m_pStorage->OpenFile(pFilename, IOFLAG_WRITE, IStorage::TYPE_SAVE);
if(!m_File)
@ -39,20 +39,17 @@ int CGhostRecorder::Start(const char *pFilename, const char *pMap, unsigned Crc,
CGhostHeader Header;
mem_zero(&Header, sizeof(Header));
mem_copy(Header.m_aMarker, gs_aHeaderMarker, sizeof(Header.m_aMarker));
Header.m_Version = gs_ActVersion;
Header.m_Version = gs_CurVersion;
str_copy(Header.m_aOwner, pName, sizeof(Header.m_aOwner));
str_copy(Header.m_aMap, pMap, sizeof(Header.m_aMap));
Header.m_aCrc[0] = (Crc >> 24) & 0xff;
Header.m_aCrc[1] = (Crc >> 16) & 0xff;
Header.m_aCrc[2] = (Crc >> 8) & 0xff;
Header.m_aCrc[3] = (Crc)&0xff;
Header.m_MapSha256 = MapSha256;
io_write(m_File, &Header, sizeof(Header));
m_LastItem.Reset();
ResetBuffer();
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "Ghost recording to '%s'", pFilename);
str_format(aBuf, sizeof(aBuf), "ghost recording to '%s'", pFilename);
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "ghost_recorder", aBuf);
return 0;
}
@ -185,7 +182,7 @@ void CGhostLoader::ResetBuffer()
m_BufferPrevItem = -1;
}
int CGhostLoader::Load(const char *pFilename, const char *pMap, unsigned Crc)
int CGhostLoader::Load(const char *pFilename, const char *pMap, SHA256_DIGEST MapSha256, unsigned MapCrc)
{
m_File = m_pStorage->OpenFile(pFilename, IOFLAG_READ, IStorage::TYPE_SAVE);
if(!m_File)
@ -209,7 +206,7 @@ int CGhostLoader::Load(const char *pFilename, const char *pMap, unsigned Crc)
return -1;
}
if(m_Header.m_Version != gs_ActVersion && m_Header.m_Version != 4)
if(!(4 <= m_Header.m_Version && m_Header.m_Version <= gs_CurVersion))
{
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "ghost version %d is not supported", m_Header.m_Version);
@ -219,14 +216,47 @@ int CGhostLoader::Load(const char *pFilename, const char *pMap, unsigned Crc)
return -1;
}
unsigned GhostMapCrc = (m_Header.m_aCrc[0] << 24) | (m_Header.m_aCrc[1] << 16) | (m_Header.m_aCrc[2] << 8) | (m_Header.m_aCrc[3]);
if(str_comp(m_Header.m_aMap, pMap) != 0 || GhostMapCrc != Crc)
if(str_comp(m_Header.m_aMap, pMap) != 0)
{
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "ghost map name '%s' does not match current map '%s'", m_Header.m_aMap, pMap);
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "ghost_loader", aBuf);
io_close(m_File);
m_File = 0;
return -1;
}
if(m_Header.m_Version >= 6)
{
if(m_Header.m_MapSha256 != MapSha256)
{
char aGhostSha256[SHA256_MAXSTRSIZE];
sha256_str(m_Header.m_MapSha256, aGhostSha256, sizeof(aGhostSha256));
char aMapSha256[SHA256_MAXSTRSIZE];
sha256_str(MapSha256, aMapSha256, sizeof(aMapSha256));
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "ghost map '%s' sha256 mismatch, wanted=%s ghost=%s", pMap, aMapSha256, aGhostSha256);
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "ghost_loader", aBuf);
io_close(m_File);
m_File = 0;
return -1;
}
}
else
{
io_skip(m_File, -(int)sizeof(SHA256_DIGEST));
unsigned GhostMapCrc = (m_Header.m_aZeroes[0] << 24) | (m_Header.m_aZeroes[1] << 16) | (m_Header.m_aZeroes[2] << 8) | (m_Header.m_aZeroes[3]);
if(str_comp(m_Header.m_aMap, pMap) != 0 || GhostMapCrc != MapCrc)
{
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "ghost map '%s' crc mismatch, wanted=%08x ghost=%08x", pMap, MapCrc, GhostMapCrc);
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "ghost_loader", aBuf);
io_close(m_File);
m_File = 0;
return -1;
}
}
m_Info = m_Header.ToGhostInfo();
m_LastItem.Reset();
ResetBuffer();
@ -336,177 +366,41 @@ void CGhostLoader::Close()
m_File = 0;
}
bool CGhostLoader::GetGhostInfo(const char *pFilename, CGhostHeader *pGhostHeader, const char *pMap, unsigned Crc)
bool CGhostLoader::GetGhostInfo(const char *pFilename, CGhostInfo *pInfo, const char *pMap, SHA256_DIGEST MapSha256, unsigned MapCrc)
{
if(!pGhostHeader)
return false;
mem_zero(pGhostHeader, sizeof(CGhostHeader));
CGhostHeader Header;
mem_zero(&Header, sizeof(Header));
IOHANDLE File = m_pStorage->OpenFile(pFilename, IOFLAG_READ, IStorage::TYPE_SAVE);
if(!File)
return false;
io_read(File, pGhostHeader, sizeof(CGhostHeader));
if(mem_comp(pGhostHeader->m_aMarker, gs_aHeaderMarker, sizeof(gs_aHeaderMarker)) == 0 && (pGhostHeader->m_Version == 2 || pGhostHeader->m_Version == 3))
{
io_close(File);
// old version... try to update
IGhostRecorder *pRecorder = Kernel()->RequestInterface<IGhostRecorder>();
if(CGhostUpdater::Update(pRecorder, m_pStorage, m_pConsole, pFilename))
{
// try again
File = m_pStorage->OpenFile(pFilename, IOFLAG_READ, IStorage::TYPE_SAVE);
io_read(File, pGhostHeader, sizeof(CGhostHeader));
}
else
return false;
}
io_read(File, &Header, sizeof(Header));
io_close(File);
if(mem_comp(pGhostHeader->m_aMarker, gs_aHeaderMarker, sizeof(gs_aHeaderMarker)) || (pGhostHeader->m_Version != gs_ActVersion && pGhostHeader->m_Version != 4))
if(mem_comp(Header.m_aMarker, gs_aHeaderMarker, sizeof(gs_aHeaderMarker)) || !(4 <= Header.m_Version && Header.m_Version <= gs_CurVersion))
return false;
unsigned GhostMapCrc = (pGhostHeader->m_aCrc[0] << 24) | (pGhostHeader->m_aCrc[1] << 16) | (pGhostHeader->m_aCrc[2] << 8) | (pGhostHeader->m_aCrc[3]);
if(str_comp(pGhostHeader->m_aMap, pMap) != 0 || GhostMapCrc != Crc)
return false;
return true;
}
inline void StrToInts(int *pInts, int Num, const char *pStr)
{
int Index = 0;
while(Num)
if(str_comp(Header.m_aMap, pMap) != 0)
{
char aBuf[4] = {0, 0, 0, 0};
for(int c = 0; c < 4 && pStr[Index]; c++, Index++)
aBuf[c] = pStr[Index];
*pInts = ((aBuf[0] + 128) << 24) | ((aBuf[1] + 128) << 16) | ((aBuf[2] + 128) << 8) | (aBuf[3] + 128);
pInts++;
Num--;
}
// null terminate
pInts[-1] &= 0xffffff00;
}
bool CGhostUpdater::Update(class IGhostRecorder *pRecorder, class IStorage *pStorage, class IConsole *pConsole, const char *pFilename)
{
pStorage->CreateFolder("ghosts/backup", IStorage::TYPE_SAVE);
const char *pExtractedName = pFilename;
for(const char *pSrc = pFilename; *pSrc; pSrc++)
if(*pSrc == '/' || *pSrc == '\\')
pExtractedName = pSrc + 1;
char aBackupFilename[512];
str_format(aBackupFilename, sizeof(aBackupFilename), "ghosts/backup/%s", pExtractedName);
if(!pStorage->RenameFile(pFilename, aBackupFilename, IStorage::TYPE_SAVE))
return false;
IOHANDLE File = pStorage->OpenFile(aBackupFilename, IOFLAG_READ, IStorage::TYPE_SAVE);
if(!File)
return false;
// read header
CGhostHeaderMain Header;
io_read(File, &Header, sizeof(Header));
if(mem_comp(Header.m_aMarker, gs_aHeaderMarker, sizeof(gs_aHeaderMarker)) != 0 || (Header.m_Version != 2 && Header.m_Version != 3))
{
pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "ghost/updater", "error: no valid ghost file");
io_close(File);
return false;
}
io_seek(File, 0, IOSEEK_START);
int Ticks, Time;
if(Header.m_Version == 2)
if(Header.m_Version >= 6)
{
pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "ghost/updater", "updating v2 ghost file");
CGhostHeaderV2 ExtHeader;
char aSkinData[ms_SkinSizeV2];
io_read(File, &ExtHeader, sizeof(ExtHeader));
io_read(File, aSkinData, sizeof(aSkinData));
Ticks = ExtHeader.m_NumShots;
Time = ExtHeader.m_Time * 1000;
unsigned Crc = (ExtHeader.m_aCrc[0] << 24) | (ExtHeader.m_aCrc[1] << 16) | (ExtHeader.m_aCrc[2] << 8) | (ExtHeader.m_aCrc[3]);
pRecorder->Start(pFilename, ExtHeader.m_aMap, Crc, ExtHeader.m_aOwner);
CGhostSkin Skin;
mem_copy(&Skin, aSkinData + ms_SkinOffsetV2, sizeof(Skin));
pRecorder->WriteData(0 /* GHOSTDATA_TYPE_SKIN */, &Skin, sizeof(Skin));
if(Header.m_MapSha256 != MapSha256)
{
return false;
}
}
else
{
pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "ghost/updater", "updating v3 ghost file");
CGhostHeaderV3 ExtHeader;
io_read(File, &ExtHeader, sizeof(ExtHeader));
Ticks = ExtHeader.m_NumShots;
Time = ExtHeader.m_Time * 1000;
unsigned Crc = (ExtHeader.m_aCrc[0] << 24) | (ExtHeader.m_aCrc[1] << 16) | (ExtHeader.m_aCrc[2] << 8) | (ExtHeader.m_aCrc[3]);
pRecorder->Start(pFilename, ExtHeader.m_aMap, Crc, ExtHeader.m_aOwner);
CGhostSkin Skin;
StrToInts(&Skin.m_Skin0, 6, ExtHeader.m_aSkinName);
Skin.m_UseCustomColor = ExtHeader.m_UseCustomColor;
Skin.m_ColorBody = ExtHeader.m_ColorBody;
Skin.m_ColorFeet = ExtHeader.m_ColorFeet;
pRecorder->WriteData(0 /* GHOSTDATA_TYPE_SKIN */, &Skin, sizeof(Skin));
}
// read data
int Index = 0;
while(Index < Ticks)
{
static char s_aCompresseddata[100 * 500];
static char s_aDecompressed[100 * 500];
static char s_aData[100 * 500];
unsigned char aSize[4];
if(io_read(File, aSize, sizeof(aSize)) != sizeof(aSize))
break;
unsigned Size = (aSize[0] << 24) | (aSize[1] << 16) | (aSize[2] << 8) | aSize[3];
if(io_read(File, s_aCompresseddata, Size) != Size)
unsigned GhostMapCrc = (Header.m_aZeroes[0] << 24) | (Header.m_aZeroes[1] << 16) | (Header.m_aZeroes[2] << 8) | (Header.m_aZeroes[3]);
if(GhostMapCrc != MapCrc)
{
pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "ghost/updater", "error reading chunk");
break;
}
int DataSize = CNetBase::Decompress(s_aCompresseddata, Size, s_aDecompressed, sizeof(s_aDecompressed));
if(DataSize < 0)
{
pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "ghost/updater", "error during network decompression");
break;
}
DataSize = CVariableInt::Decompress(s_aDecompressed, DataSize, s_aData, sizeof(s_aData));
if(DataSize < 0)
{
pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "ghost/updater", "error during intpack decompression");
break;
}
char *pTmp = s_aData;
for(int i = 0; i < DataSize / ms_GhostCharacterSize; i++)
{
pRecorder->WriteData(1 /* GHOSTDATA_TYPE_CHARACTER_NO_TICK */, pTmp, ms_GhostCharacterSize);
pTmp += ms_GhostCharacterSize;
Index++;
return false;
}
}
*pInfo = Header.ToGhostInfo();
io_close(File);
bool Error = Ticks != Index;
pRecorder->Stop(Index, Error ? 0 : Time);
return !Error;
return true;
}

View file

@ -9,6 +9,40 @@ enum
NUM_ITEMS_PER_CHUNK = 50,
};
// version 4-6
struct CGhostHeader
{
unsigned char m_aMarker[8];
unsigned char m_Version;
char m_aOwner[MAX_NAME_LENGTH];
char m_aMap[64];
unsigned char m_aZeroes[4]; // Crc before version 6
unsigned char m_aNumTicks[4];
unsigned char m_aTime[4];
SHA256_DIGEST m_MapSha256;
int GetTicks() const
{
return (m_aNumTicks[0] << 24) | (m_aNumTicks[1] << 16) | (m_aNumTicks[2] << 8) | (m_aNumTicks[3]);
}
int GetTime() const
{
return (m_aTime[0] << 24) | (m_aTime[1] << 16) | (m_aTime[2] << 8) | (m_aTime[3]);
}
CGhostInfo ToGhostInfo() const
{
CGhostInfo Result;
mem_zero(&Result, sizeof(Result));
str_copy(Result.m_aOwner, m_aOwner, sizeof(Result.m_aOwner));
str_copy(Result.m_aMap, m_aMap, sizeof(Result.m_aMap));
Result.m_NumTicks = GetTicks();
Result.m_Time = GetTime();
return Result;
}
};
class CGhostItem
{
public:
@ -42,7 +76,7 @@ public:
void Init();
int Start(const char *pFilename, const char *pMap, unsigned MapCrc, const char *pName);
int Start(const char *pFilename, const char *pMap, SHA256_DIGEST MapSha256, const char *pName);
int Stop(int Ticks, int Time);
void WriteData(int Type, const void *pData, int Size);
@ -56,6 +90,7 @@ class CGhostLoader : public IGhostLoader
class IStorage *m_pStorage;
CGhostHeader m_Header;
CGhostInfo m_Info;
CGhostItem m_LastItem;
@ -73,74 +108,13 @@ public:
void Init();
int Load(const char *pFilename, const char *pMap, unsigned Crc);
int Load(const char *pFilename, const char *pMap, SHA256_DIGEST MapSha256, unsigned MapCrc);
void Close();
const CGhostHeader *GetHeader() const { return &m_Header; }
const CGhostInfo *GetInfo() const { return &m_Info; }
bool ReadNextType(int *pType);
bool ReadData(int Type, void *pData, int Size);
bool GetGhostInfo(const char *pFilename, CGhostHeader *pGhostHeader, const char *pMap, unsigned Crc);
bool GetGhostInfo(const char *pFilename, CGhostInfo *pGhostInfo, const char *pMap, SHA256_DIGEST MapSha256, unsigned MapCrc);
};
class CGhostUpdater
{
// all
struct CGhostHeaderMain
{
unsigned char m_aMarker[8];
unsigned char m_Version;
};
// version 2
struct CGhostHeaderV2
{
unsigned char m_aMarker[8];
unsigned char m_Version;
char m_aOwner[MAX_NAME_LENGTH];
char m_aMap[64];
unsigned char m_aCrc[4];
int m_NumShots;
float m_Time;
};
static const int ms_SkinSizeV2 = 17 * sizeof(int);
static const int ms_SkinOffsetV2 = 8 * sizeof(int);
// version 3
struct CGhostHeaderV3
{
unsigned char m_aMarker[8];
unsigned char m_Version;
char m_aOwner[MAX_NAME_LENGTH];
char m_aSkinName[64];
int m_UseCustomColor;
int m_ColorBody;
int m_ColorFeet;
char m_aMap[64];
unsigned char m_aCrc[4];
int m_NumShots;
float m_Time;
};
// actual version
struct CGhostSkin
{
int m_Skin0;
int m_Skin1;
int m_Skin2;
int m_Skin3;
int m_Skin4;
int m_Skin5;
int m_UseCustomColor;
int m_ColorBody;
int m_ColorFeet;
};
static const int ms_GhostCharacterSize = 11 * sizeof(int);
public:
static bool Update(class IGhostRecorder *pRecorder, class IStorage *pStorage, class IConsole *pConsole, const char *pFilename);
};
#endif

View file

@ -119,16 +119,18 @@ CGhostCharacter *CGhost::CGhostPath::Get(int Index)
void CGhost::GetPath(char *pBuf, int Size, const char *pPlayerName, int Time) const
{
const char *pMap = Client()->GetCurrentMap();
unsigned Crc = Client()->GetMapCrc();
SHA256_DIGEST Sha256 = Client()->GetCurrentMapSha256();
char aSha256[SHA256_MAXSTRSIZE];
sha256_str(Sha256, aSha256, sizeof(aSha256));
char aPlayerName[MAX_NAME_LENGTH];
str_copy(aPlayerName, pPlayerName, sizeof(aPlayerName));
str_sanitize_filename(aPlayerName);
if(Time < 0)
str_format(pBuf, Size, "%s/%s_%s_%08x_tmp_%d.gho", ms_pGhostDir, pMap, aPlayerName, Crc, pid());
str_format(pBuf, Size, "%s/%s_%s_%s_tmp_%d.gho", ms_pGhostDir, pMap, aPlayerName, aSha256, pid());
else
str_format(pBuf, Size, "%s/%s_%s_%d.%03d_%08x.gho", ms_pGhostDir, pMap, aPlayerName, Time / 1000, Time % 1000, Crc);
str_format(pBuf, Size, "%s/%s_%s_%d.%03d_%s.gho", ms_pGhostDir, pMap, aPlayerName, Time / 1000, Time % 1000, aSha256);
}
void CGhost::AddInfos(const CNetObj_Character *pChar)
@ -139,7 +141,7 @@ void CGhost::AddInfos(const CNetObj_Character *pChar)
if(g_Config.m_ClRaceSaveGhost && !GhostRecorder()->IsRecording() && NumTicks > 0)
{
GetPath(m_aTmpFilename, sizeof(m_aTmpFilename), m_CurGhost.m_aPlayer);
GhostRecorder()->Start(m_aTmpFilename, Client()->GetCurrentMap(), Client()->GetMapCrc(), m_CurGhost.m_aPlayer);
GhostRecorder()->Start(m_aTmpFilename, Client()->GetCurrentMap(), Client()->GetCurrentMapSha256(), m_CurGhost.m_aPlayer);
GhostRecorder()->WriteData(GHOSTDATA_TYPE_START_TICK, &m_CurGhost.m_StartTick, sizeof(int));
GhostRecorder()->WriteData(GHOSTDATA_TYPE_SKIN, &m_CurGhost.m_Skin, sizeof(CGhostSkin));
@ -445,15 +447,14 @@ int CGhost::Load(const char *pFilename)
if(Slot == -1)
return -1;
if(GhostLoader()->Load(pFilename, Client()->GetCurrentMap(), Client()->GetMapCrc()) != 0)
if(GhostLoader()->Load(pFilename, Client()->GetCurrentMap(), Client()->GetCurrentMapSha256(), Client()->GetCurrentMapCrc()) != 0)
return -1;
const CGhostHeader *pHeader = GhostLoader()->GetHeader();
const CGhostInfo *pInfo = GhostLoader()->GetInfo();
int NumTicks = pHeader->GetTicks();
int Time = pHeader->GetTime();
if(NumTicks <= 0 || Time <= 0)
if(pInfo->m_NumTicks <= 0 || pInfo->m_Time <= 0)
{
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "ghost", "invalid header info");
GhostLoader()->Close();
return -1;
}
@ -461,9 +462,9 @@ int CGhost::Load(const char *pFilename)
// select ghost
CGhostItem *pGhost = &m_aActiveGhosts[Slot];
pGhost->Reset();
pGhost->m_Path.SetSize(NumTicks);
pGhost->m_Path.SetSize(pInfo->m_NumTicks);
str_copy(pGhost->m_aPlayer, pHeader->m_aOwner, sizeof(pGhost->m_aPlayer));
str_copy(pGhost->m_aPlayer, pInfo->m_aOwner, sizeof(pGhost->m_aPlayer));
int Index = 0;
bool FoundSkin = false;
@ -473,7 +474,7 @@ int CGhost::Load(const char *pFilename)
int Type;
while(!Error && GhostLoader()->ReadNextType(&Type))
{
if(Index == NumTicks && (Type == GHOSTDATA_TYPE_CHARACTER || Type == GHOSTDATA_TYPE_CHARACTER_NO_TICK))
if(Index == pInfo->m_NumTicks && (Type == GHOSTDATA_TYPE_CHARACTER || Type == GHOSTDATA_TYPE_CHARACTER_NO_TICK))
{
Error = true;
break;
@ -505,8 +506,9 @@ int CGhost::Load(const char *pFilename)
GhostLoader()->Close();
if(Error || Index != NumTicks)
if(Error || Index != pInfo->m_NumTicks)
{
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "ghost", "invalid ghost data");
pGhost->Reset();
return -1;
}
@ -514,10 +516,10 @@ int CGhost::Load(const char *pFilename)
if(NoTick)
{
int StartTick = 0;
for(int i = 1; i < NumTicks; i++) // estimate start tick
for(int i = 1; i < pInfo->m_NumTicks; i++) // estimate start tick
if(pGhost->m_Path.Get(i)->m_AttackTick != pGhost->m_Path.Get(i - 1)->m_AttackTick)
StartTick = pGhost->m_Path.Get(i)->m_AttackTick - i;
for(int i = 0; i < NumTicks; i++)
for(int i = 0; i < pInfo->m_NumTicks; i++)
pGhost->m_Path.Get(i)->m_Tick = StartTick + i;
}
@ -552,7 +554,7 @@ void CGhost::SaveGhost(CMenus::CGhostItem *pItem)
int NumTicks = pGhost->m_Path.Size();
GetPath(pItem->m_aFilename, sizeof(pItem->m_aFilename), pItem->m_aPlayer, pItem->m_Time);
GhostRecorder()->Start(pItem->m_aFilename, Client()->GetCurrentMap(), Client()->GetMapCrc(), pItem->m_aPlayer);
GhostRecorder()->Start(pItem->m_aFilename, Client()->GetCurrentMap(), Client()->GetCurrentMapSha256(), pItem->m_aPlayer);
GhostRecorder()->WriteData(GHOSTDATA_TYPE_START_TICK, &pGhost->m_StartTick, sizeof(int));
GhostRecorder()->WriteData(GHOSTDATA_TYPE_SKIN, &pGhost->m_Skin, sizeof(CGhostSkin));

View file

@ -882,14 +882,14 @@ int CMenus::GhostlistFetchCallback(const char *pName, int IsDir, int StorageType
char aFilename[256];
str_format(aFilename, sizeof(aFilename), "%s/%s", pSelf->m_pClient->m_pGhost->GetGhostDir(), pName);
CGhostHeader Header;
if(!pSelf->m_pClient->m_pGhost->GhostLoader()->GetGhostInfo(aFilename, &Header, pMap, pSelf->Client()->GetMapCrc()))
CGhostInfo Info;
if(!pSelf->m_pClient->m_pGhost->GhostLoader()->GetGhostInfo(aFilename, &Info, pMap, pSelf->Client()->GetCurrentMapSha256(), pSelf->Client()->GetCurrentMapCrc()))
return 0;
CGhostItem Item;
str_copy(Item.m_aFilename, aFilename, sizeof(Item.m_aFilename));
str_copy(Item.m_aPlayer, Header.m_aOwner, sizeof(Item.m_aPlayer));
Item.m_Time = Header.GetTime();
str_copy(Item.m_aPlayer, Info.m_aOwner, sizeof(Item.m_aPlayer));
Item.m_Time = Info.m_Time;
if(Item.m_Time > 0)
pSelf->m_lGhosts.add(Item);
return 0;