3054: Remove CRC in all possible places r=def- a=heinrich5991

Some places remain due to net/file format compatibility.

Co-authored-by: heinrich5991 <heinrich5991@gmail.com>
This commit is contained in:
bors[bot] 2020-10-14 21:20:53 +00:00 committed by GitHub
commit 3fdfe7ea2d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 278 additions and 426 deletions

View file

@ -1485,8 +1485,6 @@ set_src(ENGINE_SHARED GLOB src/engine/shared
fifo.h
filecollection.cpp
filecollection.h
ghost.cpp
ghost.h
global_uuid_manager.cpp
huffman.cpp
huffman.h
@ -1638,6 +1636,8 @@ if(CLIENT)
demoedit.h
friends.cpp
friends.h
ghost.cpp
ghost.h
graphics_threaded.cpp
graphics_threaded.h
http.cpp

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

@ -41,7 +41,6 @@
#include <engine/shared/demo.h>
#include <engine/shared/fifo.h>
#include <engine/shared/filecollection.h>
#include <engine/shared/ghost.h>
#include <engine/shared/json.h>
#include <engine/shared/network.h>
#include <engine/shared/packer.h>
@ -1165,8 +1164,8 @@ const char *CClient::LoadMap(const char *pName, const char *pFilename, SHA256_DI
return s_aErrorMsg;
}
// get the crc of the map
if(m_pMap->Crc() != WantedCrc)
// Only check CRC if we don't have the secure SHA256.
if(!pWantedSha256 && m_pMap->Crc() != WantedCrc)
{
str_format(s_aErrorMsg, sizeof(s_aErrorMsg), "map differs from the server. %08x != %08x", m_pMap->Crc(), WantedCrc);
m_pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "client", s_aErrorMsg);
@ -1196,15 +1195,17 @@ const char *CClient::LoadMapSearch(const char *pMapName, SHA256_DIGEST *pWantedS
char aWanted[256];
char aWantedSha256[SHA256_MAXSTRSIZE];
aWanted[0] = 0;
if(pWantedSha256)
{
sha256_str(*pWantedSha256, aWantedSha256, sizeof(aWantedSha256));
str_format(aWanted, sizeof(aWanted), " sha256=%s", aWantedSha256);
str_format(aWanted, sizeof(aWanted), "sha256=%s", aWantedSha256);
}
else
{
str_format(aWanted, sizeof(aWanted), "crc=%08x", WantedCrc);
}
str_format(aBuf, sizeof(aBuf), "loading map, map=%s wanted%s crc=%08x", pMapName, aWanted, WantedCrc);
str_format(aBuf, sizeof(aBuf), "loading map, map=%s wanted %s", pMapName, aWanted);
m_pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "client", aBuf);
SetState(IClient::STATE_LOADING);
@ -1217,14 +1218,15 @@ const char *CClient::LoadMapSearch(const char *pMapName, SHA256_DIGEST *pWantedS
// try the downloaded maps
if(pWantedSha256)
{
str_format(aBuf, sizeof(aBuf), "downloadedmaps/%s_%08x_%s.map", pMapName, WantedCrc, aWantedSha256);
str_format(aBuf, sizeof(aBuf), "downloadedmaps/%s_%s.map", pMapName, aWantedSha256);
}
else
{
str_format(aBuf, sizeof(aBuf), "downloadedmaps/%s_%08x.map", pMapName, WantedCrc);
pError = LoadMap(pMapName, aBuf, pWantedSha256, WantedCrc);
if(!pError)
return pError;
}
// try the downloaded maps folder without appending the sha256
str_format(aBuf, sizeof(aBuf), "downloadedmaps/%s_%08x.map", pMapName, WantedCrc);
pError = LoadMap(pMapName, aBuf, pWantedSha256, WantedCrc);
if(!pError)
return pError;
@ -1566,28 +1568,23 @@ bool CClient::ShouldSendChatTimeoutCodeHeuristic()
static void FormatMapDownloadFilename(const char *pName, SHA256_DIGEST *pSha256, int Crc, bool Temp, char *pBuffer, int BufferSize)
{
char aSha256[SHA256_MAXSTRSIZE + 1];
aSha256[0] = 0;
char aHash[SHA256_MAXSTRSIZE];
if(pSha256)
{
aSha256[0] = '_';
sha256_str(*pSha256, aSha256 + 1, sizeof(aSha256) - 1);
sha256_str(*pSha256, aHash, sizeof(aHash));
}
else
{
str_format(aHash, sizeof(aHash), "%08x", Crc);
}
if(Temp)
{
str_format(pBuffer, BufferSize, "%s_%08x%s.map.%d.tmp",
pName,
Crc,
aSha256,
pid());
str_format(pBuffer, BufferSize, "%s_%s.map.%d.tmp", pName, aHash, pid());
}
else
{
str_format(pBuffer, BufferSize, "%s_%08x%s.map",
pName,
Crc,
aSha256);
str_format(pBuffer, BufferSize, "%s_%s.map", pName, aHash);
}
}
@ -4373,7 +4370,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

@ -10,6 +10,7 @@
#include <engine/client.h>
#include <engine/client/demoedit.h>
#include <engine/client/friends.h>
#include <engine/client/ghost.h>
#include <engine/client/http.h>
#include <engine/client/serverbrowser.h>
#include <engine/client/updater.h>
@ -22,7 +23,6 @@
#include <engine/shared/config.h>
#include <engine/shared/demo.h>
#include <engine/shared/fifo.h>
#include <engine/shared/ghost.h>
#include <engine/shared/network.h>
#include <engine/sound.h>
#include <engine/steam.h>
@ -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,14 +1,14 @@
#include "ghost.h"
#include <base/system.h>
#include <engine/console.h>
#include <engine/shared/compression.h>
#include <engine/shared/network.h>
#include <engine/storage.h>
#include "compression.h"
#include "ghost.h"
#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;
}

120
src/engine/client/ghost.h Normal file
View file

@ -0,0 +1,120 @@
#ifndef ENGINE_CLIENT_GHOST_H
#define ENGINE_CLIENT_GHOST_H
#include <engine/ghost.h>
enum
{
MAX_ITEM_SIZE = 128,
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:
unsigned char m_aData[MAX_ITEM_SIZE];
int m_Type;
CGhostItem() :
m_Type(-1) {}
CGhostItem(int Type) :
m_Type(Type) {}
void Reset() { m_Type = -1; }
};
class CGhostRecorder : public IGhostRecorder
{
IOHANDLE m_File;
class IConsole *m_pConsole;
class IStorage *m_pStorage;
CGhostItem m_LastItem;
char m_aBuffer[MAX_ITEM_SIZE * NUM_ITEMS_PER_CHUNK];
char *m_pBufferPos;
int m_BufferNumItems;
void ResetBuffer();
void FlushChunk();
public:
CGhostRecorder();
void Init();
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);
bool IsRecording() const { return m_File != 0; }
};
class CGhostLoader : public IGhostLoader
{
IOHANDLE m_File;
class IConsole *m_pConsole;
class IStorage *m_pStorage;
CGhostHeader m_Header;
CGhostInfo m_Info;
CGhostItem m_LastItem;
char m_aBuffer[MAX_ITEM_SIZE * NUM_ITEMS_PER_CHUNK];
char *m_pBufferPos;
int m_BufferNumItems;
int m_BufferCurItem;
int m_BufferPrevItem;
void ResetBuffer();
int ReadChunk(int *pType);
public:
CGhostLoader();
void Init();
int Load(const char *pFilename, const char *pMap, SHA256_DIGEST MapSha256, unsigned MapCrc);
void Close();
const CGhostInfo *GetInfo() const { return &m_Info; }
bool ReadNextType(int *pType);
bool ReadData(int Type, void *pData, int Size);
bool GetGhostInfo(const char *pFilename, CGhostInfo *pGhostInfo, const char *pMap, SHA256_DIGEST MapSha256, unsigned MapCrc);
};
#endif

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

