mirror of
https://github.com/shavitush/bhoptimer.git
synced 2025-12-07 18:38:26 +00:00
make replay reading/writing functions stocks
This commit is contained in:
parent
77265805ac
commit
ab0fc28c26
@ -269,6 +269,8 @@ enum struct frame_cache_t
|
||||
// iReplayVersion >= 0x05
|
||||
int iPostFrames;
|
||||
float fTickrate;
|
||||
// blah blah not affected by iReplayVersion
|
||||
int iSteamID;
|
||||
}
|
||||
|
||||
stock void Shavit_LogQuery(const char[] query)
|
||||
|
||||
332
addons/sourcemod/scripting/include/shavit/shavit-replay-file.inc
Normal file
332
addons/sourcemod/scripting/include/shavit/shavit-replay-file.inc
Normal file
@ -0,0 +1,332 @@
|
||||
|
||||
// History of REPLAY_FORMAT_SUBVERSION:
|
||||
// 0x01: standard origin[3], angles[2], and buttons
|
||||
// 0x02: flags added movetype added
|
||||
// 0x03: integrity stuff: style, track, and map added to header. preframe count added (unimplemented until later though)
|
||||
// 0x04: steamid/accountid written as a 32-bit int instead of a string
|
||||
// 0x05: postframes & fTickrate added
|
||||
// 0x06: mousexy and vel added
|
||||
// 0x07: fixed iFrameCount because postframes were included in the value when they shouldn't be
|
||||
// 0x08: added zone-offsets to header
|
||||
|
||||
#define REPLAY_FORMAT_V2 "{SHAVITREPLAYFORMAT}{V2}"
|
||||
#define REPLAY_FORMAT_FINAL "{SHAVITREPLAYFORMAT}{FINAL}"
|
||||
#define REPLAY_FORMAT_SUBVERSION 0x08
|
||||
|
||||
#define REPLAY_FRAMES_PER_WRITE 100 // amounts of frames to write per read/write call
|
||||
|
||||
enum struct replay_header_t
|
||||
{
|
||||
char sReplayFormat[40];
|
||||
int iReplayVersion;
|
||||
char sMap[PLATFORM_MAX_PATH];
|
||||
int iStyle;
|
||||
int iTrack;
|
||||
int iPreFrames;
|
||||
int iFrameCount;
|
||||
float fTime;
|
||||
int iSteamID;
|
||||
int iPostFrames;
|
||||
float fTickrate;
|
||||
float fZoneOffset[2];
|
||||
}
|
||||
|
||||
stock bool LoadReplayCache(frame_cache_t cache, int style, int track, const char[] path, const char[] mapname)
|
||||
{
|
||||
bool success = false;
|
||||
replay_header_t header;
|
||||
File fFile = ReadReplayHeader(path, header, style, track);
|
||||
|
||||
if (fFile != null)
|
||||
{
|
||||
if (header.iReplayVersion > REPLAY_FORMAT_SUBVERSION)
|
||||
{
|
||||
// not going to try and read it
|
||||
}
|
||||
else if (header.iReplayVersion < 0x03 || (StrEqual(header.sMap, mapname, false) && header.iStyle == style && header.iTrack == track))
|
||||
{
|
||||
success = ReadReplayFrames(fFile, header, cache);
|
||||
}
|
||||
|
||||
delete fFile;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
stock bool ReadReplayFrames(File file, replay_header_t header, frame_cache_t cache)
|
||||
{
|
||||
int total_cells = 6;
|
||||
int used_cells = 6;
|
||||
bool is_btimes = false;
|
||||
|
||||
if (header.iReplayVersion > 0x01)
|
||||
{
|
||||
total_cells = 8;
|
||||
used_cells = 8;
|
||||
}
|
||||
|
||||
// We have differing total_cells & used_cells because we want to save memory during playback since the latest two cells added (vel & mousexy) aren't needed and are only useful for replay file anticheat usage stuff....
|
||||
if (header.iReplayVersion >= 0x06)
|
||||
{
|
||||
total_cells = 10;
|
||||
used_cells = 8;
|
||||
}
|
||||
|
||||
any aReplayData[sizeof(frame_t)];
|
||||
|
||||
delete cache.aFrames;
|
||||
int iTotalSize = header.iFrameCount + header.iPreFrames + header.iPostFrames;
|
||||
cache.aFrames = new ArrayList(used_cells, iTotalSize);
|
||||
|
||||
if (!header.sReplayFormat[0]) // old replay format. no header.
|
||||
{
|
||||
char sLine[320];
|
||||
char sExplodedLine[6][64];
|
||||
|
||||
if(!file.Seek(0, SEEK_SET))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
while (!file.EndOfFile())
|
||||
{
|
||||
file.ReadLine(sLine, 320);
|
||||
int iStrings = ExplodeString(sLine, "|", sExplodedLine, 6, 64);
|
||||
|
||||
aReplayData[0] = StringToFloat(sExplodedLine[0]);
|
||||
aReplayData[1] = StringToFloat(sExplodedLine[1]);
|
||||
aReplayData[2] = StringToFloat(sExplodedLine[2]);
|
||||
aReplayData[3] = StringToFloat(sExplodedLine[3]);
|
||||
aReplayData[4] = StringToFloat(sExplodedLine[4]);
|
||||
aReplayData[5] = (iStrings == 6) ? StringToInt(sExplodedLine[5]) : 0;
|
||||
|
||||
cache.aFrames.PushArray(aReplayData, 6);
|
||||
}
|
||||
|
||||
cache.iFrameCount = cache.aFrames.Length;
|
||||
}
|
||||
else // assumes the file position will be at the start of the frames
|
||||
{
|
||||
is_btimes = StrEqual(header.sReplayFormat, "btimes");
|
||||
|
||||
for (int i = 0; i < iTotalSize; i++)
|
||||
{
|
||||
if(file.Read(aReplayData, total_cells, 4) >= 0)
|
||||
{
|
||||
cache.aFrames.SetArray(i, aReplayData, used_cells);
|
||||
|
||||
if (is_btimes && (aReplayData[5] & IN_BULLRUSH))
|
||||
{
|
||||
if (!header.iPreFrames)
|
||||
{
|
||||
header.iPreFrames = i;
|
||||
header.iFrameCount -= i;
|
||||
}
|
||||
else if (!header.iPostFrames)
|
||||
{
|
||||
header.iPostFrames = header.iFrameCount + header.iPreFrames - i;
|
||||
header.iFrameCount -= header.iPostFrames;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cache.iFrameCount = header.iFrameCount;
|
||||
cache.fTime = header.fTime;
|
||||
cache.iReplayVersion = header.iReplayVersion;
|
||||
cache.bNewFormat = StrEqual(header.sReplayFormat, REPLAY_FORMAT_FINAL) || is_btimes;
|
||||
cache.sReplayName = "unknown";
|
||||
cache.iPreFrames = header.iPreFrames;
|
||||
cache.iPostFrames = header.iPostFrames;
|
||||
cache.fTickrate = header.fTickrate;
|
||||
cache.iSteamID = header.iSteamID;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
stock File ReadReplayHeader(const char[] path, replay_header_t header, int style, int track)
|
||||
{
|
||||
replay_header_t empty_header;
|
||||
header = empty_header;
|
||||
|
||||
if (!FileExists(path))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
File file = OpenFile(path, "rb");
|
||||
|
||||
if (file == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
char sHeader[64];
|
||||
|
||||
if(!file.ReadLine(sHeader, 64))
|
||||
{
|
||||
delete file;
|
||||
return null;
|
||||
}
|
||||
|
||||
TrimString(sHeader);
|
||||
char sExplodedHeader[2][64];
|
||||
ExplodeString(sHeader, ":", sExplodedHeader, 2, 64);
|
||||
|
||||
strcopy(header.sReplayFormat, sizeof(header.sReplayFormat), sExplodedHeader[1]);
|
||||
|
||||
if(StrEqual(header.sReplayFormat, REPLAY_FORMAT_FINAL)) // hopefully, the last of them
|
||||
{
|
||||
int version = StringToInt(sExplodedHeader[0]);
|
||||
|
||||
header.iReplayVersion = version;
|
||||
|
||||
// replay file integrity and PreFrames
|
||||
if(version >= 0x03)
|
||||
{
|
||||
file.ReadString(header.sMap, PLATFORM_MAX_PATH);
|
||||
file.ReadUint8(header.iStyle);
|
||||
file.ReadUint8(header.iTrack);
|
||||
|
||||
file.ReadInt32(header.iPreFrames);
|
||||
|
||||
// In case the replay was from when there could still be negative preframes
|
||||
if(header.iPreFrames < 0)
|
||||
{
|
||||
header.iPreFrames = 0;
|
||||
}
|
||||
}
|
||||
|
||||
file.ReadInt32(header.iFrameCount);
|
||||
file.ReadInt32(view_as<int>(header.fTime));
|
||||
|
||||
if (header.iReplayVersion < 0x07)
|
||||
{
|
||||
header.iFrameCount -= header.iPreFrames;
|
||||
}
|
||||
|
||||
if(version >= 0x04)
|
||||
{
|
||||
file.ReadInt32(header.iSteamID);
|
||||
}
|
||||
else
|
||||
{
|
||||
char sAuthID[32];
|
||||
file.ReadString(sAuthID, 32);
|
||||
ReplaceString(sAuthID, 32, "[U:1:", "");
|
||||
ReplaceString(sAuthID, 32, "]", "");
|
||||
header.iSteamID = StringToInt(sAuthID);
|
||||
}
|
||||
|
||||
if (version >= 0x05)
|
||||
{
|
||||
file.ReadInt32(header.iPostFrames);
|
||||
file.ReadInt32(view_as<int>(header.fTickrate));
|
||||
|
||||
if (header.iReplayVersion < 0x07)
|
||||
{
|
||||
header.iFrameCount -= header.iPostFrames;
|
||||
}
|
||||
}
|
||||
|
||||
if (version >= 0x08)
|
||||
{
|
||||
file.ReadInt32(view_as<int>(header.fZoneOffset[0]));
|
||||
file.ReadInt32(view_as<int>(header.fZoneOffset[1]));
|
||||
}
|
||||
}
|
||||
else if(StrEqual(header.sReplayFormat, REPLAY_FORMAT_V2))
|
||||
{
|
||||
header.iFrameCount = StringToInt(sExplodedHeader[0]);
|
||||
}
|
||||
else // old, outdated and slow - only used for ancient replays
|
||||
{
|
||||
// check for btimes replays
|
||||
file.Seek(0, SEEK_SET);
|
||||
any stuff[2];
|
||||
file.Read(stuff, 2, 4);
|
||||
|
||||
int btimes_player_id = stuff[0];
|
||||
float run_time = stuff[1];
|
||||
|
||||
if (btimes_player_id >= 0 && run_time > 0.0 && run_time < (10.0 * 60.0 * 60.0))
|
||||
{
|
||||
header.sReplayFormat = "btimes";
|
||||
header.fTime = run_time;
|
||||
|
||||
file.Seek(0, SEEK_END);
|
||||
header.iFrameCount = (file.Position / 4 - 2) / 6;
|
||||
file.Seek(2*4, SEEK_SET);
|
||||
}
|
||||
}
|
||||
|
||||
if (header.iReplayVersion < 0x03)
|
||||
{
|
||||
header.iStyle = style;
|
||||
header.iTrack = track;
|
||||
}
|
||||
|
||||
if (header.iReplayVersion < 0x05)
|
||||
{
|
||||
header.fTickrate = (1.0 / GetTickInterval()); // just assume it's our own tickrate...
|
||||
}
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
stock void WriteReplayHeader(File fFile, int style, int track, float time, int steamid, int preframes, int postframes, float fZoneOffset[2], int iSize, float tickrate, const char[] sMap)
|
||||
{
|
||||
fFile.WriteLine("%d:" ... REPLAY_FORMAT_FINAL, REPLAY_FORMAT_SUBVERSION);
|
||||
|
||||
fFile.WriteString(sMap, true);
|
||||
fFile.WriteInt8(style);
|
||||
fFile.WriteInt8(track);
|
||||
fFile.WriteInt32(preframes);
|
||||
|
||||
fFile.WriteInt32(iSize - preframes - postframes);
|
||||
fFile.WriteInt32(view_as<int>(time));
|
||||
fFile.WriteInt32(steamid);
|
||||
|
||||
fFile.WriteInt32(postframes);
|
||||
fFile.WriteInt32(view_as<int>(tickrate));
|
||||
|
||||
fFile.WriteInt32(view_as<int>(fZoneOffset[0]));
|
||||
fFile.WriteInt32(view_as<int>(fZoneOffset[1]));
|
||||
}
|
||||
|
||||
// file_a is usually used as the wr replay file.
|
||||
// file_b is usually used as the duplicate/backup replay file.
|
||||
stock void WriteReplayFrames(ArrayList playerrecording, int iSize, File file_a, File file_b)
|
||||
{
|
||||
any aFrameData[sizeof(frame_t)];
|
||||
any aWriteData[sizeof(frame_t) * REPLAY_FRAMES_PER_WRITE];
|
||||
int iFramesWritten = 0;
|
||||
|
||||
for(int i = 0; i < iSize; i++)
|
||||
{
|
||||
playerrecording.GetArray(i, aFrameData, sizeof(frame_t));
|
||||
|
||||
for(int j = 0; j < sizeof(frame_t); j++)
|
||||
{
|
||||
aWriteData[(sizeof(frame_t) * iFramesWritten) + j] = aFrameData[j];
|
||||
}
|
||||
|
||||
if(++iFramesWritten == REPLAY_FRAMES_PER_WRITE || i == iSize - 1)
|
||||
{
|
||||
if (file_a)
|
||||
{
|
||||
file_a.Write(aWriteData, sizeof(frame_t) * iFramesWritten, 4);
|
||||
}
|
||||
|
||||
if (file_b)
|
||||
{
|
||||
file_b.Write(aWriteData, sizeof(frame_t) * iFramesWritten, 4);
|
||||
}
|
||||
|
||||
iFramesWritten = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,18 +1,4 @@
|
||||
|
||||
// History of REPLAY_FORMAT_SUBVERSION:
|
||||
// 0x01: standard origin[3], angles[2], and buttons
|
||||
// 0x02: flags added movetype added
|
||||
// 0x03: integrity stuff: style, track, and map added to header. preframe count added (unimplemented until later though)
|
||||
// 0x04: steamid/accountid written as a 32-bit int instead of a string
|
||||
// 0x05: postframes & fTickrate added
|
||||
// 0x06: mousexy and vel added
|
||||
// 0x07: fixed iFrameCount because postframes were included in the value when they shouldn't be
|
||||
// 0x08: added zone-offsets to header
|
||||
|
||||
#define REPLAY_FORMAT_V2 "{SHAVITREPLAYFORMAT}{V2}"
|
||||
#define REPLAY_FORMAT_FINAL "{SHAVITREPLAYFORMAT}{FINAL}"
|
||||
#define REPLAY_FORMAT_SUBVERSION 0x08
|
||||
|
||||
stock bool Shavit_ReplayEnabledStyle(int style)
|
||||
{
|
||||
return !Shavit_GetStyleSettingBool(style, "unranked") && !Shavit_GetStyleSettingBool(style, "noreplay");
|
||||
|
||||
@ -25,6 +25,7 @@
|
||||
#include <shavit>
|
||||
|
||||
#include <shavit/shavit-replay-stocks>
|
||||
#include <shavit/shavit-replay-file>
|
||||
|
||||
public Plugin myinfo =
|
||||
{
|
||||
@ -35,8 +36,6 @@ public Plugin myinfo =
|
||||
url = "https://github.com/shavitush/bhoptimer"
|
||||
}
|
||||
|
||||
#define FRAMES_PER_WRITE 100 // amounts of frames to write per read/write call
|
||||
|
||||
enum struct finished_run_info
|
||||
{
|
||||
int iSteamID;
|
||||
@ -424,72 +423,20 @@ void SaveReplay(int style, int track, float time, int steamid, char[] name, int
|
||||
fCopy = OpenFile(sPath, "wb");
|
||||
}
|
||||
|
||||
if (saveWR)
|
||||
if (fWR)
|
||||
{
|
||||
WriteReplayHeader(fWR, style, track, time, steamid, preframes, postframes, fZoneOffset, iSize);
|
||||
WriteReplayHeader(fWR, style, track, time, steamid, preframes, postframes, fZoneOffset, iSize, gF_Tickrate, gS_Map);
|
||||
}
|
||||
|
||||
if (saveCopy)
|
||||
if (fCopy)
|
||||
{
|
||||
WriteReplayHeader(fCopy, style, track, time, steamid, preframes, postframes, fZoneOffset, iSize);
|
||||
WriteReplayHeader(fCopy, style, track, time, steamid, preframes, postframes, fZoneOffset, iSize, gF_Tickrate, gS_Map);
|
||||
}
|
||||
|
||||
any aFrameData[sizeof(frame_t)];
|
||||
any aWriteData[sizeof(frame_t) * FRAMES_PER_WRITE];
|
||||
int iFramesWritten = 0;
|
||||
|
||||
for(int i = 0; i < iSize; i++)
|
||||
{
|
||||
playerrecording.GetArray(i, aFrameData, sizeof(frame_t));
|
||||
|
||||
for(int j = 0; j < sizeof(frame_t); j++)
|
||||
{
|
||||
aWriteData[(sizeof(frame_t) * iFramesWritten) + j] = aFrameData[j];
|
||||
}
|
||||
|
||||
if(++iFramesWritten == FRAMES_PER_WRITE || i == iSize - 1)
|
||||
{
|
||||
if (saveWR)
|
||||
{
|
||||
fWR.Write(aWriteData, sizeof(frame_t) * iFramesWritten, 4);
|
||||
}
|
||||
|
||||
if (saveCopy)
|
||||
{
|
||||
fCopy.Write(aWriteData, sizeof(frame_t) * iFramesWritten, 4);
|
||||
}
|
||||
|
||||
iFramesWritten = 0;
|
||||
}
|
||||
}
|
||||
WriteReplayFrames(playerrecording, iSize, fWR, fCopy);
|
||||
|
||||
delete fWR;
|
||||
delete fCopy;
|
||||
|
||||
if (!saveWR)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void WriteReplayHeader(File fFile, int style, int track, float time, int steamid, int preframes, int postframes, float fZoneOffset[2], int iSize)
|
||||
{
|
||||
fFile.WriteLine("%d:" ... REPLAY_FORMAT_FINAL, REPLAY_FORMAT_SUBVERSION);
|
||||
|
||||
fFile.WriteString(gS_Map, true);
|
||||
fFile.WriteInt8(style);
|
||||
fFile.WriteInt8(track);
|
||||
fFile.WriteInt32(preframes);
|
||||
|
||||
fFile.WriteInt32(iSize - preframes - postframes);
|
||||
fFile.WriteInt32(view_as<int>(time));
|
||||
fFile.WriteInt32(steamid);
|
||||
|
||||
fFile.WriteInt32(postframes);
|
||||
fFile.WriteInt32(view_as<int>(gF_Tickrate));
|
||||
|
||||
fFile.WriteInt32(view_as<int>(fZoneOffset[0]));
|
||||
fFile.WriteInt32(view_as<int>(fZoneOffset[1]));
|
||||
}
|
||||
|
||||
public Action Shavit_OnUserCmdPre(int client, int &buttons, int &impulse, float vel[3], float angles[3], TimerStatus status, int track, int style, int mouse[2])
|
||||
|
||||
@ -30,6 +30,7 @@
|
||||
#include <adminmenu>
|
||||
|
||||
#include <shavit/shavit-replay-stocks>
|
||||
#include <shavit/shavit-replay-file>
|
||||
|
||||
#undef REQUIRE_EXTENSIONS
|
||||
#include <cstrike>
|
||||
@ -66,22 +67,6 @@ enum struct loopingbot_config_t
|
||||
char sName[MAX_NAME_LENGTH];
|
||||
}
|
||||
|
||||
enum struct replay_header_t
|
||||
{
|
||||
char sReplayFormat[40];
|
||||
int iReplayVersion;
|
||||
char sMap[PLATFORM_MAX_PATH];
|
||||
int iStyle;
|
||||
int iTrack;
|
||||
int iPreFrames;
|
||||
int iFrameCount;
|
||||
float fTime;
|
||||
int iSteamID;
|
||||
int iPostFrames;
|
||||
float fTickrate;
|
||||
float fZoneOffset[2];
|
||||
}
|
||||
|
||||
enum struct bot_info_t
|
||||
{
|
||||
int iEnt;
|
||||
@ -786,6 +771,25 @@ void StopOrRestartBots(int style, int track, bool restart)
|
||||
}
|
||||
}
|
||||
|
||||
bool LoadReplay(frame_cache_t cache, int style, int track, const char[] path, const char[] mapname)
|
||||
{
|
||||
bool ret = LoadReplayCache(cache, style, track, path, mapname);
|
||||
|
||||
if (ret && cache.iSteamID > 0)
|
||||
{
|
||||
char sQuery[192];
|
||||
FormatEx(sQuery, 192, "SELECT name FROM %susers WHERE auth = %d;", gS_MySQLPrefix, cache.iSteamID);
|
||||
|
||||
DataPack hPack = new DataPack();
|
||||
hPack.WriteCell(style);
|
||||
hPack.WriteCell(track);
|
||||
|
||||
gH_SQL.Query(SQL_GetUserName_Callback, sQuery, hPack, DBPrio_High);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool UnloadReplay(int style, int track, bool reload, bool restart, const char[] path = "")
|
||||
{
|
||||
ClearFrameCache(gA_FrameCache[style][track]);
|
||||
@ -1859,262 +1863,6 @@ bool DefaultLoadReplay(frame_cache_t cache, int style, int track)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LoadReplay(frame_cache_t cache, int style, int track, const char[] path, const char[] mapname)
|
||||
{
|
||||
bool success = false;
|
||||
replay_header_t header;
|
||||
File fFile = ReadReplayHeader(path, header, style, track);
|
||||
|
||||
if (fFile != null)
|
||||
{
|
||||
if (header.iReplayVersion > REPLAY_FORMAT_SUBVERSION)
|
||||
{
|
||||
// not going to try and read it
|
||||
}
|
||||
else if (header.iReplayVersion < 0x03 || (StrEqual(header.sMap, mapname, false) && header.iStyle == style && header.iTrack == track))
|
||||
{
|
||||
success = ReadReplayFrames(fFile, header, cache);
|
||||
}
|
||||
|
||||
delete fFile;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool ReadReplayFrames(File file, replay_header_t header, frame_cache_t cache)
|
||||
{
|
||||
int total_cells = 6;
|
||||
int used_cells = 6;
|
||||
bool is_btimes = false;
|
||||
|
||||
if (header.iReplayVersion > 0x01)
|
||||
{
|
||||
total_cells = 8;
|
||||
used_cells = 8;
|
||||
}
|
||||
|
||||
// We have differing total_cells & used_cells because we want to save memory during playback since the latest two cells added (vel & mousexy) aren't needed and are only useful for replay file anticheat usage stuff....
|
||||
if (header.iReplayVersion >= 0x06)
|
||||
{
|
||||
total_cells = 10;
|
||||
used_cells = 8;
|
||||
}
|
||||
|
||||
any aReplayData[sizeof(frame_t)];
|
||||
|
||||
delete cache.aFrames;
|
||||
int iTotalSize = header.iFrameCount + header.iPreFrames + header.iPostFrames;
|
||||
cache.aFrames = new ArrayList(used_cells, iTotalSize);
|
||||
|
||||
if (!header.sReplayFormat[0]) // old replay format. no header.
|
||||
{
|
||||
char sLine[320];
|
||||
char sExplodedLine[6][64];
|
||||
|
||||
if(!file.Seek(0, SEEK_SET))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
while (!file.EndOfFile())
|
||||
{
|
||||
file.ReadLine(sLine, 320);
|
||||
int iStrings = ExplodeString(sLine, "|", sExplodedLine, 6, 64);
|
||||
|
||||
aReplayData[0] = StringToFloat(sExplodedLine[0]);
|
||||
aReplayData[1] = StringToFloat(sExplodedLine[1]);
|
||||
aReplayData[2] = StringToFloat(sExplodedLine[2]);
|
||||
aReplayData[3] = StringToFloat(sExplodedLine[3]);
|
||||
aReplayData[4] = StringToFloat(sExplodedLine[4]);
|
||||
aReplayData[5] = (iStrings == 6) ? StringToInt(sExplodedLine[5]) : 0;
|
||||
|
||||
cache.aFrames.PushArray(aReplayData, 6);
|
||||
}
|
||||
|
||||
cache.iFrameCount = cache.aFrames.Length;
|
||||
}
|
||||
else // assumes the file position will be at the start of the frames
|
||||
{
|
||||
is_btimes = StrEqual(header.sReplayFormat, "btimes");
|
||||
|
||||
for (int i = 0; i < iTotalSize; i++)
|
||||
{
|
||||
if(file.Read(aReplayData, total_cells, 4) >= 0)
|
||||
{
|
||||
cache.aFrames.SetArray(i, aReplayData, used_cells);
|
||||
|
||||
if (is_btimes && (aReplayData[5] & IN_BULLRUSH))
|
||||
{
|
||||
if (!header.iPreFrames)
|
||||
{
|
||||
header.iPreFrames = i;
|
||||
header.iFrameCount -= i;
|
||||
}
|
||||
else if (!header.iPostFrames)
|
||||
{
|
||||
header.iPostFrames = header.iFrameCount + header.iPreFrames - i;
|
||||
header.iFrameCount -= header.iPostFrames;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (StrEqual(header.sReplayFormat, REPLAY_FORMAT_FINAL))
|
||||
{
|
||||
char sQuery[192];
|
||||
FormatEx(sQuery, 192, "SELECT name FROM %susers WHERE auth = %d;", gS_MySQLPrefix, header.iSteamID);
|
||||
|
||||
DataPack hPack = new DataPack();
|
||||
hPack.WriteCell(header.iStyle);
|
||||
hPack.WriteCell(header.iTrack);
|
||||
|
||||
gH_SQL.Query(SQL_GetUserName_Callback, sQuery, hPack, DBPrio_High);
|
||||
}
|
||||
}
|
||||
|
||||
cache.iFrameCount = header.iFrameCount;
|
||||
cache.fTime = header.fTime;
|
||||
cache.iReplayVersion = header.iReplayVersion;
|
||||
cache.bNewFormat = StrEqual(header.sReplayFormat, REPLAY_FORMAT_FINAL) || is_btimes;
|
||||
cache.sReplayName = "unknown";
|
||||
cache.iPreFrames = header.iPreFrames;
|
||||
cache.iPostFrames = header.iPostFrames;
|
||||
cache.fTickrate = header.fTickrate;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
File ReadReplayHeader(const char[] path, replay_header_t header, int style, int track)
|
||||
{
|
||||
replay_header_t empty_header;
|
||||
header = empty_header;
|
||||
|
||||
if (!FileExists(path))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
File file = OpenFile(path, "rb");
|
||||
|
||||
if (file == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
char sHeader[64];
|
||||
|
||||
if(!file.ReadLine(sHeader, 64))
|
||||
{
|
||||
delete file;
|
||||
return null;
|
||||
}
|
||||
|
||||
TrimString(sHeader);
|
||||
char sExplodedHeader[2][64];
|
||||
ExplodeString(sHeader, ":", sExplodedHeader, 2, 64);
|
||||
|
||||
strcopy(header.sReplayFormat, sizeof(header.sReplayFormat), sExplodedHeader[1]);
|
||||
|
||||
if(StrEqual(header.sReplayFormat, REPLAY_FORMAT_FINAL)) // hopefully, the last of them
|
||||
{
|
||||
int version = StringToInt(sExplodedHeader[0]);
|
||||
|
||||
header.iReplayVersion = version;
|
||||
|
||||
// replay file integrity and PreFrames
|
||||
if(version >= 0x03)
|
||||
{
|
||||
file.ReadString(header.sMap, PLATFORM_MAX_PATH);
|
||||
file.ReadUint8(header.iStyle);
|
||||
file.ReadUint8(header.iTrack);
|
||||
|
||||
file.ReadInt32(header.iPreFrames);
|
||||
|
||||
// In case the replay was from when there could still be negative preframes
|
||||
if(header.iPreFrames < 0)
|
||||
{
|
||||
header.iPreFrames = 0;
|
||||
}
|
||||
}
|
||||
|
||||
file.ReadInt32(header.iFrameCount);
|
||||
file.ReadInt32(view_as<int>(header.fTime));
|
||||
|
||||
if (header.iReplayVersion < 0x07)
|
||||
{
|
||||
header.iFrameCount -= header.iPreFrames;
|
||||
}
|
||||
|
||||
if(version >= 0x04)
|
||||
{
|
||||
file.ReadInt32(header.iSteamID);
|
||||
}
|
||||
else
|
||||
{
|
||||
char sAuthID[32];
|
||||
file.ReadString(sAuthID, 32);
|
||||
ReplaceString(sAuthID, 32, "[U:1:", "");
|
||||
ReplaceString(sAuthID, 32, "]", "");
|
||||
header.iSteamID = StringToInt(sAuthID);
|
||||
}
|
||||
|
||||
if (version >= 0x05)
|
||||
{
|
||||
file.ReadInt32(header.iPostFrames);
|
||||
file.ReadInt32(view_as<int>(header.fTickrate));
|
||||
|
||||
if (header.iReplayVersion < 0x07)
|
||||
{
|
||||
header.iFrameCount -= header.iPostFrames;
|
||||
}
|
||||
}
|
||||
|
||||
if (version >= 0x08)
|
||||
{
|
||||
file.ReadInt32(view_as<int>(header.fZoneOffset[0]));
|
||||
file.ReadInt32(view_as<int>(header.fZoneOffset[1]));
|
||||
}
|
||||
}
|
||||
else if(StrEqual(header.sReplayFormat, REPLAY_FORMAT_V2))
|
||||
{
|
||||
header.iFrameCount = StringToInt(sExplodedHeader[0]);
|
||||
}
|
||||
else // old, outdated and slow - only used for ancient replays
|
||||
{
|
||||
// check for btimes replays
|
||||
file.Seek(0, SEEK_SET);
|
||||
any stuff[2];
|
||||
file.Read(stuff, 2, 4);
|
||||
|
||||
int btimes_player_id = stuff[0];
|
||||
float run_time = stuff[1];
|
||||
|
||||
if (btimes_player_id >= 0 && run_time > 0.0 && run_time < (10.0 * 60.0 * 60.0))
|
||||
{
|
||||
header.sReplayFormat = "btimes";
|
||||
header.fTime = run_time;
|
||||
|
||||
file.Seek(0, SEEK_END);
|
||||
header.iFrameCount = (file.Position / 4 - 2) / 6;
|
||||
file.Seek(2*4, SEEK_SET);
|
||||
}
|
||||
}
|
||||
|
||||
if (header.iReplayVersion < 0x03)
|
||||
{
|
||||
header.iStyle = style;
|
||||
header.iTrack = track;
|
||||
}
|
||||
|
||||
if (header.iReplayVersion < 0x05)
|
||||
{
|
||||
header.fTickrate = gF_Tickrate;
|
||||
}
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
bool DeleteReplay(int style, int track, int accountid, const char[] mapname)
|
||||
{
|
||||
char sPath[PLATFORM_MAX_PATH];
|
||||
|
||||
Loading…
Reference in New Issue
Block a user