@ -2265,8 +2265,6 @@ int CServer::LoadMap(const char *pMapName)
sha256_str(m_aCurrentMapSha256[SIX], aSha256, sizeof(aSha256));
str_format(aBufMsg, sizeof(aBufMsg), "%s sha256 is %s", aBuf, aSha256);
Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "server", aBufMsg);
str_format(aBufMsg, sizeof(aBufMsg), "%s crc is %08x", aBuf, m_aCurrentMapCrc[SIX]);
Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "server", aBufMsg);
str_copy(m_aCurrentMap, pMapName, sizeof(m_aCurrentMap));
@ -2304,8 +2302,6 @@ int CServer::LoadMap(const char *pMapName)
sha256_str(m_aCurrentMapSha256[SIXUP], aSha256, sizeof(aSha256));
str_format(aBufMsg, sizeof(aBufMsg), "%s sha256 is %s", aBuf, aSha256);
Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "sixup", aBufMsg);
str_format(aBufMsg, sizeof(aBufMsg), "%s crc is %08x", aBuf, m_aCurrentMapCrc[SIXUP]);
Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "sixup", aBufMsg);
}
}

View file

@ -84,15 +84,13 @@ int CDemoRecorder::Start(class IStorage *pStorage, class IConsole *pConsole, con
// try the downloaded maps
if(pSha256)
{
str_format(aMapFilename, sizeof(aMapFilename), "downloadedmaps/%s_%08x_%s.map", pMap, Crc, aSha256);
MapFile = pStorage->OpenFile(aMapFilename, IOFLAG_READ, IStorage::TYPE_ALL);
str_format(aMapFilename, sizeof(aMapFilename), "downloadedmaps/%s_%s.map", pMap, aSha256);
}
if(!MapFile)
else
{
// try the downloaded maps without sha
str_format(aMapFilename, sizeof(aMapFilename), "downloadedmaps/%s_%08x.map", pMap, Crc);
MapFile = pStorage->OpenFile(aMapFilename, IOFLAG_READ, IStorage::TYPE_ALL);
}
MapFile = pStorage->OpenFile(aMapFilename, IOFLAG_READ, IStorage::TYPE_ALL);
if(!MapFile)
{
// try the normal maps folder
@ -849,7 +847,7 @@ bool CDemoPlayer::ExtractMap(class IStorage *pStorage)
// construct name
char aSha[SHA256_MAXSTRSIZE], aMapFilename[128];
sha256_str(Sha256, aSha, sizeof(aSha));
str_format(aMapFilename, sizeof(aMapFilename), "downloadedmaps/%s_%08x_%s.map", m_Info.m_Header.m_aMapName, m_MapInfo.m_Crc, aSha);
str_format(aMapFilename, sizeof(aMapFilename), "downloadedmaps/%s_%s.map", m_Info.m_Header.m_aMapName, aSha);
// save map
IOHANDLE MapFile = pStorage->OpenFile(aMapFilename, IOFLAG_WRITE, IStorage::TYPE_SAVE);

View file

@ -1,146 +0,0 @@
#ifndef ENGINE_SHARED_GHOST_H
#define ENGINE_SHARED_GHOST_H
#include <engine/ghost.h>
enum
{
MAX_ITEM_SIZE = 128,
NUM_ITEMS_PER_CHUNK = 50,
};
class CGhostItem
{
public:
unsigned char m_aData[MAX_ITEM_SIZE];
int m_Type;
CGhostItem() :
m_Type(-1) {}
CGhostItem(int Type) :
m_Type(Type) {}
void Reset() { m_Type = -1; }
};
class CGhostRecorder : public IGhostRecorder
{
IOHANDLE m_File;
class IConsole *m_pConsole;
class IStorage *m_pStorage;
CGhostItem m_LastItem;
char m_aBuffer[MAX_ITEM_SIZE * NUM_ITEMS_PER_CHUNK];
char *m_pBufferPos;
int m_BufferNumItems;
void ResetBuffer();
void FlushChunk();
public:
CGhostRecorder();
void Init();
int Start(const char *pFilename, const char *pMap, unsigned MapCrc, const char *pName);
int Stop(int Ticks, int Time);
void WriteData(int Type, const void *pData, int Size);
bool IsRecording() const { return m_File != 0; }
};
class CGhostLoader : public IGhostLoader
{
IOHANDLE m_File;
class IConsole *m_pConsole;
class IStorage *m_pStorage;
CGhostHeader m_Header;
CGhostItem m_LastItem;
char m_aBuffer[MAX_ITEM_SIZE * NUM_ITEMS_PER_CHUNK];
char *m_pBufferPos;
int m_BufferNumItems;
int m_BufferCurItem;
int m_BufferPrevItem;
void ResetBuffer();
int ReadChunk(int *pType);
public:
CGhostLoader();
void Init();
int Load(const char *pFilename, const char *pMap, unsigned Crc);
void Close();
const CGhostHeader *GetHeader() const { return &m_Header; }
bool ReadNextType(int *pType);
bool ReadData(int Type, void *pData, int Size);
bool GetGhostInfo(const char *pFilename, CGhostHeader *pGhostHeader, const char *pMap, unsigned Crc);
};
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

@ -958,22 +958,21 @@ void CMenus::RenderDemoList(CUIRect MainView)
Labels.HSplitTop(5.0f, 0, &Labels);
Labels.HSplitTop(20.0f, &Left, &Labels);
Left.VSplitLeft(150.0f, &Left, &Right);
UI()->DoLabelScaled(&Left, Localize("Crc:"), 14.0f, -1);
str_format(aBuf, sizeof(aBuf), "%08x", m_lDemos[m_DemolistSelectedIndex].m_MapInfo.m_Crc);
UI()->DoLabelScaled(&Right, aBuf, 14.0f, -1);
Labels.HSplitTop(5.0f, 0, &Labels);
Labels.HSplitTop(20.0f, &Left, &Labels);
if(m_lDemos[m_DemolistSelectedIndex].m_MapInfo.m_Sha256 != SHA256_ZEROED)
{
Left.VSplitLeft(150.0f, &Left, &Right);
UI()->DoLabelScaled(&Left, "SHA256:", 14.0f, -1);
char aSha[SHA256_MAXSTRSIZE];
sha256_str(m_lDemos[m_DemolistSelectedIndex].m_MapInfo.m_Sha256, aSha, sizeof(aSha) / 2);
UI()->DoLabelScaled(&Right, aSha, Right.w > 235 ? 14.0f : 11.0f, -1);
Labels.HSplitTop(5.0f, 0, &Labels);
Labels.HSplitTop(20.0f, &Left, &Labels);
}
else
{
UI()->DoLabelScaled(&Left, Localize("Crc:"), 14.0f, -1);
str_format(aBuf, sizeof(aBuf), "%08x", m_lDemos[m_DemolistSelectedIndex].m_MapInfo.m_Crc);
UI()->DoLabelScaled(&Right, aBuf, 14.0f, -1);
}
Labels.HSplitTop(5.0f, 0, &Labels);
Labels.HSplitTop(20.0f, &Left, &Labels);
Left.VSplitLeft(150.0f, &Left, &Right);
UI()->DoLabelScaled(&Left, Localize("Netversion:"), 14.0f, -1);

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;

View file

@ -7,13 +7,11 @@ struct CMapDescription
const char *m_pName;
int m_Size;
SHA256_DIGEST m_Sha256;
int m_Crc;
bool operator==(const CMapDescription &Other) const
{
return str_comp(m_pName, Other.m_pName) == 0 &&
m_Size == Other.m_Size &&
m_Crc == Other.m_Crc;
m_Size == Other.m_Size;
}
};
@ -45,11 +43,11 @@ static SHA256_DIGEST s(const char *pSha256)
static CMapBugsInternal MAP_BUGS[] =
{
{{"Binary", 2022597, s("65b410e197fd2298ec270e89a84b762f6739d1d18089529f8ef6cf2104d3d600"), 0x0ae3a3d5}, BugToFlag(BUG_GRENADE_DOUBLEEXPLOSION)}};
{{"Binary", 2022597, s("65b410e197fd2298ec270e89a84b762f6739d1d18089529f8ef6cf2104d3d600")}, BugToFlag(BUG_GRENADE_DOUBLEEXPLOSION)}};
CMapBugs GetMapBugs(const char *pName, int Size, SHA256_DIGEST Sha256, int Crc)
CMapBugs GetMapBugs(const char *pName, int Size, SHA256_DIGEST Sha256)
{
CMapDescription Map = {pName, Size, Sha256, Crc};
CMapDescription Map = {pName, Size, Sha256};
CMapBugs Result;
Result.m_Extra = 0;
for(unsigned int i = 0; i < sizeof(MAP_BUGS) / sizeof(MAP_BUGS[0]); i++)
@ -122,10 +120,9 @@ void CMapBugs::Dump() const
{
char aSha256[SHA256_MAXSTRSIZE];
sha256_str(pInternal->m_Map.m_Sha256, aSha256, sizeof(aSha256));
dbg_msg("mapbugs", "map='%s' map_size=%d map_sha256=%s map_crc=%08x",
dbg_msg("mapbugs", "map='%s' map_size=%d map_sha256=%s",
pInternal->m_Map.m_pName,
pInternal->m_Map.m_Size,
aSha256,
pInternal->m_Map.m_Crc);
aSha256);
}
}

View file

@ -20,7 +20,7 @@ enum
class CMapBugs
{
friend CMapBugs GetMapBugs(const char *pName, int Size, SHA256_DIGEST Sha256, int Crc);
friend CMapBugs GetMapBugs(const char *pName, int Size, SHA256_DIGEST Sha256);
void *m_pData;
unsigned int m_Extra;
@ -30,5 +30,5 @@ public:
void Dump() const;
};
CMapBugs GetMapBugs(const char *pName, int Size, SHA256_DIGEST Sha256, int Crc);
CMapBugs GetMapBugs(const char *pName, int Size, SHA256_DIGEST Sha256);
#endif // GAME_MAPBUGS_H

View file

@ -3019,7 +3019,7 @@ void CGameContext::OnInit(/*class IKernel *pKernel*/)
SHA256_DIGEST MapSha256;
int MapCrc;
Server()->GetMapInfo(aMapName, sizeof(aMapName), &MapSize, &MapSha256, &MapCrc);
m_MapBugs = GetMapBugs(aMapName, MapSize, MapSha256, MapCrc);
m_MapBugs = GetMapBugs(aMapName, MapSize, MapSha256);
// reset everything here
//world = new GAMEWORLD;

View file

@ -6,32 +6,31 @@
static const char *BINARY_SHA256 = "65b410e197fd2298ec270e89a84b762f6739d1d18089529f8ef6cf2104d3d600";
static const char *DM1_SHA256 = "0b0c481d77519c32fbe85624ef16ec0fa9991aec7367ad538bd280f28d8c26cf";
static CMapBugs GetMapBugsImpl(const char *pName, int Size, const char *pSha256, int Crc)
static CMapBugs GetMapBugsImpl(const char *pName, int Size, const char *pSha256)
{
SHA256_DIGEST Sha256 = {{0}};
dbg_assert(sha256_from_str(&Sha256, pSha256) == 0, "invalid sha256 in tests");
return GetMapBugs(pName, Size, Sha256, Crc);
return GetMapBugs(pName, Size, Sha256);
}
TEST(MapBugs, Contains)
{
EXPECT_TRUE(GetMapBugsImpl("Binary", 2022597, BINARY_SHA256, 0x0ae3a3d5).Contains(BUG_GRENADE_DOUBLEEXPLOSION));
EXPECT_FALSE(GetMapBugsImpl("Binarx", 2022597, BINARY_SHA256, 0x0ae3a3d5).Contains(BUG_GRENADE_DOUBLEEXPLOSION));
EXPECT_FALSE(GetMapBugsImpl("Binary", 2022597, BINARY_SHA256, 0x0ae3a3d6).Contains(BUG_GRENADE_DOUBLEEXPLOSION));
EXPECT_FALSE(GetMapBugsImpl("Binary", 2022598, BINARY_SHA256, 0x0ae3a3d5).Contains(BUG_GRENADE_DOUBLEEXPLOSION));
EXPECT_FALSE(GetMapBugsImpl("dm1", 5805, DM1_SHA256, 0xf2159e6e).Contains(BUG_GRENADE_DOUBLEEXPLOSION));
EXPECT_TRUE(GetMapBugsImpl("Binary", 2022597, BINARY_SHA256).Contains(BUG_GRENADE_DOUBLEEXPLOSION));
EXPECT_FALSE(GetMapBugsImpl("Binarx", 2022597, BINARY_SHA256).Contains(BUG_GRENADE_DOUBLEEXPLOSION));
EXPECT_FALSE(GetMapBugsImpl("Binary", 2022598, BINARY_SHA256).Contains(BUG_GRENADE_DOUBLEEXPLOSION));
EXPECT_FALSE(GetMapBugsImpl("dm1", 5805, DM1_SHA256).Contains(BUG_GRENADE_DOUBLEEXPLOSION));
}
TEST(MapBugs, Update)
{
{
CMapBugs Binary = GetMapBugsImpl("Binary", 2022597, BINARY_SHA256, 0x0ae3a3d5);
CMapBugs Binary = GetMapBugsImpl("Binary", 2022597, BINARY_SHA256);
EXPECT_EQ(Binary.Update("grenade-doubleexplosion@ddnet.tw"), MAPBUGUPDATE_OVERRIDDEN);
EXPECT_EQ(Binary.Update("doesntexist@invalid"), MAPBUGUPDATE_NOTFOUND);
EXPECT_TRUE(Binary.Contains(BUG_GRENADE_DOUBLEEXPLOSION));
}
{
CMapBugs Dm1 = GetMapBugsImpl("dm1", 5805, DM1_SHA256, 0xf2159e6e);
CMapBugs Dm1 = GetMapBugsImpl("dm1", 5805, DM1_SHA256);
EXPECT_FALSE(Dm1.Contains(BUG_GRENADE_DOUBLEEXPLOSION));
EXPECT_EQ(Dm1.Update("doesntexist@invalid"), MAPBUGUPDATE_NOTFOUND);
EXPECT_FALSE(Dm1.Contains(BUG_GRENADE_DOUBLEEXPLOSION));
@ -42,6 +41,6 @@ TEST(MapBugs, Update)
TEST(MapBugs, Dump)
{
GetMapBugsImpl("Binary", 2022597, BINARY_SHA256, 0x0ae3a3d5).Dump();
GetMapBugsImpl("dm1", 5805, DM1_SHA256, 0xf2159e6e).Dump();
GetMapBugsImpl("Binary", 2022597, BINARY_SHA256).Dump();
GetMapBugsImpl("dm1", 5805, DM1_SHA256).Dump();
}